diff options
author | Imre Kaloz <kaloz@openwrt.org> | 2009-06-23 21:04:37 +0000 |
---|---|---|
committer | Imre Kaloz <kaloz@openwrt.org> | 2009-06-23 21:04:37 +0000 |
commit | c49f135f72f98633653300bab5b0ac993b03c6be (patch) | |
tree | 50929b27bcea9ab4dc74b041d6e90967473301fa /target/linux/coldfire/patches/077-mcfv4e_flexcan.patch | |
parent | 50f2abfa16a2b509955312d1776beeece662c61b (diff) | |
download | upstream-c49f135f72f98633653300bab5b0ac993b03c6be.tar.gz upstream-c49f135f72f98633653300bab5b0ac993b03c6be.tar.bz2 upstream-c49f135f72f98633653300bab5b0ac993b03c6be.zip |
use broken-out patches for the coldfire to make it easier to follow differences against the bsp
SVN-Revision: 16547
Diffstat (limited to 'target/linux/coldfire/patches/077-mcfv4e_flexcan.patch')
-rw-r--r-- | target/linux/coldfire/patches/077-mcfv4e_flexcan.patch | 1376 |
1 files changed, 1376 insertions, 0 deletions
diff --git a/target/linux/coldfire/patches/077-mcfv4e_flexcan.patch b/target/linux/coldfire/patches/077-mcfv4e_flexcan.patch new file mode 100644 index 0000000000..ae7aefd649 --- /dev/null +++ b/target/linux/coldfire/patches/077-mcfv4e_flexcan.patch @@ -0,0 +1,1376 @@ +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); |