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);