All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-29 16:58 ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-29 16:58 UTC (permalink / raw)
  To: linux-kernel; +Cc: socketcan-core, netdev, sameo

The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
MODULbus carrier board. It is an intelligent CAN controller with a
microcontroller and associated firmware.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
---
 drivers/net/can/Kconfig      |   10 +
 drivers/net/can/Makefile     |    1 +
 drivers/net/can/janz-ican3.c | 1830 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1841 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/janz-ican3.c

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 05b7517..2c5227c 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -63,6 +63,16 @@ config CAN_BFIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called bfin_can.
 
+config CAN_JANZ_ICAN3
+	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
+	depends on CAN_DEV && MFD_JANZ_CMODIO
+	---help---
+	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
+	  connects to a MODULbus carrier board.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called janz-ican3.ko.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 7a702f2..9047cd0 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
+obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
new file mode 100644
index 0000000..6e533dc
--- /dev/null
+++ b/drivers/net/can/janz-ican3.c
@@ -0,0 +1,1830 @@
+/*
+ * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include <linux/mfd/janz.h>
+
+/* the DPM has 64k of memory, organized into 256x 256 byte pages */
+#define DPM_NUM_PAGES		256
+#define DPM_PAGE_SIZE		256
+#define DPM_PAGE_ADDR(p)	((p) * DPM_PAGE_SIZE)
+
+/* JANZ ICAN3 "old-style" host interface queue page numbers */
+#define QUEUE_OLD_CONTROL	0
+#define QUEUE_OLD_RB0		1
+#define QUEUE_OLD_RB1		2
+#define QUEUE_OLD_WB0		3
+#define QUEUE_OLD_WB1		4
+
+/* Janz ICAN3 "old-style" host interface control registers */
+#define MSYNC_PEER		0x00		/* ICAN only */
+#define MSYNC_LOCL		0x01		/* host only */
+#define TARGET_RUNNING		0x02
+
+#define MSYNC_RB0		0x01
+#define MSYNC_RB1		0x02
+#define MSYNC_RBLW		0x04
+#define MSYNC_RB_MASK		(MSYNC_RB0 | MSYNC_RB1)
+
+#define MSYNC_WB0		0x10
+#define MSYNC_WB1		0x20
+#define MSYNC_WBLW		0x40
+#define MSYNC_WB_MASK		(MSYNC_WB0 | MSYNC_WB1)
+
+/* Janz ICAN3 "new-style" host interface queue page numbers */
+#define QUEUE_TOHOST		5
+#define QUEUE_FROMHOST_MID	6
+#define QUEUE_FROMHOST_HIGH	7
+#define QUEUE_FROMHOST_LOW	8
+
+/* The first free page in the DPM is #9 */
+#define DPM_FREE_START		9
+
+/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
+#define DESC_VALID		0x80
+#define DESC_WRAP		0x40
+#define DESC_INTERRUPT		0x20
+#define DESC_IVALID		0x10
+#define DESC_LEN(len)		(len)
+
+/* Janz ICAN3 Firmware Messages */
+#define MSG_CONNECTI		0x02
+#define MSG_DISCONNECT		0x03
+#define MSG_IDVERS		0x04
+#define MSG_MSGLOST		0x05
+#define MSG_NEWHOSTIF		0x08
+#define MSG_INQUIRY		0x0a
+#define MSG_SETAFILMASK		0x10
+#define MSG_INITFDPMQUEUE	0x11
+#define MSG_HWCONF		0x12
+#define MSG_FMSGLOST		0x15
+#define MSG_CEVTIND		0x37
+#define MSG_CBTRREQ		0x41
+#define MSG_COFFREQ		0x42
+#define MSG_CONREQ		0x43
+#define MSG_CCONFREQ		0x47
+
+/*
+ * Janz ICAN3 CAN Inquiry Message Types
+ *
+ * NOTE: there appears to be a firmware bug here. You must send
+ * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED
+ * NOTE: response. The controller never responds to a message with
+ * NOTE: the INQUIRY_EXTENDED subspec :(
+ */
+#define INQUIRY_STATUS		0x00
+#define INQUIRY_TERMINATION	0x01
+#define INQUIRY_EXTENDED	0x04
+
+/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
+#define SETAFILMASK_REJECT	0x00
+#define SETAFILMASK_FASTIF	0x02
+
+/* Janz ICAN3 CAN Hardware Configuration Message Types */
+#define HWCONF_TERMINATE_ON	0x01
+#define HWCONF_TERMINATE_OFF	0x00
+
+/* Janz ICAN3 CAN Event Indication Message Types */
+#define CEVTIND_EI		0x01
+#define CEVTIND_DOI		0x02
+#define CEVTIND_LOST		0x04
+#define CEVTIND_FULL		0x08
+#define CEVTIND_BEI		0x10
+
+#define CEVTIND_CHIP_SJA1000	0x02
+
+#define ICAN3_BUSERR_QUOTA_MAX	255
+
+/* Janz ICAN3 CAN Frame Conversion */
+#define ICAN3_ECHO	0x10
+#define ICAN3_EFF_RTR	0x40
+#define ICAN3_SFF_RTR	0x10
+#define ICAN3_EFF	0x80
+
+#define ICAN3_CAN_TYPE_MASK	0x0f
+#define ICAN3_CAN_TYPE_SFF	0x00
+#define ICAN3_CAN_TYPE_EFF	0x01
+
+#define ICAN3_CAN_DLC_MASK	0x0f
+
+/*
+ * SJA1000 Status and Error Register Definitions
+ *
+ * Copied from drivers/net/can/sja1000/sja1000.h
+ */
+
+/* status register content */
+#define SR_BS		0x80
+#define SR_ES		0x40
+#define SR_TS		0x20
+#define SR_RS		0x10
+#define SR_TCS		0x08
+#define SR_TBS		0x04
+#define SR_DOS		0x02
+#define SR_RBS		0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG		0x1F
+#define ECC_DIR		0x20
+#define ECC_ERR		6
+#define ECC_BIT		0x00
+#define ECC_FORM	0x40
+#define ECC_STUFF	0x80
+#define ECC_MASK	0xc0
+
+/* Number of buffers for use in the "new-style" host interface */
+#define ICAN3_NEW_BUFFERS	16
+
+/* Number of buffers for use in the "fast" host interface */
+#define ICAN3_TX_BUFFERS	512
+#define ICAN3_RX_BUFFERS	1024
+
+/* SJA1000 Clock Input */
+#define ICAN3_CAN_CLOCK		8000000
+
+/* Driver Name */
+#define DRV_NAME "janz-ican3"
+
+/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
+struct ican3_dpm_control {
+	/* window address register */
+	u8 window_address;
+	u8 unused1;
+
+	/*
+	 * Read access: clear interrupt from microcontroller
+	 * Write access: send interrupt to microcontroller
+	 */
+	u8 interrupt;
+	u8 unused2;
+
+	/* write-only: reset all hardware on the module */
+	u8 hwreset;
+	u8 unused3;
+
+	/* write-only: generate an interrupt to the TPU */
+	u8 tpuinterrupt;
+};
+
+struct ican3_dev {
+
+	/* must be the first member */
+	struct can_priv can;
+
+	/* CAN network device */
+	struct net_device *ndev;
+	struct napi_struct napi;
+
+	/* Device for printing */
+	struct device *dev;
+
+	/* module number */
+	unsigned int num;
+
+	/* base address of registers and IRQ */
+	struct janz_cmodio_onboard_regs __iomem *ctrl;
+	struct ican3_dpm_control __iomem *dpmctrl;
+	void __iomem *dpm;
+	int irq;
+
+	/* CAN bus termination status */
+	struct completion termination_comp;
+	bool termination_enabled;
+
+	/* CAN bus error status registers */
+	struct completion buserror_comp;
+	struct can_berr_counter bec;
+
+	/* old and new style host interface */
+	unsigned int iftype;
+
+	/*
+	 * Any function which changes the current DPM page must hold this
+	 * lock while it is performing data accesses. This ensures that the
+	 * function will not be preempted and end up reading data from a
+	 * different DPM page than it expects.
+	 */
+	spinlock_t lock;
+
+	/* new host interface */
+	unsigned int rx_int;
+	unsigned int rx_num;
+	unsigned int tx_num;
+
+	/* fast host interface */
+	unsigned int fastrx_start;
+	unsigned int fastrx_int;
+	unsigned int fastrx_num;
+	unsigned int fasttx_start;
+	unsigned int fasttx_num;
+
+	/* first free DPM page */
+	unsigned int free_page;
+};
+
+struct ican3_msg {
+	u8 control;
+	u8 spec;
+	__le16 len;
+	u8 data[252];
+};
+
+struct ican3_new_desc {
+	u8 control;
+	u8 pointer;
+};
+
+struct ican3_fast_desc {
+	u8 control;
+	u8 command;
+	u8 data[14];
+};
+
+/* write to the window basic address register */
+static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
+{
+	BUG_ON(page >= DPM_NUM_PAGES);
+	iowrite8(page, &mod->dpmctrl->window_address);
+}
+
+/*
+ * ICAN3 "old-style" host interface
+ */
+
+/*
+ * Recieve a message from the ICAN3 "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no message exists
+ */
+static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_RB_MASK) == 0x00) {
+		dev_dbg(mod->dev, "no mbox for reading\n");
+		return -ENOMEM;
+	}
+
+	/* find the first free mbox to read */
+	if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
+		mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
+	else
+		mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
+
+	/* copy the message */
+	mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/*
+	 * notify the firmware that the read buffer is available
+	 * for it to fill again
+	 */
+	locl ^= mbox;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * Send a message through the "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no free space exists
+ */
+static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
+		dev_err(mod->dev, "no mbox for writing\n");
+		return -ENOMEM;
+	}
+
+	/* calculate a free mbox to use */
+	mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
+
+	/* copy the message to the DPM */
+	mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	locl ^= mbox;
+	if (mbox == MSYNC_WB1)
+		locl |= MSYNC_WBLW;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Setup
+ */
+
+static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_new_desc desc;
+	unsigned long flags;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* setup the internal datastructures for RX */
+	mod->rx_num = 0;
+	mod->rx_int = 0;
+
+	/* tohost queue descriptors are in page 5 */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	dst = mod->dpm;
+
+	/* initialize the tohost (rx) queue descriptors: pages 9-24 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost (tx) mid queue descriptors are in page 6 */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	dst = mod->dpm;
+
+	/* setup the internal datastructures for TX */
+	mod->tx_num = 0;
+
+	/* initialize the fromhost mid queue descriptors: pages 25-40 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost hi queue descriptors are in page 7 */
+	ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost hi queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	/* fromhost low queue descriptors are in page 8 */
+	ican3_set_page(mod, QUEUE_FROMHOST_LOW);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost low queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 Fast Host Interface Setup
+ */
+
+static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc desc;
+	unsigned long flags;
+	unsigned int addr;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* save the start recv page */
+	mod->fastrx_start = mod->free_page;
+	mod->fastrx_num = 0;
+	mod->fastrx_int = 0;
+
+	/* build a single fast tohost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = 0x00;
+	desc.command = 1;
+
+	/* build the tohost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_RX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	/* make sure we page-align the next queue */
+	if (addr != 0)
+		mod->free_page++;
+
+	/* save the start xmit page */
+	mod->fasttx_start = mod->free_page;
+	mod->fasttx_num = 0;
+
+	/* build a single fast fromhost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = DESC_VALID;
+	desc.command = 1;
+
+	/* build the fromhost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_TX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Message Helpers
+ */
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
+
+	/* switch to the fromhost mid queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	/* switch back to the descriptor, set the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the tx number */
+	mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
+	return 0;
+}
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
+
+	/* switch to the tohost queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/* switch back to the descriptor, toggle the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the rx number */
+	mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
+	return 0;
+}
+
+/*
+ * Message Send / Recv Helpers
+ */
+
+static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_send_msg(mod, msg);
+	else
+		ret = ican3_new_send_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_recv_msg(mod, msg);
+	else
+		ret = ican3_new_recv_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+/*
+ * Quick Pre-constructed Messages
+ */
+
+static int __devinit ican3_msg_connect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CONNECTI;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_DISCONNECT;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_NEWHOSTIF;
+	msg.len = cpu_to_le16(0);
+
+	/* If we're not using the old interface, switching seems bogus */
+	WARN_ON(mod->iftype != 0);
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* mark the module as using the new host interface */
+	mod->iftype = 1;
+	return 0;
+}
+
+static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	unsigned int addr;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_INITFDPMQUEUE;
+	msg.len = cpu_to_le16(8);
+
+	/* write the tohost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fastrx_start);
+	msg.data[0] = addr & 0xff;
+	msg.data[1] = (addr >> 8) & 0xff;
+	msg.data[2] = (addr >> 16) & 0xff;
+	msg.data[3] = (addr >> 24) & 0xff;
+
+	/* write the fromhost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fasttx_start);
+	msg.data[4] = addr & 0xff;
+	msg.data[5] = (addr >> 8) & 0xff;
+	msg.data[6] = (addr >> 16) & 0xff;
+	msg.data[7] = (addr >> 24) & 0xff;
+
+	/* If we're not using the new interface yet, we cannot do this */
+	WARN_ON(mod->iftype != 1);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Setup the CAN filter to either accept or reject all
+ * messages from the CAN bus.
+ */
+static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	/* Standard Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(5);
+	msg.data[0] = 0x00; /* IDLo LSB */
+	msg.data[1] = 0x00; /* IDLo MSB */
+	msg.data[2] = 0xff; /* IDHi LSB */
+	msg.data[3] = 0x07; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* Extended Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(13);
+	msg.data[0] = 0;    /* MUX = 0 */
+	msg.data[1] = 0x00; /* IDLo LSB */
+	msg.data[2] = 0x00;
+	msg.data[3] = 0x00;
+	msg.data[4] = 0x20; /* IDLo MSB */
+	msg.data[5] = 0xff; /* IDHi LSB */
+	msg.data[6] = 0xff;
+	msg.data[7] = 0xff;
+	msg.data[8] = 0x3f; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Bring the CAN bus online or offline
+ */
+static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_termination(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_HWCONF;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_INQUIRY;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = subspec;
+	msg.data[1] = 0x00;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_buserror(struct ican3_dev *mod, u8 quota)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CCONFREQ;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = quota;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * ICAN3 to Linux CAN Frame Conversion
+ */
+
+static void ican3_to_can_frame(struct ican3_dev *mod,
+			       struct ican3_fast_desc *desc,
+			       struct can_frame *cf)
+{
+	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
+		if (desc->data[1] & ICAN3_SFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		cf->can_id |= desc->data[0] << 3;
+		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
+		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
+		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
+	} else {
+		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
+		if (desc->data[0] & ICAN3_EFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		if (desc->data[0] & ICAN3_EFF) {
+			cf->can_id |= CAN_EFF_FLAG;
+			cf->can_id |= desc->data[2] << 21; /* 28-21 */
+			cf->can_id |= desc->data[3] << 13; /* 20-13 */
+			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
+			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
+		} else {
+			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
+			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
+		}
+
+		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
+	}
+}
+
+static void can_frame_to_ican3(struct ican3_dev *mod,
+			       struct can_frame *cf,
+			       struct ican3_fast_desc *desc)
+{
+	/* clear out any stale data in the descriptor */
+	memset(desc->data, 0, sizeof(desc->data));
+
+	/* we always use the extended format, with the ECHO flag set */
+	desc->command = ICAN3_CAN_TYPE_EFF;
+	desc->data[0] |= cf->can_dlc;
+	desc->data[1] |= ICAN3_ECHO;
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		desc->data[0] |= ICAN3_EFF_RTR;
+
+	/* pack the id into the correct places */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		desc->data[0] |= ICAN3_EFF;
+		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
+		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
+		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
+		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
+	} else {
+		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
+		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
+	}
+
+	/* copy the data bits into the descriptor */
+	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/*
+ * Handle an ID + Version message response from the firmware. We never generate
+ * this message in production code, but it is very useful when debugging to be
+ * able to display this message.
+ */
+static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
+}
+
+static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/*
+	 * Report that communication messages with the microcontroller firmware
+	 * are being lost. These are never CAN frames, so we do not generate an
+	 * error frame for userspace
+	 */
+	if (msg->spec == MSG_MSGLOST) {
+		dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
+		return;
+	}
+
+	/*
+	 * Oops, this indicates that we have lost messages in the fast queue,
+	 * which are exclusively CAN messages. Our driver isn't reading CAN
+	 * frames fast enough.
+	 *
+	 * We'll pretend that the SJA1000 told us that it ran out of buffer
+	 * space, because there is not a better message for this.
+	 */
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_errors++;
+		stats->rx_bytes += cf->can_dlc;
+		netif_rx(skb);
+	}
+}
+
+/*
+ * Handle CAN Event Indication Messages from the firmware
+ *
+ * The ICAN3 firmware provides the values of some SJA1000 registers when it
+ * generates this message. The code below is largely copied from the
+ * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
+ */
+static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	enum can_state state = mod->can.state;
+	u8 status, isrc, rxerr, txerr;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/* we can only handle the SJA1000 part */
+	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
+		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
+		return -ENODEV;
+	}
+
+	/* check the message length for sanity */
+	if (le16_to_cpu(msg->len) < 6) {
+		dev_err(mod->dev, "error message too short\n");
+		return -EINVAL;
+	}
+
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	isrc = msg->data[0];
+	status = msg->data[3];
+	rxerr = msg->data[4];
+	txerr = msg->data[5];
+
+	/* data overrun interrupt */
+	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
+		dev_dbg(mod->dev, "data overrun interrupt\n");
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+	}
+
+	/* error warning + passive interrupt */
+	if (isrc == CEVTIND_EI) {
+		dev_dbg(mod->dev, "error warning + passive interrupt\n");
+		if (status & SR_BS) {
+			state = CAN_STATE_BUS_OFF;
+			cf->can_id |= CAN_ERR_BUSOFF;
+			can_bus_off(dev);
+		} else if (status & SR_ES) {
+			if (rxerr >= 128 || txerr >= 128)
+				state = CAN_STATE_ERROR_PASSIVE;
+			else
+				state = CAN_STATE_ERROR_WARNING;
+		} else {
+			state = CAN_STATE_ERROR_ACTIVE;
+		}
+	}
+
+	/* bus error interrupt */
+	if (isrc == CEVTIND_BEI) {
+		u8 ecc = msg->data[2];
+
+		dev_dbg(mod->dev, "bus error interrupt\n");
+		mod->can.can_stats.bus_error++;
+		stats->rx_errors++;
+		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+		switch (ecc & ECC_MASK) {
+		case ECC_BIT:
+			cf->data[2] |= CAN_ERR_PROT_BIT;
+			break;
+		case ECC_FORM:
+			cf->data[2] |= CAN_ERR_PROT_FORM;
+			break;
+		case ECC_STUFF:
+			cf->data[2] |= CAN_ERR_PROT_STUFF;
+			break;
+		default:
+			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+			cf->data[3] = ecc & ECC_SEG;
+			break;
+		}
+
+		if ((ecc & ECC_DIR) == 0)
+			cf->data[2] |= CAN_ERR_PROT_TX;
+
+		cf->data[6] = txerr;
+		cf->data[7] = rxerr;
+	}
+
+	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
+					state == CAN_STATE_ERROR_PASSIVE)) {
+		cf->can_id |= CAN_ERR_CRTL;
+		if (state == CAN_STATE_ERROR_WARNING) {
+			mod->can.can_stats.error_warning++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_WARNING :
+				CAN_ERR_CRTL_RX_WARNING;
+		} else {
+			mod->can.can_stats.error_passive++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_PASSIVE :
+				CAN_ERR_CRTL_RX_PASSIVE;
+		}
+
+		cf->data[6] = txerr;
+		cf->data[7] = rxerr;
+	}
+
+	mod->can.state = state;
+	stats->rx_errors++;
+	stats->rx_bytes += cf->can_dlc;
+	netif_rx(skb);
+	return 0;
+}
+
+static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	switch (msg->data[0]) {
+	case INQUIRY_STATUS:
+	case INQUIRY_EXTENDED:
+		mod->bec.rxerr = msg->data[5];
+		mod->bec.txerr = msg->data[6];
+		complete(&mod->buserror_comp);
+		break;
+	case INQUIRY_TERMINATION:
+		mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON;
+		complete(&mod->termination_comp);
+		break;
+	default:
+		dev_err(mod->dev, "recieved an unknown inquiry response\n");
+		break;
+	}
+}
+
+static void ican3_handle_unknown_message(struct ican3_dev *mod,
+					struct ican3_msg *msg)
+{
+	dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
+			   msg->spec, le16_to_cpu(msg->len));
+}
+
+/*
+ * Handle a control message from the firmware
+ */
+static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
+			   mod->num, msg->spec, le16_to_cpu(msg->len));
+
+	switch (msg->spec) {
+	case MSG_IDVERS:
+		ican3_handle_idvers(mod, msg);
+		break;
+	case MSG_MSGLOST:
+	case MSG_FMSGLOST:
+		ican3_handle_msglost(mod, msg);
+		break;
+	case MSG_CEVTIND:
+		ican3_handle_cevtind(mod, msg);
+		break;
+	case MSG_INQUIRY:
+		ican3_handle_inquiry(mod, msg);
+		break;
+	default:
+		ican3_handle_unknown_message(mod, msg);
+		break;
+	}
+}
+
+/*
+ * Check that there is room in the TX ring to transmit another skb
+ *
+ * LOCKING: must hold mod->lock
+ */
+static bool ican3_txok(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc __iomem *desc;
+	u8 control;
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
+	control = ioread8(&desc->control);
+
+	/* if the control bits are not valid, then we have no more space */
+	if (!(control & DESC_VALID))
+		return false;
+
+	return true;
+}
+
+/*
+ * Recieve one CAN frame from the hardware
+ *
+ * This works like the core of a NAPI function, but is intended to be called
+ * from workqueue context instead. This driver already needs a workqueue to
+ * process control messages, so we use the workqueue instead of using NAPI.
+ * This was done to simplify locking.
+ *
+ * CONTEXT: must be called from user context
+ */
+static int ican3_recv_skb(struct ican3_dev *mod)
+{
+	struct net_device *ndev = mod->ndev;
+	struct net_device_stats *stats = &ndev->stats;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* copy the whole descriptor */
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* check that we actually have a CAN frame */
+	if (!(desc.control & DESC_VALID))
+		return -ENOBUFS;
+
+	/* allocate an skb */
+	skb = alloc_can_skb(ndev, &cf);
+	if (unlikely(skb == NULL)) {
+		stats->rx_dropped++;
+		goto err_noalloc;
+	}
+
+	/* convert the ICAN3 frame into Linux CAN format */
+	ican3_to_can_frame(mod, &desc, cf);
+
+	/* receive the skb, update statistics */
+	netif_receive_skb(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+
+err_noalloc:
+	/* toggle the valid bit and return the descriptor to the ring */
+	desc.control ^= DESC_VALID;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	memcpy_toio(desc_addr, &desc, 1);
+
+	/* update the next buffer pointer */
+	mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fastrx_num + 1);
+
+	/* there are still more buffers to process */
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return 0;
+}
+
+static int ican3_napi(struct napi_struct *napi, int budget)
+{
+	struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
+	struct ican3_msg msg;
+	unsigned long flags;
+	int received = 0;
+	int ret;
+
+	/* process all communication messages */
+	while (true) {
+		ret = ican3_recv_msg(mod, &msg);
+		if (ret)
+			break;
+
+		ican3_handle_message(mod, &msg);
+	}
+
+	/* process all CAN frames from the fast interface */
+	while (received < budget) {
+		ret = ican3_recv_skb(mod);
+		if (ret)
+			break;
+
+		received++;
+	}
+
+	/* We have processed all packets that the adapter had, but it
+	 * was less than our budget, stop polling */
+	if (received < budget)
+		napi_complete(napi);
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* Wake up the transmit queue if necessary */
+	if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
+		netif_wake_queue(mod->ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* re-enable interrupt generation */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+	return received;
+}
+
+static irqreturn_t ican3_irq(int irq, void *dev_id)
+{
+	struct ican3_dev *mod = dev_id;
+	u8 stat;
+
+	/*
+	 * The interrupt status register on this device reports interrupts
+	 * as zeroes instead of using ones like most other devices
+	 */
+	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
+	if (stat == (1 << mod->num))
+		return IRQ_NONE;
+
+	/* clear the MODULbus interrupt from the microcontroller */
+	ioread8(&mod->dpmctrl->interrupt);
+
+	/* disable interrupt generation, schedule the NAPI poller */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	napi_schedule(&mod->napi);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Firmware reset, startup, and shutdown
+ */
+
+/*
+ * Reset an ICAN module to its power-on state
+ *
+ * CONTEXT: no network device registered
+ * LOCKING: work function disabled
+ */
+static int ican3_reset_module(struct ican3_dev *mod)
+{
+	u8 val = 1 << mod->num;
+	unsigned long start;
+	u8 runold, runnew;
+
+	/* disable interrupts so no more work is scheduled */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+
+	/* flush any pending work */
+	flush_scheduled_work();
+
+	/* the first unallocated page in the DPM is #9 */
+	mod->free_page = DPM_FREE_START;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	runold = ioread8(mod->dpm + TARGET_RUNNING);
+
+	/* reset the module */
+	iowrite8(val, &mod->ctrl->reset_assert);
+	iowrite8(val, &mod->ctrl->reset_deassert);
+
+	/* wait until the module has finished resetting and is running */
+	start = jiffies;
+	do {
+		ican3_set_page(mod, QUEUE_OLD_CONTROL);
+		runnew = ioread8(mod->dpm + TARGET_RUNNING);
+		if (runnew == (runold ^ 0xff))
+			return 0;
+
+		msleep(10);
+	} while (time_before(jiffies, start + HZ / 4));
+
+	dev_err(mod->dev, "failed to reset CAN module\n");
+	return -ETIMEDOUT;
+}
+
+static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
+{
+	ican3_msg_disconnect(mod);
+	ican3_reset_module(mod);
+}
+
+/*
+ * Startup an ICAN module, bringing it into fast mode
+ */
+static int __devinit ican3_startup_module(struct ican3_dev *mod)
+{
+	int ret;
+
+	ret = ican3_reset_module(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to reset module\n");
+		return ret;
+	}
+
+	/* re-enable interrupts so we can send messages */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+
+	ret = ican3_msg_connect(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to connect to module\n");
+		return ret;
+	}
+
+	ican3_init_new_host_interface(mod);
+	ret = ican3_msg_newhostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to new-style interface\n");
+		return ret;
+	}
+
+	/* default to "termination on" */
+	ret = ican3_set_termination(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to enable termination\n");
+		return ret;
+	}
+
+	/* default to "bus errors enabled" */
+	ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-error\n");
+		return ret;
+	}
+
+	ican3_init_fast_host_interface(mod);
+	ret = ican3_msg_fasthostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to fast host interface\n");
+		return ret;
+	}
+
+	ret = ican3_set_id_filter(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set acceptance filter\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * CAN Network Device
+ */
+
+static int ican3_open(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	u8 quota;
+	int ret;
+
+	/* open the CAN layer */
+	ret = open_candev(ndev);
+	if (ret) {
+		dev_err(mod->dev, "unable to start CAN layer\n");
+		return ret;
+	}
+
+	/* set the bus error generation state appropriately */
+	if (mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		quota = ICAN3_BUSERR_QUOTA_MAX;
+	else
+		quota = 0;
+
+	ret = ican3_set_buserror(mod, quota);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-error\n");
+		close_candev(ndev);
+		return ret;
+	}
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		close_candev(ndev);
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+	netif_start_queue(ndev);
+
+	return 0;
+}
+
+static int ican3_stop(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	/* stop the network device xmit routine */
+	netif_stop_queue(ndev);
+	mod->can.state = CAN_STATE_STOPPED;
+
+	/* bring the bus offline, stop receiving packets */
+	ret = ican3_set_bus_state(mod, false);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-off\n");
+		return ret;
+	}
+
+	/* close the CAN layer */
+	close_candev(ndev);
+	return 0;
+}
+
+static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* check that we can actually transmit */
+	if (!ican3_txok(mod)) {
+		dev_err(mod->dev, "no free descriptors, stopping queue\n");
+		netif_stop_queue(ndev);
+		spin_unlock_irqrestore(&mod->lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
+	memset(&desc, 0, sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, 1);
+
+	/* convert the Linux CAN frame into ICAN3 format */
+	can_frame_to_ican3(mod, cf, &desc);
+
+	/*
+	 * the programming manual says that you must set the IVALID bit, then
+	 * interrupt, then set the valid bit. Quite weird, but it seems to be
+	 * required for this to work
+	 */
+	desc.control |= DESC_IVALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* generate a MODULbus interrupt to the microcontroller */
+	iowrite8(0x01, &mod->dpmctrl->interrupt);
+
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the next buffer pointer */
+	mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fasttx_num + 1);
+
+	/* update statistics */
+	stats->tx_packets++;
+	stats->tx_bytes += cf->can_dlc;
+	kfree_skb(skb);
+
+	/*
+	 * This hardware doesn't have TX-done notifications, so we'll try and
+	 * emulate it the best we can using ECHO skbs. Get the next TX
+	 * descriptor, and see if we have room to send. If not, stop the queue.
+	 * It will be woken when the ECHO skb for the current packet is recv'd.
+	 */
+
+	/* copy the control bits of the descriptor */
+	if (!ican3_txok(mod))
+		netif_stop_queue(ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ican3_netdev_ops = {
+	.ndo_open	= ican3_open,
+	.ndo_stop	= ican3_stop,
+	.ndo_start_xmit	= ican3_xmit,
+};
+
+/*
+ * Low-level CAN Device
+ */
+
+/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
+static struct can_bittiming_const ican3_bittiming_const = {
+	.name = DRV_NAME,
+	.tseg1_min = 1,
+	.tseg1_max = 16,
+	.tseg2_min = 1,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 64,
+	.brp_inc = 1,
+};
+
+/*
+ * This routine was stolen from drivers/net/can/sja1000/sja1000.c
+ *
+ * The bittiming register command for the ICAN3 just sets the bit timing
+ * registers on the SJA1000 chip directly
+ */
+static int ican3_set_bittiming(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct can_bittiming *bt = &mod->can.bittiming;
+	struct ican3_msg msg;
+	u8 btr0, btr1;
+
+	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+		(((bt->phase_seg2 - 1) & 0x7) << 4);
+	if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btr1 |= 0x80;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CBTRREQ;
+	msg.len = cpu_to_le16(4);
+	msg.data[0] = 0x00;
+	msg.data[1] = 0x00;
+	msg.data[2] = btr0;
+	msg.data[3] = btr1;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	if (mode != CAN_MODE_START)
+		return -ENOTSUPP;
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	if (netif_queue_stopped(ndev))
+		netif_wake_queue(ndev);
+
+	return 0;
+}
+
+static int ican3_get_berr_counter(const struct net_device *ndev,
+				  struct can_berr_counter *bec)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	ret = ican3_send_inquiry(mod, INQUIRY_STATUS);
+	if (ret)
+		return ret;
+
+	ret = wait_for_completion_timeout(&mod->buserror_comp, HZ);
+	if (ret <= 0) {
+		dev_info(mod->dev, "%s timed out\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	bec->rxerr = mod->bec.rxerr;
+	bec->txerr = mod->bec.txerr;
+	return 0;
+}
+
+/*
+ * Sysfs Attributes
+ */
+
+static ssize_t ican3_sysfs_show_term(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct ican3_dev *mod = netdev_priv(to_net_dev(dev));
+	int ret;
+
+	ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION);
+	if (ret)
+		return ret;
+
+	ret = wait_for_completion_timeout(&mod->termination_comp, HZ);
+	if (ret <= 0) {
+		dev_info(mod->dev, "%s timed out\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mod->termination_enabled);
+}
+
+static ssize_t ican3_sysfs_set_term(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct ican3_dev *mod = netdev_priv(to_net_dev(dev));
+	unsigned long enable;
+	int ret;
+
+	if (strict_strtoul(buf, 0, &enable))
+		return -EINVAL;
+
+	ret = ican3_set_termination(mod, enable);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR(termination, S_IWUGO | S_IRUGO, ican3_sysfs_show_term,
+						   ican3_sysfs_set_term);
+
+static struct attribute *ican3_sysfs_attrs[] = {
+	&dev_attr_termination.attr,
+	NULL,
+};
+
+static struct attribute_group ican3_sysfs_attr_group = {
+	.attrs = ican3_sysfs_attrs,
+};
+
+/*
+ * PCI Subsystem
+ */
+
+static int __devinit ican3_probe(struct platform_device *pdev)
+{
+	struct janz_platform_data *pdata;
+	struct net_device *ndev;
+	struct ican3_dev *mod;
+	struct resource *res;
+	struct device *dev;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENXIO;
+
+	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
+
+	/* save the struct device for printing */
+	dev = &pdev->dev;
+
+	/* allocate the CAN device and private data */
+	ndev = alloc_candev(sizeof(*mod), 0);
+	if (!ndev) {
+		dev_err(dev, "unable to allocate CANdev\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	platform_set_drvdata(pdev, ndev);
+	mod = netdev_priv(ndev);
+	mod->ndev = ndev;
+	mod->dev = &pdev->dev;
+	mod->num = pdata->modno;
+	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
+	spin_lock_init(&mod->lock);
+	init_completion(&mod->termination_comp);
+	init_completion(&mod->buserror_comp);
+
+	/* setup device-specific sysfs attributes */
+	ndev->sysfs_groups[0] = &ican3_sysfs_attr_group;
+
+	/* the first unallocated page in the DPM is 9 */
+	mod->free_page = DPM_FREE_START;
+
+	ndev->netdev_ops = &ican3_netdev_ops;
+	ndev->flags |= IFF_ECHO;
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	mod->can.clock.freq = ICAN3_CAN_CLOCK;
+	mod->can.bittiming_const = &ican3_bittiming_const;
+	mod->can.do_set_bittiming = ican3_set_bittiming;
+	mod->can.do_set_mode = ican3_set_mode;
+	mod->can.do_get_berr_counter = ican3_get_berr_counter;
+	mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES
+				    | CAN_CTRLMODE_BERR_REPORTING;
+
+	/* find our IRQ number */
+	mod->irq = platform_get_irq(pdev, 0);
+	if (mod->irq < 0) {
+		dev_err(dev, "IRQ line not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	ndev->irq = mod->irq;
+
+	/* get access to the MODULbus registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "MODULbus registers not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	mod->dpm = ioremap(res->start, resource_size(res));
+	if (!mod->dpm) {
+		dev_err(dev, "MODULbus registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_free_ndev;
+	}
+
+	mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
+
+	/* get access to the control registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(dev, "CONTROL registers not found\n");
+		ret = -ENODEV;
+		goto out_iounmap_dpm;
+	}
+
+	mod->ctrl = ioremap(res->start, resource_size(res));
+	if (!mod->ctrl) {
+		dev_err(dev, "CONTROL registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_iounmap_dpm;
+	}
+
+	/* disable our IRQ, then hookup the IRQ handler */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
+	if (ret) {
+		dev_err(dev, "unable to request IRQ\n");
+		goto out_iounmap_ctrl;
+	}
+
+	/* reset and initialize the CAN controller into fast mode */
+	napi_enable(&mod->napi);
+	ret = ican3_startup_module(mod);
+	if (ret) {
+		dev_err(dev, "%s: unable to start CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	/* register with the Linux CAN layer */
+	ret = register_candev(ndev);
+	if (ret) {
+		dev_err(dev, "%s: unable to register CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
+	return 0;
+
+out_free_irq:
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+out_iounmap_ctrl:
+	iounmap(mod->ctrl);
+out_iounmap_dpm:
+	iounmap(mod->dpm);
+out_free_ndev:
+	free_candev(ndev);
+out_return:
+	return ret;
+}
+
+static int __devexit ican3_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct ican3_dev *mod = netdev_priv(ndev);
+
+	/* unregister the netdevice, stop interrupts */
+	unregister_netdev(ndev);
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+
+	/* put the module into reset */
+	ican3_shutdown_module(mod);
+
+	/* unmap all registers */
+	iounmap(mod->ctrl);
+	iounmap(mod->dpm);
+
+	free_candev(ndev);
+
+	return 0;
+}
+
+static struct platform_driver ican3_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ican3_probe,
+	.remove		= __devexit_p(ican3_remove),
+};
+
+static int __init ican3_init(void)
+{
+	return platform_driver_register(&ican3_driver);
+}
+
+static void __exit ican3_exit(void)
+{
+	platform_driver_unregister(&ican3_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ican3");
+
+module_init(ican3_init);
+module_exit(ican3_exit);
-- 
1.5.4.3


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-29 16:58 ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-29 16:58 UTC (permalink / raw)
  To: linux-kernel-u79uwXL29TY76Z2rM5mHXA
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA, sameo-VuQAYsv1563Yd54FQh9/CA

The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
MODULbus carrier board. It is an intelligent CAN controller with a
microcontroller and associated firmware.

Signed-off-by: Ira W. Snyder <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org>
---
 drivers/net/can/Kconfig      |   10 +
 drivers/net/can/Makefile     |    1 +
 drivers/net/can/janz-ican3.c | 1830 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1841 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/janz-ican3.c

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 05b7517..2c5227c 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -63,6 +63,16 @@ config CAN_BFIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called bfin_can.
 
+config CAN_JANZ_ICAN3
+	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
+	depends on CAN_DEV && MFD_JANZ_CMODIO
+	---help---
+	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
+	  connects to a MODULbus carrier board.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called janz-ican3.ko.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 7a702f2..9047cd0 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
+obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
new file mode 100644
index 0000000..6e533dc
--- /dev/null
+++ b/drivers/net/can/janz-ican3.c
@@ -0,0 +1,1830 @@
+/*
+ * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include <linux/mfd/janz.h>
+
+/* the DPM has 64k of memory, organized into 256x 256 byte pages */
+#define DPM_NUM_PAGES		256
+#define DPM_PAGE_SIZE		256
+#define DPM_PAGE_ADDR(p)	((p) * DPM_PAGE_SIZE)
+
+/* JANZ ICAN3 "old-style" host interface queue page numbers */
+#define QUEUE_OLD_CONTROL	0
+#define QUEUE_OLD_RB0		1
+#define QUEUE_OLD_RB1		2
+#define QUEUE_OLD_WB0		3
+#define QUEUE_OLD_WB1		4
+
+/* Janz ICAN3 "old-style" host interface control registers */
+#define MSYNC_PEER		0x00		/* ICAN only */
+#define MSYNC_LOCL		0x01		/* host only */
+#define TARGET_RUNNING		0x02
+
+#define MSYNC_RB0		0x01
+#define MSYNC_RB1		0x02
+#define MSYNC_RBLW		0x04
+#define MSYNC_RB_MASK		(MSYNC_RB0 | MSYNC_RB1)
+
+#define MSYNC_WB0		0x10
+#define MSYNC_WB1		0x20
+#define MSYNC_WBLW		0x40
+#define MSYNC_WB_MASK		(MSYNC_WB0 | MSYNC_WB1)
+
+/* Janz ICAN3 "new-style" host interface queue page numbers */
+#define QUEUE_TOHOST		5
+#define QUEUE_FROMHOST_MID	6
+#define QUEUE_FROMHOST_HIGH	7
+#define QUEUE_FROMHOST_LOW	8
+
+/* The first free page in the DPM is #9 */
+#define DPM_FREE_START		9
+
+/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
+#define DESC_VALID		0x80
+#define DESC_WRAP		0x40
+#define DESC_INTERRUPT		0x20
+#define DESC_IVALID		0x10
+#define DESC_LEN(len)		(len)
+
+/* Janz ICAN3 Firmware Messages */
+#define MSG_CONNECTI		0x02
+#define MSG_DISCONNECT		0x03
+#define MSG_IDVERS		0x04
+#define MSG_MSGLOST		0x05
+#define MSG_NEWHOSTIF		0x08
+#define MSG_INQUIRY		0x0a
+#define MSG_SETAFILMASK		0x10
+#define MSG_INITFDPMQUEUE	0x11
+#define MSG_HWCONF		0x12
+#define MSG_FMSGLOST		0x15
+#define MSG_CEVTIND		0x37
+#define MSG_CBTRREQ		0x41
+#define MSG_COFFREQ		0x42
+#define MSG_CONREQ		0x43
+#define MSG_CCONFREQ		0x47
+
+/*
+ * Janz ICAN3 CAN Inquiry Message Types
+ *
+ * NOTE: there appears to be a firmware bug here. You must send
+ * NOTE: INQUIRY_STATUS and expect to receive an INQUIRY_EXTENDED
+ * NOTE: response. The controller never responds to a message with
+ * NOTE: the INQUIRY_EXTENDED subspec :(
+ */
+#define INQUIRY_STATUS		0x00
+#define INQUIRY_TERMINATION	0x01
+#define INQUIRY_EXTENDED	0x04
+
+/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
+#define SETAFILMASK_REJECT	0x00
+#define SETAFILMASK_FASTIF	0x02
+
+/* Janz ICAN3 CAN Hardware Configuration Message Types */
+#define HWCONF_TERMINATE_ON	0x01
+#define HWCONF_TERMINATE_OFF	0x00
+
+/* Janz ICAN3 CAN Event Indication Message Types */
+#define CEVTIND_EI		0x01
+#define CEVTIND_DOI		0x02
+#define CEVTIND_LOST		0x04
+#define CEVTIND_FULL		0x08
+#define CEVTIND_BEI		0x10
+
+#define CEVTIND_CHIP_SJA1000	0x02
+
+#define ICAN3_BUSERR_QUOTA_MAX	255
+
+/* Janz ICAN3 CAN Frame Conversion */
+#define ICAN3_ECHO	0x10
+#define ICAN3_EFF_RTR	0x40
+#define ICAN3_SFF_RTR	0x10
+#define ICAN3_EFF	0x80
+
+#define ICAN3_CAN_TYPE_MASK	0x0f
+#define ICAN3_CAN_TYPE_SFF	0x00
+#define ICAN3_CAN_TYPE_EFF	0x01
+
+#define ICAN3_CAN_DLC_MASK	0x0f
+
+/*
+ * SJA1000 Status and Error Register Definitions
+ *
+ * Copied from drivers/net/can/sja1000/sja1000.h
+ */
+
+/* status register content */
+#define SR_BS		0x80
+#define SR_ES		0x40
+#define SR_TS		0x20
+#define SR_RS		0x10
+#define SR_TCS		0x08
+#define SR_TBS		0x04
+#define SR_DOS		0x02
+#define SR_RBS		0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG		0x1F
+#define ECC_DIR		0x20
+#define ECC_ERR		6
+#define ECC_BIT		0x00
+#define ECC_FORM	0x40
+#define ECC_STUFF	0x80
+#define ECC_MASK	0xc0
+
+/* Number of buffers for use in the "new-style" host interface */
+#define ICAN3_NEW_BUFFERS	16
+
+/* Number of buffers for use in the "fast" host interface */
+#define ICAN3_TX_BUFFERS	512
+#define ICAN3_RX_BUFFERS	1024
+
+/* SJA1000 Clock Input */
+#define ICAN3_CAN_CLOCK		8000000
+
+/* Driver Name */
+#define DRV_NAME "janz-ican3"
+
+/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
+struct ican3_dpm_control {
+	/* window address register */
+	u8 window_address;
+	u8 unused1;
+
+	/*
+	 * Read access: clear interrupt from microcontroller
+	 * Write access: send interrupt to microcontroller
+	 */
+	u8 interrupt;
+	u8 unused2;
+
+	/* write-only: reset all hardware on the module */
+	u8 hwreset;
+	u8 unused3;
+
+	/* write-only: generate an interrupt to the TPU */
+	u8 tpuinterrupt;
+};
+
+struct ican3_dev {
+
+	/* must be the first member */
+	struct can_priv can;
+
+	/* CAN network device */
+	struct net_device *ndev;
+	struct napi_struct napi;
+
+	/* Device for printing */
+	struct device *dev;
+
+	/* module number */
+	unsigned int num;
+
+	/* base address of registers and IRQ */
+	struct janz_cmodio_onboard_regs __iomem *ctrl;
+	struct ican3_dpm_control __iomem *dpmctrl;
+	void __iomem *dpm;
+	int irq;
+
+	/* CAN bus termination status */
+	struct completion termination_comp;
+	bool termination_enabled;
+
+	/* CAN bus error status registers */
+	struct completion buserror_comp;
+	struct can_berr_counter bec;
+
+	/* old and new style host interface */
+	unsigned int iftype;
+
+	/*
+	 * Any function which changes the current DPM page must hold this
+	 * lock while it is performing data accesses. This ensures that the
+	 * function will not be preempted and end up reading data from a
+	 * different DPM page than it expects.
+	 */
+	spinlock_t lock;
+
+	/* new host interface */
+	unsigned int rx_int;
+	unsigned int rx_num;
+	unsigned int tx_num;
+
+	/* fast host interface */
+	unsigned int fastrx_start;
+	unsigned int fastrx_int;
+	unsigned int fastrx_num;
+	unsigned int fasttx_start;
+	unsigned int fasttx_num;
+
+	/* first free DPM page */
+	unsigned int free_page;
+};
+
+struct ican3_msg {
+	u8 control;
+	u8 spec;
+	__le16 len;
+	u8 data[252];
+};
+
+struct ican3_new_desc {
+	u8 control;
+	u8 pointer;
+};
+
+struct ican3_fast_desc {
+	u8 control;
+	u8 command;
+	u8 data[14];
+};
+
+/* write to the window basic address register */
+static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
+{
+	BUG_ON(page >= DPM_NUM_PAGES);
+	iowrite8(page, &mod->dpmctrl->window_address);
+}
+
+/*
+ * ICAN3 "old-style" host interface
+ */
+
+/*
+ * Recieve a message from the ICAN3 "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no message exists
+ */
+static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_RB_MASK) == 0x00) {
+		dev_dbg(mod->dev, "no mbox for reading\n");
+		return -ENOMEM;
+	}
+
+	/* find the first free mbox to read */
+	if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
+		mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
+	else
+		mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
+
+	/* copy the message */
+	mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/*
+	 * notify the firmware that the read buffer is available
+	 * for it to fill again
+	 */
+	locl ^= mbox;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * Send a message through the "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no free space exists
+ */
+static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
+		dev_err(mod->dev, "no mbox for writing\n");
+		return -ENOMEM;
+	}
+
+	/* calculate a free mbox to use */
+	mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
+
+	/* copy the message to the DPM */
+	mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	locl ^= mbox;
+	if (mbox == MSYNC_WB1)
+		locl |= MSYNC_WBLW;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Setup
+ */
+
+static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_new_desc desc;
+	unsigned long flags;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* setup the internal datastructures for RX */
+	mod->rx_num = 0;
+	mod->rx_int = 0;
+
+	/* tohost queue descriptors are in page 5 */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	dst = mod->dpm;
+
+	/* initialize the tohost (rx) queue descriptors: pages 9-24 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost (tx) mid queue descriptors are in page 6 */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	dst = mod->dpm;
+
+	/* setup the internal datastructures for TX */
+	mod->tx_num = 0;
+
+	/* initialize the fromhost mid queue descriptors: pages 25-40 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost hi queue descriptors are in page 7 */
+	ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost hi queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	/* fromhost low queue descriptors are in page 8 */
+	ican3_set_page(mod, QUEUE_FROMHOST_LOW);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost low queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 Fast Host Interface Setup
+ */
+
+static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc desc;
+	unsigned long flags;
+	unsigned int addr;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* save the start recv page */
+	mod->fastrx_start = mod->free_page;
+	mod->fastrx_num = 0;
+	mod->fastrx_int = 0;
+
+	/* build a single fast tohost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = 0x00;
+	desc.command = 1;
+
+	/* build the tohost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_RX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	/* make sure we page-align the next queue */
+	if (addr != 0)
+		mod->free_page++;
+
+	/* save the start xmit page */
+	mod->fasttx_start = mod->free_page;
+	mod->fasttx_num = 0;
+
+	/* build a single fast fromhost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = DESC_VALID;
+	desc.command = 1;
+
+	/* build the fromhost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_TX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Message Helpers
+ */
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
+
+	/* switch to the fromhost mid queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	/* switch back to the descriptor, set the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the tx number */
+	mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
+	return 0;
+}
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
+
+	/* switch to the tohost queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/* switch back to the descriptor, toggle the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the rx number */
+	mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
+	return 0;
+}
+
+/*
+ * Message Send / Recv Helpers
+ */
+
+static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_send_msg(mod, msg);
+	else
+		ret = ican3_new_send_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_recv_msg(mod, msg);
+	else
+		ret = ican3_new_recv_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+/*
+ * Quick Pre-constructed Messages
+ */
+
+static int __devinit ican3_msg_connect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CONNECTI;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_DISCONNECT;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_NEWHOSTIF;
+	msg.len = cpu_to_le16(0);
+
+	/* If we're not using the old interface, switching seems bogus */
+	WARN_ON(mod->iftype != 0);
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* mark the module as using the new host interface */
+	mod->iftype = 1;
+	return 0;
+}
+
+static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	unsigned int addr;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_INITFDPMQUEUE;
+	msg.len = cpu_to_le16(8);
+
+	/* write the tohost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fastrx_start);
+	msg.data[0] = addr & 0xff;
+	msg.data[1] = (addr >> 8) & 0xff;
+	msg.data[2] = (addr >> 16) & 0xff;
+	msg.data[3] = (addr >> 24) & 0xff;
+
+	/* write the fromhost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fasttx_start);
+	msg.data[4] = addr & 0xff;
+	msg.data[5] = (addr >> 8) & 0xff;
+	msg.data[6] = (addr >> 16) & 0xff;
+	msg.data[7] = (addr >> 24) & 0xff;
+
+	/* If we're not using the new interface yet, we cannot do this */
+	WARN_ON(mod->iftype != 1);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Setup the CAN filter to either accept or reject all
+ * messages from the CAN bus.
+ */
+static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	/* Standard Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(5);
+	msg.data[0] = 0x00; /* IDLo LSB */
+	msg.data[1] = 0x00; /* IDLo MSB */
+	msg.data[2] = 0xff; /* IDHi LSB */
+	msg.data[3] = 0x07; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* Extended Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(13);
+	msg.data[0] = 0;    /* MUX = 0 */
+	msg.data[1] = 0x00; /* IDLo LSB */
+	msg.data[2] = 0x00;
+	msg.data[3] = 0x00;
+	msg.data[4] = 0x20; /* IDLo MSB */
+	msg.data[5] = 0xff; /* IDHi LSB */
+	msg.data[6] = 0xff;
+	msg.data[7] = 0xff;
+	msg.data[8] = 0x3f; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Bring the CAN bus online or offline
+ */
+static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_termination(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_HWCONF;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_send_inquiry(struct ican3_dev *mod, u8 subspec)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_INQUIRY;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = subspec;
+	msg.data[1] = 0x00;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_buserror(struct ican3_dev *mod, u8 quota)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CCONFREQ;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = quota;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * ICAN3 to Linux CAN Frame Conversion
+ */
+
+static void ican3_to_can_frame(struct ican3_dev *mod,
+			       struct ican3_fast_desc *desc,
+			       struct can_frame *cf)
+{
+	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
+		if (desc->data[1] & ICAN3_SFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		cf->can_id |= desc->data[0] << 3;
+		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
+		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
+		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
+	} else {
+		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
+		if (desc->data[0] & ICAN3_EFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		if (desc->data[0] & ICAN3_EFF) {
+			cf->can_id |= CAN_EFF_FLAG;
+			cf->can_id |= desc->data[2] << 21; /* 28-21 */
+			cf->can_id |= desc->data[3] << 13; /* 20-13 */
+			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
+			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
+		} else {
+			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
+			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
+		}
+
+		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
+	}
+}
+
+static void can_frame_to_ican3(struct ican3_dev *mod,
+			       struct can_frame *cf,
+			       struct ican3_fast_desc *desc)
+{
+	/* clear out any stale data in the descriptor */
+	memset(desc->data, 0, sizeof(desc->data));
+
+	/* we always use the extended format, with the ECHO flag set */
+	desc->command = ICAN3_CAN_TYPE_EFF;
+	desc->data[0] |= cf->can_dlc;
+	desc->data[1] |= ICAN3_ECHO;
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		desc->data[0] |= ICAN3_EFF_RTR;
+
+	/* pack the id into the correct places */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		desc->data[0] |= ICAN3_EFF;
+		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
+		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
+		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
+		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
+	} else {
+		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
+		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
+	}
+
+	/* copy the data bits into the descriptor */
+	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/*
+ * Handle an ID + Version message response from the firmware. We never generate
+ * this message in production code, but it is very useful when debugging to be
+ * able to display this message.
+ */
+static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
+}
+
+static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/*
+	 * Report that communication messages with the microcontroller firmware
+	 * are being lost. These are never CAN frames, so we do not generate an
+	 * error frame for userspace
+	 */
+	if (msg->spec == MSG_MSGLOST) {
+		dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
+		return;
+	}
+
+	/*
+	 * Oops, this indicates that we have lost messages in the fast queue,
+	 * which are exclusively CAN messages. Our driver isn't reading CAN
+	 * frames fast enough.
+	 *
+	 * We'll pretend that the SJA1000 told us that it ran out of buffer
+	 * space, because there is not a better message for this.
+	 */
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_errors++;
+		stats->rx_bytes += cf->can_dlc;
+		netif_rx(skb);
+	}
+}
+
+/*
+ * Handle CAN Event Indication Messages from the firmware
+ *
+ * The ICAN3 firmware provides the values of some SJA1000 registers when it
+ * generates this message. The code below is largely copied from the
+ * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
+ */
+static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	enum can_state state = mod->can.state;
+	u8 status, isrc, rxerr, txerr;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/* we can only handle the SJA1000 part */
+	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
+		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
+		return -ENODEV;
+	}
+
+	/* check the message length for sanity */
+	if (le16_to_cpu(msg->len) < 6) {
+		dev_err(mod->dev, "error message too short\n");
+		return -EINVAL;
+	}
+
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	isrc = msg->data[0];
+	status = msg->data[3];
+	rxerr = msg->data[4];
+	txerr = msg->data[5];
+
+	/* data overrun interrupt */
+	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
+		dev_dbg(mod->dev, "data overrun interrupt\n");
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+	}
+
+	/* error warning + passive interrupt */
+	if (isrc == CEVTIND_EI) {
+		dev_dbg(mod->dev, "error warning + passive interrupt\n");
+		if (status & SR_BS) {
+			state = CAN_STATE_BUS_OFF;
+			cf->can_id |= CAN_ERR_BUSOFF;
+			can_bus_off(dev);
+		} else if (status & SR_ES) {
+			if (rxerr >= 128 || txerr >= 128)
+				state = CAN_STATE_ERROR_PASSIVE;
+			else
+				state = CAN_STATE_ERROR_WARNING;
+		} else {
+			state = CAN_STATE_ERROR_ACTIVE;
+		}
+	}
+
+	/* bus error interrupt */
+	if (isrc == CEVTIND_BEI) {
+		u8 ecc = msg->data[2];
+
+		dev_dbg(mod->dev, "bus error interrupt\n");
+		mod->can.can_stats.bus_error++;
+		stats->rx_errors++;
+		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+		switch (ecc & ECC_MASK) {
+		case ECC_BIT:
+			cf->data[2] |= CAN_ERR_PROT_BIT;
+			break;
+		case ECC_FORM:
+			cf->data[2] |= CAN_ERR_PROT_FORM;
+			break;
+		case ECC_STUFF:
+			cf->data[2] |= CAN_ERR_PROT_STUFF;
+			break;
+		default:
+			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+			cf->data[3] = ecc & ECC_SEG;
+			break;
+		}
+
+		if ((ecc & ECC_DIR) == 0)
+			cf->data[2] |= CAN_ERR_PROT_TX;
+
+		cf->data[6] = txerr;
+		cf->data[7] = rxerr;
+	}
+
+	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
+					state == CAN_STATE_ERROR_PASSIVE)) {
+		cf->can_id |= CAN_ERR_CRTL;
+		if (state == CAN_STATE_ERROR_WARNING) {
+			mod->can.can_stats.error_warning++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_WARNING :
+				CAN_ERR_CRTL_RX_WARNING;
+		} else {
+			mod->can.can_stats.error_passive++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_PASSIVE :
+				CAN_ERR_CRTL_RX_PASSIVE;
+		}
+
+		cf->data[6] = txerr;
+		cf->data[7] = rxerr;
+	}
+
+	mod->can.state = state;
+	stats->rx_errors++;
+	stats->rx_bytes += cf->can_dlc;
+	netif_rx(skb);
+	return 0;
+}
+
+static void ican3_handle_inquiry(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	switch (msg->data[0]) {
+	case INQUIRY_STATUS:
+	case INQUIRY_EXTENDED:
+		mod->bec.rxerr = msg->data[5];
+		mod->bec.txerr = msg->data[6];
+		complete(&mod->buserror_comp);
+		break;
+	case INQUIRY_TERMINATION:
+		mod->termination_enabled = msg->data[6] & HWCONF_TERMINATE_ON;
+		complete(&mod->termination_comp);
+		break;
+	default:
+		dev_err(mod->dev, "recieved an unknown inquiry response\n");
+		break;
+	}
+}
+
+static void ican3_handle_unknown_message(struct ican3_dev *mod,
+					struct ican3_msg *msg)
+{
+	dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
+			   msg->spec, le16_to_cpu(msg->len));
+}
+
+/*
+ * Handle a control message from the firmware
+ */
+static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
+			   mod->num, msg->spec, le16_to_cpu(msg->len));
+
+	switch (msg->spec) {
+	case MSG_IDVERS:
+		ican3_handle_idvers(mod, msg);
+		break;
+	case MSG_MSGLOST:
+	case MSG_FMSGLOST:
+		ican3_handle_msglost(mod, msg);
+		break;
+	case MSG_CEVTIND:
+		ican3_handle_cevtind(mod, msg);
+		break;
+	case MSG_INQUIRY:
+		ican3_handle_inquiry(mod, msg);
+		break;
+	default:
+		ican3_handle_unknown_message(mod, msg);
+		break;
+	}
+}
+
+/*
+ * Check that there is room in the TX ring to transmit another skb
+ *
+ * LOCKING: must hold mod->lock
+ */
+static bool ican3_txok(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc __iomem *desc;
+	u8 control;
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
+	control = ioread8(&desc->control);
+
+	/* if the control bits are not valid, then we have no more space */
+	if (!(control & DESC_VALID))
+		return false;
+
+	return true;
+}
+
+/*
+ * Recieve one CAN frame from the hardware
+ *
+ * This works like the core of a NAPI function, but is intended to be called
+ * from workqueue context instead. This driver already needs a workqueue to
+ * process control messages, so we use the workqueue instead of using NAPI.
+ * This was done to simplify locking.
+ *
+ * CONTEXT: must be called from user context
+ */
+static int ican3_recv_skb(struct ican3_dev *mod)
+{
+	struct net_device *ndev = mod->ndev;
+	struct net_device_stats *stats = &ndev->stats;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* copy the whole descriptor */
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* check that we actually have a CAN frame */
+	if (!(desc.control & DESC_VALID))
+		return -ENOBUFS;
+
+	/* allocate an skb */
+	skb = alloc_can_skb(ndev, &cf);
+	if (unlikely(skb == NULL)) {
+		stats->rx_dropped++;
+		goto err_noalloc;
+	}
+
+	/* convert the ICAN3 frame into Linux CAN format */
+	ican3_to_can_frame(mod, &desc, cf);
+
+	/* receive the skb, update statistics */
+	netif_receive_skb(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+
+err_noalloc:
+	/* toggle the valid bit and return the descriptor to the ring */
+	desc.control ^= DESC_VALID;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	memcpy_toio(desc_addr, &desc, 1);
+
+	/* update the next buffer pointer */
+	mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fastrx_num + 1);
+
+	/* there are still more buffers to process */
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return 0;
+}
+
+static int ican3_napi(struct napi_struct *napi, int budget)
+{
+	struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
+	struct ican3_msg msg;
+	unsigned long flags;
+	int received = 0;
+	int ret;
+
+	/* process all communication messages */
+	while (true) {
+		ret = ican3_recv_msg(mod, &msg);
+		if (ret)
+			break;
+
+		ican3_handle_message(mod, &msg);
+	}
+
+	/* process all CAN frames from the fast interface */
+	while (received < budget) {
+		ret = ican3_recv_skb(mod);
+		if (ret)
+			break;
+
+		received++;
+	}
+
+	/* We have processed all packets that the adapter had, but it
+	 * was less than our budget, stop polling */
+	if (received < budget)
+		napi_complete(napi);
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* Wake up the transmit queue if necessary */
+	if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
+		netif_wake_queue(mod->ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* re-enable interrupt generation */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+	return received;
+}
+
+static irqreturn_t ican3_irq(int irq, void *dev_id)
+{
+	struct ican3_dev *mod = dev_id;
+	u8 stat;
+
+	/*
+	 * The interrupt status register on this device reports interrupts
+	 * as zeroes instead of using ones like most other devices
+	 */
+	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
+	if (stat == (1 << mod->num))
+		return IRQ_NONE;
+
+	/* clear the MODULbus interrupt from the microcontroller */
+	ioread8(&mod->dpmctrl->interrupt);
+
+	/* disable interrupt generation, schedule the NAPI poller */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	napi_schedule(&mod->napi);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Firmware reset, startup, and shutdown
+ */
+
+/*
+ * Reset an ICAN module to its power-on state
+ *
+ * CONTEXT: no network device registered
+ * LOCKING: work function disabled
+ */
+static int ican3_reset_module(struct ican3_dev *mod)
+{
+	u8 val = 1 << mod->num;
+	unsigned long start;
+	u8 runold, runnew;
+
+	/* disable interrupts so no more work is scheduled */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+
+	/* flush any pending work */
+	flush_scheduled_work();
+
+	/* the first unallocated page in the DPM is #9 */
+	mod->free_page = DPM_FREE_START;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	runold = ioread8(mod->dpm + TARGET_RUNNING);
+
+	/* reset the module */
+	iowrite8(val, &mod->ctrl->reset_assert);
+	iowrite8(val, &mod->ctrl->reset_deassert);
+
+	/* wait until the module has finished resetting and is running */
+	start = jiffies;
+	do {
+		ican3_set_page(mod, QUEUE_OLD_CONTROL);
+		runnew = ioread8(mod->dpm + TARGET_RUNNING);
+		if (runnew == (runold ^ 0xff))
+			return 0;
+
+		msleep(10);
+	} while (time_before(jiffies, start + HZ / 4));
+
+	dev_err(mod->dev, "failed to reset CAN module\n");
+	return -ETIMEDOUT;
+}
+
+static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
+{
+	ican3_msg_disconnect(mod);
+	ican3_reset_module(mod);
+}
+
+/*
+ * Startup an ICAN module, bringing it into fast mode
+ */
+static int __devinit ican3_startup_module(struct ican3_dev *mod)
+{
+	int ret;
+
+	ret = ican3_reset_module(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to reset module\n");
+		return ret;
+	}
+
+	/* re-enable interrupts so we can send messages */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+
+	ret = ican3_msg_connect(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to connect to module\n");
+		return ret;
+	}
+
+	ican3_init_new_host_interface(mod);
+	ret = ican3_msg_newhostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to new-style interface\n");
+		return ret;
+	}
+
+	/* default to "termination on" */
+	ret = ican3_set_termination(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to enable termination\n");
+		return ret;
+	}
+
+	/* default to "bus errors enabled" */
+	ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-error\n");
+		return ret;
+	}
+
+	ican3_init_fast_host_interface(mod);
+	ret = ican3_msg_fasthostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to fast host interface\n");
+		return ret;
+	}
+
+	ret = ican3_set_id_filter(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set acceptance filter\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * CAN Network Device
+ */
+
+static int ican3_open(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	u8 quota;
+	int ret;
+
+	/* open the CAN layer */
+	ret = open_candev(ndev);
+	if (ret) {
+		dev_err(mod->dev, "unable to start CAN layer\n");
+		return ret;
+	}
+
+	/* set the bus error generation state appropriately */
+	if (mod->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		quota = ICAN3_BUSERR_QUOTA_MAX;
+	else
+		quota = 0;
+
+	ret = ican3_set_buserror(mod, quota);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-error\n");
+		close_candev(ndev);
+		return ret;
+	}
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		close_candev(ndev);
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+	netif_start_queue(ndev);
+
+	return 0;
+}
+
+static int ican3_stop(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	/* stop the network device xmit routine */
+	netif_stop_queue(ndev);
+	mod->can.state = CAN_STATE_STOPPED;
+
+	/* bring the bus offline, stop receiving packets */
+	ret = ican3_set_bus_state(mod, false);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-off\n");
+		return ret;
+	}
+
+	/* close the CAN layer */
+	close_candev(ndev);
+	return 0;
+}
+
+static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* check that we can actually transmit */
+	if (!ican3_txok(mod)) {
+		dev_err(mod->dev, "no free descriptors, stopping queue\n");
+		netif_stop_queue(ndev);
+		spin_unlock_irqrestore(&mod->lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
+	memset(&desc, 0, sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, 1);
+
+	/* convert the Linux CAN frame into ICAN3 format */
+	can_frame_to_ican3(mod, cf, &desc);
+
+	/*
+	 * the programming manual says that you must set the IVALID bit, then
+	 * interrupt, then set the valid bit. Quite weird, but it seems to be
+	 * required for this to work
+	 */
+	desc.control |= DESC_IVALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* generate a MODULbus interrupt to the microcontroller */
+	iowrite8(0x01, &mod->dpmctrl->interrupt);
+
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the next buffer pointer */
+	mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fasttx_num + 1);
+
+	/* update statistics */
+	stats->tx_packets++;
+	stats->tx_bytes += cf->can_dlc;
+	kfree_skb(skb);
+
+	/*
+	 * This hardware doesn't have TX-done notifications, so we'll try and
+	 * emulate it the best we can using ECHO skbs. Get the next TX
+	 * descriptor, and see if we have room to send. If not, stop the queue.
+	 * It will be woken when the ECHO skb for the current packet is recv'd.
+	 */
+
+	/* copy the control bits of the descriptor */
+	if (!ican3_txok(mod))
+		netif_stop_queue(ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ican3_netdev_ops = {
+	.ndo_open	= ican3_open,
+	.ndo_stop	= ican3_stop,
+	.ndo_start_xmit	= ican3_xmit,
+};
+
+/*
+ * Low-level CAN Device
+ */
+
+/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
+static struct can_bittiming_const ican3_bittiming_const = {
+	.name = DRV_NAME,
+	.tseg1_min = 1,
+	.tseg1_max = 16,
+	.tseg2_min = 1,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 64,
+	.brp_inc = 1,
+};
+
+/*
+ * This routine was stolen from drivers/net/can/sja1000/sja1000.c
+ *
+ * The bittiming register command for the ICAN3 just sets the bit timing
+ * registers on the SJA1000 chip directly
+ */
+static int ican3_set_bittiming(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct can_bittiming *bt = &mod->can.bittiming;
+	struct ican3_msg msg;
+	u8 btr0, btr1;
+
+	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+		(((bt->phase_seg2 - 1) & 0x7) << 4);
+	if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btr1 |= 0x80;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CBTRREQ;
+	msg.len = cpu_to_le16(4);
+	msg.data[0] = 0x00;
+	msg.data[1] = 0x00;
+	msg.data[2] = btr0;
+	msg.data[3] = btr1;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	if (mode != CAN_MODE_START)
+		return -ENOTSUPP;
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	if (netif_queue_stopped(ndev))
+		netif_wake_queue(ndev);
+
+	return 0;
+}
+
+static int ican3_get_berr_counter(const struct net_device *ndev,
+				  struct can_berr_counter *bec)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	ret = ican3_send_inquiry(mod, INQUIRY_STATUS);
+	if (ret)
+		return ret;
+
+	ret = wait_for_completion_timeout(&mod->buserror_comp, HZ);
+	if (ret <= 0) {
+		dev_info(mod->dev, "%s timed out\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	bec->rxerr = mod->bec.rxerr;
+	bec->txerr = mod->bec.txerr;
+	return 0;
+}
+
+/*
+ * Sysfs Attributes
+ */
+
+static ssize_t ican3_sysfs_show_term(struct device *dev,
+				     struct device_attribute *attr,
+				     char *buf)
+{
+	struct ican3_dev *mod = netdev_priv(to_net_dev(dev));
+	int ret;
+
+	ret = ican3_send_inquiry(mod, INQUIRY_TERMINATION);
+	if (ret)
+		return ret;
+
+	ret = wait_for_completion_timeout(&mod->termination_comp, HZ);
+	if (ret <= 0) {
+		dev_info(mod->dev, "%s timed out\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%u\n", mod->termination_enabled);
+}
+
+static ssize_t ican3_sysfs_set_term(struct device *dev,
+				    struct device_attribute *attr,
+				    const char *buf, size_t count)
+{
+	struct ican3_dev *mod = netdev_priv(to_net_dev(dev));
+	unsigned long enable;
+	int ret;
+
+	if (strict_strtoul(buf, 0, &enable))
+		return -EINVAL;
+
+	ret = ican3_set_termination(mod, enable);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+static DEVICE_ATTR(termination, S_IWUGO | S_IRUGO, ican3_sysfs_show_term,
+						   ican3_sysfs_set_term);
+
+static struct attribute *ican3_sysfs_attrs[] = {
+	&dev_attr_termination.attr,
+	NULL,
+};
+
+static struct attribute_group ican3_sysfs_attr_group = {
+	.attrs = ican3_sysfs_attrs,
+};
+
+/*
+ * PCI Subsystem
+ */
+
+static int __devinit ican3_probe(struct platform_device *pdev)
+{
+	struct janz_platform_data *pdata;
+	struct net_device *ndev;
+	struct ican3_dev *mod;
+	struct resource *res;
+	struct device *dev;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENXIO;
+
+	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
+
+	/* save the struct device for printing */
+	dev = &pdev->dev;
+
+	/* allocate the CAN device and private data */
+	ndev = alloc_candev(sizeof(*mod), 0);
+	if (!ndev) {
+		dev_err(dev, "unable to allocate CANdev\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	platform_set_drvdata(pdev, ndev);
+	mod = netdev_priv(ndev);
+	mod->ndev = ndev;
+	mod->dev = &pdev->dev;
+	mod->num = pdata->modno;
+	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
+	spin_lock_init(&mod->lock);
+	init_completion(&mod->termination_comp);
+	init_completion(&mod->buserror_comp);
+
+	/* setup device-specific sysfs attributes */
+	ndev->sysfs_groups[0] = &ican3_sysfs_attr_group;
+
+	/* the first unallocated page in the DPM is 9 */
+	mod->free_page = DPM_FREE_START;
+
+	ndev->netdev_ops = &ican3_netdev_ops;
+	ndev->flags |= IFF_ECHO;
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	mod->can.clock.freq = ICAN3_CAN_CLOCK;
+	mod->can.bittiming_const = &ican3_bittiming_const;
+	mod->can.do_set_bittiming = ican3_set_bittiming;
+	mod->can.do_set_mode = ican3_set_mode;
+	mod->can.do_get_berr_counter = ican3_get_berr_counter;
+	mod->can.ctrlmode_supported = CAN_CTRLMODE_3_SAMPLES
+				    | CAN_CTRLMODE_BERR_REPORTING;
+
+	/* find our IRQ number */
+	mod->irq = platform_get_irq(pdev, 0);
+	if (mod->irq < 0) {
+		dev_err(dev, "IRQ line not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	ndev->irq = mod->irq;
+
+	/* get access to the MODULbus registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "MODULbus registers not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	mod->dpm = ioremap(res->start, resource_size(res));
+	if (!mod->dpm) {
+		dev_err(dev, "MODULbus registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_free_ndev;
+	}
+
+	mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
+
+	/* get access to the control registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(dev, "CONTROL registers not found\n");
+		ret = -ENODEV;
+		goto out_iounmap_dpm;
+	}
+
+	mod->ctrl = ioremap(res->start, resource_size(res));
+	if (!mod->ctrl) {
+		dev_err(dev, "CONTROL registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_iounmap_dpm;
+	}
+
+	/* disable our IRQ, then hookup the IRQ handler */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
+	if (ret) {
+		dev_err(dev, "unable to request IRQ\n");
+		goto out_iounmap_ctrl;
+	}
+
+	/* reset and initialize the CAN controller into fast mode */
+	napi_enable(&mod->napi);
+	ret = ican3_startup_module(mod);
+	if (ret) {
+		dev_err(dev, "%s: unable to start CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	/* register with the Linux CAN layer */
+	ret = register_candev(ndev);
+	if (ret) {
+		dev_err(dev, "%s: unable to register CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
+	return 0;
+
+out_free_irq:
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+out_iounmap_ctrl:
+	iounmap(mod->ctrl);
+out_iounmap_dpm:
+	iounmap(mod->dpm);
+out_free_ndev:
+	free_candev(ndev);
+out_return:
+	return ret;
+}
+
+static int __devexit ican3_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct ican3_dev *mod = netdev_priv(ndev);
+
+	/* unregister the netdevice, stop interrupts */
+	unregister_netdev(ndev);
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+
+	/* put the module into reset */
+	ican3_shutdown_module(mod);
+
+	/* unmap all registers */
+	iounmap(mod->ctrl);
+	iounmap(mod->dpm);
+
+	free_candev(ndev);
+
+	return 0;
+}
+
+static struct platform_driver ican3_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ican3_probe,
+	.remove		= __devexit_p(ican3_remove),
+};
+
+static int __init ican3_init(void)
+{
+	return platform_driver_register(&ican3_driver);
+}
+
+static void __exit ican3_exit(void)
+{
+	platform_driver_unregister(&ican3_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ican3");
+
+module_init(ican3_init);
+module_exit(ican3_exit);
-- 
1.5.4.3

^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-30  8:14   ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-30  8:14 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core, netdev, sameo

Ira W. Snyder wrote:
> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> MODULbus carrier board. It is an intelligent CAN controller with a
> microcontroller and associated firmware.
> 
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>

Acked-by: Wolfgang Grandegger <wg@grandegger.com>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-30  8:14   ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-30  8:14 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Ira W. Snyder wrote:
> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> MODULbus carrier board. It is an intelligent CAN controller with a
> microcontroller and associated firmware.
> 
> Signed-off-by: Ira W. Snyder <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org>

Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-30  8:14   ` Wolfgang Grandegger
  (?)
@ 2010-03-31  6:46   ` David Miller
  -1 siblings, 0 replies; 41+ messages in thread
From: David Miller @ 2010-03-31  6:46 UTC (permalink / raw)
  To: wg; +Cc: iws, linux-kernel, socketcan-core, netdev, sameo

From: Wolfgang Grandegger <wg@grandegger.com>
Date: Tue, 30 Mar 2010 10:14:18 +0200

> Ira W. Snyder wrote:
>> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
>> MODULbus carrier board. It is an intelligent CAN controller with a
>> microcontroller and associated firmware.
>> 
>> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> 
> Acked-by: Wolfgang Grandegger <wg@grandegger.com>

Since this driver depends upon the MFD stuff and that goes
through other maintainers, just toss this CAN driver in
via whatever tree the MFD thing goes through and add my:

Acked-by: David S. Miller <davem@davemloft.net>

Thanks!

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-29 16:58 ` Ira W. Snyder
  (?)
  (?)
@ 2010-04-01 20:03 ` Andrew Morton
  2010-04-02  0:43     ` Ira W. Snyder
  -1 siblings, 1 reply; 41+ messages in thread
From: Andrew Morton @ 2010-04-01 20:03 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core, netdev, sameo

On Mon, 29 Mar 2010 09:58:51 -0700
"Ira W. Snyder" <iws@ovro.caltech.edu> wrote:

> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> MODULbus carrier board. It is an intelligent CAN controller with a
> microcontroller and associated firmware.
> 

A neat-looking driver.

> ...
>
> +	spin_lock_irqsave(&mod->lock, flags);
>
> ...

It does this rather a lot.  it seems to be doing quite a lot of work
under that lock, too - quite a lot of memcpy_toio(), other stuff.

Is there potential here to disable interrupt for too long?  Not
possible to use spin_lock_bh() here?


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-04-02  0:43     ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-04-02  0:43 UTC (permalink / raw)
  To: Andrew Morton; +Cc: linux-kernel, socketcan-core, netdev, sameo

On Thu, Apr 01, 2010 at 01:03:59PM -0700, Andrew Morton wrote:
> On Mon, 29 Mar 2010 09:58:51 -0700
> "Ira W. Snyder" <iws@ovro.caltech.edu> wrote:
> 
> > The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> > MODULbus carrier board. It is an intelligent CAN controller with a
> > microcontroller and associated firmware.
> > 
> 
> A neat-looking driver.
> 
> > ...
> >
> > +	spin_lock_irqsave(&mod->lock, flags);
> >
> > ...
> 
> It does this rather a lot.  it seems to be doing quite a lot of work
> under that lock, too - quite a lot of memcpy_toio(), other stuff.
> 

Like most similar cards, the host computer communicates to the
microcontroller through a dual ported memory (DPM) interface. In this
card, it is split into 256x 256 byte pages/windows.

The lock ensures that once code sets a window, it doesn't change while
the memcpy/iowrite happens.

> Is there potential here to disable interrupt for too long?  Not
> possible to use spin_lock_bh() here?
> 

The largest possible memcpy_(to|from)io() in the driver is 256 bytes.
Not too huge, but I understand the concern.

Looking at this again, I don't take the lock in the interrupt handler
(nor do I need to). What contexts do the network driver's xmit() and
napi() routines run in? hardirq and softirq respectively, right? In that
case, I think spin_lock_bh() is probably enough.

Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-04-02  0:43     ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-04-02  0:43 UTC (permalink / raw)
  To: Andrew Morton
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

On Thu, Apr 01, 2010 at 01:03:59PM -0700, Andrew Morton wrote:
> On Mon, 29 Mar 2010 09:58:51 -0700
> "Ira W. Snyder" <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org> wrote:
> 
> > The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> > MODULbus carrier board. It is an intelligent CAN controller with a
> > microcontroller and associated firmware.
> > 
> 
> A neat-looking driver.
> 
> > ...
> >
> > +	spin_lock_irqsave(&mod->lock, flags);
> >
> > ...
> 
> It does this rather a lot.  it seems to be doing quite a lot of work
> under that lock, too - quite a lot of memcpy_toio(), other stuff.
> 

Like most similar cards, the host computer communicates to the
microcontroller through a dual ported memory (DPM) interface. In this
card, it is split into 256x 256 byte pages/windows.

The lock ensures that once code sets a window, it doesn't change while
the memcpy/iowrite happens.

> Is there potential here to disable interrupt for too long?  Not
> possible to use spin_lock_bh() here?
> 

The largest possible memcpy_(to|from)io() in the driver is 256 bytes.
Not too huge, but I understand the concern.

Looking at this again, I don't take the lock in the interrupt handler
(nor do I need to). What contexts do the network driver's xmit() and
napi() routines run in? hardirq and softirq respectively, right? In that
case, I think spin_lock_bh() is probably enough.

Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 21:24                           ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 21:24 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: socketcan-core, netdev, linux-kernel, sameo

Ira W. Snyder wrote:
> On Mon, Mar 22, 2010 at 09:28:25PM +0100, Wolfgang Grandegger wrote:
> 
> [ big snip ]
> 
>> You could even add the tx/rx values for each error message (for both,
>> state changes and bus-errors).
>>
> 
> Ok, with that change, I get the following:
> 
> berr-reporting on:
> 
>   can0  20000088  [8] 00 00 80 19 00 00 08 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 10 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 18 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 20 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 28 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 30 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 38 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 40 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 48 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 50 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 58 00   ERRORFRAME
>   can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 60 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 68 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 70 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 78 00   ERRORFRAME
>   can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME
> 
> And now lots more of this last frame repeated, until the controller
> decides to stop. Seems fine. It has always done this.
> 
> berr-reporting off:
> 
>   can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>   can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
> 
> 
> Same as before. Excellent.

Yes, below is some more theory from the AT91 CAN manual, in case you are
interested in technical details.

Wolfgang.

-----------------------------------------------------------------------
o REC: Receive Error Counter
  When a receiver detects an error, REC will be increased by one, except
  when the detected error is a BIT ERROR while sending an ACTIVE ERROR
  FLAG or an OVERLOAD FLAG. When a receiver detects a dominant bit as
  the first bit after sending an ERROR FLAG, REC is increased by 8.
  When a receiver detects a BIT ERROR while sending an ACTIVE ERROR
  FLAG, REC is increased by 8. Any node tolerates up to 7 consecutive
  dominant bits after sending an ACTIVE ERROR FLAG, PASSIVE ERROR FLAG
  or OVERLOAD FLAG. After detecting the 14th consecutive dominant bit
  (in case of an ACTIVE ERROR FLAG or an OVER-LOAD FLAG) or after
  detecting the 8th consecutive dominant bit following a PASSIVE ERROR
  FLAG, and after each sequence of additional eight consecutive dominant
  bits, each receiver increases its REC by 8. After successful reception
  of a message, REC is decreased by 1 if it was between 1 and 127. If
  REC was 0, it stays 0, and if it was greater than 127, then it is set
  to a value between 119 and 127.

o TEC: Transmit Error Counter
  When a transmitter sends an ERROR FLAG, TEC is increased by 8 except
  when:
  - the transmitter is error passive and detects an ACKNOWLEDGMENT ERROR
    because of not detecting a dominant ACK and does not detect a
    dominant bit while sending its PASSIVE ERROR FLAG.
  - the transmitter sends an ERROR FLAG because a STUFF ERROR occurred
    during arbitration and should have been recessive and has been sent
    as recessive but monitored as dominant.
  When a transmitter detects a BIT ERROR while sending an ACTIVE ERROR
  FLAG or an OVERLOAD FLAG, the TEC will be increased by 8.
  Any node tolerates up to 7 consecutive dominant bits after sending an
  ACTIVE ERROR FLAG, PASSIVE ERROR FLAG or OVERLOAD FLAG. After
  detecting the 14th consecutive dominant bit (in case of an ACTIVE
  ERROR FLAG or an OVERLOAD FLAG) or after detecting the 8th consecutive
  dominant bit following a PASSIVE ERROR FLAG, and after each
  sequence of additional eight consecutive dominant bits every
  transmitter increases its TEC by 8. After a successful transmission
  the TEC is decreased by 1 unless it was already 0.


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 21:24                           ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 21:24 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Ira W. Snyder wrote:
> On Mon, Mar 22, 2010 at 09:28:25PM +0100, Wolfgang Grandegger wrote:
> 
> [ big snip ]
> 
>> You could even add the tx/rx values for each error message (for both,
>> state changes and bus-errors).
>>
> 
> Ok, with that change, I get the following:
> 
> berr-reporting on:
> 
>   can0  20000088  [8] 00 00 80 19 00 00 08 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 10 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 18 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 20 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 28 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 30 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 38 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 40 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 48 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 50 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 58 00   ERRORFRAME
>   can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 60 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 68 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 70 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 78 00   ERRORFRAME
>   can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME
>   can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME
> 
> And now lots more of this last frame repeated, until the controller
> decides to stop. Seems fine. It has always done this.
> 
> berr-reporting off:
> 
>   can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>   can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
> 
> 
> Same as before. Excellent.

Yes, below is some more theory from the AT91 CAN manual, in case you are
interested in technical details.

Wolfgang.

-----------------------------------------------------------------------
o REC: Receive Error Counter
  When a receiver detects an error, REC will be increased by one, except
  when the detected error is a BIT ERROR while sending an ACTIVE ERROR
  FLAG or an OVERLOAD FLAG. When a receiver detects a dominant bit as
  the first bit after sending an ERROR FLAG, REC is increased by 8.
  When a receiver detects a BIT ERROR while sending an ACTIVE ERROR
  FLAG, REC is increased by 8. Any node tolerates up to 7 consecutive
  dominant bits after sending an ACTIVE ERROR FLAG, PASSIVE ERROR FLAG
  or OVERLOAD FLAG. After detecting the 14th consecutive dominant bit
  (in case of an ACTIVE ERROR FLAG or an OVER-LOAD FLAG) or after
  detecting the 8th consecutive dominant bit following a PASSIVE ERROR
  FLAG, and after each sequence of additional eight consecutive dominant
  bits, each receiver increases its REC by 8. After successful reception
  of a message, REC is decreased by 1 if it was between 1 and 127. If
  REC was 0, it stays 0, and if it was greater than 127, then it is set
  to a value between 119 and 127.

o TEC: Transmit Error Counter
  When a transmitter sends an ERROR FLAG, TEC is increased by 8 except
  when:
  - the transmitter is error passive and detects an ACKNOWLEDGMENT ERROR
    because of not detecting a dominant ACK and does not detect a
    dominant bit while sending its PASSIVE ERROR FLAG.
  - the transmitter sends an ERROR FLAG because a STUFF ERROR occurred
    during arbitration and should have been recessive and has been sent
    as recessive but monitored as dominant.
  When a transmitter detects a BIT ERROR while sending an ACTIVE ERROR
  FLAG or an OVERLOAD FLAG, the TEC will be increased by 8.
  Any node tolerates up to 7 consecutive dominant bits after sending an
  ACTIVE ERROR FLAG, PASSIVE ERROR FLAG or OVERLOAD FLAG. After
  detecting the 14th consecutive dominant bit (in case of an ACTIVE
  ERROR FLAG or an OVERLOAD FLAG) or after detecting the 8th consecutive
  dominant bit following a PASSIVE ERROR FLAG, and after each
  sequence of additional eight consecutive dominant bits every
  transmitter increases its TEC by 8. After a successful transmission
  the TEC is decreased by 1 unless it was already 0.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:51                         ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 20:51 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: socketcan-core, netdev, linux-kernel, sameo

On Mon, Mar 22, 2010 at 09:28:25PM +0100, Wolfgang Grandegger wrote:

[ big snip ]

> 
> You could even add the tx/rx values for each error message (for both,
> state changes and bus-errors).
> 

Ok, with that change, I get the following:

berr-reporting on:

  can0  20000088  [8] 00 00 80 19 00 00 08 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 10 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 18 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 20 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 28 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 30 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 38 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 40 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 48 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 50 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 58 00   ERRORFRAME
  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 60 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 68 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 70 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 78 00   ERRORFRAME
  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME

And now lots more of this last frame repeated, until the controller
decides to stop. Seems fine. It has always done this.

berr-reporting off:

  can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
  can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME


Same as before. Excellent.

> > I haven't heard back from Samuel Ortiz yet about the changes for the mfd
> > layer. Would you like me to send out my latest CAN driver changes, or
> > should I just wait until I hear back?
> 
> As you need patch 1/3 anyway, just wait some more time. From my point of
>  view the next version of the patch will be OK.
> 

Ok, I'll wait a few more days before pinging him again. He's CC'd on all
of these emails anyway. :)

Thanks for all the help,
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:51                         ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 20:51 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

On Mon, Mar 22, 2010 at 09:28:25PM +0100, Wolfgang Grandegger wrote:

[ big snip ]

> 
> You could even add the tx/rx values for each error message (for both,
> state changes and bus-errors).
> 

Ok, with that change, I get the following:

berr-reporting on:

  can0  20000088  [8] 00 00 80 19 00 00 08 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 10 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 18 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 20 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 28 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 30 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 38 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 40 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 48 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 50 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 58 00   ERRORFRAME
  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 60 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 68 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 70 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 78 00   ERRORFRAME
  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME
  can0  20000088  [8] 00 00 80 19 00 00 80 00   ERRORFRAME

And now lots more of this last frame repeated, until the controller
decides to stop. Seems fine. It has always done this.

berr-reporting off:

  can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
  can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME


Same as before. Excellent.

> > I haven't heard back from Samuel Ortiz yet about the changes for the mfd
> > layer. Would you like me to send out my latest CAN driver changes, or
> > should I just wait until I hear back?
> 
> As you need patch 1/3 anyway, just wait some more time. From my point of
>  view the next version of the patch will be OK.
> 

Ok, I'll wait a few more days before pinging him again. He's CC'd on all
of these emails anyway. :)

Thanks for all the help,
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:28                       ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 20:28 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: socketcan-core, netdev, linux-kernel, sameo

Ira W. Snyder wrote:
> On Mon, Mar 22, 2010 at 08:17:10PM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
>>> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
>>>> Ira W. Snyder wrote:
>> [snip]
>>>>> Does this seem right? It seems pretty good to me.
>>>> Yes, I'm just missing an error-passive message. What state does "ip -d
>>>> link show can0" report.
>>>>
>>> Ok, here is what I did:
>>>
>>> $ ip link set can0 up type can bitrate 1000000
>>> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
>>> $ ip -d -s link
>>> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can       
>>>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>>     bitrate 1000000 sample-point 0.750
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000  
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          0          0          0          0          0
>>>     RX: bytes  packets  errors  dropped overrun mcast
>>>     0          0        0       0       0       0
>>>     TX: bytes  packets  errors  dropped carrier collsns
>>>     0          0        0       0       0       0
>>> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can       
>>>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>>     bitrate 1000000 sample-point 0.750
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000  
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          0          0          0          0          0
>>>     RX: bytes  packets  errors  dropped overrun mcast
>>>     0          0        0       0       0       0
>>>     TX: bytes  packets  errors  dropped carrier collsns
>>>     0          0        0       0       0       0
>>>
>>> Now, in seperate windows, I ran cansequence and candump. I stopped
>>> cansequence when it could not send any more packets (due to the cable
>>> being unplugged).
>>>
>>> $ cansequence -v -e -p can0
>>> $ cansequence -v -e -p can1
>>> $ candump any,0~0,#FFFFFFFF
>>>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>
>>> This last message is repeated lots more times. That's the flooding we're
>>> avoiding with berr-reporting off.
>>>
>>> I see two types of messages here:
>>> 1) bus error (only on can1)
>>> 2) controller problems -- tx warning limit reached (both)
>>>
>>> Am I missing some message? My error frame generation was mostly copied
>>> from the sja1000 driver.
>> It seem that you are not getting the error passive interrupt even...
>>
>>> $ ip -d -s link
>>> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can 
>>>     can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
>> if the hardware already reports >= 128 errors --^.
>>
> 
> Re-reading the documentation, it appears that the firmware uses the
> error interrupt for two different indications. In the SJA1000 driver,
> they map to IRQ_EI and IRQ_EPI.
> 
> The documentation says that you can tell when you get an error-passive
> only by checking the rxerr + txerr registers in the message. You'll note
> I omitted the IRQ_EPI-equivalent code from my driver when I copied the
> sja1000.c implementation.
> 
> I've added an if-statement in the CEVTIND_EI path, which now looks like
> this. It handles both cases now.
> 
> /* error warning interrupt */
> if (isrc == CEVTIND_EI) {
> 	u8 rxerr = msg->data[4];
> 	u8 txerr = msg->data[5];
> 
> 	dev_dbg(mod->dev, "error warning interrupt\n");
> 	if (status & SR_BS) {
> 		state = CAN_STATE_BUS_OFF;
> 		cf->can_id |= CAN_ERR_BUSOFF;
> 		can_bus_off(dev);
> 	} else if (status & SR_ES) {
> 		if (rxerr >= 127 || txerr >= 127)
> 			state = CAN_STATE_ERROR_PASSIVE;
> 		else
> 			state = CAN_STATE_ERROR_WARNING;
> 	} else {
> 		state = CAN_STATE_ERROR_ACTIVE;
> 	}
> }
> 
> The only change is in the "else if (status & SR_ES)" path. I had to add
> the if-statement that checks the rxerr and txerr registers. Does that
> seem ok? I got the 127 values from this webpage (provided to me on this
> mailing list).

It should be >= 128.

> http://www.softing.com/home/en/industrial-automation/products/can-bus/more-can-bus/error-handling/error-states.php?navanchor=3010510
> 
>>>     bitrate 1000000 sample-point 0.750 
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          0          0          1          0          0         
>>>     RX: bytes  packets  errors  dropped overrun mcast   
>>>     16         0        2       0       0       0      
>>>     TX: bytes  packets  errors  dropped carrier collsns 
>>>     513        513      0       0       0       0      
>>> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can 
>>>     can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
>>>     bitrate 1000000 sample-point 0.750 
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          126        0          1          0          0         
>> But that's mabe because you stopped the test too early (just 126 bus errors).
>>
> 
> This is the best I could do. Without the cable connected, that's where
> the controller stops sending messages (cansequence just hangs waiting
> for buffer space to become available).
> 
>>>     RX: bytes  packets  errors  dropped overrun mcast   
>>>     1024       0        254     0       0       0      
>>>     TX: bytes  packets  errors  dropped carrier collsns 
>>>     513        513      0       0       0       0      
>> When I send out messages without cable connected I get:
>>
>> -bash-3.2# ./ip -d -s link show can0
>> 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>     link/can 
>>     can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
>>     bitrate 500000 sample-point 0.875 
>>     tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
>>     sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>     clock 8000000
>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>     0          54101      0          1          1          0         
>>     RX: bytes  packets  errors  dropped overrun mcast   
>>     432808     54101    54101   0       0       0      
>>     TX: bytes  packets  errors  dropped carrier collsns 
>>     0          0        0       0       0       0      
>>
>> The following output is without BERR-REPORTING:
>>
>> -bash-3.2# ./candump -t d any,0:0,#FFFFFFFF
>>  (0.000000)  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>>  (0.000474)  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
>>                                                     ^  ^
>>                                                    TX RX error counter
> 
> With my newest changes, I get:
> 
> 8: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
>     bitrate 1000000 sample-point 0.750 
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          3          3          0         
>     RX: bytes  packets  errors  dropped overrun mcast   
>     236045     235949   12      0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     235938     235938   0       0       0       0      
> 
>   can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>   can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
> 
> So it looks like both drivers agree (finally!). :)
> 
> With berr-reporting on, I get the same flood of bus-error messages, with
> these two messages as well.

Looks good now.

>> The patch I mentioned also copies the rx and tx error counter values to
>> the data field 6 and 7.
>>
> 
> I missed this. It has been added. Thanks for pointing it out.

You could even add the tx/rx values for each error message (for both,
state changes and bus-errors).

> I haven't heard back from Samuel Ortiz yet about the changes for the mfd
> layer. Would you like me to send out my latest CAN driver changes, or
> should I just wait until I hear back?

As you need patch 1/3 anyway, just wait some more time. From my point of
 view the next version of the patch will be OK.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:28                       ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 20:28 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Ira W. Snyder wrote:
> On Mon, Mar 22, 2010 at 08:17:10PM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
>>> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
>>>> Ira W. Snyder wrote:
>> [snip]
>>>>> Does this seem right? It seems pretty good to me.
>>>> Yes, I'm just missing an error-passive message. What state does "ip -d
>>>> link show can0" report.
>>>>
>>> Ok, here is what I did:
>>>
>>> $ ip link set can0 up type can bitrate 1000000
>>> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
>>> $ ip -d -s link
>>> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can       
>>>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>>     bitrate 1000000 sample-point 0.750
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000  
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          0          0          0          0          0
>>>     RX: bytes  packets  errors  dropped overrun mcast
>>>     0          0        0       0       0       0
>>>     TX: bytes  packets  errors  dropped carrier collsns
>>>     0          0        0       0       0       0
>>> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can       
>>>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>>     bitrate 1000000 sample-point 0.750
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000  
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          0          0          0          0          0
>>>     RX: bytes  packets  errors  dropped overrun mcast
>>>     0          0        0       0       0       0
>>>     TX: bytes  packets  errors  dropped carrier collsns
>>>     0          0        0       0       0       0
>>>
>>> Now, in seperate windows, I ran cansequence and candump. I stopped
>>> cansequence when it could not send any more packets (due to the cable
>>> being unplugged).
>>>
>>> $ cansequence -v -e -p can0
>>> $ cansequence -v -e -p can1
>>> $ candump any,0~0,#FFFFFFFF
>>>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>>
>>> This last message is repeated lots more times. That's the flooding we're
>>> avoiding with berr-reporting off.
>>>
>>> I see two types of messages here:
>>> 1) bus error (only on can1)
>>> 2) controller problems -- tx warning limit reached (both)
>>>
>>> Am I missing some message? My error frame generation was mostly copied
>>> from the sja1000 driver.
>> It seem that you are not getting the error passive interrupt even...
>>
>>> $ ip -d -s link
>>> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can 
>>>     can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
>> if the hardware already reports >= 128 errors --^.
>>
> 
> Re-reading the documentation, it appears that the firmware uses the
> error interrupt for two different indications. In the SJA1000 driver,
> they map to IRQ_EI and IRQ_EPI.
> 
> The documentation says that you can tell when you get an error-passive
> only by checking the rxerr + txerr registers in the message. You'll note
> I omitted the IRQ_EPI-equivalent code from my driver when I copied the
> sja1000.c implementation.
> 
> I've added an if-statement in the CEVTIND_EI path, which now looks like
> this. It handles both cases now.
> 
> /* error warning interrupt */
> if (isrc == CEVTIND_EI) {
> 	u8 rxerr = msg->data[4];
> 	u8 txerr = msg->data[5];
> 
> 	dev_dbg(mod->dev, "error warning interrupt\n");
> 	if (status & SR_BS) {
> 		state = CAN_STATE_BUS_OFF;
> 		cf->can_id |= CAN_ERR_BUSOFF;
> 		can_bus_off(dev);
> 	} else if (status & SR_ES) {
> 		if (rxerr >= 127 || txerr >= 127)
> 			state = CAN_STATE_ERROR_PASSIVE;
> 		else
> 			state = CAN_STATE_ERROR_WARNING;
> 	} else {
> 		state = CAN_STATE_ERROR_ACTIVE;
> 	}
> }
> 
> The only change is in the "else if (status & SR_ES)" path. I had to add
> the if-statement that checks the rxerr and txerr registers. Does that
> seem ok? I got the 127 values from this webpage (provided to me on this
> mailing list).

It should be >= 128.

> http://www.softing.com/home/en/industrial-automation/products/can-bus/more-can-bus/error-handling/error-states.php?navanchor=3010510
> 
>>>     bitrate 1000000 sample-point 0.750 
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          0          0          1          0          0         
>>>     RX: bytes  packets  errors  dropped overrun mcast   
>>>     16         0        2       0       0       0      
>>>     TX: bytes  packets  errors  dropped carrier collsns 
>>>     513        513      0       0       0       0      
>>> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>>     link/can 
>>>     can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
>>>     bitrate 1000000 sample-point 0.750 
>>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>>     clock 8000000
>>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>>     0          126        0          1          0          0         
>> But that's mabe because you stopped the test too early (just 126 bus errors).
>>
> 
> This is the best I could do. Without the cable connected, that's where
> the controller stops sending messages (cansequence just hangs waiting
> for buffer space to become available).
> 
>>>     RX: bytes  packets  errors  dropped overrun mcast   
>>>     1024       0        254     0       0       0      
>>>     TX: bytes  packets  errors  dropped carrier collsns 
>>>     513        513      0       0       0       0      
>> When I send out messages without cable connected I get:
>>
>> -bash-3.2# ./ip -d -s link show can0
>> 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>     link/can 
>>     can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
>>     bitrate 500000 sample-point 0.875 
>>     tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
>>     sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>     clock 8000000
>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>     0          54101      0          1          1          0         
>>     RX: bytes  packets  errors  dropped overrun mcast   
>>     432808     54101    54101   0       0       0      
>>     TX: bytes  packets  errors  dropped carrier collsns 
>>     0          0        0       0       0       0      
>>
>> The following output is without BERR-REPORTING:
>>
>> -bash-3.2# ./candump -t d any,0:0,#FFFFFFFF
>>  (0.000000)  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>>  (0.000474)  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
>>                                                     ^  ^
>>                                                    TX RX error counter
> 
> With my newest changes, I get:
> 
> 8: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
>     bitrate 1000000 sample-point 0.750 
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          3          3          0         
>     RX: bytes  packets  errors  dropped overrun mcast   
>     236045     235949   12      0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     235938     235938   0       0       0       0      
> 
>   can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>   can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
> 
> So it looks like both drivers agree (finally!). :)
> 
> With berr-reporting on, I get the same flood of bus-error messages, with
> these two messages as well.

Looks good now.

>> The patch I mentioned also copies the rx and tx error counter values to
>> the data field 6 and 7.
>>
> 
> I missed this. It has been added. Thanks for pointing it out.

You could even add the tx/rx values for each error message (for both,
state changes and bus-errors).

> I haven't heard back from Samuel Ortiz yet about the changes for the mfd
> layer. Would you like me to send out my latest CAN driver changes, or
> should I just wait until I hear back?

As you need patch 1/3 anyway, just wait some more time. From my point of
 view the next version of the patch will be OK.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:12                       ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 20:12 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: socketcan-core, netdev, linux-kernel, sameo

On Mon, Mar 22, 2010 at 08:23:42PM +0100, Wolfgang Grandegger wrote:
> Wolfgang Grandegger wrote:
> > Ira W. Snyder wrote:
> >> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
> >>> Ira W. Snyder wrote:
> > [snip]
> >>>> Does this seem right? It seems pretty good to me.
> >>> Yes, I'm just missing an error-passive message. What state does "ip -d
> >>> link show can0" report.
> >>>
> >> Ok, here is what I did:
> >>
> >> $ ip link set can0 up type can bitrate 1000000
> >> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
> >> $ ip -d -s link
> >> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >>     link/can       
> >>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >>     bitrate 1000000 sample-point 0.750
> >>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >>     clock 8000000  
> >>     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >>     0          0          0          0          0          0
> >>     RX: bytes  packets  errors  dropped overrun mcast
> >>     0          0        0       0       0       0
> >>     TX: bytes  packets  errors  dropped carrier collsns
> >>     0          0        0       0       0       0
> >> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >>     link/can       
> >>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >>     bitrate 1000000 sample-point 0.750
> >>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >>     clock 8000000  
> >>     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >>     0          0          0          0          0          0
> >>     RX: bytes  packets  errors  dropped overrun mcast
> >>     0          0        0       0       0       0
> >>     TX: bytes  packets  errors  dropped carrier collsns
> >>     0          0        0       0       0       0
> >>
> >> Now, in seperate windows, I ran cansequence and candump. I stopped
> >> cansequence when it could not send any more packets (due to the cable
> >> being unplugged).
> >>
> >> $ cansequence -v -e -p can0
> >> $ cansequence -v -e -p can1
> >> $ candump any,0~0,#FFFFFFFF
> >>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>
> >> This last message is repeated lots more times. That's the flooding we're
> >> avoiding with berr-reporting off.
> >>
> >> I see two types of messages here:
> >> 1) bus error (only on can1)
> >> 2) controller problems -- tx warning limit reached (both)
> >>
> >> Am I missing some message? My error frame generation was mostly copied
> >> from the sja1000 driver.
> > 
> > It seem that you are not getting the error passive interrupt even...
> 
> Because you do not enable/handle it. CEVTIND_EPI seems to be missing:
> 
> http://lxr.linux.no/#linux+v2.6.33/drivers/net/can/sja1000/sja1000.c#L403
> 

See the message I just sent. In short, the firmware coalesces the IRQ_EI
and IRQ_EPI messages into CEVTIND_EI. You can only tell them apart via
the rxerr and txerr registers.

Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:12                       ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 20:12 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

On Mon, Mar 22, 2010 at 08:23:42PM +0100, Wolfgang Grandegger wrote:
> Wolfgang Grandegger wrote:
> > Ira W. Snyder wrote:
> >> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
> >>> Ira W. Snyder wrote:
> > [snip]
> >>>> Does this seem right? It seems pretty good to me.
> >>> Yes, I'm just missing an error-passive message. What state does "ip -d
> >>> link show can0" report.
> >>>
> >> Ok, here is what I did:
> >>
> >> $ ip link set can0 up type can bitrate 1000000
> >> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
> >> $ ip -d -s link
> >> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >>     link/can       
> >>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >>     bitrate 1000000 sample-point 0.750
> >>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >>     clock 8000000  
> >>     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >>     0          0          0          0          0          0
> >>     RX: bytes  packets  errors  dropped overrun mcast
> >>     0          0        0       0       0       0
> >>     TX: bytes  packets  errors  dropped carrier collsns
> >>     0          0        0       0       0       0
> >> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >>     link/can       
> >>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >>     bitrate 1000000 sample-point 0.750
> >>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >>     clock 8000000  
> >>     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >>     0          0          0          0          0          0
> >>     RX: bytes  packets  errors  dropped overrun mcast
> >>     0          0        0       0       0       0
> >>     TX: bytes  packets  errors  dropped carrier collsns
> >>     0          0        0       0       0       0
> >>
> >> Now, in seperate windows, I ran cansequence and candump. I stopped
> >> cansequence when it could not send any more packets (due to the cable
> >> being unplugged).
> >>
> >> $ cansequence -v -e -p can0
> >> $ cansequence -v -e -p can1
> >> $ candump any,0~0,#FFFFFFFF
> >>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >>
> >> This last message is repeated lots more times. That's the flooding we're
> >> avoiding with berr-reporting off.
> >>
> >> I see two types of messages here:
> >> 1) bus error (only on can1)
> >> 2) controller problems -- tx warning limit reached (both)
> >>
> >> Am I missing some message? My error frame generation was mostly copied
> >> from the sja1000 driver.
> > 
> > It seem that you are not getting the error passive interrupt even...
> 
> Because you do not enable/handle it. CEVTIND_EPI seems to be missing:
> 
> http://lxr.linux.no/#linux+v2.6.33/drivers/net/can/sja1000/sja1000.c#L403
> 

See the message I just sent. In short, the firmware coalesces the IRQ_EI
and IRQ_EPI messages into CEVTIND_EI. You can only tell them apart via
the rxerr and txerr registers.

Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:10                     ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 20:10 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: socketcan-core, netdev, linux-kernel, sameo

On Mon, Mar 22, 2010 at 08:17:10PM +0100, Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
> > On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
> >> Ira W. Snyder wrote:
> [snip]
> >>> Does this seem right? It seems pretty good to me.
> >> Yes, I'm just missing an error-passive message. What state does "ip -d
> >> link show can0" report.
> >>
> > 
> > Ok, here is what I did:
> > 
> > $ ip link set can0 up type can bitrate 1000000
> > $ ip link set can1 up type can bitrate 1000000 berr-reporting on
> > $ ip -d -s link
> > 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can       
> >     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >     bitrate 1000000 sample-point 0.750
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000  
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          0          0          0          0          0
> >     RX: bytes  packets  errors  dropped overrun mcast
> >     0          0        0       0       0       0
> >     TX: bytes  packets  errors  dropped carrier collsns
> >     0          0        0       0       0       0
> > 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can       
> >     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >     bitrate 1000000 sample-point 0.750
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000  
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          0          0          0          0          0
> >     RX: bytes  packets  errors  dropped overrun mcast
> >     0          0        0       0       0       0
> >     TX: bytes  packets  errors  dropped carrier collsns
> >     0          0        0       0       0       0
> > 
> > Now, in seperate windows, I ran cansequence and candump. I stopped
> > cansequence when it could not send any more packets (due to the cable
> > being unplugged).
> > 
> > $ cansequence -v -e -p can0
> > $ cansequence -v -e -p can1
> > $ candump any,0~0,#FFFFFFFF
> >   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> > 
> > This last message is repeated lots more times. That's the flooding we're
> > avoiding with berr-reporting off.
> > 
> > I see two types of messages here:
> > 1) bus error (only on can1)
> > 2) controller problems -- tx warning limit reached (both)
> > 
> > Am I missing some message? My error frame generation was mostly copied
> > from the sja1000 driver.
> 
> It seem that you are not getting the error passive interrupt even...
> 
> > $ ip -d -s link
> > 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can 
> >     can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
> 
> if the hardware already reports >= 128 errors --^.
> 

Re-reading the documentation, it appears that the firmware uses the
error interrupt for two different indications. In the SJA1000 driver,
they map to IRQ_EI and IRQ_EPI.

The documentation says that you can tell when you get an error-passive
only by checking the rxerr + txerr registers in the message. You'll note
I omitted the IRQ_EPI-equivalent code from my driver when I copied the
sja1000.c implementation.

I've added an if-statement in the CEVTIND_EI path, which now looks like
this. It handles both cases now.

/* error warning interrupt */
if (isrc == CEVTIND_EI) {
	u8 rxerr = msg->data[4];
	u8 txerr = msg->data[5];

	dev_dbg(mod->dev, "error warning interrupt\n");
	if (status & SR_BS) {
		state = CAN_STATE_BUS_OFF;
		cf->can_id |= CAN_ERR_BUSOFF;
		can_bus_off(dev);
	} else if (status & SR_ES) {
		if (rxerr >= 127 || txerr >= 127)
			state = CAN_STATE_ERROR_PASSIVE;
		else
			state = CAN_STATE_ERROR_WARNING;
	} else {
		state = CAN_STATE_ERROR_ACTIVE;
	}
}

The only change is in the "else if (status & SR_ES)" path. I had to add
the if-statement that checks the rxerr and txerr registers. Does that
seem ok? I got the 127 values from this webpage (provided to me on this
mailing list).

http://www.softing.com/home/en/industrial-automation/products/can-bus/more-can-bus/error-handling/error-states.php?navanchor=3010510

> >     bitrate 1000000 sample-point 0.750 
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          0          0          1          0          0         
> >     RX: bytes  packets  errors  dropped overrun mcast   
> >     16         0        2       0       0       0      
> >     TX: bytes  packets  errors  dropped carrier collsns 
> >     513        513      0       0       0       0      
> > 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can 
> >     can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
> >     bitrate 1000000 sample-point 0.750 
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          126        0          1          0          0         
> 
> But that's mabe because you stopped the test too early (just 126 bus errors).
> 

This is the best I could do. Without the cable connected, that's where
the controller stops sending messages (cansequence just hangs waiting
for buffer space to become available).

> >     RX: bytes  packets  errors  dropped overrun mcast   
> >     1024       0        254     0       0       0      
> >     TX: bytes  packets  errors  dropped carrier collsns 
> >     513        513      0       0       0       0      
> 
> When I send out messages without cable connected I get:
> 
> -bash-3.2# ./ip -d -s link show can0
> 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
>     bitrate 500000 sample-point 0.875 
>     tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
>     sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          54101      0          1          1          0         
>     RX: bytes  packets  errors  dropped overrun mcast   
>     432808     54101    54101   0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     0          0        0       0       0       0      
> 
> The following output is without BERR-REPORTING:
> 
> -bash-3.2# ./candump -t d any,0:0,#FFFFFFFF
>  (0.000000)  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>  (0.000474)  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
>                                                     ^  ^
>                                                    TX RX error counter

With my newest changes, I get:

8: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 1000000 sample-point 0.750 
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          3          3          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    236045     235949   12      0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    235938     235938   0       0       0       0      

  can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
  can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME

So it looks like both drivers agree (finally!). :)

With berr-reporting on, I get the same flood of bus-error messages, with
these two messages as well.

> 
> The patch I mentioned also copies the rx and tx error counter values to
> the data field 6 and 7.
> 

I missed this. It has been added. Thanks for pointing it out.

I haven't heard back from Samuel Ortiz yet about the changes for the mfd
layer. Would you like me to send out my latest CAN driver changes, or
should I just wait until I hear back?

Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 20:10                     ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 20:10 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

On Mon, Mar 22, 2010 at 08:17:10PM +0100, Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
> > On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
> >> Ira W. Snyder wrote:
> [snip]
> >>> Does this seem right? It seems pretty good to me.
> >> Yes, I'm just missing an error-passive message. What state does "ip -d
> >> link show can0" report.
> >>
> > 
> > Ok, here is what I did:
> > 
> > $ ip link set can0 up type can bitrate 1000000
> > $ ip link set can1 up type can bitrate 1000000 berr-reporting on
> > $ ip -d -s link
> > 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can       
> >     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >     bitrate 1000000 sample-point 0.750
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000  
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          0          0          0          0          0
> >     RX: bytes  packets  errors  dropped overrun mcast
> >     0          0        0       0       0       0
> >     TX: bytes  packets  errors  dropped carrier collsns
> >     0          0        0       0       0       0
> > 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can       
> >     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
> >     bitrate 1000000 sample-point 0.750
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000  
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          0          0          0          0          0
> >     RX: bytes  packets  errors  dropped overrun mcast
> >     0          0        0       0       0       0
> >     TX: bytes  packets  errors  dropped carrier collsns
> >     0          0        0       0       0       0
> > 
> > Now, in seperate windows, I ran cansequence and candump. I stopped
> > cansequence when it could not send any more packets (due to the cable
> > being unplugged).
> > 
> > $ cansequence -v -e -p can0
> > $ cansequence -v -e -p can1
> > $ candump any,0~0,#FFFFFFFF
> >   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> >   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> > 
> > This last message is repeated lots more times. That's the flooding we're
> > avoiding with berr-reporting off.
> > 
> > I see two types of messages here:
> > 1) bus error (only on can1)
> > 2) controller problems -- tx warning limit reached (both)
> > 
> > Am I missing some message? My error frame generation was mostly copied
> > from the sja1000 driver.
> 
> It seem that you are not getting the error passive interrupt even...
> 
> > $ ip -d -s link
> > 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can 
> >     can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
> 
> if the hardware already reports >= 128 errors --^.
> 

Re-reading the documentation, it appears that the firmware uses the
error interrupt for two different indications. In the SJA1000 driver,
they map to IRQ_EI and IRQ_EPI.

The documentation says that you can tell when you get an error-passive
only by checking the rxerr + txerr registers in the message. You'll note
I omitted the IRQ_EPI-equivalent code from my driver when I copied the
sja1000.c implementation.

I've added an if-statement in the CEVTIND_EI path, which now looks like
this. It handles both cases now.

/* error warning interrupt */
if (isrc == CEVTIND_EI) {
	u8 rxerr = msg->data[4];
	u8 txerr = msg->data[5];

	dev_dbg(mod->dev, "error warning interrupt\n");
	if (status & SR_BS) {
		state = CAN_STATE_BUS_OFF;
		cf->can_id |= CAN_ERR_BUSOFF;
		can_bus_off(dev);
	} else if (status & SR_ES) {
		if (rxerr >= 127 || txerr >= 127)
			state = CAN_STATE_ERROR_PASSIVE;
		else
			state = CAN_STATE_ERROR_WARNING;
	} else {
		state = CAN_STATE_ERROR_ACTIVE;
	}
}

The only change is in the "else if (status & SR_ES)" path. I had to add
the if-statement that checks the rxerr and txerr registers. Does that
seem ok? I got the 127 values from this webpage (provided to me on this
mailing list).

http://www.softing.com/home/en/industrial-automation/products/can-bus/more-can-bus/error-handling/error-states.php?navanchor=3010510

> >     bitrate 1000000 sample-point 0.750 
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          0          0          1          0          0         
> >     RX: bytes  packets  errors  dropped overrun mcast   
> >     16         0        2       0       0       0      
> >     TX: bytes  packets  errors  dropped carrier collsns 
> >     513        513      0       0       0       0      
> > 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
> >     link/can 
> >     can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
> >     bitrate 1000000 sample-point 0.750 
> >     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
> >     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
> >     clock 8000000
> >     re-started bus-errors arbit-lost error-warn error-pass bus-off
> >     0          126        0          1          0          0         
> 
> But that's mabe because you stopped the test too early (just 126 bus errors).
> 

This is the best I could do. Without the cable connected, that's where
the controller stops sending messages (cansequence just hangs waiting
for buffer space to become available).

> >     RX: bytes  packets  errors  dropped overrun mcast   
> >     1024       0        254     0       0       0      
> >     TX: bytes  packets  errors  dropped carrier collsns 
> >     513        513      0       0       0       0      
> 
> When I send out messages without cable connected I get:
> 
> -bash-3.2# ./ip -d -s link show can0
> 2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
>     bitrate 500000 sample-point 0.875 
>     tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
>     sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          54101      0          1          1          0         
>     RX: bytes  packets  errors  dropped overrun mcast   
>     432808     54101    54101   0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     0          0        0       0       0       0      
> 
> The following output is without BERR-REPORTING:
> 
> -bash-3.2# ./candump -t d any,0:0,#FFFFFFFF
>  (0.000000)  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
>  (0.000474)  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
>                                                     ^  ^
>                                                    TX RX error counter

With my newest changes, I get:

8: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 1000000 sample-point 0.750 
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          3          3          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    236045     235949   12      0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    235938     235938   0       0       0       0      

  can1  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
  can1  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME

So it looks like both drivers agree (finally!). :)

With berr-reporting on, I get the same flood of bus-error messages, with
these two messages as well.

> 
> The patch I mentioned also copies the rx and tx error counter values to
> the data field 6 and 7.
> 

I missed this. It has been added. Thanks for pointing it out.

I haven't heard back from Samuel Ortiz yet about the changes for the mfd
layer. Would you like me to send out my latest CAN driver changes, or
should I just wait until I hear back?

Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 19:23                     ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 19:23 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: socketcan-core, netdev, linux-kernel, sameo

Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
>> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
>>> Ira W. Snyder wrote:
> [snip]
>>>> Does this seem right? It seems pretty good to me.
>>> Yes, I'm just missing an error-passive message. What state does "ip -d
>>> link show can0" report.
>>>
>> Ok, here is what I did:
>>
>> $ ip link set can0 up type can bitrate 1000000
>> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
>> $ ip -d -s link
>> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>     link/can       
>>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>     bitrate 1000000 sample-point 0.750
>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>     clock 8000000  
>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>     0          0          0          0          0          0
>>     RX: bytes  packets  errors  dropped overrun mcast
>>     0          0        0       0       0       0
>>     TX: bytes  packets  errors  dropped carrier collsns
>>     0          0        0       0       0       0
>> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>     link/can       
>>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>     bitrate 1000000 sample-point 0.750
>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>     clock 8000000  
>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>     0          0          0          0          0          0
>>     RX: bytes  packets  errors  dropped overrun mcast
>>     0          0        0       0       0       0
>>     TX: bytes  packets  errors  dropped carrier collsns
>>     0          0        0       0       0       0
>>
>> Now, in seperate windows, I ran cansequence and candump. I stopped
>> cansequence when it could not send any more packets (due to the cable
>> being unplugged).
>>
>> $ cansequence -v -e -p can0
>> $ cansequence -v -e -p can1
>> $ candump any,0~0,#FFFFFFFF
>>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>
>> This last message is repeated lots more times. That's the flooding we're
>> avoiding with berr-reporting off.
>>
>> I see two types of messages here:
>> 1) bus error (only on can1)
>> 2) controller problems -- tx warning limit reached (both)
>>
>> Am I missing some message? My error frame generation was mostly copied
>> from the sja1000 driver.
> 
> It seem that you are not getting the error passive interrupt even...

Because you do not enable/handle it. CEVTIND_EPI seems to be missing:

http://lxr.linux.no/#linux+v2.6.33/drivers/net/can/sja1000/sja1000.c#L403

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 19:23                     ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 19:23 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
>> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
>>> Ira W. Snyder wrote:
> [snip]
>>>> Does this seem right? It seems pretty good to me.
>>> Yes, I'm just missing an error-passive message. What state does "ip -d
>>> link show can0" report.
>>>
>> Ok, here is what I did:
>>
>> $ ip link set can0 up type can bitrate 1000000
>> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
>> $ ip -d -s link
>> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>     link/can       
>>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>     bitrate 1000000 sample-point 0.750
>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>     clock 8000000  
>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>     0          0          0          0          0          0
>>     RX: bytes  packets  errors  dropped overrun mcast
>>     0          0        0       0       0       0
>>     TX: bytes  packets  errors  dropped carrier collsns
>>     0          0        0       0       0       0
>> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>>     link/can       
>>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>>     bitrate 1000000 sample-point 0.750
>>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>>     clock 8000000  
>>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>>     0          0          0          0          0          0
>>     RX: bytes  packets  errors  dropped overrun mcast
>>     0          0        0       0       0       0
>>     TX: bytes  packets  errors  dropped carrier collsns
>>     0          0        0       0       0       0
>>
>> Now, in seperate windows, I ran cansequence and candump. I stopped
>> cansequence when it could not send any more packets (due to the cable
>> being unplugged).
>>
>> $ cansequence -v -e -p can0
>> $ cansequence -v -e -p can1
>> $ candump any,0~0,#FFFFFFFF
>>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>>
>> This last message is repeated lots more times. That's the flooding we're
>> avoiding with berr-reporting off.
>>
>> I see two types of messages here:
>> 1) bus error (only on can1)
>> 2) controller problems -- tx warning limit reached (both)
>>
>> Am I missing some message? My error frame generation was mostly copied
>> from the sja1000 driver.
> 
> It seem that you are not getting the error passive interrupt even...

Because you do not enable/handle it. CEVTIND_EPI seems to be missing:

http://lxr.linux.no/#linux+v2.6.33/drivers/net/can/sja1000/sja1000.c#L403

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 19:17                   ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 19:17 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: socketcan-core, netdev, linux-kernel, sameo

Ira W. Snyder wrote:
> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
[snip]
>>> Does this seem right? It seems pretty good to me.
>> Yes, I'm just missing an error-passive message. What state does "ip -d
>> link show can0" report.
>>
> 
> Ok, here is what I did:
> 
> $ ip link set can0 up type can bitrate 1000000
> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
> $ ip -d -s link
> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can       
>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>     bitrate 1000000 sample-point 0.750
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000  
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          0          0          0
>     RX: bytes  packets  errors  dropped overrun mcast
>     0          0        0       0       0       0
>     TX: bytes  packets  errors  dropped carrier collsns
>     0          0        0       0       0       0
> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can       
>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>     bitrate 1000000 sample-point 0.750
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000  
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          0          0          0
>     RX: bytes  packets  errors  dropped overrun mcast
>     0          0        0       0       0       0
>     TX: bytes  packets  errors  dropped carrier collsns
>     0          0        0       0       0       0
> 
> Now, in seperate windows, I ran cansequence and candump. I stopped
> cansequence when it could not send any more packets (due to the cable
> being unplugged).
> 
> $ cansequence -v -e -p can0
> $ cansequence -v -e -p can1
> $ candump any,0~0,#FFFFFFFF
>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> 
> This last message is repeated lots more times. That's the flooding we're
> avoiding with berr-reporting off.
> 
> I see two types of messages here:
> 1) bus error (only on can1)
> 2) controller problems -- tx warning limit reached (both)
> 
> Am I missing some message? My error frame generation was mostly copied
> from the sja1000 driver.

It seem that you are not getting the error passive interrupt even...

> $ ip -d -s link
> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 

if the hardware already reports >= 128 errors --^.

>     bitrate 1000000 sample-point 0.750 
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          1          0          0         
>     RX: bytes  packets  errors  dropped overrun mcast   
>     16         0        2       0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     513        513      0       0       0       0      
> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
>     bitrate 1000000 sample-point 0.750 
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          126        0          1          0          0         

But that's mabe because you stopped the test too early (just 126 bus errors).

>     RX: bytes  packets  errors  dropped overrun mcast   
>     1024       0        254     0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     513        513      0       0       0       0      

When I send out messages without cable connected I get:

-bash-3.2# ./ip -d -s link show can0
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 500000 sample-point 0.875 
    tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
    sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          54101      0          1          1          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    432808     54101    54101   0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    0          0        0       0       0       0      

The following output is without BERR-REPORTING:

-bash-3.2# ./candump -t d any,0:0,#FFFFFFFF
 (0.000000)  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
 (0.000474)  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
                                                    ^  ^
                                                   TX RX error counter

The patch I mentioned also copies the rx and tx error counter values to
the data field 6 and 7.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 19:17                   ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-22 19:17 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Ira W. Snyder wrote:
> On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
[snip]
>>> Does this seem right? It seems pretty good to me.
>> Yes, I'm just missing an error-passive message. What state does "ip -d
>> link show can0" report.
>>
> 
> Ok, here is what I did:
> 
> $ ip link set can0 up type can bitrate 1000000
> $ ip link set can1 up type can bitrate 1000000 berr-reporting on
> $ ip -d -s link
> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can       
>     can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>     bitrate 1000000 sample-point 0.750
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000  
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          0          0          0
>     RX: bytes  packets  errors  dropped overrun mcast
>     0          0        0       0       0       0
>     TX: bytes  packets  errors  dropped carrier collsns
>     0          0        0       0       0       0
> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can       
>     can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
>     bitrate 1000000 sample-point 0.750
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000  
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          0          0          0
>     RX: bytes  packets  errors  dropped overrun mcast
>     0          0        0       0       0       0
>     TX: bytes  packets  errors  dropped carrier collsns
>     0          0        0       0       0       0
> 
> Now, in seperate windows, I ran cansequence and candump. I stopped
> cansequence when it could not send any more packets (due to the cable
> being unplugged).
> 
> $ cansequence -v -e -p can0
> $ cansequence -v -e -p can1
> $ candump any,0~0,#FFFFFFFF
>   can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
>   can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
> 
> This last message is repeated lots more times. That's the flooding we're
> avoiding with berr-reporting off.
> 
> I see two types of messages here:
> 1) bus error (only on can1)
> 2) controller problems -- tx warning limit reached (both)
> 
> Am I missing some message? My error frame generation was mostly copied
> from the sja1000 driver.

It seem that you are not getting the error passive interrupt even...

> $ ip -d -s link
> 5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 

if the hardware already reports >= 128 errors --^.

>     bitrate 1000000 sample-point 0.750 
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          0          0          1          0          0         
>     RX: bytes  packets  errors  dropped overrun mcast   
>     16         0        2       0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     513        513      0       0       0       0      
> 6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
>     link/can 
>     can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
>     bitrate 1000000 sample-point 0.750 
>     tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
>     janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
>     clock 8000000
>     re-started bus-errors arbit-lost error-warn error-pass bus-off
>     0          126        0          1          0          0         

But that's mabe because you stopped the test too early (just 126 bus errors).

>     RX: bytes  packets  errors  dropped overrun mcast   
>     1024       0        254     0       0       0      
>     TX: bytes  packets  errors  dropped carrier collsns 
>     513        513      0       0       0       0      

When I send out messages without cable connected I get:

-bash-3.2# ./ip -d -s link show can0
2: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can <BERR-REPORTING> state ERROR-PASSIVE (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 500000 sample-point 0.875 
    tq 125 prop-seg 6 phase-seg1 7 phase-seg2 2 sjw 1
    sja1000: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          54101      0          1          1          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    432808     54101    54101   0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    0          0        0       0       0       0      

The following output is without BERR-REPORTING:

-bash-3.2# ./candump -t d any,0:0,#FFFFFFFF
 (0.000000)  can0  20000004  [8] 00 08 00 00 00 00 60 00   ERRORFRAME
 (0.000474)  can0  20000004  [8] 00 20 00 00 00 00 80 00   ERRORFRAME
                                                    ^  ^
                                                   TX RX error counter

The patch I mentioned also copies the rx and tx error counter values to
the data field 6 and 7.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 15:53                 ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 15:53 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: linux-kernel, socketcan-core, netdev, sameo

On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
> > On Fri, Mar 19, 2010 at 09:13:37PM +0100, Wolfgang Grandegger wrote:
> >> Ira W. Snyder wrote:
> >>> On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
> >>>> Ira W. Snyder wrote:
> >>>>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
> >>>>>> Hi Ira,
> >>>>>>
> >>>>>> we already discussed this patch on the SocketCAN mailing list and there
> >>>>>> are just a few minor issues and the request to add support for the new
> >>>>>> "berr-reporting" option, if feasible. See:
> >>>>>>
> >>>>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
> >>>>>>   Author: Wolfgang Grandegger <wg@grandegger.com>
> >>>>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
> >>>>>>
> >>>>>>     can: netlink support for bus-error reporting and counters
> >>>>>>     
> >>>>>>     This patch makes the bus-error reporting configurable and allows to
> >>>>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
> >>>>>>     I have added support for the SJA1000. The TX and RX bus error counters
> >>>>>>     are also copied to the data fields 6..7 of error messages when state
> >>>>>>     changes are reported.
> >>>>>>
> >>>>>> Should not be a big deal.
> >>>>>>
> >>>>> I think this patch came along since my last post of the driver. I must
> >>>>> have missed it. I'll try and add support.
> >>>> No problem, it's really new. Just just need to enable BEI depending on
> >>>> CAN_CTRLMODE_BERR_REPORTING.
> >>>>
> >>> I have one final question about this.
> >>>
> >>> The documentation for the firmware isn't very specific here. I believe
> >>> that in order to get any kind of error messages, I need the bus error
> >>> feature turned on. What is the expected behavior of an SJA1000 with the
> >>> BEI (bus error interrupt) turned off? Will you still get warning
> >>> messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?
> >> Yes. State transitions are enabled with EI and EPI.
> >>
> > 
> > I cannot set the registers directly, but I think I got it right. See
> > below.
> > 
> >>> I'm not sure how I would go about testing this feature, either. Ideas?
> >> Send messages without cable connected and watch the error messages with
> >> "candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
> >> see additional bus-errors.
> >>
> > 
> > Ok, I tried this. On one controller, I turned on bus-error reporting. On
> > the other, I turn off bus-error reporting. I then tried sending lots of
> > messages with the cable unplugged. Here is what happened:
> > 
> > bus-error reporting on:
> > Lots of CAN_ERR_BUSERR messages are flooded in candump. There is also a
> > CAN_ERR_CRTL_TX_WARNING message, when there are too many TX errors.
> 
> OK, you will now also understand why bus-error reporting is off by
> default. On low-end systems bus-error flooding may even hang the system.
> 
> > bus-error reporting off:
> > There was only one message reported before the controller went into
> > ERROR-WARNING state. It was the same CAN_ERR_CRTL_TX_WARNING message as
> > above. There was no flooding of CAN_ERR_BUSERR messages.
> > 
> > Does this seem right? It seems pretty good to me.
> 
> Yes, I'm just missing an error-passive message. What state does "ip -d
> link show can0" report.
> 

Ok, here is what I did:

$ ip link set can0 up type can bitrate 1000000
$ ip link set can1 up type can bitrate 1000000 berr-reporting on
$ ip -d -s link
5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can       
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
    bitrate 1000000 sample-point 0.750
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000  
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          0          0          0
    RX: bytes  packets  errors  dropped overrun mcast
    0          0        0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    0          0        0       0       0       0
6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can       
    can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
    bitrate 1000000 sample-point 0.750
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000  
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          0          0          0
    RX: bytes  packets  errors  dropped overrun mcast
    0          0        0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    0          0        0       0       0       0

Now, in seperate windows, I ran cansequence and candump. I stopped
cansequence when it could not send any more packets (due to the cable
being unplugged).

$ cansequence -v -e -p can0
$ cansequence -v -e -p can1
$ candump any,0~0,#FFFFFFFF
  can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME

This last message is repeated lots more times. That's the flooding we're
avoiding with berr-reporting off.

I see two types of messages here:
1) bus error (only on can1)
2) controller problems -- tx warning limit reached (both)

Am I missing some message? My error frame generation was mostly copied
from the sja1000 driver.

$ ip -d -s link
5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 1000000 sample-point 0.750 
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          1          0          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    16         0        2       0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    513        513      0       0       0       0      
6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 1000000 sample-point 0.750 
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          126        0          1          0          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    1024       0        254     0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    513        513      0       0       0       0      


> >>> I also noticed that I can enable "self test mode" and "listen only mode"
> >>> using the same firmware command. It appears that there are netlink
> >>> messages for this as well. Should I try and support these, too? I don't
> >>> really have any use for them (yet). I assume "self test mode" is
> >>> equivalent to "loopback mode" in the netlink messages.
> >> List-only is straight forward while "self test mode" is not exactly like
> >> "loopback mode", IIRC. Feel free to send a follow-up patch when you have
> >> time for a thorough implementation and testing. It's also on my to-do
> >> list for the SJA1000.
> >>
> > 
> > Ok, then I'll put this off for a while. Feel free to pester me about it
> > when there is a working implementation in the SJA1000 driver for me to
> > borrow from. :)
> 
> I will let you know when I have something working.
> 

Thanks,
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-22 15:53                 ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-22 15:53 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

On Sat, Mar 20, 2010 at 08:55:16AM +0100, Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
> > On Fri, Mar 19, 2010 at 09:13:37PM +0100, Wolfgang Grandegger wrote:
> >> Ira W. Snyder wrote:
> >>> On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
> >>>> Ira W. Snyder wrote:
> >>>>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
> >>>>>> Hi Ira,
> >>>>>>
> >>>>>> we already discussed this patch on the SocketCAN mailing list and there
> >>>>>> are just a few minor issues and the request to add support for the new
> >>>>>> "berr-reporting" option, if feasible. See:
> >>>>>>
> >>>>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
> >>>>>>   Author: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
> >>>>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
> >>>>>>
> >>>>>>     can: netlink support for bus-error reporting and counters
> >>>>>>     
> >>>>>>     This patch makes the bus-error reporting configurable and allows to
> >>>>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
> >>>>>>     I have added support for the SJA1000. The TX and RX bus error counters
> >>>>>>     are also copied to the data fields 6..7 of error messages when state
> >>>>>>     changes are reported.
> >>>>>>
> >>>>>> Should not be a big deal.
> >>>>>>
> >>>>> I think this patch came along since my last post of the driver. I must
> >>>>> have missed it. I'll try and add support.
> >>>> No problem, it's really new. Just just need to enable BEI depending on
> >>>> CAN_CTRLMODE_BERR_REPORTING.
> >>>>
> >>> I have one final question about this.
> >>>
> >>> The documentation for the firmware isn't very specific here. I believe
> >>> that in order to get any kind of error messages, I need the bus error
> >>> feature turned on. What is the expected behavior of an SJA1000 with the
> >>> BEI (bus error interrupt) turned off? Will you still get warning
> >>> messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?
> >> Yes. State transitions are enabled with EI and EPI.
> >>
> > 
> > I cannot set the registers directly, but I think I got it right. See
> > below.
> > 
> >>> I'm not sure how I would go about testing this feature, either. Ideas?
> >> Send messages without cable connected and watch the error messages with
> >> "candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
> >> see additional bus-errors.
> >>
> > 
> > Ok, I tried this. On one controller, I turned on bus-error reporting. On
> > the other, I turn off bus-error reporting. I then tried sending lots of
> > messages with the cable unplugged. Here is what happened:
> > 
> > bus-error reporting on:
> > Lots of CAN_ERR_BUSERR messages are flooded in candump. There is also a
> > CAN_ERR_CRTL_TX_WARNING message, when there are too many TX errors.
> 
> OK, you will now also understand why bus-error reporting is off by
> default. On low-end systems bus-error flooding may even hang the system.
> 
> > bus-error reporting off:
> > There was only one message reported before the controller went into
> > ERROR-WARNING state. It was the same CAN_ERR_CRTL_TX_WARNING message as
> > above. There was no flooding of CAN_ERR_BUSERR messages.
> > 
> > Does this seem right? It seems pretty good to me.
> 
> Yes, I'm just missing an error-passive message. What state does "ip -d
> link show can0" report.
> 

Ok, here is what I did:

$ ip link set can0 up type can bitrate 1000000
$ ip link set can1 up type can bitrate 1000000 berr-reporting on
$ ip -d -s link
5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can       
    can state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
    bitrate 1000000 sample-point 0.750
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000  
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          0          0          0
    RX: bytes  packets  errors  dropped overrun mcast
    0          0        0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    0          0        0       0       0       0
6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can       
    can <BERR-REPORTING> state ERROR-ACTIVE (berr-counter tx 0 rx 0) restart-ms 0
    bitrate 1000000 sample-point 0.750
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000  
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          0          0          0
    RX: bytes  packets  errors  dropped overrun mcast
    0          0        0       0       0       0
    TX: bytes  packets  errors  dropped carrier collsns
    0          0        0       0       0       0

Now, in seperate windows, I ran cansequence and candump. I stopped
cansequence when it could not send any more packets (due to the cable
being unplugged).

$ cansequence -v -e -p can0
$ cansequence -v -e -p can1
$ candump any,0~0,#FFFFFFFF
  can0  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000004  [8] 00 08 00 00 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME
  can1  20000088  [8] 00 00 80 19 00 00 00 00   ERRORFRAME

This last message is repeated lots more times. That's the flooding we're
avoiding with berr-reporting off.

I see two types of messages here:
1) bus error (only on can1)
2) controller problems -- tx warning limit reached (both)

Am I missing some message? My error frame generation was mostly copied
from the sja1000 driver.

$ ip -d -s link
5: can0: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 1000000 sample-point 0.750 
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          0          0          1          0          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    16         0        2       0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    513        513      0       0       0       0      
6: can1: <NOARP,UP,LOWER_UP,ECHO> mtu 16 qdisc pfifo_fast state UNKNOWN qlen 10
    link/can 
    can <BERR-REPORTING> state ERROR-WARNING (berr-counter tx 128 rx 0) restart-ms 0 
    bitrate 1000000 sample-point 0.750 
    tq 125 prop-seg 2 phase-seg1 3 phase-seg2 2 sjw 1
    janz-ican3: tseg1 1..16 tseg2 1..8 sjw 1..4 brp 1..64 brp-inc 1
    clock 8000000
    re-started bus-errors arbit-lost error-warn error-pass bus-off
    0          126        0          1          0          0         
    RX: bytes  packets  errors  dropped overrun mcast   
    1024       0        254     0       0       0      
    TX: bytes  packets  errors  dropped carrier collsns 
    513        513      0       0       0       0      


> >>> I also noticed that I can enable "self test mode" and "listen only mode"
> >>> using the same firmware command. It appears that there are netlink
> >>> messages for this as well. Should I try and support these, too? I don't
> >>> really have any use for them (yet). I assume "self test mode" is
> >>> equivalent to "loopback mode" in the netlink messages.
> >> List-only is straight forward while "self test mode" is not exactly like
> >> "loopback mode", IIRC. Feel free to send a follow-up patch when you have
> >> time for a thorough implementation and testing. It's also on my to-do
> >> list for the SJA1000.
> >>
> > 
> > Ok, then I'll put this off for a while. Feel free to pester me about it
> > when there is a working implementation in the SJA1000 driver for me to
> > borrow from. :)
> 
> I will let you know when I have something working.
> 

Thanks,
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-20  7:55               ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-20  7:55 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core, netdev, sameo

Ira W. Snyder wrote:
> On Fri, Mar 19, 2010 at 09:13:37PM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
>>> On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
>>>> Ira W. Snyder wrote:
>>>>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
>>>>>> Hi Ira,
>>>>>>
>>>>>> we already discussed this patch on the SocketCAN mailing list and there
>>>>>> are just a few minor issues and the request to add support for the new
>>>>>> "berr-reporting" option, if feasible. See:
>>>>>>
>>>>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>>>>>>   Author: Wolfgang Grandegger <wg@grandegger.com>
>>>>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
>>>>>>
>>>>>>     can: netlink support for bus-error reporting and counters
>>>>>>     
>>>>>>     This patch makes the bus-error reporting configurable and allows to
>>>>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
>>>>>>     I have added support for the SJA1000. The TX and RX bus error counters
>>>>>>     are also copied to the data fields 6..7 of error messages when state
>>>>>>     changes are reported.
>>>>>>
>>>>>> Should not be a big deal.
>>>>>>
>>>>> I think this patch came along since my last post of the driver. I must
>>>>> have missed it. I'll try and add support.
>>>> No problem, it's really new. Just just need to enable BEI depending on
>>>> CAN_CTRLMODE_BERR_REPORTING.
>>>>
>>> I have one final question about this.
>>>
>>> The documentation for the firmware isn't very specific here. I believe
>>> that in order to get any kind of error messages, I need the bus error
>>> feature turned on. What is the expected behavior of an SJA1000 with the
>>> BEI (bus error interrupt) turned off? Will you still get warning
>>> messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?
>> Yes. State transitions are enabled with EI and EPI.
>>
> 
> I cannot set the registers directly, but I think I got it right. See
> below.
> 
>>> I'm not sure how I would go about testing this feature, either. Ideas?
>> Send messages without cable connected and watch the error messages with
>> "candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
>> see additional bus-errors.
>>
> 
> Ok, I tried this. On one controller, I turned on bus-error reporting. On
> the other, I turn off bus-error reporting. I then tried sending lots of
> messages with the cable unplugged. Here is what happened:
> 
> bus-error reporting on:
> Lots of CAN_ERR_BUSERR messages are flooded in candump. There is also a
> CAN_ERR_CRTL_TX_WARNING message, when there are too many TX errors.

OK, you will now also understand why bus-error reporting is off by
default. On low-end systems bus-error flooding may even hang the system.

> bus-error reporting off:
> There was only one message reported before the controller went into
> ERROR-WARNING state. It was the same CAN_ERR_CRTL_TX_WARNING message as
> above. There was no flooding of CAN_ERR_BUSERR messages.
> 
> Does this seem right? It seems pretty good to me.

Yes, I'm just missing an error-passive message. What state does "ip -d
link show can0" report.

>>> I also noticed that I can enable "self test mode" and "listen only mode"
>>> using the same firmware command. It appears that there are netlink
>>> messages for this as well. Should I try and support these, too? I don't
>>> really have any use for them (yet). I assume "self test mode" is
>>> equivalent to "loopback mode" in the netlink messages.
>> List-only is straight forward while "self test mode" is not exactly like
>> "loopback mode", IIRC. Feel free to send a follow-up patch when you have
>> time for a thorough implementation and testing. It's also on my to-do
>> list for the SJA1000.
>>
> 
> Ok, then I'll put this off for a while. Feel free to pester me about it
> when there is a working implementation in the SJA1000 driver for me to
> borrow from. :)

I will let you know when I have something working.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-20  7:55               ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-20  7:55 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Ira W. Snyder wrote:
> On Fri, Mar 19, 2010 at 09:13:37PM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
>>> On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
>>>> Ira W. Snyder wrote:
>>>>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
>>>>>> Hi Ira,
>>>>>>
>>>>>> we already discussed this patch on the SocketCAN mailing list and there
>>>>>> are just a few minor issues and the request to add support for the new
>>>>>> "berr-reporting" option, if feasible. See:
>>>>>>
>>>>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>>>>>>   Author: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
>>>>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
>>>>>>
>>>>>>     can: netlink support for bus-error reporting and counters
>>>>>>     
>>>>>>     This patch makes the bus-error reporting configurable and allows to
>>>>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
>>>>>>     I have added support for the SJA1000. The TX and RX bus error counters
>>>>>>     are also copied to the data fields 6..7 of error messages when state
>>>>>>     changes are reported.
>>>>>>
>>>>>> Should not be a big deal.
>>>>>>
>>>>> I think this patch came along since my last post of the driver. I must
>>>>> have missed it. I'll try and add support.
>>>> No problem, it's really new. Just just need to enable BEI depending on
>>>> CAN_CTRLMODE_BERR_REPORTING.
>>>>
>>> I have one final question about this.
>>>
>>> The documentation for the firmware isn't very specific here. I believe
>>> that in order to get any kind of error messages, I need the bus error
>>> feature turned on. What is the expected behavior of an SJA1000 with the
>>> BEI (bus error interrupt) turned off? Will you still get warning
>>> messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?
>> Yes. State transitions are enabled with EI and EPI.
>>
> 
> I cannot set the registers directly, but I think I got it right. See
> below.
> 
>>> I'm not sure how I would go about testing this feature, either. Ideas?
>> Send messages without cable connected and watch the error messages with
>> "candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
>> see additional bus-errors.
>>
> 
> Ok, I tried this. On one controller, I turned on bus-error reporting. On
> the other, I turn off bus-error reporting. I then tried sending lots of
> messages with the cable unplugged. Here is what happened:
> 
> bus-error reporting on:
> Lots of CAN_ERR_BUSERR messages are flooded in candump. There is also a
> CAN_ERR_CRTL_TX_WARNING message, when there are too many TX errors.

OK, you will now also understand why bus-error reporting is off by
default. On low-end systems bus-error flooding may even hang the system.

> bus-error reporting off:
> There was only one message reported before the controller went into
> ERROR-WARNING state. It was the same CAN_ERR_CRTL_TX_WARNING message as
> above. There was no flooding of CAN_ERR_BUSERR messages.
> 
> Does this seem right? It seems pretty good to me.

Yes, I'm just missing an error-passive message. What state does "ip -d
link show can0" report.

>>> I also noticed that I can enable "self test mode" and "listen only mode"
>>> using the same firmware command. It appears that there are netlink
>>> messages for this as well. Should I try and support these, too? I don't
>>> really have any use for them (yet). I assume "self test mode" is
>>> equivalent to "loopback mode" in the netlink messages.
>> List-only is straight forward while "self test mode" is not exactly like
>> "loopback mode", IIRC. Feel free to send a follow-up patch when you have
>> time for a thorough implementation and testing. It's also on my to-do
>> list for the SJA1000.
>>
> 
> Ok, then I'll put this off for a while. Feel free to pester me about it
> when there is a working implementation in the SJA1000 driver for me to
> borrow from. :)

I will let you know when I have something working.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-19 20:13           ` Wolfgang Grandegger
  (?)
@ 2010-03-19 21:52           ` Ira W. Snyder
  2010-03-20  7:55               ` Wolfgang Grandegger
  -1 siblings, 1 reply; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-19 21:52 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: linux-kernel, socketcan-core, netdev, sameo

On Fri, Mar 19, 2010 at 09:13:37PM +0100, Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
> > On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
> >> Ira W. Snyder wrote:
> >>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
> >>>> Hi Ira,
> >>>>
> >>>> we already discussed this patch on the SocketCAN mailing list and there
> >>>> are just a few minor issues and the request to add support for the new
> >>>> "berr-reporting" option, if feasible. See:
> >>>>
> >>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
> >>>>   Author: Wolfgang Grandegger <wg@grandegger.com>
> >>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
> >>>>
> >>>>     can: netlink support for bus-error reporting and counters
> >>>>     
> >>>>     This patch makes the bus-error reporting configurable and allows to
> >>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
> >>>>     I have added support for the SJA1000. The TX and RX bus error counters
> >>>>     are also copied to the data fields 6..7 of error messages when state
> >>>>     changes are reported.
> >>>>
> >>>> Should not be a big deal.
> >>>>
> >>> I think this patch came along since my last post of the driver. I must
> >>> have missed it. I'll try and add support.
> >> No problem, it's really new. Just just need to enable BEI depending on
> >> CAN_CTRLMODE_BERR_REPORTING.
> >>
> > 
> > I have one final question about this.
> > 
> > The documentation for the firmware isn't very specific here. I believe
> > that in order to get any kind of error messages, I need the bus error
> > feature turned on. What is the expected behavior of an SJA1000 with the
> > BEI (bus error interrupt) turned off? Will you still get warning
> > messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?
> 
> Yes. State transitions are enabled with EI and EPI.
> 

I cannot set the registers directly, but I think I got it right. See
below.

> > I'm not sure how I would go about testing this feature, either. Ideas?
> 
> Send messages without cable connected and watch the error messages with
> "candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
> see additional bus-errors.
> 

Ok, I tried this. On one controller, I turned on bus-error reporting. On
the other, I turn off bus-error reporting. I then tried sending lots of
messages with the cable unplugged. Here is what happened:

bus-error reporting on:
Lots of CAN_ERR_BUSERR messages are flooded in candump. There is also a
CAN_ERR_CRTL_TX_WARNING message, when there are too many TX errors.

bus-error reporting off:
There was only one message reported before the controller went into
ERROR-WARNING state. It was the same CAN_ERR_CRTL_TX_WARNING message as
above. There was no flooding of CAN_ERR_BUSERR messages.

Does this seem right? It seems pretty good to me.

> > I also noticed that I can enable "self test mode" and "listen only mode"
> > using the same firmware command. It appears that there are netlink
> > messages for this as well. Should I try and support these, too? I don't
> > really have any use for them (yet). I assume "self test mode" is
> > equivalent to "loopback mode" in the netlink messages.
> 
> List-only is straight forward while "self test mode" is not exactly like
> "loopback mode", IIRC. Feel free to send a follow-up patch when you have
> time for a thorough implementation and testing. It's also on my to-do
> list for the SJA1000.
> 

Ok, then I'll put this off for a while. Feel free to pester me about it
when there is a working implementation in the SJA1000 driver for me to
borrow from. :)

Thanks for all the help.
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-19 20:13           ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-19 20:13 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core, netdev, sameo

Ira W. Snyder wrote:
> On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
>>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
>>>> Hi Ira,
>>>>
>>>> we already discussed this patch on the SocketCAN mailing list and there
>>>> are just a few minor issues and the request to add support for the new
>>>> "berr-reporting" option, if feasible. See:
>>>>
>>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>>>>   Author: Wolfgang Grandegger <wg@grandegger.com>
>>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
>>>>
>>>>     can: netlink support for bus-error reporting and counters
>>>>     
>>>>     This patch makes the bus-error reporting configurable and allows to
>>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
>>>>     I have added support for the SJA1000. The TX and RX bus error counters
>>>>     are also copied to the data fields 6..7 of error messages when state
>>>>     changes are reported.
>>>>
>>>> Should not be a big deal.
>>>>
>>> I think this patch came along since my last post of the driver. I must
>>> have missed it. I'll try and add support.
>> No problem, it's really new. Just just need to enable BEI depending on
>> CAN_CTRLMODE_BERR_REPORTING.
>>
> 
> I have one final question about this.
> 
> The documentation for the firmware isn't very specific here. I believe
> that in order to get any kind of error messages, I need the bus error
> feature turned on. What is the expected behavior of an SJA1000 with the
> BEI (bus error interrupt) turned off? Will you still get warning
> messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?

Yes. State transitions are enabled with EI and EPI.

> I'm not sure how I would go about testing this feature, either. Ideas?

Send messages without cable connected and watch the error messages with
"candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
see additional bus-errors.

> I also noticed that I can enable "self test mode" and "listen only mode"
> using the same firmware command. It appears that there are netlink
> messages for this as well. Should I try and support these, too? I don't
> really have any use for them (yet). I assume "self test mode" is
> equivalent to "loopback mode" in the netlink messages.

List-only is straight forward while "self test mode" is not exactly like
"loopback mode", IIRC. Feel free to send a follow-up patch when you have
time for a thorough implementation and testing. It's also on my to-do
list for the SJA1000.

Wolfgang.

> 
> Thanks,
> Ira
> 
> 


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-19 20:13           ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-19 20:13 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Ira W. Snyder wrote:
> On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
>> Ira W. Snyder wrote:
>>> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
>>>> Hi Ira,
>>>>
>>>> we already discussed this patch on the SocketCAN mailing list and there
>>>> are just a few minor issues and the request to add support for the new
>>>> "berr-reporting" option, if feasible. See:
>>>>
>>>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>>>>   Author: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
>>>>   Date:   Mon Feb 22 22:21:17 2010 +0000
>>>>
>>>>     can: netlink support for bus-error reporting and counters
>>>>     
>>>>     This patch makes the bus-error reporting configurable and allows to
>>>>     retrieve the CAN TX and RX bus error counters via netlink interface.
>>>>     I have added support for the SJA1000. The TX and RX bus error counters
>>>>     are also copied to the data fields 6..7 of error messages when state
>>>>     changes are reported.
>>>>
>>>> Should not be a big deal.
>>>>
>>> I think this patch came along since my last post of the driver. I must
>>> have missed it. I'll try and add support.
>> No problem, it's really new. Just just need to enable BEI depending on
>> CAN_CTRLMODE_BERR_REPORTING.
>>
> 
> I have one final question about this.
> 
> The documentation for the firmware isn't very specific here. I believe
> that in order to get any kind of error messages, I need the bus error
> feature turned on. What is the expected behavior of an SJA1000 with the
> BEI (bus error interrupt) turned off? Will you still get warning
> messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?

Yes. State transitions are enabled with EI and EPI.

> I'm not sure how I would go about testing this feature, either. Ideas?

Send messages without cable connected and watch the error messages with
"candump any,0:0,#ffffffff". With "ip ... berr-reporting on" you should
see additional bus-errors.

> I also noticed that I can enable "self test mode" and "listen only mode"
> using the same firmware command. It appears that there are netlink
> messages for this as well. Should I try and support these, too? I don't
> really have any use for them (yet). I assume "self test mode" is
> equivalent to "loopback mode" in the netlink messages.

List-only is straight forward while "self test mode" is not exactly like
"loopback mode", IIRC. Feel free to send a follow-up patch when you have
time for a thorough implementation and testing. It's also on my to-do
list for the SJA1000.

Wolfgang.

> 
> Thanks,
> Ira
> 
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-19 15:45     ` Wolfgang Grandegger
@ 2010-03-19 20:03       ` Ira W. Snyder
  2010-03-19 20:13           ` Wolfgang Grandegger
  0 siblings, 1 reply; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-19 20:03 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: linux-kernel, socketcan-core, netdev, sameo

On Fri, Mar 19, 2010 at 04:45:09PM +0100, Wolfgang Grandegger wrote:
> Ira W. Snyder wrote:
> > On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
> >> Hi Ira,
> >>
> >> we already discussed this patch on the SocketCAN mailing list and there
> >> are just a few minor issues and the request to add support for the new
> >> "berr-reporting" option, if feasible. See:
> >>
> >>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
> >>   Author: Wolfgang Grandegger <wg@grandegger.com>
> >>   Date:   Mon Feb 22 22:21:17 2010 +0000
> >>
> >>     can: netlink support for bus-error reporting and counters
> >>     
> >>     This patch makes the bus-error reporting configurable and allows to
> >>     retrieve the CAN TX and RX bus error counters via netlink interface.
> >>     I have added support for the SJA1000. The TX and RX bus error counters
> >>     are also copied to the data fields 6..7 of error messages when state
> >>     changes are reported.
> >>
> >> Should not be a big deal.
> >>
> > 
> > I think this patch came along since my last post of the driver. I must
> > have missed it. I'll try and add support.
> 
> No problem, it's really new. Just just need to enable BEI depending on
> CAN_CTRLMODE_BERR_REPORTING.
> 

I have one final question about this.

The documentation for the firmware isn't very specific here. I believe
that in order to get any kind of error messages, I need the bus error
feature turned on. What is the expected behavior of an SJA1000 with the
BEI (bus error interrupt) turned off? Will you still get warning
messages for ERROR_ACTIVE -> ERROR_PASSIVE state transitions?

I'm not sure how I would go about testing this feature, either. Ideas?

I also noticed that I can enable "self test mode" and "listen only mode"
using the same firmware command. It appears that there are netlink
messages for this as well. Should I try and support these, too? I don't
really have any use for them (yet). I assume "self test mode" is
equivalent to "loopback mode" in the netlink messages.

Thanks,
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-19 15:19     ` Ira W. Snyder
  (?)
@ 2010-03-19 15:45     ` Wolfgang Grandegger
  2010-03-19 20:03       ` Ira W. Snyder
  -1 siblings, 1 reply; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-19 15:45 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core, netdev, sameo

Ira W. Snyder wrote:
> On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
>> Hi Ira,
>>
>> we already discussed this patch on the SocketCAN mailing list and there
>> are just a few minor issues and the request to add support for the new
>> "berr-reporting" option, if feasible. See:
>>
>>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>>   Author: Wolfgang Grandegger <wg@grandegger.com>
>>   Date:   Mon Feb 22 22:21:17 2010 +0000
>>
>>     can: netlink support for bus-error reporting and counters
>>     
>>     This patch makes the bus-error reporting configurable and allows to
>>     retrieve the CAN TX and RX bus error counters via netlink interface.
>>     I have added support for the SJA1000. The TX and RX bus error counters
>>     are also copied to the data fields 6..7 of error messages when state
>>     changes are reported.
>>
>> Should not be a big deal.
>>
> 
> I think this patch came along since my last post of the driver. I must
> have missed it. I'll try and add support.

No problem, it's really new. Just just need to enable BEI depending on
CAN_CTRLMODE_BERR_REPORTING.

>> More inline...
>>
>> Ira W. Snyder wrote:
>>> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
>>> MODULbus carrier board. It is an intelligent CAN controller with a
>>> microcontroller and associated firmware.
>>>
>>> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
>>> Cc: socketcan-core@lists.berlios.de
>>> Cc: netdev@vger.kernel.org
>>> ---
>>>  drivers/net/can/Kconfig      |   10 +
>>>  drivers/net/can/Makefile     |    1 +
>>>  drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
>>>  3 files changed, 1670 insertions(+), 0 deletions(-)
>>>  create mode 100644 drivers/net/can/janz-ican3.c
>>>
>>> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
>>> index 05b7517..2c5227c 100644
>>> --- a/drivers/net/can/Kconfig
>>> +++ b/drivers/net/can/Kconfig
>>> @@ -63,6 +63,16 @@ config CAN_BFIN
>>>  	  To compile this driver as a module, choose M here: the
>>>  	  module will be called bfin_can.
>>>  
>>> +config CAN_JANZ_ICAN3
>>> +	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
>>> +	depends on CAN_DEV && MFD_JANZ_CMODIO
>>> +	---help---
>>> +	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
>>> +	  connects to a MODULbus carrier board.
>>> +
>>> +	  This driver can also be built as a module. If so, the module will be
>>> +	  called janz-ican3.ko.
>>> +
>>>  source "drivers/net/can/mscan/Kconfig"
>>>  
>>>  source "drivers/net/can/sja1000/Kconfig"
>>> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
>>> index 7a702f2..9047cd0 100644
>>> --- a/drivers/net/can/Makefile
>>> +++ b/drivers/net/can/Makefile
>>> @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
>>>  obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
>>>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
>>>  obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
>>> +obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
>>>  
>>>  ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
>>> diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
>>> new file mode 100644
>>> index 0000000..94d4995
>>> --- /dev/null
>>> +++ b/drivers/net/can/janz-ican3.c
>> [snip]
>>  +struct ican3_dev {
>>> +
>>> +	/* must be the first member */
>>> +	struct can_priv can;
>>> +
>>> +	/* CAN network device */
>>> +	struct net_device *ndev;
>>> +	struct napi_struct napi;
>>> +
>>> +	/* Device for printing */
>>> +	struct device *dev;
>>> +
>>> +	/* module number */
>>> +	unsigned int num;
>>> +
>>> +	/* base address of registers and IRQ */
>>> +	struct janz_cmodio_onboard_regs __iomem *ctrl;
>>> +	struct ican3_dpm_control *dpmctrl;
>>> +	void __iomem *dpm;
>>> +	int irq;
>>> +
>>> +	/* old and new style host interface */
>>> +	unsigned int iftype;
>>> +	spinlock_t lock;
>> Please describe what the lock is used for.
>>
>>> +	/* new host interface */
>>> +	unsigned int rx_int;
>>> +	unsigned int rx_num;
>>> +	unsigned int tx_num;
>>> +
>>> +	/* fast host interface */
>>> +	unsigned int fastrx_start;
>>> +	unsigned int fastrx_int;
>>> +	unsigned int fastrx_num;
>>> +	unsigned int fasttx_start;
>>> +	unsigned int fasttx_num;
>>> +
>>> +	/* first free DPM page */
>>> +	unsigned int free_page;
>>> +};
>> [snip]
>>> +static void ican3_to_can_frame(struct ican3_dev *mod,
>>> +			       struct ican3_fast_desc *desc,
>>> +			       struct can_frame *cf)
>>> +{
>>> +	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
>>> +		dev_dbg(mod->dev, "%s: old frame format\n", __func__);
>> This prints a debug message for every message which is not really
>> useful for the user. Please remove.
>>
>>> +		if (desc->data[1] & ICAN3_SFF_RTR)
>>> +			cf->can_id |= CAN_RTR_FLAG;
>>> +
>>> +		cf->can_id |= desc->data[0] << 3;
>>> +		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
>>> +		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
>>> +		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
>>> +	} else {
>>> +		dev_dbg(mod->dev, "%s: new frame format\n", __func__);
>> Ditto.
>>
>>> +		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
>>> +		if (desc->data[0] & ICAN3_EFF_RTR)
>>> +			cf->can_id |= CAN_RTR_FLAG;
>>> +
>>> +		if (desc->data[0] & ICAN3_EFF) {
>>> +			cf->can_id |= CAN_EFF_FLAG;
>>> +			cf->can_id |= desc->data[2] << 21; /* 28-21 */
>>> +			cf->can_id |= desc->data[3] << 13; /* 20-13 */
>>> +			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
>>> +			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
>>> +		} else {
>>> +			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
>>> +			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
>>> +		}
>>> +
>>> +		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
>>> +	}
>>> +}
>>> +
>>> +static void can_frame_to_ican3(struct ican3_dev *mod,
>>> +			       struct can_frame *cf,
>>> +			       struct ican3_fast_desc *desc)
>>> +{
>>> +	/* clear out any stale data in the descriptor */
>>> +	memset(desc->data, 0, sizeof(desc->data));
>>> +
>>> +	/* we always use the extended format, with the ECHO flag set */
>>> +	desc->command = ICAN3_CAN_TYPE_EFF;
>>> +	desc->data[0] |= cf->can_dlc;
>>> +	desc->data[1] |= ICAN3_ECHO;
>>> +
>>> +	if (cf->can_id & CAN_RTR_FLAG)
>>> +		desc->data[0] |= ICAN3_EFF_RTR;
>>> +
>>> +	/* pack the id into the correct places */
>>> +	if (cf->can_id & CAN_EFF_FLAG) {
>>> +		dev_dbg(mod->dev, "%s: extended frame\n", __func__);
>> Ditto.
>>
>>> +		desc->data[0] |= ICAN3_EFF;
>>> +		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
>>> +		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
>>> +		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
>>> +		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
>>> +	} else {
>>> +		dev_dbg(mod->dev, "%s: standard frame\n", __func__);
>> Ditto.
>>
>>> +		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
>>> +		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
>>> +	}
>>> +
>>> +	/* copy the data bits into the descriptor */
>>> +	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
>>> +}
>> [snip]
>>> +/*
>>> + * Handle CAN Event Indication Messages from the firmware
>>> + *
>>> + * The ICAN3 firmware provides the values of some SJA1000 registers when it
>>> + * generates this message. The code below is largely copied from the
>>> + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
>>> + */
>>> +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
>>> +{
>>> +	struct net_device *dev = mod->ndev;
>>> +	struct net_device_stats *stats = &dev->stats;
>>> +	enum can_state state = mod->can.state;
>>> +	struct can_frame *cf;
>>> +	struct sk_buff *skb;
>>> +	u8 status, isrc;
>>> +
>>> +	/* we can only handle the SJA1000 part */
>>> +	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
>>> +		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
>>> +		return -ENODEV;
>>> +	}
>>> +
>>> +	/* check the message length for sanity */
>>> +	if (msg->len < 6) {
>>> +		dev_err(mod->dev, "error message too short\n");
>>> +		return -EINVAL;
>>> +	}
>>> +
>>> +	skb = alloc_can_err_skb(dev, &cf);
>>> +	if (skb == NULL)
>>> +		return -ENOMEM;
>>> +
>>> +	isrc = msg->data[0];
>>> +	status = msg->data[3];
>>> +
>>> +	/* data overrun interrupt */
>>> +	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
>> Here and for the other errors below a dev_dbg() would be useful. Please
>> check sja1000.c.
>>
>>> +		cf->can_id |= CAN_ERR_CRTL;
>>> +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
>>> +		stats->rx_over_errors++;
>>> +		stats->rx_errors++;
>>> +		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
>> s/dev_info/dev_dbg/ ?
>>
> 
> Whoops, a leftover from debugging. Will change to dev_dbg().
> 
>>> +	}
>>> +
>>> +	/* error warning interrupt */
>>> +	if (isrc == CEVTIND_EI) {
>>> +		if (status & SR_BS) {
>>> +			state = CAN_STATE_BUS_OFF;
>>> +			cf->can_id |= CAN_ERR_BUSOFF;
>>> +			can_bus_off(dev);
>>> +		} else if (status & SR_ES) {
>>> +			state = CAN_STATE_ERROR_WARNING;
>>> +		} else {
>>> +			state = CAN_STATE_ERROR_ACTIVE;
>>> +		}
>>> +	}
>>> +
>>> +	/* bus error interrupt */
>>> +	if (isrc == CEVTIND_BEI) {
>>> +		u8 ecc = msg->data[2];
>> Add an empty line, please.
>>
>>> +		mod->can.can_stats.bus_error++;
>>> +		stats->rx_errors++;
>>> +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
>>> +
>>> +		switch (ecc & ECC_MASK) {
>>> +		case ECC_BIT:
>>> +			cf->data[2] |= CAN_ERR_PROT_BIT;
>>> +			break;
>>> +		case ECC_FORM:
>>> +			cf->data[2] |= CAN_ERR_PROT_FORM;
>>> +			break;
>>> +		case ECC_STUFF:
>>> +			cf->data[2] |= CAN_ERR_PROT_STUFF;
>>> +			break;
>>> +		default:
>>> +			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
>>> +			cf->data[3] = ecc & ECC_SEG;
>>> +			break;
>>> +		}
>>> +
>>> +		if ((ecc & ECC_DIR) == 0)
>>> +			cf->data[2] |= CAN_ERR_PROT_TX;
>>> +	}
>>> +
>>> +	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
>>> +					state == CAN_STATE_ERROR_PASSIVE)) {
>>> +		u8 rxerr = msg->data[4];
>>> +		u8 txerr = msg->data[5];
>> Ditto.
>>
>>> +		cf->can_id |= CAN_ERR_CRTL;
>>> +		if (state == CAN_STATE_ERROR_WARNING) {
>>> +			mod->can.can_stats.error_warning++;
>>> +			cf->data[1] = (txerr > rxerr) ?
>>> +				CAN_ERR_CRTL_TX_WARNING :
>>> +				CAN_ERR_CRTL_RX_WARNING;
>>> +		} else {
>>> +			mod->can.can_stats.error_passive++;
>>> +			cf->data[1] = (txerr > rxerr) ?
>>> +				CAN_ERR_CRTL_TX_PASSIVE :
>>> +				CAN_ERR_CRTL_RX_PASSIVE;
>>> +		}
>>> +	}
>>> +
>>> +	mod->can.state = state;
>>> +	stats->rx_errors++;
>>> +	stats->rx_bytes += cf->can_dlc;
>>> +	netif_rx(skb);
>>> +	return 0;
>>> +}
>> [snip]
>>> +static irqreturn_t ican3_irq(int irq, void *dev_id)
>>> +{
>>> +	struct ican3_dev *mod = dev_id;
>>> +	u8 stat;
>>> +
>>> +	/*
>>> +	 * The interrupt status register on this device reports interrupts
>>> +	 * as zeroes instead of using ones like most other devices
>>> +	 */
>>> +	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
>>> +	if (stat == (1 << mod->num))
>>> +		return IRQ_NONE;
>>> +
>>> +	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
>> Please remove this dev_dbg() as well.
>>
>> [snip]
>>> +/*
>>> + * Startup an ICAN module, bringing it into fast mode
>>> + */
>>> +static int __devinit ican3_startup_module(struct ican3_dev *mod)
>>> +{
>>> +	int ret;
>>> +
>>> +	ret = ican3_reset_module(mod);
>>> +	if (ret) {
>>> +		dev_err(mod->dev, "unable to reset module\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	/* re-enable interrupts so we can send messages */
>>> +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
>>> +
>>> +	ret = ican3_msg_connect(mod);
>>> +	if (ret) {
>>> +		dev_err(mod->dev, "unable to connect to module\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ican3_init_new_host_interface(mod);
>>> +	ret = ican3_msg_newhostif(mod);
>>> +	if (ret) {
>>> +		dev_err(mod->dev, "unable to switch to new-style interface\n");
>>> +		return ret;
>>> +	}
>>> +
>>> +	ret = ican3_set_termination(mod, true);
>>> +	if (ret) {
>>> +		dev_err(mod->dev, "unable to enable termination\n");
>>> +		return ret;
>>> +	}
>>
>> Could you please allow the user to disable termination, e.g. via module parameter
>> "bus_termination=0" in case he uses a cable with built-in termination.
>>
> 
> What would you think about a sysfs node instead, so it could be changed
> at runtime, on a per-daughtercard basis? Do you think enabling bus
> termination is a good default? IMO, it is a pretty safe default for most
> users.

I have no problem with the default. There should just be a way to
disable termination somehow. A dev_info(dev, "CAN bus termination
enabled") would furthermore make clear, that it's active. Setting
termination via SysFS is the better solution, of course, as it's per
device. It might even be useful to handle this in future in a common way
via CAN_CTRLMODE_BUS_TERMINATION.

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-19 15:19     ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-19 15:19 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: linux-kernel, socketcan-core, netdev, sameo

On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
> Hi Ira,
> 
> we already discussed this patch on the SocketCAN mailing list and there
> are just a few minor issues and the request to add support for the new
> "berr-reporting" option, if feasible. See:
> 
>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>   Author: Wolfgang Grandegger <wg@grandegger.com>
>   Date:   Mon Feb 22 22:21:17 2010 +0000
> 
>     can: netlink support for bus-error reporting and counters
>     
>     This patch makes the bus-error reporting configurable and allows to
>     retrieve the CAN TX and RX bus error counters via netlink interface.
>     I have added support for the SJA1000. The TX and RX bus error counters
>     are also copied to the data fields 6..7 of error messages when state
>     changes are reported.
> 
> Should not be a big deal.
> 

I think this patch came along since my last post of the driver. I must
have missed it. I'll try and add support.

> More inline...
> 
> Ira W. Snyder wrote:
> > The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> > MODULbus carrier board. It is an intelligent CAN controller with a
> > microcontroller and associated firmware.
> > 
> > Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> > Cc: socketcan-core@lists.berlios.de
> > Cc: netdev@vger.kernel.org
> > ---
> >  drivers/net/can/Kconfig      |   10 +
> >  drivers/net/can/Makefile     |    1 +
> >  drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 1670 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/net/can/janz-ican3.c
> > 
> > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> > index 05b7517..2c5227c 100644
> > --- a/drivers/net/can/Kconfig
> > +++ b/drivers/net/can/Kconfig
> > @@ -63,6 +63,16 @@ config CAN_BFIN
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called bfin_can.
> >  
> > +config CAN_JANZ_ICAN3
> > +	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
> > +	depends on CAN_DEV && MFD_JANZ_CMODIO
> > +	---help---
> > +	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
> > +	  connects to a MODULbus carrier board.
> > +
> > +	  This driver can also be built as a module. If so, the module will be
> > +	  called janz-ican3.ko.
> > +
> >  source "drivers/net/can/mscan/Kconfig"
> >  
> >  source "drivers/net/can/sja1000/Kconfig"
> > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> > index 7a702f2..9047cd0 100644
> > --- a/drivers/net/can/Makefile
> > +++ b/drivers/net/can/Makefile
> > @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
> >  obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
> >  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
> >  obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
> > +obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
> >  
> >  ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> > diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
> > new file mode 100644
> > index 0000000..94d4995
> > --- /dev/null
> > +++ b/drivers/net/can/janz-ican3.c
> [snip]
>  +struct ican3_dev {
> > +
> > +	/* must be the first member */
> > +	struct can_priv can;
> > +
> > +	/* CAN network device */
> > +	struct net_device *ndev;
> > +	struct napi_struct napi;
> > +
> > +	/* Device for printing */
> > +	struct device *dev;
> > +
> > +	/* module number */
> > +	unsigned int num;
> > +
> > +	/* base address of registers and IRQ */
> > +	struct janz_cmodio_onboard_regs __iomem *ctrl;
> > +	struct ican3_dpm_control *dpmctrl;
> > +	void __iomem *dpm;
> > +	int irq;
> > +
> > +	/* old and new style host interface */
> > +	unsigned int iftype;
> > +	spinlock_t lock;
> 
> Please describe what the lock is used for.
> 
> > +	/* new host interface */
> > +	unsigned int rx_int;
> > +	unsigned int rx_num;
> > +	unsigned int tx_num;
> > +
> > +	/* fast host interface */
> > +	unsigned int fastrx_start;
> > +	unsigned int fastrx_int;
> > +	unsigned int fastrx_num;
> > +	unsigned int fasttx_start;
> > +	unsigned int fasttx_num;
> > +
> > +	/* first free DPM page */
> > +	unsigned int free_page;
> > +};
> 
> [snip]
> > +static void ican3_to_can_frame(struct ican3_dev *mod,
> > +			       struct ican3_fast_desc *desc,
> > +			       struct can_frame *cf)
> > +{
> > +	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
> > +		dev_dbg(mod->dev, "%s: old frame format\n", __func__);
> 
> This prints a debug message for every message which is not really
> useful for the user. Please remove.
> 
> > +		if (desc->data[1] & ICAN3_SFF_RTR)
> > +			cf->can_id |= CAN_RTR_FLAG;
> > +
> > +		cf->can_id |= desc->data[0] << 3;
> > +		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
> > +		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
> > +		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
> > +	} else {
> > +		dev_dbg(mod->dev, "%s: new frame format\n", __func__);
> 
> Ditto.
> 
> > +		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
> > +		if (desc->data[0] & ICAN3_EFF_RTR)
> > +			cf->can_id |= CAN_RTR_FLAG;
> > +
> > +		if (desc->data[0] & ICAN3_EFF) {
> > +			cf->can_id |= CAN_EFF_FLAG;
> > +			cf->can_id |= desc->data[2] << 21; /* 28-21 */
> > +			cf->can_id |= desc->data[3] << 13; /* 20-13 */
> > +			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
> > +			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
> > +		} else {
> > +			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
> > +			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
> > +		}
> > +
> > +		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
> > +	}
> > +}
> > +
> > +static void can_frame_to_ican3(struct ican3_dev *mod,
> > +			       struct can_frame *cf,
> > +			       struct ican3_fast_desc *desc)
> > +{
> > +	/* clear out any stale data in the descriptor */
> > +	memset(desc->data, 0, sizeof(desc->data));
> > +
> > +	/* we always use the extended format, with the ECHO flag set */
> > +	desc->command = ICAN3_CAN_TYPE_EFF;
> > +	desc->data[0] |= cf->can_dlc;
> > +	desc->data[1] |= ICAN3_ECHO;
> > +
> > +	if (cf->can_id & CAN_RTR_FLAG)
> > +		desc->data[0] |= ICAN3_EFF_RTR;
> > +
> > +	/* pack the id into the correct places */
> > +	if (cf->can_id & CAN_EFF_FLAG) {
> > +		dev_dbg(mod->dev, "%s: extended frame\n", __func__);
> 
> Ditto.
> 
> > +		desc->data[0] |= ICAN3_EFF;
> > +		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
> > +		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
> > +		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
> > +		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
> > +	} else {
> > +		dev_dbg(mod->dev, "%s: standard frame\n", __func__);
> 
> Ditto.
> 
> > +		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
> > +		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
> > +	}
> > +
> > +	/* copy the data bits into the descriptor */
> > +	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
> > +}
> 
> [snip]
> > +/*
> > + * Handle CAN Event Indication Messages from the firmware
> > + *
> > + * The ICAN3 firmware provides the values of some SJA1000 registers when it
> > + * generates this message. The code below is largely copied from the
> > + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
> > + */
> > +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
> > +{
> > +	struct net_device *dev = mod->ndev;
> > +	struct net_device_stats *stats = &dev->stats;
> > +	enum can_state state = mod->can.state;
> > +	struct can_frame *cf;
> > +	struct sk_buff *skb;
> > +	u8 status, isrc;
> > +
> > +	/* we can only handle the SJA1000 part */
> > +	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
> > +		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	/* check the message length for sanity */
> > +	if (msg->len < 6) {
> > +		dev_err(mod->dev, "error message too short\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	skb = alloc_can_err_skb(dev, &cf);
> > +	if (skb == NULL)
> > +		return -ENOMEM;
> > +
> > +	isrc = msg->data[0];
> > +	status = msg->data[3];
> > +
> > +	/* data overrun interrupt */
> > +	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
> 
> Here and for the other errors below a dev_dbg() would be useful. Please
> check sja1000.c.
> 
> > +		cf->can_id |= CAN_ERR_CRTL;
> > +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> > +		stats->rx_over_errors++;
> > +		stats->rx_errors++;
> > +		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
> 
> s/dev_info/dev_dbg/ ?
> 

Whoops, a leftover from debugging. Will change to dev_dbg().

> > +	}
> > +
> > +	/* error warning interrupt */
> > +	if (isrc == CEVTIND_EI) {
> > +		if (status & SR_BS) {
> > +			state = CAN_STATE_BUS_OFF;
> > +			cf->can_id |= CAN_ERR_BUSOFF;
> > +			can_bus_off(dev);
> > +		} else if (status & SR_ES) {
> > +			state = CAN_STATE_ERROR_WARNING;
> > +		} else {
> > +			state = CAN_STATE_ERROR_ACTIVE;
> > +		}
> > +	}
> > +
> > +	/* bus error interrupt */
> > +	if (isrc == CEVTIND_BEI) {
> > +		u8 ecc = msg->data[2];
> 
> Add an empty line, please.
> 
> > +		mod->can.can_stats.bus_error++;
> > +		stats->rx_errors++;
> > +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> > +
> > +		switch (ecc & ECC_MASK) {
> > +		case ECC_BIT:
> > +			cf->data[2] |= CAN_ERR_PROT_BIT;
> > +			break;
> > +		case ECC_FORM:
> > +			cf->data[2] |= CAN_ERR_PROT_FORM;
> > +			break;
> > +		case ECC_STUFF:
> > +			cf->data[2] |= CAN_ERR_PROT_STUFF;
> > +			break;
> > +		default:
> > +			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> > +			cf->data[3] = ecc & ECC_SEG;
> > +			break;
> > +		}
> > +
> > +		if ((ecc & ECC_DIR) == 0)
> > +			cf->data[2] |= CAN_ERR_PROT_TX;
> > +	}
> > +
> > +	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
> > +					state == CAN_STATE_ERROR_PASSIVE)) {
> > +		u8 rxerr = msg->data[4];
> > +		u8 txerr = msg->data[5];
> 
> Ditto.
> 
> > +		cf->can_id |= CAN_ERR_CRTL;
> > +		if (state == CAN_STATE_ERROR_WARNING) {
> > +			mod->can.can_stats.error_warning++;
> > +			cf->data[1] = (txerr > rxerr) ?
> > +				CAN_ERR_CRTL_TX_WARNING :
> > +				CAN_ERR_CRTL_RX_WARNING;
> > +		} else {
> > +			mod->can.can_stats.error_passive++;
> > +			cf->data[1] = (txerr > rxerr) ?
> > +				CAN_ERR_CRTL_TX_PASSIVE :
> > +				CAN_ERR_CRTL_RX_PASSIVE;
> > +		}
> > +	}
> > +
> > +	mod->can.state = state;
> > +	stats->rx_errors++;
> > +	stats->rx_bytes += cf->can_dlc;
> > +	netif_rx(skb);
> > +	return 0;
> > +}
> 
> [snip]
> > +static irqreturn_t ican3_irq(int irq, void *dev_id)
> > +{
> > +	struct ican3_dev *mod = dev_id;
> > +	u8 stat;
> > +
> > +	/*
> > +	 * The interrupt status register on this device reports interrupts
> > +	 * as zeroes instead of using ones like most other devices
> > +	 */
> > +	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
> > +	if (stat == (1 << mod->num))
> > +		return IRQ_NONE;
> > +
> > +	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
> 
> Please remove this dev_dbg() as well.
> 
> [snip]
> > +/*
> > + * Startup an ICAN module, bringing it into fast mode
> > + */
> > +static int __devinit ican3_startup_module(struct ican3_dev *mod)
> > +{
> > +	int ret;
> > +
> > +	ret = ican3_reset_module(mod);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to reset module\n");
> > +		return ret;
> > +	}
> > +
> > +	/* re-enable interrupts so we can send messages */
> > +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
> > +
> > +	ret = ican3_msg_connect(mod);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to connect to module\n");
> > +		return ret;
> > +	}
> > +
> > +	ican3_init_new_host_interface(mod);
> > +	ret = ican3_msg_newhostif(mod);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to switch to new-style interface\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = ican3_set_termination(mod, true);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to enable termination\n");
> > +		return ret;
> > +	}
> 
> 
> Could you please allow the user to disable termination, e.g. via module parameter
> "bus_termination=0" in case he uses a cable with built-in termination.
> 

What would you think about a sysfs node instead, so it could be changed
at runtime, on a per-daughtercard basis? Do you think enabling bus
termination is a good default? IMO, it is a pretty safe default for most
users.

> [snip]
> > +static int __devinit ican3_probe(struct platform_device *pdev)
> > +{
> > +	struct janz_platform_data *pdata;
> > +	struct net_device *ndev;
> > +	struct ican3_dev *mod;
> > +	struct resource *res;
> > +	struct device *dev;
> > +	int ret;
> > +
> > +	pdata = pdev->dev.platform_data;
> > +	if (!pdata)
> > +		return -ENXIO;
> > +
> > +	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
> > +
> > +	/* save the struct device for printing */
> > +	dev = &pdev->dev;
> > +
> > +	/* allocate the CAN device and private data */
> > +	ndev = alloc_candev(sizeof(*mod), 0);
> > +	if (!ndev) {
> > +		dev_err(dev, "unable to allocate CANdev\n");
> > +		ret = -ENOMEM;
> > +		goto out_return;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, ndev);
> > +	mod = netdev_priv(ndev);
> > +	mod->ndev = ndev;
> > +	mod->dev = &pdev->dev;
> > +	mod->num = pdata->modno;
> > +	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
> > +	spin_lock_init(&mod->lock);
> > +
> > +	/* the first unallocated page in the DPM is 9 */
> > +	mod->free_page = DPM_FREE_START;
> > +
> > +	ndev->netdev_ops = &ican3_netdev_ops;
> > +	ndev->flags |= IFF_ECHO;
> > +	SET_NETDEV_DEV(ndev, &pdev->dev);
> > +
> > +	mod->can.clock.freq = 8000000;
> 
> Please use a constant here.
> [snip]
> 
> Please fix and resubmit with my:
> 
> "Acked-by: Wolfgang Grandegger <wg@grandegger.com>"
> 
> for the SocketCAN part.
> 

Thanks for the review!
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-19 15:19     ` Ira W. Snyder
  0 siblings, 0 replies; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-19 15:19 UTC (permalink / raw)
  To: Wolfgang Grandegger
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

On Fri, Mar 19, 2010 at 10:01:14AM +0100, Wolfgang Grandegger wrote:
> Hi Ira,
> 
> we already discussed this patch on the SocketCAN mailing list and there
> are just a few minor issues and the request to add support for the new
> "berr-reporting" option, if feasible. See:
> 
>   commit 52c793f24054f5dc30d228e37e0e19cc8313f086
>   Author: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
>   Date:   Mon Feb 22 22:21:17 2010 +0000
> 
>     can: netlink support for bus-error reporting and counters
>     
>     This patch makes the bus-error reporting configurable and allows to
>     retrieve the CAN TX and RX bus error counters via netlink interface.
>     I have added support for the SJA1000. The TX and RX bus error counters
>     are also copied to the data fields 6..7 of error messages when state
>     changes are reported.
> 
> Should not be a big deal.
> 

I think this patch came along since my last post of the driver. I must
have missed it. I'll try and add support.

> More inline...
> 
> Ira W. Snyder wrote:
> > The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> > MODULbus carrier board. It is an intelligent CAN controller with a
> > microcontroller and associated firmware.
> > 
> > Signed-off-by: Ira W. Snyder <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org>
> > Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
> > Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> > ---
> >  drivers/net/can/Kconfig      |   10 +
> >  drivers/net/can/Makefile     |    1 +
> >  drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
> >  3 files changed, 1670 insertions(+), 0 deletions(-)
> >  create mode 100644 drivers/net/can/janz-ican3.c
> > 
> > diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> > index 05b7517..2c5227c 100644
> > --- a/drivers/net/can/Kconfig
> > +++ b/drivers/net/can/Kconfig
> > @@ -63,6 +63,16 @@ config CAN_BFIN
> >  	  To compile this driver as a module, choose M here: the
> >  	  module will be called bfin_can.
> >  
> > +config CAN_JANZ_ICAN3
> > +	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
> > +	depends on CAN_DEV && MFD_JANZ_CMODIO
> > +	---help---
> > +	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
> > +	  connects to a MODULbus carrier board.
> > +
> > +	  This driver can also be built as a module. If so, the module will be
> > +	  called janz-ican3.ko.
> > +
> >  source "drivers/net/can/mscan/Kconfig"
> >  
> >  source "drivers/net/can/sja1000/Kconfig"
> > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> > index 7a702f2..9047cd0 100644
> > --- a/drivers/net/can/Makefile
> > +++ b/drivers/net/can/Makefile
> > @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
> >  obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
> >  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
> >  obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
> > +obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
> >  
> >  ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> > diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
> > new file mode 100644
> > index 0000000..94d4995
> > --- /dev/null
> > +++ b/drivers/net/can/janz-ican3.c
> [snip]
>  +struct ican3_dev {
> > +
> > +	/* must be the first member */
> > +	struct can_priv can;
> > +
> > +	/* CAN network device */
> > +	struct net_device *ndev;
> > +	struct napi_struct napi;
> > +
> > +	/* Device for printing */
> > +	struct device *dev;
> > +
> > +	/* module number */
> > +	unsigned int num;
> > +
> > +	/* base address of registers and IRQ */
> > +	struct janz_cmodio_onboard_regs __iomem *ctrl;
> > +	struct ican3_dpm_control *dpmctrl;
> > +	void __iomem *dpm;
> > +	int irq;
> > +
> > +	/* old and new style host interface */
> > +	unsigned int iftype;
> > +	spinlock_t lock;
> 
> Please describe what the lock is used for.
> 
> > +	/* new host interface */
> > +	unsigned int rx_int;
> > +	unsigned int rx_num;
> > +	unsigned int tx_num;
> > +
> > +	/* fast host interface */
> > +	unsigned int fastrx_start;
> > +	unsigned int fastrx_int;
> > +	unsigned int fastrx_num;
> > +	unsigned int fasttx_start;
> > +	unsigned int fasttx_num;
> > +
> > +	/* first free DPM page */
> > +	unsigned int free_page;
> > +};
> 
> [snip]
> > +static void ican3_to_can_frame(struct ican3_dev *mod,
> > +			       struct ican3_fast_desc *desc,
> > +			       struct can_frame *cf)
> > +{
> > +	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
> > +		dev_dbg(mod->dev, "%s: old frame format\n", __func__);
> 
> This prints a debug message for every message which is not really
> useful for the user. Please remove.
> 
> > +		if (desc->data[1] & ICAN3_SFF_RTR)
> > +			cf->can_id |= CAN_RTR_FLAG;
> > +
> > +		cf->can_id |= desc->data[0] << 3;
> > +		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
> > +		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
> > +		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
> > +	} else {
> > +		dev_dbg(mod->dev, "%s: new frame format\n", __func__);
> 
> Ditto.
> 
> > +		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
> > +		if (desc->data[0] & ICAN3_EFF_RTR)
> > +			cf->can_id |= CAN_RTR_FLAG;
> > +
> > +		if (desc->data[0] & ICAN3_EFF) {
> > +			cf->can_id |= CAN_EFF_FLAG;
> > +			cf->can_id |= desc->data[2] << 21; /* 28-21 */
> > +			cf->can_id |= desc->data[3] << 13; /* 20-13 */
> > +			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
> > +			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
> > +		} else {
> > +			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
> > +			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
> > +		}
> > +
> > +		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
> > +	}
> > +}
> > +
> > +static void can_frame_to_ican3(struct ican3_dev *mod,
> > +			       struct can_frame *cf,
> > +			       struct ican3_fast_desc *desc)
> > +{
> > +	/* clear out any stale data in the descriptor */
> > +	memset(desc->data, 0, sizeof(desc->data));
> > +
> > +	/* we always use the extended format, with the ECHO flag set */
> > +	desc->command = ICAN3_CAN_TYPE_EFF;
> > +	desc->data[0] |= cf->can_dlc;
> > +	desc->data[1] |= ICAN3_ECHO;
> > +
> > +	if (cf->can_id & CAN_RTR_FLAG)
> > +		desc->data[0] |= ICAN3_EFF_RTR;
> > +
> > +	/* pack the id into the correct places */
> > +	if (cf->can_id & CAN_EFF_FLAG) {
> > +		dev_dbg(mod->dev, "%s: extended frame\n", __func__);
> 
> Ditto.
> 
> > +		desc->data[0] |= ICAN3_EFF;
> > +		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
> > +		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
> > +		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
> > +		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
> > +	} else {
> > +		dev_dbg(mod->dev, "%s: standard frame\n", __func__);
> 
> Ditto.
> 
> > +		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
> > +		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
> > +	}
> > +
> > +	/* copy the data bits into the descriptor */
> > +	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
> > +}
> 
> [snip]
> > +/*
> > + * Handle CAN Event Indication Messages from the firmware
> > + *
> > + * The ICAN3 firmware provides the values of some SJA1000 registers when it
> > + * generates this message. The code below is largely copied from the
> > + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
> > + */
> > +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
> > +{
> > +	struct net_device *dev = mod->ndev;
> > +	struct net_device_stats *stats = &dev->stats;
> > +	enum can_state state = mod->can.state;
> > +	struct can_frame *cf;
> > +	struct sk_buff *skb;
> > +	u8 status, isrc;
> > +
> > +	/* we can only handle the SJA1000 part */
> > +	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
> > +		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
> > +		return -ENODEV;
> > +	}
> > +
> > +	/* check the message length for sanity */
> > +	if (msg->len < 6) {
> > +		dev_err(mod->dev, "error message too short\n");
> > +		return -EINVAL;
> > +	}
> > +
> > +	skb = alloc_can_err_skb(dev, &cf);
> > +	if (skb == NULL)
> > +		return -ENOMEM;
> > +
> > +	isrc = msg->data[0];
> > +	status = msg->data[3];
> > +
> > +	/* data overrun interrupt */
> > +	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
> 
> Here and for the other errors below a dev_dbg() would be useful. Please
> check sja1000.c.
> 
> > +		cf->can_id |= CAN_ERR_CRTL;
> > +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> > +		stats->rx_over_errors++;
> > +		stats->rx_errors++;
> > +		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
> 
> s/dev_info/dev_dbg/ ?
> 

Whoops, a leftover from debugging. Will change to dev_dbg().

> > +	}
> > +
> > +	/* error warning interrupt */
> > +	if (isrc == CEVTIND_EI) {
> > +		if (status & SR_BS) {
> > +			state = CAN_STATE_BUS_OFF;
> > +			cf->can_id |= CAN_ERR_BUSOFF;
> > +			can_bus_off(dev);
> > +		} else if (status & SR_ES) {
> > +			state = CAN_STATE_ERROR_WARNING;
> > +		} else {
> > +			state = CAN_STATE_ERROR_ACTIVE;
> > +		}
> > +	}
> > +
> > +	/* bus error interrupt */
> > +	if (isrc == CEVTIND_BEI) {
> > +		u8 ecc = msg->data[2];
> 
> Add an empty line, please.
> 
> > +		mod->can.can_stats.bus_error++;
> > +		stats->rx_errors++;
> > +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> > +
> > +		switch (ecc & ECC_MASK) {
> > +		case ECC_BIT:
> > +			cf->data[2] |= CAN_ERR_PROT_BIT;
> > +			break;
> > +		case ECC_FORM:
> > +			cf->data[2] |= CAN_ERR_PROT_FORM;
> > +			break;
> > +		case ECC_STUFF:
> > +			cf->data[2] |= CAN_ERR_PROT_STUFF;
> > +			break;
> > +		default:
> > +			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> > +			cf->data[3] = ecc & ECC_SEG;
> > +			break;
> > +		}
> > +
> > +		if ((ecc & ECC_DIR) == 0)
> > +			cf->data[2] |= CAN_ERR_PROT_TX;
> > +	}
> > +
> > +	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
> > +					state == CAN_STATE_ERROR_PASSIVE)) {
> > +		u8 rxerr = msg->data[4];
> > +		u8 txerr = msg->data[5];
> 
> Ditto.
> 
> > +		cf->can_id |= CAN_ERR_CRTL;
> > +		if (state == CAN_STATE_ERROR_WARNING) {
> > +			mod->can.can_stats.error_warning++;
> > +			cf->data[1] = (txerr > rxerr) ?
> > +				CAN_ERR_CRTL_TX_WARNING :
> > +				CAN_ERR_CRTL_RX_WARNING;
> > +		} else {
> > +			mod->can.can_stats.error_passive++;
> > +			cf->data[1] = (txerr > rxerr) ?
> > +				CAN_ERR_CRTL_TX_PASSIVE :
> > +				CAN_ERR_CRTL_RX_PASSIVE;
> > +		}
> > +	}
> > +
> > +	mod->can.state = state;
> > +	stats->rx_errors++;
> > +	stats->rx_bytes += cf->can_dlc;
> > +	netif_rx(skb);
> > +	return 0;
> > +}
> 
> [snip]
> > +static irqreturn_t ican3_irq(int irq, void *dev_id)
> > +{
> > +	struct ican3_dev *mod = dev_id;
> > +	u8 stat;
> > +
> > +	/*
> > +	 * The interrupt status register on this device reports interrupts
> > +	 * as zeroes instead of using ones like most other devices
> > +	 */
> > +	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
> > +	if (stat == (1 << mod->num))
> > +		return IRQ_NONE;
> > +
> > +	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
> 
> Please remove this dev_dbg() as well.
> 
> [snip]
> > +/*
> > + * Startup an ICAN module, bringing it into fast mode
> > + */
> > +static int __devinit ican3_startup_module(struct ican3_dev *mod)
> > +{
> > +	int ret;
> > +
> > +	ret = ican3_reset_module(mod);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to reset module\n");
> > +		return ret;
> > +	}
> > +
> > +	/* re-enable interrupts so we can send messages */
> > +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
> > +
> > +	ret = ican3_msg_connect(mod);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to connect to module\n");
> > +		return ret;
> > +	}
> > +
> > +	ican3_init_new_host_interface(mod);
> > +	ret = ican3_msg_newhostif(mod);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to switch to new-style interface\n");
> > +		return ret;
> > +	}
> > +
> > +	ret = ican3_set_termination(mod, true);
> > +	if (ret) {
> > +		dev_err(mod->dev, "unable to enable termination\n");
> > +		return ret;
> > +	}
> 
> 
> Could you please allow the user to disable termination, e.g. via module parameter
> "bus_termination=0" in case he uses a cable with built-in termination.
> 

What would you think about a sysfs node instead, so it could be changed
at runtime, on a per-daughtercard basis? Do you think enabling bus
termination is a good default? IMO, it is a pretty safe default for most
users.

> [snip]
> > +static int __devinit ican3_probe(struct platform_device *pdev)
> > +{
> > +	struct janz_platform_data *pdata;
> > +	struct net_device *ndev;
> > +	struct ican3_dev *mod;
> > +	struct resource *res;
> > +	struct device *dev;
> > +	int ret;
> > +
> > +	pdata = pdev->dev.platform_data;
> > +	if (!pdata)
> > +		return -ENXIO;
> > +
> > +	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
> > +
> > +	/* save the struct device for printing */
> > +	dev = &pdev->dev;
> > +
> > +	/* allocate the CAN device and private data */
> > +	ndev = alloc_candev(sizeof(*mod), 0);
> > +	if (!ndev) {
> > +		dev_err(dev, "unable to allocate CANdev\n");
> > +		ret = -ENOMEM;
> > +		goto out_return;
> > +	}
> > +
> > +	platform_set_drvdata(pdev, ndev);
> > +	mod = netdev_priv(ndev);
> > +	mod->ndev = ndev;
> > +	mod->dev = &pdev->dev;
> > +	mod->num = pdata->modno;
> > +	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
> > +	spin_lock_init(&mod->lock);
> > +
> > +	/* the first unallocated page in the DPM is 9 */
> > +	mod->free_page = DPM_FREE_START;
> > +
> > +	ndev->netdev_ops = &ican3_netdev_ops;
> > +	ndev->flags |= IFF_ECHO;
> > +	SET_NETDEV_DEV(ndev, &pdev->dev);
> > +
> > +	mod->can.clock.freq = 8000000;
> 
> Please use a constant here.
> [snip]
> 
> Please fix and resubmit with my:
> 
> "Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>"
> 
> for the SocketCAN part.
> 

Thanks for the review!
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-19  9:01   ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-19  9:01 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core, netdev, sameo

Hi Ira,

we already discussed this patch on the SocketCAN mailing list and there
are just a few minor issues and the request to add support for the new
"berr-reporting" option, if feasible. See:

  commit 52c793f24054f5dc30d228e37e0e19cc8313f086
  Author: Wolfgang Grandegger <wg@grandegger.com>
  Date:   Mon Feb 22 22:21:17 2010 +0000

    can: netlink support for bus-error reporting and counters
    
    This patch makes the bus-error reporting configurable and allows to
    retrieve the CAN TX and RX bus error counters via netlink interface.
    I have added support for the SJA1000. The TX and RX bus error counters
    are also copied to the data fields 6..7 of error messages when state
    changes are reported.

Should not be a big deal.

More inline...

Ira W. Snyder wrote:
> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> MODULbus carrier board. It is an intelligent CAN controller with a
> microcontroller and associated firmware.
> 
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> Cc: socketcan-core@lists.berlios.de
> Cc: netdev@vger.kernel.org
> ---
>  drivers/net/can/Kconfig      |   10 +
>  drivers/net/can/Makefile     |    1 +
>  drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1670 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/can/janz-ican3.c
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 05b7517..2c5227c 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -63,6 +63,16 @@ config CAN_BFIN
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called bfin_can.
>  
> +config CAN_JANZ_ICAN3
> +	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
> +	depends on CAN_DEV && MFD_JANZ_CMODIO
> +	---help---
> +	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
> +	  connects to a MODULbus carrier board.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called janz-ican3.ko.
> +
>  source "drivers/net/can/mscan/Kconfig"
>  
>  source "drivers/net/can/sja1000/Kconfig"
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index 7a702f2..9047cd0 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
>  obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
>  obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
> +obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
>  
>  ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
> new file mode 100644
> index 0000000..94d4995
> --- /dev/null
> +++ b/drivers/net/can/janz-ican3.c
[snip]
 +struct ican3_dev {
> +
> +	/* must be the first member */
> +	struct can_priv can;
> +
> +	/* CAN network device */
> +	struct net_device *ndev;
> +	struct napi_struct napi;
> +
> +	/* Device for printing */
> +	struct device *dev;
> +
> +	/* module number */
> +	unsigned int num;
> +
> +	/* base address of registers and IRQ */
> +	struct janz_cmodio_onboard_regs __iomem *ctrl;
> +	struct ican3_dpm_control *dpmctrl;
> +	void __iomem *dpm;
> +	int irq;
> +
> +	/* old and new style host interface */
> +	unsigned int iftype;
> +	spinlock_t lock;

Please describe what the lock is used for.

> +	/* new host interface */
> +	unsigned int rx_int;
> +	unsigned int rx_num;
> +	unsigned int tx_num;
> +
> +	/* fast host interface */
> +	unsigned int fastrx_start;
> +	unsigned int fastrx_int;
> +	unsigned int fastrx_num;
> +	unsigned int fasttx_start;
> +	unsigned int fasttx_num;
> +
> +	/* first free DPM page */
> +	unsigned int free_page;
> +};

[snip]
> +static void ican3_to_can_frame(struct ican3_dev *mod,
> +			       struct ican3_fast_desc *desc,
> +			       struct can_frame *cf)
> +{
> +	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
> +		dev_dbg(mod->dev, "%s: old frame format\n", __func__);

This prints a debug message for every message which is not really
useful for the user. Please remove.

> +		if (desc->data[1] & ICAN3_SFF_RTR)
> +			cf->can_id |= CAN_RTR_FLAG;
> +
> +		cf->can_id |= desc->data[0] << 3;
> +		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
> +		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
> +		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
> +	} else {
> +		dev_dbg(mod->dev, "%s: new frame format\n", __func__);

Ditto.

> +		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
> +		if (desc->data[0] & ICAN3_EFF_RTR)
> +			cf->can_id |= CAN_RTR_FLAG;
> +
> +		if (desc->data[0] & ICAN3_EFF) {
> +			cf->can_id |= CAN_EFF_FLAG;
> +			cf->can_id |= desc->data[2] << 21; /* 28-21 */
> +			cf->can_id |= desc->data[3] << 13; /* 20-13 */
> +			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
> +			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
> +		} else {
> +			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
> +			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
> +		}
> +
> +		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
> +	}
> +}
> +
> +static void can_frame_to_ican3(struct ican3_dev *mod,
> +			       struct can_frame *cf,
> +			       struct ican3_fast_desc *desc)
> +{
> +	/* clear out any stale data in the descriptor */
> +	memset(desc->data, 0, sizeof(desc->data));
> +
> +	/* we always use the extended format, with the ECHO flag set */
> +	desc->command = ICAN3_CAN_TYPE_EFF;
> +	desc->data[0] |= cf->can_dlc;
> +	desc->data[1] |= ICAN3_ECHO;
> +
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		desc->data[0] |= ICAN3_EFF_RTR;
> +
> +	/* pack the id into the correct places */
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		dev_dbg(mod->dev, "%s: extended frame\n", __func__);

Ditto.

> +		desc->data[0] |= ICAN3_EFF;
> +		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
> +		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
> +		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
> +		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
> +	} else {
> +		dev_dbg(mod->dev, "%s: standard frame\n", __func__);

Ditto.

> +		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
> +		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
> +	}
> +
> +	/* copy the data bits into the descriptor */
> +	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
> +}

[snip]
> +/*
> + * Handle CAN Event Indication Messages from the firmware
> + *
> + * The ICAN3 firmware provides the values of some SJA1000 registers when it
> + * generates this message. The code below is largely copied from the
> + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
> + */
> +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	struct net_device *dev = mod->ndev;
> +	struct net_device_stats *stats = &dev->stats;
> +	enum can_state state = mod->can.state;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	u8 status, isrc;
> +
> +	/* we can only handle the SJA1000 part */
> +	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
> +		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
> +		return -ENODEV;
> +	}
> +
> +	/* check the message length for sanity */
> +	if (msg->len < 6) {
> +		dev_err(mod->dev, "error message too short\n");
> +		return -EINVAL;
> +	}
> +
> +	skb = alloc_can_err_skb(dev, &cf);
> +	if (skb == NULL)
> +		return -ENOMEM;
> +
> +	isrc = msg->data[0];
> +	status = msg->data[3];
> +
> +	/* data overrun interrupt */
> +	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {

Here and for the other errors below a dev_dbg() would be useful. Please
check sja1000.c.

> +		cf->can_id |= CAN_ERR_CRTL;
> +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +		stats->rx_over_errors++;
> +		stats->rx_errors++;
> +		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);

s/dev_info/dev_dbg/ ?

> +	}
> +
> +	/* error warning interrupt */
> +	if (isrc == CEVTIND_EI) {
> +		if (status & SR_BS) {
> +			state = CAN_STATE_BUS_OFF;
> +			cf->can_id |= CAN_ERR_BUSOFF;
> +			can_bus_off(dev);
> +		} else if (status & SR_ES) {
> +			state = CAN_STATE_ERROR_WARNING;
> +		} else {
> +			state = CAN_STATE_ERROR_ACTIVE;
> +		}
> +	}
> +
> +	/* bus error interrupt */
> +	if (isrc == CEVTIND_BEI) {
> +		u8 ecc = msg->data[2];

Add an empty line, please.

> +		mod->can.can_stats.bus_error++;
> +		stats->rx_errors++;
> +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +		switch (ecc & ECC_MASK) {
> +		case ECC_BIT:
> +			cf->data[2] |= CAN_ERR_PROT_BIT;
> +			break;
> +		case ECC_FORM:
> +			cf->data[2] |= CAN_ERR_PROT_FORM;
> +			break;
> +		case ECC_STUFF:
> +			cf->data[2] |= CAN_ERR_PROT_STUFF;
> +			break;
> +		default:
> +			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +			cf->data[3] = ecc & ECC_SEG;
> +			break;
> +		}
> +
> +		if ((ecc & ECC_DIR) == 0)
> +			cf->data[2] |= CAN_ERR_PROT_TX;
> +	}
> +
> +	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
> +					state == CAN_STATE_ERROR_PASSIVE)) {
> +		u8 rxerr = msg->data[4];
> +		u8 txerr = msg->data[5];

Ditto.

> +		cf->can_id |= CAN_ERR_CRTL;
> +		if (state == CAN_STATE_ERROR_WARNING) {
> +			mod->can.can_stats.error_warning++;
> +			cf->data[1] = (txerr > rxerr) ?
> +				CAN_ERR_CRTL_TX_WARNING :
> +				CAN_ERR_CRTL_RX_WARNING;
> +		} else {
> +			mod->can.can_stats.error_passive++;
> +			cf->data[1] = (txerr > rxerr) ?
> +				CAN_ERR_CRTL_TX_PASSIVE :
> +				CAN_ERR_CRTL_RX_PASSIVE;
> +		}
> +	}
> +
> +	mod->can.state = state;
> +	stats->rx_errors++;
> +	stats->rx_bytes += cf->can_dlc;
> +	netif_rx(skb);
> +	return 0;
> +}

[snip]
> +static irqreturn_t ican3_irq(int irq, void *dev_id)
> +{
> +	struct ican3_dev *mod = dev_id;
> +	u8 stat;
> +
> +	/*
> +	 * The interrupt status register on this device reports interrupts
> +	 * as zeroes instead of using ones like most other devices
> +	 */
> +	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
> +	if (stat == (1 << mod->num))
> +		return IRQ_NONE;
> +
> +	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);

Please remove this dev_dbg() as well.

[snip]
> +/*
> + * Startup an ICAN module, bringing it into fast mode
> + */
> +static int __devinit ican3_startup_module(struct ican3_dev *mod)
> +{
> +	int ret;
> +
> +	ret = ican3_reset_module(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to reset module\n");
> +		return ret;
> +	}
> +
> +	/* re-enable interrupts so we can send messages */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
> +
> +	ret = ican3_msg_connect(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to connect to module\n");
> +		return ret;
> +	}
> +
> +	ican3_init_new_host_interface(mod);
> +	ret = ican3_msg_newhostif(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to switch to new-style interface\n");
> +		return ret;
> +	}
> +
> +	ret = ican3_set_termination(mod, true);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to enable termination\n");
> +		return ret;
> +	}


Could you please allow the user to disable termination, e.g. via module parameter
"bus_termination=0" in case he uses a cable with built-in termination.

[snip]
> +static int __devinit ican3_probe(struct platform_device *pdev)
> +{
> +	struct janz_platform_data *pdata;
> +	struct net_device *ndev;
> +	struct ican3_dev *mod;
> +	struct resource *res;
> +	struct device *dev;
> +	int ret;
> +
> +	pdata = pdev->dev.platform_data;
> +	if (!pdata)
> +		return -ENXIO;
> +
> +	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
> +
> +	/* save the struct device for printing */
> +	dev = &pdev->dev;
> +
> +	/* allocate the CAN device and private data */
> +	ndev = alloc_candev(sizeof(*mod), 0);
> +	if (!ndev) {
> +		dev_err(dev, "unable to allocate CANdev\n");
> +		ret = -ENOMEM;
> +		goto out_return;
> +	}
> +
> +	platform_set_drvdata(pdev, ndev);
> +	mod = netdev_priv(ndev);
> +	mod->ndev = ndev;
> +	mod->dev = &pdev->dev;
> +	mod->num = pdata->modno;
> +	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
> +	spin_lock_init(&mod->lock);
> +
> +	/* the first unallocated page in the DPM is 9 */
> +	mod->free_page = DPM_FREE_START;
> +
> +	ndev->netdev_ops = &ican3_netdev_ops;
> +	ndev->flags |= IFF_ECHO;
> +	SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> +	mod->can.clock.freq = 8000000;

Please use a constant here.
[snip]

Please fix and resubmit with my:

"Acked-by: Wolfgang Grandegger <wg@grandegger.com>"

for the SocketCAN part.

Thanks,

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-19  9:01   ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-19  9:01 UTC (permalink / raw)
  To: Ira W. Snyder
  Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w,
	netdev-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	sameo-VuQAYsv1563Yd54FQh9/CA

Hi Ira,

we already discussed this patch on the SocketCAN mailing list and there
are just a few minor issues and the request to add support for the new
"berr-reporting" option, if feasible. See:

  commit 52c793f24054f5dc30d228e37e0e19cc8313f086
  Author: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>
  Date:   Mon Feb 22 22:21:17 2010 +0000

    can: netlink support for bus-error reporting and counters
    
    This patch makes the bus-error reporting configurable and allows to
    retrieve the CAN TX and RX bus error counters via netlink interface.
    I have added support for the SJA1000. The TX and RX bus error counters
    are also copied to the data fields 6..7 of error messages when state
    changes are reported.

Should not be a big deal.

More inline...

Ira W. Snyder wrote:
> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> MODULbus carrier board. It is an intelligent CAN controller with a
> microcontroller and associated firmware.
> 
> Signed-off-by: Ira W. Snyder <iws-lulEs6mt1IksTUYHLfqkUA@public.gmane.org>
> Cc: socketcan-core-0fE9KPoRgkgATYTw5x5z8w@public.gmane.org
> Cc: netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> ---
>  drivers/net/can/Kconfig      |   10 +
>  drivers/net/can/Makefile     |    1 +
>  drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1670 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/can/janz-ican3.c
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 05b7517..2c5227c 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -63,6 +63,16 @@ config CAN_BFIN
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called bfin_can.
>  
> +config CAN_JANZ_ICAN3
> +	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
> +	depends on CAN_DEV && MFD_JANZ_CMODIO
> +	---help---
> +	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
> +	  connects to a MODULbus carrier board.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called janz-ican3.ko.
> +
>  source "drivers/net/can/mscan/Kconfig"
>  
>  source "drivers/net/can/sja1000/Kconfig"
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index 7a702f2..9047cd0 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
>  obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
>  obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
> +obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
>  
>  ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
> new file mode 100644
> index 0000000..94d4995
> --- /dev/null
> +++ b/drivers/net/can/janz-ican3.c
[snip]
 +struct ican3_dev {
> +
> +	/* must be the first member */
> +	struct can_priv can;
> +
> +	/* CAN network device */
> +	struct net_device *ndev;
> +	struct napi_struct napi;
> +
> +	/* Device for printing */
> +	struct device *dev;
> +
> +	/* module number */
> +	unsigned int num;
> +
> +	/* base address of registers and IRQ */
> +	struct janz_cmodio_onboard_regs __iomem *ctrl;
> +	struct ican3_dpm_control *dpmctrl;
> +	void __iomem *dpm;
> +	int irq;
> +
> +	/* old and new style host interface */
> +	unsigned int iftype;
> +	spinlock_t lock;

Please describe what the lock is used for.

> +	/* new host interface */
> +	unsigned int rx_int;
> +	unsigned int rx_num;
> +	unsigned int tx_num;
> +
> +	/* fast host interface */
> +	unsigned int fastrx_start;
> +	unsigned int fastrx_int;
> +	unsigned int fastrx_num;
> +	unsigned int fasttx_start;
> +	unsigned int fasttx_num;
> +
> +	/* first free DPM page */
> +	unsigned int free_page;
> +};

[snip]
> +static void ican3_to_can_frame(struct ican3_dev *mod,
> +			       struct ican3_fast_desc *desc,
> +			       struct can_frame *cf)
> +{
> +	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
> +		dev_dbg(mod->dev, "%s: old frame format\n", __func__);

This prints a debug message for every message which is not really
useful for the user. Please remove.

> +		if (desc->data[1] & ICAN3_SFF_RTR)
> +			cf->can_id |= CAN_RTR_FLAG;
> +
> +		cf->can_id |= desc->data[0] << 3;
> +		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
> +		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
> +		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
> +	} else {
> +		dev_dbg(mod->dev, "%s: new frame format\n", __func__);

Ditto.

> +		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
> +		if (desc->data[0] & ICAN3_EFF_RTR)
> +			cf->can_id |= CAN_RTR_FLAG;
> +
> +		if (desc->data[0] & ICAN3_EFF) {
> +			cf->can_id |= CAN_EFF_FLAG;
> +			cf->can_id |= desc->data[2] << 21; /* 28-21 */
> +			cf->can_id |= desc->data[3] << 13; /* 20-13 */
> +			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
> +			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
> +		} else {
> +			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
> +			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
> +		}
> +
> +		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
> +	}
> +}
> +
> +static void can_frame_to_ican3(struct ican3_dev *mod,
> +			       struct can_frame *cf,
> +			       struct ican3_fast_desc *desc)
> +{
> +	/* clear out any stale data in the descriptor */
> +	memset(desc->data, 0, sizeof(desc->data));
> +
> +	/* we always use the extended format, with the ECHO flag set */
> +	desc->command = ICAN3_CAN_TYPE_EFF;
> +	desc->data[0] |= cf->can_dlc;
> +	desc->data[1] |= ICAN3_ECHO;
> +
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		desc->data[0] |= ICAN3_EFF_RTR;
> +
> +	/* pack the id into the correct places */
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		dev_dbg(mod->dev, "%s: extended frame\n", __func__);

Ditto.

> +		desc->data[0] |= ICAN3_EFF;
> +		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
> +		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
> +		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
> +		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
> +	} else {
> +		dev_dbg(mod->dev, "%s: standard frame\n", __func__);

Ditto.

> +		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
> +		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
> +	}
> +
> +	/* copy the data bits into the descriptor */
> +	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
> +}

[snip]
> +/*
> + * Handle CAN Event Indication Messages from the firmware
> + *
> + * The ICAN3 firmware provides the values of some SJA1000 registers when it
> + * generates this message. The code below is largely copied from the
> + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
> + */
> +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	struct net_device *dev = mod->ndev;
> +	struct net_device_stats *stats = &dev->stats;
> +	enum can_state state = mod->can.state;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	u8 status, isrc;
> +
> +	/* we can only handle the SJA1000 part */
> +	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
> +		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
> +		return -ENODEV;
> +	}
> +
> +	/* check the message length for sanity */
> +	if (msg->len < 6) {
> +		dev_err(mod->dev, "error message too short\n");
> +		return -EINVAL;
> +	}
> +
> +	skb = alloc_can_err_skb(dev, &cf);
> +	if (skb == NULL)
> +		return -ENOMEM;
> +
> +	isrc = msg->data[0];
> +	status = msg->data[3];
> +
> +	/* data overrun interrupt */
> +	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {

Here and for the other errors below a dev_dbg() would be useful. Please
check sja1000.c.

> +		cf->can_id |= CAN_ERR_CRTL;
> +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +		stats->rx_over_errors++;
> +		stats->rx_errors++;
> +		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);

s/dev_info/dev_dbg/ ?

> +	}
> +
> +	/* error warning interrupt */
> +	if (isrc == CEVTIND_EI) {
> +		if (status & SR_BS) {
> +			state = CAN_STATE_BUS_OFF;
> +			cf->can_id |= CAN_ERR_BUSOFF;
> +			can_bus_off(dev);
> +		} else if (status & SR_ES) {
> +			state = CAN_STATE_ERROR_WARNING;
> +		} else {
> +			state = CAN_STATE_ERROR_ACTIVE;
> +		}
> +	}
> +
> +	/* bus error interrupt */
> +	if (isrc == CEVTIND_BEI) {
> +		u8 ecc = msg->data[2];

Add an empty line, please.

> +		mod->can.can_stats.bus_error++;
> +		stats->rx_errors++;
> +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +		switch (ecc & ECC_MASK) {
> +		case ECC_BIT:
> +			cf->data[2] |= CAN_ERR_PROT_BIT;
> +			break;
> +		case ECC_FORM:
> +			cf->data[2] |= CAN_ERR_PROT_FORM;
> +			break;
> +		case ECC_STUFF:
> +			cf->data[2] |= CAN_ERR_PROT_STUFF;
> +			break;
> +		default:
> +			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +			cf->data[3] = ecc & ECC_SEG;
> +			break;
> +		}
> +
> +		if ((ecc & ECC_DIR) == 0)
> +			cf->data[2] |= CAN_ERR_PROT_TX;
> +	}
> +
> +	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
> +					state == CAN_STATE_ERROR_PASSIVE)) {
> +		u8 rxerr = msg->data[4];
> +		u8 txerr = msg->data[5];

Ditto.

> +		cf->can_id |= CAN_ERR_CRTL;
> +		if (state == CAN_STATE_ERROR_WARNING) {
> +			mod->can.can_stats.error_warning++;
> +			cf->data[1] = (txerr > rxerr) ?
> +				CAN_ERR_CRTL_TX_WARNING :
> +				CAN_ERR_CRTL_RX_WARNING;
> +		} else {
> +			mod->can.can_stats.error_passive++;
> +			cf->data[1] = (txerr > rxerr) ?
> +				CAN_ERR_CRTL_TX_PASSIVE :
> +				CAN_ERR_CRTL_RX_PASSIVE;
> +		}
> +	}
> +
> +	mod->can.state = state;
> +	stats->rx_errors++;
> +	stats->rx_bytes += cf->can_dlc;
> +	netif_rx(skb);
> +	return 0;
> +}

[snip]
> +static irqreturn_t ican3_irq(int irq, void *dev_id)
> +{
> +	struct ican3_dev *mod = dev_id;
> +	u8 stat;
> +
> +	/*
> +	 * The interrupt status register on this device reports interrupts
> +	 * as zeroes instead of using ones like most other devices
> +	 */
> +	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
> +	if (stat == (1 << mod->num))
> +		return IRQ_NONE;
> +
> +	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);

Please remove this dev_dbg() as well.

[snip]
> +/*
> + * Startup an ICAN module, bringing it into fast mode
> + */
> +static int __devinit ican3_startup_module(struct ican3_dev *mod)
> +{
> +	int ret;
> +
> +	ret = ican3_reset_module(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to reset module\n");
> +		return ret;
> +	}
> +
> +	/* re-enable interrupts so we can send messages */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
> +
> +	ret = ican3_msg_connect(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to connect to module\n");
> +		return ret;
> +	}
> +
> +	ican3_init_new_host_interface(mod);
> +	ret = ican3_msg_newhostif(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to switch to new-style interface\n");
> +		return ret;
> +	}
> +
> +	ret = ican3_set_termination(mod, true);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to enable termination\n");
> +		return ret;
> +	}


Could you please allow the user to disable termination, e.g. via module parameter
"bus_termination=0" in case he uses a cable with built-in termination.

[snip]
> +static int __devinit ican3_probe(struct platform_device *pdev)
> +{
> +	struct janz_platform_data *pdata;
> +	struct net_device *ndev;
> +	struct ican3_dev *mod;
> +	struct resource *res;
> +	struct device *dev;
> +	int ret;
> +
> +	pdata = pdev->dev.platform_data;
> +	if (!pdata)
> +		return -ENXIO;
> +
> +	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
> +
> +	/* save the struct device for printing */
> +	dev = &pdev->dev;
> +
> +	/* allocate the CAN device and private data */
> +	ndev = alloc_candev(sizeof(*mod), 0);
> +	if (!ndev) {
> +		dev_err(dev, "unable to allocate CANdev\n");
> +		ret = -ENOMEM;
> +		goto out_return;
> +	}
> +
> +	platform_set_drvdata(pdev, ndev);
> +	mod = netdev_priv(ndev);
> +	mod->ndev = ndev;
> +	mod->dev = &pdev->dev;
> +	mod->num = pdata->modno;
> +	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
> +	spin_lock_init(&mod->lock);
> +
> +	/* the first unallocated page in the DPM is 9 */
> +	mod->free_page = DPM_FREE_START;
> +
> +	ndev->netdev_ops = &ican3_netdev_ops;
> +	ndev->flags |= IFF_ECHO;
> +	SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> +	mod->can.clock.freq = 8000000;

Please use a constant here.
[snip]

Please fix and resubmit with my:

"Acked-by: Wolfgang Grandegger <wg-5Yr1BZd7O62+XT7JhA+gdA@public.gmane.org>"

for the SocketCAN part.

Thanks,

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
@ 2010-03-18 16:38 Ira W. Snyder
  2010-03-19  9:01   ` Wolfgang Grandegger
  0 siblings, 1 reply; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-18 16:38 UTC (permalink / raw)
  To: linux-kernel; +Cc: netdev, sameo, socketcan-core

The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
MODULbus carrier board. It is an intelligent CAN controller with a
microcontroller and associated firmware.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
Cc: socketcan-core@lists.berlios.de
Cc: netdev@vger.kernel.org
---
 drivers/net/can/Kconfig      |   10 +
 drivers/net/can/Makefile     |    1 +
 drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1670 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/janz-ican3.c

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 05b7517..2c5227c 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -63,6 +63,16 @@ config CAN_BFIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called bfin_can.
 
+config CAN_JANZ_ICAN3
+	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
+	depends on CAN_DEV && MFD_JANZ_CMODIO
+	---help---
+	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
+	  connects to a MODULbus carrier board.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called janz-ican3.ko.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 7a702f2..9047cd0 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
+obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
new file mode 100644
index 0000000..94d4995
--- /dev/null
+++ b/drivers/net/can/janz-ican3.c
@@ -0,0 +1,1659 @@
+/*
+ * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include <linux/mfd/janz.h>
+
+/* the DPM has 64k of memory, organized into 256x 256 byte pages */
+#define DPM_NUM_PAGES		256
+#define DPM_PAGE_SIZE		256
+#define DPM_PAGE_ADDR(p)	((p) * DPM_PAGE_SIZE)
+
+/* JANZ ICAN3 "old-style" host interface queue page numbers */
+#define QUEUE_OLD_CONTROL	0
+#define QUEUE_OLD_RB0		1
+#define QUEUE_OLD_RB1		2
+#define QUEUE_OLD_WB0		3
+#define QUEUE_OLD_WB1		4
+
+/* Janz ICAN3 "old-style" host interface control registers */
+#define MSYNC_PEER		0x00		/* ICAN only */
+#define MSYNC_LOCL		0x01		/* host only */
+#define TARGET_RUNNING		0x02
+
+#define MSYNC_RB0		0x01
+#define MSYNC_RB1		0x02
+#define MSYNC_RBLW		0x04
+#define MSYNC_RB_MASK		(MSYNC_RB0 | MSYNC_RB1)
+
+#define MSYNC_WB0		0x10
+#define MSYNC_WB1		0x20
+#define MSYNC_WBLW		0x40
+#define MSYNC_WB_MASK		(MSYNC_WB0 | MSYNC_WB1)
+
+/* Janz ICAN3 "new-style" host interface queue page numbers */
+#define QUEUE_TOHOST		5
+#define QUEUE_FROMHOST_MID	6
+#define QUEUE_FROMHOST_HIGH	7
+#define QUEUE_FROMHOST_LOW	8
+
+/* The first free page in the DPM is #9 */
+#define DPM_FREE_START		9
+
+/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
+#define DESC_VALID		0x80
+#define DESC_WRAP		0x40
+#define DESC_INTERRUPT		0x20
+#define DESC_IVALID		0x10
+#define DESC_LEN(len)		(len)
+
+/* Janz ICAN3 Firmware Messages */
+#define MSG_CONNECTI		0x02
+#define MSG_DISCONNECT		0x03
+#define MSG_IDVERS		0x04
+#define MSG_MSGLOST		0x05
+#define MSG_NEWHOSTIF		0x08
+#define MSG_SETAFILMASK		0x10
+#define MSG_INITFDPMQUEUE	0x11
+#define MSG_HWCONF		0x12
+#define MSG_FMSGLOST		0x15
+#define MSG_CEVTIND		0x37
+#define MSG_CBTRREQ		0x41
+#define MSG_COFFREQ		0x42
+#define MSG_CONREQ		0x43
+#define MSG_CCONFREQ		0x47
+
+/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
+#define SETAFILMASK_REJECT	0x00
+#define SETAFILMASK_FASTIF	0x02
+
+/* Janz ICAN3 CAN Hardware Configuration Message Types */
+#define HWCONF_TERMINATE_ON	0x01
+#define HWCONF_TERMINATE_OFF	0x00
+
+/* Janz ICAN3 CAN Event Indication Message Types */
+#define CEVTIND_EI		0x01
+#define CEVTIND_DOI		0x02
+#define CEVTIND_LOST		0x04
+#define CEVTIND_FULL		0x08
+#define CEVTIND_BEI		0x10
+
+#define CEVTIND_CHIP_SJA1000	0x02
+
+#define ICAN3_BUSERR_QUOTA_MAX	255
+
+/* Janz ICAN3 CAN Frame Conversion */
+#define ICAN3_ECHO	0x10
+#define ICAN3_EFF_RTR	0x40
+#define ICAN3_SFF_RTR	0x10
+#define ICAN3_EFF	0x80
+
+#define ICAN3_CAN_TYPE_MASK	0x0f
+#define ICAN3_CAN_TYPE_SFF	0x00
+#define ICAN3_CAN_TYPE_EFF	0x01
+
+#define ICAN3_CAN_DLC_MASK	0x0f
+
+/*
+ * SJA1000 Status and Error Register Definitions
+ *
+ * Copied from drivers/net/can/sja1000/sja1000.h
+ */
+
+/* status register content */
+#define SR_BS		0x80
+#define SR_ES		0x40
+#define SR_TS		0x20
+#define SR_RS		0x10
+#define SR_TCS		0x08
+#define SR_TBS		0x04
+#define SR_DOS		0x02
+#define SR_RBS		0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG		0x1F
+#define ECC_DIR		0x20
+#define ECC_ERR		6
+#define ECC_BIT		0x00
+#define ECC_FORM	0x40
+#define ECC_STUFF	0x80
+#define ECC_MASK	0xc0
+
+/* Number of buffers for use in the "new-style" host interface */
+#define ICAN3_NEW_BUFFERS	16
+
+/* Number of buffers for use in the "fast" host interface */
+#define ICAN3_TX_BUFFERS	512
+#define ICAN3_RX_BUFFERS	1024
+
+/* Driver Name */
+#define DRV_NAME "janz-ican3"
+
+/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
+struct ican3_dpm_control {
+	/* window address register */
+	u8 window_address;
+	u8 unused1;
+
+	/*
+	 * Read access: clear interrupt from microcontroller
+	 * Write access: send interrupt to microcontroller
+	 */
+	u8 interrupt;
+	u8 unused2;
+
+	/* write-only: reset all hardware on the module */
+	u8 hwreset;
+	u8 unused3;
+
+	/* write-only: generate an interrupt to the TPU */
+	u8 tpuinterrupt;
+};
+
+struct ican3_dev {
+
+	/* must be the first member */
+	struct can_priv can;
+
+	/* CAN network device */
+	struct net_device *ndev;
+	struct napi_struct napi;
+
+	/* Device for printing */
+	struct device *dev;
+
+	/* module number */
+	unsigned int num;
+
+	/* base address of registers and IRQ */
+	struct janz_cmodio_onboard_regs __iomem *ctrl;
+	struct ican3_dpm_control *dpmctrl;
+	void __iomem *dpm;
+	int irq;
+
+	/* old and new style host interface */
+	unsigned int iftype;
+	spinlock_t lock;
+
+	/* new host interface */
+	unsigned int rx_int;
+	unsigned int rx_num;
+	unsigned int tx_num;
+
+	/* fast host interface */
+	unsigned int fastrx_start;
+	unsigned int fastrx_int;
+	unsigned int fastrx_num;
+	unsigned int fasttx_start;
+	unsigned int fasttx_num;
+
+	/* first free DPM page */
+	unsigned int free_page;
+};
+
+struct ican3_msg {
+	u8 control;
+	u8 spec;
+	__le16 len;
+	u8 data[252];
+};
+
+struct ican3_new_desc {
+	u8 control;
+	u8 pointer;
+};
+
+struct ican3_fast_desc {
+	u8 control;
+	u8 command;
+	u8 data[14];
+};
+
+/* write to the window basic address register */
+static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
+{
+	BUG_ON(page >= DPM_NUM_PAGES);
+	iowrite8(page, &mod->dpmctrl->window_address);
+}
+
+/*
+ * ICAN3 "old-style" host interface
+ */
+
+/*
+ * Recieve a message from the ICAN3 "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no message exists
+ */
+static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_RB_MASK) == 0x00) {
+		dev_dbg(mod->dev, "no mbox for reading\n");
+		return -ENOMEM;
+	}
+
+	/* find the first free mbox to read */
+	if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
+		mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
+	else
+		mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
+
+	/* copy the message */
+	mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/*
+	 * notify the firmware that the read buffer is available
+	 * for it to fill again
+	 */
+	locl ^= mbox;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * Send a message through the "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no free space exists
+ */
+static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
+		dev_err(mod->dev, "no mbox for writing\n");
+		return -ENOMEM;
+	}
+
+	/* calculate a free mbox to use */
+	mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
+
+	/* copy the message to the DPM */
+	mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	locl ^= mbox;
+	if (mbox == MSYNC_WB1)
+		locl |= MSYNC_WBLW;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Setup
+ */
+
+static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_new_desc desc;
+	unsigned long flags;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* setup the internal datastructures for RX */
+	mod->rx_num = 0;
+	mod->rx_int = 0;
+
+	/* tohost queue descriptors are in page 5 */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	dst = mod->dpm;
+
+	/* initialize the tohost (rx) queue descriptors: pages 9-24 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost (tx) mid queue descriptors are in page 6 */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	dst = mod->dpm;
+
+	/* setup the internal datastructures for TX */
+	mod->tx_num = 0;
+
+	/* initialize the fromhost mid queue descriptors: pages 25-40 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost hi queue descriptors are in page 7 */
+	ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost hi queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	/* fromhost low queue descriptors are in page 8 */
+	ican3_set_page(mod, QUEUE_FROMHOST_LOW);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost low queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 Fast Host Interface Setup
+ */
+
+static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc desc;
+	unsigned long flags;
+	unsigned int addr;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* save the start recv page */
+	mod->fastrx_start = mod->free_page;
+	mod->fastrx_num = 0;
+	mod->fastrx_int = 0;
+
+	/* build a single fast tohost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = 0x00;
+	desc.command = 1;
+
+	/* build the tohost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_RX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	/* make sure we page-align the next queue */
+	if (addr != 0)
+		mod->free_page++;
+
+	/* save the start xmit page */
+	mod->fasttx_start = mod->free_page;
+	mod->fasttx_num = 0;
+
+	/* build a single fast fromhost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = DESC_VALID;
+	desc.command = 1;
+
+	/* build the fromhost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_TX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Message Helpers
+ */
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
+
+	/* switch to the fromhost mid queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	/* switch back to the descriptor, set the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the tx number */
+	mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
+	return 0;
+}
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
+
+	/* switch to the tohost queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/* switch back to the descriptor, toggle the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the rx number */
+	mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
+	return 0;
+}
+
+/*
+ * Message Send / Recv Helpers
+ */
+
+static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_send_msg(mod, msg);
+	else
+		ret = ican3_new_send_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_recv_msg(mod, msg);
+	else
+		ret = ican3_new_recv_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+/*
+ * Quick Pre-constructed Messages
+ */
+
+static int __devinit ican3_msg_connect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CONNECTI;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_DISCONNECT;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_NEWHOSTIF;
+	msg.len = cpu_to_le16(0);
+
+	/* If we're not using the old interface, switching seems bogus */
+	WARN_ON(mod->iftype != 0);
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* mark the module as using the new host interface */
+	mod->iftype = 1;
+	return 0;
+}
+
+static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	unsigned int addr;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_INITFDPMQUEUE;
+	msg.len = cpu_to_le16(8);
+
+	/* write the tohost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fastrx_start);
+	msg.data[0] = addr & 0xff;
+	msg.data[1] = (addr >> 8) & 0xff;
+	msg.data[2] = (addr >> 16) & 0xff;
+	msg.data[3] = (addr >> 24) & 0xff;
+
+	/* write the fromhost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fasttx_start);
+	msg.data[4] = addr & 0xff;
+	msg.data[5] = (addr >> 8) & 0xff;
+	msg.data[6] = (addr >> 16) & 0xff;
+	msg.data[7] = (addr >> 24) & 0xff;
+
+	/* If we're not using the new interface yet, we cannot do this */
+	WARN_ON(mod->iftype != 1);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Setup the CAN filter to either accept or reject all
+ * messages from the CAN bus.
+ */
+static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	/* Standard Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(5);
+	msg.data[0] = 0x00; /* IDLo LSB */
+	msg.data[1] = 0x00; /* IDLo MSB */
+	msg.data[2] = 0xff; /* IDHi LSB */
+	msg.data[3] = 0x07; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* Extended Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(13);
+	msg.data[0] = 0;    /* MUX = 0 */
+	msg.data[1] = 0x00; /* IDLo LSB */
+	msg.data[2] = 0x00;
+	msg.data[3] = 0x00;
+	msg.data[4] = 0x20; /* IDLo MSB */
+	msg.data[5] = 0xff; /* IDHi LSB */
+	msg.data[6] = 0xff;
+	msg.data[7] = 0xff;
+	msg.data[8] = 0x3f; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Bring the CAN bus online or offline
+ */
+static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_set_termination(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_HWCONF;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_set_buserror(struct ican3_dev *mod, u8 quota)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CCONFREQ;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = quota;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * ICAN3 to Linux CAN Frame Conversion
+ */
+
+static void ican3_to_can_frame(struct ican3_dev *mod,
+			       struct ican3_fast_desc *desc,
+			       struct can_frame *cf)
+{
+	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
+		dev_dbg(mod->dev, "%s: old frame format\n", __func__);
+		if (desc->data[1] & ICAN3_SFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		cf->can_id |= desc->data[0] << 3;
+		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
+		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
+		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
+	} else {
+		dev_dbg(mod->dev, "%s: new frame format\n", __func__);
+		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
+		if (desc->data[0] & ICAN3_EFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		if (desc->data[0] & ICAN3_EFF) {
+			cf->can_id |= CAN_EFF_FLAG;
+			cf->can_id |= desc->data[2] << 21; /* 28-21 */
+			cf->can_id |= desc->data[3] << 13; /* 20-13 */
+			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
+			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
+		} else {
+			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
+			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
+		}
+
+		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
+	}
+}
+
+static void can_frame_to_ican3(struct ican3_dev *mod,
+			       struct can_frame *cf,
+			       struct ican3_fast_desc *desc)
+{
+	/* clear out any stale data in the descriptor */
+	memset(desc->data, 0, sizeof(desc->data));
+
+	/* we always use the extended format, with the ECHO flag set */
+	desc->command = ICAN3_CAN_TYPE_EFF;
+	desc->data[0] |= cf->can_dlc;
+	desc->data[1] |= ICAN3_ECHO;
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		desc->data[0] |= ICAN3_EFF_RTR;
+
+	/* pack the id into the correct places */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		dev_dbg(mod->dev, "%s: extended frame\n", __func__);
+		desc->data[0] |= ICAN3_EFF;
+		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
+		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
+		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
+		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
+	} else {
+		dev_dbg(mod->dev, "%s: standard frame\n", __func__);
+		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
+		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
+	}
+
+	/* copy the data bits into the descriptor */
+	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/*
+ * Handle an ID + Version message response from the firmware. We never generate
+ * this message in production code, but it is very useful when debugging to be
+ * able to display this message.
+ */
+static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
+}
+
+static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/*
+	 * Report that communication messages with the microcontroller firmware
+	 * are being lost. These are never CAN frames, so we do not generate an
+	 * error frame for userspace
+	 */
+	if (msg->spec == MSG_MSGLOST) {
+		dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
+		return;
+	}
+
+	/*
+	 * Oops, this indicates that we have lost messages in the fast queue,
+	 * which are exclusively CAN messages. Our driver isn't reading CAN
+	 * frames fast enough.
+	 *
+	 * We'll pretend that the SJA1000 told us that it ran out of buffer
+	 * space, because there is not a better message for this.
+	 */
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_errors++;
+		stats->rx_bytes += cf->can_dlc;
+		netif_rx(skb);
+	}
+}
+
+/*
+ * Handle CAN Event Indication Messages from the firmware
+ *
+ * The ICAN3 firmware provides the values of some SJA1000 registers when it
+ * generates this message. The code below is largely copied from the
+ * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
+ */
+static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	enum can_state state = mod->can.state;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u8 status, isrc;
+
+	/* we can only handle the SJA1000 part */
+	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
+		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
+		return -ENODEV;
+	}
+
+	/* check the message length for sanity */
+	if (msg->len < 6) {
+		dev_err(mod->dev, "error message too short\n");
+		return -EINVAL;
+	}
+
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	isrc = msg->data[0];
+	status = msg->data[3];
+
+	/* data overrun interrupt */
+	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
+	}
+
+	/* error warning interrupt */
+	if (isrc == CEVTIND_EI) {
+		if (status & SR_BS) {
+			state = CAN_STATE_BUS_OFF;
+			cf->can_id |= CAN_ERR_BUSOFF;
+			can_bus_off(dev);
+		} else if (status & SR_ES) {
+			state = CAN_STATE_ERROR_WARNING;
+		} else {
+			state = CAN_STATE_ERROR_ACTIVE;
+		}
+	}
+
+	/* bus error interrupt */
+	if (isrc == CEVTIND_BEI) {
+		u8 ecc = msg->data[2];
+		mod->can.can_stats.bus_error++;
+		stats->rx_errors++;
+		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+		switch (ecc & ECC_MASK) {
+		case ECC_BIT:
+			cf->data[2] |= CAN_ERR_PROT_BIT;
+			break;
+		case ECC_FORM:
+			cf->data[2] |= CAN_ERR_PROT_FORM;
+			break;
+		case ECC_STUFF:
+			cf->data[2] |= CAN_ERR_PROT_STUFF;
+			break;
+		default:
+			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+			cf->data[3] = ecc & ECC_SEG;
+			break;
+		}
+
+		if ((ecc & ECC_DIR) == 0)
+			cf->data[2] |= CAN_ERR_PROT_TX;
+	}
+
+	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
+					state == CAN_STATE_ERROR_PASSIVE)) {
+		u8 rxerr = msg->data[4];
+		u8 txerr = msg->data[5];
+		cf->can_id |= CAN_ERR_CRTL;
+		if (state == CAN_STATE_ERROR_WARNING) {
+			mod->can.can_stats.error_warning++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_WARNING :
+				CAN_ERR_CRTL_RX_WARNING;
+		} else {
+			mod->can.can_stats.error_passive++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_PASSIVE :
+				CAN_ERR_CRTL_RX_PASSIVE;
+		}
+	}
+
+	mod->can.state = state;
+	stats->rx_errors++;
+	stats->rx_bytes += cf->can_dlc;
+	netif_rx(skb);
+	return 0;
+}
+
+static void ican3_handle_unknown_message(struct ican3_dev *mod,
+					struct ican3_msg *msg)
+{
+	dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
+			   msg->spec, le16_to_cpu(msg->len));
+}
+
+/*
+ * Handle a control message from the firmware
+ */
+static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
+			   mod->num, msg->spec, le16_to_cpu(msg->len));
+
+	switch (msg->spec) {
+	case MSG_IDVERS:
+		ican3_handle_idvers(mod, msg);
+		break;
+	case MSG_MSGLOST:
+	case MSG_FMSGLOST:
+		ican3_handle_msglost(mod, msg);
+		break;
+	case MSG_CEVTIND:
+		ican3_handle_cevtind(mod, msg);
+		break;
+	default:
+		ican3_handle_unknown_message(mod, msg);
+		break;
+	}
+}
+
+/*
+ * Check that there is room in the TX ring to transmit another skb
+ *
+ * LOCKING: must hold mod->lock
+ */
+static bool ican3_txok(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc __iomem *desc;
+	u8 control;
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
+	control = ioread8(&desc->control);
+
+	/* if the control bits are not valid, then we have no more space */
+	if (!(control & DESC_VALID))
+		return false;
+
+	return true;
+}
+
+/*
+ * Recieve one CAN frame from the hardware
+ *
+ * This works like the core of a NAPI function, but is intended to be called
+ * from workqueue context instead. This driver already needs a workqueue to
+ * process control messages, so we use the workqueue instead of using NAPI.
+ * This was done to simplify locking.
+ *
+ * CONTEXT: must be called from user context
+ */
+static int ican3_recv_skb(struct ican3_dev *mod)
+{
+	struct net_device *ndev = mod->ndev;
+	struct net_device_stats *stats = &ndev->stats;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* copy the whole descriptor */
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* check that we actually have a CAN frame */
+	if (!(desc.control & DESC_VALID))
+		return -ENOBUFS;
+
+	/* allocate an skb */
+	skb = alloc_can_skb(ndev, &cf);
+	if (unlikely(skb == NULL)) {
+		stats->rx_dropped++;
+		goto err_noalloc;
+	}
+
+	/* convert the ICAN3 frame into Linux CAN format */
+	ican3_to_can_frame(mod, &desc, cf);
+
+	/* receive the skb, update statistics */
+	netif_receive_skb(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+
+err_noalloc:
+	/* toggle the valid bit and return the descriptor to the ring */
+	desc.control ^= DESC_VALID;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	memcpy_toio(desc_addr, &desc, 1);
+
+	/* update the next buffer pointer */
+	mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fastrx_num + 1);
+
+	/* there are still more buffers to process */
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return 0;
+}
+
+static int ican3_napi(struct napi_struct *napi, int budget)
+{
+	struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
+	struct ican3_msg msg;
+	unsigned long flags;
+	int received = 0;
+	int ret;
+
+	/* process all communication messages */
+	while (true) {
+		ret = ican3_recv_msg(mod, &msg);
+		if (ret)
+			break;
+
+		ican3_handle_message(mod, &msg);
+	}
+
+	/* process all CAN frames from the fast interface */
+	while (received < budget) {
+		ret = ican3_recv_skb(mod);
+		if (ret)
+			break;
+
+		received++;
+	}
+
+	/* We have processed all packets that the adapter had, but it
+	 * was less than our budget, stop polling */
+	if (received < budget)
+		napi_complete(napi);
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* Wake up the transmit queue if necessary */
+	if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
+		netif_wake_queue(mod->ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* re-enable interrupt generation */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+	return received;
+}
+
+static irqreturn_t ican3_irq(int irq, void *dev_id)
+{
+	struct ican3_dev *mod = dev_id;
+	u8 stat;
+
+	/*
+	 * The interrupt status register on this device reports interrupts
+	 * as zeroes instead of using ones like most other devices
+	 */
+	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
+	if (stat == (1 << mod->num))
+		return IRQ_NONE;
+
+	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
+
+	/* clear the MODULbus interrupt from the microcontroller */
+	ioread8(&mod->dpmctrl->interrupt);
+
+	/* disable interrupt generation, schedule the NAPI poller */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	napi_schedule(&mod->napi);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Firmware reset, startup, and shutdown
+ */
+
+/*
+ * Reset an ICAN module to its power-on state
+ *
+ * CONTEXT: no network device registered
+ * LOCKING: work function disabled
+ */
+static int ican3_reset_module(struct ican3_dev *mod)
+{
+	u8 val = 1 << mod->num;
+	unsigned long start;
+	u8 runold, runnew;
+
+	/* disable interrupts so no more work is scheduled */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+
+	/* flush any pending work */
+	flush_scheduled_work();
+
+	/* the first unallocated page in the DPM is #9 */
+	mod->free_page = DPM_FREE_START;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	runold = ioread8(mod->dpm + TARGET_RUNNING);
+
+	/* reset the module */
+	iowrite8(val, &mod->ctrl->reset_assert);
+	iowrite8(val, &mod->ctrl->reset_deassert);
+
+	/* wait until the module has finished resetting and is running */
+	start = jiffies;
+	do {
+		ican3_set_page(mod, QUEUE_OLD_CONTROL);
+		runnew = ioread8(mod->dpm + TARGET_RUNNING);
+		if (runnew == (runold ^ 0xff))
+			return 0;
+
+		msleep(10);
+	} while (time_before(jiffies, start + HZ / 4));
+
+	dev_err(mod->dev, "failed to reset CAN module\n");
+	return -ETIMEDOUT;
+}
+
+static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
+{
+	ican3_msg_disconnect(mod);
+	ican3_reset_module(mod);
+}
+
+/*
+ * Startup an ICAN module, bringing it into fast mode
+ */
+static int __devinit ican3_startup_module(struct ican3_dev *mod)
+{
+	int ret;
+
+	ret = ican3_reset_module(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to reset module\n");
+		return ret;
+	}
+
+	/* re-enable interrupts so we can send messages */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+
+	ret = ican3_msg_connect(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to connect to module\n");
+		return ret;
+	}
+
+	ican3_init_new_host_interface(mod);
+	ret = ican3_msg_newhostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to new-style interface\n");
+		return ret;
+	}
+
+	ret = ican3_set_termination(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to enable termination\n");
+		return ret;
+	}
+
+	ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-error\n");
+		return ret;
+	}
+
+	ican3_init_fast_host_interface(mod);
+	ret = ican3_msg_fasthostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to fast host interface\n");
+		return ret;
+	}
+
+	ret = ican3_set_id_filter(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set acceptance filter\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * CAN Network Device
+ */
+
+static int ican3_open(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	/* open the CAN layer */
+	ret = open_candev(ndev);
+	if (ret) {
+		dev_err(mod->dev, "unable to start CAN layer\n");
+		return ret;
+	}
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		close_candev(ndev);
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+	netif_start_queue(ndev);
+
+	return 0;
+}
+
+static int ican3_stop(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	/* stop the network device xmit routine */
+	netif_stop_queue(ndev);
+	mod->can.state = CAN_STATE_STOPPED;
+
+	/* bring the bus offline, stop receiving packets */
+	ret = ican3_set_bus_state(mod, false);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-off\n");
+		return ret;
+	}
+
+	/* close the CAN layer */
+	close_candev(ndev);
+	return 0;
+}
+
+static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* check that we can actually transmit */
+	if (!ican3_txok(mod)) {
+		dev_err(mod->dev, "no free descriptors, stopping queue\n");
+		netif_stop_queue(ndev);
+		spin_unlock_irqrestore(&mod->lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
+	memset(&desc, 0, sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, 1);
+
+	/* convert the Linux CAN frame into ICAN3 format */
+	can_frame_to_ican3(mod, cf, &desc);
+
+	/*
+	 * the programming manual says that you must set the IVALID bit, then
+	 * interrupt, then set the valid bit. Quite weird, but it seems to be
+	 * required for this to work
+	 */
+	desc.control |= DESC_IVALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* generate a MODULbus interrupt to the microcontroller */
+	iowrite8(0x01, &mod->dpmctrl->interrupt);
+
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the next buffer pointer */
+	mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fasttx_num + 1);
+
+	/* update statistics */
+	stats->tx_packets++;
+	stats->tx_bytes += cf->can_dlc;
+	kfree_skb(skb);
+
+	/*
+	 * This hardware doesn't have TX-done notifications, so we'll try and
+	 * emulate it the best we can using ECHO skbs. Get the next TX
+	 * descriptor, and see if we have room to send. If not, stop the queue.
+	 * It will be woken when the ECHO skb for the current packet is recv'd.
+	 */
+
+	/* copy the control bits of the descriptor */
+	if (!ican3_txok(mod))
+		netif_stop_queue(ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ican3_netdev_ops = {
+	.ndo_open	= ican3_open,
+	.ndo_stop	= ican3_stop,
+	.ndo_start_xmit	= ican3_xmit,
+};
+
+/*
+ * Low-level CAN Device
+ */
+
+/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
+static struct can_bittiming_const ican3_bittiming_const = {
+	.name = DRV_NAME,
+	.tseg1_min = 1,
+	.tseg1_max = 16,
+	.tseg2_min = 1,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 64,
+	.brp_inc = 1,
+};
+
+/*
+ * This routine was stolen from drivers/net/can/sja1000/sja1000.c
+ *
+ * The bittiming register command for the ICAN3 just sets the bit timing
+ * registers on the SJA1000 chip directly
+ */
+static int ican3_set_bittiming(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct can_bittiming *bt = &mod->can.bittiming;
+	struct ican3_msg msg;
+	u8 btr0, btr1;
+
+	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+		(((bt->phase_seg2 - 1) & 0x7) << 4);
+	if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btr1 |= 0x80;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CBTRREQ;
+	msg.len = cpu_to_le16(4);
+	msg.data[0] = 0x00;
+	msg.data[1] = 0x00;
+	msg.data[2] = btr0;
+	msg.data[3] = btr1;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	if (mode != CAN_MODE_START)
+		return -ENOTSUPP;
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	if (netif_queue_stopped(ndev))
+		netif_wake_queue(ndev);
+
+	return 0;
+}
+
+/*
+ * PCI Subsystem
+ */
+
+static int __devinit ican3_probe(struct platform_device *pdev)
+{
+	struct janz_platform_data *pdata;
+	struct net_device *ndev;
+	struct ican3_dev *mod;
+	struct resource *res;
+	struct device *dev;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENXIO;
+
+	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
+
+	/* save the struct device for printing */
+	dev = &pdev->dev;
+
+	/* allocate the CAN device and private data */
+	ndev = alloc_candev(sizeof(*mod), 0);
+	if (!ndev) {
+		dev_err(dev, "unable to allocate CANdev\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	platform_set_drvdata(pdev, ndev);
+	mod = netdev_priv(ndev);
+	mod->ndev = ndev;
+	mod->dev = &pdev->dev;
+	mod->num = pdata->modno;
+	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
+	spin_lock_init(&mod->lock);
+
+	/* the first unallocated page in the DPM is 9 */
+	mod->free_page = DPM_FREE_START;
+
+	ndev->netdev_ops = &ican3_netdev_ops;
+	ndev->flags |= IFF_ECHO;
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	mod->can.clock.freq = 8000000;
+	mod->can.bittiming_const = &ican3_bittiming_const;
+	mod->can.do_set_bittiming = ican3_set_bittiming;
+	mod->can.do_set_mode = ican3_set_mode;
+
+	/* find our IRQ number */
+	mod->irq = platform_get_irq(pdev, 0);
+	if (mod->irq < 0) {
+		dev_err(dev, "IRQ line not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	ndev->irq = mod->irq;
+
+	/* get access to the MODULbus registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "MODULbus registers not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	mod->dpm = ioremap(res->start, resource_size(res));
+	if (!mod->dpm) {
+		dev_err(dev, "MODULbus registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_free_ndev;
+	}
+
+	mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
+
+	/* get access to the control registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(dev, "CONTROL registers not found\n");
+		ret = -ENODEV;
+		goto out_iounmap_dpm;
+	}
+
+	mod->ctrl = ioremap(res->start, resource_size(res));
+	if (!mod->ctrl) {
+		dev_err(dev, "CONTROL registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_iounmap_dpm;
+	}
+
+	/* disable our IRQ, then hookup the IRQ handler */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
+	if (ret) {
+		dev_err(dev, "unable to request IRQ\n");
+		goto out_iounmap_ctrl;
+	}
+
+	/* reset and initialize the CAN controller into fast mode */
+	napi_enable(&mod->napi);
+	ret = ican3_startup_module(mod);
+	if (ret) {
+		dev_err(dev, "%s: unable to start CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	/* register with the Linux CAN layer */
+	ret = register_candev(ndev);
+	if (ret) {
+		dev_err(dev, "%s: unable to register CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
+	return 0;
+
+out_free_irq:
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+out_iounmap_ctrl:
+	iounmap(mod->ctrl);
+out_iounmap_dpm:
+	iounmap(mod->dpm);
+out_free_ndev:
+	free_candev(ndev);
+out_return:
+	return ret;
+}
+
+static int __devexit ican3_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct ican3_dev *mod = netdev_priv(ndev);
+
+	/* unregister the netdevice, stop interrupts */
+	unregister_netdev(ndev);
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+
+	/* put the module into reset */
+	ican3_shutdown_module(mod);
+
+	/* unmap all registers */
+	iounmap(mod->ctrl);
+	iounmap(mod->dpm);
+
+	free_candev(ndev);
+
+	return 0;
+}
+
+static struct platform_driver ican3_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ican3_probe,
+	.remove		= __devexit_p(ican3_remove),
+};
+
+static int __init ican3_init(void)
+{
+	return platform_driver_register(&ican3_driver);
+}
+
+static void __exit ican3_exit(void)
+{
+	platform_driver_unregister(&ican3_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ican3");
+
+module_init(ican3_init);
+module_exit(ican3_exit);
-- 
1.5.4.3


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-18 15:19       ` Ira W. Snyder
@ 2010-03-18 16:06         ` Wolfgang Grandegger
  0 siblings, 0 replies; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-18 16:06 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: socketcan-core, linux-kernel

Ira W. Snyder wrote:
> On Thu, Mar 18, 2010 at 09:36:25AM +0100, Wolfgang Grandegger wrote:
>> Hi Ira,
>>
>> Ira W. Snyder wrote:
>>> Hello SocketCAN folks,
>>>
>>> Is there any feedback on this patch? I've waited two weeks without a
>>> response to any part of the series. As this is the biggest patch in the
>>> series, if you're fine with it, I think the other supporting patches
>>> should be fairly uncontroversial.
>> For some strange reason this patch did not show up in my socketcan mbox,
>> sorry. At least I cannot find it. Oliver, or somebody else, did you
>> receive it? Nevertheless, for kernel inclusion the patch should go
>> through the netdev mailing list as described in:
>>
>> http://svn.berlios.de/svnroot/repos/socketcan/trunk/README.submitting-patches
>>
>> It's also confusing that one 2/3 shows up on the Socketcan-core ML. At
>> least 0/3 should also be presented there.
>>
>> Could you please resubmit?
>>
> 
> Sure, I'll resubmit.
> 
> The patch showed up just fine on socketcan-core for me (I'm subscribed).

It seems to be a local problem then :-).

> It also showed up on LKML (I'm not subscribed). I'll make sure to CC

I'm not subscribed either.

> everyone with patch 0/3, as well as CC'ing netdev.

Thanks,

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-18  8:36     ` Wolfgang Grandegger
@ 2010-03-18 15:19       ` Ira W. Snyder
  2010-03-18 16:06         ` Wolfgang Grandegger
  0 siblings, 1 reply; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-18 15:19 UTC (permalink / raw)
  To: Wolfgang Grandegger; +Cc: linux-kernel, socketcan-core

On Thu, Mar 18, 2010 at 09:36:25AM +0100, Wolfgang Grandegger wrote:
> Hi Ira,
> 
> Ira W. Snyder wrote:
> > Hello SocketCAN folks,
> > 
> > Is there any feedback on this patch? I've waited two weeks without a
> > response to any part of the series. As this is the biggest patch in the
> > series, if you're fine with it, I think the other supporting patches
> > should be fairly uncontroversial.
> 
> For some strange reason this patch did not show up in my socketcan mbox,
> sorry. At least I cannot find it. Oliver, or somebody else, did you
> receive it? Nevertheless, for kernel inclusion the patch should go
> through the netdev mailing list as described in:
> 
> http://svn.berlios.de/svnroot/repos/socketcan/trunk/README.submitting-patches
> 
> It's also confusing that one 2/3 shows up on the Socketcan-core ML. At
> least 0/3 should also be presented there.
> 
> Could you please resubmit?
> 

Sure, I'll resubmit.

The patch showed up just fine on socketcan-core for me (I'm subscribed).
It also showed up on LKML (I'm not subscribed). I'll make sure to CC
everyone with patch 0/3, as well as CC'ing netdev.

Thanks,
Ira

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-17 19:33   ` Ira W. Snyder
@ 2010-03-18  8:36     ` Wolfgang Grandegger
  2010-03-18 15:19       ` Ira W. Snyder
  0 siblings, 1 reply; 41+ messages in thread
From: Wolfgang Grandegger @ 2010-03-18  8:36 UTC (permalink / raw)
  To: Ira W. Snyder; +Cc: linux-kernel, socketcan-core

Hi Ira,

Ira W. Snyder wrote:
> Hello SocketCAN folks,
> 
> Is there any feedback on this patch? I've waited two weeks without a
> response to any part of the series. As this is the biggest patch in the
> series, if you're fine with it, I think the other supporting patches
> should be fairly uncontroversial.

For some strange reason this patch did not show up in my socketcan mbox,
sorry. At least I cannot find it. Oliver, or somebody else, did you
receive it? Nevertheless, for kernel inclusion the patch should go
through the netdev mailing list as described in:

http://svn.berlios.de/svnroot/repos/socketcan/trunk/README.submitting-patches

It's also confusing that one 2/3 shows up on the Socketcan-core ML. At
least 0/3 should also be presented there.

Could you please resubmit?

Wolfgang.

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-02 21:22 ` [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module Ira W. Snyder
@ 2010-03-17 19:33   ` Ira W. Snyder
  2010-03-18  8:36     ` Wolfgang Grandegger
  0 siblings, 1 reply; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-17 19:33 UTC (permalink / raw)
  To: linux-kernel, socketcan-core

Hello SocketCAN folks,

Is there any feedback on this patch? I've waited two weeks without a
response to any part of the series. As this is the biggest patch in the
series, if you're fine with it, I think the other supporting patches
should be fairly uncontroversial.

Thanks,
Ira

On Tue, Mar 02, 2010 at 01:22:08PM -0800, Ira W. Snyder wrote:
> The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
> MODULbus carrier board. It is an intelligent CAN controller with a
> microcontroller and associated firmware.
> 
> Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
> Cc: socketcan-core@lists.berlios.de
> ---
>  drivers/net/can/Kconfig      |   10 +
>  drivers/net/can/Makefile     |    1 +
>  drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1670 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/net/can/janz-ican3.c
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 05b7517..2c5227c 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -63,6 +63,16 @@ config CAN_BFIN
>  	  To compile this driver as a module, choose M here: the
>  	  module will be called bfin_can.
>  
> +config CAN_JANZ_ICAN3
> +	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
> +	depends on CAN_DEV && MFD_JANZ_CMODIO
> +	---help---
> +	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
> +	  connects to a MODULbus carrier board.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called janz-ican3.ko.
> +
>  source "drivers/net/can/mscan/Kconfig"
>  
>  source "drivers/net/can/sja1000/Kconfig"
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index 7a702f2..9047cd0 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
>  obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
>  obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
>  obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
> +obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
>  
>  ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
> diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
> new file mode 100644
> index 0000000..94d4995
> --- /dev/null
> +++ b/drivers/net/can/janz-ican3.c
> @@ -0,0 +1,1659 @@
> +/*
> + * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
> + *
> + * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
> + *
> + * 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.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/netdevice.h>
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +#include <linux/can/error.h>
> +
> +#include <linux/mfd/janz.h>
> +
> +/* the DPM has 64k of memory, organized into 256x 256 byte pages */
> +#define DPM_NUM_PAGES		256
> +#define DPM_PAGE_SIZE		256
> +#define DPM_PAGE_ADDR(p)	((p) * DPM_PAGE_SIZE)
> +
> +/* JANZ ICAN3 "old-style" host interface queue page numbers */
> +#define QUEUE_OLD_CONTROL	0
> +#define QUEUE_OLD_RB0		1
> +#define QUEUE_OLD_RB1		2
> +#define QUEUE_OLD_WB0		3
> +#define QUEUE_OLD_WB1		4
> +
> +/* Janz ICAN3 "old-style" host interface control registers */
> +#define MSYNC_PEER		0x00		/* ICAN only */
> +#define MSYNC_LOCL		0x01		/* host only */
> +#define TARGET_RUNNING		0x02
> +
> +#define MSYNC_RB0		0x01
> +#define MSYNC_RB1		0x02
> +#define MSYNC_RBLW		0x04
> +#define MSYNC_RB_MASK		(MSYNC_RB0 | MSYNC_RB1)
> +
> +#define MSYNC_WB0		0x10
> +#define MSYNC_WB1		0x20
> +#define MSYNC_WBLW		0x40
> +#define MSYNC_WB_MASK		(MSYNC_WB0 | MSYNC_WB1)
> +
> +/* Janz ICAN3 "new-style" host interface queue page numbers */
> +#define QUEUE_TOHOST		5
> +#define QUEUE_FROMHOST_MID	6
> +#define QUEUE_FROMHOST_HIGH	7
> +#define QUEUE_FROMHOST_LOW	8
> +
> +/* The first free page in the DPM is #9 */
> +#define DPM_FREE_START		9
> +
> +/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
> +#define DESC_VALID		0x80
> +#define DESC_WRAP		0x40
> +#define DESC_INTERRUPT		0x20
> +#define DESC_IVALID		0x10
> +#define DESC_LEN(len)		(len)
> +
> +/* Janz ICAN3 Firmware Messages */
> +#define MSG_CONNECTI		0x02
> +#define MSG_DISCONNECT		0x03
> +#define MSG_IDVERS		0x04
> +#define MSG_MSGLOST		0x05
> +#define MSG_NEWHOSTIF		0x08
> +#define MSG_SETAFILMASK		0x10
> +#define MSG_INITFDPMQUEUE	0x11
> +#define MSG_HWCONF		0x12
> +#define MSG_FMSGLOST		0x15
> +#define MSG_CEVTIND		0x37
> +#define MSG_CBTRREQ		0x41
> +#define MSG_COFFREQ		0x42
> +#define MSG_CONREQ		0x43
> +#define MSG_CCONFREQ		0x47
> +
> +/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
> +#define SETAFILMASK_REJECT	0x00
> +#define SETAFILMASK_FASTIF	0x02
> +
> +/* Janz ICAN3 CAN Hardware Configuration Message Types */
> +#define HWCONF_TERMINATE_ON	0x01
> +#define HWCONF_TERMINATE_OFF	0x00
> +
> +/* Janz ICAN3 CAN Event Indication Message Types */
> +#define CEVTIND_EI		0x01
> +#define CEVTIND_DOI		0x02
> +#define CEVTIND_LOST		0x04
> +#define CEVTIND_FULL		0x08
> +#define CEVTIND_BEI		0x10
> +
> +#define CEVTIND_CHIP_SJA1000	0x02
> +
> +#define ICAN3_BUSERR_QUOTA_MAX	255
> +
> +/* Janz ICAN3 CAN Frame Conversion */
> +#define ICAN3_ECHO	0x10
> +#define ICAN3_EFF_RTR	0x40
> +#define ICAN3_SFF_RTR	0x10
> +#define ICAN3_EFF	0x80
> +
> +#define ICAN3_CAN_TYPE_MASK	0x0f
> +#define ICAN3_CAN_TYPE_SFF	0x00
> +#define ICAN3_CAN_TYPE_EFF	0x01
> +
> +#define ICAN3_CAN_DLC_MASK	0x0f
> +
> +/*
> + * SJA1000 Status and Error Register Definitions
> + *
> + * Copied from drivers/net/can/sja1000/sja1000.h
> + */
> +
> +/* status register content */
> +#define SR_BS		0x80
> +#define SR_ES		0x40
> +#define SR_TS		0x20
> +#define SR_RS		0x10
> +#define SR_TCS		0x08
> +#define SR_TBS		0x04
> +#define SR_DOS		0x02
> +#define SR_RBS		0x01
> +
> +#define SR_CRIT (SR_BS|SR_ES)
> +
> +/* ECC register */
> +#define ECC_SEG		0x1F
> +#define ECC_DIR		0x20
> +#define ECC_ERR		6
> +#define ECC_BIT		0x00
> +#define ECC_FORM	0x40
> +#define ECC_STUFF	0x80
> +#define ECC_MASK	0xc0
> +
> +/* Number of buffers for use in the "new-style" host interface */
> +#define ICAN3_NEW_BUFFERS	16
> +
> +/* Number of buffers for use in the "fast" host interface */
> +#define ICAN3_TX_BUFFERS	512
> +#define ICAN3_RX_BUFFERS	1024
> +
> +/* Driver Name */
> +#define DRV_NAME "janz-ican3"
> +
> +/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
> +struct ican3_dpm_control {
> +	/* window address register */
> +	u8 window_address;
> +	u8 unused1;
> +
> +	/*
> +	 * Read access: clear interrupt from microcontroller
> +	 * Write access: send interrupt to microcontroller
> +	 */
> +	u8 interrupt;
> +	u8 unused2;
> +
> +	/* write-only: reset all hardware on the module */
> +	u8 hwreset;
> +	u8 unused3;
> +
> +	/* write-only: generate an interrupt to the TPU */
> +	u8 tpuinterrupt;
> +};
> +
> +struct ican3_dev {
> +
> +	/* must be the first member */
> +	struct can_priv can;
> +
> +	/* CAN network device */
> +	struct net_device *ndev;
> +	struct napi_struct napi;
> +
> +	/* Device for printing */
> +	struct device *dev;
> +
> +	/* module number */
> +	unsigned int num;
> +
> +	/* base address of registers and IRQ */
> +	struct janz_cmodio_onboard_regs __iomem *ctrl;
> +	struct ican3_dpm_control *dpmctrl;
> +	void __iomem *dpm;
> +	int irq;
> +
> +	/* old and new style host interface */
> +	unsigned int iftype;
> +	spinlock_t lock;
> +
> +	/* new host interface */
> +	unsigned int rx_int;
> +	unsigned int rx_num;
> +	unsigned int tx_num;
> +
> +	/* fast host interface */
> +	unsigned int fastrx_start;
> +	unsigned int fastrx_int;
> +	unsigned int fastrx_num;
> +	unsigned int fasttx_start;
> +	unsigned int fasttx_num;
> +
> +	/* first free DPM page */
> +	unsigned int free_page;
> +};
> +
> +struct ican3_msg {
> +	u8 control;
> +	u8 spec;
> +	__le16 len;
> +	u8 data[252];
> +};
> +
> +struct ican3_new_desc {
> +	u8 control;
> +	u8 pointer;
> +};
> +
> +struct ican3_fast_desc {
> +	u8 control;
> +	u8 command;
> +	u8 data[14];
> +};
> +
> +/* write to the window basic address register */
> +static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
> +{
> +	BUG_ON(page >= DPM_NUM_PAGES);
> +	iowrite8(page, &mod->dpmctrl->window_address);
> +}
> +
> +/*
> + * ICAN3 "old-style" host interface
> + */
> +
> +/*
> + * Recieve a message from the ICAN3 "old-style" firmware interface
> + *
> + * LOCKING: must hold mod->lock
> + *
> + * returns 0 on success, -ENOMEM when no message exists
> + */
> +static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	unsigned int mbox, mbox_page;
> +	u8 locl, peer, xord;
> +
> +	/* get the MSYNC registers */
> +	ican3_set_page(mod, QUEUE_OLD_CONTROL);
> +	peer = ioread8(mod->dpm + MSYNC_PEER);
> +	locl = ioread8(mod->dpm + MSYNC_LOCL);
> +	xord = locl ^ peer;
> +
> +	if ((xord & MSYNC_RB_MASK) == 0x00) {
> +		dev_dbg(mod->dev, "no mbox for reading\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* find the first free mbox to read */
> +	if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
> +		mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
> +	else
> +		mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
> +
> +	/* copy the message */
> +	mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
> +	ican3_set_page(mod, mbox_page);
> +	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
> +
> +	/*
> +	 * notify the firmware that the read buffer is available
> +	 * for it to fill again
> +	 */
> +	locl ^= mbox;
> +
> +	ican3_set_page(mod, QUEUE_OLD_CONTROL);
> +	iowrite8(locl, mod->dpm + MSYNC_LOCL);
> +	return 0;
> +}
> +
> +/*
> + * Send a message through the "old-style" firmware interface
> + *
> + * LOCKING: must hold mod->lock
> + *
> + * returns 0 on success, -ENOMEM when no free space exists
> + */
> +static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	unsigned int mbox, mbox_page;
> +	u8 locl, peer, xord;
> +
> +	/* get the MSYNC registers */
> +	ican3_set_page(mod, QUEUE_OLD_CONTROL);
> +	peer = ioread8(mod->dpm + MSYNC_PEER);
> +	locl = ioread8(mod->dpm + MSYNC_LOCL);
> +	xord = locl ^ peer;
> +
> +	if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
> +		dev_err(mod->dev, "no mbox for writing\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* calculate a free mbox to use */
> +	mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
> +
> +	/* copy the message to the DPM */
> +	mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
> +	ican3_set_page(mod, mbox_page);
> +	memcpy_toio(mod->dpm, msg, sizeof(*msg));
> +
> +	locl ^= mbox;
> +	if (mbox == MSYNC_WB1)
> +		locl |= MSYNC_WBLW;
> +
> +	ican3_set_page(mod, QUEUE_OLD_CONTROL);
> +	iowrite8(locl, mod->dpm + MSYNC_LOCL);
> +	return 0;
> +}
> +
> +/*
> + * ICAN3 "new-style" Host Interface Setup
> + */
> +
> +static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
> +{
> +	struct ican3_new_desc desc;
> +	unsigned long flags;
> +	void __iomem *dst;
> +	int i;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	/* setup the internal datastructures for RX */
> +	mod->rx_num = 0;
> +	mod->rx_int = 0;
> +
> +	/* tohost queue descriptors are in page 5 */
> +	ican3_set_page(mod, QUEUE_TOHOST);
> +	dst = mod->dpm;
> +
> +	/* initialize the tohost (rx) queue descriptors: pages 9-24 */
> +	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
> +		desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
> +		desc.pointer = mod->free_page;
> +
> +		/* set wrap flag on last buffer */
> +		if (i == ICAN3_NEW_BUFFERS - 1)
> +			desc.control |= DESC_WRAP;
> +
> +		memcpy_toio(dst, &desc, sizeof(desc));
> +		dst += sizeof(desc);
> +		mod->free_page++;
> +	}
> +
> +	/* fromhost (tx) mid queue descriptors are in page 6 */
> +	ican3_set_page(mod, QUEUE_FROMHOST_MID);
> +	dst = mod->dpm;
> +
> +	/* setup the internal datastructures for TX */
> +	mod->tx_num = 0;
> +
> +	/* initialize the fromhost mid queue descriptors: pages 25-40 */
> +	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
> +		desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
> +		desc.pointer = mod->free_page;
> +
> +		/* set wrap flag on last buffer */
> +		if (i == ICAN3_NEW_BUFFERS - 1)
> +			desc.control |= DESC_WRAP;
> +
> +		memcpy_toio(dst, &desc, sizeof(desc));
> +		dst += sizeof(desc);
> +		mod->free_page++;
> +	}
> +
> +	/* fromhost hi queue descriptors are in page 7 */
> +	ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
> +	dst = mod->dpm;
> +
> +	/* initialize only a single buffer in the fromhost hi queue (unused) */
> +	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
> +	desc.pointer = mod->free_page;
> +	memcpy_toio(dst, &desc, sizeof(desc));
> +	mod->free_page++;
> +
> +	/* fromhost low queue descriptors are in page 8 */
> +	ican3_set_page(mod, QUEUE_FROMHOST_LOW);
> +	dst = mod->dpm;
> +
> +	/* initialize only a single buffer in the fromhost low queue (unused) */
> +	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
> +	desc.pointer = mod->free_page;
> +	memcpy_toio(dst, &desc, sizeof(desc));
> +	mod->free_page++;
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +}
> +
> +/*
> + * ICAN3 Fast Host Interface Setup
> + */
> +
> +static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
> +{
> +	struct ican3_fast_desc desc;
> +	unsigned long flags;
> +	unsigned int addr;
> +	void __iomem *dst;
> +	int i;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	/* save the start recv page */
> +	mod->fastrx_start = mod->free_page;
> +	mod->fastrx_num = 0;
> +	mod->fastrx_int = 0;
> +
> +	/* build a single fast tohost queue descriptor */
> +	memset(&desc, 0, sizeof(desc));
> +	desc.control = 0x00;
> +	desc.command = 1;
> +
> +	/* build the tohost queue descriptor ring in memory */
> +	addr = 0;
> +	for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
> +
> +		/* set the wrap bit on the last buffer */
> +		if (i == ICAN3_RX_BUFFERS - 1)
> +			desc.control |= DESC_WRAP;
> +
> +		/* switch to the correct page */
> +		ican3_set_page(mod, mod->free_page);
> +
> +		/* copy the descriptor to the DPM */
> +		dst = mod->dpm + addr;
> +		memcpy_toio(dst, &desc, sizeof(desc));
> +		addr += sizeof(desc);
> +
> +		/* move to the next page if necessary */
> +		if (addr >= DPM_PAGE_SIZE) {
> +			addr = 0;
> +			mod->free_page++;
> +		}
> +	}
> +
> +	/* make sure we page-align the next queue */
> +	if (addr != 0)
> +		mod->free_page++;
> +
> +	/* save the start xmit page */
> +	mod->fasttx_start = mod->free_page;
> +	mod->fasttx_num = 0;
> +
> +	/* build a single fast fromhost queue descriptor */
> +	memset(&desc, 0, sizeof(desc));
> +	desc.control = DESC_VALID;
> +	desc.command = 1;
> +
> +	/* build the fromhost queue descriptor ring in memory */
> +	addr = 0;
> +	for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
> +
> +		/* set the wrap bit on the last buffer */
> +		if (i == ICAN3_TX_BUFFERS - 1)
> +			desc.control |= DESC_WRAP;
> +
> +		/* switch to the correct page */
> +		ican3_set_page(mod, mod->free_page);
> +
> +		/* copy the descriptor to the DPM */
> +		dst = mod->dpm + addr;
> +		memcpy_toio(dst, &desc, sizeof(desc));
> +		addr += sizeof(desc);
> +
> +		/* move to the next page if necessary */
> +		if (addr >= DPM_PAGE_SIZE) {
> +			addr = 0;
> +			mod->free_page++;
> +		}
> +	}
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +}
> +
> +/*
> + * ICAN3 "new-style" Host Interface Message Helpers
> + */
> +
> +/*
> + * LOCKING: must hold mod->lock
> + */
> +static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	struct ican3_new_desc desc;
> +	void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
> +
> +	/* switch to the fromhost mid queue, and read the buffer descriptor */
> +	ican3_set_page(mod, QUEUE_FROMHOST_MID);
> +	memcpy_fromio(&desc, desc_addr, sizeof(desc));
> +
> +	if (!(desc.control & DESC_VALID)) {
> +		dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
> +		return -ENOMEM;
> +	}
> +
> +	/* switch to the data page, copy the data */
> +	ican3_set_page(mod, desc.pointer);
> +	memcpy_toio(mod->dpm, msg, sizeof(*msg));
> +
> +	/* switch back to the descriptor, set the valid bit, write it back */
> +	ican3_set_page(mod, QUEUE_FROMHOST_MID);
> +	desc.control ^= DESC_VALID;
> +	memcpy_toio(desc_addr, &desc, sizeof(desc));
> +
> +	/* update the tx number */
> +	mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
> +	return 0;
> +}
> +
> +/*
> + * LOCKING: must hold mod->lock
> + */
> +static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	struct ican3_new_desc desc;
> +	void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
> +
> +	/* switch to the tohost queue, and read the buffer descriptor */
> +	ican3_set_page(mod, QUEUE_TOHOST);
> +	memcpy_fromio(&desc, desc_addr, sizeof(desc));
> +
> +	if (!(desc.control & DESC_VALID)) {
> +		dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
> +		return -ENOMEM;
> +	}
> +
> +	/* switch to the data page, copy the data */
> +	ican3_set_page(mod, desc.pointer);
> +	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
> +
> +	/* switch back to the descriptor, toggle the valid bit, write it back */
> +	ican3_set_page(mod, QUEUE_TOHOST);
> +	desc.control ^= DESC_VALID;
> +	memcpy_toio(desc_addr, &desc, sizeof(desc));
> +
> +	/* update the rx number */
> +	mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
> +	return 0;
> +}
> +
> +/*
> + * Message Send / Recv Helpers
> + */
> +
> +static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	unsigned long flags;
> +	int ret;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	if (mod->iftype == 0)
> +		ret = ican3_old_send_msg(mod, msg);
> +	else
> +		ret = ican3_new_send_msg(mod, msg);
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +	return ret;
> +}
> +
> +static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	unsigned long flags;
> +	int ret;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	if (mod->iftype == 0)
> +		ret = ican3_old_recv_msg(mod, msg);
> +	else
> +		ret = ican3_new_recv_msg(mod, msg);
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +	return ret;
> +}
> +
> +/*
> + * Quick Pre-constructed Messages
> + */
> +
> +static int __devinit ican3_msg_connect(struct ican3_dev *mod)
> +{
> +	struct ican3_msg msg;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_CONNECTI;
> +	msg.len = cpu_to_le16(0);
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
> +{
> +	struct ican3_msg msg;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_DISCONNECT;
> +	msg.len = cpu_to_le16(0);
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
> +{
> +	struct ican3_msg msg;
> +	int ret;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_NEWHOSTIF;
> +	msg.len = cpu_to_le16(0);
> +
> +	/* If we're not using the old interface, switching seems bogus */
> +	WARN_ON(mod->iftype != 0);
> +
> +	ret = ican3_send_msg(mod, &msg);
> +	if (ret)
> +		return ret;
> +
> +	/* mark the module as using the new host interface */
> +	mod->iftype = 1;
> +	return 0;
> +}
> +
> +static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
> +{
> +	struct ican3_msg msg;
> +	unsigned int addr;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_INITFDPMQUEUE;
> +	msg.len = cpu_to_le16(8);
> +
> +	/* write the tohost queue start address */
> +	addr = DPM_PAGE_ADDR(mod->fastrx_start);
> +	msg.data[0] = addr & 0xff;
> +	msg.data[1] = (addr >> 8) & 0xff;
> +	msg.data[2] = (addr >> 16) & 0xff;
> +	msg.data[3] = (addr >> 24) & 0xff;
> +
> +	/* write the fromhost queue start address */
> +	addr = DPM_PAGE_ADDR(mod->fasttx_start);
> +	msg.data[4] = addr & 0xff;
> +	msg.data[5] = (addr >> 8) & 0xff;
> +	msg.data[6] = (addr >> 16) & 0xff;
> +	msg.data[7] = (addr >> 24) & 0xff;
> +
> +	/* If we're not using the new interface yet, we cannot do this */
> +	WARN_ON(mod->iftype != 1);
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +/*
> + * Setup the CAN filter to either accept or reject all
> + * messages from the CAN bus.
> + */
> +static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
> +{
> +	struct ican3_msg msg;
> +	int ret;
> +
> +	/* Standard Frame Format */
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_SETAFILMASK;
> +	msg.len = cpu_to_le16(5);
> +	msg.data[0] = 0x00; /* IDLo LSB */
> +	msg.data[1] = 0x00; /* IDLo MSB */
> +	msg.data[2] = 0xff; /* IDHi LSB */
> +	msg.data[3] = 0x07; /* IDHi MSB */
> +
> +	/* accept all frames for fast host if, or reject all frames */
> +	msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
> +
> +	ret = ican3_send_msg(mod, &msg);
> +	if (ret)
> +		return ret;
> +
> +	/* Extended Frame Format */
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_SETAFILMASK;
> +	msg.len = cpu_to_le16(13);
> +	msg.data[0] = 0;    /* MUX = 0 */
> +	msg.data[1] = 0x00; /* IDLo LSB */
> +	msg.data[2] = 0x00;
> +	msg.data[3] = 0x00;
> +	msg.data[4] = 0x20; /* IDLo MSB */
> +	msg.data[5] = 0xff; /* IDHi LSB */
> +	msg.data[6] = 0xff;
> +	msg.data[7] = 0xff;
> +	msg.data[8] = 0x3f; /* IDHi MSB */
> +
> +	/* accept all frames for fast host if, or reject all frames */
> +	msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +/*
> + * Bring the CAN bus online or offline
> + */
> +static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
> +{
> +	struct ican3_msg msg;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
> +	msg.len = cpu_to_le16(0);
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +static int __devinit ican3_set_termination(struct ican3_dev *mod, bool on)
> +{
> +	struct ican3_msg msg;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_HWCONF;
> +	msg.len = cpu_to_le16(2);
> +	msg.data[0] = 0x00;
> +	msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +static int __devinit ican3_set_buserror(struct ican3_dev *mod, u8 quota)
> +{
> +	struct ican3_msg msg;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_CCONFREQ;
> +	msg.len = cpu_to_le16(2);
> +	msg.data[0] = 0x00;
> +	msg.data[1] = quota;
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +/*
> + * ICAN3 to Linux CAN Frame Conversion
> + */
> +
> +static void ican3_to_can_frame(struct ican3_dev *mod,
> +			       struct ican3_fast_desc *desc,
> +			       struct can_frame *cf)
> +{
> +	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
> +		dev_dbg(mod->dev, "%s: old frame format\n", __func__);
> +		if (desc->data[1] & ICAN3_SFF_RTR)
> +			cf->can_id |= CAN_RTR_FLAG;
> +
> +		cf->can_id |= desc->data[0] << 3;
> +		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
> +		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
> +		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
> +	} else {
> +		dev_dbg(mod->dev, "%s: new frame format\n", __func__);
> +		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
> +		if (desc->data[0] & ICAN3_EFF_RTR)
> +			cf->can_id |= CAN_RTR_FLAG;
> +
> +		if (desc->data[0] & ICAN3_EFF) {
> +			cf->can_id |= CAN_EFF_FLAG;
> +			cf->can_id |= desc->data[2] << 21; /* 28-21 */
> +			cf->can_id |= desc->data[3] << 13; /* 20-13 */
> +			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
> +			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
> +		} else {
> +			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
> +			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
> +		}
> +
> +		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
> +	}
> +}
> +
> +static void can_frame_to_ican3(struct ican3_dev *mod,
> +			       struct can_frame *cf,
> +			       struct ican3_fast_desc *desc)
> +{
> +	/* clear out any stale data in the descriptor */
> +	memset(desc->data, 0, sizeof(desc->data));
> +
> +	/* we always use the extended format, with the ECHO flag set */
> +	desc->command = ICAN3_CAN_TYPE_EFF;
> +	desc->data[0] |= cf->can_dlc;
> +	desc->data[1] |= ICAN3_ECHO;
> +
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		desc->data[0] |= ICAN3_EFF_RTR;
> +
> +	/* pack the id into the correct places */
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		dev_dbg(mod->dev, "%s: extended frame\n", __func__);
> +		desc->data[0] |= ICAN3_EFF;
> +		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
> +		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
> +		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
> +		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
> +	} else {
> +		dev_dbg(mod->dev, "%s: standard frame\n", __func__);
> +		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
> +		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
> +	}
> +
> +	/* copy the data bits into the descriptor */
> +	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
> +}
> +
> +/*
> + * Interrupt Handling
> + */
> +
> +/*
> + * Handle an ID + Version message response from the firmware. We never generate
> + * this message in production code, but it is very useful when debugging to be
> + * able to display this message.
> + */
> +static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
> +}
> +
> +static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	struct net_device *dev = mod->ndev;
> +	struct net_device_stats *stats = &dev->stats;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +
> +	/*
> +	 * Report that communication messages with the microcontroller firmware
> +	 * are being lost. These are never CAN frames, so we do not generate an
> +	 * error frame for userspace
> +	 */
> +	if (msg->spec == MSG_MSGLOST) {
> +		dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
> +		return;
> +	}
> +
> +	/*
> +	 * Oops, this indicates that we have lost messages in the fast queue,
> +	 * which are exclusively CAN messages. Our driver isn't reading CAN
> +	 * frames fast enough.
> +	 *
> +	 * We'll pretend that the SJA1000 told us that it ran out of buffer
> +	 * space, because there is not a better message for this.
> +	 */
> +	skb = alloc_can_err_skb(dev, &cf);
> +	if (skb) {
> +		cf->can_id |= CAN_ERR_CRTL;
> +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +		stats->rx_errors++;
> +		stats->rx_bytes += cf->can_dlc;
> +		netif_rx(skb);
> +	}
> +}
> +
> +/*
> + * Handle CAN Event Indication Messages from the firmware
> + *
> + * The ICAN3 firmware provides the values of some SJA1000 registers when it
> + * generates this message. The code below is largely copied from the
> + * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
> + */
> +static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	struct net_device *dev = mod->ndev;
> +	struct net_device_stats *stats = &dev->stats;
> +	enum can_state state = mod->can.state;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	u8 status, isrc;
> +
> +	/* we can only handle the SJA1000 part */
> +	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
> +		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
> +		return -ENODEV;
> +	}
> +
> +	/* check the message length for sanity */
> +	if (msg->len < 6) {
> +		dev_err(mod->dev, "error message too short\n");
> +		return -EINVAL;
> +	}
> +
> +	skb = alloc_can_err_skb(dev, &cf);
> +	if (skb == NULL)
> +		return -ENOMEM;
> +
> +	isrc = msg->data[0];
> +	status = msg->data[3];
> +
> +	/* data overrun interrupt */
> +	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
> +		cf->can_id |= CAN_ERR_CRTL;
> +		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +		stats->rx_over_errors++;
> +		stats->rx_errors++;
> +		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
> +	}
> +
> +	/* error warning interrupt */
> +	if (isrc == CEVTIND_EI) {
> +		if (status & SR_BS) {
> +			state = CAN_STATE_BUS_OFF;
> +			cf->can_id |= CAN_ERR_BUSOFF;
> +			can_bus_off(dev);
> +		} else if (status & SR_ES) {
> +			state = CAN_STATE_ERROR_WARNING;
> +		} else {
> +			state = CAN_STATE_ERROR_ACTIVE;
> +		}
> +	}
> +
> +	/* bus error interrupt */
> +	if (isrc == CEVTIND_BEI) {
> +		u8 ecc = msg->data[2];
> +		mod->can.can_stats.bus_error++;
> +		stats->rx_errors++;
> +		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +		switch (ecc & ECC_MASK) {
> +		case ECC_BIT:
> +			cf->data[2] |= CAN_ERR_PROT_BIT;
> +			break;
> +		case ECC_FORM:
> +			cf->data[2] |= CAN_ERR_PROT_FORM;
> +			break;
> +		case ECC_STUFF:
> +			cf->data[2] |= CAN_ERR_PROT_STUFF;
> +			break;
> +		default:
> +			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +			cf->data[3] = ecc & ECC_SEG;
> +			break;
> +		}
> +
> +		if ((ecc & ECC_DIR) == 0)
> +			cf->data[2] |= CAN_ERR_PROT_TX;
> +	}
> +
> +	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
> +					state == CAN_STATE_ERROR_PASSIVE)) {
> +		u8 rxerr = msg->data[4];
> +		u8 txerr = msg->data[5];
> +		cf->can_id |= CAN_ERR_CRTL;
> +		if (state == CAN_STATE_ERROR_WARNING) {
> +			mod->can.can_stats.error_warning++;
> +			cf->data[1] = (txerr > rxerr) ?
> +				CAN_ERR_CRTL_TX_WARNING :
> +				CAN_ERR_CRTL_RX_WARNING;
> +		} else {
> +			mod->can.can_stats.error_passive++;
> +			cf->data[1] = (txerr > rxerr) ?
> +				CAN_ERR_CRTL_TX_PASSIVE :
> +				CAN_ERR_CRTL_RX_PASSIVE;
> +		}
> +	}
> +
> +	mod->can.state = state;
> +	stats->rx_errors++;
> +	stats->rx_bytes += cf->can_dlc;
> +	netif_rx(skb);
> +	return 0;
> +}
> +
> +static void ican3_handle_unknown_message(struct ican3_dev *mod,
> +					struct ican3_msg *msg)
> +{
> +	dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
> +			   msg->spec, le16_to_cpu(msg->len));
> +}
> +
> +/*
> + * Handle a control message from the firmware
> + */
> +static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
> +{
> +	dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
> +			   mod->num, msg->spec, le16_to_cpu(msg->len));
> +
> +	switch (msg->spec) {
> +	case MSG_IDVERS:
> +		ican3_handle_idvers(mod, msg);
> +		break;
> +	case MSG_MSGLOST:
> +	case MSG_FMSGLOST:
> +		ican3_handle_msglost(mod, msg);
> +		break;
> +	case MSG_CEVTIND:
> +		ican3_handle_cevtind(mod, msg);
> +		break;
> +	default:
> +		ican3_handle_unknown_message(mod, msg);
> +		break;
> +	}
> +}
> +
> +/*
> + * Check that there is room in the TX ring to transmit another skb
> + *
> + * LOCKING: must hold mod->lock
> + */
> +static bool ican3_txok(struct ican3_dev *mod)
> +{
> +	struct ican3_fast_desc __iomem *desc;
> +	u8 control;
> +
> +	/* copy the control bits of the descriptor */
> +	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
> +	desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
> +	control = ioread8(&desc->control);
> +
> +	/* if the control bits are not valid, then we have no more space */
> +	if (!(control & DESC_VALID))
> +		return false;
> +
> +	return true;
> +}
> +
> +/*
> + * Recieve one CAN frame from the hardware
> + *
> + * This works like the core of a NAPI function, but is intended to be called
> + * from workqueue context instead. This driver already needs a workqueue to
> + * process control messages, so we use the workqueue instead of using NAPI.
> + * This was done to simplify locking.
> + *
> + * CONTEXT: must be called from user context
> + */
> +static int ican3_recv_skb(struct ican3_dev *mod)
> +{
> +	struct net_device *ndev = mod->ndev;
> +	struct net_device_stats *stats = &ndev->stats;
> +	struct ican3_fast_desc desc;
> +	void __iomem *desc_addr;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	/* copy the whole descriptor */
> +	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
> +	desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
> +	memcpy_fromio(&desc, desc_addr, sizeof(desc));
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +
> +	/* check that we actually have a CAN frame */
> +	if (!(desc.control & DESC_VALID))
> +		return -ENOBUFS;
> +
> +	/* allocate an skb */
> +	skb = alloc_can_skb(ndev, &cf);
> +	if (unlikely(skb == NULL)) {
> +		stats->rx_dropped++;
> +		goto err_noalloc;
> +	}
> +
> +	/* convert the ICAN3 frame into Linux CAN format */
> +	ican3_to_can_frame(mod, &desc, cf);
> +
> +	/* receive the skb, update statistics */
> +	netif_receive_skb(skb);
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;
> +
> +err_noalloc:
> +	/* toggle the valid bit and return the descriptor to the ring */
> +	desc.control ^= DESC_VALID;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
> +	memcpy_toio(desc_addr, &desc, 1);
> +
> +	/* update the next buffer pointer */
> +	mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
> +						     : (mod->fastrx_num + 1);
> +
> +	/* there are still more buffers to process */
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +	return 0;
> +}
> +
> +static int ican3_napi(struct napi_struct *napi, int budget)
> +{
> +	struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
> +	struct ican3_msg msg;
> +	unsigned long flags;
> +	int received = 0;
> +	int ret;
> +
> +	/* process all communication messages */
> +	while (true) {
> +		ret = ican3_recv_msg(mod, &msg);
> +		if (ret)
> +			break;
> +
> +		ican3_handle_message(mod, &msg);
> +	}
> +
> +	/* process all CAN frames from the fast interface */
> +	while (received < budget) {
> +		ret = ican3_recv_skb(mod);
> +		if (ret)
> +			break;
> +
> +		received++;
> +	}
> +
> +	/* We have processed all packets that the adapter had, but it
> +	 * was less than our budget, stop polling */
> +	if (received < budget)
> +		napi_complete(napi);
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	/* Wake up the transmit queue if necessary */
> +	if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
> +		netif_wake_queue(mod->ndev);
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +
> +	/* re-enable interrupt generation */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
> +	return received;
> +}
> +
> +static irqreturn_t ican3_irq(int irq, void *dev_id)
> +{
> +	struct ican3_dev *mod = dev_id;
> +	u8 stat;
> +
> +	/*
> +	 * The interrupt status register on this device reports interrupts
> +	 * as zeroes instead of using ones like most other devices
> +	 */
> +	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
> +	if (stat == (1 << mod->num))
> +		return IRQ_NONE;
> +
> +	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
> +
> +	/* clear the MODULbus interrupt from the microcontroller */
> +	ioread8(&mod->dpmctrl->interrupt);
> +
> +	/* disable interrupt generation, schedule the NAPI poller */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
> +	napi_schedule(&mod->napi);
> +	return IRQ_HANDLED;
> +}
> +
> +/*
> + * Firmware reset, startup, and shutdown
> + */
> +
> +/*
> + * Reset an ICAN module to its power-on state
> + *
> + * CONTEXT: no network device registered
> + * LOCKING: work function disabled
> + */
> +static int ican3_reset_module(struct ican3_dev *mod)
> +{
> +	u8 val = 1 << mod->num;
> +	unsigned long start;
> +	u8 runold, runnew;
> +
> +	/* disable interrupts so no more work is scheduled */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
> +
> +	/* flush any pending work */
> +	flush_scheduled_work();
> +
> +	/* the first unallocated page in the DPM is #9 */
> +	mod->free_page = DPM_FREE_START;
> +
> +	ican3_set_page(mod, QUEUE_OLD_CONTROL);
> +	runold = ioread8(mod->dpm + TARGET_RUNNING);
> +
> +	/* reset the module */
> +	iowrite8(val, &mod->ctrl->reset_assert);
> +	iowrite8(val, &mod->ctrl->reset_deassert);
> +
> +	/* wait until the module has finished resetting and is running */
> +	start = jiffies;
> +	do {
> +		ican3_set_page(mod, QUEUE_OLD_CONTROL);
> +		runnew = ioread8(mod->dpm + TARGET_RUNNING);
> +		if (runnew == (runold ^ 0xff))
> +			return 0;
> +
> +		msleep(10);
> +	} while (time_before(jiffies, start + HZ / 4));
> +
> +	dev_err(mod->dev, "failed to reset CAN module\n");
> +	return -ETIMEDOUT;
> +}
> +
> +static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
> +{
> +	ican3_msg_disconnect(mod);
> +	ican3_reset_module(mod);
> +}
> +
> +/*
> + * Startup an ICAN module, bringing it into fast mode
> + */
> +static int __devinit ican3_startup_module(struct ican3_dev *mod)
> +{
> +	int ret;
> +
> +	ret = ican3_reset_module(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to reset module\n");
> +		return ret;
> +	}
> +
> +	/* re-enable interrupts so we can send messages */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
> +
> +	ret = ican3_msg_connect(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to connect to module\n");
> +		return ret;
> +	}
> +
> +	ican3_init_new_host_interface(mod);
> +	ret = ican3_msg_newhostif(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to switch to new-style interface\n");
> +		return ret;
> +	}
> +
> +	ret = ican3_set_termination(mod, true);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to enable termination\n");
> +		return ret;
> +	}
> +
> +	ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to set bus-error\n");
> +		return ret;
> +	}
> +
> +	ican3_init_fast_host_interface(mod);
> +	ret = ican3_msg_fasthostif(mod);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to switch to fast host interface\n");
> +		return ret;
> +	}
> +
> +	ret = ican3_set_id_filter(mod, true);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to set acceptance filter\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * CAN Network Device
> + */
> +
> +static int ican3_open(struct net_device *ndev)
> +{
> +	struct ican3_dev *mod = netdev_priv(ndev);
> +	int ret;
> +
> +	/* open the CAN layer */
> +	ret = open_candev(ndev);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to start CAN layer\n");
> +		return ret;
> +	}
> +
> +	/* bring the bus online */
> +	ret = ican3_set_bus_state(mod, true);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to set bus-on\n");
> +		close_candev(ndev);
> +		return ret;
> +	}
> +
> +	/* start up the network device */
> +	mod->can.state = CAN_STATE_ERROR_ACTIVE;
> +	netif_start_queue(ndev);
> +
> +	return 0;
> +}
> +
> +static int ican3_stop(struct net_device *ndev)
> +{
> +	struct ican3_dev *mod = netdev_priv(ndev);
> +	int ret;
> +
> +	/* stop the network device xmit routine */
> +	netif_stop_queue(ndev);
> +	mod->can.state = CAN_STATE_STOPPED;
> +
> +	/* bring the bus offline, stop receiving packets */
> +	ret = ican3_set_bus_state(mod, false);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to set bus-off\n");
> +		return ret;
> +	}
> +
> +	/* close the CAN layer */
> +	close_candev(ndev);
> +	return 0;
> +}
> +
> +static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
> +{
> +	struct ican3_dev *mod = netdev_priv(ndev);
> +	struct net_device_stats *stats = &ndev->stats;
> +	struct can_frame *cf = (struct can_frame *)skb->data;
> +	struct ican3_fast_desc desc;
> +	void __iomem *desc_addr;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&mod->lock, flags);
> +
> +	/* check that we can actually transmit */
> +	if (!ican3_txok(mod)) {
> +		dev_err(mod->dev, "no free descriptors, stopping queue\n");
> +		netif_stop_queue(ndev);
> +		spin_unlock_irqrestore(&mod->lock, flags);
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	/* copy the control bits of the descriptor */
> +	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
> +	desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
> +	memset(&desc, 0, sizeof(desc));
> +	memcpy_fromio(&desc, desc_addr, 1);
> +
> +	/* convert the Linux CAN frame into ICAN3 format */
> +	can_frame_to_ican3(mod, cf, &desc);
> +
> +	/*
> +	 * the programming manual says that you must set the IVALID bit, then
> +	 * interrupt, then set the valid bit. Quite weird, but it seems to be
> +	 * required for this to work
> +	 */
> +	desc.control |= DESC_IVALID;
> +	memcpy_toio(desc_addr, &desc, sizeof(desc));
> +
> +	/* generate a MODULbus interrupt to the microcontroller */
> +	iowrite8(0x01, &mod->dpmctrl->interrupt);
> +
> +	desc.control ^= DESC_VALID;
> +	memcpy_toio(desc_addr, &desc, sizeof(desc));
> +
> +	/* update the next buffer pointer */
> +	mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
> +						     : (mod->fasttx_num + 1);
> +
> +	/* update statistics */
> +	stats->tx_packets++;
> +	stats->tx_bytes += cf->can_dlc;
> +	kfree_skb(skb);
> +
> +	/*
> +	 * This hardware doesn't have TX-done notifications, so we'll try and
> +	 * emulate it the best we can using ECHO skbs. Get the next TX
> +	 * descriptor, and see if we have room to send. If not, stop the queue.
> +	 * It will be woken when the ECHO skb for the current packet is recv'd.
> +	 */
> +
> +	/* copy the control bits of the descriptor */
> +	if (!ican3_txok(mod))
> +		netif_stop_queue(ndev);
> +
> +	spin_unlock_irqrestore(&mod->lock, flags);
> +	return NETDEV_TX_OK;
> +}
> +
> +static const struct net_device_ops ican3_netdev_ops = {
> +	.ndo_open	= ican3_open,
> +	.ndo_stop	= ican3_stop,
> +	.ndo_start_xmit	= ican3_xmit,
> +};
> +
> +/*
> + * Low-level CAN Device
> + */
> +
> +/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
> +static struct can_bittiming_const ican3_bittiming_const = {
> +	.name = DRV_NAME,
> +	.tseg1_min = 1,
> +	.tseg1_max = 16,
> +	.tseg2_min = 1,
> +	.tseg2_max = 8,
> +	.sjw_max = 4,
> +	.brp_min = 1,
> +	.brp_max = 64,
> +	.brp_inc = 1,
> +};
> +
> +/*
> + * This routine was stolen from drivers/net/can/sja1000/sja1000.c
> + *
> + * The bittiming register command for the ICAN3 just sets the bit timing
> + * registers on the SJA1000 chip directly
> + */
> +static int ican3_set_bittiming(struct net_device *ndev)
> +{
> +	struct ican3_dev *mod = netdev_priv(ndev);
> +	struct can_bittiming *bt = &mod->can.bittiming;
> +	struct ican3_msg msg;
> +	u8 btr0, btr1;
> +
> +	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
> +	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
> +		(((bt->phase_seg2 - 1) & 0x7) << 4);
> +	if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
> +		btr1 |= 0x80;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.spec = MSG_CBTRREQ;
> +	msg.len = cpu_to_le16(4);
> +	msg.data[0] = 0x00;
> +	msg.data[1] = 0x00;
> +	msg.data[2] = btr0;
> +	msg.data[3] = btr1;
> +
> +	return ican3_send_msg(mod, &msg);
> +}
> +
> +static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
> +{
> +	struct ican3_dev *mod = netdev_priv(ndev);
> +	int ret;
> +
> +	if (mode != CAN_MODE_START)
> +		return -ENOTSUPP;
> +
> +	/* bring the bus online */
> +	ret = ican3_set_bus_state(mod, true);
> +	if (ret) {
> +		dev_err(mod->dev, "unable to set bus-on\n");
> +		return ret;
> +	}
> +
> +	/* start up the network device */
> +	mod->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +	if (netif_queue_stopped(ndev))
> +		netif_wake_queue(ndev);
> +
> +	return 0;
> +}
> +
> +/*
> + * PCI Subsystem
> + */
> +
> +static int __devinit ican3_probe(struct platform_device *pdev)
> +{
> +	struct janz_platform_data *pdata;
> +	struct net_device *ndev;
> +	struct ican3_dev *mod;
> +	struct resource *res;
> +	struct device *dev;
> +	int ret;
> +
> +	pdata = pdev->dev.platform_data;
> +	if (!pdata)
> +		return -ENXIO;
> +
> +	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
> +
> +	/* save the struct device for printing */
> +	dev = &pdev->dev;
> +
> +	/* allocate the CAN device and private data */
> +	ndev = alloc_candev(sizeof(*mod), 0);
> +	if (!ndev) {
> +		dev_err(dev, "unable to allocate CANdev\n");
> +		ret = -ENOMEM;
> +		goto out_return;
> +	}
> +
> +	platform_set_drvdata(pdev, ndev);
> +	mod = netdev_priv(ndev);
> +	mod->ndev = ndev;
> +	mod->dev = &pdev->dev;
> +	mod->num = pdata->modno;
> +	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
> +	spin_lock_init(&mod->lock);
> +
> +	/* the first unallocated page in the DPM is 9 */
> +	mod->free_page = DPM_FREE_START;
> +
> +	ndev->netdev_ops = &ican3_netdev_ops;
> +	ndev->flags |= IFF_ECHO;
> +	SET_NETDEV_DEV(ndev, &pdev->dev);
> +
> +	mod->can.clock.freq = 8000000;
> +	mod->can.bittiming_const = &ican3_bittiming_const;
> +	mod->can.do_set_bittiming = ican3_set_bittiming;
> +	mod->can.do_set_mode = ican3_set_mode;
> +
> +	/* find our IRQ number */
> +	mod->irq = platform_get_irq(pdev, 0);
> +	if (mod->irq < 0) {
> +		dev_err(dev, "IRQ line not found\n");
> +		ret = -ENODEV;
> +		goto out_free_ndev;
> +	}
> +
> +	ndev->irq = mod->irq;
> +
> +	/* get access to the MODULbus registers for this module */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(dev, "MODULbus registers not found\n");
> +		ret = -ENODEV;
> +		goto out_free_ndev;
> +	}
> +
> +	mod->dpm = ioremap(res->start, resource_size(res));
> +	if (!mod->dpm) {
> +		dev_err(dev, "MODULbus registers not ioremap\n");
> +		ret = -ENOMEM;
> +		goto out_free_ndev;
> +	}
> +
> +	mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
> +
> +	/* get access to the control registers for this module */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	if (!res) {
> +		dev_err(dev, "CONTROL registers not found\n");
> +		ret = -ENODEV;
> +		goto out_iounmap_dpm;
> +	}
> +
> +	mod->ctrl = ioremap(res->start, resource_size(res));
> +	if (!mod->ctrl) {
> +		dev_err(dev, "CONTROL registers not ioremap\n");
> +		ret = -ENOMEM;
> +		goto out_iounmap_dpm;
> +	}
> +
> +	/* disable our IRQ, then hookup the IRQ handler */
> +	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
> +	ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
> +	if (ret) {
> +		dev_err(dev, "unable to request IRQ\n");
> +		goto out_iounmap_ctrl;
> +	}
> +
> +	/* reset and initialize the CAN controller into fast mode */
> +	napi_enable(&mod->napi);
> +	ret = ican3_startup_module(mod);
> +	if (ret) {
> +		dev_err(dev, "%s: unable to start CANdev\n", __func__);
> +		goto out_free_irq;
> +	}
> +
> +	/* register with the Linux CAN layer */
> +	ret = register_candev(ndev);
> +	if (ret) {
> +		dev_err(dev, "%s: unable to register CANdev\n", __func__);
> +		goto out_free_irq;
> +	}
> +
> +	dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
> +	return 0;
> +
> +out_free_irq:
> +	napi_disable(&mod->napi);
> +	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
> +	free_irq(mod->irq, mod);
> +out_iounmap_ctrl:
> +	iounmap(mod->ctrl);
> +out_iounmap_dpm:
> +	iounmap(mod->dpm);
> +out_free_ndev:
> +	free_candev(ndev);
> +out_return:
> +	return ret;
> +}
> +
> +static int __devexit ican3_remove(struct platform_device *pdev)
> +{
> +	struct net_device *ndev = platform_get_drvdata(pdev);
> +	struct ican3_dev *mod = netdev_priv(ndev);
> +
> +	/* unregister the netdevice, stop interrupts */
> +	unregister_netdev(ndev);
> +	napi_disable(&mod->napi);
> +	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
> +	free_irq(mod->irq, mod);
> +
> +	/* put the module into reset */
> +	ican3_shutdown_module(mod);
> +
> +	/* unmap all registers */
> +	iounmap(mod->ctrl);
> +	iounmap(mod->dpm);
> +
> +	free_candev(ndev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver ican3_driver = {
> +	.driver		= {
> +		.name	= DRV_NAME,
> +		.owner	= THIS_MODULE,
> +	},
> +	.probe		= ican3_probe,
> +	.remove		= __devexit_p(ican3_remove),
> +};
> +
> +static int __init ican3_init(void)
> +{
> +	return platform_driver_register(&ican3_driver);
> +}
> +
> +static void __exit ican3_exit(void)
> +{
> +	platform_driver_unregister(&ican3_driver);
> +}
> +
> +MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
> +MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:janz-ican3");
> +
> +module_init(ican3_init);
> +module_exit(ican3_exit);
> -- 
> 1.5.4.3
> 
> _______________________________________________
> Socketcan-core mailing list
> Socketcan-core@lists.berlios.de
> https://lists.berlios.de/mailman/listinfo/socketcan-core

^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module
  2010-03-02 21:22 [PATCH 0/3 RFCv4] add support for Janz MODULbus devices Ira W. Snyder
@ 2010-03-02 21:22 ` Ira W. Snyder
  2010-03-17 19:33   ` Ira W. Snyder
  0 siblings, 1 reply; 41+ messages in thread
From: Ira W. Snyder @ 2010-03-02 21:22 UTC (permalink / raw)
  To: linux-kernel; +Cc: socketcan-core

The Janz VMOD-ICAN3 is a MODULbus daughterboard which fits onto any
MODULbus carrier board. It is an intelligent CAN controller with a
microcontroller and associated firmware.

Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu>
Cc: socketcan-core@lists.berlios.de
---
 drivers/net/can/Kconfig      |   10 +
 drivers/net/can/Makefile     |    1 +
 drivers/net/can/janz-ican3.c | 1659 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1670 insertions(+), 0 deletions(-)
 create mode 100644 drivers/net/can/janz-ican3.c

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 05b7517..2c5227c 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -63,6 +63,16 @@ config CAN_BFIN
 	  To compile this driver as a module, choose M here: the
 	  module will be called bfin_can.
 
+config CAN_JANZ_ICAN3
+	tristate "Janz VMOD-ICAN3 Intelligent CAN controller"
+	depends on CAN_DEV && MFD_JANZ_CMODIO
+	---help---
+	  Driver for Janz VMOD-ICAN3 Intelligent CAN controller module, which
+	  connects to a MODULbus carrier board.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called janz-ican3.ko.
+
 source "drivers/net/can/mscan/Kconfig"
 
 source "drivers/net/can/sja1000/Kconfig"
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 7a702f2..9047cd0 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_CAN_AT91)		+= at91_can.o
 obj-$(CONFIG_CAN_TI_HECC)	+= ti_hecc.o
 obj-$(CONFIG_CAN_MCP251X)	+= mcp251x.o
 obj-$(CONFIG_CAN_BFIN)		+= bfin_can.o
+obj-$(CONFIG_CAN_JANZ_ICAN3)	+= janz-ican3.o
 
 ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/janz-ican3.c b/drivers/net/can/janz-ican3.c
new file mode 100644
index 0000000..94d4995
--- /dev/null
+++ b/drivers/net/can/janz-ican3.c
@@ -0,0 +1,1659 @@
+/*
+ * Janz MODULbus VMOD-ICAN3 CAN Interface Driver
+ *
+ * Copyright (c) 2010 Ira W. Snyder <iws@ovro.caltech.edu>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <linux/netdevice.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+#include <linux/can/error.h>
+
+#include <linux/mfd/janz.h>
+
+/* the DPM has 64k of memory, organized into 256x 256 byte pages */
+#define DPM_NUM_PAGES		256
+#define DPM_PAGE_SIZE		256
+#define DPM_PAGE_ADDR(p)	((p) * DPM_PAGE_SIZE)
+
+/* JANZ ICAN3 "old-style" host interface queue page numbers */
+#define QUEUE_OLD_CONTROL	0
+#define QUEUE_OLD_RB0		1
+#define QUEUE_OLD_RB1		2
+#define QUEUE_OLD_WB0		3
+#define QUEUE_OLD_WB1		4
+
+/* Janz ICAN3 "old-style" host interface control registers */
+#define MSYNC_PEER		0x00		/* ICAN only */
+#define MSYNC_LOCL		0x01		/* host only */
+#define TARGET_RUNNING		0x02
+
+#define MSYNC_RB0		0x01
+#define MSYNC_RB1		0x02
+#define MSYNC_RBLW		0x04
+#define MSYNC_RB_MASK		(MSYNC_RB0 | MSYNC_RB1)
+
+#define MSYNC_WB0		0x10
+#define MSYNC_WB1		0x20
+#define MSYNC_WBLW		0x40
+#define MSYNC_WB_MASK		(MSYNC_WB0 | MSYNC_WB1)
+
+/* Janz ICAN3 "new-style" host interface queue page numbers */
+#define QUEUE_TOHOST		5
+#define QUEUE_FROMHOST_MID	6
+#define QUEUE_FROMHOST_HIGH	7
+#define QUEUE_FROMHOST_LOW	8
+
+/* The first free page in the DPM is #9 */
+#define DPM_FREE_START		9
+
+/* Janz ICAN3 "new-style" and "fast" host interface descriptor flags */
+#define DESC_VALID		0x80
+#define DESC_WRAP		0x40
+#define DESC_INTERRUPT		0x20
+#define DESC_IVALID		0x10
+#define DESC_LEN(len)		(len)
+
+/* Janz ICAN3 Firmware Messages */
+#define MSG_CONNECTI		0x02
+#define MSG_DISCONNECT		0x03
+#define MSG_IDVERS		0x04
+#define MSG_MSGLOST		0x05
+#define MSG_NEWHOSTIF		0x08
+#define MSG_SETAFILMASK		0x10
+#define MSG_INITFDPMQUEUE	0x11
+#define MSG_HWCONF		0x12
+#define MSG_FMSGLOST		0x15
+#define MSG_CEVTIND		0x37
+#define MSG_CBTRREQ		0x41
+#define MSG_COFFREQ		0x42
+#define MSG_CONREQ		0x43
+#define MSG_CCONFREQ		0x47
+
+/* Janz ICAN3 CAN Set Acceptance Filter Mask Message Types */
+#define SETAFILMASK_REJECT	0x00
+#define SETAFILMASK_FASTIF	0x02
+
+/* Janz ICAN3 CAN Hardware Configuration Message Types */
+#define HWCONF_TERMINATE_ON	0x01
+#define HWCONF_TERMINATE_OFF	0x00
+
+/* Janz ICAN3 CAN Event Indication Message Types */
+#define CEVTIND_EI		0x01
+#define CEVTIND_DOI		0x02
+#define CEVTIND_LOST		0x04
+#define CEVTIND_FULL		0x08
+#define CEVTIND_BEI		0x10
+
+#define CEVTIND_CHIP_SJA1000	0x02
+
+#define ICAN3_BUSERR_QUOTA_MAX	255
+
+/* Janz ICAN3 CAN Frame Conversion */
+#define ICAN3_ECHO	0x10
+#define ICAN3_EFF_RTR	0x40
+#define ICAN3_SFF_RTR	0x10
+#define ICAN3_EFF	0x80
+
+#define ICAN3_CAN_TYPE_MASK	0x0f
+#define ICAN3_CAN_TYPE_SFF	0x00
+#define ICAN3_CAN_TYPE_EFF	0x01
+
+#define ICAN3_CAN_DLC_MASK	0x0f
+
+/*
+ * SJA1000 Status and Error Register Definitions
+ *
+ * Copied from drivers/net/can/sja1000/sja1000.h
+ */
+
+/* status register content */
+#define SR_BS		0x80
+#define SR_ES		0x40
+#define SR_TS		0x20
+#define SR_RS		0x10
+#define SR_TCS		0x08
+#define SR_TBS		0x04
+#define SR_DOS		0x02
+#define SR_RBS		0x01
+
+#define SR_CRIT (SR_BS|SR_ES)
+
+/* ECC register */
+#define ECC_SEG		0x1F
+#define ECC_DIR		0x20
+#define ECC_ERR		6
+#define ECC_BIT		0x00
+#define ECC_FORM	0x40
+#define ECC_STUFF	0x80
+#define ECC_MASK	0xc0
+
+/* Number of buffers for use in the "new-style" host interface */
+#define ICAN3_NEW_BUFFERS	16
+
+/* Number of buffers for use in the "fast" host interface */
+#define ICAN3_TX_BUFFERS	512
+#define ICAN3_RX_BUFFERS	1024
+
+/* Driver Name */
+#define DRV_NAME "janz-ican3"
+
+/* DPM Control Registers -- starts at offset 0x100 in the MODULbus registers */
+struct ican3_dpm_control {
+	/* window address register */
+	u8 window_address;
+	u8 unused1;
+
+	/*
+	 * Read access: clear interrupt from microcontroller
+	 * Write access: send interrupt to microcontroller
+	 */
+	u8 interrupt;
+	u8 unused2;
+
+	/* write-only: reset all hardware on the module */
+	u8 hwreset;
+	u8 unused3;
+
+	/* write-only: generate an interrupt to the TPU */
+	u8 tpuinterrupt;
+};
+
+struct ican3_dev {
+
+	/* must be the first member */
+	struct can_priv can;
+
+	/* CAN network device */
+	struct net_device *ndev;
+	struct napi_struct napi;
+
+	/* Device for printing */
+	struct device *dev;
+
+	/* module number */
+	unsigned int num;
+
+	/* base address of registers and IRQ */
+	struct janz_cmodio_onboard_regs __iomem *ctrl;
+	struct ican3_dpm_control *dpmctrl;
+	void __iomem *dpm;
+	int irq;
+
+	/* old and new style host interface */
+	unsigned int iftype;
+	spinlock_t lock;
+
+	/* new host interface */
+	unsigned int rx_int;
+	unsigned int rx_num;
+	unsigned int tx_num;
+
+	/* fast host interface */
+	unsigned int fastrx_start;
+	unsigned int fastrx_int;
+	unsigned int fastrx_num;
+	unsigned int fasttx_start;
+	unsigned int fasttx_num;
+
+	/* first free DPM page */
+	unsigned int free_page;
+};
+
+struct ican3_msg {
+	u8 control;
+	u8 spec;
+	__le16 len;
+	u8 data[252];
+};
+
+struct ican3_new_desc {
+	u8 control;
+	u8 pointer;
+};
+
+struct ican3_fast_desc {
+	u8 control;
+	u8 command;
+	u8 data[14];
+};
+
+/* write to the window basic address register */
+static inline void ican3_set_page(struct ican3_dev *mod, unsigned int page)
+{
+	BUG_ON(page >= DPM_NUM_PAGES);
+	iowrite8(page, &mod->dpmctrl->window_address);
+}
+
+/*
+ * ICAN3 "old-style" host interface
+ */
+
+/*
+ * Recieve a message from the ICAN3 "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no message exists
+ */
+static int ican3_old_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_RB_MASK) == 0x00) {
+		dev_dbg(mod->dev, "no mbox for reading\n");
+		return -ENOMEM;
+	}
+
+	/* find the first free mbox to read */
+	if ((xord & MSYNC_RB_MASK) == MSYNC_RB_MASK)
+		mbox = (xord & MSYNC_RBLW) ? MSYNC_RB0 : MSYNC_RB1;
+	else
+		mbox = (xord & MSYNC_RB0) ? MSYNC_RB0 : MSYNC_RB1;
+
+	/* copy the message */
+	mbox_page = (mbox == MSYNC_RB0) ? QUEUE_OLD_RB0 : QUEUE_OLD_RB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/*
+	 * notify the firmware that the read buffer is available
+	 * for it to fill again
+	 */
+	locl ^= mbox;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * Send a message through the "old-style" firmware interface
+ *
+ * LOCKING: must hold mod->lock
+ *
+ * returns 0 on success, -ENOMEM when no free space exists
+ */
+static int ican3_old_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned int mbox, mbox_page;
+	u8 locl, peer, xord;
+
+	/* get the MSYNC registers */
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	peer = ioread8(mod->dpm + MSYNC_PEER);
+	locl = ioread8(mod->dpm + MSYNC_LOCL);
+	xord = locl ^ peer;
+
+	if ((xord & MSYNC_WB_MASK) == MSYNC_WB_MASK) {
+		dev_err(mod->dev, "no mbox for writing\n");
+		return -ENOMEM;
+	}
+
+	/* calculate a free mbox to use */
+	mbox = (xord & MSYNC_WB0) ? MSYNC_WB1 : MSYNC_WB0;
+
+	/* copy the message to the DPM */
+	mbox_page = (mbox == MSYNC_WB0) ? QUEUE_OLD_WB0 : QUEUE_OLD_WB1;
+	ican3_set_page(mod, mbox_page);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	locl ^= mbox;
+	if (mbox == MSYNC_WB1)
+		locl |= MSYNC_WBLW;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	iowrite8(locl, mod->dpm + MSYNC_LOCL);
+	return 0;
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Setup
+ */
+
+static void __devinit ican3_init_new_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_new_desc desc;
+	unsigned long flags;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* setup the internal datastructures for RX */
+	mod->rx_num = 0;
+	mod->rx_int = 0;
+
+	/* tohost queue descriptors are in page 5 */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	dst = mod->dpm;
+
+	/* initialize the tohost (rx) queue descriptors: pages 9-24 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_INTERRUPT | DESC_LEN(1); /* I L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost (tx) mid queue descriptors are in page 6 */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	dst = mod->dpm;
+
+	/* setup the internal datastructures for TX */
+	mod->tx_num = 0;
+
+	/* initialize the fromhost mid queue descriptors: pages 25-40 */
+	for (i = 0; i < ICAN3_NEW_BUFFERS; i++) {
+		desc.control = DESC_VALID | DESC_LEN(1); /* V L=1 */
+		desc.pointer = mod->free_page;
+
+		/* set wrap flag on last buffer */
+		if (i == ICAN3_NEW_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		memcpy_toio(dst, &desc, sizeof(desc));
+		dst += sizeof(desc);
+		mod->free_page++;
+	}
+
+	/* fromhost hi queue descriptors are in page 7 */
+	ican3_set_page(mod, QUEUE_FROMHOST_HIGH);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost hi queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	/* fromhost low queue descriptors are in page 8 */
+	ican3_set_page(mod, QUEUE_FROMHOST_LOW);
+	dst = mod->dpm;
+
+	/* initialize only a single buffer in the fromhost low queue (unused) */
+	desc.control = DESC_VALID | DESC_WRAP | DESC_LEN(1); /* VW L=1 */
+	desc.pointer = mod->free_page;
+	memcpy_toio(dst, &desc, sizeof(desc));
+	mod->free_page++;
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 Fast Host Interface Setup
+ */
+
+static void __devinit ican3_init_fast_host_interface(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc desc;
+	unsigned long flags;
+	unsigned int addr;
+	void __iomem *dst;
+	int i;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* save the start recv page */
+	mod->fastrx_start = mod->free_page;
+	mod->fastrx_num = 0;
+	mod->fastrx_int = 0;
+
+	/* build a single fast tohost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = 0x00;
+	desc.command = 1;
+
+	/* build the tohost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_RX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_RX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	/* make sure we page-align the next queue */
+	if (addr != 0)
+		mod->free_page++;
+
+	/* save the start xmit page */
+	mod->fasttx_start = mod->free_page;
+	mod->fasttx_num = 0;
+
+	/* build a single fast fromhost queue descriptor */
+	memset(&desc, 0, sizeof(desc));
+	desc.control = DESC_VALID;
+	desc.command = 1;
+
+	/* build the fromhost queue descriptor ring in memory */
+	addr = 0;
+	for (i = 0; i < ICAN3_TX_BUFFERS; i++) {
+
+		/* set the wrap bit on the last buffer */
+		if (i == ICAN3_TX_BUFFERS - 1)
+			desc.control |= DESC_WRAP;
+
+		/* switch to the correct page */
+		ican3_set_page(mod, mod->free_page);
+
+		/* copy the descriptor to the DPM */
+		dst = mod->dpm + addr;
+		memcpy_toio(dst, &desc, sizeof(desc));
+		addr += sizeof(desc);
+
+		/* move to the next page if necessary */
+		if (addr >= DPM_PAGE_SIZE) {
+			addr = 0;
+			mod->free_page++;
+		}
+	}
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+}
+
+/*
+ * ICAN3 "new-style" Host Interface Message Helpers
+ */
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->tx_num * sizeof(desc));
+
+	/* switch to the fromhost mid queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no free buffers\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_toio(mod->dpm, msg, sizeof(*msg));
+
+	/* switch back to the descriptor, set the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_FROMHOST_MID);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the tx number */
+	mod->tx_num = (desc.control & DESC_WRAP) ? 0 : (mod->tx_num + 1);
+	return 0;
+}
+
+/*
+ * LOCKING: must hold mod->lock
+ */
+static int ican3_new_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct ican3_new_desc desc;
+	void __iomem *desc_addr = mod->dpm + (mod->rx_num * sizeof(desc));
+
+	/* switch to the tohost queue, and read the buffer descriptor */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	if (!(desc.control & DESC_VALID)) {
+		dev_dbg(mod->dev, "%s: no buffers to recv\n", __func__);
+		return -ENOMEM;
+	}
+
+	/* switch to the data page, copy the data */
+	ican3_set_page(mod, desc.pointer);
+	memcpy_fromio(msg, mod->dpm, sizeof(*msg));
+
+	/* switch back to the descriptor, toggle the valid bit, write it back */
+	ican3_set_page(mod, QUEUE_TOHOST);
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the rx number */
+	mod->rx_num = (desc.control & DESC_WRAP) ? 0 : (mod->rx_num + 1);
+	return 0;
+}
+
+/*
+ * Message Send / Recv Helpers
+ */
+
+static int ican3_send_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_send_msg(mod, msg);
+	else
+		ret = ican3_new_send_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+static int ican3_recv_msg(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	unsigned long flags;
+	int ret;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	if (mod->iftype == 0)
+		ret = ican3_old_recv_msg(mod, msg);
+	else
+		ret = ican3_new_recv_msg(mod, msg);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return ret;
+}
+
+/*
+ * Quick Pre-constructed Messages
+ */
+
+static int __devinit ican3_msg_connect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CONNECTI;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devexit ican3_msg_disconnect(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_DISCONNECT;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_msg_newhostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_NEWHOSTIF;
+	msg.len = cpu_to_le16(0);
+
+	/* If we're not using the old interface, switching seems bogus */
+	WARN_ON(mod->iftype != 0);
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* mark the module as using the new host interface */
+	mod->iftype = 1;
+	return 0;
+}
+
+static int __devinit ican3_msg_fasthostif(struct ican3_dev *mod)
+{
+	struct ican3_msg msg;
+	unsigned int addr;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_INITFDPMQUEUE;
+	msg.len = cpu_to_le16(8);
+
+	/* write the tohost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fastrx_start);
+	msg.data[0] = addr & 0xff;
+	msg.data[1] = (addr >> 8) & 0xff;
+	msg.data[2] = (addr >> 16) & 0xff;
+	msg.data[3] = (addr >> 24) & 0xff;
+
+	/* write the fromhost queue start address */
+	addr = DPM_PAGE_ADDR(mod->fasttx_start);
+	msg.data[4] = addr & 0xff;
+	msg.data[5] = (addr >> 8) & 0xff;
+	msg.data[6] = (addr >> 16) & 0xff;
+	msg.data[7] = (addr >> 24) & 0xff;
+
+	/* If we're not using the new interface yet, we cannot do this */
+	WARN_ON(mod->iftype != 1);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Setup the CAN filter to either accept or reject all
+ * messages from the CAN bus.
+ */
+static int __devinit ican3_set_id_filter(struct ican3_dev *mod, bool accept)
+{
+	struct ican3_msg msg;
+	int ret;
+
+	/* Standard Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(5);
+	msg.data[0] = 0x00; /* IDLo LSB */
+	msg.data[1] = 0x00; /* IDLo MSB */
+	msg.data[2] = 0xff; /* IDHi LSB */
+	msg.data[3] = 0x07; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[4] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	ret = ican3_send_msg(mod, &msg);
+	if (ret)
+		return ret;
+
+	/* Extended Frame Format */
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_SETAFILMASK;
+	msg.len = cpu_to_le16(13);
+	msg.data[0] = 0;    /* MUX = 0 */
+	msg.data[1] = 0x00; /* IDLo LSB */
+	msg.data[2] = 0x00;
+	msg.data[3] = 0x00;
+	msg.data[4] = 0x20; /* IDLo MSB */
+	msg.data[5] = 0xff; /* IDHi LSB */
+	msg.data[6] = 0xff;
+	msg.data[7] = 0xff;
+	msg.data[8] = 0x3f; /* IDHi MSB */
+
+	/* accept all frames for fast host if, or reject all frames */
+	msg.data[9] = accept ? SETAFILMASK_FASTIF : SETAFILMASK_REJECT;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * Bring the CAN bus online or offline
+ */
+static int ican3_set_bus_state(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = on ? MSG_CONREQ : MSG_COFFREQ;
+	msg.len = cpu_to_le16(0);
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_set_termination(struct ican3_dev *mod, bool on)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_HWCONF;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = on ? HWCONF_TERMINATE_ON : HWCONF_TERMINATE_OFF;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int __devinit ican3_set_buserror(struct ican3_dev *mod, u8 quota)
+{
+	struct ican3_msg msg;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CCONFREQ;
+	msg.len = cpu_to_le16(2);
+	msg.data[0] = 0x00;
+	msg.data[1] = quota;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+/*
+ * ICAN3 to Linux CAN Frame Conversion
+ */
+
+static void ican3_to_can_frame(struct ican3_dev *mod,
+			       struct ican3_fast_desc *desc,
+			       struct can_frame *cf)
+{
+	if ((desc->command & ICAN3_CAN_TYPE_MASK) == ICAN3_CAN_TYPE_SFF) {
+		dev_dbg(mod->dev, "%s: old frame format\n", __func__);
+		if (desc->data[1] & ICAN3_SFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		cf->can_id |= desc->data[0] << 3;
+		cf->can_id |= (desc->data[1] & 0xe0) >> 5;
+		cf->can_dlc = desc->data[1] & ICAN3_CAN_DLC_MASK;
+		memcpy(cf->data, &desc->data[2], sizeof(cf->data));
+	} else {
+		dev_dbg(mod->dev, "%s: new frame format\n", __func__);
+		cf->can_dlc = desc->data[0] & ICAN3_CAN_DLC_MASK;
+		if (desc->data[0] & ICAN3_EFF_RTR)
+			cf->can_id |= CAN_RTR_FLAG;
+
+		if (desc->data[0] & ICAN3_EFF) {
+			cf->can_id |= CAN_EFF_FLAG;
+			cf->can_id |= desc->data[2] << 21; /* 28-21 */
+			cf->can_id |= desc->data[3] << 13; /* 20-13 */
+			cf->can_id |= desc->data[4] << 5;  /* 12-5  */
+			cf->can_id |= (desc->data[5] & 0xf8) >> 3;
+		} else {
+			cf->can_id |= desc->data[2] << 3;  /* 10-3  */
+			cf->can_id |= desc->data[3] >> 5;  /* 2-0   */
+		}
+
+		memcpy(cf->data, &desc->data[6], sizeof(cf->data));
+	}
+}
+
+static void can_frame_to_ican3(struct ican3_dev *mod,
+			       struct can_frame *cf,
+			       struct ican3_fast_desc *desc)
+{
+	/* clear out any stale data in the descriptor */
+	memset(desc->data, 0, sizeof(desc->data));
+
+	/* we always use the extended format, with the ECHO flag set */
+	desc->command = ICAN3_CAN_TYPE_EFF;
+	desc->data[0] |= cf->can_dlc;
+	desc->data[1] |= ICAN3_ECHO;
+
+	if (cf->can_id & CAN_RTR_FLAG)
+		desc->data[0] |= ICAN3_EFF_RTR;
+
+	/* pack the id into the correct places */
+	if (cf->can_id & CAN_EFF_FLAG) {
+		dev_dbg(mod->dev, "%s: extended frame\n", __func__);
+		desc->data[0] |= ICAN3_EFF;
+		desc->data[2] = (cf->can_id & 0x1fe00000) >> 21; /* 28-21 */
+		desc->data[3] = (cf->can_id & 0x001fe000) >> 13; /* 20-13 */
+		desc->data[4] = (cf->can_id & 0x00001fe0) >> 5;  /* 12-5  */
+		desc->data[5] = (cf->can_id & 0x0000001f) << 3;  /* 4-0   */
+	} else {
+		dev_dbg(mod->dev, "%s: standard frame\n", __func__);
+		desc->data[2] = (cf->can_id & 0x7F8) >> 3; /* bits 10-3 */
+		desc->data[3] = (cf->can_id & 0x007) << 5; /* bits 2-0  */
+	}
+
+	/* copy the data bits into the descriptor */
+	memcpy(&desc->data[6], cf->data, sizeof(cf->data));
+}
+
+/*
+ * Interrupt Handling
+ */
+
+/*
+ * Handle an ID + Version message response from the firmware. We never generate
+ * this message in production code, but it is very useful when debugging to be
+ * able to display this message.
+ */
+static void ican3_handle_idvers(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "IDVERS response: %s\n", msg->data);
+}
+
+static void ican3_handle_msglost(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	/*
+	 * Report that communication messages with the microcontroller firmware
+	 * are being lost. These are never CAN frames, so we do not generate an
+	 * error frame for userspace
+	 */
+	if (msg->spec == MSG_MSGLOST) {
+		dev_err(mod->dev, "lost %d control messages\n", msg->data[0]);
+		return;
+	}
+
+	/*
+	 * Oops, this indicates that we have lost messages in the fast queue,
+	 * which are exclusively CAN messages. Our driver isn't reading CAN
+	 * frames fast enough.
+	 *
+	 * We'll pretend that the SJA1000 told us that it ran out of buffer
+	 * space, because there is not a better message for this.
+	 */
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_errors++;
+		stats->rx_bytes += cf->can_dlc;
+		netif_rx(skb);
+	}
+}
+
+/*
+ * Handle CAN Event Indication Messages from the firmware
+ *
+ * The ICAN3 firmware provides the values of some SJA1000 registers when it
+ * generates this message. The code below is largely copied from the
+ * drivers/net/can/sja1000/sja1000.c file, and adapted as necessary
+ */
+static int ican3_handle_cevtind(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	struct net_device *dev = mod->ndev;
+	struct net_device_stats *stats = &dev->stats;
+	enum can_state state = mod->can.state;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	u8 status, isrc;
+
+	/* we can only handle the SJA1000 part */
+	if (msg->data[1] != CEVTIND_CHIP_SJA1000) {
+		dev_err(mod->dev, "unable to handle errors on non-SJA1000\n");
+		return -ENODEV;
+	}
+
+	/* check the message length for sanity */
+	if (msg->len < 6) {
+		dev_err(mod->dev, "error message too short\n");
+		return -EINVAL;
+	}
+
+	skb = alloc_can_err_skb(dev, &cf);
+	if (skb == NULL)
+		return -ENOMEM;
+
+	isrc = msg->data[0];
+	status = msg->data[3];
+
+	/* data overrun interrupt */
+	if (isrc == CEVTIND_DOI || isrc == CEVTIND_LOST) {
+		cf->can_id |= CAN_ERR_CRTL;
+		cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+		stats->rx_over_errors++;
+		stats->rx_errors++;
+		dev_info(mod->dev, "%s: overflow frame generated\n", __func__);
+	}
+
+	/* error warning interrupt */
+	if (isrc == CEVTIND_EI) {
+		if (status & SR_BS) {
+			state = CAN_STATE_BUS_OFF;
+			cf->can_id |= CAN_ERR_BUSOFF;
+			can_bus_off(dev);
+		} else if (status & SR_ES) {
+			state = CAN_STATE_ERROR_WARNING;
+		} else {
+			state = CAN_STATE_ERROR_ACTIVE;
+		}
+	}
+
+	/* bus error interrupt */
+	if (isrc == CEVTIND_BEI) {
+		u8 ecc = msg->data[2];
+		mod->can.can_stats.bus_error++;
+		stats->rx_errors++;
+		cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+		switch (ecc & ECC_MASK) {
+		case ECC_BIT:
+			cf->data[2] |= CAN_ERR_PROT_BIT;
+			break;
+		case ECC_FORM:
+			cf->data[2] |= CAN_ERR_PROT_FORM;
+			break;
+		case ECC_STUFF:
+			cf->data[2] |= CAN_ERR_PROT_STUFF;
+			break;
+		default:
+			cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+			cf->data[3] = ecc & ECC_SEG;
+			break;
+		}
+
+		if ((ecc & ECC_DIR) == 0)
+			cf->data[2] |= CAN_ERR_PROT_TX;
+	}
+
+	if (state != mod->can.state && (state == CAN_STATE_ERROR_WARNING ||
+					state == CAN_STATE_ERROR_PASSIVE)) {
+		u8 rxerr = msg->data[4];
+		u8 txerr = msg->data[5];
+		cf->can_id |= CAN_ERR_CRTL;
+		if (state == CAN_STATE_ERROR_WARNING) {
+			mod->can.can_stats.error_warning++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_WARNING :
+				CAN_ERR_CRTL_RX_WARNING;
+		} else {
+			mod->can.can_stats.error_passive++;
+			cf->data[1] = (txerr > rxerr) ?
+				CAN_ERR_CRTL_TX_PASSIVE :
+				CAN_ERR_CRTL_RX_PASSIVE;
+		}
+	}
+
+	mod->can.state = state;
+	stats->rx_errors++;
+	stats->rx_bytes += cf->can_dlc;
+	netif_rx(skb);
+	return 0;
+}
+
+static void ican3_handle_unknown_message(struct ican3_dev *mod,
+					struct ican3_msg *msg)
+{
+	dev_warn(mod->dev, "recieved unknown message: spec 0x%.2x length %d\n",
+			   msg->spec, le16_to_cpu(msg->len));
+}
+
+/*
+ * Handle a control message from the firmware
+ */
+static void ican3_handle_message(struct ican3_dev *mod, struct ican3_msg *msg)
+{
+	dev_dbg(mod->dev, "%s: modno %d spec 0x%.2x len %d bytes\n", __func__,
+			   mod->num, msg->spec, le16_to_cpu(msg->len));
+
+	switch (msg->spec) {
+	case MSG_IDVERS:
+		ican3_handle_idvers(mod, msg);
+		break;
+	case MSG_MSGLOST:
+	case MSG_FMSGLOST:
+		ican3_handle_msglost(mod, msg);
+		break;
+	case MSG_CEVTIND:
+		ican3_handle_cevtind(mod, msg);
+		break;
+	default:
+		ican3_handle_unknown_message(mod, msg);
+		break;
+	}
+}
+
+/*
+ * Check that there is room in the TX ring to transmit another skb
+ *
+ * LOCKING: must hold mod->lock
+ */
+static bool ican3_txok(struct ican3_dev *mod)
+{
+	struct ican3_fast_desc __iomem *desc;
+	u8 control;
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc = mod->dpm + ((mod->fasttx_num % 16) * sizeof(*desc));
+	control = ioread8(&desc->control);
+
+	/* if the control bits are not valid, then we have no more space */
+	if (!(control & DESC_VALID))
+		return false;
+
+	return true;
+}
+
+/*
+ * Recieve one CAN frame from the hardware
+ *
+ * This works like the core of a NAPI function, but is intended to be called
+ * from workqueue context instead. This driver already needs a workqueue to
+ * process control messages, so we use the workqueue instead of using NAPI.
+ * This was done to simplify locking.
+ *
+ * CONTEXT: must be called from user context
+ */
+static int ican3_recv_skb(struct ican3_dev *mod)
+{
+	struct net_device *ndev = mod->ndev;
+	struct net_device_stats *stats = &ndev->stats;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* copy the whole descriptor */
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	desc_addr = mod->dpm + ((mod->fastrx_num % 16) * sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, sizeof(desc));
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* check that we actually have a CAN frame */
+	if (!(desc.control & DESC_VALID))
+		return -ENOBUFS;
+
+	/* allocate an skb */
+	skb = alloc_can_skb(ndev, &cf);
+	if (unlikely(skb == NULL)) {
+		stats->rx_dropped++;
+		goto err_noalloc;
+	}
+
+	/* convert the ICAN3 frame into Linux CAN format */
+	ican3_to_can_frame(mod, &desc, cf);
+
+	/* receive the skb, update statistics */
+	netif_receive_skb(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+
+err_noalloc:
+	/* toggle the valid bit and return the descriptor to the ring */
+	desc.control ^= DESC_VALID;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	ican3_set_page(mod, mod->fastrx_start + (mod->fastrx_num / 16));
+	memcpy_toio(desc_addr, &desc, 1);
+
+	/* update the next buffer pointer */
+	mod->fastrx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fastrx_num + 1);
+
+	/* there are still more buffers to process */
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return 0;
+}
+
+static int ican3_napi(struct napi_struct *napi, int budget)
+{
+	struct ican3_dev *mod = container_of(napi, struct ican3_dev, napi);
+	struct ican3_msg msg;
+	unsigned long flags;
+	int received = 0;
+	int ret;
+
+	/* process all communication messages */
+	while (true) {
+		ret = ican3_recv_msg(mod, &msg);
+		if (ret)
+			break;
+
+		ican3_handle_message(mod, &msg);
+	}
+
+	/* process all CAN frames from the fast interface */
+	while (received < budget) {
+		ret = ican3_recv_skb(mod);
+		if (ret)
+			break;
+
+		received++;
+	}
+
+	/* We have processed all packets that the adapter had, but it
+	 * was less than our budget, stop polling */
+	if (received < budget)
+		napi_complete(napi);
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* Wake up the transmit queue if necessary */
+	if (netif_queue_stopped(mod->ndev) && ican3_txok(mod))
+		netif_wake_queue(mod->ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+
+	/* re-enable interrupt generation */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+	return received;
+}
+
+static irqreturn_t ican3_irq(int irq, void *dev_id)
+{
+	struct ican3_dev *mod = dev_id;
+	u8 stat;
+
+	/*
+	 * The interrupt status register on this device reports interrupts
+	 * as zeroes instead of using ones like most other devices
+	 */
+	stat = ioread8(&mod->ctrl->int_disable) & (1 << mod->num);
+	if (stat == (1 << mod->num))
+		return IRQ_NONE;
+
+	dev_dbg(mod->dev, "IRQ: module %d\n", mod->num);
+
+	/* clear the MODULbus interrupt from the microcontroller */
+	ioread8(&mod->dpmctrl->interrupt);
+
+	/* disable interrupt generation, schedule the NAPI poller */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	napi_schedule(&mod->napi);
+	return IRQ_HANDLED;
+}
+
+/*
+ * Firmware reset, startup, and shutdown
+ */
+
+/*
+ * Reset an ICAN module to its power-on state
+ *
+ * CONTEXT: no network device registered
+ * LOCKING: work function disabled
+ */
+static int ican3_reset_module(struct ican3_dev *mod)
+{
+	u8 val = 1 << mod->num;
+	unsigned long start;
+	u8 runold, runnew;
+
+	/* disable interrupts so no more work is scheduled */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+
+	/* flush any pending work */
+	flush_scheduled_work();
+
+	/* the first unallocated page in the DPM is #9 */
+	mod->free_page = DPM_FREE_START;
+
+	ican3_set_page(mod, QUEUE_OLD_CONTROL);
+	runold = ioread8(mod->dpm + TARGET_RUNNING);
+
+	/* reset the module */
+	iowrite8(val, &mod->ctrl->reset_assert);
+	iowrite8(val, &mod->ctrl->reset_deassert);
+
+	/* wait until the module has finished resetting and is running */
+	start = jiffies;
+	do {
+		ican3_set_page(mod, QUEUE_OLD_CONTROL);
+		runnew = ioread8(mod->dpm + TARGET_RUNNING);
+		if (runnew == (runold ^ 0xff))
+			return 0;
+
+		msleep(10);
+	} while (time_before(jiffies, start + HZ / 4));
+
+	dev_err(mod->dev, "failed to reset CAN module\n");
+	return -ETIMEDOUT;
+}
+
+static void __devexit ican3_shutdown_module(struct ican3_dev *mod)
+{
+	ican3_msg_disconnect(mod);
+	ican3_reset_module(mod);
+}
+
+/*
+ * Startup an ICAN module, bringing it into fast mode
+ */
+static int __devinit ican3_startup_module(struct ican3_dev *mod)
+{
+	int ret;
+
+	ret = ican3_reset_module(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to reset module\n");
+		return ret;
+	}
+
+	/* re-enable interrupts so we can send messages */
+	iowrite8(1 << mod->num, &mod->ctrl->int_enable);
+
+	ret = ican3_msg_connect(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to connect to module\n");
+		return ret;
+	}
+
+	ican3_init_new_host_interface(mod);
+	ret = ican3_msg_newhostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to new-style interface\n");
+		return ret;
+	}
+
+	ret = ican3_set_termination(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to enable termination\n");
+		return ret;
+	}
+
+	ret = ican3_set_buserror(mod, ICAN3_BUSERR_QUOTA_MAX);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-error\n");
+		return ret;
+	}
+
+	ican3_init_fast_host_interface(mod);
+	ret = ican3_msg_fasthostif(mod);
+	if (ret) {
+		dev_err(mod->dev, "unable to switch to fast host interface\n");
+		return ret;
+	}
+
+	ret = ican3_set_id_filter(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set acceptance filter\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+/*
+ * CAN Network Device
+ */
+
+static int ican3_open(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	/* open the CAN layer */
+	ret = open_candev(ndev);
+	if (ret) {
+		dev_err(mod->dev, "unable to start CAN layer\n");
+		return ret;
+	}
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		close_candev(ndev);
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+	netif_start_queue(ndev);
+
+	return 0;
+}
+
+static int ican3_stop(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	/* stop the network device xmit routine */
+	netif_stop_queue(ndev);
+	mod->can.state = CAN_STATE_STOPPED;
+
+	/* bring the bus offline, stop receiving packets */
+	ret = ican3_set_bus_state(mod, false);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-off\n");
+		return ret;
+	}
+
+	/* close the CAN layer */
+	close_candev(ndev);
+	return 0;
+}
+
+static int ican3_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct net_device_stats *stats = &ndev->stats;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct ican3_fast_desc desc;
+	void __iomem *desc_addr;
+	unsigned long flags;
+
+	spin_lock_irqsave(&mod->lock, flags);
+
+	/* check that we can actually transmit */
+	if (!ican3_txok(mod)) {
+		dev_err(mod->dev, "no free descriptors, stopping queue\n");
+		netif_stop_queue(ndev);
+		spin_unlock_irqrestore(&mod->lock, flags);
+		return NETDEV_TX_BUSY;
+	}
+
+	/* copy the control bits of the descriptor */
+	ican3_set_page(mod, mod->fasttx_start + (mod->fasttx_num / 16));
+	desc_addr = mod->dpm + ((mod->fasttx_num % 16) * sizeof(desc));
+	memset(&desc, 0, sizeof(desc));
+	memcpy_fromio(&desc, desc_addr, 1);
+
+	/* convert the Linux CAN frame into ICAN3 format */
+	can_frame_to_ican3(mod, cf, &desc);
+
+	/*
+	 * the programming manual says that you must set the IVALID bit, then
+	 * interrupt, then set the valid bit. Quite weird, but it seems to be
+	 * required for this to work
+	 */
+	desc.control |= DESC_IVALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* generate a MODULbus interrupt to the microcontroller */
+	iowrite8(0x01, &mod->dpmctrl->interrupt);
+
+	desc.control ^= DESC_VALID;
+	memcpy_toio(desc_addr, &desc, sizeof(desc));
+
+	/* update the next buffer pointer */
+	mod->fasttx_num = (desc.control & DESC_WRAP) ? 0
+						     : (mod->fasttx_num + 1);
+
+	/* update statistics */
+	stats->tx_packets++;
+	stats->tx_bytes += cf->can_dlc;
+	kfree_skb(skb);
+
+	/*
+	 * This hardware doesn't have TX-done notifications, so we'll try and
+	 * emulate it the best we can using ECHO skbs. Get the next TX
+	 * descriptor, and see if we have room to send. If not, stop the queue.
+	 * It will be woken when the ECHO skb for the current packet is recv'd.
+	 */
+
+	/* copy the control bits of the descriptor */
+	if (!ican3_txok(mod))
+		netif_stop_queue(ndev);
+
+	spin_unlock_irqrestore(&mod->lock, flags);
+	return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ican3_netdev_ops = {
+	.ndo_open	= ican3_open,
+	.ndo_stop	= ican3_stop,
+	.ndo_start_xmit	= ican3_xmit,
+};
+
+/*
+ * Low-level CAN Device
+ */
+
+/* This structure was stolen from drivers/net/can/sja1000/sja1000.c */
+static struct can_bittiming_const ican3_bittiming_const = {
+	.name = DRV_NAME,
+	.tseg1_min = 1,
+	.tseg1_max = 16,
+	.tseg2_min = 1,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 64,
+	.brp_inc = 1,
+};
+
+/*
+ * This routine was stolen from drivers/net/can/sja1000/sja1000.c
+ *
+ * The bittiming register command for the ICAN3 just sets the bit timing
+ * registers on the SJA1000 chip directly
+ */
+static int ican3_set_bittiming(struct net_device *ndev)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	struct can_bittiming *bt = &mod->can.bittiming;
+	struct ican3_msg msg;
+	u8 btr0, btr1;
+
+	btr0 = ((bt->brp - 1) & 0x3f) | (((bt->sjw - 1) & 0x3) << 6);
+	btr1 = ((bt->prop_seg + bt->phase_seg1 - 1) & 0xf) |
+		(((bt->phase_seg2 - 1) & 0x7) << 4);
+	if (mod->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES)
+		btr1 |= 0x80;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.spec = MSG_CBTRREQ;
+	msg.len = cpu_to_le16(4);
+	msg.data[0] = 0x00;
+	msg.data[1] = 0x00;
+	msg.data[2] = btr0;
+	msg.data[3] = btr1;
+
+	return ican3_send_msg(mod, &msg);
+}
+
+static int ican3_set_mode(struct net_device *ndev, enum can_mode mode)
+{
+	struct ican3_dev *mod = netdev_priv(ndev);
+	int ret;
+
+	if (mode != CAN_MODE_START)
+		return -ENOTSUPP;
+
+	/* bring the bus online */
+	ret = ican3_set_bus_state(mod, true);
+	if (ret) {
+		dev_err(mod->dev, "unable to set bus-on\n");
+		return ret;
+	}
+
+	/* start up the network device */
+	mod->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	if (netif_queue_stopped(ndev))
+		netif_wake_queue(ndev);
+
+	return 0;
+}
+
+/*
+ * PCI Subsystem
+ */
+
+static int __devinit ican3_probe(struct platform_device *pdev)
+{
+	struct janz_platform_data *pdata;
+	struct net_device *ndev;
+	struct ican3_dev *mod;
+	struct resource *res;
+	struct device *dev;
+	int ret;
+
+	pdata = pdev->dev.platform_data;
+	if (!pdata)
+		return -ENXIO;
+
+	dev_dbg(&pdev->dev, "probe: module number %d\n", pdata->modno);
+
+	/* save the struct device for printing */
+	dev = &pdev->dev;
+
+	/* allocate the CAN device and private data */
+	ndev = alloc_candev(sizeof(*mod), 0);
+	if (!ndev) {
+		dev_err(dev, "unable to allocate CANdev\n");
+		ret = -ENOMEM;
+		goto out_return;
+	}
+
+	platform_set_drvdata(pdev, ndev);
+	mod = netdev_priv(ndev);
+	mod->ndev = ndev;
+	mod->dev = &pdev->dev;
+	mod->num = pdata->modno;
+	netif_napi_add(ndev, &mod->napi, ican3_napi, ICAN3_RX_BUFFERS);
+	spin_lock_init(&mod->lock);
+
+	/* the first unallocated page in the DPM is 9 */
+	mod->free_page = DPM_FREE_START;
+
+	ndev->netdev_ops = &ican3_netdev_ops;
+	ndev->flags |= IFF_ECHO;
+	SET_NETDEV_DEV(ndev, &pdev->dev);
+
+	mod->can.clock.freq = 8000000;
+	mod->can.bittiming_const = &ican3_bittiming_const;
+	mod->can.do_set_bittiming = ican3_set_bittiming;
+	mod->can.do_set_mode = ican3_set_mode;
+
+	/* find our IRQ number */
+	mod->irq = platform_get_irq(pdev, 0);
+	if (mod->irq < 0) {
+		dev_err(dev, "IRQ line not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	ndev->irq = mod->irq;
+
+	/* get access to the MODULbus registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "MODULbus registers not found\n");
+		ret = -ENODEV;
+		goto out_free_ndev;
+	}
+
+	mod->dpm = ioremap(res->start, resource_size(res));
+	if (!mod->dpm) {
+		dev_err(dev, "MODULbus registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_free_ndev;
+	}
+
+	mod->dpmctrl = mod->dpm + DPM_PAGE_SIZE;
+
+	/* get access to the control registers for this module */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		dev_err(dev, "CONTROL registers not found\n");
+		ret = -ENODEV;
+		goto out_iounmap_dpm;
+	}
+
+	mod->ctrl = ioremap(res->start, resource_size(res));
+	if (!mod->ctrl) {
+		dev_err(dev, "CONTROL registers not ioremap\n");
+		ret = -ENOMEM;
+		goto out_iounmap_dpm;
+	}
+
+	/* disable our IRQ, then hookup the IRQ handler */
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	ret = request_irq(mod->irq, ican3_irq, IRQF_SHARED, DRV_NAME, mod);
+	if (ret) {
+		dev_err(dev, "unable to request IRQ\n");
+		goto out_iounmap_ctrl;
+	}
+
+	/* reset and initialize the CAN controller into fast mode */
+	napi_enable(&mod->napi);
+	ret = ican3_startup_module(mod);
+	if (ret) {
+		dev_err(dev, "%s: unable to start CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	/* register with the Linux CAN layer */
+	ret = register_candev(ndev);
+	if (ret) {
+		dev_err(dev, "%s: unable to register CANdev\n", __func__);
+		goto out_free_irq;
+	}
+
+	dev_info(dev, "module %d: registered CAN device\n", pdata->modno);
+	return 0;
+
+out_free_irq:
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+out_iounmap_ctrl:
+	iounmap(mod->ctrl);
+out_iounmap_dpm:
+	iounmap(mod->dpm);
+out_free_ndev:
+	free_candev(ndev);
+out_return:
+	return ret;
+}
+
+static int __devexit ican3_remove(struct platform_device *pdev)
+{
+	struct net_device *ndev = platform_get_drvdata(pdev);
+	struct ican3_dev *mod = netdev_priv(ndev);
+
+	/* unregister the netdevice, stop interrupts */
+	unregister_netdev(ndev);
+	napi_disable(&mod->napi);
+	iowrite8(1 << mod->num, &mod->ctrl->int_disable);
+	free_irq(mod->irq, mod);
+
+	/* put the module into reset */
+	ican3_shutdown_module(mod);
+
+	/* unmap all registers */
+	iounmap(mod->ctrl);
+	iounmap(mod->dpm);
+
+	free_candev(ndev);
+
+	return 0;
+}
+
+static struct platform_driver ican3_driver = {
+	.driver		= {
+		.name	= DRV_NAME,
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ican3_probe,
+	.remove		= __devexit_p(ican3_remove),
+};
+
+static int __init ican3_init(void)
+{
+	return platform_driver_register(&ican3_driver);
+}
+
+static void __exit ican3_exit(void)
+{
+	platform_driver_unregister(&ican3_driver);
+}
+
+MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
+MODULE_DESCRIPTION("Janz MODULbus VMOD-ICAN3 Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:janz-ican3");
+
+module_init(ican3_init);
+module_exit(ican3_exit);
-- 
1.5.4.3


^ permalink raw reply related	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2010-04-02  0:43 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-29 16:58 [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module Ira W. Snyder
2010-03-29 16:58 ` Ira W. Snyder
2010-03-30  8:14 ` Wolfgang Grandegger
2010-03-30  8:14   ` Wolfgang Grandegger
2010-03-31  6:46   ` David Miller
2010-04-01 20:03 ` Andrew Morton
2010-04-02  0:43   ` Ira W. Snyder
2010-04-02  0:43     ` Ira W. Snyder
  -- strict thread matches above, loose matches on Subject: below --
2010-03-18 16:38 Ira W. Snyder
2010-03-19  9:01 ` Wolfgang Grandegger
2010-03-19  9:01   ` Wolfgang Grandegger
2010-03-19 15:19   ` Ira W. Snyder
2010-03-19 15:19     ` Ira W. Snyder
2010-03-19 15:45     ` Wolfgang Grandegger
2010-03-19 20:03       ` Ira W. Snyder
2010-03-19 20:13         ` Wolfgang Grandegger
2010-03-19 20:13           ` Wolfgang Grandegger
2010-03-19 21:52           ` Ira W. Snyder
2010-03-20  7:55             ` Wolfgang Grandegger
2010-03-20  7:55               ` Wolfgang Grandegger
2010-03-22 15:53               ` Ira W. Snyder
2010-03-22 15:53                 ` Ira W. Snyder
2010-03-22 19:17                 ` Wolfgang Grandegger
2010-03-22 19:17                   ` Wolfgang Grandegger
2010-03-22 19:23                   ` Wolfgang Grandegger
2010-03-22 19:23                     ` Wolfgang Grandegger
2010-03-22 20:12                     ` Ira W. Snyder
2010-03-22 20:12                       ` Ira W. Snyder
2010-03-22 20:10                   ` Ira W. Snyder
2010-03-22 20:10                     ` Ira W. Snyder
2010-03-22 20:28                     ` Wolfgang Grandegger
2010-03-22 20:28                       ` Wolfgang Grandegger
2010-03-22 20:51                       ` Ira W. Snyder
2010-03-22 20:51                         ` Ira W. Snyder
2010-03-22 21:24                         ` Wolfgang Grandegger
2010-03-22 21:24                           ` Wolfgang Grandegger
2010-03-02 21:22 [PATCH 0/3 RFCv4] add support for Janz MODULbus devices Ira W. Snyder
2010-03-02 21:22 ` [PATCH 2/3] can: add support for Janz VMOD-ICAN3 Intelligent CAN module Ira W. Snyder
2010-03-17 19:33   ` Ira W. Snyder
2010-03-18  8:36     ` Wolfgang Grandegger
2010-03-18 15:19       ` Ira W. Snyder
2010-03-18 16:06         ` Wolfgang Grandegger

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.