mirror of https://github.com/hak5/openwrt.git
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);
|