mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			1377 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			1377 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From c44de58d0972d05851512ba8cbd928b7adef5187 Mon Sep 17 00:00:00 2001
 | |
| From: Kurt Mahan <kmahan@freescale.com>
 | |
| Date: Tue, 8 Jul 2008 17:11:33 -0600
 | |
| Subject: [PATCH] Add FlexCAN support.
 | |
| 
 | |
| LTIBName: mcfv4e-flexcan
 | |
| Signed-off-by: Kurt Mahan <kmahan@freescale.com>
 | |
| Signed-off-by: Huan Wang <b18965@freescale.com>
 | |
| ---
 | |
|  drivers/net/can/Kconfig               |   13 ++
 | |
|  drivers/net/can/Makefile              |    1 +
 | |
|  drivers/net/can/flexcan/Makefile      |    5 +
 | |
|  drivers/net/can/flexcan/flexcan.c     |  378 +++++++++++++++++++++++++++++++++
 | |
|  drivers/net/can/flexcan/flexcan.h     |  148 +++++++++++++
 | |
|  drivers/net/can/flexcan/mcf548x_can.c |  213 ++++++++++++++++++
 | |
|  include/asm-m68k/m5485sim.h           |    2 +
 | |
|  include/linux/can/dev.h               |   62 ++++++
 | |
|  include/linux/can/ioctl.h             |  152 +++++++++++++
 | |
|  include/linux/can/version.h           |   22 ++
 | |
|  net/can/Makefile                      |    3 +
 | |
|  net/can/dev.c                         |  292 +++++++++++++++++++++++++
 | |
|  12 files changed, 1291 insertions(+), 0 deletions(-)
 | |
|  create mode 100644 drivers/net/can/flexcan/Makefile
 | |
|  create mode 100644 drivers/net/can/flexcan/flexcan.c
 | |
|  create mode 100644 drivers/net/can/flexcan/flexcan.h
 | |
|  create mode 100644 drivers/net/can/flexcan/mcf548x_can.c
 | |
|  create mode 100644 include/linux/can/dev.h
 | |
|  create mode 100644 include/linux/can/ioctl.h
 | |
|  create mode 100644 include/linux/can/version.h
 | |
|  create mode 100644 net/can/dev.c
 | |
| 
 | |
| --- a/drivers/net/can/Kconfig
 | |
| +++ b/drivers/net/can/Kconfig
 | |
| @@ -12,6 +12,19 @@ config CAN_VCAN
 | |
|  	  This driver can also be built as a module.  If so, the module
 | |
|  	  will be called vcan.
 | |
|  
 | |
| +config CAN_FLEXCAN
 | |
| +	tristate "Support for Freescale FLEXCAN based chips"
 | |
| +	depends on CAN && (PPC || M68K || M68KNOMMU)
 | |
| +	---help---
 | |
| +	  Say Y here if you want to support for Freescale FlexCAN.
 | |
| +
 | |
| +config CAN_MCF547X_8X
 | |
| +	tristate "Freescale MCF547X/MCF548X onboard CAN controller"
 | |
| +	depends on CAN_FLEXCAN && (M547X || M548X)
 | |
| +	---help---
 | |
| +	  Say Y here if you want to support for Freescale MCF547x/MCF548x
 | |
| +	  onboard dualCAN controller.
 | |
| +
 | |
|  config CAN_DEBUG_DEVICES
 | |
|  	bool "CAN devices debugging messages"
 | |
|  	depends on CAN
 | |
| --- a/drivers/net/can/Makefile
 | |
| +++ b/drivers/net/can/Makefile
 | |
| @@ -3,3 +3,4 @@
 | |
|  #
 | |
|  
 | |
|  obj-$(CONFIG_CAN_VCAN)		+= vcan.o
 | |
| +obj-$(CONFIG_CAN_FLEXCAN)	+= flexcan/
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/can/flexcan/Makefile
 | |
| @@ -0,0 +1,5 @@
 | |
| +
 | |
| +obj-$(CONFIG_CAN_MCF547X_8X)	+= flexcan-mcf548x.o
 | |
| +
 | |
| +flexcan-mcf548x-objs	:= flexcan.o mcf548x_can.o
 | |
| +
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/can/flexcan/flexcan.c
 | |
| @@ -0,0 +1,378 @@
 | |
| +/*
 | |
| + * flexcan.c
 | |
| + *
 | |
| + * DESCRIPTION:
 | |
| + *  CAN bus driver for the alone generic (as possible as) FLEXCAN controller.
 | |
| + *
 | |
| + * AUTHOR:
 | |
| + *  Andrey Volkov <avolkov@varma-el.com>
 | |
| + *
 | |
| + * COPYRIGHT:
 | |
| + *  2005-2006, Varma Electronics Oy
 | |
| + *
 | |
| + * LICENCE:
 | |
| + *  This program is free software; you can redistribute it and/or modify
 | |
| + *  it under the terms of the GNU General Public License as published by
 | |
| + *  the Free Software Foundation; either version 2 of the License, or
 | |
| + *  (at your option) any later version.
 | |
| + *
 | |
| + *  This program is distributed in the hope that it will be useful,
 | |
| + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| + *  GNU General Public License for more details.
 | |
| + *
 | |
| + *  You should have received a copy of the GNU General Public License
 | |
| + *  along with this program; if not, write to the Free Software
 | |
| + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| + *
 | |
| + *  HISTORY:
 | |
| + *  	2008-06-23 Support for MCF548x's FlexCAN
 | |
| + * 			Huan, Wang <b18965@freescale.com>
 | |
| + */
 | |
| +
 | |
| +
 | |
| +
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/delay.h>
 | |
| +#include <linux/netdevice.h>
 | |
| +#include <linux/if_arp.h>
 | |
| +#include <linux/if_ether.h>
 | |
| +#include <linux/can.h>
 | |
| +#include <linux/list.h>
 | |
| +#include <linux/io.h>
 | |
| +
 | |
| +#include <linux/can/dev.h>
 | |
| +#include <linux/can/error.h>
 | |
| +#include "flexcan.h"
 | |
| +#include <asm/coldfire.h>
 | |
| +#include <asm/m5485sim.h>
 | |
| +#include <linux/can/version.h>	/* for RCSID. Removed by mkpatch script */
 | |
| +RCSID("$Id$");
 | |
| +
 | |
| +struct flexcan_priv {
 | |
| +	struct can_priv can;
 | |
| +	volatile unsigned long flags;
 | |
| +	u8 shadow_statflg;
 | |
| +	u8 shadow_canrier;
 | |
| +	u8 cur_pri;
 | |
| +	u8 tx_active;
 | |
| +
 | |
| +	struct list_head tx_head;
 | |
| +	struct napi_struct napi;
 | |
| +	struct net_device *dev;
 | |
| +};
 | |
| +
 | |
| +
 | |
| +static int flexcan_hard_start_xmit(struct sk_buff *skb, struct net_device *dev)
 | |
| +{
 | |
| +	struct can_frame *frame = (struct can_frame *)skb->data;
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +	int i, len;
 | |
| +	int txbuf = 0;
 | |
| +	u32 can_id, can_ext, tmp, tmp1;
 | |
| +
 | |
| +	/* Transmission inactive */
 | |
| +	regs->cantxfg[txbuf].can_dlc = MB_CNT_CODE(0x08);
 | |
| +
 | |
| +	can_ext = frame->can_id;
 | |
| +	if (can_ext & CAN_EFF_FLAG) {
 | |
| +		/* Frame format is extended */
 | |
| +		regs->cantxfg[txbuf].can_dlc |= (1 << 21);
 | |
| +		regs->cantxfg[txbuf].can_dlc |= (1 << 22);
 | |
| +		can_id = frame->can_id & MB_ID_EXT;
 | |
| +		if (frame->can_id & CAN_RTR_FLAG)
 | |
| +			regs->cantxfg[txbuf].can_dlc |= (1 << 20);
 | |
| +
 | |
| +		tmp = (can_id & CAN_SFF_MASK) << 18;
 | |
| +		tmp1 = can_id >> 11;
 | |
| +		can_id = tmp | tmp1;
 | |
| +		regs->cantxfg[txbuf].can_id = can_id;
 | |
| +	} else {
 | |
| +		/* Frame format is standard */
 | |
| +		can_id = frame->can_id & MB_ID_EXT;
 | |
| +		if (frame->can_id & CAN_RTR_FLAG)
 | |
| +			regs->cantxfg[txbuf].can_dlc |= (1 << 20);
 | |
| +
 | |
| +		regs->cantxfg[txbuf].can_id = can_id << 18;
 | |
| +	}
 | |
| +
 | |
| +	len = 8;
 | |
| +	for (i = 0; i < len; i++)
 | |
| +		regs->cantxfg[txbuf].data[i] = frame->data[i];
 | |
| +
 | |
| +	regs->cantxfg[txbuf].can_dlc |= len << 16;
 | |
| +	/* Transmission active */
 | |
| +	regs->cantxfg[txbuf].can_dlc |= MB_CNT_CODE(0x0c);
 | |
| +	kfree_skb(skb);
 | |
| +	return NETDEV_TX_OK;
 | |
| +}
 | |
| +
 | |
| +static void flexcan_tx_timeout(struct net_device *dev)
 | |
| +{
 | |
| +	struct sk_buff *skb;
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +	struct can_frame *frame;
 | |
| +	int length = 8;
 | |
| +
 | |
| +	/* Diable the interrupts */
 | |
| +	regs->imask = IMASK_BUFF_DISABLE_ALL;
 | |
| +
 | |
| +	skb = dev_alloc_skb(sizeof(struct can_frame));
 | |
| +	if (!skb) {
 | |
| +		if (printk_ratelimit())
 | |
| +			dev_notice(ND2D(dev), "TIMEOUT packet dropped.\n");
 | |
| +		return;
 | |
| +	}
 | |
| +	frame = (struct can_frame *)skb_put(skb, sizeof(struct can_frame));
 | |
| +
 | |
| +	frame->can_dlc = length;
 | |
| +
 | |
| +	skb->dev = dev;
 | |
| +	skb->protocol = __constant_htons(ETH_P_CAN);
 | |
| +	skb->pkt_type = PACKET_BROADCAST;
 | |
| +	skb->ip_summed = CHECKSUM_UNNECESSARY;
 | |
| +
 | |
| +	netif_rx(skb);
 | |
| +}
 | |
| +
 | |
| +static irqreturn_t flexcan_isr(int irq, void *dev_id)
 | |
| +{
 | |
| +	struct net_device *dev = (struct net_device *)dev_id;
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +	struct net_device_stats *stats = dev->get_stats(dev);
 | |
| +	struct sk_buff *skb;
 | |
| +	struct can_frame *frame;
 | |
| +	u32 iflags, oflags;
 | |
| +	int i, k;
 | |
| +	int retval = 1;
 | |
| +
 | |
| +	iflags = regs->iflag;
 | |
| +	oflags = iflags;
 | |
| +	for (i = 0; i < 16; i++) {
 | |
| +		if (iflags & (0x01 << i)) {
 | |
| +			struct flexcan_mb *mb = ®s->cantxfg[i];
 | |
| +			int ctrl = mb->can_dlc;
 | |
| +			int code = (ctrl >> 24) & 0x0f;
 | |
| +			int length = (ctrl >> 16) & 0x0f;
 | |
| +			u32 tmp, tmp1;
 | |
| +
 | |
| +			if (code < 8 && (length > 0)) {
 | |
| +				/* receive frame */
 | |
| +				skb = dev_alloc_skb(sizeof(struct can_frame));
 | |
| +				if (!skb)
 | |
| +					dev_notice(ND2D(dev),
 | |
| +							"Packets dropped.\n");
 | |
| +				skb->dev = dev;
 | |
| +				frame = (struct can_frame *)skb_put(skb,
 | |
| +						sizeof(struct can_frame));
 | |
| +
 | |
| +				frame->can_id &= 0x0;
 | |
| +				frame->can_dlc = length;
 | |
| +				tmp1 = mb->can_id & MB_ID_EXT;
 | |
| +				if (ctrl & MB_CNT_IDE) {
 | |
| +					tmp = tmp1;
 | |
| +					tmp = (tmp >> 18) & CAN_SFF_MASK;
 | |
| +					frame->can_id = (tmp1 << 11) | tmp;
 | |
| +					frame->can_id &= CAN_EFF_MASK;
 | |
| +					frame->can_id |= CAN_EFF_FLAG;
 | |
| +					if (ctrl & MB_CNT_RTR)
 | |
| +						frame->can_id |= CAN_RTR_FLAG;
 | |
| +				} else {
 | |
| +					frame->can_id  = tmp1 >> 18;
 | |
| +					if (ctrl & MB_CNT_RTR)
 | |
| +						frame->can_id |= CAN_RTR_FLAG;
 | |
| +				}
 | |
| +
 | |
| +				for (k = 0; k < 8; k++)
 | |
| +					frame->data[k] = mb->data[k];
 | |
| +
 | |
| +				mb->can_dlc &= MB_CODE_MASK;
 | |
| +				mb->can_dlc |= MB_CNT_CODE(0x04);
 | |
| +
 | |
| +				stats->rx_packets++;
 | |
| +				stats->rx_bytes += frame->can_dlc;
 | |
| +				skb->dev = dev;
 | |
| +				skb->protocol = __constant_htons(ETH_P_CAN);
 | |
| +				skb->ip_summed = CHECKSUM_UNNECESSARY;
 | |
| +
 | |
| +				retval = netif_rx(skb);
 | |
| +				if (retval == NET_RX_DROP)
 | |
| +					dev_notice(ND2D(dev),
 | |
| +							"Packets dropped.\n");
 | |
| +			} else {
 | |
| +				/* transmit frame */
 | |
| +				mb->can_dlc = MB_CNT_CODE(0x04);
 | |
| +			}
 | |
| +		}
 | |
| +	}
 | |
| +	regs->iflag = oflags;
 | |
| +
 | |
| +	return IRQ_HANDLED;
 | |
| +}
 | |
| +
 | |
| +static int flexcan_do_set_bit_time(struct net_device *dev,
 | |
| +				 struct can_bittime *bt)
 | |
| +{
 | |
| +	struct flexcan_priv *priv = netdev_priv(dev);
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +	int ret = 0;
 | |
| +	u32 reg;
 | |
| +
 | |
| +	if (bt->type != CAN_BITTIME_STD)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	spin_lock_irq(&priv->can.irq_lock);
 | |
| +
 | |
| +	reg = CANCTRL_PRESDIV(bt->std.brp) | CANCTRL_PSEG1(bt->std.phase_seg1
 | |
| +			- 1) | CANCTRL_PSEG2(bt->std.phase_seg2 - 1);
 | |
| +	regs->canctrl &= CANCTRL_BITTIME;
 | |
| +	regs->canctrl |= (reg | CANCTRL_SAMP(bt->std.sam) |
 | |
| +		CANCTRL_PROPSEG(bt->std.prop_seg - 1));
 | |
| +
 | |
| +	spin_unlock_irq(&priv->can.irq_lock);
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +
 | |
| +static int flexcan_open(struct net_device *dev)
 | |
| +{
 | |
| +	int ret, i, j;
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +
 | |
| +#if defined(CONFIG_M547X_8X)
 | |
| +	MCF_PAR_TIMER = MCF_PAR_TIMER | 0x28;
 | |
| +	MCF_PAR_TIMER = MCF_PAR_TIMER & 0xf8;
 | |
| +	MCF_PAR_DSPI = MCF_PAR_DSPI | 0x0a00;
 | |
| +	MCF_PAR_FECI2CIRQ = MCF_PAR_FECI2CIRQ | 0x0283;
 | |
| +	MCF_PAR_PSCn(2) = MCF_PAR_PSCn(2) & 0x0f;
 | |
| +	MCF_PAR_PSCn(2) = MCF_PAR_PSCn(2) | 0x50;
 | |
| +#endif
 | |
| +
 | |
| +	regs->canmcr |= CANMCR_SOFTRST;
 | |
| +	regs->canmcr |= CANMCR_MDIS;
 | |
| +	udelay(10);
 | |
| +
 | |
| +	if ((regs->canmcr & CANMCR_SOFTRST) != 0x0) {
 | |
| +		dev_err(ND2D(dev), "Failed to softreset can module.\n");
 | |
| +		return -1;
 | |
| +	}
 | |
| +
 | |
| +	/* Enable error and bus off interrupt */
 | |
| +	regs->canctrl |= (CANCTRL_RJW(3) | CANCTRL_ERRMSK |
 | |
| +			CANCTRL_BOFFMSK);
 | |
| +
 | |
| +	/* Set lowest buffer transmitted first */
 | |
| +	regs->canctrl |= CANCTRL_LBUF;
 | |
| +
 | |
| +	for (i = 0; i < 16; i++) {
 | |
| +		regs->cantxfg[i].can_dlc = 0;
 | |
| +		regs->cantxfg[i].can_id = 0;
 | |
| +		for (j = 0; j < 8; j++)
 | |
| +			regs->cantxfg[i].data[j] = 0;
 | |
| +
 | |
| +		/* Put MB into rx queue */
 | |
| +		regs->cantxfg[i].can_dlc = MB_CNT_CODE(0x04);
 | |
| +	}
 | |
| +
 | |
| +	/* acceptance mask/acceptance code (accept everything) */
 | |
| +	regs->rxgmask = 0x00000000;
 | |
| +	regs->rx14mask = 0x00000000;
 | |
| +	regs->rx15mask = 0x00000000;
 | |
| +	/* extended frame */
 | |
| +	regs->cantxfg[14].can_dlc |= 0x600000;
 | |
| +	/* Enable flexcan module */
 | |
| +	regs->canmcr &= ~CANMCR_MDIS;
 | |
| +	/* Synchronize with the can bus */
 | |
| +	regs->canmcr &= ~CANMCR_HALT;
 | |
| +
 | |
| +#if defined(CONFIG_M547X_8X)
 | |
| +	for (i = 0; i < 2; i++) {
 | |
| +		MCF_ICR(ISC_CANn_MBOR(i)) = 0x33;
 | |
| +		MCF_ICR(ISC_CANn_ERR(i)) = 0x33;
 | |
| +		MCF_ICR(ISC_CANn_BUSOFF(i)) = 0x33;
 | |
| +	}
 | |
| +
 | |
| +	ret = request_irq(dev->irq + 64, flexcan_isr, IRQF_DISABLED,
 | |
| +			dev->name, dev);
 | |
| +	ret = request_irq(dev->irq + 1 + 64, flexcan_isr, IRQF_DISABLED,
 | |
| +			dev->name, dev);
 | |
| +	ret = request_irq(dev->irq + 2 + 64, flexcan_isr, IRQF_DISABLED,
 | |
| +			dev->name, dev);
 | |
| +	if (ret < 0) {
 | |
| +		printk(KERN_ERR "%s - failed to attach interrupt.\n",
 | |
| +		       dev->name);
 | |
| +		return ret;
 | |
| +	}
 | |
| +#endif
 | |
| +
 | |
| +	/* Enable all interrupts */
 | |
| +	regs->imask = IMASK_BUFF_ENABLE_ALL;
 | |
| +	netif_start_queue(dev);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static int flexcan_close(struct net_device *dev)
 | |
| +{
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +
 | |
| +	netif_stop_queue(dev);
 | |
| +
 | |
| +	/* Disable all interrupts */
 | |
| +	regs->imask = IMASK_BUFF_DISABLE_ALL;
 | |
| +	free_irq(dev->irq + 64, dev);
 | |
| +	free_irq(dev->irq + 1 + 64, dev);
 | |
| +	free_irq(dev->irq + 2 + 64, dev);
 | |
| +
 | |
| +	/* Disable module */
 | |
| +	regs->canmcr |= CANMCR_MDIS;
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +int register_flexcandev(struct net_device *dev, int clock_src)
 | |
| +{
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +
 | |
| +	regs->canmcr &= ~CANMCR_MDIS;
 | |
| +	udelay(100);
 | |
| +	regs->canmcr |= (CANMCR_FRZ | CANMCR_HALT);
 | |
| +	return register_netdev(dev);
 | |
| +}
 | |
| +EXPORT_SYMBOL(register_flexcandev);
 | |
| +
 | |
| +void unregister_flexcandev(struct net_device *dev)
 | |
| +{
 | |
| +	struct flexcan_regs *regs = (struct flexcan_regs *)dev->base_addr;
 | |
| +
 | |
| +	regs->canmcr |= (CANMCR_FRZ | CANMCR_HALT);
 | |
| +	regs->canmcr |= CANMCR_MDIS;
 | |
| +
 | |
| +	unregister_netdev(dev);
 | |
| +}
 | |
| +EXPORT_SYMBOL(unregister_flexcandev);
 | |
| +
 | |
| +struct net_device *alloc_flexcandev(void)
 | |
| +{
 | |
| +	struct net_device *dev;
 | |
| +	struct flexcan_priv *priv;
 | |
| +
 | |
| +	dev = alloc_candev(sizeof(struct flexcan_priv));
 | |
| +	if (!dev)
 | |
| +		return NULL;
 | |
| +
 | |
| +	priv = netdev_priv(dev);
 | |
| +	priv->dev = dev;
 | |
| +	dev->open = flexcan_open;
 | |
| +	dev->stop = flexcan_close;
 | |
| +	dev->hard_start_xmit = flexcan_hard_start_xmit;
 | |
| +	dev->tx_timeout = flexcan_tx_timeout;
 | |
| +	dev->flags |= IFF_NOARP;
 | |
| +	priv->can.do_set_bit_time = flexcan_do_set_bit_time;
 | |
| +	return dev;
 | |
| +}
 | |
| +EXPORT_SYMBOL(alloc_flexcandev);
 | |
| +
 | |
| +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
 | |
| +MODULE_LICENSE("GPL v2");
 | |
| +MODULE_DESCRIPTION("CAN port driver for flexcan based chip");
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/can/flexcan/flexcan.h
 | |
| @@ -0,0 +1,148 @@
 | |
| +/*
 | |
| + * flexcan.h
 | |
| + *
 | |
| + * DESCRIPTION:
 | |
| + *  Definitions of consts/structs to drive the Freescale FLEXCAN.
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#ifndef __FLEXCAN_H__
 | |
| +#define __FLEXCAN_H__
 | |
| +
 | |
| +#include <linux/autoconf.h>
 | |
| +#include <linux/types.h>
 | |
| +
 | |
| +/* FLEXCAN module configuration register (CANMCR) bits */
 | |
| +#define CANMCR_MDIS				0x80000000
 | |
| +#define CANMCR_FRZ				0x40000000
 | |
| +#define CANMCR_HALT				0x10000000
 | |
| +#define CANMCR_SOFTRST				0x02000000
 | |
| +#define CANMCR_FRZACK				0x01000000
 | |
| +#define CANMCR_SUPV				0x00800000
 | |
| +#define CANMCR_MAXMB(x)				((x)&0x0f)
 | |
| +
 | |
| +/* FLEXCAN control register (CANCTRL) bits */
 | |
| +#define CANCTRL_PRESDIV(x)			(((x)&0xff)<<24)
 | |
| +#define CANCTRL_RJW(x)				(((x)&0x03)<<22)
 | |
| +#define CANCTRL_PSEG1(x)			(((x)&0x07)<<19)
 | |
| +#define CANCTRL_PSEG2(x)			(((x)&0x07)<<16)
 | |
| +#define CANCTRL_BOFFMSK				0x00008000
 | |
| +#define CANCTRL_ERRMSK				0x00004000
 | |
| +#define CANCTRL_LPB				0x00001000
 | |
| +#define CANCTRL_SAMP(x)				(((x)&0x1)<<7)
 | |
| +#define CANCTRL_BOFFREC				0x00000040
 | |
| +#define CANCTRL_TSYNC				0x00000020
 | |
| +#define CANCTRL_LBUF				0x00000010
 | |
| +#define CANCTRL_LOM				0x00000008
 | |
| +#define CANCTRL_PROPSEG(x)			((x)&0x07)
 | |
| +#define CANCTRL_BITTIME				0x00c0d078
 | |
| +
 | |
| +/* FLEXCAN error counter register (ERRCNT) bits */
 | |
| +#define ERRCNT_REXECTR(x)			(((x)&0xff)<<8)
 | |
| +#define ERRCNT_TXECTR(x)			((x)&0xff)
 | |
| +
 | |
| +/* FLEXCAN error and status register (ERRSTAT) bits */
 | |
| +#define ERRSTAT_BITERR(x)			(((x)&0x03)<<14)
 | |
| +#define ERRSTAT_ACKERR				0x00002000
 | |
| +#define ERRSTAT_CRCERR				0x00001000
 | |
| +#define ERRSTAT_FRMERR				0x00000800
 | |
| +#define ERRSTAT_STFERR				0x00000400
 | |
| +#define ERRSTAT_TXWRN				0x00000200
 | |
| +#define ERRSTAT_RXWRN				0x00000100
 | |
| +#define ERRSTAT_IDLE 				0x00000080
 | |
| +#define ERRSTAT_TXRX				0x00000040
 | |
| +#define ERRSTAT_FLTCONF(x)			(((x)&0x03)<<4)
 | |
| +#define ERRSTAT_BOFFINT				0x00000004
 | |
| +#define ERRSTAT_ERRINT          		0x00000002
 | |
| +
 | |
| +/* FLEXCAN interrupt mask register (IMASK) bits */
 | |
| +#define IMASK_BUF15M				0x8000
 | |
| +#define IMASK_BUF14M				0x4000
 | |
| +#define IMASK_BUF13M				0x2000
 | |
| +#define IMASK_BUF12M				0x1000
 | |
| +#define IMASK_BUF11M				0x0800
 | |
| +#define IMASK_BUF10M				0x0400
 | |
| +#define IMASK_BUF9M				0x0200
 | |
| +#define IMASK_BUF8M				0x0100
 | |
| +#define IMASK_BUF7M				0x0080
 | |
| +#define IMASK_BUF6M				0x0040
 | |
| +#define IMASK_BUF5M				0x0020
 | |
| +#define IMASK_BUF4M				0x0010
 | |
| +#define IMASK_BUF3M				0x0008
 | |
| +#define IMASK_BUF2M				0x0004
 | |
| +#define IMASK_BUF1M				0x0002
 | |
| +#define IMASK_BUF0M				0x0001
 | |
| +#define IMASK_BUFnM(x)				(0x1<<(x))
 | |
| +#define IMASK_BUFF_ENABLE_ALL			0xffff
 | |
| +#define IMASK_BUFF_DISABLE_ALL 			0x0000
 | |
| +
 | |
| +/* FLEXCAN interrupt flag register (IFLAG) bits */
 | |
| +#define IFLAG_BUF15M				0x8000
 | |
| +#define IFLAG_BUF14M				0x4000
 | |
| +#define IFLAG_BUF13M				0x2000
 | |
| +#define IFLAG_BUF12M				0x1000
 | |
| +#define IFLAG_BUF11M				0x0800
 | |
| +#define IFLAG_BUF10M				0x0400
 | |
| +#define IFLAG_BUF9M				0x0200
 | |
| +#define IFLAG_BUF8M				0x0100
 | |
| +#define IFLAG_BUF7M				0x0080
 | |
| +#define IFLAG_BUF6M				0x0040
 | |
| +#define IFLAG_BUF5M				0x0020
 | |
| +#define IFLAG_BUF4M				0x0010
 | |
| +#define IFLAG_BUF3M				0x0008
 | |
| +#define IFLAG_BUF2M				0x0004
 | |
| +#define IFLAG_BUF1M				0x0002
 | |
| +#define IFLAG_BUF0M				0x0001
 | |
| +#define IFLAG_BUFnM(x)				(0x1<<(x))
 | |
| +#define IFLAG_BUFF_SET_ALL			0xffff
 | |
| +#define IFLAG_BUFF_DISABLE_ALL 			0x0000
 | |
| +
 | |
| +/* FLEXCAN message buffers */
 | |
| +#define MB_CNT_CODE(x)				(((x)&0x0f)<<24)
 | |
| +#define MB_CNT_SRR				0x00400000
 | |
| +#define MB_CNT_IDE				0x00200000
 | |
| +#define MB_CNT_RTR				0x00100000
 | |
| +#define MB_CNT_LENGTH(x)			(((x)&0x0f)<<16)
 | |
| +#define MB_CNT_TIMESTAMP(x)			((x)&0xffff)
 | |
| +
 | |
| +#define MB_ID_STD				((0x7ff)<<18)
 | |
| +#define MB_ID_EXT				0x1fffffff
 | |
| +#define MB_CODE_MASK				0xf0ffffff
 | |
| +
 | |
| +/* Structure of the message buffer */
 | |
| +struct flexcan_mb {
 | |
| +	u32	can_dlc;
 | |
| +	u32	can_id;
 | |
| +	u8	data[8];
 | |
| +};
 | |
| +
 | |
| +/* Structure of the hardware registers */
 | |
| +struct flexcan_regs {
 | |
| +	u32	canmcr;
 | |
| +	u32	canctrl;
 | |
| +	u32	timer;
 | |
| +	u32	reserved1;
 | |
| +	u32 	rxgmask;
 | |
| +	u32 	rx14mask;
 | |
| +	u32 	rx15mask;
 | |
| +	u32	errcnt;
 | |
| +	u32 	errstat;
 | |
| +	u32	reserved2;
 | |
| +	u32	imask;
 | |
| +	u32	reserved3;
 | |
| +	u32 	iflag;
 | |
| +	u32	reserved4[19];
 | |
| +	struct	flexcan_mb cantxfg[16];
 | |
| +};
 | |
| +
 | |
| +struct flexcan_platform_data {
 | |
| +	u8 clock_src;		/* FLEXCAN clock source CRIN or SYSCLK */
 | |
| +	u32 clock_frq;		/* can ref. clock, in Hz */
 | |
| +};
 | |
| +
 | |
| +struct net_device *alloc_flexcandev(void);
 | |
| +
 | |
| +extern int register_flexcandev(struct net_device *dev, int clock_src);
 | |
| +extern void unregister_flexcandev(struct net_device *dev);
 | |
| +
 | |
| +#endif				/* __FLEXCAN_H__ */
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/can/flexcan/mcf548x_can.c
 | |
| @@ -0,0 +1,213 @@
 | |
| +/*
 | |
| + * DESCRIPTION:
 | |
| + *  CAN bus driver for the Freescale MCF548x embedded CPU.
 | |
| + *
 | |
| + * AUTHOR:
 | |
| + *  Andrey Volkov <avolkov@varma-el.com>
 | |
| + *
 | |
| + * COPYRIGHT:
 | |
| + *  2004-2005, Varma Electronics Oy
 | |
| + *
 | |
| + * LICENCE:
 | |
| + *  This program is free software; you can redistribute it and/or modify
 | |
| + *  it under the terms of the GNU General Public License as published by
 | |
| + *  the Free Software Foundation; either version 2 of the License, or
 | |
| + *  (at your option) any later version.
 | |
| + *
 | |
| + *  This program is distributed in the hope that it will be useful,
 | |
| + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
| + *  GNU General Public License for more details.
 | |
| + *
 | |
| + *  You should have received a copy of the GNU General Public License
 | |
| + *  along with this program; if not, write to the Free Software
 | |
| + *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| + *
 | |
| + * HISTORY:
 | |
| + * 	2008-06-23 support for MCF548x's FlexCAN
 | |
| + * 		Huan, Wang  <b18965@freescale.com>
 | |
| + * 	2005-02-03 created
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#include <linux/kernel.h>
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/interrupt.h>
 | |
| +#include <linux/platform_device.h>
 | |
| +#include <linux/netdevice.h>
 | |
| +#include <linux/can.h>
 | |
| +#include <linux/can/dev.h>
 | |
| +#include <linux/io.h>
 | |
| +
 | |
| +#include "flexcan.h"
 | |
| +#include <asm/coldfire.h>
 | |
| +#include <asm/m5485sim.h>
 | |
| +#include <linux/can/version.h>	/* for RCSID. Removed by mkpatch script */
 | |
| +
 | |
| +RCSID("$Id$");
 | |
| +
 | |
| +#define PDEV_MAX 2
 | |
| +
 | |
| +struct platform_device *pdev[PDEV_MAX];
 | |
| +
 | |
| +static int __devinit mcf548x_can_probe(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct resource *mem;
 | |
| +	struct net_device *dev;
 | |
| +	struct flexcan_platform_data *pdata = pdev->dev.platform_data;
 | |
| +	struct can_priv *can;
 | |
| +	u32 mem_size;
 | |
| +	int ret = -ENODEV;
 | |
| +
 | |
| +	if (!pdata)
 | |
| +		return ret;
 | |
| +
 | |
| +	dev = alloc_flexcandev();
 | |
| +	if (!dev)
 | |
| +		return -ENOMEM;
 | |
| +	can = netdev_priv(dev);
 | |
| +
 | |
| +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| +
 | |
| +	dev->irq = platform_get_irq(pdev, 0);
 | |
| +	if (!mem || !dev->irq)
 | |
| +		goto req_error;
 | |
| +
 | |
| +	mem_size = mem->end - mem->start + 1;
 | |
| +	if (!request_mem_region(mem->start, mem_size, pdev->dev.driver->name)) {
 | |
| +		dev_err(&pdev->dev, "resource unavailable\n");
 | |
| +		goto req_error;
 | |
| +	}
 | |
| +	SET_NETDEV_DEV(dev, &pdev->dev);
 | |
| +
 | |
| +	dev->base_addr = (unsigned long)ioremap_nocache(mem->start, mem_size);
 | |
| +	if (!dev->base_addr) {
 | |
| +		dev_err(&pdev->dev, "failed to map can port\n");
 | |
| +		ret = -ENOMEM;
 | |
| +		goto fail_map;
 | |
| +	}
 | |
| +	can->can_sys_clock = pdata->clock_frq;
 | |
| +	platform_set_drvdata(pdev, dev);
 | |
| +	ret = register_flexcandev(dev, pdata->clock_src);
 | |
| +	if (ret >= 0) {
 | |
| +		dev_info(&pdev->dev, "probe for port 0x%lX done\n",
 | |
| +			 dev->base_addr);
 | |
| +		return ret;
 | |
| +	}
 | |
| +
 | |
| +	iounmap((unsigned long *)dev->base_addr);
 | |
| +fail_map:
 | |
| +	release_mem_region(mem->start, mem_size);
 | |
| +req_error:
 | |
| +	free_candev(dev);
 | |
| +	dev_err(&pdev->dev, "probe failed\n");
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static int __devexit mcf548x_can_remove(struct platform_device *pdev)
 | |
| +{
 | |
| +	struct net_device *dev = platform_get_drvdata(pdev);
 | |
| +	struct resource *mem;
 | |
| +
 | |
| +	platform_set_drvdata(pdev, NULL);
 | |
| +	unregister_flexcandev(dev);
 | |
| +	iounmap((unsigned long *)dev->base_addr);
 | |
| +
 | |
| +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | |
| +	release_mem_region(mem->start, mem->end - mem->start + 1);
 | |
| +	free_candev(dev);
 | |
| +	return 0;
 | |
| +}
 | |
| +
 | |
| +static struct platform_driver mcf548x_can_driver = {
 | |
| +	.driver = {
 | |
| +		   .name = "mcf548x-flexcan",
 | |
| +		   },
 | |
| +	.probe = mcf548x_can_probe,
 | |
| +	.remove = __devexit_p(mcf548x_can_remove),
 | |
| +};
 | |
| +
 | |
| +static struct resource mcf548x_can0_resources[] = {
 | |
| +	[0] = {
 | |
| +		.start 		= MCF_MBAR + 0x0000A000,
 | |
| +		.end		= MCF_MBAR + 0x0000A7FF,
 | |
| +		.flags		= IORESOURCE_MEM,
 | |
| +	},
 | |
| +	[1] = {
 | |
| +		.start		= 49,
 | |
| +		.end		= 49,
 | |
| +		.flags		= IORESOURCE_IRQ,
 | |
| +	},
 | |
| +};
 | |
| +
 | |
| +static struct resource mcf548x_can1_resources[] = {
 | |
| +	[0] = {
 | |
| +		.start 		= MCF_MBAR + 0x0000A800,
 | |
| +		.end		= MCF_MBAR + 0x0000AFFF,
 | |
| +		.flags		= IORESOURCE_MEM,
 | |
| +	},
 | |
| +	[1] = {
 | |
| +		.start		= 55,
 | |
| +		.end		= 55,
 | |
| +		.flags		= IORESOURCE_IRQ,
 | |
| +	},
 | |
| +};
 | |
| +
 | |
| +
 | |
| +static int __init mcf548x_of_to_pdev(void)
 | |
| +{
 | |
| +	unsigned int i;
 | |
| +	int err = -ENODEV;
 | |
| +	struct flexcan_platform_data pdata;
 | |
| +
 | |
| +	pdev[0] = platform_device_register_simple("mcf548x-flexcan", 0,
 | |
| +			mcf548x_can0_resources, 2);
 | |
| +	if (IS_ERR(pdev[0])) {
 | |
| +		err = PTR_ERR(pdev[0]);
 | |
| +		return err;
 | |
| +	}
 | |
| +	pdev[1] = platform_device_register_simple("mcf548x-flexcan", 1,
 | |
| +			mcf548x_can1_resources, 2);
 | |
| +	if (IS_ERR(pdev[1])) {
 | |
| +		err = PTR_ERR(pdev[1]);
 | |
| +		return err;
 | |
| +	}
 | |
| +
 | |
| +	/* FlexCAN clock */
 | |
| +	pdata.clock_frq = 100000000;
 | |
| +
 | |
| +	for (i = 0; i < PDEV_MAX; i++) {
 | |
| +		err = platform_device_add_data(pdev[i], &pdata, sizeof(pdata));
 | |
| +		if (err)
 | |
| +			return err;
 | |
| +	}
 | |
| +	return err;
 | |
| +}
 | |
| +
 | |
| +int __init mcf548x_can_init(void)
 | |
| +{
 | |
| +	int err = mcf548x_of_to_pdev();
 | |
| +
 | |
| +	if (err) {
 | |
| +		printk(KERN_ERR "%s init failed with err=%d\n",
 | |
| +			mcf548x_can_driver.driver.name, err);
 | |
| +		return err;
 | |
| +	}
 | |
| +
 | |
| +	return platform_driver_register(&mcf548x_can_driver);
 | |
| +}
 | |
| +
 | |
| +void __exit mcf548x_can_exit(void)
 | |
| +{
 | |
| +	int i;
 | |
| +	platform_driver_unregister(&mcf548x_can_driver);
 | |
| +	for (i = 0; i < PDEV_MAX; i++)
 | |
| +		platform_device_unregister(pdev[i]);
 | |
| +}
 | |
| +
 | |
| +module_init(mcf548x_can_init);
 | |
| +module_exit(mcf548x_can_exit);
 | |
| +
 | |
| +MODULE_AUTHOR("Andrey Volkov <avolkov@varma-el.com>");
 | |
| +MODULE_DESCRIPTION("Freescale MCF548x CAN driver");
 | |
| +MODULE_LICENSE("GPL v2");
 | |
| --- a/include/asm-m68k/m5485sim.h
 | |
| +++ b/include/asm-m68k/m5485sim.h
 | |
| @@ -186,6 +186,8 @@
 | |
|  #define MCF_PAR_PCIBR   	   MCF_REG16(0x000A4A)
 | |
|  #define MCF_PAR_PSCn(x)            MCF_REG08(0x000A4F-((x)&0x3))
 | |
|  #define MCF_PAR_FECI2CIRQ   	   MCF_REG16(0x000A44)
 | |
| +#define MCF_PAR_DSPI               MCF_REG16(0x000A50)
 | |
| +#define MCF_PAR_TIMER              MCF_REG08(0X000A52)
 | |
|  #define MCF_EPPAR       	   MCF_REG16(0x000F00)
 | |
|  #define MCF_EPIER       	   MCF_REG08(0x000F05)
 | |
|  #define MCF_EPFR       	   	   MCF_REG08(0x000F0C)
 | |
| --- /dev/null
 | |
| +++ b/include/linux/can/dev.h
 | |
| @@ -0,0 +1,62 @@
 | |
| +/*
 | |
| + * linux/can/dev.h
 | |
| + *
 | |
| + * Definitions for CAN controller network devices lib (work in progress)
 | |
| + *
 | |
| + * * * $Id$
 | |
| + *
 | |
| + * Author: Andrey Volkov <avolkov@varma-el.com>
 | |
| + * Copyright (c) 2006 Varma Electronics Oy
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#ifndef CAN_DEVICE_H
 | |
| +#define CAN_DEVICE_H
 | |
| +
 | |
| +#include <linux/version.h>
 | |
| +#include <linux/can/error.h>
 | |
| +#include <linux/can/ioctl.h>
 | |
| +
 | |
| +struct can_priv {
 | |
| +	struct can_device_stats can_stats;
 | |
| +
 | |
| +	/* can-bus oscillator frequency, in Hz,
 | |
| +	   BE CAREFUL! SOME CONTROLLERS (LIKE SJA1000)
 | |
| +	   FOOLISH ABOUT THIS FRQ (for sja1000 as ex. this
 | |
| +	   clock must be xtal clock divided by 2). */
 | |
| +	u32	can_sys_clock;
 | |
| +
 | |
| +	/* by default max_brp is equal 64,
 | |
| +	   but for a Freescale TouCAN, as ex., it can be 255*/
 | |
| +	u32	max_brp;
 | |
| +	/* For the mostly all controllers, max_sjw is equal 4, but
 | |
| +	   some, hmm, CAN implementations hardwared it to 1 */
 | |
| +	u8	max_sjw;
 | |
| +
 | |
| +	u32	baudrate;	/* in bauds */
 | |
| +	struct can_bittime	bit_time;
 | |
| +
 | |
| +	spinlock_t irq_lock;
 | |
| +	/* Please hold this lock when touching net_stats/can_stats*/
 | |
| +	spinlock_t stats_lock;
 | |
| +
 | |
| +	can_state_t state;
 | |
| +	can_mode_t  mode;
 | |
| +	can_ctrlmode_t ctrlmode;
 | |
| +
 | |
| +	int (*do_set_bit_time)(struct net_device *dev, struct can_bittime *br);
 | |
| +	int (*do_get_state)   (struct net_device *dev, can_state_t *state);
 | |
| +	int (*do_set_mode)    (struct net_device *dev, can_mode_t mode);
 | |
| +	int (*do_set_ctrlmode)(struct net_device *dev, can_ctrlmode_t ctrlmode);
 | |
| +	int (*do_get_ctrlmode)(struct net_device *dev, can_ctrlmode_t *ctrlmode);
 | |
| +};
 | |
| +
 | |
| +#define ND2D(_ndev)		(_ndev->dev.parent)
 | |
| +
 | |
| +struct net_device *alloc_candev(int sizeof_priv);
 | |
| +void free_candev(struct net_device *dev);
 | |
| +
 | |
| +int can_calc_bit_time(struct can_priv *can, u32 baudrate,
 | |
| +		      struct can_bittime_std *bit_time);
 | |
| +
 | |
| +#endif /* CAN_DEVICE_H */
 | |
| --- /dev/null
 | |
| +++ b/include/linux/can/ioctl.h
 | |
| @@ -0,0 +1,152 @@
 | |
| +/*
 | |
| + * linux/can/ioctl.h
 | |
| + *
 | |
| + * Definitions for CAN controller setup (work in progress)
 | |
| + *
 | |
| + * $Id$
 | |
| + *
 | |
| + * Send feedback to <socketcan-users@lists.berlios.de>
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#ifndef CAN_IOCTL_H
 | |
| +#define CAN_IOCTL_H
 | |
| +
 | |
| +#include <linux/sockios.h>
 | |
| +
 | |
| +
 | |
| +/* max. 16 private ioctls */
 | |
| +
 | |
| +#define SIOCSCANBAUDRATE	(SIOCDEVPRIVATE+0)
 | |
| +#define SIOCGCANBAUDRATE	(SIOCDEVPRIVATE+1)
 | |
| +
 | |
| +#define SIOCSCANCUSTOMBITTIME   (SIOCDEVPRIVATE+2)
 | |
| +#define SIOCGCANCUSTOMBITTIME   (SIOCDEVPRIVATE+3)
 | |
| +
 | |
| +#define SIOCSCANMODE		(SIOCDEVPRIVATE+4)
 | |
| +#define SIOCGCANMODE		(SIOCDEVPRIVATE+5)
 | |
| +
 | |
| +#define SIOCSCANCTRLMODE	(SIOCDEVPRIVATE+6)
 | |
| +#define SIOCGCANCTRLMODE	(SIOCDEVPRIVATE+7)
 | |
| +
 | |
| +#define SIOCSCANFILTER		(SIOCDEVPRIVATE+8)
 | |
| +#define SIOCGCANFILTER		(SIOCDEVPRIVATE+9)
 | |
| +
 | |
| +#define SIOCGCANSTATE		(SIOCDEVPRIVATE+10)
 | |
| +#define SIOCGCANSTATS		(SIOCDEVPRIVATE+11)
 | |
| +
 | |
| +#define SIOCSCANERRORCONFIG	(SIOCDEVPRIVATE+12)
 | |
| +#define SIOCGCANERRORCONFIG	(SIOCDEVPRIVATE+13)
 | |
| +
 | |
| +/* parameters for ioctls */
 | |
| +
 | |
| +/* SIOC[SG]CANBAUDRATE */
 | |
| +/* baudrate for CAN-controller in bits per second. */
 | |
| +/* 0 = Scan for baudrate (Autobaud) */
 | |
| +
 | |
| +typedef __u32 can_baudrate_t;
 | |
| +
 | |
| +
 | |
| +/* SIOC[SG]CANCUSTOMBITTIME */
 | |
| +
 | |
| +typedef enum CAN_BITTIME_TYPE {
 | |
| +	CAN_BITTIME_STD,
 | |
| +	CAN_BITTIME_BTR
 | |
| +} can_bittime_type_t;
 | |
| +
 | |
| +/* TSEG1 of controllers usually is a sum of synch_seg (always 1),
 | |
| + * prop_seg and phase_seg1, TSEG2 = phase_seg2 */
 | |
| +
 | |
| +struct can_bittime_std {
 | |
| +	__u32 brp;        /* baud rate prescaler */
 | |
| +	__u8  prop_seg;   /* from 1 to 8 */
 | |
| +	__u8  phase_seg1; /* from 1 to 8 */
 | |
| +	__u8  phase_seg2; /* from 1 to 8 */
 | |
| +	__u8  sjw:7;      /* from 1 to 4 */
 | |
| +	__u8  sam:1;      /* 1 - enable triple sampling */
 | |
| +};
 | |
| +
 | |
| +struct can_bittime_btr {
 | |
| +	__u8  btr0;
 | |
| +	__u8  btr1;
 | |
| +};
 | |
| +
 | |
| +struct can_bittime {
 | |
| +	can_bittime_type_t type;
 | |
| +	union {
 | |
| +		struct can_bittime_std std;
 | |
| +		struct can_bittime_btr btr;
 | |
| +	};
 | |
| +};
 | |
| +
 | |
| +#define CAN_BAUDRATE_UNCONFIGURED	((__u32) 0xFFFFFFFFU)
 | |
| +#define CAN_BAUDRATE_UNKNOWN		0
 | |
| +
 | |
| +/* SIOC[SG]CANMODE */
 | |
| +
 | |
| +typedef __u32 can_mode_t;
 | |
| +
 | |
| +#define CAN_MODE_STOP	0
 | |
| +#define CAN_MODE_START	1
 | |
| +#define CAN_MODE_SLEEP	2
 | |
| +
 | |
| +
 | |
| +/* SIOC[SG]CANCTRLMODE */
 | |
| +
 | |
| +typedef __u32 can_ctrlmode_t;
 | |
| +
 | |
| +#define CAN_CTRLMODE_LOOPBACK   0x1
 | |
| +#define CAN_CTRLMODE_LISTENONLY 0x2
 | |
| +
 | |
| +
 | |
| +/* SIOCGCANFILTER */
 | |
| +
 | |
| +typedef __u32 can_filter_t;
 | |
| +
 | |
| +/* filter modes (may vary due to controller specific capabilities) */
 | |
| +#define CAN_FILTER_CAPAB       0  /* get filter type capabilities
 | |
| +				     (32 Bit value) */
 | |
| +#define CAN_FILTER_MASK_VALUE  1  /* easy bit filter (see struct can_filter) */
 | |
| +#define CAN_FILTER_SFF_BITMASK 2  /* bitfield with 2048 bit SFF filter */
 | |
| +				  /* filters 3 - 31 currently undefined */
 | |
| +
 | |
| +#define CAN_FILTER_MAX         31 /* max. filter type value */
 | |
| +
 | |
| +
 | |
| +/* SIOCGCANSTATE */
 | |
| +
 | |
| +typedef __u32 can_state_t;
 | |
| +
 | |
| +#define CAN_STATE_ACTIVE		0
 | |
| +#define CAN_STATE_BUS_WARNING		1
 | |
| +#define CAN_STATE_BUS_PASSIVE		2
 | |
| +#define CAN_STATE_BUS_OFF		3
 | |
| +#define CAN_STATE_SCANNING_BAUDRATE	4
 | |
| +#define CAN_STATE_STOPPED		5
 | |
| +#define CAN_STATE_SLEEPING		6
 | |
| +
 | |
| +
 | |
| +/* SIOCGCANSTATS */
 | |
| +
 | |
| +struct can_device_stats {
 | |
| +	int error_warning;
 | |
| +	int data_overrun;
 | |
| +	int wakeup;
 | |
| +	int bus_error;
 | |
| +	int error_passive;
 | |
| +	int arbitration_lost;
 | |
| +	int restarts;
 | |
| +	int bus_error_at_init;
 | |
| +};
 | |
| +
 | |
| +/* SIOC[SG]CANERRORCONFIG */
 | |
| +
 | |
| +typedef enum CAN_ERRCFG_TYPE {
 | |
| +	CAN_ERRCFG_MASK,
 | |
| +	CAN_ERRCFG_BUSERR,
 | |
| +	CAN_ERRCFG_BUSOFF
 | |
| +} can_errcfg_type_t;
 | |
| +
 | |
| +/* tbd */
 | |
| +
 | |
| +#endif /* CAN_IOCTL_H */
 | |
| --- /dev/null
 | |
| +++ b/include/linux/can/version.h
 | |
| @@ -0,0 +1,22 @@
 | |
| +/*
 | |
| + * linux/can/version.h
 | |
| + *
 | |
| + * Version information for the CAN network layer implementation
 | |
| +
 | |
| + * Author: Urs Thuermann   <urs.thuermann@volkswagen.de>
 | |
| + * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
 | |
| + * All rights reserved.
 | |
| + *
 | |
| + * Send feedback to <socketcan-users@lists.berlios.de>
 | |
| + *
 | |
| + */
 | |
| +
 | |
| +#ifndef CAN_VERSION_H
 | |
| +#define CAN_VERSION_H
 | |
| +
 | |
| +#define RCSID(s) asm(".section .rodata.str1.1,\"aMS\",@progbits,1\n\t" \
 | |
| +		     ".string \"" s "\"\n\t.previous\n")
 | |
| +
 | |
| +RCSID("$Id$");
 | |
| +
 | |
| +#endif /* CAN_VERSION_H */
 | |
| --- a/net/can/Makefile
 | |
| +++ b/net/can/Makefile
 | |
| @@ -10,3 +10,6 @@ can-raw-objs		:= raw.o
 | |
|  
 | |
|  obj-$(CONFIG_CAN_BCM)	+= can-bcm.o
 | |
|  can-bcm-objs		:= bcm.o
 | |
| +
 | |
| +obj-$(CONFIG_CAN)       += candev.o
 | |
| +candev-objs             := dev.o
 | |
| --- /dev/null
 | |
| +++ b/net/can/dev.c
 | |
| @@ -0,0 +1,292 @@
 | |
| +/*
 | |
| + * $Id$
 | |
| + *
 | |
| + * Copyright (C) 2005 Marc Kleine-Budde, Pengutronix
 | |
| + * Copyright (C) 2006 Andrey Volkov, Varma Electronics
 | |
| + *
 | |
| + * This program is free software; you can redistribute it and/or modify
 | |
| + * it under the terms of the version 2 of the GNU General Public License
 | |
| + * as published by the Free Software Foundation
 | |
| + *
 | |
| + * This program is distributed in the hope that it will be useful,
 | |
| + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
| + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 | |
| + * GNU General Public License for more details.
 | |
| + *
 | |
| + * You should have received a copy of the GNU General Public License
 | |
| + * along with this program; if not, write to the Free Software
 | |
| + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 | |
| + */
 | |
| +
 | |
| +#include <linux/module.h>
 | |
| +#include <linux/netdevice.h>
 | |
| +#include <linux/if_arp.h>
 | |
| +#include <linux/can.h>
 | |
| +#include <linux/can/dev.h>
 | |
| +
 | |
| +MODULE_DESCRIPTION("CAN netdevice library");
 | |
| +MODULE_LICENSE("GPL v2");
 | |
| +MODULE_AUTHOR("Marc Kleine-Budde <mkl@pengutronix.de>, "
 | |
| +	      "Andrey Volkov <avolkov@varma-el.com>");
 | |
| +
 | |
| +/*
 | |
| + Abstract:
 | |
| +	Baud rate calculated with next formula:
 | |
| +	baud = frq/(brp*(1 + prop_seg+ phase_seg1 + phase_seg2))
 | |
| +
 | |
| +	This calc function based on work of Florian Hartwich and Armin Bassemi
 | |
| +	"The Configuration of the CAN Bit Timing"
 | |
| +	(http://www.semiconductors.bosch.de/pdf/CiA99Paper.pdf)
 | |
| +
 | |
| + Parameters:
 | |
| +  [in]
 | |
| +    bit_time_nsec - expected bit time in nanosecs
 | |
| +
 | |
| +  [out]
 | |
| +	bit_time	- calculated time segments, for meaning of
 | |
| +			  each field read CAN standard.
 | |
| +*/
 | |
| +
 | |
| +#define DEFAULT_MAX_BRP			64U
 | |
| +#define DEFAULT_MAX_SJW			4U
 | |
| +
 | |
| +/* All below values in tq units */
 | |
| +#define MAX_BIT_TIME	25U
 | |
| +#define MIN_BIT_TIME	8U
 | |
| +#define MAX_PROP_SEG	8U
 | |
| +#define MAX_PHASE_SEG1	8U
 | |
| +#define MAX_PHASE_SEG2	8U
 | |
| +
 | |
| +int can_calc_bit_time(struct can_priv *can, u32 baudrate,
 | |
| +		      struct can_bittime_std *bit_time)
 | |
| +{
 | |
| +	int best_error  = -1; /* Ariphmetic error */
 | |
| +	int df, best_df = -1; /* oscillator's tolerance range */
 | |
| +	u32 quanta;	      /*in tq units*/
 | |
| +	u32 brp, phase_seg1, phase_seg2, sjw, prop_seg;
 | |
| +	u32 brp_min, brp_max, brp_expected;
 | |
| +	u64 tmp;
 | |
| +
 | |
| +	/* baudrate range [1baud,1Mbaud] */
 | |
| +	if (baudrate == 0 || baudrate > 1000000UL)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	tmp = (u64)can->can_sys_clock*1000;
 | |
| +	do_div(tmp, baudrate);
 | |
| +	brp_expected = (u32)tmp;
 | |
| +
 | |
| +	brp_min = brp_expected / (1000 * MAX_BIT_TIME);
 | |
| +	if (brp_min == 0)
 | |
| +		brp_min = 1;
 | |
| +	if (brp_min > can->max_brp)
 | |
| +		return -ERANGE;
 | |
| +
 | |
| +	brp_max = (brp_expected + 500 * MIN_BIT_TIME) / (1000 * MIN_BIT_TIME);
 | |
| +	if (brp_max == 0)
 | |
| +		brp_max = 1;
 | |
| +	if (brp_max > can->max_brp)
 | |
| +		brp_max = can->max_brp;
 | |
| +
 | |
| +	for (brp = brp_min; brp <= brp_max; brp++) {
 | |
| +		quanta = brp_expected / (brp * 1000);
 | |
| +		if (quanta < MAX_BIT_TIME && quanta * brp * 1000 !=
 | |
| +				brp_expected)
 | |
| +			quanta++;
 | |
| +		if (quanta < MIN_BIT_TIME || quanta > MAX_BIT_TIME)
 | |
| +			continue;
 | |
| +
 | |
| +		phase_seg2 = min((quanta - 3) / 2, MAX_PHASE_SEG2);
 | |
| +		for (sjw = can->max_sjw; sjw > 0; sjw--) {
 | |
| +			for (; phase_seg2 > sjw; phase_seg2--) {
 | |
| +				u32 err1, err2;
 | |
| +				phase_seg1 = phase_seg2 % 2 ?
 | |
| +					phase_seg2-1 : phase_seg2;
 | |
| +				prop_seg = quanta-1 - phase_seg2 - phase_seg1;
 | |
| +				/*
 | |
| +				 * FIXME: support of longer lines
 | |
| +				 * (i.e. bigger prop_seg) is more prefered
 | |
| +				 * than support of cheap oscillators
 | |
| +				 * (i.e. bigger df/phase_seg1/phase_seg2)
 | |
| +				 * */
 | |
| +
 | |
| +				if (prop_seg < phase_seg1)
 | |
| +						continue;
 | |
| +				if (prop_seg > MAX_PROP_SEG)
 | |
| +						goto next_brp;
 | |
| +
 | |
| +				err1 = phase_seg1 * brp * 500 * 1000 /
 | |
| +					(13 * brp_expected - phase_seg2 *
 | |
| +					 brp * 1000);
 | |
| +				err2 = sjw * brp * 50 * 1000 / brp_expected;
 | |
| +
 | |
| +				df = min(err1, err2);
 | |
| +				if (df >= best_df) {
 | |
| +					unsigned error = abs(brp_expected * 10 /
 | |
| +							(brp * (1 + prop_seg +
 | |
| +								phase_seg1 +
 | |
| +								phase_seg2)) - 10000);
 | |
| +
 | |
| +					if (error > 10 || error > best_error)
 | |
| +						continue;
 | |
| +
 | |
| +					if (error == best_error && prop_seg <
 | |
| +							bit_time->prop_seg)
 | |
| +						continue;
 | |
| +
 | |
| +					best_error = error;
 | |
| +					best_df = df;
 | |
| +					bit_time->brp = brp;
 | |
| +					bit_time->prop_seg = prop_seg;
 | |
| +					bit_time->phase_seg1 = phase_seg1;
 | |
| +					bit_time->phase_seg2 = phase_seg2;
 | |
| +					bit_time->sjw = sjw;
 | |
| +					bit_time->sam =
 | |
| +						(bit_time->phase_seg1 > 3);
 | |
| +				}
 | |
| +			}
 | |
| +		}
 | |
| +next_brp:	;
 | |
| +	}
 | |
| +
 | |
| +	if (best_error < 0)
 | |
| +		return -EDOM;
 | |
| +	return 0;
 | |
| +}
 | |
| +EXPORT_SYMBOL(can_calc_bit_time);
 | |
| +
 | |
| +static int can_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 | |
| +{
 | |
| +	struct can_priv *can = netdev_priv(dev);
 | |
| +	struct can_bittime *bt = (struct can_bittime *)&ifr->ifr_ifru;
 | |
| +	ulong *baudrate = (ulong *)&ifr->ifr_ifru;
 | |
| +	int ret = -EOPNOTSUPP;
 | |
| +
 | |
| +	dev_dbg(ND2D(dev), "(%s) 0x%08x %p\n", __func__, cmd, &ifr->ifr_ifru);
 | |
| +
 | |
| +	switch (cmd) {
 | |
| +	case SIOCSCANBAUDRATE:
 | |
| +		if (can->do_set_bit_time) {
 | |
| +			struct can_bittime bit_time;
 | |
| +			ret = can_calc_bit_time(can, *baudrate, &bit_time.std);
 | |
| +			if (ret != 0)
 | |
| +				break;
 | |
| +			bit_time.type = CAN_BITTIME_STD;
 | |
| +			ret = can->do_set_bit_time(dev, &bit_time);
 | |
| +			if (!ret) {
 | |
| +				can->baudrate = *baudrate;
 | |
| +				can->bit_time = bit_time;
 | |
| +			}
 | |
| +		}
 | |
| +		break;
 | |
| +	case SIOCGCANBAUDRATE:
 | |
| +		*baudrate = can->baudrate;
 | |
| +		ret = 0;
 | |
| +		break;
 | |
| +	case SIOCSCANCUSTOMBITTIME:
 | |
| +		if (can->do_set_bit_time) {
 | |
| +			ret = can->do_set_bit_time(dev, bt);
 | |
| +			if (!ret) {
 | |
| +				can->bit_time = *bt;
 | |
| +				if (bt->type == CAN_BITTIME_STD && bt->std.brp) {
 | |
| +					can->baudrate = can->can_sys_clock /
 | |
| +						(bt->std.brp * (1 + bt->std.prop_seg +
 | |
| +								bt->std.phase_seg1 +
 | |
| +								bt->std.phase_seg2));
 | |
| +				} else
 | |
| +					can->baudrate = CAN_BAUDRATE_UNKNOWN;
 | |
| +			}
 | |
| +		}
 | |
| +		break;
 | |
| +	case SIOCGCANCUSTOMBITTIME:
 | |
| +		*bt = can->bit_time;
 | |
| +		ret = 0;
 | |
| +		break;
 | |
| +	case SIOCSCANMODE:
 | |
| +		if (can->do_set_mode) {
 | |
| +			can_mode_t mode =
 | |
| +				*((can_mode_t *)(&ifr->ifr_ifru));
 | |
| +			if (mode == CAN_MODE_START &&
 | |
| +			    can->baudrate == CAN_BAUDRATE_UNCONFIGURED) {
 | |
| +				dev_info(ND2D(dev), "Impossible to start \
 | |
| +						on UNKNOWN speed\n");
 | |
| +				ret = EINVAL;
 | |
| +			} else
 | |
| +				return can->do_set_mode(dev, mode);
 | |
| +		}
 | |
| +		break;
 | |
| +	case SIOCGCANMODE:
 | |
| +		*((can_mode_t *)(&ifr->ifr_ifru)) = can->mode;
 | |
| +		ret = 0;
 | |
| +		break;
 | |
| +	case SIOCSCANCTRLMODE:
 | |
| +		if (can->do_set_ctrlmode) {
 | |
| +			can_ctrlmode_t ctrlmode =
 | |
| +				*((can_ctrlmode_t *)(&ifr->ifr_ifru));
 | |
| +			return can->do_set_ctrlmode(dev, ctrlmode);
 | |
| +		}
 | |
| +		break;
 | |
| +	case SIOCGCANCTRLMODE:
 | |
| +		*((can_ctrlmode_t *)(&ifr->ifr_ifru)) = can->ctrlmode;
 | |
| +		ret = 0;
 | |
| +		break;
 | |
| +	case SIOCSCANFILTER:
 | |
| +		break;
 | |
| +	case SIOCGCANFILTER:
 | |
| +		break;
 | |
| +	case SIOCGCANSTATE:
 | |
| +		if (can->do_get_state)
 | |
| +			return can->do_get_state(dev,
 | |
| +					(can_state_t *)(&ifr->ifr_ifru));
 | |
| +		break;
 | |
| +	case SIOCGCANSTATS:
 | |
| +		*((struct can_device_stats *)(&ifr->ifr_ifru)) = can->can_stats;
 | |
| +		ret = 0;
 | |
| +		break;
 | |
| +	}
 | |
| +
 | |
| +	return ret;
 | |
| +}
 | |
| +
 | |
| +static void can_setup(struct net_device *dev)
 | |
| +{
 | |
| +	dev->type            = ARPHRD_CAN;
 | |
| +	dev->mtu             = sizeof(struct can_frame);
 | |
| +	dev->do_ioctl        = can_ioctl;
 | |
| +	dev->hard_header_len = 0;
 | |
| +	dev->addr_len        = 0;
 | |
| +	dev->tx_queue_len    = 10;
 | |
| +
 | |
| +	/* New-style flags. */
 | |
| +	dev->flags           = IFF_NOARP;
 | |
| +	dev->features        = NETIF_F_NO_CSUM;
 | |
| +}
 | |
| +
 | |
| +/*
 | |
| + * Function  alloc_candev
 | |
| + * 	Allocates and sets up an CAN device
 | |
| + */
 | |
| +struct net_device *alloc_candev(int sizeof_priv)
 | |
| +{
 | |
| +	struct net_device *dev;
 | |
| +	struct can_priv *priv;
 | |
| +
 | |
| +	dev = alloc_netdev(sizeof_priv, "can%d", can_setup);
 | |
| +	if (!dev)
 | |
| +		return NULL;
 | |
| +
 | |
| +	priv = netdev_priv(dev);
 | |
| +
 | |
| +	priv->baudrate = CAN_BAUDRATE_UNCONFIGURED;
 | |
| +	priv->max_brp  = DEFAULT_MAX_BRP;
 | |
| +	priv->max_sjw  = DEFAULT_MAX_SJW;
 | |
| +	spin_lock_init(&priv->irq_lock);
 | |
| +
 | |
| +	return dev;
 | |
| +}
 | |
| +EXPORT_SYMBOL(alloc_candev);
 | |
| +
 | |
| +void free_candev(struct net_device *dev)
 | |
| +{
 | |
| +	free_netdev(dev);
 | |
| +}
 | |
| +EXPORT_SYMBOL(free_candev);
 |