summaryrefslogtreecommitdiff
path: root/drivers/net/can/old/sja1000/sja1000.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/can/old/sja1000/sja1000.c')
-rw-r--r--drivers/net/can/old/sja1000/sja1000.c1140
1 files changed, 1140 insertions, 0 deletions
diff --git a/drivers/net/can/old/sja1000/sja1000.c b/drivers/net/can/old/sja1000/sja1000.c
new file mode 100644
index 000000000000..8d7185a2962b
--- /dev/null
+++ b/drivers/net/can/old/sja1000/sja1000.c
@@ -0,0 +1,1140 @@
+/*
+ * sja1000.c - Philips SJA1000 network device driver
+ *
+ * Copyright (c) 2003 Matthias Brukner, Trajet Gmbh, Rebenring 33,
+ * 38106 Braunschweig, GERMANY
+ *
+ * Copyright (c) 2002-2007 Volkswagen Group Electronic Research
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Volkswagen nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * Alternatively, provided that this notice is retained in full, this
+ * software may be distributed under the terms of the GNU General
+ * Public License ("GPL") version 2, in which case the provisions of the
+ * GPL apply INSTEAD OF those given above.
+ *
+ * The provided data structures and external interfaces from this code
+ * are not restricted to be used by modules with a GPL compatible license.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Send feedback to <socketcan-users@lists.berlios.de>
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+#include <linux/can.h>
+#include <linux/can/ioctl.h> /* for struct can_device_stats */
+#include "sja1000.h"
+#include "hal.h"
+
+MODULE_AUTHOR("Oliver Hartkopp <oliver.hartkopp@volkswagen.de>");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("LLCF/socketcan '" CHIP_NAME "' network device driver");
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+#define DBG(args...) ((priv->debug > 0) ? printk(args) : 0)
+/* logging in interrupt context! */
+#define iDBG(args...) ((priv->debug > 1) ? printk(args) : 0)
+#define iiDBG(args...) ((priv->debug > 2) ? printk(args) : 0)
+#else
+#define DBG(args...)
+#define iDBG(args...)
+#define iiDBG(args...)
+#endif
+
+char drv_name[DRV_NAME_LEN] = "undefined";
+
+/* driver and version information */
+static const char *drv_version = "0.1.1";
+static const char *drv_reldate = "2007-04-13";
+
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+static const char *ecc_errors[] = {
+ NULL,
+ NULL,
+ "ID.28 to ID.28",
+ "start of frame",
+ "bit SRTR",
+ "bit IDE",
+ "ID.20 to ID.18",
+ "ID.17 to ID.13",
+ "CRC sequence",
+ "reserved bit 0",
+ "data field",
+ "data length code",
+ "bit RTR",
+ "reserved bit 1",
+ "ID.4 to ID.0",
+ "ID.12 to ID.5",
+ NULL,
+ "active error flag",
+ "intermission",
+ "tolerate dominant bits",
+ NULL,
+ NULL,
+ "passive error flag",
+ "error delimiter",
+ "CRC delimiter",
+ "acknowledge slot",
+ "end of frame",
+ "acknowledge delimiter",
+ "overload flag",
+ NULL,
+ NULL,
+ NULL
+};
+
+static const char *ecc_types[] = {
+ "bit error",
+ "form error",
+ "stuff error",
+ "other type of error"
+};
+#endif
+
+/* array of all can chips */
+struct net_device *can_dev[MAXDEV];
+
+/* module parameters */
+unsigned long base[MAXDEV] = { 0 }; /* hardware address */
+unsigned long rbase[MAXDEV] = { 0 }; /* (remapped) device address */
+unsigned int irq[MAXDEV] = { 0 };
+
+unsigned int speed[MAXDEV] = { DEFAULT_SPEED, DEFAULT_SPEED };
+unsigned int btr[MAXDEV] = { 0 };
+
+static int rx_probe[MAXDEV] = { 0 };
+static int clk = DEFAULT_HW_CLK;
+static int debug = 0;
+static int restart_ms = 100;
+static int echo = 1;
+
+static int base_n;
+static int irq_n;
+static int speed_n;
+static int btr_n;
+static int rx_probe_n;
+
+module_param_array(base, int, &base_n, 0);
+module_param_array(irq, int, &irq_n, 0);
+module_param_array(speed, int, &speed_n, 0);
+module_param_array(btr, int, &btr_n, 0);
+module_param_array(rx_probe, int, &rx_probe_n, 0);
+
+module_param(clk, int, 0);
+module_param(debug, int, 0);
+module_param(restart_ms, int, 0);
+module_param(echo, int, S_IRUGO);
+
+MODULE_PARM_DESC(base, "CAN controller base address");
+MODULE_PARM_DESC(irq, "CAN controller interrupt");
+MODULE_PARM_DESC(speed, "CAN bus bitrate");
+MODULE_PARM_DESC(btr, "Bit Timing Register value 0x<btr0><btr1>, e.g. 0x4014");
+MODULE_PARM_DESC(rx_probe, "switch to trx mode after correct msg receiption. (default off)");
+
+MODULE_PARM_DESC(clk, "CAN controller chip clock (default: 16MHz)");
+MODULE_PARM_DESC(debug, "set debug mask (default: 0)");
+MODULE_PARM_DESC(restart_ms, "restart chip on heavy bus errors / bus off after x ms (default 100ms)");
+MODULE_PARM_DESC(echo, "Echo sent frames. default: 1 (On)");
+
+/*
+ * CAN network devices *should* support a local echo functionality
+ * (see Documentation/networking/can.txt). To test the handling of CAN
+ * interfaces that do not support the local echo both driver types are
+ * implemented inside this sja1000 driver. In the case that the driver does
+ * not support the echo the IFF_ECHO remains clear in dev->flags.
+ * This causes the PF_CAN core to perform the echo as a fallback solution.
+ */
+
+/* function declarations */
+
+static void can_restart_dev(unsigned long data);
+static void chipset_init(struct net_device *dev, int wake);
+static void chipset_init_rx(struct net_device *dev);
+static void chipset_init_trx(struct net_device *dev);
+static void can_netdev_setup(struct net_device *dev);
+static struct net_device* can_create_netdev(int dev_num, int hw_regs);
+static int can_set_drv_name(void);
+int set_reset_mode(struct net_device *dev);
+
+static int sja1000_probe_chip(unsigned long base)
+{
+ if (base && (hw_readreg(base, 0) == 0xFF)) {
+ printk(KERN_INFO "%s: probing @0x%lX failed\n",
+ drv_name, base);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * set baud rate divisor values
+ */
+static void set_btr(struct net_device *dev, int btr0, int btr1)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* no bla bla when restarting the device */
+ if (priv->state == STATE_UNINITIALIZED)
+ printk(KERN_INFO "%s: setting BTR0=%02X BTR1=%02X\n",
+ dev->name, btr0, btr1);
+
+ hw_writereg(dev->base_addr, REG_BTR0, btr0);
+ hw_writereg(dev->base_addr, REG_BTR1, btr1);
+}
+
+/*
+ * calculate baud rate divisor values
+ */
+static void set_baud(struct net_device *dev, int baud, int clock)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ int error;
+ int brp;
+ int tseg;
+ int tseg1 = 0;
+ int tseg2 = 0;
+
+ int best_error = 1000000000;
+ int best_tseg = 0;
+ int best_brp = 0;
+ int best_baud = 0;
+
+ int SAM = (baud > 100000 ? 0 : 1);
+
+ clock >>= 1;
+
+ for (tseg = (0 + 0 + 2) * 2;
+ tseg <= (MAX_TSEG2 + MAX_TSEG1 + 2) * 2 + 1;
+ tseg++) {
+ brp = clock / ((1 + tseg / 2) * baud) + tseg % 2;
+ if ((brp > 0) && (brp <= 64)) {
+ error = baud - clock / (brp * (1 + tseg / 2));
+ if (error < 0) {
+ error = -error;
+ }
+ if (error <= best_error) {
+ best_error = error;
+ best_tseg = tseg / 2;
+ best_brp = brp - 1;
+ best_baud = clock / (brp * (1 + tseg / 2));
+ }
+ }
+ }
+ if (best_error && (baud / best_error < 10)) {
+ printk("%s: unable to set baud rate %d (ext clock %dHz)\n",
+ dev->name, baud, clock * 2);
+ return;
+// return -EINVAL;
+ }
+ tseg2 = best_tseg - (SAMPLE_POINT * (best_tseg + 1)) / 100;
+ if (tseg2 < 0) {
+ tseg2 = 0;
+ } else if (tseg2 > MAX_TSEG2) {
+ tseg2 = MAX_TSEG2;
+ }
+ tseg1 = best_tseg - tseg2 - 2;
+ if (tseg1 > MAX_TSEG1) {
+ tseg1 = MAX_TSEG1;
+ tseg2 = best_tseg - tseg1 - 2;
+ }
+
+ priv->btr = ((best_brp | JUMPWIDTH)<<8) +
+ ((SAM << 7) | (tseg2 << 4) | tseg1);
+
+ printk(KERN_INFO "%s: calculated best baudrate: %d / btr is 0x%04X\n",
+ dev->name, best_baud, priv->btr);
+
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+// set_btr(dev, best_brp | JUMPWIDTH, (SAM << 7) | (tseg2 << 4) | tseg1);
+}
+
+int set_reset_mode(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ priv->can_stats.bus_error_at_init = priv->can_stats.bus_error;
+
+ /* disable interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_OFF);
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if (status & MOD_RM) {
+ if (i > 1) {
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+ priv->state = STATE_RESET_MODE;
+ return 0;
+ }
+
+ hw_writereg(dev->base_addr, REG_MOD, MOD_RM); /* reset chip */
+ status = hw_readreg(dev->base_addr, REG_MOD);
+
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into reset mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_normal_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set chip to normal mode */
+ hw_writereg(dev->base_addr, REG_MOD, 0x00);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into normal mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+static int set_listen_mode(struct net_device *dev)
+{
+ unsigned char status = hw_readreg(dev->base_addr, REG_MOD);
+ int i;
+
+ for (i = 0; i < 10; i++) {
+ /* check reset mode bit */
+ if ((status & MOD_RM) == 0) {
+#ifdef CONFIG_CAN_DEBUG_DEVICES
+ if (i > 1) {
+ struct can_priv *priv = netdev_priv(dev);
+ iDBG(KERN_INFO "%s: %s looped %d times\n",
+ dev->name, __FUNCTION__, i);
+ }
+#endif
+ return 0;
+ }
+
+ /* set listen only mode, clear reset */
+ hw_writereg(dev->base_addr, REG_MOD, MOD_LOM);
+ status = hw_readreg(dev->base_addr, REG_MOD);
+ }
+
+ printk(KERN_ERR "%s: setting sja1000 into listen mode failed!\n",
+ dev->name);
+ return 1;
+
+}
+
+/*
+ * initialize SJA1000 chip:
+ * - reset chip
+ * - set output mode
+ * - set baudrate
+ * - enable interrupts
+ * - start operating mode
+ */
+static void chipset_init_regs(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ unsigned long base = dev->base_addr;
+
+ /* go into Pelican mode, disable clkout, disable comparator */
+ hw_writereg(base, REG_CDR, 0xCF);
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+
+ /* set acceptance filter (accept all) */
+ hw_writereg(base, REG_ACCC0, 0x00);
+ hw_writereg(base, REG_ACCC1, 0x00);
+ hw_writereg(base, REG_ACCC2, 0x00);
+ hw_writereg(base, REG_ACCC3, 0x00);
+
+ hw_writereg(base, REG_ACCM0, 0xFF);
+ hw_writereg(base, REG_ACCM1, 0xFF);
+ hw_writereg(base, REG_ACCM2, 0xFF);
+ hw_writereg(base, REG_ACCM3, 0xFF);
+
+ /* set baudrate */
+ if (priv->btr) { /* no calculation when btr is provided */
+ set_btr(dev, (priv->btr>>8) & 0xFF, priv->btr & 0xFF);
+ } else {
+ if (priv->speed == 0) {
+ priv->speed = DEFAULT_SPEED;
+ }
+ set_baud(dev, priv->speed * 1000, priv->clock);
+ }
+
+ /* output control */
+ /* connected to external transceiver */
+ hw_writereg(base, REG_OCR, 0x1A);
+}
+
+static void chipset_init(struct net_device *dev, int wake)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->rx_probe)
+ chipset_init_rx(dev); /* wait for valid reception first */
+ else
+ chipset_init_trx(dev);
+
+ if ((wake) && netif_queue_stopped(dev)) {
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev);
+ }
+}
+
+static void chipset_init_rx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* automatic bit rate detection */
+ set_listen_mode(dev);
+
+ priv->state = STATE_PROBE;
+
+ /* enable receive and error interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_RI | IRQ_EI);
+}
+
+static void chipset_init_trx(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ iDBG(KERN_INFO "%s: %s()\n", dev->name, __FUNCTION__);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ /* set registers */
+ chipset_init_regs(dev);
+
+ /* leave reset mode */
+ set_normal_mode(dev);
+
+ priv->state = STATE_ACTIVE;
+
+ /* enable all interrupts */
+ hw_writereg(dev->base_addr, REG_IER, IRQ_ALL);
+}
+
+/*
+ * transmit a CAN message
+ * message layout in the sk_buff should be like this:
+ * xx xx xx xx ff ll 00 11 22 33 44 55 66 77
+ * [ can-id ] [flags] [len] [can data (up to 8 bytes]
+ */
+static int can_start_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ struct can_frame *cf = (struct can_frame*)skb->data;
+ unsigned long base = dev->base_addr;
+ uint8_t fi;
+ uint8_t dlc;
+ canid_t id;
+ uint8_t dreg;
+ int loop;
+ int i;
+
+ netif_stop_queue(dev);
+
+ fi = dlc = cf->can_dlc;
+ id = cf->can_id;
+
+ if (id & CAN_RTR_FLAG)
+ fi |= FI_RTR;
+
+ if (id & CAN_EFF_FLAG) {
+ fi |= FI_FF;
+ dreg = EFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x1fe00000) >> (5 + 16));
+ hw_writereg(base, REG_ID2, (id & 0x001fe000) >> (5 + 8));
+ hw_writereg(base, REG_ID3, (id & 0x00001fe0) >> 5);
+ hw_writereg(base, REG_ID4, (id & 0x0000001f) << 3);
+ } else {
+ dreg = SFF_BUF;
+ hw_writereg(base, REG_FI, fi);
+ hw_writereg(base, REG_ID1, (id & 0x000007f8) >> 3);
+ hw_writereg(base, REG_ID2, (id & 0x00000007) << 5);
+ }
+
+ for (i = 0; i < dlc; i++) {
+ hw_writereg(base, dreg++, cf->data[i]);
+ }
+
+ hw_writereg(base, REG_CMR, CMD_TR);
+
+ stats->tx_bytes += dlc;
+
+ dev->trans_start = jiffies;
+
+ /* set flag whether this packet has to be looped back */
+ loop = skb->pkt_type == PACKET_LOOPBACK;
+
+ if (!echo || !loop) {
+ kfree_skb(skb);
+ return 0;
+ }
+
+ if (!priv->echo_skb) {
+ struct sock *srcsk = skb->sk;
+
+ if (atomic_read(&skb->users) != 1) {
+ struct sk_buff *old_skb = skb;
+
+ skb = skb_clone(old_skb, GFP_ATOMIC);
+ DBG(KERN_INFO "%s: %s: freeing old skbuff %p, "
+ "using new skbuff %p\n",
+ dev->name, __FUNCTION__, old_skb, skb);
+ kfree_skb(old_skb);
+ if (!skb) {
+ return 0;
+ }
+ } else
+ skb_orphan(skb);
+
+ skb->sk = srcsk;
+
+ /* make settings for echo to reduce code in irq context */
+ skb->protocol = htons(ETH_P_CAN);
+ skb->pkt_type = PACKET_BROADCAST;
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ skb->dev = dev;
+
+ /* save this skb for tx interrupt echo handling */
+ priv->echo_skb = skb;
+
+ } else {
+ /* locking problem with netif_stop_queue() ?? */
+ printk(KERN_ERR "%s: %s: occupied echo_skb!\n",
+ dev->name, __FUNCTION__ );
+ kfree_skb(skb);
+ }
+
+ return 0;
+}
+
+static void can_tx_timeout(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+
+ stats->tx_errors++;
+
+ /* do not conflict with e.g. bus error handling */
+ if (!(priv->timer.expires)){ /* no restart on the run */
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ if (priv->echo_skb) { /* pending echo? */
+ kfree_skb(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+ netif_wake_queue(dev); /* wakeup here */
+ }
+ else
+ DBG(KERN_INFO "%s: %s: can_restart_dev already active.\n",
+ dev->name, __FUNCTION__ );
+
+}
+
+static void can_restart_on(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (!(priv->timer.expires)){ /* no restart on the run */
+
+ set_reset_mode(dev);
+
+ priv->timer.function = can_restart_dev;
+ priv->timer.data = (unsigned long) dev;
+
+ /* restart chip on persistent error in <xxx> ms */
+ priv->timer.expires = jiffies + (priv->restart_ms * HZ) / 1000;
+ add_timer(&priv->timer);
+
+ iDBG(KERN_INFO "%s: %s start (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+ } else
+ iDBG(KERN_INFO "%s: %s already (%ld)\n",
+ dev->name, __FUNCTION__ , jiffies);
+}
+
+static void can_restart_dev(unsigned long data)
+{
+ struct net_device *dev = (struct net_device*) data;
+ struct can_priv *priv = netdev_priv(dev);
+
+ DBG(KERN_INFO "%s: can_restart_dev (%ld)\n",
+ dev->name, jiffies);
+
+ /* mark inactive timer */
+ priv->timer.expires = 0;
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+
+#if 0
+/* the timerless version */
+
+static void can_restart_now(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ if (priv->state != STATE_UNINITIALIZED) {
+
+ /* count number of restarts */
+ priv->can_stats.restarts++;
+
+ chipset_init(dev, 1);
+ }
+}
+#endif
+
+static void can_rx(struct net_device *dev)
+{
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ struct can_frame *cf;
+ struct sk_buff *skb;
+ uint8_t fi;
+ uint8_t dreg;
+ canid_t id;
+ uint8_t dlc;
+ int i;
+
+ skb = dev_alloc_skb(sizeof(struct can_frame));
+ if (skb == NULL) {
+ return;
+ }
+ skb->dev = dev;
+ skb->protocol = htons(ETH_P_CAN);
+
+ fi = hw_readreg(base, REG_FI);
+ dlc = fi & 0x0F;
+
+ if (fi & FI_FF) {
+ /* extended frame format (EFF) */
+ dreg = EFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << (5+16))
+ | (hw_readreg(base, REG_ID2) << (5+8))
+ | (hw_readreg(base, REG_ID3) << 5)
+ | (hw_readreg(base, REG_ID4) >> 3);
+ id |= CAN_EFF_FLAG;
+ } else {
+ /* standard frame format (SFF) */
+ dreg = SFF_BUF;
+ id = (hw_readreg(base, REG_ID1) << 3)
+ | (hw_readreg(base, REG_ID2) >> 5);
+ }
+
+ if (fi & FI_RTR)
+ id |= CAN_RTR_FLAG;
+
+ cf = (struct can_frame*)skb_put(skb, sizeof(struct can_frame));
+ memset(cf, 0, sizeof(struct can_frame));
+ cf->can_id = id;
+ cf->can_dlc = dlc;
+ for (i = 0; i < dlc; i++) {
+ cf->data[i] = hw_readreg(base, dreg++);
+ }
+ while (i < 8)
+ cf->data[i++] = 0;
+
+ /* release receive buffer */
+ hw_writereg(base, REG_CMR, CMD_RRB);
+
+ netif_rx(skb);
+
+ dev->last_rx = jiffies;
+ stats->rx_packets++;
+ stats->rx_bytes += dlc;
+}
+
+/*
+ * SJA1000 interrupt handler
+ */
+static irqreturn_t can_interrupt(int irq, void *dev_id)
+{
+ struct net_device *dev = (struct net_device*)dev_id;
+ struct can_priv *priv = netdev_priv(dev);
+ struct net_device_stats *stats = &dev->stats;
+ unsigned long base = dev->base_addr;
+ uint8_t isrc, status, ecc, alc;
+ int n = 0;
+
+ hw_preirq(dev);
+
+ iiDBG(KERN_INFO "%s: interrupt\n", dev->name);
+
+ if (priv->state == STATE_UNINITIALIZED) {
+ printk(KERN_ERR "%s: %s: uninitialized controller!\n",
+ dev->name, __FUNCTION__);
+ chipset_init(dev, 1); /* should be possible at this stage */
+ return IRQ_NONE;
+ }
+
+ if (priv->state == STATE_RESET_MODE) {
+ iiDBG(KERN_ERR "%s: %s: controller is in reset mode! "
+ "MOD=0x%02X IER=0x%02X IR=0x%02X SR=0x%02X!\n",
+ dev->name, __FUNCTION__, hw_readreg(base, REG_MOD),
+ hw_readreg(base, REG_IER), hw_readreg(base, REG_IR),
+ hw_readreg(base, REG_SR));
+ return IRQ_NONE;
+ }
+
+ while ((isrc = hw_readreg(base, REG_IR)) && (n < 20)) {
+ n++;
+ status = hw_readreg(base, REG_SR);
+
+ if (isrc & IRQ_WUI) {
+ /* wake-up interrupt */
+ priv->can_stats.wakeup++;
+ }
+ if (isrc & IRQ_TI) {
+ /* transmission complete interrupt */
+ stats->tx_packets++;
+
+ if (echo && priv->echo_skb) {
+ netif_rx(priv->echo_skb);
+ priv->echo_skb = NULL;
+ }
+
+ netif_wake_queue(dev);
+ }
+ if (isrc & IRQ_RI) {
+ /* receive interrupt */
+
+ while (status & SR_RBS) {
+ can_rx(dev);
+ status = hw_readreg(base, REG_SR);
+ }
+ if (priv->state == STATE_PROBE) {
+ /* valid RX -> switch to trx-mode */
+ iDBG(KERN_INFO "%s: RI #%d#\n", dev->name, n);
+ chipset_init_trx(dev); /* no tx queue wakeup */
+ break; /* check again after init controller */
+ }
+ }
+ if (isrc & IRQ_DOI) {
+ /* data overrun interrupt */
+ iiDBG(KERN_INFO "%s: data overrun isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: DOI #%d#\n", dev->name, n);
+ priv->can_stats.data_overrun++;
+ hw_writereg(base, REG_CMR, CMD_CDO); /* clear bit */
+ }
+ if (isrc & IRQ_EI) {
+ /* error warning interrupt */
+ iiDBG(KERN_INFO "%s: error warning isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EI #%d#\n", dev->name, n);
+ priv->can_stats.error_warning++;
+ if (status & SR_BS) {
+ printk(KERN_INFO "%s: BUS OFF, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: error\n", dev->name);
+ }
+ }
+ if (isrc & IRQ_BEI) {
+ /* bus error interrupt */
+ iiDBG(KERN_INFO "%s: bus error isrc=0x%02X "
+ "status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: BEI #%d# [%d]\n", dev->name, n,
+ priv->can_stats.bus_error -
+ priv->can_stats.bus_error_at_init);
+ priv->can_stats.bus_error++;
+ ecc = hw_readreg(base, REG_ECC);
+ iDBG(KERN_INFO "%s: ECC = 0x%02X (%s, %s, %s)\n",
+ dev->name, ecc,
+ (ecc & ECC_DIR) ? "RX" : "TX",
+ ecc_types[ecc >> ECC_ERR],
+ ecc_errors[ecc & ECC_SEG]);
+
+ /* when the bus errors flood the system, */
+ /* restart the controller */
+ if (priv->can_stats.bus_error_at_init +
+ MAX_BUS_ERRORS < priv->can_stats.bus_error) {
+ iDBG(KERN_INFO "%s: heavy bus errors,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#if 1
+ /* don't know, if this is a good idea, */
+ /* but it works fine ... */
+ if (hw_readreg(base, REG_RXERR) > 128) {
+ iDBG(KERN_INFO "%s: RX_ERR > 128,"
+ " restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ }
+#endif
+ }
+ if (isrc & IRQ_EPI) {
+ /* error passive interrupt */
+ iiDBG(KERN_INFO "%s: error passive isrc=0x%02X"
+ " status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: EPI #%d#\n", dev->name, n);
+ priv->can_stats.error_passive++;
+ if (status & SR_ES) {
+ iDBG(KERN_INFO "%s: -> ERROR PASSIVE, "
+ "restarting device\n", dev->name);
+ can_restart_on(dev);
+ /* controller has been restarted: leave here */
+ goto out;
+ } else {
+ iDBG(KERN_INFO "%s: -> ERROR ACTIVE\n",
+ dev->name);
+ }
+ }
+ if (isrc & IRQ_ALI) {
+ /* arbitration lost interrupt */
+ iiDBG(KERN_INFO "%s: error arbitration lost "
+ "isrc=0x%02X status=0x%02X\n",
+ dev->name, isrc, status);
+ iDBG(KERN_INFO "%s: ALI #%d#\n", dev->name, n);
+ priv->can_stats.arbitration_lost++;
+ alc = hw_readreg(base, REG_ALC);
+ iDBG(KERN_INFO "%s: ALC = 0x%02X\n", dev->name, alc);
+ }
+ }
+ if (n > 1) {
+ iDBG(KERN_INFO "%s: handled %d IRQs\n", dev->name, n);
+ }
+out:
+ hw_postirq(dev);
+
+ return n == 0 ? IRQ_NONE : IRQ_HANDLED;
+}
+
+/*
+ * initialize CAN bus driver
+ */
+static int can_open(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->state = STATE_UNINITIALIZED;
+
+ /* register interrupt handler */
+ if (request_irq(dev->irq, &can_interrupt, IRQF_SHARED,
+ dev->name, (void*)dev)) {
+ return -EAGAIN;
+ }
+
+ /* init chip */
+ chipset_init(dev, 0);
+ priv->open_time = jiffies;
+
+ netif_start_queue(dev);
+
+ return 0;
+}
+
+/*
+ * stop CAN bus activity
+ */
+static int can_close(struct net_device *dev)
+{
+ struct can_priv *priv = netdev_priv(dev);
+
+ /* set chip into reset mode */
+ set_reset_mode(dev);
+
+ priv->open_time = 0;
+
+ if (priv->timer.expires) {
+ del_timer(&priv->timer);
+ priv->timer.expires = 0;
+ }
+
+ free_irq(dev->irq, (void*)dev);
+ priv->state = STATE_UNINITIALIZED;
+
+ netif_stop_queue(dev);
+
+ return 0;
+}
+
+#if 0
+static void test_if(struct net_device *dev)
+{
+ int i;
+ int j;
+ int x;
+
+ hw_writereg(base, REG_CDR, 0xCF);
+ for (i = 0; i < 10000; i++) {
+ for (j = 0; j < 256; j++) {
+ hw_writereg(base, REG_EWL, j);
+ x = hw_readreg(base, REG_EWL);
+ if (x != j) {
+ printk(KERN_INFO "%s: is: %02X expected: "
+ "%02X (%d)\n", dev->name, x, j, i);
+ }
+ }
+ }
+}
+#endif
+
+void can_netdev_setup(struct net_device *dev)
+{
+ /* Fill in the the fields of the device structure
+ with CAN netdev generic values */
+
+ dev->change_mtu = NULL;
+ dev->set_mac_address = NULL;
+ dev->header_ops = NULL;
+
+ dev->type = ARPHRD_CAN;
+ dev->hard_header_len = 0;
+ dev->mtu = sizeof(struct can_frame);
+ dev->addr_len = 0;
+ dev->tx_queue_len = 10;
+
+ dev->flags = IFF_NOARP;
+
+ /* set flags according to driver capabilities */
+ if (echo)
+ dev->flags |= IFF_ECHO;
+
+ dev->features = NETIF_F_NO_CSUM;
+
+ dev->open = can_open;
+ dev->stop = can_close;
+ dev->hard_start_xmit = can_start_xmit;
+
+ dev->tx_timeout = can_tx_timeout;
+ dev->watchdog_timeo = TX_TIMEOUT;
+}
+
+static struct net_device* can_create_netdev(int dev_num, int hw_regs)
+{
+ struct net_device *dev;
+ struct can_priv *priv;
+
+ if (!(dev = alloc_netdev(sizeof(struct can_priv), CAN_NETDEV_NAME,
+ can_netdev_setup))) {
+ printk(KERN_ERR "%s: out of memory\n", CHIP_NAME);
+ return NULL;
+ }
+
+ printk(KERN_INFO "%s: base 0x%lX / irq %d / speed %d / "
+ "btr 0x%X / rx_probe %d\n",
+ drv_name, rbase[dev_num], irq[dev_num],
+ speed[dev_num], btr[dev_num], rx_probe[dev_num]);
+
+ /* fill net_device structure */
+
+ priv = netdev_priv(dev);
+
+ dev->irq = irq[dev_num];
+ dev->base_addr = rbase[dev_num];
+
+ priv->speed = speed[dev_num];
+ priv->btr = btr[dev_num];
+ priv->rx_probe = rx_probe[dev_num];
+ priv->clock = clk;
+ priv->hw_regs = hw_regs;
+ priv->restart_ms = restart_ms;
+ priv->debug = debug;
+
+ init_timer(&priv->timer);
+ priv->timer.expires = 0;
+
+ if (register_netdev(dev)) {
+ printk(KERN_INFO "%s: register netdev failed\n", CHIP_NAME);
+ free_netdev(dev);
+ return NULL;
+ }
+
+ return dev;
+}
+
+int can_set_drv_name(void)
+{
+ char *hname = hal_name();
+
+ if (strlen(CHIP_NAME) + strlen(hname) >= DRV_NAME_LEN-1) {
+ printk(KERN_ERR "%s: driver name too long!\n", CHIP_NAME);
+ return -EINVAL;
+ }
+ sprintf(drv_name, "%s-%s", CHIP_NAME, hname);
+ return 0;
+}
+
+static void sja1000_exit_module(void)
+{
+ int i, ret;
+
+ for (i = 0; i < MAXDEV; i++) {
+ if (can_dev[i] != NULL) {
+ struct can_priv *priv = netdev_priv(can_dev[i]);
+ unregister_netdev(can_dev[i]);
+ del_timer(&priv->timer);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ free_netdev(can_dev[i]);
+ }
+ }
+ can_proc_remove(drv_name);
+
+ if ((ret = hal_exit()))
+ printk(KERN_INFO "%s: hal_exit error %d.\n", drv_name, ret);
+}
+
+static __init int sja1000_init_module(void)
+{
+ int i, ret;
+ struct net_device *dev;
+
+ if ((ret = hal_init()))
+ return ret;
+
+ if ((ret = can_set_drv_name()))
+ return ret;
+
+ if (clk < 1000 ) /* MHz command line value */
+ clk *= 1000000;
+
+ if (clk < 1000000 ) /* kHz command line value */
+ clk *= 1000;
+
+ printk(KERN_INFO "%s driver v%s (%s)\n",
+ drv_name, drv_version, drv_reldate);
+ printk(KERN_INFO "%s - options [clk %d.%06d MHz] [restart_ms %dms]"
+ " [debug %d]\n",
+ drv_name, clk/1000000, clk%1000000, restart_ms, debug);
+
+ if (!base[0]) {
+ printk(KERN_INFO "%s: loading defaults.\n", drv_name);
+ hal_use_defaults();
+ }
+
+ for (i = 0; base[i]; i++) {
+ printk(KERN_DEBUG "%s: checking for %s on address 0x%lX ...\n",
+ drv_name, CHIP_NAME, base[i]);
+
+ if (!hal_request_region(i, SJA1000_IO_SIZE_BASIC, drv_name)) {
+ printk(KERN_ERR "%s: memory already in use\n",
+ drv_name);
+ sja1000_exit_module();
+ return -EBUSY;
+ }
+
+ hw_attach(i);
+ hw_reset_dev(i);
+
+ if (!sja1000_probe_chip(rbase[i])) {
+ printk(KERN_ERR "%s: probably missing controller"
+ " hardware\n", drv_name);
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ sja1000_exit_module();
+ return -ENODEV;
+ }
+
+ dev = can_create_netdev(i, SJA1000_IO_SIZE_BASIC);
+
+ if (dev != NULL) {
+ can_dev[i] = dev;
+ set_reset_mode(dev);
+ can_proc_create(drv_name);
+ } else {
+ can_dev[i] = NULL;
+ hw_detach(i);
+ hal_release_region(i, SJA1000_IO_SIZE_BASIC);
+ }
+ }
+ return 0;
+}
+
+module_init(sja1000_init_module);
+module_exit(sja1000_exit_module);
+