linux-can.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
@ 2014-10-13 11:23 Thomas Körper
  2014-10-20 16:26 ` Oliver Hartkopp
  0 siblings, 1 reply; 6+ messages in thread
From: Thomas Körper @ 2014-10-13 11:23 UTC (permalink / raw)
  To: linux-can; +Cc: Thomas Körper

Some infos: Hardware: Card based on our own CAN controller ("esdACC"). Bus master DMA and MSI / Different configurations with up to 4 can cores / CAN termination jumpers on board.
Driver: "core" refers to a net/controller specific part, overview ("ov") to the general card. DMA transfer (Rx direction) is done via bus master messages ("bm_" prefix)

Signed-off-by: Thomas Körper <Thomas.Koerper@esd.eu>
---
 drivers/net/can/Kconfig      |    2 +
 drivers/net/can/Makefile     |    1 +
 drivers/net/can/esd/Kconfig  |    6 +
 drivers/net/can/esd/Makefile |    4 +
 drivers/net/can/esd/esd402.c | 1115 ++++++++++++++++++++++++++++++++++++++++++
 drivers/net/can/esd/esd402.h |  332 +++++++++++++
 6 files changed, 1460 insertions(+)
 create mode 100644 drivers/net/can/esd/Kconfig
 create mode 100644 drivers/net/can/esd/Makefile
 create mode 100644 drivers/net/can/esd/esd402.c
 create mode 100644 drivers/net/can/esd/esd402.h

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 4168822..077ee72 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -151,6 +151,8 @@ source "drivers/net/can/usb/Kconfig"
 
 source "drivers/net/can/softing/Kconfig"
 
+source "drivers/net/can/esd/Kconfig"
+
 endif
 
 config CAN_DEBUG_DEVICES
diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
index 1697f22..5b8681e 100644
--- a/drivers/net/can/Makefile
+++ b/drivers/net/can/Makefile
@@ -13,6 +13,7 @@ can-dev-$(CONFIG_CAN_LEDS)	+= led.o
 obj-y				+= spi/
 obj-y				+= usb/
 obj-y				+= softing/
+obj-y				+= esd/
 
 obj-$(CONFIG_CAN_SJA1000)	+= sja1000/
 obj-$(CONFIG_CAN_MSCAN)		+= mscan/
diff --git a/drivers/net/can/esd/Kconfig b/drivers/net/can/esd/Kconfig
new file mode 100644
index 0000000..faefced
--- /dev/null
+++ b/drivers/net/can/esd/Kconfig
@@ -0,0 +1,6 @@
+config CAN_ESD_402
+	tristate "esd gmbh CAN PCIe/402 support"
+	default y
+	depends on PCI && HAS_DMA
+	---help---
+	  Support for CAN PCIe/402 cards from esd electronic system design gmbh
diff --git a/drivers/net/can/esd/Makefile b/drivers/net/can/esd/Makefile
new file mode 100644
index 0000000..211b64d
--- /dev/null
+++ b/drivers/net/can/esd/Makefile
@@ -0,0 +1,4 @@
+
+obj-m += esd402.o
+
+ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG
diff --git a/drivers/net/can/esd/esd402.c b/drivers/net/can/esd/esd402.c
new file mode 100644
index 0000000..54e55d1
--- /dev/null
+++ b/drivers/net/can/esd/esd402.c
@@ -0,0 +1,1115 @@
+/* Copyright (C) 2014 esd electronic system design gmbh
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/dma-mapping.h>
+#include <linux/can.h>
+#include <linux/can/dev.h>
+
+#include "esd402.h"
+
+#define DRV_NAME			"esd402_driver"
+
+#define ESD_CAN_EFF_FLAG		0x20000000
+#define ESD_CAN_RTR_FLAG		0x10
+#define ESD_CAN_DLC_MASK		0x0f
+
+static inline u32 ov_read32(struct esd_pci402_card *card, unsigned short offs)
+{
+	return ioread32be(card->addr_overview + offs);
+}
+
+static inline void ov_write32(struct esd_pci402_card *card,
+			      unsigned short offs, u32 v)
+{
+	iowrite32be(v, card->addr_overview + offs);
+}
+
+static void ov_set_bits(struct esd_pci402_card *card,
+			unsigned short offs, u32 b)
+{
+	u32 v = ov_read32(card, offs);
+
+	v |= b;
+	ov_write32(card, offs, v);
+}
+
+static void ov_clear_bits(struct esd_pci402_card *card,
+			  unsigned short offs, u32 b)
+{
+	u32 v = ov_read32(card, offs);
+
+	v &= ~b;
+	ov_write32(card, offs, v);
+}
+
+static ktime_t ov_ts_to_ktime(struct esd_pci402_card *card, u64 ts)
+{
+	unsigned long long ns;
+	unsigned int rem;
+
+	rem = do_div(ts, card->timestamp_frequency);
+	ns = ts * NSEC_PER_SEC;
+
+	ts = (unsigned long long)rem * NSEC_PER_SEC;
+	do_div(ts, card->timestamp_frequency);
+	ns += ts;
+
+	return ns_to_ktime(ns);
+}
+
+static inline u32 core_read32(struct esd_pci402_cancore *core,
+			      unsigned short offs)
+{
+	return ioread32be(core->addr + offs);
+}
+
+static inline void core_write32(struct esd_pci402_cancore *core,
+				unsigned short offs, u32 v)
+{
+	iowrite32be(v, core->addr + offs);
+}
+
+static inline void core_write32_noswap(struct esd_pci402_cancore *core,
+				       unsigned short offs, u32 v)
+{
+	iowrite32(v, core->addr + offs);
+}
+
+static void core_set_bits(struct esd_pci402_cancore *core,
+			  unsigned short offs, u32 mask)
+{
+	u32 v = core_read32(core, offs);
+
+	v |= mask;
+	core_write32(core, offs, v);
+}
+
+static void core_clear_bits(struct esd_pci402_cancore *core,
+			    unsigned short offs, u32 mask)
+{
+	u32 v = core_read32(core, offs);
+
+	v &= ~mask;
+	core_write32(core, offs, v);
+}
+
+static int core_resetmode_entered(struct esd_pci402_cancore *core)
+{
+	u32 ctrl = core_read32(core, OFFS_CORE_CTRL_MODE);
+
+	return (ctrl & REG_CONTROL_MASK_MODE_RESETMODE) != 0;
+}
+
+static void core_resetmode_enter(struct esd_pci402_cancore *core)
+{
+	int i;
+
+	core_set_bits(core, OFFS_CORE_CTRL_MODE,
+		      REG_CONTROL_MASK_MODE_RESETMODE);
+
+	for (i = 0; i < 10; ++i) {
+		if (core_resetmode_entered(core))
+			return;
+
+		udelay(5);
+	}
+
+	netdev_warn(core->net_dev, "Entering reset mode timed out\n");
+}
+
+static void core_resetmode_leave(struct esd_pci402_cancore *core)
+{
+	int i;
+
+	core_clear_bits(core, OFFS_CORE_CTRL_MODE,
+			REG_CONTROL_MASK_MODE_RESETMODE);
+
+	for (i = 0; i < 10; ++i) {
+		if (!core_resetmode_entered(core))
+			return;
+
+		udelay(5);
+	}
+
+	netdev_warn(core->net_dev, "Leaving reset mode timed out\n");
+}
+
+static int core_txq_isready(struct esd_pci402_cancore *core)
+{
+	u8 fifo_level = (u8)(core_read32(core, OFFS_CORE_TXFIFO_STATUS) >> 16);
+
+	return (fifo_level < core->tx_fifo_size - 1);
+}
+
+static void core_txq_put(struct esd_pci402_cancore *core, u32 esd_id,
+			 u8 esd_len, const void *data)
+{
+	core_write32_noswap(core, OFFS_CORE_TXFIFO_DATA_0,
+			    *((const u32 *)data));
+	core_write32_noswap(core, OFFS_CORE_TXFIFO_DATA_1,
+			    *((const u32 *)(data + 4)));
+	core_write32(core, OFFS_CORE_TXFIFO_DLC, esd_len);
+	/* CAN id must be written at last: */
+	core_write32(core, OFFS_CORE_TXFIFO_ID, esd_id);
+}
+
+static void core_handlemsg_rxtxdone(struct esd_pci402_cancore *core,
+				    const struct bm_msg_rxtxdone *msg)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(core->net_dev);
+	struct net_device_stats *stats = &core->net_dev->stats;
+
+	if (msg->dlc.rxtx.len & ESD_PCI402_BM_LENFLAG_TX) {
+		if (core->tx_fifo_head == core->tx_fifo_tail) {
+			netdev_warn(core->net_dev,
+				    "TX interrupt, but queue is empty!?\n");
+			return;
+		}
+		stats->tx_packets++;
+		stats->tx_bytes +=
+				get_can_dlc(msg->dlc.tx.len & ESD_CAN_DLC_MASK);
+
+		can_get_echo_skb(core->net_dev, core->tx_fifo_tail);
+		core->tx_fifo_tail++;
+		if (core->tx_fifo_tail >= core->tx_fifo_size)
+			core->tx_fifo_tail = 0;
+		netif_wake_queue(core->net_dev);
+
+	} else {
+		struct skb_shared_hwtstamps *skb_ts;
+		struct can_frame *cf;
+		struct sk_buff *skb;
+
+		skb = alloc_can_skb(core->net_dev, &cf);
+		if (skb == NULL) {
+			stats->rx_dropped++;
+			return;
+		}
+
+		cf->can_id = msg->id & CAN_EFF_MASK;
+		if (msg->id & ESD_CAN_EFF_FLAG)
+			cf->can_id |= CAN_EFF_FLAG;
+
+		cf->can_dlc = get_can_dlc(msg->dlc.rx.len);
+		if (msg->dlc.rx.len & ESD_CAN_RTR_FLAG)
+			cf->can_id |= CAN_RTR_FLAG;
+		else
+			memcpy(cf->data, msg->data, cf->can_dlc);
+
+		skb_ts = skb_hwtstamps(skb);
+		skb_ts->hwtstamp = ov_ts_to_ktime(priv->card, msg->timestamp);
+		netif_rx(skb);
+
+		stats->rx_packets++;
+		stats->rx_bytes += cf->can_dlc;
+	}
+}
+
+static void core_handlemsg_txabort(struct esd_pci402_cancore *core,
+				   const struct bm_msg_txabort *msg)
+{
+	struct net_device_stats *stats = &core->net_dev->stats;
+	int i;
+
+	/* abort_mask signals which frames were aborted in card's fifo */
+	for (i = 0; i < sizeof(msg->abort_mask) * BITS_PER_BYTE; ++i) {
+		if (!(msg->abort_mask & (1 << i)))
+			continue;
+
+		if (core->tx_fifo_head == core->tx_fifo_tail) {
+			netdev_warn(core->net_dev,
+				    "TX Err interrupt, but queue is empty!?\n");
+			break;
+		}
+		stats->tx_errors++;
+
+		can_free_echo_skb(core->net_dev, core->tx_fifo_tail);
+		core->tx_fifo_tail++;
+		if (core->tx_fifo_tail >= core->tx_fifo_size)
+			core->tx_fifo_tail = 0;
+	}
+
+	if (!core_resetmode_entered(core))
+		netif_wake_queue(core->net_dev);
+}
+
+static void core_handlemsg_overrun(struct esd_pci402_cancore *core,
+				   const struct bm_msg_overrun *msg)
+{
+	struct net_device_stats *stats = &core->net_dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	skb = alloc_can_err_skb(core->net_dev, &cf);
+	if (skb == NULL)
+		return;
+
+	/* lost_cnt may be 0 if not supported by FPGA version */
+	if (msg->lost_cnt) {
+		stats->rx_dropped += msg->lost_cnt;
+		stats->rx_over_errors +=  msg->lost_cnt;
+	} else {
+		stats->rx_dropped++;
+		stats->rx_over_errors++;
+	}
+
+	cf->can_id |= CAN_ERR_CRTL;
+	cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
+
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+}
+
+static void core_handlemsg_buserr(struct esd_pci402_cancore *core,
+				  const struct bm_msg_buserr *msg)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(core->net_dev);
+	struct net_device_stats *stats = &core->net_dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+
+	priv->can.can_stats.bus_error++;
+	stats->rx_errors++;
+
+	skb = alloc_can_err_skb(core->net_dev, &cf);
+	if (skb == NULL)
+		return;
+
+	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+	/* msg->ecc acts like SJA1000's ECC register */
+	switch (msg->ecc & ESD_PCI402_ECC_MASK) {
+	case ESD_PCI402_ECC_BIT:
+		cf->data[2] |= CAN_ERR_PROT_BIT;
+		break;
+	case ESD_PCI402_ECC_FORM:
+		cf->data[2] |= CAN_ERR_PROT_FORM;
+		break;
+	case ESD_PCI402_ECC_STUFF:
+		cf->data[2] |= CAN_ERR_PROT_STUFF;
+		break;
+	default:
+		cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+		cf->data[3] = msg->ecc & ESD_PCI402_ECC_SEG;
+		break;
+	}
+
+	if ((msg->ecc & ESD_PCI402_ECC_DIR) == 0)
+		cf->data[2] |= CAN_ERR_PROT_TX;
+
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+}
+
+static void
+core_handlemsg_errstatechange(struct esd_pci402_cancore *core,
+			      const struct bm_msg_errstatechange *msg)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(core->net_dev);
+	struct net_device_stats *stats = &core->net_dev->stats;
+	struct can_frame *cf;
+	struct sk_buff *skb;
+	int is_busoff;
+	int is_passive;
+	int is_warning;
+	u8 txerr;
+	u8 rxerr;
+
+	txerr = (u8)(msg->reg_status >> 8);
+	rxerr = (u8)msg->reg_status;
+	is_warning = (msg->reg_status & REG_STATUS_MASK_STATUS_ES) != 0;
+	is_passive = (msg->reg_status & REG_STATUS_MASK_STATUS_EP) != 0;
+	is_busoff = (msg->reg_status & REG_STATUS_MASK_STATUS_BS) != 0;
+
+	skb = alloc_can_err_skb(core->net_dev, &cf);
+	if (skb) {
+		if (is_busoff) {
+			priv->can.state = CAN_STATE_BUS_OFF;
+			/* bus-offs counted by can_bus_off() */
+			cf->can_id |= CAN_ERR_BUSOFF;
+		} else if (is_passive) {
+			priv->can.state = CAN_STATE_ERROR_PASSIVE;
+			priv->can.can_stats.error_passive++;
+			cf->data[1] = (txerr > rxerr) ? CAN_ERR_CRTL_TX_PASSIVE
+					: CAN_ERR_CRTL_RX_PASSIVE;
+			cf->can_id |= CAN_ERR_CRTL;
+			cf->data[6] = txerr;
+			cf->data[7] = rxerr;
+		} else if (is_warning) {
+			priv->can.state = CAN_STATE_ERROR_WARNING;
+			priv->can.can_stats.error_warning++;
+			cf->data[1] = (txerr > rxerr) ? CAN_ERR_CRTL_TX_WARNING
+					: CAN_ERR_CRTL_RX_WARNING;
+			cf->can_id |= CAN_ERR_CRTL;
+			cf->data[6] = txerr;
+			cf->data[7] = rxerr;
+		} else {
+			priv->can.state = CAN_STATE_ERROR_ACTIVE;
+			/* restarts counted in dev.c */
+		}
+
+		netif_rx(skb);
+		stats->rx_packets++;
+		stats->rx_bytes += cf->can_dlc;
+	}
+
+	if (is_busoff) {
+		core_write32(core, OFFS_CORE_TX_ABORT_MASK, 0xffff);
+		can_bus_off(core->net_dev);
+	}
+}
+
+static void core_handle_interrupt(struct esd_pci402_cancore *core)
+{
+	u32 msg_fifo_head = core->irq_cnt & 0xff;
+
+	while (core->msg_fifo_tail != msg_fifo_head) {
+		const struct bm_msg *msg = &core->bm_fifo[core->msg_fifo_tail];
+
+		switch (msg->u.msg_id) {
+		case BM_MSG_ID_RXTXDONE:
+			core_handlemsg_rxtxdone(core, &msg->u.rxtxdone);
+			break;
+
+		case BM_MSG_ID_TXABORT:
+			core_handlemsg_txabort(core, &msg->u.txabort);
+			break;
+
+		case BM_MSG_ID_OVERRUN:
+			core_handlemsg_overrun(core, &msg->u.overrun);
+			break;
+
+		case BM_MSG_ID_BUSERR:
+			core_handlemsg_buserr(core, &msg->u.buserr);
+			break;
+
+		case BM_MSG_ID_ERRPASSIVE:
+		case BM_MSG_ID_ERRWARN:
+			core_handlemsg_errstatechange(core,
+						      &msg->u.errstatechange);
+			break;
+
+		default:
+			break;
+		}
+
+		core->msg_fifo_tail++;
+		if (core->msg_fifo_tail >= ESD_PCI402_DMA_FIFO_ITEMCOUNT)
+			core->msg_fifo_tail = 0;
+	}
+}
+
+static irqreturn_t esd_pci402_interrupt(int irq, void *dev_id)
+{
+	struct esd_pci402_card *card;
+	u32 irqmask;
+	int i;
+
+	card = pci_get_drvdata((struct pci_dev *)dev_id);
+
+	/* First we look for whom interrupts are pending, card/overview
+	 * or any of the cores. Two bits in irqmask are used for each;
+	 * set to BM_IRQ_MASK then:
+	 */
+	irqmask = 0;
+	if (*card->bm_irq_cnt != card->irq_cnt) {
+		irqmask |= BM_IRQ_MASK;
+		card->irq_cnt = *card->bm_irq_cnt;
+	}
+
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+
+		if (*core->bm_irq_cnt != core->irq_cnt) {
+			irqmask |= (BM_IRQ_MASK << (2 * (i + 1)));
+			core->irq_cnt = *core->bm_irq_cnt;
+		}
+	}
+
+	if (!irqmask)
+		return IRQ_NONE;
+
+	/* At second we tell the card we're working on them by writing irqmask,
+	 * call esd_pci402_interrupt_{ov|core} and then acknowledge the
+	 * interrupts by writing irq_cnt:
+	 */
+	ov_write32(card, OFFS_OV_BM_IRQ_MASK, irqmask);
+
+	if (irqmask & BM_IRQ_MASK) {
+		/* ov_handle_interrupt(card); - no use yet. */
+		ov_write32(card, OFFS_OV_BM_IRQ_COUNTER, card->irq_cnt);
+	}
+
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+
+		if (irqmask & (BM_IRQ_MASK << (2 * (i + 1)))) {
+			core_handle_interrupt(core);
+			core_write32(core, OFFS_OV_BM_IRQ_COUNTER,
+				     core->irq_cnt);
+		}
+	}
+
+	ov_write32(card, OFFS_OV_BM_IRQ_MASK, BM_IRQ_UNMASK_ALL);
+
+	return IRQ_HANDLED;
+}
+
+static int esd_pci402_open(struct net_device *netdev)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
+	u32 ctrl_mode;
+	int err;
+
+	err = open_candev(netdev);
+	if (err)
+		return err;
+
+	ctrl_mode = REG_CONTROL_MASK_IE_RXTX |
+			REG_CONTROL_MASK_IE_TXERROR |
+			REG_CONTROL_MASK_IE_ERRPASS |
+			REG_CONTROL_MASK_IE_OVERRUN |
+			REG_CONTROL_MASK_IE_ERROR;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		ctrl_mode |= REG_CONTROL_MASK_IE_BUSERR;
+
+	core_set_bits(priv->core, OFFS_CORE_CTRL_MODE, ctrl_mode);
+
+	netif_start_queue(netdev);
+	return 0;
+}
+
+static int esd_pci402_close(struct net_device *netdev)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
+
+	core_clear_bits(priv->core, OFFS_CORE_CTRL_MODE,
+			REG_CONTROL_MASK_IE_RXTX |
+			REG_CONTROL_MASK_IE_TXERROR |
+			REG_CONTROL_MASK_IE_OVERRUN);
+
+	netif_stop_queue(netdev);
+	priv->can.state = CAN_STATE_STOPPED;
+
+	close_candev(netdev);
+	return 0;
+}
+
+static netdev_tx_t esd_pci402_start_xmit(struct sk_buff *skb,
+					 struct net_device *netdev)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	struct esd_pci402_cancore *core = priv->core;
+	u8 new_fifo_head = (core->tx_fifo_head + 1) % core->tx_fifo_size;
+	u32 esd_id;
+	u8 esd_len;
+
+	if ((new_fifo_head == core->tx_fifo_tail) || !core_txq_isready(core)) {
+		netif_stop_queue(netdev);
+		return NETDEV_TX_BUSY;
+	}
+
+	esd_len = can_dlc2len(cf->can_dlc);
+	if (cf->can_id & CAN_RTR_FLAG)
+		esd_len |= ESD_CAN_RTR_FLAG;
+
+	if (cf->can_id & CAN_EFF_FLAG) {
+		esd_id = cf->can_id & CAN_EFF_MASK;
+		esd_id |= ESD_CAN_EFF_FLAG;
+	} else {
+		esd_id = cf->can_id & CAN_SFF_MASK;
+	}
+
+	can_put_echo_skb(skb, netdev, core->tx_fifo_head);
+	core->tx_fifo_head = new_fifo_head;
+
+	core_txq_put(core, esd_id, esd_len, cf->data);
+
+	return NETDEV_TX_OK;
+}
+
+static int esd_pci402_set_bittiming(struct net_device *netdev)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	u32 btr;
+	u8 brp;
+
+	brp = (u8)(bt->brp - 1);
+	core_resetmode_enter(priv->core);
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+		core_set_bits(priv->core, OFFS_CORE_CTRL_MODE,
+			      REG_CONTROL_MASK_MODE_LOM);
+	else
+		core_clear_bits(priv->core, OFFS_CORE_CTRL_MODE,
+				REG_CONTROL_MASK_MODE_LOM);
+
+	if (priv->card->version < ESD_PCI402_VERSION_NEWBTR) {
+		btr = brp;
+
+		/* TSEG1: Bit12..15 */
+		btr |= ((bt->phase_seg1 + bt->prop_seg - 1) & 0xf) << 12;
+		/* TSEG2: Bit20..22 */
+		btr |= ((bt->phase_seg2 - 1) & 0x7) << 20;
+		/* SJW: Bit25..26 */
+		btr |= ((bt->sjw - 1) & 0x3) << 25;
+
+		core_write32(priv->core, OFFS_CORE_BTR, btr);
+	} else {
+		/* TSEG1: Bit0..7 */
+		btr = (bt->phase_seg1 + bt->prop_seg - 1) & 0xff;
+		/* TSEG2: Bit16..22 */
+		btr |= ((bt->phase_seg2 - 1) & 0x7f) << 16;
+		/* SJW: Bit24..30 */
+		btr |= ((bt->sjw - 1) & 0x7f) << 24;
+
+		core_write32(priv->core, OFFS_CORE_BTR, brp);
+		core_write32(priv->core, OFFS_CORE_BTR_FD, btr);
+	}
+
+	core_resetmode_leave(priv->core);
+	priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	return 0;
+}
+
+static int esd_pci402_get_berr_counter(const struct net_device *netdev,
+				       struct can_berr_counter *bec)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
+	u32 core_status = core_read32(priv->core, OFFS_CORE_STATUS);
+
+	bec->txerr = (u8)(core_status >> 8);
+	bec->rxerr = (u8)core_status;
+
+	return 0;
+}
+
+static int esd_pci402_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
+
+	switch (mode) {
+	case CAN_MODE_START:
+		core_resetmode_leave(priv->core);
+		netif_wake_queue(netdev);
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+/* For cards with FPGA Version < ESD_PCI402_VERSION_NEWBTR */
+static const struct can_bittiming_const esd_pci402_bittiming_const_old = {
+	.name = "esd_pci402_old",
+	.tseg1_min = 1,
+	.tseg1_max = 16,
+	.tseg2_min = 1,
+	.tseg2_max = 8,
+	.sjw_max = 4,
+	.brp_min = 1,
+	.brp_max = 256,
+	.brp_inc = 1,
+};
+
+static const struct can_bittiming_const esd_pci402_bittiming_const = {
+	.name = "esd_pci402",
+	.tseg1_min = 1,
+	.tseg1_max = 256,
+	.tseg2_min = 1,
+	.tseg2_max = 128,
+	.sjw_max = 128,
+	.brp_min = 1,
+	.brp_max = 256,
+	.brp_inc = 1,
+};
+
+static const struct net_device_ops esd_pci402_netdev_ops = {
+	.ndo_open = esd_pci402_open,
+	.ndo_stop = esd_pci402_close,
+	.ndo_start_xmit = esd_pci402_start_xmit,
+	.ndo_change_mtu = can_change_mtu
+};
+
+static int dump_infos;
+module_param(dump_infos, int, S_IRUGO);
+MODULE_PARM_DESC(dump_infos, "Dump card infos while probing. Default: 0 (Off)");
+
+static void esd_pci402_reset_fpga(struct esd_pci402_card *card)
+{
+	ov_write32(card, OFFS_OV_MODE, REG_OV_MODE_MASK_FPGA_RESET);
+
+	/* Also reset I²C, to re-detect card addons at every driver start: */
+	ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_I2C_ENABLE);
+	mdelay(2);
+	ov_set_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_I2C_ENABLE);
+	mdelay(10);
+}
+
+static int esd_pci402_set_msiconfig(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	u32 addr_lo_offs = 0;
+	u32 addr_lo = 0;
+	u32 addr_hi = 0;
+	u32 data = 0;
+	u16 csr = 0;
+	int err;
+
+	err = pci_read_config_word(pdev, ESD_PCI402_PCICFG_MSICAP_CSR, &csr);
+	if (err)
+		goto failed;
+
+	err = pci_read_config_dword(pdev, ESD_PCI402_PCICFG_MSICAP_ADDR,
+				    &addr_lo);
+	if (err)
+		goto failed;
+
+	err = pci_read_config_dword(pdev, ESD_PCI402_PCICFG_MSICAP_ADDR + 4,
+				    &addr_hi);
+	if (err)
+		goto failed;
+
+	err = pci_read_config_dword(pdev, ESD_PCI402_PCICFG_MSICAP_DATA, &data);
+	if (err)
+		goto failed;
+
+	addr_lo_offs = addr_lo & 0x0000ffff;
+	addr_lo &= 0xffff0000;
+
+	if (addr_hi)
+		addr_lo |= 1; /* Enable 64-Bit addressing in address space */
+
+	if (!(csr & 0x0001)) /* Enable bit */
+		goto failed;
+
+	iowrite32(addr_lo, card->addr_pciep + OFFS_PCIEP_MSI_ADDR_LO);
+	iowrite32(addr_hi, card->addr_pciep + OFFS_PCIEP_MSI_ADDR_HI);
+	ov_write32(card, OFFS_OV_MSI_ADDRESSOFFSET, addr_lo_offs);
+	ov_write32(card, OFFS_OV_MSI_DATA, data);
+
+	return 0;
+
+failed:
+	dev_warn(&pdev->dev, "Error while setting MSI configuration:\n"
+		 "CSR: 0x%.4x, addr: 0x%.8x%.8x, data: 0x%.8x\n",
+		 csr, addr_hi, addr_lo, data);
+
+	return -1;
+}
+
+static int esd_pci402_init_card(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	u32 temp;
+
+	card->addr_overview = card->addr + ESD_PCI402_IO_OV_OFFS;
+	card->addr_pciep = card->addr + ESD_PCI402_IO_PCIEP_OFFS;
+
+	esd_pci402_reset_fpga(card);
+
+#ifdef __LITTLE_ENDIAN
+	ov_set_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_ENDIAN_LITTLE);
+	/* So card converts all busmastered data to LE for us */
+#endif
+
+	temp = ov_read32(card, OFFS_OV_VERSION);
+	card->features = (u16)(temp >> 16);
+	card->version = (u16)temp;
+
+	temp = ov_read32(card, OFFS_OV_INFO);
+	card->active_cores = (u8)(temp >> 8);
+	card->total_cores = (u8)temp;
+	if (card->active_cores > ESD_PCI402_MAX_CORES) {
+		dev_warn(&pdev->dev, "Card has more active cores than supported by driver, %u core(s) will be ignored\n",
+			 card->active_cores - ESD_PCI402_MAX_CORES);
+		card->active_cores = ESD_PCI402_MAX_CORES;
+	}
+
+	card->core_frequency = ov_read32(card, OFFS_OV_CANCORE_FREQ);
+	card->timestamp_frequency = ov_read32(card, OFFS_OV_TS_FREQ_LO);
+
+	if (dump_infos) {
+		dev_info(&pdev->dev, "\"dump_infos\" selected:");
+
+		temp = ov_read32(card, OFFS_OV_PROBE);
+		dev_info(&pdev->dev, "  Probe register: 0x%.8x\n", temp);
+		dev_info(&pdev->dev, "  Features: 0x%.4x\n", card->features);
+		dev_info(&pdev->dev, "  FPGA Version: 0x%.4x\n", card->version);
+		dev_info(&pdev->dev, "  Strappings: 0x%.4x\n",
+			 ov_read32(card, OFFS_OV_INFO) >> 16);
+		dev_info(&pdev->dev, "  Active cores: %u\n",
+			 card->active_cores);
+		dev_info(&pdev->dev, "  Total cores: %u\n",
+			 card->total_cores);
+		dev_info(&pdev->dev, "  Core frequency: %u\n",
+			 card->core_frequency);
+		dev_info(&pdev->dev, "  Timestamp frequency: %u\n",
+			 card->timestamp_frequency);
+	}
+
+	if (card->version < ESD_PCI402_VERSION_MIN) {
+		dev_err(&pdev->dev,
+			"FPGA version (0x%.4x) outdated, please update\n",
+			card->version);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int esd_pci402_init_interrupt(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	int err;
+
+	err = pci_enable_msi(pdev);
+	if (!err) {
+		err = esd_pci402_set_msiconfig(pdev);
+		if (!err) {
+			card->msi_enabled = 1;
+			ov_set_bits(card, OFFS_OV_MODE,
+				    REG_OV_MODE_MASK_MSI_ENABLE);
+			dev_info(&pdev->dev, "MSI enabled\n");
+		}
+	}
+
+	err = request_irq(pdev->irq, esd_pci402_interrupt,
+			  IRQF_SHARED, DRV_NAME, pdev);
+	if (err)
+		goto failure_msidis;
+
+	iowrite32(1, card->addr_pciep + OFFS_PCIEP_INT_ENABLE);
+
+	return 0;
+
+failure_msidis:
+	if (card->msi_enabled) {
+		ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_MSI_ENABLE);
+		pci_disable_msi(pdev);
+		card->msi_enabled = 0;
+	}
+
+	return err;
+}
+
+static void esd_pci402_finish_interrupt(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+
+	iowrite32(0, card->addr_pciep + OFFS_PCIEP_INT_ENABLE);
+	free_irq(pdev->irq, pdev);
+
+	if (card->msi_enabled) {
+		ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_MSI_ENABLE);
+		pci_disable_msi(pdev);
+		card->msi_enabled = 0;
+	}
+}
+
+static int esd_pci402_init_dma(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	int err;
+	int i;
+
+	err = pci_set_consistent_dma_mask(pdev, ESD_PCI402_DMA_MASK);
+	if (err) {
+		dev_err(&pdev->dev, "dma set mask failed!");
+		return err;
+	}
+
+	card->dma_buf = pci_alloc_consistent(pdev, ESD_PCI402_DMA_SIZE,
+					     &card->dma_hnd);
+	if (!card->dma_buf) {
+		dev_err(&pdev->dev, "dma alloc failed!");
+		return -ENOMEM;
+	}
+
+	/* Setting card/core "bm_" pointers according to this DMA buffer layout:
+	 * +-----------------------+
+	 * | FIFO Card/Overview    |
+	 * +-----------------------+
+	 * | FIFO Core0            |
+	 * +-----------------------+
+	 * | FIFO Core1            |
+	 * +-----------------------+
+	 * | ...                   |
+	 * +-----------------------+
+	 * | irq_cnt Card/Overview |
+	 * +-----------------------+
+	 * | irq_cnt Core0         |
+	 * +-----------------------+
+	 * | irq_cnt Core1         |
+	 * +-----------------------+
+	 * | ...                   |
+	 * +-----------------------+
+	 */
+	card->bm_fifo = card->dma_buf;
+	card->bm_irq_cnt = card->dma_buf + (card->total_cores + 1)
+						* ESD_PCI402_DMA_FIFOSIZE;
+
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+
+		core->bm_fifo = card->dma_buf + (i + 1)
+						* ESD_PCI402_DMA_FIFOSIZE;
+		core->bm_irq_cnt = card->bm_irq_cnt + 1 + i;
+	}
+
+	iowrite32((u32)card->dma_hnd,
+		  card->addr_pciep + OFFS_PCIEP_BM_ADDR_LO);
+	iowrite32((u32)(card->dma_hnd >> 32),
+		  card->addr_pciep + OFFS_PCIEP_BM_ADDR_HI);
+
+	ov_set_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_BM_ENABLE);
+
+	return 0;
+}
+
+static void esd_pci402_finish_dma(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	int i;
+
+	ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_BM_ENABLE);
+
+	iowrite32(0, card->addr_pciep + OFFS_PCIEP_BM_ADDR_LO);
+	iowrite32(0, card->addr_pciep + OFFS_PCIEP_BM_ADDR_HI);
+
+	card->bm_fifo = NULL;
+	card->bm_irq_cnt = NULL;
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+
+		core->bm_fifo = NULL;
+		core->bm_irq_cnt = NULL;
+	}
+
+	pci_free_consistent(pdev, ESD_PCI402_DMA_SIZE, card->dma_buf,
+			    card->dma_hnd);
+	card->dma_buf = NULL;
+}
+
+static int esd_pci402_init_cores(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	const struct can_bittiming_const *bittiming_const;
+	int num_register_ok = 0;
+	int err;
+	int i;
+
+	if (card->version < ESD_PCI402_VERSION_NEWBTR)
+		bittiming_const = &esd_pci402_bittiming_const_old;
+	else
+		bittiming_const = &esd_pci402_bittiming_const;
+
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+		struct esd_pci402_net_priv *priv;
+		struct net_device *netdev;
+		u32 fifo_config;
+
+		core->addr = card->addr_overview +
+				(i + 1) * ESD_PCI402_IO_LEN_CORE;
+
+		fifo_config = core_read32(core, OFFS_CORE_TXFIFO_CONFIG);
+		core->tx_fifo_size = (u8)(fifo_config >> 24);
+		BUG_ON(core->tx_fifo_size <= 1);
+
+		netdev = alloc_candev(sizeof(*priv), core->tx_fifo_size);
+		if (!netdev) {
+			err = -ENOMEM;
+			goto failure;
+		}
+		core->net_dev = netdev;
+
+		netdev->flags |= IFF_ECHO;
+		netdev->netdev_ops = &esd_pci402_netdev_ops;
+		SET_NETDEV_DEV(netdev, &pdev->dev);
+
+		priv = netdev_priv(netdev);
+		priv->can.state = CAN_STATE_STOPPED;
+		priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
+						CAN_CTRLMODE_LOOPBACK |
+						CAN_CTRLMODE_BERR_REPORTING;
+		priv->can.clock.freq = card->core_frequency / 2;
+		priv->can.bittiming_const = bittiming_const;
+		priv->can.do_set_bittiming = esd_pci402_set_bittiming;
+		priv->can.do_set_mode = esd_pci402_set_mode;
+		priv->can.do_get_berr_counter = esd_pci402_get_berr_counter;
+
+		priv->card = card;
+		priv->core = core;
+
+		err = register_candev(netdev);
+		if (err)
+			goto failure;
+
+		netdev_info(netdev, "registered\n");
+		num_register_ok++;
+	}
+
+	return 0;
+
+failure:
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+
+		if (!core->net_dev)
+			continue;
+
+		if (i < num_register_ok) {
+			netdev_info(core->net_dev, "unregistering...\n");
+			unregister_candev(core->net_dev);
+		}
+
+		free_candev(core->net_dev);
+		core->net_dev = NULL;
+	}
+
+	return err;
+}
+
+static void esd_pci402_finish_cores(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < card->active_cores; ++i) {
+		struct esd_pci402_cancore *core = &card->core[i];
+
+		unregister_candev(core->net_dev);
+		free_candev(core->net_dev);
+		core->net_dev = NULL;
+	}
+}
+
+static int esd_pci402_probe(struct pci_dev *pdev,
+			    const struct pci_device_id *ent)
+{
+	struct esd_pci402_card *card = NULL;
+	int err;
+
+	BUILD_BUG_ON(sizeof(struct bm_msg) != ESD_PCI402_DMA_FIFO_ITEMSIZE);
+
+	err = pci_enable_device(pdev);
+	if (err)
+		goto failure;
+
+	pci_set_master(pdev);
+
+	card = kzalloc(sizeof(*card), GFP_KERNEL);
+	if (!card)
+		goto failure;
+
+	pci_set_drvdata(pdev, card);
+
+	err = pci_request_regions(pdev, DRV_NAME);
+	if (err)
+		goto failure_disable_pci;
+
+	card->addr = pci_iomap(pdev, ESD_PCI402_BAR, ESD_PCI402_IO_LEN_TOTAL);
+	if (!card->addr) {
+		err = -ENOMEM;
+		goto failure_release_regions;
+	}
+
+	err = esd_pci402_init_card(pdev);
+	if (err)
+		goto failure_unmap;
+
+	err = esd_pci402_init_dma(pdev);
+	if (err)
+		goto failure_unmap;
+
+	err = esd_pci402_init_interrupt(pdev);
+	if (err)
+		goto failure_finish_dma;
+
+	err = esd_pci402_init_cores(pdev);
+	if (err)
+		goto failure_finish_interrupt;
+
+	return 0;
+
+failure_finish_interrupt:
+	esd_pci402_finish_interrupt(pdev);
+
+failure_finish_dma:
+	esd_pci402_finish_dma(pdev);
+
+failure_unmap:
+	pci_iounmap(pdev, card->addr);
+
+failure_release_regions:
+	pci_release_regions(pdev);
+
+failure_disable_pci:
+	pci_disable_device(pdev);
+
+failure:
+	kfree(card);
+
+	return err;
+}
+
+static void esd_pci402_remove(struct pci_dev *pdev)
+{
+	struct esd_pci402_card *card = pci_get_drvdata(pdev);
+
+	esd_pci402_finish_interrupt(pdev);
+	esd_pci402_finish_cores(pdev);
+	esd_pci402_finish_dma(pdev);
+	pci_iounmap(pdev, card->addr);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+	kfree(card);
+}
+
+static const struct pci_device_id esd_pci402_tbl[] = {
+	{ PCI_VENDOR_ID_ESDGMBH, ESD_PCI_DEVICE_ID_PCI402,
+			PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }
+};
+
+static struct pci_driver esd_pci402_driver = {
+	.name = DRV_NAME,
+	.id_table = esd_pci402_tbl,
+	.probe = esd_pci402_probe,
+	.remove = esd_pci402_remove,
+};
+
+module_pci_driver(esd_pci402_driver);
+
+MODULE_DESCRIPTION("linux-can driver for esd CAN PCIe/402 card");
+MODULE_AUTHOR("Thomas Körper <Thomas.Koerper@esd.eu>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/esd/esd402.h b/drivers/net/can/esd/esd402.h
new file mode 100644
index 0000000..509afd4
--- /dev/null
+++ b/drivers/net/can/esd/esd402.h
@@ -0,0 +1,332 @@
+/* Copyright (C) 2014 esd electronic system design gmbh
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the version 2 of the GNU General Public License
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/kernel.h>
+
+#define ESD_PCI_DEVICE_ID_PCI402	0x0402
+
+#define ESD_PCI402_VERSION_MIN		0x0025
+#define ESD_PCI402_VERSION_NEWBTR	0x0032
+
+#define ESD_PCI402_MAX_CORES		4
+#define ESD_PCI402_BAR			0
+#define ESD_PCI402_IO_OV_OFFS		0
+#define ESD_PCI402_IO_PCIEP_OFFS	0x10000
+#define ESD_PCI402_IO_LEN_TOTAL		0x20000
+#define ESD_PCI402_IO_LEN_CORE		0x2000
+#define ESD_PCI402_PCICFG_MSICAP_CSR	0x52
+#define ESD_PCI402_PCICFG_MSICAP_ADDR	0x54
+#define ESD_PCI402_PCICFG_MSICAP_DATA	0x5c
+
+#define ESD_PCI402_DMA_MASK		(DMA_BIT_MASK(32) & 0xffff0000)
+#define ESD_PCI402_DMA_SIZE		ALIGN(0x10000, PAGE_SIZE)
+#define ESD_PCI402_DMA_FIFO_ITEMCOUNT	256
+#define ESD_PCI402_DMA_FIFO_ITEMSIZE	32
+#define ESD_PCI402_DMA_FIFOSIZE		\
+		(ESD_PCI402_DMA_FIFO_ITEMCOUNT * ESD_PCI402_DMA_FIFO_ITEMSIZE)
+
+enum bm_msg_id {
+	BM_MSG_ID_RXTXDONE = 0x01,
+	BM_MSG_ID_TXABORT = 0x02,
+	BM_MSG_ID_OVERRUN = 0x03,
+	BM_MSG_ID_BUSERR = 0x04,
+	BM_MSG_ID_ERRPASSIVE = 0x05,
+	BM_MSG_ID_ERRWARN = 0x06,
+	BM_MSG_ID_TIMESLICE = 0x07,
+	BM_MSG_ID_HWTIMER = 0x08,
+	BM_MSG_ID_HOTPLUG = 0x09,
+	BM_MSG_ID_CANFDDATA0 = 0x0a,
+	BM_MSG_ID_CANFDDATA1 = 0x0b
+};
+
+struct bm_msg_rxtxdone {
+	u8 msg_id;
+	u8 txfifo_level;
+	u8 reserved1[2];
+	u8 txtsfifo_level;
+	u8 reserved2[3];
+	u32 id;
+	union {
+		struct {
+			u8 len;
+			u8 reserved0;
+			u8 bits;
+			u8 state;
+		} rxtx;
+		struct {
+			u8 len;
+			u8 msg_lost;
+			u8 bits;
+			u8 state;
+		} rx;
+		struct {
+			u8 len;
+			u8 txfifo_idx;
+			u8 bits;
+			u8 state;
+		} tx;
+	} dlc;
+	u8 data[8];
+	u64 timestamp;
+} __packed;
+
+struct bm_msg_txabort {
+	u8 msg_id;
+	u8 txfifo_level;
+	u16 abort_mask;
+	u8 txtsfifo_level;
+	u8 reserved2[1];
+	u16 abort_mask_txts;
+	u64 ts;
+	u32 reserved3[4];
+} __packed;
+
+struct bm_msg_overrun {
+	u8 msg_id;
+	u8 txfifo_level;
+	u8 lost_cnt;
+	u8 reserved1;
+	u8 txtsfifo_level;
+	u8 reserved2[3];
+	u64 ts;
+	u32 reserved3[4];
+} __packed;
+
+struct bm_msg_buserr {
+	u8 msg_id;
+	u8 txfifo_level;
+	u8 ecc;
+	u8 reserved1;
+	u8 txtsfifo_level;
+	u8 reserved2[3];
+	u64 ts;
+	u32 reg_status;
+	u32 reg_btr;
+	u32 reserved3[2];
+} __packed;
+
+struct bm_msg_errstatechange {
+	u8 msg_id;
+	u8 txfifo_level;
+	u8 reserved1[2];
+	u8 txtsfifo_level;
+	u8 reserved2[3];
+	u64 ts;
+	u32 reg_status;
+	u32 reserved3[3];
+} __packed;
+
+struct bm_msg_timeslice {
+	u8 msg_id;
+	u8 txfifo_level;
+	u8 reserved1[2];
+	u8 txtsfifo_level;
+	u8 reserved2[3];
+	u64 ts;
+	u32 reserved3[4];
+} __packed;
+
+struct bm_msg_hwtimer {
+	u8 msg_id;
+	u8 reserved1[3];
+	u32 reserved2[1];
+	u64 timer;
+	u32 reserved3[4];
+} __packed;
+
+struct bm_msg_hotplug {
+	u8 msg_id;
+	u8 reserved1[3];
+	u32 reserved2[7];
+} __packed;
+
+struct bm_msg_canfddata {
+	u8 msg_id;
+	u8 reserved1[3];
+	union {
+		u8 ui8[28];
+		u32 ui32[7];
+	} d;
+} __packed;
+
+struct bm_msg {
+	union {
+		u8 msg_id;
+		struct bm_msg_rxtxdone rxtxdone;
+		struct bm_msg_canfddata canfddata;
+		struct bm_msg_txabort txabort;
+		struct bm_msg_overrun overrun;
+		struct bm_msg_buserr buserr;
+		struct bm_msg_errstatechange errstatechange;
+		struct bm_msg_timeslice timeslice;
+		struct bm_msg_hwtimer hwtimer;
+	} u;
+};
+
+struct esd_pci402_cancore {
+	void __iomem *addr;
+	struct net_device *net_dev;
+
+	const struct bm_msg *bm_fifo;
+	/* Bits0..7: bm_fifo head index */
+	const u32 *bm_irq_cnt;
+	/* Local copy of bm_irq_cnt */
+	u32 irq_cnt;
+	u32 msg_fifo_tail;
+
+	u8 tx_fifo_size;
+	u8 tx_fifo_head;
+	u8 tx_fifo_tail;
+};
+
+struct esd_pci402_card {
+	/* Actually mapped io space, all other iomem derived from this */
+	void __iomem *addr;
+
+	void __iomem *addr_pciep;
+	void __iomem *addr_overview;
+
+	void *dma_buf;
+	dma_addr_t dma_hnd;
+
+	const struct bm_msg *bm_fifo;
+	/* Bits0..7: bm_fifo head index */
+	const u32 *bm_irq_cnt;
+	/* Local copy of bm_irq_cnt */
+	u32 irq_cnt;
+
+	struct esd_pci402_cancore core[ESD_PCI402_MAX_CORES];
+
+	u32 timestamp_frequency;
+	u32 core_frequency;
+	u16 version;
+	u16 features;
+	u8 total_cores;
+	u8 active_cores;
+
+	int msi_enabled;
+};
+
+struct esd_pci402_net_priv {
+	struct can_priv can; /* must be the first member! */
+	struct esd_pci402_card *card;
+	struct esd_pci402_cancore *core;
+};
+
+#define OFFS_CORE_CTRL_MODE		0x0000
+#define OFFS_CORE_STATUS_IRQ		0x0008
+#define OFFS_CORE_BTR			0x000c
+#define OFFS_CORE_BTR_FD		0x0010
+#define OFFS_CORE_BTR_FDF		0x0014
+#define OFFS_CORE_STATUS		0x0030
+#define OFFS_CORE_TXFIFO_CONFIG		0x0048
+#define OFFS_CORE_TXFIFO_STATUS		0x004c
+#define OFFS_CORE_TX_STATUS_IRQ		0x0050
+#define OFFS_CORE_TX_ABORT_MASK		0x0054
+#define OFFS_CORE_BM_IRQ_COUNTER	0x0070
+#define OFFS_CORE_TXFIFO_ID		0x00c0
+#define OFFS_CORE_TXFIFO_DLC		0x00c4
+#define OFFS_CORE_TXFIFO_DATA_0		0x00c8
+#define OFFS_CORE_TXFIFO_DATA_1		0x00cc
+
+#define OFFS_OV_PROBE			0x0000
+#define OFFS_OV_VERSION			0x0004
+#define OFFS_OV_INFO			0x0008
+#define OFFS_OV_CANCORE_FREQ		0x000c
+#define OFFS_OV_TS_FREQ_LO		0x0010
+#define OFFS_OV_TS_FREQ_HI		0x0014
+#define OFFS_OV_IRQ_STATUS_CORES	0x0018
+#define OFFS_OV_TS_CURR_LO		0x001c
+#define OFFS_OV_TS_CURR_HI		0x0020
+#define OFFS_OV_IRQ_STATUS		0x0028
+#define OFFS_OV_MODE			0x002c
+#define OFFS_OV_BM_IRQ_COUNTER		0x0070
+#define OFFS_OV_BM_IRQ_MASK		0x0074
+#define OFFS_OV_MSI_DATA		0x0080
+#define OFFS_OV_MSI_ADDRESSOFFSET	0x0084
+
+#define OFFS_PCIEP_INT_ENABLE		0x0050
+#define OFFS_PCIEP_BM_ADDR_LO		0x1000
+#define OFFS_PCIEP_BM_ADDR_HI		0x1004
+#define OFFS_PCIEP_MSI_ADDR_LO		0x1008
+#define OFFS_PCIEP_MSI_ADDR_HI		0x100c
+
+#define REG_OV_MODE_MASK_ENDIAN_LITTLE  0x00000001
+#define REG_OV_MODE_MASK_BM_ENABLE      0x00000002
+#define REG_OV_MODE_MASK_MODE_LED       0x00000004
+#define REG_OV_MODE_MASK_TIMER          0x00000070
+#define REG_OV_MODE_MASK_TIMER_ENABLE   0x00000010
+#define REG_OV_MODE_MASK_TIMER_ONE_SHOT 0x00000020
+#define REG_OV_MODE_MASK_TIMER_ABSOLUTE 0x00000040
+#define REG_OV_MODE_MASK_TS_SRC         0x00000180
+#define REG_OV_MODE_MASK_I2C_ENABLE     0x00000800
+#define REG_OV_MODE_MASK_MSI_ENABLE     0x00004000
+#define REG_OV_MODE_MASK_FPGA_RESET     0x80000000
+
+#define BM_IRQ_UNMASK_ALL               0x55555555
+#define BM_IRQ_MASK_ALL                 0xaaaaaaaa
+#define BM_IRQ_MASK                     0x00000002
+#define BM_IRQ_UNMASK                   0x00000001
+
+#define REG_CONTROL_IDX_MODE_RESETMODE  0
+#define REG_CONTROL_IDX_MODE_LOM        1
+#define REG_CONTROL_IDX_MODE_STM        2
+#define REG_CONTROL_IDX_MODE_TRANSEN    5
+#define REG_CONTROL_IDX_MODE_TS         6
+#define REG_CONTROL_IDX_MODE_SCHEDULE   7
+#define REG_CONTROL_MASK_MODE_RESETMODE BIT(REG_CONTROL_IDX_MODE_RESETMODE)
+#define REG_CONTROL_MASK_MODE_LOM       BIT(REG_CONTROL_IDX_MODE_LOM)
+#define REG_CONTROL_MASK_MODE_STM       BIT(REG_CONTROL_IDX_MODE_STM)
+#define REG_CONTROL_MASK_MODE_TRANSEN   BIT(REG_CONTROL_IDX_MODE_TRANSEN)
+#define REG_CONTROL_MASK_MODE_TS        BIT(REG_CONTROL_IDX_MODE_TS)
+#define REG_CONTROL_MASK_MODE_SCHEDULE  BIT(REG_CONTROL_IDX_MODE_SCHEDULE)
+
+#define REG_CONTROL_IDX_IE_RXTX         8
+#define REG_CONTROL_IDX_IE_TXERROR      9
+#define REG_CONTROL_IDX_IE_ERROR        10
+#define REG_CONTROL_IDX_IE_OVERRUN      11
+#define REG_CONTROL_IDX_IE_TSI          12
+#define REG_CONTROL_IDX_IE_ERRPASS      13
+#define REG_CONTROL_IDX_IE_BUSERR       15
+#define REG_CONTROL_MASK_IE_RXTX        BIT(REG_CONTROL_IDX_IE_RXTX)
+#define REG_CONTROL_MASK_IE_TXERROR     BIT(REG_CONTROL_IDX_IE_TXERROR)
+#define REG_CONTROL_MASK_IE_ERROR       BIT(REG_CONTROL_IDX_IE_ERROR)
+#define REG_CONTROL_MASK_IE_OVERRUN     BIT(REG_CONTROL_IDX_IE_OVERRUN)
+#define REG_CONTROL_MASK_IE_TSI         BIT(REG_CONTROL_IDX_IE_TSI)
+#define REG_CONTROL_MASK_IE_ERRPASS     BIT(REG_CONTROL_IDX_IE_ERRPASS)
+#define REG_CONTROL_MASK_IE_BUSERR      BIT(REG_CONTROL_IDX_IE_BUSERR)
+
+#define REG_STATUS_IDX_STATUS_DOS       16
+#define REG_STATUS_IDX_STATUS_ES        17
+#define REG_STATUS_IDX_STATUS_EP        18
+#define REG_STATUS_IDX_STATUS_BS        19
+#define REG_STATUS_IDX_STATUS_RBS       20
+#define REG_STATUS_IDX_STATUS_RS        21
+#define REG_STATUS_MASK_STATUS_DOS      BIT(REG_STATUS_IDX_STATUS_DOS)
+#define REG_STATUS_MASK_STATUS_ES       BIT(REG_STATUS_IDX_STATUS_ES)
+#define REG_STATUS_MASK_STATUS_EP       BIT(REG_STATUS_IDX_STATUS_EP)
+#define REG_STATUS_MASK_STATUS_BS       BIT(REG_STATUS_IDX_STATUS_BS)
+#define REG_STATUS_MASK_STATUS_RBS      BIT(REG_STATUS_IDX_STATUS_RBS)
+#define REG_STATUS_MASK_STATUS_RS       BIT(REG_STATUS_IDX_STATUS_RS)
+
+#define ESD_PCI402_BM_LENFLAG_TX	0x20
+
+/* ecc value of PCI402 equals SJA1000's ECC register */
+#define ESD_PCI402_ECC_SEG		0x1f
+#define ESD_PCI402_ECC_DIR		0x20
+#define ESD_PCI402_ECC_BIT		0x00
+#define ESD_PCI402_ECC_FORM		0x40
+#define ESD_PCI402_ECC_STUFF		0x80
+#define ESD_PCI402_ECC_MASK		0xc0
-- 
1.7.9.5


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

* Re: [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
  2014-10-13 11:23 [PATCH 1/1] can: Add support for esd CAN PCIe/402 card Thomas Körper
@ 2014-10-20 16:26 ` Oliver Hartkopp
  2014-10-21  7:46   ` AW: " Thomas Körper
  0 siblings, 1 reply; 6+ messages in thread
From: Oliver Hartkopp @ 2014-10-20 16:26 UTC (permalink / raw)
  To: Thomas Körper, linux-can

Hello Thomas,

thanks for contributing the first escACC-based driver!

On 10/13/2014 01:23 PM, Thomas Körper wrote:
> Some infos: Hardware: Card based on our own CAN controller ("esdACC"). Bus master DMA and MSI / Different configurations with up to 4 can cores / CAN termination jumpers on board.
> Driver: "core" refers to a net/controller specific part, overview ("ov") to the general card. DMA transfer (Rx direction) is done via bus master messages ("bm_" prefix)

First the description should fit the standard linebreak length too.
It should only state what kind of CAN card is supported (e.g. together with
some URL to the website).

http://esd.eu/en/products/esdacc
http://esd.eu/en/products/can-pcie402

Details about the implementation ("ov") should go somewhere into the code -
but not into the commit message ...

> 
> Signed-off-by: Thomas Körper <Thomas.Koerper@esd.eu>

Capital letters in mail addresses - ugh.

> ---
>  drivers/net/can/Kconfig      |    2 +
>  drivers/net/can/Makefile     |    1 +
>  drivers/net/can/esd/Kconfig  |    6 +
>  drivers/net/can/esd/Makefile |    4 +
>  drivers/net/can/esd/esd402.c | 1115 ++++++++++++++++++++++++++++++++++++++++++
>  drivers/net/can/esd/esd402.h |  332 +++++++++++++
>  6 files changed, 1460 insertions(+)
>  create mode 100644 drivers/net/can/esd/Kconfig
>  create mode 100644 drivers/net/can/esd/Makefile
>  create mode 100644 drivers/net/can/esd/esd402.c
>  create mode 100644 drivers/net/can/esd/esd402.h

As the esdACC core is available in several hardware cards I would suggest to
split your files in another way.

E.g. introduce a esdacc.h include and esdacc.c which contains the esdACC
specific stuff (compare to sja1000.[ch]).

Does your driver support the PCI400 too?
http://esd.eu/en/products/can-pci400

You because I have one of it and could do some testing then.

> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 4168822..077ee72 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -151,6 +151,8 @@ source "drivers/net/can/usb/Kconfig"
>  
>  source "drivers/net/can/softing/Kconfig"
>  
> +source "drivers/net/can/esd/Kconfig"
> +
>  endif
>  
>  config CAN_DEBUG_DEVICES
> diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile
> index 1697f22..5b8681e 100644
> --- a/drivers/net/can/Makefile
> +++ b/drivers/net/can/Makefile
> @@ -13,6 +13,7 @@ can-dev-$(CONFIG_CAN_LEDS)	+= led.o
>  obj-y				+= spi/
>  obj-y				+= usb/
>  obj-y				+= softing/
> +obj-y				+= esd/

Does it make sense to name the directory 'esdacc' ?
There is ESD hardware in drivers/net/can/usb too.

With esdacc we would have a CAN IP core specific directory like we have for
the sja1000, c_can, etc ...

>  
>  obj-$(CONFIG_CAN_SJA1000)	+= sja1000/
>  obj-$(CONFIG_CAN_MSCAN)		+= mscan/
> diff --git a/drivers/net/can/esd/Kconfig b/drivers/net/can/esd/Kconfig
> new file mode 100644
> index 0000000..faefced
> --- /dev/null
> +++ b/drivers/net/can/esd/Kconfig
> @@ -0,0 +1,6 @@
> +config CAN_ESD_402
> +	tristate "esd gmbh CAN PCIe/402 support"
> +	default y
> +	depends on PCI && HAS_DMA
> +	---help---
> +	  Support for CAN PCIe/402 cards from esd electronic system design gmbh

GmbH ??

Add URL: http://esd.eu/en


> diff --git a/drivers/net/can/esd/Makefile b/drivers/net/can/esd/Makefile
> new file mode 100644
> index 0000000..211b64d
> --- /dev/null
> +++ b/drivers/net/can/esd/Makefile
> @@ -0,0 +1,4 @@
> +
> +obj-m += esd402.o
> +
> +ccflags-$(CONFIG_CAN_DEBUG_DEVICES) := -DDEBUG

This is obsolete now.

> diff --git a/drivers/net/can/esd/esd402.c b/drivers/net/can/esd/esd402.c
> new file mode 100644
> index 0000000..54e55d1
> --- /dev/null
> +++ b/drivers/net/can/esd/esd402.c
> @@ -0,0 +1,1115 @@
> +/* Copyright (C) 2014 esd electronic system design gmbh
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the version 2 of the GNU General Public License
> + * as published by the Free Software Foundation
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/moduleparam.h>
> +#include <linux/interrupt.h>
> +#include <linux/pci.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/netdevice.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/can.h>
> +#include <linux/can/dev.h>
> +
> +#include "esd402.h"
> +
> +#define DRV_NAME			"esd402_driver"

esd402_pcie

?

> +
> +#define ESD_CAN_EFF_FLAG		0x20000000
> +#define ESD_CAN_RTR_FLAG		0x10
> +#define ESD_CAN_DLC_MASK		0x0f

A candidate for esdacc.h ...


> +
> +static inline u32 ov_read32(struct esd_pci402_card *card, unsigned short offs)
> +{
> +	return ioread32be(card->addr_overview + offs);
> +}
> +
> +static inline void ov_write32(struct esd_pci402_card *card,
> +			      unsigned short offs, u32 v)
> +{
> +	iowrite32be(v, card->addr_overview + offs);
> +}

Does it make sense to put these access functions into the same indirection as
you can see in the sja1000 or c_can driver?

> +
> +static void ov_set_bits(struct esd_pci402_card *card,
> +			unsigned short offs, u32 b)
> +{
> +	u32 v = ov_read32(card, offs);
> +
> +	v |= b;
> +	ov_write32(card, offs, v);
> +}
> +
> +static void ov_clear_bits(struct esd_pci402_card *card,
> +			  unsigned short offs, u32 b)
> +{
> +	u32 v = ov_read32(card, offs);
> +
> +	v &= ~b;
> +	ov_write32(card, offs, v);
> +}
> +
> +static ktime_t ov_ts_to_ktime(struct esd_pci402_card *card, u64 ts)
> +{
> +	unsigned long long ns;
> +	unsigned int rem;
> +
> +	rem = do_div(ts, card->timestamp_frequency);
> +	ns = ts * NSEC_PER_SEC;
> +
> +	ts = (unsigned long long)rem * NSEC_PER_SEC;
> +	do_div(ts, card->timestamp_frequency);
> +	ns += ts;
> +
> +	return ns_to_ktime(ns);
> +}

Looks generic too.
Move to esdacc.c?

> +
> +static inline u32 core_read32(struct esd_pci402_cancore *core,
> +			      unsigned short offs)
> +{
> +	return ioread32be(core->addr + offs);
> +}
> +
> +static inline void core_write32(struct esd_pci402_cancore *core,
> +				unsigned short offs, u32 v)
> +{
> +	iowrite32be(v, core->addr + offs);
> +}
> +
> +static inline void core_write32_noswap(struct esd_pci402_cancore *core,
> +				       unsigned short offs, u32 v)
> +{
> +	iowrite32(v, core->addr + offs);
> +}
> +
> +static void core_set_bits(struct esd_pci402_cancore *core,
> +			  unsigned short offs, u32 mask)
> +{
> +	u32 v = core_read32(core, offs);
> +
> +	v |= mask;
> +	core_write32(core, offs, v);
> +}
> +
> +static void core_clear_bits(struct esd_pci402_cancore *core,
> +			    unsigned short offs, u32 mask)
> +{
> +	u32 v = core_read32(core, offs);
> +
> +	v &= ~mask;
> +	core_write32(core, offs, v);
> +}
> +
> +static int core_resetmode_entered(struct esd_pci402_cancore *core)
> +{
> +	u32 ctrl = core_read32(core, OFFS_CORE_CTRL_MODE);
> +
> +	return (ctrl & REG_CONTROL_MASK_MODE_RESETMODE) != 0;
> +}
> +
> +static void core_resetmode_enter(struct esd_pci402_cancore *core)
> +{
> +	int i;
> +
> +	core_set_bits(core, OFFS_CORE_CTRL_MODE,
> +		      REG_CONTROL_MASK_MODE_RESETMODE);
> +
> +	for (i = 0; i < 10; ++i) {
> +		if (core_resetmode_entered(core))
> +			return;
> +
> +		udelay(5);
> +	}
> +
> +	netdev_warn(core->net_dev, "Entering reset mode timed out\n");
> +}
> +
> +static void core_resetmode_leave(struct esd_pci402_cancore *core)
> +{
> +	int i;
> +
> +	core_clear_bits(core, OFFS_CORE_CTRL_MODE,
> +			REG_CONTROL_MASK_MODE_RESETMODE);
> +
> +	for (i = 0; i < 10; ++i) {
> +		if (!core_resetmode_entered(core))
> +			return;
> +
> +		udelay(5);
> +	}
> +
> +	netdev_warn(core->net_dev, "Leaving reset mode timed out\n");
> +}
> +
> +static int core_txq_isready(struct esd_pci402_cancore *core)
> +{
> +	u8 fifo_level = (u8)(core_read32(core, OFFS_CORE_TXFIFO_STATUS) >> 16);
> +
> +	return (fifo_level < core->tx_fifo_size - 1);
> +}
> +
> +static void core_txq_put(struct esd_pci402_cancore *core, u32 esd_id,
> +			 u8 esd_len, const void *data)
> +{
> +	core_write32_noswap(core, OFFS_CORE_TXFIFO_DATA_0,
> +			    *((const u32 *)data));
> +	core_write32_noswap(core, OFFS_CORE_TXFIFO_DATA_1,
> +			    *((const u32 *)(data + 4)));
> +	core_write32(core, OFFS_CORE_TXFIFO_DLC, esd_len);
> +	/* CAN id must be written at last: */
> +	core_write32(core, OFFS_CORE_TXFIFO_ID, esd_id);
> +}
> +
> +static void core_handlemsg_rxtxdone(struct esd_pci402_cancore *core,
> +				    const struct bm_msg_rxtxdone *msg)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(core->net_dev);
> +	struct net_device_stats *stats = &core->net_dev->stats;
> +
> +	if (msg->dlc.rxtx.len & ESD_PCI402_BM_LENFLAG_TX) {
> +		if (core->tx_fifo_head == core->tx_fifo_tail) {
> +			netdev_warn(core->net_dev,
> +				    "TX interrupt, but queue is empty!?\n");
> +			return;
> +		}
> +		stats->tx_packets++;
> +		stats->tx_bytes +=
> +				get_can_dlc(msg->dlc.tx.len & ESD_CAN_DLC_MASK);
> +
> +		can_get_echo_skb(core->net_dev, core->tx_fifo_tail);
> +		core->tx_fifo_tail++;
> +		if (core->tx_fifo_tail >= core->tx_fifo_size)
> +			core->tx_fifo_tail = 0;
> +		netif_wake_queue(core->net_dev);
> +
> +	} else {
> +		struct skb_shared_hwtstamps *skb_ts;
> +		struct can_frame *cf;
> +		struct sk_buff *skb;
> +
> +		skb = alloc_can_skb(core->net_dev, &cf);
> +		if (skb == NULL) {
> +			stats->rx_dropped++;
> +			return;
> +		}
> +
> +		cf->can_id = msg->id & CAN_EFF_MASK;
> +		if (msg->id & ESD_CAN_EFF_FLAG)
> +			cf->can_id |= CAN_EFF_FLAG;
> +
> +		cf->can_dlc = get_can_dlc(msg->dlc.rx.len);
> +		if (msg->dlc.rx.len & ESD_CAN_RTR_FLAG)
> +			cf->can_id |= CAN_RTR_FLAG;
> +		else
> +			memcpy(cf->data, msg->data, cf->can_dlc);
> +
> +		skb_ts = skb_hwtstamps(skb);
> +		skb_ts->hwtstamp = ov_ts_to_ktime(priv->card, msg->timestamp);
> +		netif_rx(skb);
> +
> +		stats->rx_packets++;
> +		stats->rx_bytes += cf->can_dlc;
> +	}
> +}
> +
> +static void core_handlemsg_txabort(struct esd_pci402_cancore *core,
> +				   const struct bm_msg_txabort *msg)
> +{
> +	struct net_device_stats *stats = &core->net_dev->stats;
> +	int i;
> +
> +	/* abort_mask signals which frames were aborted in card's fifo */
> +	for (i = 0; i < sizeof(msg->abort_mask) * BITS_PER_BYTE; ++i) {
> +		if (!(msg->abort_mask & (1 << i)))
> +			continue;
> +
> +		if (core->tx_fifo_head == core->tx_fifo_tail) {
> +			netdev_warn(core->net_dev,
> +				    "TX Err interrupt, but queue is empty!?\n");
> +			break;
> +		}
> +		stats->tx_errors++;
> +
> +		can_free_echo_skb(core->net_dev, core->tx_fifo_tail);
> +		core->tx_fifo_tail++;
> +		if (core->tx_fifo_tail >= core->tx_fifo_size)
> +			core->tx_fifo_tail = 0;
> +	}
> +
> +	if (!core_resetmode_entered(core))
> +		netif_wake_queue(core->net_dev);
> +}
> +
> +static void core_handlemsg_overrun(struct esd_pci402_cancore *core,
> +				   const struct bm_msg_overrun *msg)
> +{
> +	struct net_device_stats *stats = &core->net_dev->stats;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +
> +	skb = alloc_can_err_skb(core->net_dev, &cf);
> +	if (skb == NULL)
> +		return;
> +
> +	/* lost_cnt may be 0 if not supported by FPGA version */
> +	if (msg->lost_cnt) {
> +		stats->rx_dropped += msg->lost_cnt;
> +		stats->rx_over_errors +=  msg->lost_cnt;
> +	} else {
> +		stats->rx_dropped++;
> +		stats->rx_over_errors++;
> +	}
> +
> +	cf->can_id |= CAN_ERR_CRTL;
> +	cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
> +
> +	netif_rx(skb);
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void core_handlemsg_buserr(struct esd_pci402_cancore *core,
> +				  const struct bm_msg_buserr *msg)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(core->net_dev);
> +	struct net_device_stats *stats = &core->net_dev->stats;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +
> +	priv->can.can_stats.bus_error++;
> +	stats->rx_errors++;
> +
> +	skb = alloc_can_err_skb(core->net_dev, &cf);
> +	if (skb == NULL)
> +		return;
> +
> +	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +	/* msg->ecc acts like SJA1000's ECC register */
> +	switch (msg->ecc & ESD_PCI402_ECC_MASK) {
> +	case ESD_PCI402_ECC_BIT:
> +		cf->data[2] |= CAN_ERR_PROT_BIT;
> +		break;
> +	case ESD_PCI402_ECC_FORM:
> +		cf->data[2] |= CAN_ERR_PROT_FORM;
> +		break;
> +	case ESD_PCI402_ECC_STUFF:
> +		cf->data[2] |= CAN_ERR_PROT_STUFF;
> +		break;
> +	default:
> +		cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +		cf->data[3] = msg->ecc & ESD_PCI402_ECC_SEG;
> +		break;
> +	}
> +
> +	if ((msg->ecc & ESD_PCI402_ECC_DIR) == 0)
> +		cf->data[2] |= CAN_ERR_PROT_TX;
> +
> +	netif_rx(skb);
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;
> +}
> +
> +static void
> +core_handlemsg_errstatechange(struct esd_pci402_cancore *core,
> +			      const struct bm_msg_errstatechange *msg)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(core->net_dev);
> +	struct net_device_stats *stats = &core->net_dev->stats;
> +	struct can_frame *cf;
> +	struct sk_buff *skb;
> +	int is_busoff;
> +	int is_passive;
> +	int is_warning;
> +	u8 txerr;
> +	u8 rxerr;
> +
> +	txerr = (u8)(msg->reg_status >> 8);
> +	rxerr = (u8)msg->reg_status;
> +	is_warning = (msg->reg_status & REG_STATUS_MASK_STATUS_ES) != 0;
> +	is_passive = (msg->reg_status & REG_STATUS_MASK_STATUS_EP) != 0;
> +	is_busoff = (msg->reg_status & REG_STATUS_MASK_STATUS_BS) != 0;
> +
> +	skb = alloc_can_err_skb(core->net_dev, &cf);
> +	if (skb) {
> +		if (is_busoff) {
> +			priv->can.state = CAN_STATE_BUS_OFF;
> +			/* bus-offs counted by can_bus_off() */
> +			cf->can_id |= CAN_ERR_BUSOFF;
> +		} else if (is_passive) {
> +			priv->can.state = CAN_STATE_ERROR_PASSIVE;
> +			priv->can.can_stats.error_passive++;
> +			cf->data[1] = (txerr > rxerr) ? CAN_ERR_CRTL_TX_PASSIVE
> +					: CAN_ERR_CRTL_RX_PASSIVE;
> +			cf->can_id |= CAN_ERR_CRTL;
> +			cf->data[6] = txerr;
> +			cf->data[7] = rxerr;
> +		} else if (is_warning) {
> +			priv->can.state = CAN_STATE_ERROR_WARNING;
> +			priv->can.can_stats.error_warning++;
> +			cf->data[1] = (txerr > rxerr) ? CAN_ERR_CRTL_TX_WARNING
> +					: CAN_ERR_CRTL_RX_WARNING;
> +			cf->can_id |= CAN_ERR_CRTL;
> +			cf->data[6] = txerr;
> +			cf->data[7] = rxerr;
> +		} else {
> +			priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +			/* restarts counted in dev.c */
> +		}
> +
> +		netif_rx(skb);
> +		stats->rx_packets++;
> +		stats->rx_bytes += cf->can_dlc;
> +	}
> +
> +	if (is_busoff) {
> +		core_write32(core, OFFS_CORE_TX_ABORT_MASK, 0xffff);
> +		can_bus_off(core->net_dev);
> +	}
> +}
> +
> +static void core_handle_interrupt(struct esd_pci402_cancore *core)
> +{
> +	u32 msg_fifo_head = core->irq_cnt & 0xff;
> +
> +	while (core->msg_fifo_tail != msg_fifo_head) {
> +		const struct bm_msg *msg = &core->bm_fifo[core->msg_fifo_tail];
> +
> +		switch (msg->u.msg_id) {
> +		case BM_MSG_ID_RXTXDONE:
> +			core_handlemsg_rxtxdone(core, &msg->u.rxtxdone);
> +			break;
> +
> +		case BM_MSG_ID_TXABORT:
> +			core_handlemsg_txabort(core, &msg->u.txabort);
> +			break;
> +
> +		case BM_MSG_ID_OVERRUN:
> +			core_handlemsg_overrun(core, &msg->u.overrun);
> +			break;
> +
> +		case BM_MSG_ID_BUSERR:
> +			core_handlemsg_buserr(core, &msg->u.buserr);
> +			break;
> +
> +		case BM_MSG_ID_ERRPASSIVE:
> +		case BM_MSG_ID_ERRWARN:
> +			core_handlemsg_errstatechange(core,
> +						      &msg->u.errstatechange);
> +			break;
> +
> +		default:
> +			break;
> +		}
> +
> +		core->msg_fifo_tail++;
> +		if (core->msg_fifo_tail >= ESD_PCI402_DMA_FIFO_ITEMCOUNT)
> +			core->msg_fifo_tail = 0;
> +	}
> +}
> +
> +static irqreturn_t esd_pci402_interrupt(int irq, void *dev_id)
> +{
> +	struct esd_pci402_card *card;
> +	u32 irqmask;
> +	int i;
> +
> +	card = pci_get_drvdata((struct pci_dev *)dev_id);
> +
> +	/* First we look for whom interrupts are pending, card/overview
> +	 * or any of the cores. Two bits in irqmask are used for each;
> +	 * set to BM_IRQ_MASK then:
> +	 */
> +	irqmask = 0;
> +	if (*card->bm_irq_cnt != card->irq_cnt) {
> +		irqmask |= BM_IRQ_MASK;
> +		card->irq_cnt = *card->bm_irq_cnt;
> +	}
> +
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +
> +		if (*core->bm_irq_cnt != core->irq_cnt) {
> +			irqmask |= (BM_IRQ_MASK << (2 * (i + 1)));
> +			core->irq_cnt = *core->bm_irq_cnt;
> +		}
> +	}
> +
> +	if (!irqmask)
> +		return IRQ_NONE;
> +
> +	/* At second we tell the card we're working on them by writing irqmask,
> +	 * call esd_pci402_interrupt_{ov|core} and then acknowledge the
> +	 * interrupts by writing irq_cnt:
> +	 */
> +	ov_write32(card, OFFS_OV_BM_IRQ_MASK, irqmask);
> +
> +	if (irqmask & BM_IRQ_MASK) {
> +		/* ov_handle_interrupt(card); - no use yet. */
> +		ov_write32(card, OFFS_OV_BM_IRQ_COUNTER, card->irq_cnt);
> +	}
> +
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +
> +		if (irqmask & (BM_IRQ_MASK << (2 * (i + 1)))) {
> +			core_handle_interrupt(core);
> +			core_write32(core, OFFS_OV_BM_IRQ_COUNTER,
> +				     core->irq_cnt);
> +		}
> +	}
> +
> +	ov_write32(card, OFFS_OV_BM_IRQ_MASK, BM_IRQ_UNMASK_ALL);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int esd_pci402_open(struct net_device *netdev)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
> +	u32 ctrl_mode;
> +	int err;
> +
> +	err = open_candev(netdev);
> +	if (err)
> +		return err;
> +
> +	ctrl_mode = REG_CONTROL_MASK_IE_RXTX |
> +			REG_CONTROL_MASK_IE_TXERROR |
> +			REG_CONTROL_MASK_IE_ERRPASS |
> +			REG_CONTROL_MASK_IE_OVERRUN |
> +			REG_CONTROL_MASK_IE_ERROR;
> +
> +	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +		ctrl_mode |= REG_CONTROL_MASK_IE_BUSERR;
> +
> +	core_set_bits(priv->core, OFFS_CORE_CTRL_MODE, ctrl_mode);
> +
> +	netif_start_queue(netdev);
> +	return 0;
> +}
> +
> +static int esd_pci402_close(struct net_device *netdev)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
> +
> +	core_clear_bits(priv->core, OFFS_CORE_CTRL_MODE,
> +			REG_CONTROL_MASK_IE_RXTX |
> +			REG_CONTROL_MASK_IE_TXERROR |
> +			REG_CONTROL_MASK_IE_OVERRUN);
> +
> +	netif_stop_queue(netdev);
> +	priv->can.state = CAN_STATE_STOPPED;
> +
> +	close_candev(netdev);
> +	return 0;
> +}
> +
> +static netdev_tx_t esd_pci402_start_xmit(struct sk_buff *skb,
> +					 struct net_device *netdev)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
> +	struct can_frame *cf = (struct can_frame *)skb->data;
> +	struct esd_pci402_cancore *core = priv->core;
> +	u8 new_fifo_head = (core->tx_fifo_head + 1) % core->tx_fifo_size;
> +	u32 esd_id;
> +	u8 esd_len;
> +
> +	if ((new_fifo_head == core->tx_fifo_tail) || !core_txq_isready(core)) {
> +		netif_stop_queue(netdev);
> +		return NETDEV_TX_BUSY;
> +	}
> +
> +	esd_len = can_dlc2len(cf->can_dlc);
> +	if (cf->can_id & CAN_RTR_FLAG)
> +		esd_len |= ESD_CAN_RTR_FLAG;
> +
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		esd_id = cf->can_id & CAN_EFF_MASK;
> +		esd_id |= ESD_CAN_EFF_FLAG;
> +	} else {
> +		esd_id = cf->can_id & CAN_SFF_MASK;
> +	}
> +
> +	can_put_echo_skb(skb, netdev, core->tx_fifo_head);
> +	core->tx_fifo_head = new_fifo_head;
> +
> +	core_txq_put(core, esd_id, esd_len, cf->data);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +static int esd_pci402_set_bittiming(struct net_device *netdev)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
> +	struct can_bittiming *bt = &priv->can.bittiming;
> +	u32 btr;
> +	u8 brp;
> +
> +	brp = (u8)(bt->brp - 1);
> +	core_resetmode_enter(priv->core);
> +
> +	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> +		core_set_bits(priv->core, OFFS_CORE_CTRL_MODE,
> +			      REG_CONTROL_MASK_MODE_LOM);
> +	else
> +		core_clear_bits(priv->core, OFFS_CORE_CTRL_MODE,
> +				REG_CONTROL_MASK_MODE_LOM);
> +
> +	if (priv->card->version < ESD_PCI402_VERSION_NEWBTR) {
> +		btr = brp;
> +
> +		/* TSEG1: Bit12..15 */
> +		btr |= ((bt->phase_seg1 + bt->prop_seg - 1) & 0xf) << 12;
> +		/* TSEG2: Bit20..22 */
> +		btr |= ((bt->phase_seg2 - 1) & 0x7) << 20;
> +		/* SJW: Bit25..26 */
> +		btr |= ((bt->sjw - 1) & 0x3) << 25;
> +
> +		core_write32(priv->core, OFFS_CORE_BTR, btr);
> +	} else {
> +		/* TSEG1: Bit0..7 */
> +		btr = (bt->phase_seg1 + bt->prop_seg - 1) & 0xff;
> +		/* TSEG2: Bit16..22 */
> +		btr |= ((bt->phase_seg2 - 1) & 0x7f) << 16;
> +		/* SJW: Bit24..30 */
> +		btr |= ((bt->sjw - 1) & 0x7f) << 24;
> +
> +		core_write32(priv->core, OFFS_CORE_BTR, brp);
> +		core_write32(priv->core, OFFS_CORE_BTR_FD, btr);
> +	}
> +
> +	core_resetmode_leave(priv->core);
> +	priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +	return 0;
> +}
> +
> +static int esd_pci402_get_berr_counter(const struct net_device *netdev,
> +				       struct can_berr_counter *bec)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
> +	u32 core_status = core_read32(priv->core, OFFS_CORE_STATUS);
> +
> +	bec->txerr = (u8)(core_status >> 8);
> +	bec->rxerr = (u8)core_status;
> +
> +	return 0;
> +}
> +
> +static int esd_pci402_set_mode(struct net_device *netdev, enum can_mode mode)
> +{
> +	struct esd_pci402_net_priv *priv = netdev_priv(netdev);
> +
> +	switch (mode) {
> +	case CAN_MODE_START:
> +		core_resetmode_leave(priv->core);
> +		netif_wake_queue(netdev);
> +		break;
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +/* For cards with FPGA Version < ESD_PCI402_VERSION_NEWBTR */
> +static const struct can_bittiming_const esd_pci402_bittiming_const_old = {
> +	.name = "esd_pci402_old",
> +	.tseg1_min = 1,
> +	.tseg1_max = 16,
> +	.tseg2_min = 1,
> +	.tseg2_max = 8,
> +	.sjw_max = 4,
> +	.brp_min = 1,
> +	.brp_max = 256,
> +	.brp_inc = 1,
> +};
> +
> +static const struct can_bittiming_const esd_pci402_bittiming_const = {
> +	.name = "esd_pci402",
> +	.tseg1_min = 1,
> +	.tseg1_max = 256,
> +	.tseg2_min = 1,
> +	.tseg2_max = 128,
> +	.sjw_max = 128,
> +	.brp_min = 1,
> +	.brp_max = 256,
> +	.brp_inc = 1,
> +};
> +

acc specific or card specific?

-> esdacc.h ??

> +static const struct net_device_ops esd_pci402_netdev_ops = {
> +	.ndo_open = esd_pci402_open,
> +	.ndo_stop = esd_pci402_close,
> +	.ndo_start_xmit = esd_pci402_start_xmit,
> +	.ndo_change_mtu = can_change_mtu
> +};
> +
> +static int dump_infos;
> +module_param(dump_infos, int, S_IRUGO);
> +MODULE_PARM_DESC(dump_infos, "Dump card infos while probing. Default: 0 (Off)");
> +
> +static void esd_pci402_reset_fpga(struct esd_pci402_card *card)
> +{
> +	ov_write32(card, OFFS_OV_MODE, REG_OV_MODE_MASK_FPGA_RESET);
> +
> +	/* Also reset I²C, to re-detect card addons at every driver start: */
> +	ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_I2C_ENABLE);
> +	mdelay(2);
> +	ov_set_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_I2C_ENABLE);
> +	mdelay(10);
> +}
> +
> +static int esd_pci402_set_msiconfig(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	u32 addr_lo_offs = 0;
> +	u32 addr_lo = 0;
> +	u32 addr_hi = 0;
> +	u32 data = 0;
> +	u16 csr = 0;
> +	int err;
> +
> +	err = pci_read_config_word(pdev, ESD_PCI402_PCICFG_MSICAP_CSR, &csr);
> +	if (err)
> +		goto failed;
> +
> +	err = pci_read_config_dword(pdev, ESD_PCI402_PCICFG_MSICAP_ADDR,
> +				    &addr_lo);
> +	if (err)
> +		goto failed;
> +
> +	err = pci_read_config_dword(pdev, ESD_PCI402_PCICFG_MSICAP_ADDR + 4,
> +				    &addr_hi);
> +	if (err)
> +		goto failed;
> +
> +	err = pci_read_config_dword(pdev, ESD_PCI402_PCICFG_MSICAP_DATA, &data);
> +	if (err)
> +		goto failed;
> +
> +	addr_lo_offs = addr_lo & 0x0000ffff;
> +	addr_lo &= 0xffff0000;
> +
> +	if (addr_hi)
> +		addr_lo |= 1; /* Enable 64-Bit addressing in address space */
> +
> +	if (!(csr & 0x0001)) /* Enable bit */
> +		goto failed;
> +
> +	iowrite32(addr_lo, card->addr_pciep + OFFS_PCIEP_MSI_ADDR_LO);
> +	iowrite32(addr_hi, card->addr_pciep + OFFS_PCIEP_MSI_ADDR_HI);
> +	ov_write32(card, OFFS_OV_MSI_ADDRESSOFFSET, addr_lo_offs);
> +	ov_write32(card, OFFS_OV_MSI_DATA, data);
> +
> +	return 0;
> +
> +failed:
> +	dev_warn(&pdev->dev, "Error while setting MSI configuration:\n"
> +		 "CSR: 0x%.4x, addr: 0x%.8x%.8x, data: 0x%.8x\n",
> +		 csr, addr_hi, addr_lo, data);
> +
> +	return -1;
> +}
> +
> +static int esd_pci402_init_card(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	u32 temp;
> +
> +	card->addr_overview = card->addr + ESD_PCI402_IO_OV_OFFS;
> +	card->addr_pciep = card->addr + ESD_PCI402_IO_PCIEP_OFFS;
> +
> +	esd_pci402_reset_fpga(card);
> +
> +#ifdef __LITTLE_ENDIAN
> +	ov_set_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_ENDIAN_LITTLE);
> +	/* So card converts all busmastered data to LE for us */
> +#endif
> +
> +	temp = ov_read32(card, OFFS_OV_VERSION);
> +	card->features = (u16)(temp >> 16);
> +	card->version = (u16)temp;
> +
> +	temp = ov_read32(card, OFFS_OV_INFO);
> +	card->active_cores = (u8)(temp >> 8);
> +	card->total_cores = (u8)temp;
> +	if (card->active_cores > ESD_PCI402_MAX_CORES) {
> +		dev_warn(&pdev->dev, "Card has more active cores than supported by driver, %u core(s) will be ignored\n",
> +			 card->active_cores - ESD_PCI402_MAX_CORES);
> +		card->active_cores = ESD_PCI402_MAX_CORES;
> +	}
> +
> +	card->core_frequency = ov_read32(card, OFFS_OV_CANCORE_FREQ);
> +	card->timestamp_frequency = ov_read32(card, OFFS_OV_TS_FREQ_LO);
> +
> +	if (dump_infos) {
> +		dev_info(&pdev->dev, "\"dump_infos\" selected:");
> +
> +		temp = ov_read32(card, OFFS_OV_PROBE);
> +		dev_info(&pdev->dev, "  Probe register: 0x%.8x\n", temp);
> +		dev_info(&pdev->dev, "  Features: 0x%.4x\n", card->features);
> +		dev_info(&pdev->dev, "  FPGA Version: 0x%.4x\n", card->version);
> +		dev_info(&pdev->dev, "  Strappings: 0x%.4x\n",
> +			 ov_read32(card, OFFS_OV_INFO) >> 16);
> +		dev_info(&pdev->dev, "  Active cores: %u\n",
> +			 card->active_cores);
> +		dev_info(&pdev->dev, "  Total cores: %u\n",
> +			 card->total_cores);
> +		dev_info(&pdev->dev, "  Core frequency: %u\n",
> +			 card->core_frequency);
> +		dev_info(&pdev->dev, "  Timestamp frequency: %u\n",
> +			 card->timestamp_frequency);
> +	}
> +
> +	if (card->version < ESD_PCI402_VERSION_MIN) {
> +		dev_err(&pdev->dev,
> +			"FPGA version (0x%.4x) outdated, please update\n",
> +			card->version);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int esd_pci402_init_interrupt(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	int err;
> +
> +	err = pci_enable_msi(pdev);
> +	if (!err) {
> +		err = esd_pci402_set_msiconfig(pdev);
> +		if (!err) {
> +			card->msi_enabled = 1;
> +			ov_set_bits(card, OFFS_OV_MODE,
> +				    REG_OV_MODE_MASK_MSI_ENABLE);
> +			dev_info(&pdev->dev, "MSI enabled\n");
> +		}
> +	}
> +
> +	err = request_irq(pdev->irq, esd_pci402_interrupt,
> +			  IRQF_SHARED, DRV_NAME, pdev);
> +	if (err)
> +		goto failure_msidis;
> +
> +	iowrite32(1, card->addr_pciep + OFFS_PCIEP_INT_ENABLE);
> +
> +	return 0;
> +
> +failure_msidis:
> +	if (card->msi_enabled) {
> +		ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_MSI_ENABLE);
> +		pci_disable_msi(pdev);
> +		card->msi_enabled = 0;
> +	}
> +
> +	return err;
> +}
> +
> +static void esd_pci402_finish_interrupt(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +
> +	iowrite32(0, card->addr_pciep + OFFS_PCIEP_INT_ENABLE);
> +	free_irq(pdev->irq, pdev);
> +
> +	if (card->msi_enabled) {
> +		ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_MSI_ENABLE);
> +		pci_disable_msi(pdev);
> +		card->msi_enabled = 0;
> +	}
> +}
> +
> +static int esd_pci402_init_dma(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	int err;
> +	int i;
> +
> +	err = pci_set_consistent_dma_mask(pdev, ESD_PCI402_DMA_MASK);
> +	if (err) {
> +		dev_err(&pdev->dev, "dma set mask failed!");
> +		return err;
> +	}
> +
> +	card->dma_buf = pci_alloc_consistent(pdev, ESD_PCI402_DMA_SIZE,
> +					     &card->dma_hnd);
> +	if (!card->dma_buf) {
> +		dev_err(&pdev->dev, "dma alloc failed!");
> +		return -ENOMEM;
> +	}
> +
> +	/* Setting card/core "bm_" pointers according to this DMA buffer layout:
> +	 * +-----------------------+
> +	 * | FIFO Card/Overview    |
> +	 * +-----------------------+
> +	 * | FIFO Core0            |
> +	 * +-----------------------+
> +	 * | FIFO Core1            |
> +	 * +-----------------------+
> +	 * | ...                   |
> +	 * +-----------------------+
> +	 * | irq_cnt Card/Overview |
> +	 * +-----------------------+
> +	 * | irq_cnt Core0         |
> +	 * +-----------------------+
> +	 * | irq_cnt Core1         |
> +	 * +-----------------------+
> +	 * | ...                   |
> +	 * +-----------------------+
> +	 */
> +	card->bm_fifo = card->dma_buf;
> +	card->bm_irq_cnt = card->dma_buf + (card->total_cores + 1)
> +						* ESD_PCI402_DMA_FIFOSIZE;
> +
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +
> +		core->bm_fifo = card->dma_buf + (i + 1)
> +						* ESD_PCI402_DMA_FIFOSIZE;
> +		core->bm_irq_cnt = card->bm_irq_cnt + 1 + i;
> +	}
> +
> +	iowrite32((u32)card->dma_hnd,
> +		  card->addr_pciep + OFFS_PCIEP_BM_ADDR_LO);
> +	iowrite32((u32)(card->dma_hnd >> 32),
> +		  card->addr_pciep + OFFS_PCIEP_BM_ADDR_HI);
> +
> +	ov_set_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_BM_ENABLE);
> +
> +	return 0;
> +}
> +
> +static void esd_pci402_finish_dma(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	int i;
> +
> +	ov_clear_bits(card, OFFS_OV_MODE, REG_OV_MODE_MASK_BM_ENABLE);
> +
> +	iowrite32(0, card->addr_pciep + OFFS_PCIEP_BM_ADDR_LO);
> +	iowrite32(0, card->addr_pciep + OFFS_PCIEP_BM_ADDR_HI);
> +
> +	card->bm_fifo = NULL;
> +	card->bm_irq_cnt = NULL;
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +
> +		core->bm_fifo = NULL;
> +		core->bm_irq_cnt = NULL;
> +	}
> +
> +	pci_free_consistent(pdev, ESD_PCI402_DMA_SIZE, card->dma_buf,
> +			    card->dma_hnd);
> +	card->dma_buf = NULL;
> +}
> +
> +static int esd_pci402_init_cores(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	const struct can_bittiming_const *bittiming_const;
> +	int num_register_ok = 0;
> +	int err;
> +	int i;
> +
> +	if (card->version < ESD_PCI402_VERSION_NEWBTR)
> +		bittiming_const = &esd_pci402_bittiming_const_old;
> +	else
> +		bittiming_const = &esd_pci402_bittiming_const;
> +
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +		struct esd_pci402_net_priv *priv;
> +		struct net_device *netdev;
> +		u32 fifo_config;
> +
> +		core->addr = card->addr_overview +
> +				(i + 1) * ESD_PCI402_IO_LEN_CORE;
> +
> +		fifo_config = core_read32(core, OFFS_CORE_TXFIFO_CONFIG);
> +		core->tx_fifo_size = (u8)(fifo_config >> 24);
> +		BUG_ON(core->tx_fifo_size <= 1);
> +
> +		netdev = alloc_candev(sizeof(*priv), core->tx_fifo_size);
> +		if (!netdev) {
> +			err = -ENOMEM;
> +			goto failure;
> +		}
> +		core->net_dev = netdev;
> +
> +		netdev->flags |= IFF_ECHO;
> +		netdev->netdev_ops = &esd_pci402_netdev_ops;
> +		SET_NETDEV_DEV(netdev, &pdev->dev);
> +
> +		priv = netdev_priv(netdev);
> +		priv->can.state = CAN_STATE_STOPPED;
> +		priv->can.ctrlmode_supported = CAN_CTRLMODE_LISTENONLY |
> +						CAN_CTRLMODE_LOOPBACK |
> +						CAN_CTRLMODE_BERR_REPORTING;
> +		priv->can.clock.freq = card->core_frequency / 2;
> +		priv->can.bittiming_const = bittiming_const;
> +		priv->can.do_set_bittiming = esd_pci402_set_bittiming;
> +		priv->can.do_set_mode = esd_pci402_set_mode;
> +		priv->can.do_get_berr_counter = esd_pci402_get_berr_counter;
> +
> +		priv->card = card;
> +		priv->core = core;
> +
> +		err = register_candev(netdev);
> +		if (err)
> +			goto failure;
> +
> +		netdev_info(netdev, "registered\n");
> +		num_register_ok++;
> +	}
> +
> +	return 0;
> +
> +failure:
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +
> +		if (!core->net_dev)
> +			continue;
> +
> +		if (i < num_register_ok) {
> +			netdev_info(core->net_dev, "unregistering...\n");
> +			unregister_candev(core->net_dev);
> +		}
> +
> +		free_candev(core->net_dev);
> +		core->net_dev = NULL;
> +	}
> +
> +	return err;
> +}
> +
> +static void esd_pci402_finish_cores(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +	int i;
> +
> +	for (i = 0; i < card->active_cores; ++i) {
> +		struct esd_pci402_cancore *core = &card->core[i];
> +
> +		unregister_candev(core->net_dev);
> +		free_candev(core->net_dev);
> +		core->net_dev = NULL;
> +	}
> +}
> +
> +static int esd_pci402_probe(struct pci_dev *pdev,
> +			    const struct pci_device_id *ent)
> +{
> +	struct esd_pci402_card *card = NULL;
> +	int err;
> +
> +	BUILD_BUG_ON(sizeof(struct bm_msg) != ESD_PCI402_DMA_FIFO_ITEMSIZE);
> +
> +	err = pci_enable_device(pdev);
> +	if (err)
> +		goto failure;
> +
> +	pci_set_master(pdev);
> +
> +	card = kzalloc(sizeof(*card), GFP_KERNEL);
> +	if (!card)
> +		goto failure;
> +
> +	pci_set_drvdata(pdev, card);
> +
> +	err = pci_request_regions(pdev, DRV_NAME);
> +	if (err)
> +		goto failure_disable_pci;
> +
> +	card->addr = pci_iomap(pdev, ESD_PCI402_BAR, ESD_PCI402_IO_LEN_TOTAL);
> +	if (!card->addr) {
> +		err = -ENOMEM;
> +		goto failure_release_regions;
> +	}
> +
> +	err = esd_pci402_init_card(pdev);
> +	if (err)
> +		goto failure_unmap;
> +
> +	err = esd_pci402_init_dma(pdev);
> +	if (err)
> +		goto failure_unmap;
> +
> +	err = esd_pci402_init_interrupt(pdev);
> +	if (err)
> +		goto failure_finish_dma;
> +
> +	err = esd_pci402_init_cores(pdev);
> +	if (err)
> +		goto failure_finish_interrupt;
> +
> +	return 0;
> +
> +failure_finish_interrupt:
> +	esd_pci402_finish_interrupt(pdev);
> +
> +failure_finish_dma:
> +	esd_pci402_finish_dma(pdev);
> +
> +failure_unmap:
> +	pci_iounmap(pdev, card->addr);
> +
> +failure_release_regions:
> +	pci_release_regions(pdev);
> +
> +failure_disable_pci:
> +	pci_disable_device(pdev);
> +
> +failure:
> +	kfree(card);
> +
> +	return err;
> +}
> +
> +static void esd_pci402_remove(struct pci_dev *pdev)
> +{
> +	struct esd_pci402_card *card = pci_get_drvdata(pdev);
> +
> +	esd_pci402_finish_interrupt(pdev);
> +	esd_pci402_finish_cores(pdev);
> +	esd_pci402_finish_dma(pdev);
> +	pci_iounmap(pdev, card->addr);
> +	pci_release_regions(pdev);
> +	pci_disable_device(pdev);
> +	kfree(card);
> +}
> +
> +static const struct pci_device_id esd_pci402_tbl[] = {
> +	{ PCI_VENDOR_ID_ESDGMBH, ESD_PCI_DEVICE_ID_PCI402,
> +			PCI_ANY_ID, PCI_ANY_ID, },
> +	{ 0, }
> +};
> +
> +static struct pci_driver esd_pci402_driver = {
> +	.name = DRV_NAME,
> +	.id_table = esd_pci402_tbl,
> +	.probe = esd_pci402_probe,
> +	.remove = esd_pci402_remove,
> +};
> +
> +module_pci_driver(esd_pci402_driver);
> +
> +MODULE_DESCRIPTION("linux-can driver for esd CAN PCIe/402 card");
> +MODULE_AUTHOR("Thomas Körper <Thomas.Koerper@esd.eu>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/net/can/esd/esd402.h b/drivers/net/can/esd/esd402.h
> new file mode 100644
> index 0000000..509afd4
> --- /dev/null
> +++ b/drivers/net/can/esd/esd402.h
> @@ -0,0 +1,332 @@
> +/* Copyright (C) 2014 esd electronic system design gmbh
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the version 2 of the GNU General Public License
> + * as published by the Free Software Foundation
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/kernel.h>
> +
> +#define ESD_PCI_DEVICE_ID_PCI402	0x0402
> +
> +#define ESD_PCI402_VERSION_MIN		0x0025
> +#define ESD_PCI402_VERSION_NEWBTR	0x0032
> +
> +#define ESD_PCI402_MAX_CORES		4
> +#define ESD_PCI402_BAR			0
> +#define ESD_PCI402_IO_OV_OFFS		0
> +#define ESD_PCI402_IO_PCIEP_OFFS	0x10000
> +#define ESD_PCI402_IO_LEN_TOTAL		0x20000
> +#define ESD_PCI402_IO_LEN_CORE		0x2000
> +#define ESD_PCI402_PCICFG_MSICAP_CSR	0x52
> +#define ESD_PCI402_PCICFG_MSICAP_ADDR	0x54
> +#define ESD_PCI402_PCICFG_MSICAP_DATA	0x5c

Is this PCI402 or esdAAC specific?

> +
> +#define ESD_PCI402_DMA_MASK		(DMA_BIT_MASK(32) & 0xffff0000)
> +#define ESD_PCI402_DMA_SIZE		ALIGN(0x10000, PAGE_SIZE)
> +#define ESD_PCI402_DMA_FIFO_ITEMCOUNT	256
> +#define ESD_PCI402_DMA_FIFO_ITEMSIZE	32
> +#define ESD_PCI402_DMA_FIFOSIZE		\
> +		(ESD_PCI402_DMA_FIFO_ITEMCOUNT * ESD_PCI402_DMA_FIFO_ITEMSIZE)
> +
> +enum bm_msg_id {
> +	BM_MSG_ID_RXTXDONE = 0x01,
> +	BM_MSG_ID_TXABORT = 0x02,
> +	BM_MSG_ID_OVERRUN = 0x03,
> +	BM_MSG_ID_BUSERR = 0x04,
> +	BM_MSG_ID_ERRPASSIVE = 0x05,
> +	BM_MSG_ID_ERRWARN = 0x06,
> +	BM_MSG_ID_TIMESLICE = 0x07,
> +	BM_MSG_ID_HWTIMER = 0x08,
> +	BM_MSG_ID_HOTPLUG = 0x09,
> +	BM_MSG_ID_CANFDDATA0 = 0x0a,
> +	BM_MSG_ID_CANFDDATA1 = 0x0b
> +};
> +
> +struct bm_msg_rxtxdone {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u8 reserved1[2];
> +	u8 txtsfifo_level;
> +	u8 reserved2[3];
> +	u32 id;
> +	union {
> +		struct {
> +			u8 len;
> +			u8 reserved0;
> +			u8 bits;
> +			u8 state;
> +		} rxtx;
> +		struct {
> +			u8 len;
> +			u8 msg_lost;
> +			u8 bits;
> +			u8 state;
> +		} rx;
> +		struct {
> +			u8 len;
> +			u8 txfifo_idx;
> +			u8 bits;
> +			u8 state;
> +		} tx;
> +	} dlc;
> +	u8 data[8];
> +	u64 timestamp;
> +} __packed;
> +
> +struct bm_msg_txabort {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u16 abort_mask;
> +	u8 txtsfifo_level;
> +	u8 reserved2[1];
> +	u16 abort_mask_txts;
> +	u64 ts;
> +	u32 reserved3[4];
> +} __packed;
> +
> +struct bm_msg_overrun {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u8 lost_cnt;
> +	u8 reserved1;
> +	u8 txtsfifo_level;
> +	u8 reserved2[3];
> +	u64 ts;
> +	u32 reserved3[4];
> +} __packed;
> +
> +struct bm_msg_buserr {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u8 ecc;
> +	u8 reserved1;
> +	u8 txtsfifo_level;
> +	u8 reserved2[3];
> +	u64 ts;
> +	u32 reg_status;
> +	u32 reg_btr;
> +	u32 reserved3[2];
> +} __packed;
> +
> +struct bm_msg_errstatechange {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u8 reserved1[2];
> +	u8 txtsfifo_level;
> +	u8 reserved2[3];
> +	u64 ts;
> +	u32 reg_status;
> +	u32 reserved3[3];
> +} __packed;
> +
> +struct bm_msg_timeslice {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u8 reserved1[2];
> +	u8 txtsfifo_level;
> +	u8 reserved2[3];
> +	u64 ts;
> +	u32 reserved3[4];
> +} __packed;
> +
> +struct bm_msg_hwtimer {
> +	u8 msg_id;
> +	u8 reserved1[3];
> +	u32 reserved2[1];
> +	u64 timer;
> +	u32 reserved3[4];
> +} __packed;
> +
> +struct bm_msg_hotplug {
> +	u8 msg_id;
> +	u8 reserved1[3];
> +	u32 reserved2[7];
> +} __packed;
> +
> +struct bm_msg_canfddata {
> +	u8 msg_id;
> +	u8 reserved1[3];
> +	union {
> +		u8 ui8[28];
> +		u32 ui32[7];
> +	} d;
> +} __packed;
> +
> +struct bm_msg {
> +	union {
> +		u8 msg_id;
> +		struct bm_msg_rxtxdone rxtxdone;
> +		struct bm_msg_canfddata canfddata;
> +		struct bm_msg_txabort txabort;
> +		struct bm_msg_overrun overrun;
> +		struct bm_msg_buserr buserr;
> +		struct bm_msg_errstatechange errstatechange;
> +		struct bm_msg_timeslice timeslice;
> +		struct bm_msg_hwtimer hwtimer;
> +	} u;
> +};
> +
> +struct esd_pci402_cancore {
> +	void __iomem *addr;
> +	struct net_device *net_dev;
> +
> +	const struct bm_msg *bm_fifo;
> +	/* Bits0..7: bm_fifo head index */
> +	const u32 *bm_irq_cnt;
> +	/* Local copy of bm_irq_cnt */
> +	u32 irq_cnt;
> +	u32 msg_fifo_tail;
> +
> +	u8 tx_fifo_size;
> +	u8 tx_fifo_head;
> +	u8 tx_fifo_tail;
> +};
> +
> +struct esd_pci402_card {
> +	/* Actually mapped io space, all other iomem derived from this */
> +	void __iomem *addr;
> +
> +	void __iomem *addr_pciep;
> +	void __iomem *addr_overview;
> +
> +	void *dma_buf;
> +	dma_addr_t dma_hnd;
> +
> +	const struct bm_msg *bm_fifo;
> +	/* Bits0..7: bm_fifo head index */
> +	const u32 *bm_irq_cnt;
> +	/* Local copy of bm_irq_cnt */
> +	u32 irq_cnt;
> +
> +	struct esd_pci402_cancore core[ESD_PCI402_MAX_CORES];
> +
> +	u32 timestamp_frequency;
> +	u32 core_frequency;
> +	u16 version;
> +	u16 features;
> +	u8 total_cores;
> +	u8 active_cores;
> +
> +	int msi_enabled;
> +};
> +
> +struct esd_pci402_net_priv {
> +	struct can_priv can; /* must be the first member! */
> +	struct esd_pci402_card *card;
> +	struct esd_pci402_cancore *core;

esd_pci402_cancore == esd_aac_core ??

> +};
> +
> +#define OFFS_CORE_CTRL_MODE		0x0000
> +#define OFFS_CORE_STATUS_IRQ		0x0008
> +#define OFFS_CORE_BTR			0x000c
> +#define OFFS_CORE_BTR_FD		0x0010
> +#define OFFS_CORE_BTR_FDF		0x0014
> +#define OFFS_CORE_STATUS		0x0030
> +#define OFFS_CORE_TXFIFO_CONFIG		0x0048
> +#define OFFS_CORE_TXFIFO_STATUS		0x004c
> +#define OFFS_CORE_TX_STATUS_IRQ		0x0050
> +#define OFFS_CORE_TX_ABORT_MASK		0x0054
> +#define OFFS_CORE_BM_IRQ_COUNTER	0x0070
> +#define OFFS_CORE_TXFIFO_ID		0x00c0
> +#define OFFS_CORE_TXFIFO_DLC		0x00c4
> +#define OFFS_CORE_TXFIFO_DATA_0		0x00c8
> +#define OFFS_CORE_TXFIFO_DATA_1		0x00cc
> +
> +#define OFFS_OV_PROBE			0x0000
> +#define OFFS_OV_VERSION			0x0004
> +#define OFFS_OV_INFO			0x0008
> +#define OFFS_OV_CANCORE_FREQ		0x000c
> +#define OFFS_OV_TS_FREQ_LO		0x0010
> +#define OFFS_OV_TS_FREQ_HI		0x0014
> +#define OFFS_OV_IRQ_STATUS_CORES	0x0018
> +#define OFFS_OV_TS_CURR_LO		0x001c
> +#define OFFS_OV_TS_CURR_HI		0x0020
> +#define OFFS_OV_IRQ_STATUS		0x0028
> +#define OFFS_OV_MODE			0x002c
> +#define OFFS_OV_BM_IRQ_COUNTER		0x0070
> +#define OFFS_OV_BM_IRQ_MASK		0x0074
> +#define OFFS_OV_MSI_DATA		0x0080
> +#define OFFS_OV_MSI_ADDRESSOFFSET	0x0084

-> esdaac.h

> +
> +#define OFFS_PCIEP_INT_ENABLE		0x0050
> +#define OFFS_PCIEP_BM_ADDR_LO		0x1000
> +#define OFFS_PCIEP_BM_ADDR_HI		0x1004
> +#define OFFS_PCIEP_MSI_ADDR_LO		0x1008
> +#define OFFS_PCIEP_MSI_ADDR_HI		0x100c
> +
> +#define REG_OV_MODE_MASK_ENDIAN_LITTLE  0x00000001
> +#define REG_OV_MODE_MASK_BM_ENABLE      0x00000002
> +#define REG_OV_MODE_MASK_MODE_LED       0x00000004
> +#define REG_OV_MODE_MASK_TIMER          0x00000070
> +#define REG_OV_MODE_MASK_TIMER_ENABLE   0x00000010
> +#define REG_OV_MODE_MASK_TIMER_ONE_SHOT 0x00000020
> +#define REG_OV_MODE_MASK_TIMER_ABSOLUTE 0x00000040
> +#define REG_OV_MODE_MASK_TS_SRC         0x00000180
> +#define REG_OV_MODE_MASK_I2C_ENABLE     0x00000800
> +#define REG_OV_MODE_MASK_MSI_ENABLE     0x00004000
> +#define REG_OV_MODE_MASK_FPGA_RESET     0x80000000
> +
> +#define BM_IRQ_UNMASK_ALL               0x55555555
> +#define BM_IRQ_MASK_ALL                 0xaaaaaaaa
> +#define BM_IRQ_MASK                     0x00000002
> +#define BM_IRQ_UNMASK                   0x00000001
> +
> +#define REG_CONTROL_IDX_MODE_RESETMODE  0
> +#define REG_CONTROL_IDX_MODE_LOM        1
> +#define REG_CONTROL_IDX_MODE_STM        2
> +#define REG_CONTROL_IDX_MODE_TRANSEN    5
> +#define REG_CONTROL_IDX_MODE_TS         6
> +#define REG_CONTROL_IDX_MODE_SCHEDULE   7
> +#define REG_CONTROL_MASK_MODE_RESETMODE BIT(REG_CONTROL_IDX_MODE_RESETMODE)
> +#define REG_CONTROL_MASK_MODE_LOM       BIT(REG_CONTROL_IDX_MODE_LOM)
> +#define REG_CONTROL_MASK_MODE_STM       BIT(REG_CONTROL_IDX_MODE_STM)
> +#define REG_CONTROL_MASK_MODE_TRANSEN   BIT(REG_CONTROL_IDX_MODE_TRANSEN)
> +#define REG_CONTROL_MASK_MODE_TS        BIT(REG_CONTROL_IDX_MODE_TS)
> +#define REG_CONTROL_MASK_MODE_SCHEDULE  BIT(REG_CONTROL_IDX_MODE_SCHEDULE)
> +
> +#define REG_CONTROL_IDX_IE_RXTX         8
> +#define REG_CONTROL_IDX_IE_TXERROR      9
> +#define REG_CONTROL_IDX_IE_ERROR        10
> +#define REG_CONTROL_IDX_IE_OVERRUN      11
> +#define REG_CONTROL_IDX_IE_TSI          12
> +#define REG_CONTROL_IDX_IE_ERRPASS      13
> +#define REG_CONTROL_IDX_IE_BUSERR       15
> +#define REG_CONTROL_MASK_IE_RXTX        BIT(REG_CONTROL_IDX_IE_RXTX)
> +#define REG_CONTROL_MASK_IE_TXERROR     BIT(REG_CONTROL_IDX_IE_TXERROR)
> +#define REG_CONTROL_MASK_IE_ERROR       BIT(REG_CONTROL_IDX_IE_ERROR)
> +#define REG_CONTROL_MASK_IE_OVERRUN     BIT(REG_CONTROL_IDX_IE_OVERRUN)
> +#define REG_CONTROL_MASK_IE_TSI         BIT(REG_CONTROL_IDX_IE_TSI)
> +#define REG_CONTROL_MASK_IE_ERRPASS     BIT(REG_CONTROL_IDX_IE_ERRPASS)
> +#define REG_CONTROL_MASK_IE_BUSERR      BIT(REG_CONTROL_IDX_IE_BUSERR)
> +
> +#define REG_STATUS_IDX_STATUS_DOS       16
> +#define REG_STATUS_IDX_STATUS_ES        17
> +#define REG_STATUS_IDX_STATUS_EP        18
> +#define REG_STATUS_IDX_STATUS_BS        19
> +#define REG_STATUS_IDX_STATUS_RBS       20
> +#define REG_STATUS_IDX_STATUS_RS        21
> +#define REG_STATUS_MASK_STATUS_DOS      BIT(REG_STATUS_IDX_STATUS_DOS)
> +#define REG_STATUS_MASK_STATUS_ES       BIT(REG_STATUS_IDX_STATUS_ES)
> +#define REG_STATUS_MASK_STATUS_EP       BIT(REG_STATUS_IDX_STATUS_EP)
> +#define REG_STATUS_MASK_STATUS_BS       BIT(REG_STATUS_IDX_STATUS_BS)
> +#define REG_STATUS_MASK_STATUS_RBS      BIT(REG_STATUS_IDX_STATUS_RBS)
> +#define REG_STATUS_MASK_STATUS_RS       BIT(REG_STATUS_IDX_STATUS_RS)
> +
> +#define ESD_PCI402_BM_LENFLAG_TX	0x20
> +
> +/* ecc value of PCI402 equals SJA1000's ECC register */
> +#define ESD_PCI402_ECC_SEG		0x1f
> +#define ESD_PCI402_ECC_DIR		0x20
> +#define ESD_PCI402_ECC_BIT		0x00
> +#define ESD_PCI402_ECC_FORM		0x40
> +#define ESD_PCI402_ECC_STUFF		0x80
> +#define ESD_PCI402_ECC_MASK		0xc0
> 

Regards,
Oliver

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

* AW: [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
  2014-10-20 16:26 ` Oliver Hartkopp
@ 2014-10-21  7:46   ` Thomas Körper
  2014-10-21  8:27     ` Oliver Hartkopp
  0 siblings, 1 reply; 6+ messages in thread
From: Thomas Körper @ 2014-10-21  7:46 UTC (permalink / raw)
  To: Oliver Hartkopp, linux-can@vger.kernel.org

Hello Oliver,

thanks for the feedback - an updated patch will follow. Remaining comments/questions below.

> > Signed-off-by: Thomas Körper <Thomas.Koerper@esd.eu>
> 
> Capital letters in mail addresses - ugh.

At least in the Signed-off string I will/can fix it :)
Btw, what about the umlaut?, haven't found infos on how source may/shall be encoded.

> > ---
> >  drivers/net/can/Kconfig      |    2 +
> >  drivers/net/can/Makefile     |    1 +
> >  drivers/net/can/esd/Kconfig  |    6 +
> >  drivers/net/can/esd/Makefile |    4 +
> >  drivers/net/can/esd/esd402.c | 1115 
> > ++++++++++++++++++++++++++++++++++++++++++
> >  drivers/net/can/esd/esd402.h |  332 +++++++++++++
> >  6 files changed, 1460 insertions(+)
> >  create mode 100644 drivers/net/can/esd/Kconfig  create mode 100644 
> > drivers/net/can/esd/Makefile  create mode 100644 
> > drivers/net/can/esd/esd402.c  create mode 100644 
> > drivers/net/can/esd/esd402.h
> 
> As the esdACC core is available in several hardware cards I would suggest to split your files in another way.
> 
> E.g. introduce a esdacc.h include and esdacc.c which contains the esdACC specific stuff (compare to sja1000.[ch]).

Good idea, esd402.h will become esdacc.h and another source esdacc.c will be added.

> Does your driver support the PCI400 too?
> http://esd.eu/en/products/can-pci400
> 
> You because I have one of it and could do some testing then.

No. But if you'd like to have a PCIe/402 for testing this shouldn't be problem, I guess.

> >  config CAN_DEBUG_DEVICES
> > diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 
> > 1697f22..5b8681e 100644
> > --- a/drivers/net/can/Makefile
> > +++ b/drivers/net/can/Makefile
> > @@ -13,6 +13,7 @@ can-dev-$(CONFIG_CAN_LEDS)	+= led.o
> >  obj-y				+= spi/
> >  obj-y				+= usb/
> >  obj-y				+= softing/
> > +obj-y				+= esd/
> 
> Does it make sense to name the directory 'esdacc' ?
> There is ESD hardware in drivers/net/can/usb too.
> 
> With esdacc we would have a CAN IP core specific directory like we have for the sja1000, c_can, etc ...

I don't know. I was thinking of more (also none-esdACC) esd cards that might be added in the future.

> > +	  Support for CAN PCIe/402 cards from esd electronic system design 
> > +gmbh
> 
> GmbH ??

No...

> > +#define DRV_NAME			"esd402_driver"
> 
> esd402_pcie
> 
> ?

But a PCI/402 is under development, so what about esd402_pci?
 
> > +
> > +static inline u32 ov_read32(struct esd_pci402_card *card, unsigned 
> > +short offs) {
> > +	return ioread32be(card->addr_overview + offs); }
> > +
> > +static inline void ov_write32(struct esd_pci402_card *card,
> > +			      unsigned short offs, u32 v)
> > +{
> > +	iowrite32be(v, card->addr_overview + offs); }
> 
> Does it make sense to put these access functions into the same indirection as you can see in the sja1000 or c_can driver?

You mean the priv pointer instead of my card pointer? Think it should become some acc pointer when moved to esdacc.c.


Regards,
	Thomas

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

* Re: AW: [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
  2014-10-21  7:46   ` AW: " Thomas Körper
@ 2014-10-21  8:27     ` Oliver Hartkopp
  2014-10-21  9:41       ` AW: " Thomas Körper
  2014-10-21 18:39       ` Robert Schwebel
  0 siblings, 2 replies; 6+ messages in thread
From: Oliver Hartkopp @ 2014-10-21  8:27 UTC (permalink / raw)
  To: Thomas Körper, linux-can@vger.kernel.org

On 21.10.2014 09:46, Thomas Körper wrote:
>>> Signed-off-by: Thomas Körper <Thomas.Koerper@esd.eu>
>>
>> Capital letters in mail addresses - ugh.
>
> At least in the Signed-off string I will/can fix it :)
> Btw, what about the umlaut?, haven't found infos on how source may/shall be encoded.

Some people use UTF-8 and many setups display strange things afterwards.
If you don't mind try 'oe' like in the mail address :-)


>> E.g. introduce a esdacc.h include and esdacc.c which contains the esdACC specific stuff (compare to sja1000.[ch]).
>
> Good idea, esd402.h will become esdacc.h and another source esdacc.c will be added.

cool

>
>> Does your driver support the PCI400 too?
>> http://esd.eu/en/products/can-pci400
>>
>> You because I have one of it and could do some testing then.
>
> No. But if you'd like to have a PCIe/402 for testing this shouldn't be problem, I guess.

Thanks but I just lack some PCIe capable PC :-)
Do you plan to support the PCI400 later?


>> Does it make sense to name the directory 'esdacc' ?
>> There is ESD hardware in drivers/net/can/usb too.
>>
>> With esdacc we would have a CAN IP core specific directory like we have for the sja1000, c_can, etc ...
>
> I don't know. I was thinking of more (also none-esdACC) esd cards that might be added in the future.
>

ok. When you support active cards (like the softing cards) naming it esd fits 
indeed. But SJA1000 based cards (from ESD) should still go into the sja1000 tree.

>>> +	  Support for CAN PCIe/402 cards from esd electronic system design
>>> +gmbh
>>
>> GmbH ??
>
> No...

Ah - I've seen it on your website.
'gmbh' looks like a part of a brand name.

Together with the website URL it looks fine.

>
>>> +#define DRV_NAME			"esd402_driver"
>>
>> esd402_pcie
>>
>> ?
>
> But a PCI/402 is under development, so what about esd402_pci?

ACK.

>
>>> +
>>> +static inline u32 ov_read32(struct esd_pci402_card *card, unsigned
>>> +short offs) {
>>> +	return ioread32be(card->addr_overview + offs); }
>>> +
>>> +static inline void ov_write32(struct esd_pci402_card *card,
>>> +			      unsigned short offs, u32 v)
>>> +{
>>> +	iowrite32be(v, card->addr_overview + offs); }
>>
>> Does it make sense to put these access functions into the same indirection as you can see in the sja1000 or c_can driver?
>
> You mean the priv pointer instead of my card pointer? Think it should become some acc pointer when moved to esdacc.c.

Yes. Exactly this was my intention.

Regards,
Oliver


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

* AW: AW: [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
  2014-10-21  8:27     ` Oliver Hartkopp
@ 2014-10-21  9:41       ` Thomas Körper
  2014-10-21 18:39       ` Robert Schwebel
  1 sibling, 0 replies; 6+ messages in thread
From: Thomas Körper @ 2014-10-21  9:41 UTC (permalink / raw)
  To: Oliver Hartkopp, linux-can@vger.kernel.org

Hi Oliver,

> Thanks but I just lack some PCIe capable PC :-) Do you plan to support the PCI400 later?

As there'll be a PCI402 we currently don't have such plans.


Regards,
    Thomas



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

* Re: AW: [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
  2014-10-21  8:27     ` Oliver Hartkopp
  2014-10-21  9:41       ` AW: " Thomas Körper
@ 2014-10-21 18:39       ` Robert Schwebel
  1 sibling, 0 replies; 6+ messages in thread
From: Robert Schwebel @ 2014-10-21 18:39 UTC (permalink / raw)
  To: Oliver Hartkopp; +Cc: Thomas Körper, linux-can@vger.kernel.org

On Tue, Oct 21, 2014 at 10:27:52AM +0200, Oliver Hartkopp wrote:
> On 21.10.2014 09:46, Thomas Körper wrote:
> >>>Signed-off-by: Thomas Körper <Thomas.Koerper@esd.eu>
> >>
> >>Capital letters in mail addresses - ugh.
> >
> >At least in the Signed-off string I will/can fix it :)
> >Btw, what about the umlaut?, haven't found infos on how source may/shall be encoded.
> 
> Some people use UTF-8 and many setups display strange things afterwards.
> If you don't mind try 'oe' like in the mail address :-)

The Linux kernel is UTF-8, with support for Klingon language support:
http://lxr.free-electrons.com/source/Documentation/unicode.txt#L1

rsc
-- 
Pengutronix e.K.                           |                             |
Industrial Linux Solutions                 | http://www.pengutronix.de/  |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

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

end of thread, other threads:[~2014-10-21 18:39 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-10-13 11:23 [PATCH 1/1] can: Add support for esd CAN PCIe/402 card Thomas Körper
2014-10-20 16:26 ` Oliver Hartkopp
2014-10-21  7:46   ` AW: " Thomas Körper
2014-10-21  8:27     ` Oliver Hartkopp
2014-10-21  9:41       ` AW: " Thomas Körper
2014-10-21 18:39       ` Robert Schwebel

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).