linux-can.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
@ 2015-03-16 12:15 Thomas Körper
  2015-03-16 12:46 ` Andri Yngvason
  2015-03-17 21:27 ` Marc Kleine-Budde
  0 siblings, 2 replies; 9+ messages in thread
From: Thomas Körper @ 2015-03-16 12:15 UTC (permalink / raw)
  To: linux-can; +Cc: Thomas Körper

This patch adds support for the CAN PCIe/402 card from esd electronic
system design gmbh: FPGA based CAN controller, supporting up to 4
nets, MSI and bus mastering. See http://esd.eu/en/products/can-pcie402
for details.

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      |   7 +
 drivers/net/can/esd/Makefile     |   3 +
 drivers/net/can/esd/esd402_pci.c | 453 +++++++++++++++++++++++++++++
 drivers/net/can/esd/esdacc.c     | 600 +++++++++++++++++++++++++++++++++++++++
 drivers/net/can/esd/esdacc.h     | 428 ++++++++++++++++++++++++++++
 7 files changed, 1494 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_pci.c
 create mode 100644 drivers/net/can/esd/esdacc.c
 create mode 100644 drivers/net/can/esd/esdacc.h

diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
index 58808f6..54d9ce8 100644
--- a/drivers/net/can/Kconfig
+++ b/drivers/net/can/Kconfig
@@ -153,6 +153,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 c533c62..c691233 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..cebd5ae
--- /dev/null
+++ b/drivers/net/can/esd/Kconfig
@@ -0,0 +1,7 @@
+config CAN_ESD_402_PCI
+	tristate "esd gmbh CAN PCIe/402 support"
+	default y
+	depends on PCI && HAS_DMA
+	---help---
+	  Support for CAN PCIe/402 card from esd electronic system design gmbh
+	  (http://esd.eu/en)
diff --git a/drivers/net/can/esd/Makefile b/drivers/net/can/esd/Makefile
new file mode 100644
index 0000000..80d970e
--- /dev/null
+++ b/drivers/net/can/esd/Makefile
@@ -0,0 +1,3 @@
+esd_402_pci-y := esdacc.o esd402_pci.o
+
+obj-$(CONFIG_CAN_ESD_402_PCI) += esd_402_pci.o
diff --git a/drivers/net/can/esd/esd402_pci.c b/drivers/net/can/esd/esd402_pci.c
new file mode 100644
index 0000000..d2b5600
--- /dev/null
+++ b/drivers/net/can/esd/esd402_pci.c
@@ -0,0 +1,453 @@
+/* Copyright (C) 2015 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/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 "esdacc.h"
+
+#define DRV_NAME			"esd_402_pci"
+
+#define ESD_PCI_DEVICE_ID_PCIE402	0x0402
+
+#define PCI402_FPGA_VER_MIN		0x0025
+#define PCI402_FPGA_VER_NEWBTR		0x0032
+#define PCI402_MAX_CORES		4
+#define PCI402_BAR			0
+#define PCI402_IO_OV_OFFS		0
+#define PCI402_IO_PCIEP_OFFS		0x10000
+#define PCI402_IO_LEN_TOTAL		0x20000
+#define PCI402_IO_LEN_CORE		0x2000
+#define PCI402_PCICFG_MSICAP_CSR	0x52
+#define PCI402_PCICFG_MSICAP_ADDR	0x54
+#define PCI402_PCICFG_MSICAP_DATA	0x5c
+
+#define PCI402_DMA_MASK			(DMA_BIT_MASK(32) & 0xffff0000)
+#define PCI402_DMA_SIZE			ALIGN(0x10000, PAGE_SIZE)
+#define PCI402_DMA_FIFO_ITEMSIZE	32
+
+#define PCI402_PCIEP_OF_INT_ENABLE	0x0050
+#define PCI402_PCIEP_OF_BM_ADDR_LO	0x1000
+#define PCI402_PCIEP_OF_BM_ADDR_HI	0x1004
+#define PCI402_PCIEP_OF_MSI_ADDR_LO	0x1008
+#define PCI402_PCIEP_OF_MSI_ADDR_HI	0x100c
+
+struct pci402_card {
+	/* Actually mapped io space, all other iomem derived from this */
+	void __iomem *addr;
+	void __iomem *addr_pciep;
+
+	void *dma_buf;
+	dma_addr_t dma_hnd;
+
+	struct acc_ov ov;
+	struct acc_core core[PCI402_MAX_CORES];
+
+	bool msi_enabled;
+};
+
+static irqreturn_t pci402_interrupt(int irq, void *dev_id)
+{
+	struct pci402_card *card = pci_get_drvdata((struct pci_dev *)dev_id);
+
+	return acc_card_interrupt(&card->ov, card->core);
+}
+
+static int pci402_set_msiconfig(struct pci_dev *pdev)
+{
+	struct 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, PCI402_PCICFG_MSICAP_CSR, &csr);
+	if (err)
+		goto failed;
+
+	err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP_ADDR, &addr_lo);
+	if (err)
+		goto failed;
+	err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP_ADDR + 4,
+				    &addr_hi);
+	if (err)
+		goto failed;
+
+	err = pci_read_config_dword(pdev, 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 + PCI402_PCIEP_OF_MSI_ADDR_LO);
+	iowrite32(addr_hi, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_HI);
+	acc_ov_write32(&card->ov, ACC_OV_OF_MSI_ADDRESSOFFSET, addr_lo_offs);
+	acc_ov_write32(&card->ov, ACC_OV_OF_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 pci402_init_card(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+
+	card->ov.addr = card->addr + PCI402_IO_OV_OFFS;
+	card->addr_pciep = card->addr + PCI402_IO_PCIEP_OFFS;
+
+	acc_reset_fpga(&card->ov);
+	acc_init_ov(&card->ov, &pdev->dev);
+
+	if (card->ov.version < PCI402_FPGA_VER_MIN) {
+		dev_err(&pdev->dev,
+			"FPGA version (0x%.4x) outdated, please update\n",
+			card->ov.version);
+		return -EINVAL;
+	}
+
+	if (card->ov.active_cores > PCI402_MAX_CORES) {
+		dev_warn(&pdev->dev, "Card has more active cores than supported by driver, %u core(s) will be ignored\n",
+			 card->ov.active_cores - PCI402_MAX_CORES);
+		card->ov.active_cores = PCI402_MAX_CORES;
+	}
+
+	card->ov.deprecated_btr = card->ov.version < PCI402_FPGA_VER_NEWBTR;
+
+#ifdef __LITTLE_ENDIAN
+	/* So card converts all busmastered data to LE for us: */
+	acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
+			ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE);
+#endif
+
+	return 0;
+}
+
+static int pci402_init_interrupt(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+	int err;
+
+	err = pci_enable_msi(pdev);
+	if (!err) {
+		err = pci402_set_msiconfig(pdev);
+		if (!err) {
+			card->msi_enabled = 1;
+			acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
+					ACC_OV_REG_MODE_MASK_MSI_ENABLE);
+			dev_info(&pdev->dev, "MSI enabled\n");
+		}
+	}
+
+	err = request_irq(pdev->irq, pci402_interrupt,
+			  IRQF_SHARED, DRV_NAME, pdev);
+	if (err)
+		goto failure_msidis;
+
+	iowrite32(1, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
+
+	return 0;
+
+failure_msidis:
+	if (card->msi_enabled) {
+		acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
+				  ACC_OV_REG_MODE_MASK_MSI_ENABLE);
+		pci_disable_msi(pdev);
+		card->msi_enabled = 0;
+	}
+
+	return err;
+}
+
+static void pci402_finish_interrupt(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+
+	iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
+	free_irq(pdev->irq, pdev);
+
+	if (card->msi_enabled) {
+		acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
+				  ACC_OV_REG_MODE_MASK_MSI_ENABLE);
+		pci_disable_msi(pdev);
+		card->msi_enabled = 0;
+	}
+}
+
+static int pci402_init_dma(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+	int err;
+
+	err = pci_set_consistent_dma_mask(pdev, PCI402_DMA_MASK);
+	if (err) {
+		dev_err(&pdev->dev, "dma set mask failed!");
+		return err;
+	}
+
+	card->dma_buf = pci_alloc_consistent(pdev, PCI402_DMA_SIZE,
+					     &card->dma_hnd);
+	if (!card->dma_buf) {
+		dev_err(&pdev->dev, "dma alloc failed!");
+		return -ENOMEM;
+	}
+
+	acc_init_bm_ptr(&card->ov, card->core, card->dma_buf);
+
+	iowrite32((u32)card->dma_hnd,
+		  card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
+	iowrite32((u32)(card->dma_hnd >> 32),
+		  card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);
+
+	acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
+			ACC_OV_REG_MODE_MASK_BM_ENABLE);
+
+	return 0;
+}
+
+static void pci402_finish_dma(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+	int i;
+
+	acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
+			  ACC_OV_REG_MODE_MASK_BM_ENABLE);
+
+	iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
+	iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);
+
+	card->ov.bmfifo.messages = NULL;
+	card->ov.bmfifo.irq_cnt = NULL;
+	for (i = 0; i < card->ov.active_cores; ++i) {
+		struct acc_core *core = &card->core[i];
+
+		core->bmfifo.messages = NULL;
+		core->bmfifo.irq_cnt = NULL;
+	}
+
+	pci_free_consistent(pdev, PCI402_DMA_SIZE, card->dma_buf,
+			    card->dma_hnd);
+	card->dma_buf = NULL;
+}
+
+static int pci402_init_cores(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+	int num_register_ok = 0;
+	int err;
+	int i;
+
+	for (i = 0; i < card->ov.active_cores; ++i) {
+		struct acc_core *core = &card->core[i];
+		struct acc_net_priv *priv;
+		struct net_device *netdev;
+		u32 fifo_config;
+
+		core->addr = card->ov.addr + (i + 1) * PCI402_IO_LEN_CORE;
+
+		fifo_config = acc_read32(core, ACC_CORE_OF_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 = &acc_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->ov.core_frequency / 2;
+		priv->can.bittiming_const = (card->ov.deprecated_btr) ?
+				&acc_bittiming_const_pci402_old :
+				&acc_bittiming_const_pci402;
+		priv->can.do_set_bittiming = acc_set_bittiming;
+		priv->can.do_set_mode = acc_set_mode;
+		priv->can.do_get_berr_counter = acc_get_berr_counter;
+
+		priv->core = core;
+		priv->ov = &card->ov;
+
+		err = register_candev(netdev);
+		if (err)
+			goto failure;
+
+		netdev_info(netdev, "registered\n");
+		num_register_ok++;
+	}
+
+	return 0;
+
+failure:
+	for (i = 0; i < card->ov.active_cores; ++i) {
+		struct acc_core *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 pci402_finish_cores(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+	int i;
+
+	for (i = 0; i < card->ov.active_cores; ++i) {
+		struct acc_core *core = &card->core[i];
+
+		unregister_candev(core->net_dev);
+		free_candev(core->net_dev);
+		core->net_dev = NULL;
+	}
+}
+
+static int pci402_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct pci402_card *card = NULL;
+	int err;
+
+	BUILD_BUG_ON(PCI402_DMA_FIFO_ITEMSIZE != sizeof(struct acc_bmmsg));
+
+	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, PCI402_BAR, PCI402_IO_LEN_TOTAL);
+	if (!card->addr) {
+		err = -ENOMEM;
+		goto failure_release_regions;
+	}
+
+	err = pci402_init_card(pdev);
+	if (err)
+		goto failure_unmap;
+
+	err = pci402_init_dma(pdev);
+	if (err)
+		goto failure_unmap;
+
+	err = pci402_init_interrupt(pdev);
+	if (err)
+		goto failure_finish_dma;
+
+	err = pci402_init_cores(pdev);
+	if (err)
+		goto failure_finish_interrupt;
+
+	return 0;
+
+failure_finish_interrupt:
+	pci402_finish_interrupt(pdev);
+
+failure_finish_dma:
+	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 pci402_remove(struct pci_dev *pdev)
+{
+	struct pci402_card *card = pci_get_drvdata(pdev);
+
+	pci402_finish_interrupt(pdev);
+	pci402_finish_cores(pdev);
+	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 pci402_tbl[] = {
+	{ PCI_VENDOR_ID_ESDGMBH, ESD_PCI_DEVICE_ID_PCIE402,
+			PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, }
+};
+
+static struct pci_driver pci402_driver = {
+	.name = DRV_NAME,
+	.id_table = pci402_tbl,
+	.probe = pci402_probe,
+	.remove = pci402_remove,
+};
+
+module_pci_driver(pci402_driver);
+
+MODULE_DESCRIPTION("linux-can driver for esd CAN PCIe/402 cards");
+MODULE_AUTHOR("Thomas Körper <thomas.koerper@esd.eu>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c
new file mode 100644
index 0000000..46feae6
--- /dev/null
+++ b/drivers/net/can/esd/esdacc.c
@@ -0,0 +1,600 @@
+/* Copyright (C) 2015 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/ktime.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include "esdacc.h"
+
+static void acc_resetmode_enter(struct acc_core *core)
+{
+	int i;
+
+	acc_set_bits(core, ACC_CORE_OF_CTRL_MODE,
+		     ACC_REG_CONTROL_MASK_MODE_RESETMODE);
+
+	for (i = 0; i < 10; ++i) {
+		if (acc_resetmode_entered(core))
+			return;
+
+		udelay(5);
+	}
+
+	netdev_warn(core->net_dev, "Entering reset mode timed out\n");
+}
+
+static void acc_resetmode_leave(struct acc_core *core)
+{
+	int i;
+
+	acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE,
+		       ACC_REG_CONTROL_MASK_MODE_RESETMODE);
+
+	for (i = 0; i < 10; ++i) {
+		if (!acc_resetmode_entered(core))
+			return;
+
+		udelay(5);
+	}
+
+	netdev_warn(core->net_dev, "Leaving reset mode timed out\n");
+}
+
+static int acc_txq_isready(struct acc_core *core)
+{
+	u8 fifo_level = (u8)(acc_read32(core, ACC_CORE_OF_TXFIFO_STATUS) >> 16);
+
+	return (fifo_level < core->tx_fifo_size - 1);
+}
+
+static void acc_txq_put(struct acc_core *core, u32 esd_id, u8 esd_len,
+			const void *data)
+{
+	acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_0,
+			   *((const u32 *)data));
+	acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_1,
+			   *((const u32 *)(data + 4)));
+	acc_write32(core, ACC_CORE_OF_TXFIFO_DLC, esd_len);
+	/* CAN id must be written at last: */
+	acc_write32(core, ACC_CORE_OF_TXFIFO_ID, esd_id);
+}
+
+static ktime_t acc_ts_to_ktime(struct acc_ov *ov, u64 ts)
+{
+	unsigned long long ns;
+	unsigned int rem;
+
+	rem = do_div(ts, ov->timestamp_frequency);
+	ns = ts * NSEC_PER_SEC;
+
+	ts = (unsigned long long)rem * NSEC_PER_SEC;
+	do_div(ts, ov->timestamp_frequency);
+	ns += ts;
+
+	return ns_to_ktime(ns);
+}
+
+void acc_init_ov(struct acc_ov *ov, struct device *dev)
+{
+	u32 temp;
+
+	temp = acc_ov_read32(ov, ACC_OV_OF_VERSION);
+	ov->features = (u16)(temp >> 16);
+	ov->version = (u16)temp;
+
+	temp = acc_ov_read32(ov, ACC_OV_OF_INFO);
+	ov->active_cores = (u8)(temp >> 8);
+	ov->total_cores = (u8)temp;
+
+	ov->core_frequency = acc_ov_read32(ov, ACC_OV_OF_CANCORE_FREQ);
+	ov->timestamp_frequency = acc_ov_read32(ov, ACC_OV_OF_TS_FREQ_LO);
+
+	dev_info(dev,
+		 "FPGA v%u, freq: %u/%u, feat/strap: 0x%x/0x%x, cores: %u/%u\n",
+		 ov->version, ov->core_frequency, ov->timestamp_frequency,
+		 ov->features, acc_ov_read32(ov, ACC_OV_OF_INFO) >> 16,
+		 ov->active_cores, ov->total_cores);
+}
+
+void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, const void *mem)
+{
+	int i;
+
+	/* DMA buffer layout:
+	 * +-----------------------+
+	 * | FIFO Card/Overview    |
+	 * |                       |
+	 * +-----------------------+
+	 * | FIFO Core0            |
+	 * |                       |
+	 * +-----------------------+
+	 * | FIFO Core1            |
+	 * |                       |
+	 * +-----------------------+
+	 * | ...                   |
+	 * |                       |
+	 * +-----------------------+
+	 * | irq_cnt Card/Overview |
+	 * +-----------------------+
+	 * | irq_cnt Core0         |
+	 * +-----------------------+
+	 * | irq_cnt Core1         |
+	 * +-----------------------+
+	 * | ...                   |
+	 * +-----------------------+
+	 */
+	ov->bmfifo.messages = mem;
+	ov->bmfifo.irq_cnt = mem + (ov->total_cores + 1) * ACC_CORE_DMABUF_SIZE;
+
+	for (i = 0; i < ov->active_cores; ++i) {
+		struct acc_core *core = &cores[i];
+
+		core->bmfifo.messages = mem + (i + 1) * ACC_CORE_DMABUF_SIZE;
+		core->bmfifo.irq_cnt = ov->bmfifo.irq_cnt + 1 + i;
+	}
+}
+
+int acc_open(struct net_device *netdev)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+	u32 ctrl_mode;
+	int err;
+
+	err = open_candev(netdev);
+	if (err)
+		return err;
+
+	ctrl_mode = ACC_REG_CONTROL_MASK_IE_RXTX |
+			ACC_REG_CONTROL_MASK_IE_TXERROR |
+			ACC_REG_CONTROL_MASK_IE_ERRPASS |
+			ACC_REG_CONTROL_MASK_IE_OVERRUN |
+			ACC_REG_CONTROL_MASK_IE_ERROR;
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
+		ctrl_mode |= ACC_REG_CONTROL_MASK_IE_BUSERR;
+
+	acc_set_bits(priv->core, ACC_CORE_OF_CTRL_MODE, ctrl_mode);
+
+	netif_start_queue(netdev);
+	return 0;
+}
+
+int acc_close(struct net_device *netdev)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+
+	acc_clear_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
+		       ACC_REG_CONTROL_MASK_IE_RXTX |
+		       ACC_REG_CONTROL_MASK_IE_TXERROR |
+		       ACC_REG_CONTROL_MASK_IE_OVERRUN);
+
+	netif_stop_queue(netdev);
+	priv->can.state = CAN_STATE_STOPPED;
+
+	close_candev(netdev);
+	return 0;
+}
+
+netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+	struct acc_core *core = priv->core;
+	struct can_frame *cf = (struct can_frame *)skb->data;
+	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) || !acc_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 |= ACC_CAN_RTR_FLAG;
+
+	if (cf->can_id & CAN_EFF_FLAG) {
+		esd_id = cf->can_id & CAN_EFF_MASK;
+		esd_id |= ACC_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;
+
+	acc_txq_put(core, esd_id, esd_len, cf->data);
+
+	return NETDEV_TX_OK;
+}
+
+int acc_get_berr_counter(const struct net_device *netdev,
+			 struct can_berr_counter *bec)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+	u32 core_status = acc_read32(priv->core, ACC_CORE_OF_STATUS);
+
+	bec->txerr = (u8)(core_status >> 8);
+	bec->rxerr = (u8)core_status;
+
+	return 0;
+}
+
+int acc_set_mode(struct net_device *netdev, enum can_mode mode)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+
+	switch (mode) {
+	case CAN_MODE_START:
+		acc_resetmode_leave(priv->core);
+		netif_wake_queue(netdev);
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+int acc_set_bittiming(struct net_device *netdev)
+{
+	struct acc_net_priv *priv = netdev_priv(netdev);
+	struct can_bittiming *bt = &priv->can.bittiming;
+	u32 btr;
+	u8 brp;
+
+	brp = (u8)(bt->brp - 1);
+	acc_resetmode_enter(priv->core);
+
+	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
+		acc_set_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
+			     ACC_REG_CONTROL_MASK_MODE_LOM);
+	else
+		acc_clear_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
+			       ACC_REG_CONTROL_MASK_MODE_LOM);
+
+	if (priv->ov->deprecated_btr) {
+		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;
+
+		acc_write32(priv->core, ACC_CORE_OF_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;
+
+		acc_write32(priv->core, ACC_CORE_OF_BTR, brp);
+		acc_write32(priv->core, ACC_CORE_OF_BTR_FD, btr);
+	}
+
+	acc_resetmode_leave(priv->core);
+	priv->can.state = CAN_STATE_ERROR_ACTIVE;
+
+	return 0;
+}
+
+static void handle_core_msg_rxtxdone(struct acc_core *core,
+				     const struct acc_bmmsg_rxtxdone *msg)
+{
+	struct acc_net_priv *priv = netdev_priv(core->net_dev);
+	struct net_device_stats *stats = &core->net_dev->stats;
+
+	if (msg->dlc.rxtx.len & ACC_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 & ACC_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) {
+			stats->rx_dropped++;
+			return;
+		}
+
+		cf->can_id = msg->id & CAN_EFF_MASK;
+		if (msg->id & ACC_CAN_EFF_FLAG)
+			cf->can_id |= CAN_EFF_FLAG;
+
+		cf->can_dlc = get_can_dlc(msg->dlc.rx.len);
+		if (msg->dlc.rx.len & ACC_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 = acc_ts_to_ktime(priv->ov, msg->timestamp);
+		netif_rx(skb);
+
+		stats->rx_packets++;
+		stats->rx_bytes += cf->can_dlc;
+	}
+}
+
+static void handle_core_msg_txabort(struct acc_core *core,
+				    const struct acc_bmmsg_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 (!acc_resetmode_entered(core))
+		netif_wake_queue(core->net_dev);
+}
+
+static void handle_core_msg_overrun(struct acc_core *core,
+				    const struct acc_bmmsg_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)
+		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 handle_core_msg_buserr(struct acc_core *core,
+				   const struct acc_bmmsg_buserr *msg)
+{
+	struct acc_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)
+		return;
+
+	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
+
+	/* msg->ecc acts like SJA1000's ECC register */
+	switch (msg->ecc & ACC_ECC_MASK) {
+	case ACC_ECC_BIT:
+		cf->data[2] |= CAN_ERR_PROT_BIT;
+		break;
+	case ACC_ECC_FORM:
+		cf->data[2] |= CAN_ERR_PROT_FORM;
+		break;
+	case ACC_ECC_STUFF:
+		cf->data[2] |= CAN_ERR_PROT_STUFF;
+		break;
+	default:
+		cf->data[2] |= CAN_ERR_PROT_UNSPEC;
+		cf->data[3] = msg->ecc & ACC_ECC_SEG;
+		break;
+	}
+
+	if ((msg->ecc & ACC_ECC_DIR) == 0)
+		cf->data[2] |= CAN_ERR_PROT_TX;
+
+	netif_rx(skb);
+	stats->rx_packets++;
+	stats->rx_bytes += cf->can_dlc;
+}
+
+static void
+handle_core_msg_errstatechange(struct acc_core *core,
+			       const struct acc_bmmsg_errstatechange *msg)
+{
+	struct acc_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 & ACC_REG_STATUS_MASK_STATUS_ES) != 0;
+	is_passive = (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) != 0;
+	is_busoff = (msg->reg_status & ACC_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) {
+		acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
+		can_bus_off(core->net_dev);
+	}
+}
+
+static void handle_core_interrupt(struct acc_core *core)
+{
+	u32 msg_fifo_head = core->bmfifo.local_irq_cnt & 0xff;
+
+	while (core->bmfifo.msg_fifo_tail != msg_fifo_head) {
+		const struct acc_bmmsg *msg =
+			&core->bmfifo.messages[core->bmfifo.msg_fifo_tail];
+
+		switch (msg->u.msg_id) {
+		case BM_MSG_ID_RXTXDONE:
+			handle_core_msg_rxtxdone(core, &msg->u.rxtxdone);
+			break;
+
+		case BM_MSG_ID_TXABORT:
+			handle_core_msg_txabort(core, &msg->u.txabort);
+			break;
+
+		case BM_MSG_ID_OVERRUN:
+			handle_core_msg_overrun(core, &msg->u.overrun);
+			break;
+
+		case BM_MSG_ID_BUSERR:
+			handle_core_msg_buserr(core, &msg->u.buserr);
+			break;
+
+		case BM_MSG_ID_ERRPASSIVE:
+		case BM_MSG_ID_ERRWARN:
+			handle_core_msg_errstatechange(core,
+						       &msg->u.errstatechange);
+			break;
+
+		default:
+			break;
+		}
+
+		core->bmfifo.msg_fifo_tail =
+				(core->bmfifo.msg_fifo_tail + 1) & 0xff;
+	}
+}
+
+irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores)
+{
+	u32 irqmask;
+	int i;
+
+	/* 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 (*ov->bmfifo.irq_cnt != ov->bmfifo.local_irq_cnt) {
+		irqmask |= ACC_BM_IRQ_MASK;
+		ov->bmfifo.local_irq_cnt = *ov->bmfifo.irq_cnt;
+	}
+
+	for (i = 0; i < ov->active_cores; ++i) {
+		struct acc_core *core = &cores[i];
+
+		if (*core->bmfifo.irq_cnt != core->bmfifo.local_irq_cnt) {
+			irqmask |= (ACC_BM_IRQ_MASK << (2 * (i + 1)));
+			core->bmfifo.local_irq_cnt = *core->bmfifo.irq_cnt;
+		}
+	}
+
+	if (!irqmask)
+		return IRQ_NONE;
+
+	/* At second we tell the card we're working on them by writing irqmask,
+	 * call handle_{ov|core}_interrupt and then acknowledge the
+	 * interrupts by writing irq_cnt:
+	 */
+	acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, irqmask);
+
+	if (irqmask & ACC_BM_IRQ_MASK) {
+		/* handle_ov_interrupt(); - no use yet. */
+		acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_COUNTER,
+			       ov->bmfifo.local_irq_cnt);
+	}
+
+	for (i = 0; i < ov->active_cores; ++i) {
+		struct acc_core *core = &cores[i];
+
+		if (irqmask & (ACC_BM_IRQ_MASK << (2 * (i + 1)))) {
+			handle_core_interrupt(core);
+			acc_write32(core, ACC_OV_OF_BM_IRQ_COUNTER,
+				    core->bmfifo.local_irq_cnt);
+		}
+	}
+
+	acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, ACC_BM_IRQ_UNMASK_ALL);
+
+	return IRQ_HANDLED;
+}
diff --git a/drivers/net/can/esd/esdacc.h b/drivers/net/can/esd/esdacc.h
new file mode 100644
index 0000000..97d0d3b
--- /dev/null
+++ b/drivers/net/can/esd/esdacc.h
@@ -0,0 +1,428 @@
+/* Copyright (C) 2015 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/netdevice.h>
+#include <linux/can/dev.h>
+
+#define ACC_CAN_EFF_FLAG			0x20000000
+#define ACC_CAN_RTR_FLAG			0x10
+#define ACC_CAN_DLC_MASK			0x0f
+
+#define ACC_OV_OF_PROBE				0x0000
+#define ACC_OV_OF_VERSION			0x0004
+#define ACC_OV_OF_INFO				0x0008
+#define ACC_OV_OF_CANCORE_FREQ			0x000c
+#define ACC_OV_OF_TS_FREQ_LO			0x0010
+#define ACC_OV_OF_TS_FREQ_HI			0x0014
+#define ACC_OV_OF_IRQ_STATUS_CORES		0x0018
+#define ACC_OV_OF_TS_CURR_LO			0x001c
+#define ACC_OV_OF_TS_CURR_HI			0x0020
+#define ACC_OV_OF_IRQ_STATUS			0x0028
+#define ACC_OV_OF_MODE				0x002c
+#define ACC_OV_OF_BM_IRQ_COUNTER		0x0070
+#define ACC_OV_OF_BM_IRQ_MASK			0x0074
+#define ACC_OV_OF_MSI_DATA			0x0080
+#define ACC_OV_OF_MSI_ADDRESSOFFSET		0x0084
+
+#define ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE	0x00000001
+#define ACC_OV_REG_MODE_MASK_BM_ENABLE		0x00000002
+#define ACC_OV_REG_MODE_MASK_MODE_LED		0x00000004
+#define ACC_OV_REG_MODE_MASK_TIMER		0x00000070
+#define ACC_OV_REG_MODE_MASK_TIMER_ENABLE	0x00000010
+#define ACC_OV_REG_MODE_MASK_TIMER_ONE_SHOT	0x00000020
+#define ACC_OV_REG_MODE_MASK_TIMER_ABSOLUTE	0x00000040
+#define ACC_OV_REG_MODE_MASK_TS_SRC		0x00000180
+#define ACC_OV_REG_MODE_MASK_I2C_ENABLE		0x00000800
+#define ACC_OV_REG_MODE_MASK_MSI_ENABLE		0x00004000
+#define ACC_OV_REG_MODE_MASK_FPGA_RESET		0x80000000
+
+#define ACC_BM_IRQ_UNMASK_ALL			0x55555555
+#define ACC_BM_IRQ_MASK_ALL			0xaaaaaaaa
+#define ACC_BM_IRQ_MASK				0x2
+#define ACC_BM_IRQ_UNMASK			0x1
+#define ACC_BM_LENFLAG_TX			0x20
+
+#define ACC_CORE_OF_CTRL_MODE			0x0000
+#define ACC_CORE_OF_STATUS_IRQ			0x0008
+#define ACC_CORE_OF_BTR				0x000c
+#define ACC_CORE_OF_BTR_FD			0x0010
+#define ACC_CORE_OF_BTR_FDF			0x0014
+#define ACC_CORE_OF_STATUS			0x0030
+#define ACC_CORE_OF_TXFIFO_CONFIG		0x0048
+#define ACC_CORE_OF_TXFIFO_STATUS		0x004c
+#define ACC_CORE_OF_TX_STATUS_IRQ		0x0050
+#define ACC_CORE_OF_TX_ABORT_MASK		0x0054
+#define ACC_CORE_OF_BM_IRQ_COUNTER		0x0070
+#define ACC_CORE_OF_TXFIFO_ID			0x00c0
+#define ACC_CORE_OF_TXFIFO_DLC			0x00c4
+#define ACC_CORE_OF_TXFIFO_DATA_0		0x00c8
+#define ACC_CORE_OF_TXFIFO_DATA_1		0x00cc
+
+/* ecc value of esdACC equals SJA1000's ECC register */
+#define ACC_ECC_SEG				0x1f
+#define ACC_ECC_DIR				0x20
+#define ACC_ECC_BIT				0x00
+#define ACC_ECC_FORM				0x40
+#define ACC_ECC_STUFF				0x80
+#define ACC_ECC_MASK				0xc0
+
+#define ACC_REG_CONTROL_IDX_MODE_RESETMODE	0
+#define ACC_REG_CONTROL_IDX_MODE_LOM		1
+#define ACC_REG_CONTROL_IDX_MODE_STM		2
+#define ACC_REG_CONTROL_IDX_MODE_TRANSEN	5
+#define ACC_REG_CONTROL_IDX_MODE_TS		6
+#define ACC_REG_CONTROL_IDX_MODE_SCHEDULE	7
+#define ACC_REG_CONTROL_MASK_MODE_RESETMODE	\
+				BIT(ACC_REG_CONTROL_IDX_MODE_RESETMODE)
+#define ACC_REG_CONTROL_MASK_MODE_LOM		\
+				BIT(ACC_REG_CONTROL_IDX_MODE_LOM)
+#define ACC_REG_CONTROL_MASK_MODE_STM		\
+				BIT(ACC_REG_CONTROL_IDX_MODE_STM)
+#define ACC_REG_CONTROL_MASK_MODE_TRANSEN	\
+				BIT(ACC_REG_CONTROL_IDX_MODE_TRANSEN)
+#define ACC_REG_CONTROL_MASK_MODE_TS		\
+				BIT(ACC_REG_CONTROL_IDX_MODE_TS)
+#define ACC_REG_CONTROL_MASK_MODE_SCHEDULE	\
+				BIT(ACC_REG_CONTROL_IDX_MODE_SCHEDULE)
+
+#define ACC_REG_CONTROL_IDX_IE_RXTX	8
+#define ACC_REG_CONTROL_IDX_IE_TXERROR	9
+#define ACC_REG_CONTROL_IDX_IE_ERROR	10
+#define ACC_REG_CONTROL_IDX_IE_OVERRUN	11
+#define ACC_REG_CONTROL_IDX_IE_TSI	12
+#define ACC_REG_CONTROL_IDX_IE_ERRPASS	13
+#define ACC_REG_CONTROL_IDX_IE_BUSERR	15
+#define ACC_REG_CONTROL_MASK_IE_RXTX	BIT(ACC_REG_CONTROL_IDX_IE_RXTX)
+#define ACC_REG_CONTROL_MASK_IE_TXERROR	BIT(ACC_REG_CONTROL_IDX_IE_TXERROR)
+#define ACC_REG_CONTROL_MASK_IE_ERROR	BIT(ACC_REG_CONTROL_IDX_IE_ERROR)
+#define ACC_REG_CONTROL_MASK_IE_OVERRUN	BIT(ACC_REG_CONTROL_IDX_IE_OVERRUN)
+#define ACC_REG_CONTROL_MASK_IE_TSI	BIT(ACC_REG_CONTROL_IDX_IE_TSI)
+#define ACC_REG_CONTROL_MASK_IE_ERRPASS	BIT(ACC_REG_CONTROL_IDX_IE_ERRPASS)
+#define ACC_REG_CONTROL_MASK_IE_BUSERR	BIT(ACC_REG_CONTROL_IDX_IE_BUSERR)
+
+#define ACC_REG_STATUS_IDX_STATUS_DOS	16
+#define ACC_REG_STATUS_IDX_STATUS_ES	17
+#define ACC_REG_STATUS_IDX_STATUS_EP	18
+#define ACC_REG_STATUS_IDX_STATUS_BS	19
+#define ACC_REG_STATUS_IDX_STATUS_RBS	20
+#define ACC_REG_STATUS_IDX_STATUS_RS	21
+#define ACC_REG_STATUS_MASK_STATUS_DOS	BIT(ACC_REG_STATUS_IDX_STATUS_DOS)
+#define ACC_REG_STATUS_MASK_STATUS_ES	BIT(ACC_REG_STATUS_IDX_STATUS_ES)
+#define ACC_REG_STATUS_MASK_STATUS_EP	BIT(ACC_REG_STATUS_IDX_STATUS_EP)
+#define ACC_REG_STATUS_MASK_STATUS_BS	BIT(ACC_REG_STATUS_IDX_STATUS_BS)
+#define ACC_REG_STATUS_MASK_STATUS_RBS	BIT(ACC_REG_STATUS_IDX_STATUS_RBS)
+#define ACC_REG_STATUS_MASK_STATUS_RS	BIT(ACC_REG_STATUS_IDX_STATUS_RS)
+
+#define ACC_CORE_DMABUF_SIZE		(256 * 32) /* 256 messages */
+
+enum acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_timeslice {
+	u8 msg_id;
+	u8 txfifo_level;
+	u8 reserved1[2];
+	u8 txtsfifo_level;
+	u8 reserved2[3];
+	u64 ts;
+	u32 reserved3[4];
+} __packed;
+
+struct acc_bmmsg_hwtimer {
+	u8 msg_id;
+	u8 reserved1[3];
+	u32 reserved2[1];
+	u64 timer;
+	u32 reserved3[4];
+} __packed;
+
+struct acc_bmmsg_hotplug {
+	u8 msg_id;
+	u8 reserved1[3];
+	u32 reserved2[7];
+} __packed;
+
+struct acc_bmmsg_canfddata {
+	u8 msg_id;
+	u8 reserved1[3];
+	union {
+		u8 ui8[28];
+		u32 ui32[7];
+	} d;
+} __packed;
+
+struct acc_bmmsg {
+	union {
+		u8 msg_id;
+		struct acc_bmmsg_rxtxdone rxtxdone;
+		struct acc_bmmsg_canfddata canfddata;
+		struct acc_bmmsg_txabort txabort;
+		struct acc_bmmsg_overrun overrun;
+		struct acc_bmmsg_buserr buserr;
+		struct acc_bmmsg_errstatechange errstatechange;
+		struct acc_bmmsg_timeslice timeslice;
+		struct acc_bmmsg_hwtimer hwtimer;
+	} u;
+};
+
+struct acc_bmfifo {
+	const struct acc_bmmsg *messages;
+	/* Bits0..7: bm_fifo head index */
+	const u32 *irq_cnt;
+	u32 local_irq_cnt;
+	u32 msg_fifo_tail;
+};
+
+struct acc_core {
+	void __iomem *addr;
+	struct net_device *net_dev;
+	struct acc_bmfifo bmfifo;
+	u8 tx_fifo_size;
+	u8 tx_fifo_head;
+	u8 tx_fifo_tail;
+};
+
+struct acc_ov {
+	void __iomem *addr;
+	struct acc_bmfifo bmfifo;
+	u32 timestamp_frequency;
+	u32 core_frequency;
+	u16 version;
+	u16 features;
+	u8 total_cores;
+	u8 active_cores;
+	bool deprecated_btr;
+};
+
+struct acc_net_priv {
+	struct can_priv can; /* must be the first member! */
+	struct acc_core *core;
+	struct acc_ov *ov;
+};
+
+static inline u32 acc_read32(struct acc_core *core, unsigned short offs)
+{
+	return ioread32be(core->addr + offs);
+}
+
+static inline void acc_write32(struct acc_core *core,
+			       unsigned short offs, u32 v)
+{
+	iowrite32be(v, core->addr + offs);
+}
+
+static inline void acc_write32_noswap(struct acc_core *core,
+				      unsigned short offs, u32 v)
+{
+	iowrite32(v, core->addr + offs);
+}
+
+static inline void acc_set_bits(struct acc_core *core,
+				unsigned short offs, u32 mask)
+{
+	u32 v = acc_read32(core, offs);
+
+	v |= mask;
+	acc_write32(core, offs, v);
+}
+
+static inline void acc_clear_bits(struct acc_core *core,
+				  unsigned short offs, u32 mask)
+{
+	u32 v = acc_read32(core, offs);
+
+	v &= ~mask;
+	acc_write32(core, offs, v);
+}
+
+static inline int acc_resetmode_entered(struct acc_core *core)
+{
+	u32 ctrl = acc_read32(core, ACC_CORE_OF_CTRL_MODE);
+
+	return (ctrl & ACC_REG_CONTROL_MASK_MODE_RESETMODE) != 0;
+}
+
+static inline u32 acc_ov_read32(struct acc_ov *ov, unsigned short offs)
+{
+	return ioread32be(ov->addr + offs);
+}
+
+static inline void acc_ov_write32(struct acc_ov *ov,
+				  unsigned short offs, u32 v)
+{
+	iowrite32be(v, ov->addr + offs);
+}
+
+static inline void acc_ov_set_bits(struct acc_ov *ov,
+				   unsigned short offs, u32 b)
+{
+	u32 v = acc_ov_read32(ov, offs);
+
+	v |= b;
+	acc_ov_write32(ov, offs, v);
+}
+
+static inline void acc_ov_clear_bits(struct acc_ov *ov,
+				     unsigned short offs, u32 b)
+{
+	u32 v = acc_ov_read32(ov, offs);
+
+	v &= ~b;
+	acc_ov_write32(ov, offs, v);
+}
+
+static inline void acc_reset_fpga(struct acc_ov *ov)
+{
+	acc_ov_write32(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_FPGA_RESET);
+
+	/* Also reset I²C, to re-detect card addons at every driver start: */
+	acc_ov_clear_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
+	mdelay(2);
+	acc_ov_set_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
+	mdelay(10);
+}
+
+void acc_init_ov(struct acc_ov *ov, struct device *dev);
+void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores,
+		     const void *mem);
+int acc_open(struct net_device *netdev);
+int acc_close(struct net_device *netdev);
+netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev);
+int acc_get_berr_counter(const struct net_device *netdev,
+			 struct can_berr_counter *bec);
+int acc_set_mode(struct net_device *netdev, enum can_mode mode);
+int acc_set_bittiming(struct net_device *netdev);
+irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores);
+
+/* Used in PCIe/402 cards with FPGA Version < 0x0032 */
+static const struct can_bittiming_const acc_bittiming_const_pci402_old = {
+	.name = "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 acc_bittiming_const_pci402 = {
+	.name = "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 acc_netdev_ops = {
+	.ndo_open = acc_open,
+	.ndo_stop = acc_close,
+	.ndo_start_xmit = acc_start_xmit,
+	.ndo_change_mtu = can_change_mtu
+};
-- 
2.3.1


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

* Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-16 12:15 [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card Thomas Körper
@ 2015-03-16 12:46 ` Andri Yngvason
  2015-03-16 13:35   ` Marc Kleine-Budde
  2015-03-17  6:30   ` AW: " Thomas Körper
  2015-03-17 21:27 ` Marc Kleine-Budde
  1 sibling, 2 replies; 9+ messages in thread
From: Andri Yngvason @ 2015-03-16 12:46 UTC (permalink / raw)
  To: linux-can; +Cc: Thomas Körper

Quoting Thomas Körper (2015-03-16 12:15:13)
[...]
> +static void
> +handle_core_msg_errstatechange(struct acc_core *core,
> +                              const struct acc_bmmsg_errstatechange *msg)
> +{
> +       struct acc_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 & ACC_REG_STATUS_MASK_STATUS_ES) != 0;
> +       is_passive = (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) != 0;
> +       is_busoff = (msg->reg_status & ACC_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 */
>
There is now a back-to-error-active message available for use, so you can do
    cf->can_id = CAN_ERR_CRTL;
    cf->data[1] = CAN_ERROR_CRTL_ACTIVE.
However, you could shave off a few lines in this code by using
can_change_state().
>
> +               }
> +
> +               netif_rx(skb);
> +               stats->rx_packets++;
> +               stats->rx_bytes += cf->can_dlc;
> +       }
> +
> +       if (is_busoff) {
> +               acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
> +               can_bus_off(core->net_dev);
> +       }
> +}
> +
[...]

can_change_state() is now used in mscan, flexcan, sja1000, kvaser and peak.
Those drivers should provide sufficent examples for you to learn how to use the
function.

Best regards,
Andri Yngvason

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

* Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-16 12:46 ` Andri Yngvason
@ 2015-03-16 13:35   ` Marc Kleine-Budde
  2015-03-17  6:30   ` AW: " Thomas Körper
  1 sibling, 0 replies; 9+ messages in thread
From: Marc Kleine-Budde @ 2015-03-16 13:35 UTC (permalink / raw)
  To: Andri Yngvason, linux-can; +Cc: Thomas Körper

[-- Attachment #1: Type: text/plain, Size: 570 bytes --]

On 03/16/2015 01:46 PM, Andri Yngvason wrote:
> can_change_state() is now used in mscan, flexcan, sja1000, kvaser and peak.
> Those drivers should provide sufficent examples for you to learn how to use the
> function.

Thanks for keeping an eye on the CAN error handling, Andri.

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* AW: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-16 12:46 ` Andri Yngvason
  2015-03-16 13:35   ` Marc Kleine-Budde
@ 2015-03-17  6:30   ` Thomas Körper
  2015-03-17  7:26     ` Ahmed S. Darwish
  2015-03-17 10:33     ` AW: " Andri Yngvason
  1 sibling, 2 replies; 9+ messages in thread
From: Thomas Körper @ 2015-03-17  6:30 UTC (permalink / raw)
  To: Andri Yngvason; +Cc: linux-can

Hi Andri,

I've looked at the sources you mentioned, but I'm a little bit unsure now /
the handling seems not perfectly consistent to me. (flexcan calls
can_change_state() with tx/rx_state of 0 in the bus off path. Only
kvaser_usb counts rx_dropped++ if skb alloc failed, and setting 
cf->data[6]/cf->data[7] to the counter values seems also rarely used)

...so let me show the reworked function first before I post a new patch:

static void
handle_core_msg_errstatechange(struct acc_core *core,
			       const struct acc_bmmsg_errstatechange *msg)
{
	struct acc_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;

	skb = alloc_can_err_skb(core->net_dev, &cf);
	if (skb) {
		enum can_state new_state;
		u8 txerr;
		u8 rxerr;

		txerr = (u8)(msg->reg_status >> 8);
		rxerr = (u8)msg->reg_status;

		cf->data[6] = txerr;
		cf->data[7] = rxerr;

		if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_BS) {
			new_state = CAN_STATE_BUS_OFF;
		} else if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) {
			new_state = CAN_STATE_ERROR_PASSIVE;
		} else if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_ES) {
			new_state = CAN_STATE_ERROR_WARNING;
		} else {
			new_state = CAN_STATE_ERROR_ACTIVE;
		}

		if (new_state != priv->can.state) {
			enum can_state tx_state, rx_state;

			tx_state = (txerr >= rxerr) ? new_state : 0;
			rx_state = (rxerr >= txerr) ? new_state : 0;

			can_change_state(core->net_dev, cf, tx_state, rx_state);
		}

		netif_rx(skb);
		stats->rx_packets++;
		stats->rx_bytes += cf->can_dlc;
	} else {
		stats->rx_dropped++;
	}

	if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_BS) {
		acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
		can_bus_off(core->net_dev);
	}
}


Regards,
    Thomas


-----Ursprüngliche Nachricht-----
Von: Andri Yngvason [mailto:andri.yngvason@marel.com] 
Gesendet: Montag, 16. März 2015 13:46
An: linux-can@vger.kernel.org
Cc: Thomas Körper
Betreff: Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card

Quoting Thomas Körper (2015-03-16 12:15:13)
[...]
> +static void
> +handle_core_msg_errstatechange(struct acc_core *core,
> +                              const struct acc_bmmsg_errstatechange *msg)
> +{
> +       struct acc_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 & ACC_REG_STATUS_MASK_STATUS_ES) != 0;
> +       is_passive = (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) != 0;
> +       is_busoff = (msg->reg_status & ACC_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 */
>
There is now a back-to-error-active message available for use, so you can do
    cf->can_id = CAN_ERR_CRTL;
    cf->data[1] = CAN_ERROR_CRTL_ACTIVE.
However, you could shave off a few lines in this code by using
can_change_state().
>
> +               }
> +
> +               netif_rx(skb);
> +               stats->rx_packets++;
> +               stats->rx_bytes += cf->can_dlc;
> +       }
> +
> +       if (is_busoff) {
> +               acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
> +               can_bus_off(core->net_dev);
> +       }
> +}
> +
[...]

can_change_state() is now used in mscan, flexcan, sja1000, kvaser and peak.
Those drivers should provide sufficent examples for you to learn how to use the
function.

Best regards,
Andri Yngvason



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

* Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-17  6:30   ` AW: " Thomas Körper
@ 2015-03-17  7:26     ` Ahmed S. Darwish
  2015-03-17 10:10       ` Andri Yngvason
  2015-03-17 10:33     ` AW: " Andri Yngvason
  1 sibling, 1 reply; 9+ messages in thread
From: Ahmed S. Darwish @ 2015-03-17  7:26 UTC (permalink / raw)
  To: Thomas Körper; +Cc: Andri Yngvason, Marc Kleine-Budde, linux-can

Hi Thomas,

On Tue, Mar 17, 2015 at 07:30:02AM +0100, Thomas Körper wrote:
> Hi Andri,
> 
> I've looked at the sources you mentioned, but I'm a little bit unsure now /
> the handling seems not perfectly consistent to me. (flexcan calls
> can_change_state() with tx/rx_state of 0 in the bus off path.
>

There're discrpeancies indeed on the BUSOFF path:

1) flexcan calls can_change_state(dev, cf, 0, 0). kvaser and others
   do can_change_state(dev, cf, tx, rx) where either tx or rx =
   CAN_STATE_BUS_OFF. In this case, kvaser and the other drivers
   are the _correct_ behavior. This is validated by can-dev
   can_chage_state() code:

	if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
		cf->can_id |= CAN_ERR_BUSOFF;
		return;
	}

2) flexcan calls can_bus_off(dev), while kvaser goes a layer deeper
   and directly calls netif_carrier_off(netdev). In this case,
   flexcan is the more correct behavior.


Thanks,
Darwish

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

* Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-17  7:26     ` Ahmed S. Darwish
@ 2015-03-17 10:10       ` Andri Yngvason
  0 siblings, 0 replies; 9+ messages in thread
From: Andri Yngvason @ 2015-03-17 10:10 UTC (permalink / raw)
  To: Ahmed S.Darwish, "Thomas Körper; +Cc: Marc Kleine-Budde, linux-can

Quoting Ahmed S. Darwish (2015-03-17 07:26:39)
> Hi Thomas,
> 
> On Tue, Mar 17, 2015 at 07:30:02AM +0100, Thomas Körper wrote:
> > Hi Andri,
> > 
> > I've looked at the sources you mentioned, but I'm a little bit unsure now /
> > the handling seems not perfectly consistent to me. (flexcan calls
> > can_change_state() with tx/rx_state of 0 in the bus off path.
> >
> 
> There're discrpeancies indeed on the BUSOFF path:
> 
> 1) flexcan calls can_change_state(dev, cf, 0, 0). kvaser and others
>    do can_change_state(dev, cf, tx, rx) where either tx or rx =
>    CAN_STATE_BUS_OFF. In this case, kvaser and the other drivers
>    are the _correct_ behavior. This is validated by can-dev
>    can_chage_state() code:
> 
>         if (unlikely(new_state == CAN_STATE_BUS_OFF)) {
>                 cf->can_id |= CAN_ERR_BUSOFF;
>                 return;
>         }
Indeed, I'll have to fix that. Well spotted Thomas!
> 
> 2) flexcan calls can_bus_off(dev), while kvaser goes a layer deeper
>    and directly calls netif_carrier_off(netdev). In this case,
>    flexcan is the more correct behavior.
> 
IMO, that's fine as long as it doesn't add to inconsistency between outwards
behaviour of the system; which it doesn't.

Best regards,
Andri

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

* Re: AW: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-17  6:30   ` AW: " Thomas Körper
  2015-03-17  7:26     ` Ahmed S. Darwish
@ 2015-03-17 10:33     ` Andri Yngvason
  1 sibling, 0 replies; 9+ messages in thread
From: Andri Yngvason @ 2015-03-17 10:33 UTC (permalink / raw)
  To: Thomas Körper; +Cc: linux-can

Hi Thomas,

Quoting Thomas Körper (2015-03-17 06:30:02)
> Hi Andri,
> 
> I've looked at the sources you mentioned, but I'm a little bit unsure now /
> the handling seems not perfectly consistent to me. (flexcan calls
> can_change_state() with tx/rx_state of 0 in the bus off path. Only
That's an oversight on my part.

> kvaser_usb counts rx_dropped++ if skb alloc failed, and setting 
Yes, Darwish is very diligent in handling OOM.

> cf->data[6]/cf->data[7] to the counter values seems also rarely used)
Some chips don't expose the error counters.

> 
> ...so let me show the reworked function first before I post a new patch:
> 
> static void
> handle_core_msg_errstatechange(struct acc_core *core,
>                                const struct acc_bmmsg_errstatechange *msg)
> {
>         struct acc_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;
> 
>         skb = alloc_can_err_skb(core->net_dev, &cf);
>         if (skb) {
>                 enum can_state new_state;
>                 u8 txerr;
>                 u8 rxerr;
> 
>                 txerr = (u8)(msg->reg_status >> 8);
>                 rxerr = (u8)msg->reg_status;
> 
>                 cf->data[6] = txerr;
>                 cf->data[7] = rxerr;
> 
>                 if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_BS) {
>                         new_state = CAN_STATE_BUS_OFF;
>                 } else if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) {
>                         new_state = CAN_STATE_ERROR_PASSIVE;
>                 } else if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_ES) {
>                         new_state = CAN_STATE_ERROR_WARNING;
>                 } else {
>                         new_state = CAN_STATE_ERROR_ACTIVE;
>                 }
> 
>                 if (new_state != priv->can.state) {
>                         enum can_state tx_state, rx_state;
> 
>                         tx_state = (txerr >= rxerr) ? new_state : 0;
>                         rx_state = (rxerr >= txerr) ? new_state : 0;
> 
>                         can_change_state(core->net_dev, cf, tx_state, rx_state);
>                 }
> 
>                 netif_rx(skb);
>                 stats->rx_packets++;
>                 stats->rx_bytes += cf->can_dlc;
>         } else {
>                 stats->rx_dropped++;
>         }
> 
>         if (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_BS) {
>                 acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
>                 can_bus_off(core->net_dev);
>         }
> }
> 

Looks good!

Best regards,
Andri

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

* Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-16 12:15 [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card Thomas Körper
  2015-03-16 12:46 ` Andri Yngvason
@ 2015-03-17 21:27 ` Marc Kleine-Budde
  2015-03-18  5:08   ` Thomas Körper
  1 sibling, 1 reply; 9+ messages in thread
From: Marc Kleine-Budde @ 2015-03-17 21:27 UTC (permalink / raw)
  To: Thomas Körper, linux-can

[-- Attachment #1: Type: text/plain, Size: 48622 bytes --]

On 03/16/2015 01:15 PM, Thomas Körper wrote:
> This patch adds support for the CAN PCIe/402 card from esd electronic
> system design gmbh: FPGA based CAN controller, supporting up to 4
> nets, MSI and bus mastering. See http://esd.eu/en/products/can-pcie402
> for details.

I assume all packed strucks are coming from the hardware, do you have to
take care of the endianess? I'll consult a colleague of mine, too. Can
you explain me how endianess is handled in the driver/hw? What's the
purpose of the __LITTLE_ENDIAN? Have you actually tested on any BE platform?

Your TX path flow control is broken, you should stop the queue before
enabling the tx-ready IRQ (or sending the CAN frame) if all your HW
buffers are used. Enable the queue after freeing your HW buffer in tx
complete.

Why do you split your driver into two .c files? Probably to have a
separation of low level functions, but this can be done in one .c file,
too. But you might convince me in this point :). Your .h file can be
cleaned up, please put only the bare minimum into the header, defines
and structs that are only used in one .c file should go there.

Please re-arange your .c file, so that you don't need any forward
declarations of functions.

> 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      |   7 +
>  drivers/net/can/esd/Makefile     |   3 +
>  drivers/net/can/esd/esd402_pci.c | 453 +++++++++++++++++++++++++++++
>  drivers/net/can/esd/esdacc.c     | 600 +++++++++++++++++++++++++++++++++++++++
>  drivers/net/can/esd/esdacc.h     | 428 ++++++++++++++++++++++++++++
>  7 files changed, 1494 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_pci.c
>  create mode 100644 drivers/net/can/esd/esdacc.c
>  create mode 100644 drivers/net/can/esd/esdacc.h
> 
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 58808f6..54d9ce8 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -153,6 +153,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 c533c62..c691233 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..cebd5ae
> --- /dev/null
> +++ b/drivers/net/can/esd/Kconfig
> @@ -0,0 +1,7 @@
> +config CAN_ESD_402_PCI
> +	tristate "esd gmbh CAN PCIe/402 support"
> +	default y

I think no default would be a sensible default here.

> +	depends on PCI && HAS_DMA
> +	---help---
> +	  Support for CAN PCIe/402 card from esd electronic system design gmbh
> +	  (http://esd.eu/en)
> diff --git a/drivers/net/can/esd/Makefile b/drivers/net/can/esd/Makefile
> new file mode 100644
> index 0000000..80d970e
> --- /dev/null
> +++ b/drivers/net/can/esd/Makefile
> @@ -0,0 +1,3 @@
> +esd_402_pci-y := esdacc.o esd402_pci.o
> +
> +obj-$(CONFIG_CAN_ESD_402_PCI) += esd_402_pci.o
> diff --git a/drivers/net/can/esd/esd402_pci.c b/drivers/net/can/esd/esd402_pci.c
> new file mode 100644
> index 0000000..d2b5600
> --- /dev/null
> +++ b/drivers/net/can/esd/esd402_pci.c
> @@ -0,0 +1,453 @@
> +/* Copyright (C) 2015 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/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 "esdacc.h"
> +
> +#define DRV_NAME			"esd_402_pci"
> +
> +#define ESD_PCI_DEVICE_ID_PCIE402	0x0402
> +
> +#define PCI402_FPGA_VER_MIN		0x0025
> +#define PCI402_FPGA_VER_NEWBTR		0x0032
> +#define PCI402_MAX_CORES		4
> +#define PCI402_BAR			0
> +#define PCI402_IO_OV_OFFS		0
> +#define PCI402_IO_PCIEP_OFFS		0x10000
> +#define PCI402_IO_LEN_TOTAL		0x20000
> +#define PCI402_IO_LEN_CORE		0x2000
> +#define PCI402_PCICFG_MSICAP_CSR	0x52
> +#define PCI402_PCICFG_MSICAP_ADDR	0x54
> +#define PCI402_PCICFG_MSICAP_DATA	0x5c
> +
> +#define PCI402_DMA_MASK			(DMA_BIT_MASK(32) & 0xffff0000)
> +#define PCI402_DMA_SIZE			ALIGN(0x10000, PAGE_SIZE)
> +#define PCI402_DMA_FIFO_ITEMSIZE	32
> +
> +#define PCI402_PCIEP_OF_INT_ENABLE	0x0050
> +#define PCI402_PCIEP_OF_BM_ADDR_LO	0x1000
> +#define PCI402_PCIEP_OF_BM_ADDR_HI	0x1004
> +#define PCI402_PCIEP_OF_MSI_ADDR_LO	0x1008
> +#define PCI402_PCIEP_OF_MSI_ADDR_HI	0x100c
> +
> +struct pci402_card {
> +	/* Actually mapped io space, all other iomem derived from this */
> +	void __iomem *addr;
> +	void __iomem *addr_pciep;
> +
> +	void *dma_buf;
> +	dma_addr_t dma_hnd;
> +
> +	struct acc_ov ov;
> +	struct acc_core core[PCI402_MAX_CORES];
> +
> +	bool msi_enabled;
> +};
> +
> +static irqreturn_t pci402_interrupt(int irq, void *dev_id)
> +{
> +	struct pci402_card *card = pci_get_drvdata((struct pci_dev *)dev_id);

The cast is not needed.
> +
> +	return acc_card_interrupt(&card->ov, card->core);

Can you integrate acc_card_interrupt() here?

> +}
> +
> +static int pci402_set_msiconfig(struct pci_dev *pdev)
> +{
> +	struct 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, PCI402_PCICFG_MSICAP_CSR, &csr);
> +	if (err)
> +		goto failed;
> +
> +	err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP_ADDR, &addr_lo);
> +	if (err)
> +		goto failed;
> +	err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP_ADDR + 4,
> +				    &addr_hi);
> +	if (err)
> +		goto failed;
> +
> +	err = pci_read_config_dword(pdev, 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 + PCI402_PCIEP_OF_MSI_ADDR_LO);
> +	iowrite32(addr_hi, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_HI);
> +	acc_ov_write32(&card->ov, ACC_OV_OF_MSI_ADDRESSOFFSET, addr_lo_offs);
> +	acc_ov_write32(&card->ov, ACC_OV_OF_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;

Please use sensible -ERRNO in the kernel.

> +}
> +
> +static int pci402_init_card(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +
> +	card->ov.addr = card->addr + PCI402_IO_OV_OFFS;
> +	card->addr_pciep = card->addr + PCI402_IO_PCIEP_OFFS;
> +
> +	acc_reset_fpga(&card->ov);
> +	acc_init_ov(&card->ov, &pdev->dev);
> +
> +	if (card->ov.version < PCI402_FPGA_VER_MIN) {
> +		dev_err(&pdev->dev,
> +			"FPGA version (0x%.4x) outdated, please update\n",
> +			card->ov.version);
> +		return -EINVAL;
> +	}
> +
> +	if (card->ov.active_cores > PCI402_MAX_CORES) {
> +		dev_warn(&pdev->dev, "Card has more active cores than supported by driver, %u core(s) will be ignored\n",
> +			 card->ov.active_cores - PCI402_MAX_CORES);
> +		card->ov.active_cores = PCI402_MAX_CORES;
> +	}
> +
> +	card->ov.deprecated_btr = card->ov.version < PCI402_FPGA_VER_NEWBTR;
> +
> +#ifdef __LITTLE_ENDIAN
> +	/* So card converts all busmastered data to LE for us: */
> +	acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
> +			ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE);
> +#endif
> +
> +	return 0;
> +}
> +
> +static int pci402_init_interrupt(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +	int err;
> +
> +	err = pci_enable_msi(pdev);
> +	if (!err) {
> +		err = pci402_set_msiconfig(pdev);
> +		if (!err) {
> +			card->msi_enabled = 1;

please use true/false as it's a bool

> +			acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
> +					ACC_OV_REG_MODE_MASK_MSI_ENABLE);
> +			dev_info(&pdev->dev, "MSI enabled\n");
> +		}
> +	}
> +
> +	err = request_irq(pdev->irq, pci402_interrupt,
> +			  IRQF_SHARED, DRV_NAME, pdev);

please use dev->name instead of DRV_NAME
please use devm_request_irq function

> +	if (err)
> +		goto failure_msidis;
> +
> +	iowrite32(1, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
> +
> +	return 0;
> +
> +failure_msidis:
> +	if (card->msi_enabled) {
> +		acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
> +				  ACC_OV_REG_MODE_MASK_MSI_ENABLE);
> +		pci_disable_msi(pdev);
> +		card->msi_enabled = 0;
> +	}
> +
> +	return err;
> +}
> +
> +static void pci402_finish_interrupt(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +
> +	iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
> +	free_irq(pdev->irq, pdev);
> +
> +	if (card->msi_enabled) {
> +		acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
> +				  ACC_OV_REG_MODE_MASK_MSI_ENABLE);
> +		pci_disable_msi(pdev);
> +		card->msi_enabled = 0;
> +	}
> +}
> +
> +static int pci402_init_dma(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +	int err;
> +
> +	err = pci_set_consistent_dma_mask(pdev, PCI402_DMA_MASK);
> +	if (err) {
> +		dev_err(&pdev->dev, "dma set mask failed!");
> +		return err;
> +	}
> +
> +	card->dma_buf = pci_alloc_consistent(pdev, PCI402_DMA_SIZE,
> +					     &card->dma_hnd);
> +	if (!card->dma_buf) {
> +		dev_err(&pdev->dev, "dma alloc failed!");
> +		return -ENOMEM;
> +	}
> +
> +	acc_init_bm_ptr(&card->ov, card->core, card->dma_buf);
> +
> +	iowrite32((u32)card->dma_hnd,
> +		  card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
> +	iowrite32((u32)(card->dma_hnd >> 32),
> +		  card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);
> +
> +	acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
> +			ACC_OV_REG_MODE_MASK_BM_ENABLE);
> +
> +	return 0;
> +}
> +
> +static void pci402_finish_dma(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +	int i;
> +
> +	acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
> +			  ACC_OV_REG_MODE_MASK_BM_ENABLE);
> +
> +	iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
> +	iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);
> +
> +	card->ov.bmfifo.messages = NULL;
> +	card->ov.bmfifo.irq_cnt = NULL;
> +	for (i = 0; i < card->ov.active_cores; ++i) {
> +		struct acc_core *core = &card->core[i];
> +
> +		core->bmfifo.messages = NULL;
> +		core->bmfifo.irq_cnt = NULL;
> +	}
> +
> +	pci_free_consistent(pdev, PCI402_DMA_SIZE, card->dma_buf,
> +			    card->dma_hnd);
> +	card->dma_buf = NULL;
> +}
> +
> +static int pci402_init_cores(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +	int num_register_ok = 0;
> +	int err;
> +	int i;
> +
> +	for (i = 0; i < card->ov.active_cores; ++i) {
i++ common style (and doesn't make a difference here)
> +		struct acc_core *core = &card->core[i];
> +		struct acc_net_priv *priv;
> +		struct net_device *netdev;
> +		u32 fifo_config;
> +
> +		core->addr = card->ov.addr + (i + 1) * PCI402_IO_LEN_CORE;
> +
> +		fifo_config = acc_read32(core, ACC_CORE_OF_TXFIFO_CONFIG);
> +		core->tx_fifo_size = (u8)(fifo_config >> 24);
cast not needed
> +		BUG_ON(core->tx_fifo_size <= 1);

better exit more gracefully here

> +
> +		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 = &acc_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->ov.core_frequency / 2;
> +		priv->can.bittiming_const = (card->ov.deprecated_btr) ?
> +				&acc_bittiming_const_pci402_old :
> +				&acc_bittiming_const_pci402;
> +		priv->can.do_set_bittiming = acc_set_bittiming;
> +		priv->can.do_set_mode = acc_set_mode;
> +		priv->can.do_get_berr_counter = acc_get_berr_counter;
> +
> +		priv->core = core;
> +		priv->ov = &card->ov;
> +
> +		err = register_candev(netdev);
> +		if (err)

free the candev here.....

> +			goto failure;
> +
> +		netdev_info(netdev, "registered\n");
> +		num_register_ok++;
> +	}
> +
> +	return 0;
> +
> +failure:
> +	for (i = 0; i < card->ov.active_cores; ++i) {

...and iterate backwards: for(i--; i >= 0; i--)

then you don't need num_registered_ok anymore.

> +		struct acc_core *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 pci402_finish_cores(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +	int i;
> +
> +	for (i = 0; i < card->ov.active_cores; ++i) {
> +		struct acc_core *core = &card->core[i];
> +
> +		unregister_candev(core->net_dev);
> +		free_candev(core->net_dev);
> +		core->net_dev = NULL;
> +	}
> +}
> +
> +static int pci402_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +	struct pci402_card *card = NULL;
> +	int err;
> +
> +	BUILD_BUG_ON(PCI402_DMA_FIFO_ITEMSIZE != sizeof(struct acc_bmmsg));
> +
> +	err = pci_enable_device(pdev);
> +	if (err)
> +		goto failure;
> +
> +	pci_set_master(pdev);
> +
> +	card = kzalloc(sizeof(*card), GFP_KERNEL);
devm_kzalloc()
> +	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, PCI402_BAR, PCI402_IO_LEN_TOTAL);
> +	if (!card->addr) {
> +		err = -ENOMEM;
> +		goto failure_release_regions;
> +	}
> +
> +	err = pci402_init_card(pdev);
> +	if (err)
> +		goto failure_unmap;
> +
> +	err = pci402_init_dma(pdev);
> +	if (err)
> +		goto failure_unmap;
> +
> +	err = pci402_init_interrupt(pdev);
> +	if (err)
> +		goto failure_finish_dma;
> +
> +	err = pci402_init_cores(pdev);
> +	if (err)
> +		goto failure_finish_interrupt;
> +
> +	return 0;
> +
> +failure_finish_interrupt:
> +	pci402_finish_interrupt(pdev);
> +
> +failure_finish_dma:
> +	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 pci402_remove(struct pci_dev *pdev)
> +{
> +	struct pci402_card *card = pci_get_drvdata(pdev);
> +
> +	pci402_finish_interrupt(pdev);
> +	pci402_finish_cores(pdev);
> +	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 pci402_tbl[] = {
> +	{ PCI_VENDOR_ID_ESDGMBH, ESD_PCI_DEVICE_ID_PCIE402,
> +			PCI_ANY_ID, PCI_ANY_ID, },
> +	{ 0, }
> +};
> +
> +static struct pci_driver pci402_driver = {
> +	.name = DRV_NAME,
> +	.id_table = pci402_tbl,
> +	.probe = pci402_probe,
> +	.remove = pci402_remove,
> +};
> +
> +module_pci_driver(pci402_driver);
> +
> +MODULE_DESCRIPTION("linux-can driver for esd CAN PCIe/402 cards");
> +MODULE_AUTHOR("Thomas Körper <thomas.koerper@esd.eu>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c
> new file mode 100644
> index 0000000..46feae6
> --- /dev/null
> +++ b/drivers/net/can/esd/esdacc.c
> @@ -0,0 +1,600 @@
> +/* Copyright (C) 2015 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/ktime.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include "esdacc.h"
> +
> +static void acc_resetmode_enter(struct acc_core *core)
> +{
> +	int i;
> +
> +	acc_set_bits(core, ACC_CORE_OF_CTRL_MODE,
> +		     ACC_REG_CONTROL_MASK_MODE_RESETMODE);
> +
> +	for (i = 0; i < 10; ++i) {
> +		if (acc_resetmode_entered(core))
> +			return;
> +
> +		udelay(5);
> +	}
> +
> +	netdev_warn(core->net_dev, "Entering reset mode timed out\n");
> +}
> +
> +static void acc_resetmode_leave(struct acc_core *core)
> +{
> +	int i;
> +
> +	acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE,
> +		       ACC_REG_CONTROL_MASK_MODE_RESETMODE);
> +
> +	for (i = 0; i < 10; ++i) {
> +		if (!acc_resetmode_entered(core))
> +			return;
> +
> +		udelay(5);
> +	}
> +
> +	netdev_warn(core->net_dev, "Leaving reset mode timed out\n");
> +}
> +
> +static int acc_txq_isready(struct acc_core *core)
> +{
> +	u8 fifo_level = (u8)(acc_read32(core, ACC_CORE_OF_TXFIFO_STATUS) >> 16);
> +
> +	return (fifo_level < core->tx_fifo_size - 1);
> +}
> +
> +static void acc_txq_put(struct acc_core *core, u32 esd_id, u8 esd_len,
> +			const void *data)
> +{
> +	acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_0,
> +			   *((const u32 *)data));
> +	acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_1,
> +			   *((const u32 *)(data + 4)));
> +	acc_write32(core, ACC_CORE_OF_TXFIFO_DLC, esd_len);
> +	/* CAN id must be written at last: */
> +	acc_write32(core, ACC_CORE_OF_TXFIFO_ID, esd_id);
> +}
> +
> +static ktime_t acc_ts_to_ktime(struct acc_ov *ov, u64 ts)
> +{
> +	unsigned long long ns;
> +	unsigned int rem;
> +
> +	rem = do_div(ts, ov->timestamp_frequency);
> +	ns = ts * NSEC_PER_SEC;
> +
> +	ts = (unsigned long long)rem * NSEC_PER_SEC;
> +	do_div(ts, ov->timestamp_frequency);
> +	ns += ts;
> +
> +	return ns_to_ktime(ns);
> +}
> +
> +void acc_init_ov(struct acc_ov *ov, struct device *dev)
> +{
> +	u32 temp;
> +
> +	temp = acc_ov_read32(ov, ACC_OV_OF_VERSION);
> +	ov->features = (u16)(temp >> 16);
> +	ov->version = (u16)temp;
> +
> +	temp = acc_ov_read32(ov, ACC_OV_OF_INFO);
> +	ov->active_cores = (u8)(temp >> 8);
> +	ov->total_cores = (u8)temp;
> +
> +	ov->core_frequency = acc_ov_read32(ov, ACC_OV_OF_CANCORE_FREQ);
> +	ov->timestamp_frequency = acc_ov_read32(ov, ACC_OV_OF_TS_FREQ_LO);
> +
> +	dev_info(dev,
> +		 "FPGA v%u, freq: %u/%u, feat/strap: 0x%x/0x%x, cores: %u/%u\n",
> +		 ov->version, ov->core_frequency, ov->timestamp_frequency,
> +		 ov->features, acc_ov_read32(ov, ACC_OV_OF_INFO) >> 16,
> +		 ov->active_cores, ov->total_cores);
> +}
> +
> +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, const void *mem)
> +{
> +	int i;
> +
> +	/* DMA buffer layout:
> +	 * +-----------------------+
> +	 * | FIFO Card/Overview    |
> +	 * |                       |
> +	 * +-----------------------+
> +	 * | FIFO Core0            |
> +	 * |                       |
> +	 * +-----------------------+
> +	 * | FIFO Core1            |
> +	 * |                       |
> +	 * +-----------------------+
> +	 * | ...                   |
> +	 * |                       |
> +	 * +-----------------------+
> +	 * | irq_cnt Card/Overview |
> +	 * +-----------------------+
> +	 * | irq_cnt Core0         |
> +	 * +-----------------------+
> +	 * | irq_cnt Core1         |
> +	 * +-----------------------+
> +	 * | ...                   |
> +	 * +-----------------------+
> +	 */
> +	ov->bmfifo.messages = mem;
> +	ov->bmfifo.irq_cnt = mem + (ov->total_cores + 1) * ACC_CORE_DMABUF_SIZE;
> +
> +	for (i = 0; i < ov->active_cores; ++i) {
> +		struct acc_core *core = &cores[i];
> +
> +		core->bmfifo.messages = mem + (i + 1) * ACC_CORE_DMABUF_SIZE;
> +		core->bmfifo.irq_cnt = ov->bmfifo.irq_cnt + 1 + i;
> +	}
> +}
> +
> +int acc_open(struct net_device *netdev)
> +{
> +	struct acc_net_priv *priv = netdev_priv(netdev);
> +	u32 ctrl_mode;
> +	int err;
> +
> +	err = open_candev(netdev);
> +	if (err)
> +		return err;
> +
> +	ctrl_mode = ACC_REG_CONTROL_MASK_IE_RXTX |
> +			ACC_REG_CONTROL_MASK_IE_TXERROR |
> +			ACC_REG_CONTROL_MASK_IE_ERRPASS |
> +			ACC_REG_CONTROL_MASK_IE_OVERRUN |
> +			ACC_REG_CONTROL_MASK_IE_ERROR;
> +
> +	if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +		ctrl_mode |= ACC_REG_CONTROL_MASK_IE_BUSERR;
> +
> +	acc_set_bits(priv->core, ACC_CORE_OF_CTRL_MODE, ctrl_mode);
> +
> +	netif_start_queue(netdev);
> +	return 0;
> +}
> +
> +int acc_close(struct net_device *netdev)
> +{
> +	struct acc_net_priv *priv = netdev_priv(netdev);
> +
> +	acc_clear_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
> +		       ACC_REG_CONTROL_MASK_IE_RXTX |
> +		       ACC_REG_CONTROL_MASK_IE_TXERROR |
> +		       ACC_REG_CONTROL_MASK_IE_OVERRUN);
> +
> +	netif_stop_queue(netdev);
> +	priv->can.state = CAN_STATE_STOPPED;
> +
> +	close_candev(netdev);
> +	return 0;
> +}
> +
> +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> +	struct acc_net_priv *priv = netdev_priv(netdev);
> +	struct acc_core *core = priv->core;
> +	struct can_frame *cf = (struct can_frame *)skb->data;
> +	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) || !acc_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 |= ACC_CAN_RTR_FLAG;
> +
> +	if (cf->can_id & CAN_EFF_FLAG) {
> +		esd_id = cf->can_id & CAN_EFF_MASK;
> +		esd_id |= ACC_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;
> +
> +	acc_txq_put(core, esd_id, esd_len, cf->data);
> +
> +	return NETDEV_TX_OK;
> +}
> +
> +int acc_get_berr_counter(const struct net_device *netdev,
> +			 struct can_berr_counter *bec)
> +{
> +	struct acc_net_priv *priv = netdev_priv(netdev);
> +	u32 core_status = acc_read32(priv->core, ACC_CORE_OF_STATUS);
> +
> +	bec->txerr = (u8)(core_status >> 8);
> +	bec->rxerr = (u8)core_status;
> +
> +	return 0;
> +}
> +
> +int acc_set_mode(struct net_device *netdev, enum can_mode mode)
> +{
> +	struct acc_net_priv *priv = netdev_priv(netdev);
> +
> +	switch (mode) {
> +	case CAN_MODE_START:
> +		acc_resetmode_leave(priv->core);
> +		netif_wake_queue(netdev);
> +		break;
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +
> +	return 0;
> +}
> +
> +int acc_set_bittiming(struct net_device *netdev)
> +{
> +	struct acc_net_priv *priv = netdev_priv(netdev);
> +	struct can_bittiming *bt = &priv->can.bittiming;
> +	u32 btr;
> +	u8 brp;
> +
> +	brp = (u8)(bt->brp - 1);
> +	acc_resetmode_enter(priv->core);
> +
> +	if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> +		acc_set_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
> +			     ACC_REG_CONTROL_MASK_MODE_LOM);
> +	else
> +		acc_clear_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
> +			       ACC_REG_CONTROL_MASK_MODE_LOM);
> +
> +	if (priv->ov->deprecated_btr) {
> +		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;
> +
> +		acc_write32(priv->core, ACC_CORE_OF_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;
> +
> +		acc_write32(priv->core, ACC_CORE_OF_BTR, brp);
> +		acc_write32(priv->core, ACC_CORE_OF_BTR_FD, btr);
> +	}
> +
> +	acc_resetmode_leave(priv->core);
> +	priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +	return 0;
> +}
> +
> +static void handle_core_msg_rxtxdone(struct acc_core *core,
> +				     const struct acc_bmmsg_rxtxdone *msg)
> +{
> +	struct acc_net_priv *priv = netdev_priv(core->net_dev);
> +	struct net_device_stats *stats = &core->net_dev->stats;
> +
> +	if (msg->dlc.rxtx.len & ACC_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 & ACC_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) {
> +			stats->rx_dropped++;
> +			return;
> +		}
> +
> +		cf->can_id = msg->id & CAN_EFF_MASK;
> +		if (msg->id & ACC_CAN_EFF_FLAG)
> +			cf->can_id |= CAN_EFF_FLAG;
> +
> +		cf->can_dlc = get_can_dlc(msg->dlc.rx.len);
> +		if (msg->dlc.rx.len & ACC_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 = acc_ts_to_ktime(priv->ov, msg->timestamp);
> +		netif_rx(skb);
> +
> +		stats->rx_packets++;
> +		stats->rx_bytes += cf->can_dlc;
> +	}
> +}
> +
> +static void handle_core_msg_txabort(struct acc_core *core,
> +				    const struct acc_bmmsg_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 (!acc_resetmode_entered(core))
> +		netif_wake_queue(core->net_dev);
> +}
> +
> +static void handle_core_msg_overrun(struct acc_core *core,
> +				    const struct acc_bmmsg_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)
> +		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 handle_core_msg_buserr(struct acc_core *core,
> +				   const struct acc_bmmsg_buserr *msg)
> +{
> +	struct acc_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)
> +		return;
> +
> +	cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +	/* msg->ecc acts like SJA1000's ECC register */
> +	switch (msg->ecc & ACC_ECC_MASK) {
> +	case ACC_ECC_BIT:
> +		cf->data[2] |= CAN_ERR_PROT_BIT;
> +		break;
> +	case ACC_ECC_FORM:
> +		cf->data[2] |= CAN_ERR_PROT_FORM;
> +		break;
> +	case ACC_ECC_STUFF:
> +		cf->data[2] |= CAN_ERR_PROT_STUFF;
> +		break;
> +	default:
> +		cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +		cf->data[3] = msg->ecc & ACC_ECC_SEG;
> +		break;
> +	}
> +
> +	if ((msg->ecc & ACC_ECC_DIR) == 0)
> +		cf->data[2] |= CAN_ERR_PROT_TX;
> +
> +	netif_rx(skb);
> +	stats->rx_packets++;
> +	stats->rx_bytes += cf->can_dlc;

the skb is't valid anymore after netif_rx()
> +}
> +
> +static void
> +handle_core_msg_errstatechange(struct acc_core *core,
> +			       const struct acc_bmmsg_errstatechange *msg)
> +{
> +	struct acc_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 & ACC_REG_STATUS_MASK_STATUS_ES) != 0;
> +	is_passive = (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) != 0;
> +	is_busoff = (msg->reg_status & ACC_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;
same here
> +	}
> +
> +	if (is_busoff) {
> +		acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
> +		can_bus_off(core->net_dev);
> +	}
> +}
> +
> +static void handle_core_interrupt(struct acc_core *core)
> +{
> +	u32 msg_fifo_head = core->bmfifo.local_irq_cnt & 0xff;
> +
> +	while (core->bmfifo.msg_fifo_tail != msg_fifo_head) {
> +		const struct acc_bmmsg *msg =
> +			&core->bmfifo.messages[core->bmfifo.msg_fifo_tail];
> +
> +		switch (msg->u.msg_id) {
> +		case BM_MSG_ID_RXTXDONE:
> +			handle_core_msg_rxtxdone(core, &msg->u.rxtxdone);
> +			break;
> +
> +		case BM_MSG_ID_TXABORT:
> +			handle_core_msg_txabort(core, &msg->u.txabort);
> +			break;
> +
> +		case BM_MSG_ID_OVERRUN:
> +			handle_core_msg_overrun(core, &msg->u.overrun);
> +			break;
> +
> +		case BM_MSG_ID_BUSERR:
> +			handle_core_msg_buserr(core, &msg->u.buserr);
> +			break;
> +
> +		case BM_MSG_ID_ERRPASSIVE:
> +		case BM_MSG_ID_ERRWARN:
> +			handle_core_msg_errstatechange(core,
> +						       &msg->u.errstatechange);
> +			break;
> +
> +		default:
> +			break;
> +		}
> +
> +		core->bmfifo.msg_fifo_tail =
> +				(core->bmfifo.msg_fifo_tail + 1) & 0xff;
> +	}
> +}
> +
> +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores)
> +{
> +	u32 irqmask;
> +	int i;
> +
> +	/* 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 (*ov->bmfifo.irq_cnt != ov->bmfifo.local_irq_cnt) {
> +		irqmask |= ACC_BM_IRQ_MASK;
> +		ov->bmfifo.local_irq_cnt = *ov->bmfifo.irq_cnt;
> +	}
> +
> +	for (i = 0; i < ov->active_cores; ++i) {
> +		struct acc_core *core = &cores[i];
> +
> +		if (*core->bmfifo.irq_cnt != core->bmfifo.local_irq_cnt) {
> +			irqmask |= (ACC_BM_IRQ_MASK << (2 * (i + 1)));
> +			core->bmfifo.local_irq_cnt = *core->bmfifo.irq_cnt;
> +		}
> +	}
> +
> +	if (!irqmask)
> +		return IRQ_NONE;
> +
> +	/* At second we tell the card we're working on them by writing irqmask,
> +	 * call handle_{ov|core}_interrupt and then acknowledge the
> +	 * interrupts by writing irq_cnt:
> +	 */
> +	acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, irqmask);
> +
> +	if (irqmask & ACC_BM_IRQ_MASK) {
> +		/* handle_ov_interrupt(); - no use yet. */
> +		acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_COUNTER,
> +			       ov->bmfifo.local_irq_cnt);
> +	}
> +
> +	for (i = 0; i < ov->active_cores; ++i) {
> +		struct acc_core *core = &cores[i];
> +
> +		if (irqmask & (ACC_BM_IRQ_MASK << (2 * (i + 1)))) {
> +			handle_core_interrupt(core);
> +			acc_write32(core, ACC_OV_OF_BM_IRQ_COUNTER,
> +				    core->bmfifo.local_irq_cnt);
> +		}
> +	}
> +
> +	acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, ACC_BM_IRQ_UNMASK_ALL);
> +
> +	return IRQ_HANDLED;
> +}
> diff --git a/drivers/net/can/esd/esdacc.h b/drivers/net/can/esd/esdacc.h
> new file mode 100644
> index 0000000..97d0d3b
> --- /dev/null
> +++ b/drivers/net/can/esd/esdacc.h
> @@ -0,0 +1,428 @@
> +/* Copyright (C) 2015 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/netdevice.h>
> +#include <linux/can/dev.h>
> +
> +#define ACC_CAN_EFF_FLAG			0x20000000
> +#define ACC_CAN_RTR_FLAG			0x10
> +#define ACC_CAN_DLC_MASK			0x0f
> +
> +#define ACC_OV_OF_PROBE				0x0000
> +#define ACC_OV_OF_VERSION			0x0004
> +#define ACC_OV_OF_INFO				0x0008
> +#define ACC_OV_OF_CANCORE_FREQ			0x000c
> +#define ACC_OV_OF_TS_FREQ_LO			0x0010
> +#define ACC_OV_OF_TS_FREQ_HI			0x0014
> +#define ACC_OV_OF_IRQ_STATUS_CORES		0x0018
> +#define ACC_OV_OF_TS_CURR_LO			0x001c
> +#define ACC_OV_OF_TS_CURR_HI			0x0020
> +#define ACC_OV_OF_IRQ_STATUS			0x0028
> +#define ACC_OV_OF_MODE				0x002c
> +#define ACC_OV_OF_BM_IRQ_COUNTER		0x0070
> +#define ACC_OV_OF_BM_IRQ_MASK			0x0074
> +#define ACC_OV_OF_MSI_DATA			0x0080
> +#define ACC_OV_OF_MSI_ADDRESSOFFSET		0x0084
> +
> +#define ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE	0x00000001
> +#define ACC_OV_REG_MODE_MASK_BM_ENABLE		0x00000002
> +#define ACC_OV_REG_MODE_MASK_MODE_LED		0x00000004
> +#define ACC_OV_REG_MODE_MASK_TIMER		0x00000070
> +#define ACC_OV_REG_MODE_MASK_TIMER_ENABLE	0x00000010
> +#define ACC_OV_REG_MODE_MASK_TIMER_ONE_SHOT	0x00000020
> +#define ACC_OV_REG_MODE_MASK_TIMER_ABSOLUTE	0x00000040
> +#define ACC_OV_REG_MODE_MASK_TS_SRC		0x00000180
> +#define ACC_OV_REG_MODE_MASK_I2C_ENABLE		0x00000800
> +#define ACC_OV_REG_MODE_MASK_MSI_ENABLE		0x00004000
> +#define ACC_OV_REG_MODE_MASK_FPGA_RESET		0x80000000
> +
> +#define ACC_BM_IRQ_UNMASK_ALL			0x55555555
> +#define ACC_BM_IRQ_MASK_ALL			0xaaaaaaaa
> +#define ACC_BM_IRQ_MASK				0x2
> +#define ACC_BM_IRQ_UNMASK			0x1
> +#define ACC_BM_LENFLAG_TX			0x20
> +
> +#define ACC_CORE_OF_CTRL_MODE			0x0000
> +#define ACC_CORE_OF_STATUS_IRQ			0x0008
> +#define ACC_CORE_OF_BTR				0x000c
> +#define ACC_CORE_OF_BTR_FD			0x0010
> +#define ACC_CORE_OF_BTR_FDF			0x0014
> +#define ACC_CORE_OF_STATUS			0x0030
> +#define ACC_CORE_OF_TXFIFO_CONFIG		0x0048
> +#define ACC_CORE_OF_TXFIFO_STATUS		0x004c
> +#define ACC_CORE_OF_TX_STATUS_IRQ		0x0050
> +#define ACC_CORE_OF_TX_ABORT_MASK		0x0054
> +#define ACC_CORE_OF_BM_IRQ_COUNTER		0x0070
> +#define ACC_CORE_OF_TXFIFO_ID			0x00c0
> +#define ACC_CORE_OF_TXFIFO_DLC			0x00c4
> +#define ACC_CORE_OF_TXFIFO_DATA_0		0x00c8
> +#define ACC_CORE_OF_TXFIFO_DATA_1		0x00cc
> +
> +/* ecc value of esdACC equals SJA1000's ECC register */
> +#define ACC_ECC_SEG				0x1f
> +#define ACC_ECC_DIR				0x20
> +#define ACC_ECC_BIT				0x00
> +#define ACC_ECC_FORM				0x40
> +#define ACC_ECC_STUFF				0x80
> +#define ACC_ECC_MASK				0xc0
> +
> +#define ACC_REG_CONTROL_IDX_MODE_RESETMODE	0
> +#define ACC_REG_CONTROL_IDX_MODE_LOM		1
> +#define ACC_REG_CONTROL_IDX_MODE_STM		2
> +#define ACC_REG_CONTROL_IDX_MODE_TRANSEN	5
> +#define ACC_REG_CONTROL_IDX_MODE_TS		6
> +#define ACC_REG_CONTROL_IDX_MODE_SCHEDULE	7
> +#define ACC_REG_CONTROL_MASK_MODE_RESETMODE	\
> +				BIT(ACC_REG_CONTROL_IDX_MODE_RESETMODE)
> +#define ACC_REG_CONTROL_MASK_MODE_LOM		\
> +				BIT(ACC_REG_CONTROL_IDX_MODE_LOM)
> +#define ACC_REG_CONTROL_MASK_MODE_STM		\
> +				BIT(ACC_REG_CONTROL_IDX_MODE_STM)
> +#define ACC_REG_CONTROL_MASK_MODE_TRANSEN	\
> +				BIT(ACC_REG_CONTROL_IDX_MODE_TRANSEN)
> +#define ACC_REG_CONTROL_MASK_MODE_TS		\
> +				BIT(ACC_REG_CONTROL_IDX_MODE_TS)
> +#define ACC_REG_CONTROL_MASK_MODE_SCHEDULE	\
> +				BIT(ACC_REG_CONTROL_IDX_MODE_SCHEDULE)
> +
> +#define ACC_REG_CONTROL_IDX_IE_RXTX	8
> +#define ACC_REG_CONTROL_IDX_IE_TXERROR	9
> +#define ACC_REG_CONTROL_IDX_IE_ERROR	10
> +#define ACC_REG_CONTROL_IDX_IE_OVERRUN	11
> +#define ACC_REG_CONTROL_IDX_IE_TSI	12
> +#define ACC_REG_CONTROL_IDX_IE_ERRPASS	13
> +#define ACC_REG_CONTROL_IDX_IE_BUSERR	15
> +#define ACC_REG_CONTROL_MASK_IE_RXTX	BIT(ACC_REG_CONTROL_IDX_IE_RXTX)
> +#define ACC_REG_CONTROL_MASK_IE_TXERROR	BIT(ACC_REG_CONTROL_IDX_IE_TXERROR)
> +#define ACC_REG_CONTROL_MASK_IE_ERROR	BIT(ACC_REG_CONTROL_IDX_IE_ERROR)
> +#define ACC_REG_CONTROL_MASK_IE_OVERRUN	BIT(ACC_REG_CONTROL_IDX_IE_OVERRUN)
> +#define ACC_REG_CONTROL_MASK_IE_TSI	BIT(ACC_REG_CONTROL_IDX_IE_TSI)
> +#define ACC_REG_CONTROL_MASK_IE_ERRPASS	BIT(ACC_REG_CONTROL_IDX_IE_ERRPASS)
> +#define ACC_REG_CONTROL_MASK_IE_BUSERR	BIT(ACC_REG_CONTROL_IDX_IE_BUSERR)
> +
> +#define ACC_REG_STATUS_IDX_STATUS_DOS	16
> +#define ACC_REG_STATUS_IDX_STATUS_ES	17
> +#define ACC_REG_STATUS_IDX_STATUS_EP	18
> +#define ACC_REG_STATUS_IDX_STATUS_BS	19
> +#define ACC_REG_STATUS_IDX_STATUS_RBS	20
> +#define ACC_REG_STATUS_IDX_STATUS_RS	21
> +#define ACC_REG_STATUS_MASK_STATUS_DOS	BIT(ACC_REG_STATUS_IDX_STATUS_DOS)
> +#define ACC_REG_STATUS_MASK_STATUS_ES	BIT(ACC_REG_STATUS_IDX_STATUS_ES)
> +#define ACC_REG_STATUS_MASK_STATUS_EP	BIT(ACC_REG_STATUS_IDX_STATUS_EP)
> +#define ACC_REG_STATUS_MASK_STATUS_BS	BIT(ACC_REG_STATUS_IDX_STATUS_BS)
> +#define ACC_REG_STATUS_MASK_STATUS_RBS	BIT(ACC_REG_STATUS_IDX_STATUS_RBS)
> +#define ACC_REG_STATUS_MASK_STATUS_RS	BIT(ACC_REG_STATUS_IDX_STATUS_RS)
> +
> +#define ACC_CORE_DMABUF_SIZE		(256 * 32) /* 256 messages */
> +
> +enum acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_timeslice {
> +	u8 msg_id;
> +	u8 txfifo_level;
> +	u8 reserved1[2];
> +	u8 txtsfifo_level;
> +	u8 reserved2[3];
> +	u64 ts;
> +	u32 reserved3[4];
> +} __packed;
> +
> +struct acc_bmmsg_hwtimer {
> +	u8 msg_id;
> +	u8 reserved1[3];
> +	u32 reserved2[1];
> +	u64 timer;
> +	u32 reserved3[4];
> +} __packed;
> +
> +struct acc_bmmsg_hotplug {
> +	u8 msg_id;
> +	u8 reserved1[3];
> +	u32 reserved2[7];
> +} __packed;
> +
> +struct acc_bmmsg_canfddata {
> +	u8 msg_id;
> +	u8 reserved1[3];
> +	union {
> +		u8 ui8[28];
> +		u32 ui32[7];
> +	} d;
> +} __packed;
> +
> +struct acc_bmmsg {
> +	union {
> +		u8 msg_id;
> +		struct acc_bmmsg_rxtxdone rxtxdone;
> +		struct acc_bmmsg_canfddata canfddata;
> +		struct acc_bmmsg_txabort txabort;
> +		struct acc_bmmsg_overrun overrun;
> +		struct acc_bmmsg_buserr buserr;
> +		struct acc_bmmsg_errstatechange errstatechange;
> +		struct acc_bmmsg_timeslice timeslice;
> +		struct acc_bmmsg_hwtimer hwtimer;
> +	} u;
> +};
> +
> +struct acc_bmfifo {
> +	const struct acc_bmmsg *messages;
> +	/* Bits0..7: bm_fifo head index */
> +	const u32 *irq_cnt;
> +	u32 local_irq_cnt;
> +	u32 msg_fifo_tail;
> +};
> +
> +struct acc_core {
> +	void __iomem *addr;
> +	struct net_device *net_dev;
> +	struct acc_bmfifo bmfifo;
> +	u8 tx_fifo_size;
> +	u8 tx_fifo_head;
> +	u8 tx_fifo_tail;
> +};
> +
> +struct acc_ov {
> +	void __iomem *addr;
> +	struct acc_bmfifo bmfifo;
> +	u32 timestamp_frequency;
> +	u32 core_frequency;
> +	u16 version;
> +	u16 features;
> +	u8 total_cores;
> +	u8 active_cores;
> +	bool deprecated_btr;
> +};
> +
> +struct acc_net_priv {
> +	struct can_priv can; /* must be the first member! */
> +	struct acc_core *core;
> +	struct acc_ov *ov;
> +};
> +
> +static inline u32 acc_read32(struct acc_core *core, unsigned short offs)
> +{
> +	return ioread32be(core->addr + offs);
> +}
> +
> +static inline void acc_write32(struct acc_core *core,
> +			       unsigned short offs, u32 v)
> +{
> +	iowrite32be(v, core->addr + offs);
> +}
> +
> +static inline void acc_write32_noswap(struct acc_core *core,
> +				      unsigned short offs, u32 v)
> +{
> +	iowrite32(v, core->addr + offs);
> +}
> +
> +static inline void acc_set_bits(struct acc_core *core,
> +				unsigned short offs, u32 mask)
> +{
> +	u32 v = acc_read32(core, offs);
> +
> +	v |= mask;
> +	acc_write32(core, offs, v);
> +}
> +
> +static inline void acc_clear_bits(struct acc_core *core,
> +				  unsigned short offs, u32 mask)
> +{
> +	u32 v = acc_read32(core, offs);
> +
> +	v &= ~mask;
> +	acc_write32(core, offs, v);
> +}
> +
> +static inline int acc_resetmode_entered(struct acc_core *core)
> +{
> +	u32 ctrl = acc_read32(core, ACC_CORE_OF_CTRL_MODE);
> +
> +	return (ctrl & ACC_REG_CONTROL_MASK_MODE_RESETMODE) != 0;
> +}
> +
> +static inline u32 acc_ov_read32(struct acc_ov *ov, unsigned short offs)
> +{
> +	return ioread32be(ov->addr + offs);
> +}
> +
> +static inline void acc_ov_write32(struct acc_ov *ov,
> +				  unsigned short offs, u32 v)
> +{
> +	iowrite32be(v, ov->addr + offs);
> +}
> +
> +static inline void acc_ov_set_bits(struct acc_ov *ov,
> +				   unsigned short offs, u32 b)
> +{
> +	u32 v = acc_ov_read32(ov, offs);
> +
> +	v |= b;
> +	acc_ov_write32(ov, offs, v);
> +}
> +
> +static inline void acc_ov_clear_bits(struct acc_ov *ov,
> +				     unsigned short offs, u32 b)
> +{
> +	u32 v = acc_ov_read32(ov, offs);
> +
> +	v &= ~b;
> +	acc_ov_write32(ov, offs, v);
> +}
> +
> +static inline void acc_reset_fpga(struct acc_ov *ov)
> +{
> +	acc_ov_write32(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_FPGA_RESET);
> +
> +	/* Also reset I²C, to re-detect card addons at every driver start: */
> +	acc_ov_clear_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
> +	mdelay(2);
> +	acc_ov_set_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
> +	mdelay(10);
> +}
> +
> +void acc_init_ov(struct acc_ov *ov, struct device *dev);
> +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores,
> +		     const void *mem);
> +int acc_open(struct net_device *netdev);
> +int acc_close(struct net_device *netdev);
> +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev);
> +int acc_get_berr_counter(const struct net_device *netdev,
> +			 struct can_berr_counter *bec);
> +int acc_set_mode(struct net_device *netdev, enum can_mode mode);
> +int acc_set_bittiming(struct net_device *netdev);
> +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores);
> +
> +/* Used in PCIe/402 cards with FPGA Version < 0x0032 */
> +static const struct can_bittiming_const acc_bittiming_const_pci402_old = {
> +	.name = "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 acc_bittiming_const_pci402 = {
> +	.name = "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 acc_netdev_ops = {
> +	.ndo_open = acc_open,
> +	.ndo_stop = acc_close,
> +	.ndo_start_xmit = acc_start_xmit,
> +	.ndo_change_mtu = can_change_mtu
> +};
> 

Marc

-- 
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card
  2015-03-17 21:27 ` Marc Kleine-Budde
@ 2015-03-18  5:08   ` Thomas Körper
  0 siblings, 0 replies; 9+ messages in thread
From: Thomas Körper @ 2015-03-18  5:08 UTC (permalink / raw)
  To: Marc Kleine-Budde, Ahmed S. Darwish; +Cc: linux-can, Andri Yngvason

Hi Marc, Hi Darwish,

thanks for the input. I'll need time for the next patch now :)

First I'll fix the TX flow control path as both of you mentioned.

Regarding Marc's notes:
basically all regs are BE and accessed by the acc_read() functions etc.
Additionally the card can handle endianess for all busmastered data
itself, that what's done by setting the bit in the __LITTLE_ENDIAN
path - so busmastered data is always host endianess.
But with that driver it was not actually tested on a BE system.

Splitting the driver into two files because Oliver suggested to :)
http://permalink.gmane.org/gmane.linux.can/6684

Will re-arrange files as mentioned too, and apply all other suggestions.


Regards,
    Thomas


-----Ursprüngliche Nachricht-----
Von: Marc Kleine-Budde [mailto:mkl@pengutronix.de]
Gesendet: Dienstag, 17. März 2015 22:28
An: Thomas Körper; linux-can@vger.kernel.org
Betreff: Re: [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card

On 03/16/2015 01:15 PM, Thomas Körper wrote:
> This patch adds support for the CAN PCIe/402 card from esd electronic
> system design gmbh: FPGA based CAN controller, supporting up to 4
> nets, MSI and bus mastering. See http://esd.eu/en/products/can-pcie402
> for details.

I assume all packed strucks are coming from the hardware, do you have to
take care of the endianess? I'll consult a colleague of mine, too. Can
you explain me how endianess is handled in the driver/hw? What's the
purpose of the __LITTLE_ENDIAN? Have you actually tested on any BE platform?

Your TX path flow control is broken, you should stop the queue before
enabling the tx-ready IRQ (or sending the CAN frame) if all your HW
buffers are used. Enable the queue after freeing your HW buffer in tx
complete.

Why do you split your driver into two .c files? Probably to have a
separation of low level functions, but this can be done in one .c file,
too. But you might convince me in this point :). Your .h file can be
cleaned up, please put only the bare minimum into the header, defines
and structs that are only used in one .c file should go there.

Please re-arange your .c file, so that you don't need any forward
declarations of functions.

> 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      |   7 +
>  drivers/net/can/esd/Makefile     |   3 +
>  drivers/net/can/esd/esd402_pci.c | 453 +++++++++++++++++++++++++++++
>  drivers/net/can/esd/esdacc.c     | 600 +++++++++++++++++++++++++++++++++++++++
>  drivers/net/can/esd/esdacc.h     | 428 ++++++++++++++++++++++++++++
>  7 files changed, 1494 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_pci.c
>  create mode 100644 drivers/net/can/esd/esdacc.c
>  create mode 100644 drivers/net/can/esd/esdacc.h
>
> diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig
> index 58808f6..54d9ce8 100644
> --- a/drivers/net/can/Kconfig
> +++ b/drivers/net/can/Kconfig
> @@ -153,6 +153,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 c533c62..c691233 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..cebd5ae
> --- /dev/null
> +++ b/drivers/net/can/esd/Kconfig
> @@ -0,0 +1,7 @@
> +config CAN_ESD_402_PCI
> +     tristate "esd gmbh CAN PCIe/402 support"
> +     default y

I think no default would be a sensible default here.

> +     depends on PCI && HAS_DMA
> +     ---help---
> +       Support for CAN PCIe/402 card from esd electronic system design gmbh
> +       (http://esd.eu/en)
> diff --git a/drivers/net/can/esd/Makefile b/drivers/net/can/esd/Makefile
> new file mode 100644
> index 0000000..80d970e
> --- /dev/null
> +++ b/drivers/net/can/esd/Makefile
> @@ -0,0 +1,3 @@
> +esd_402_pci-y := esdacc.o esd402_pci.o
> +
> +obj-$(CONFIG_CAN_ESD_402_PCI) += esd_402_pci.o
> diff --git a/drivers/net/can/esd/esd402_pci.c b/drivers/net/can/esd/esd402_pci.c
> new file mode 100644
> index 0000000..d2b5600
> --- /dev/null
> +++ b/drivers/net/can/esd/esd402_pci.c
> @@ -0,0 +1,453 @@
> +/* Copyright (C) 2015 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/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 "esdacc.h"
> +
> +#define DRV_NAME                     "esd_402_pci"
> +
> +#define ESD_PCI_DEVICE_ID_PCIE402    0x0402
> +
> +#define PCI402_FPGA_VER_MIN          0x0025
> +#define PCI402_FPGA_VER_NEWBTR               0x0032
> +#define PCI402_MAX_CORES             4
> +#define PCI402_BAR                   0
> +#define PCI402_IO_OV_OFFS            0
> +#define PCI402_IO_PCIEP_OFFS         0x10000
> +#define PCI402_IO_LEN_TOTAL          0x20000
> +#define PCI402_IO_LEN_CORE           0x2000
> +#define PCI402_PCICFG_MSICAP_CSR     0x52
> +#define PCI402_PCICFG_MSICAP_ADDR    0x54
> +#define PCI402_PCICFG_MSICAP_DATA    0x5c
> +
> +#define PCI402_DMA_MASK                      (DMA_BIT_MASK(32) & 0xffff0000)
> +#define PCI402_DMA_SIZE                      ALIGN(0x10000, PAGE_SIZE)
> +#define PCI402_DMA_FIFO_ITEMSIZE     32
> +
> +#define PCI402_PCIEP_OF_INT_ENABLE   0x0050
> +#define PCI402_PCIEP_OF_BM_ADDR_LO   0x1000
> +#define PCI402_PCIEP_OF_BM_ADDR_HI   0x1004
> +#define PCI402_PCIEP_OF_MSI_ADDR_LO  0x1008
> +#define PCI402_PCIEP_OF_MSI_ADDR_HI  0x100c
> +
> +struct pci402_card {
> +     /* Actually mapped io space, all other iomem derived from this */
> +     void __iomem *addr;
> +     void __iomem *addr_pciep;
> +
> +     void *dma_buf;
> +     dma_addr_t dma_hnd;
> +
> +     struct acc_ov ov;
> +     struct acc_core core[PCI402_MAX_CORES];
> +
> +     bool msi_enabled;
> +};
> +
> +static irqreturn_t pci402_interrupt(int irq, void *dev_id)
> +{
> +     struct pci402_card *card = pci_get_drvdata((struct pci_dev *)dev_id);

The cast is not needed.
> +
> +     return acc_card_interrupt(&card->ov, card->core);

Can you integrate acc_card_interrupt() here?

> +}
> +
> +static int pci402_set_msiconfig(struct pci_dev *pdev)
> +{
> +     struct 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, PCI402_PCICFG_MSICAP_CSR, &csr);
> +     if (err)
> +             goto failed;
> +
> +     err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP_ADDR, &addr_lo);
> +     if (err)
> +             goto failed;
> +     err = pci_read_config_dword(pdev, PCI402_PCICFG_MSICAP_ADDR + 4,
> +                                 &addr_hi);
> +     if (err)
> +             goto failed;
> +
> +     err = pci_read_config_dword(pdev, 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 + PCI402_PCIEP_OF_MSI_ADDR_LO);
> +     iowrite32(addr_hi, card->addr_pciep + PCI402_PCIEP_OF_MSI_ADDR_HI);
> +     acc_ov_write32(&card->ov, ACC_OV_OF_MSI_ADDRESSOFFSET, addr_lo_offs);
> +     acc_ov_write32(&card->ov, ACC_OV_OF_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;

Please use sensible -ERRNO in the kernel.

> +}
> +
> +static int pci402_init_card(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +
> +     card->ov.addr = card->addr + PCI402_IO_OV_OFFS;
> +     card->addr_pciep = card->addr + PCI402_IO_PCIEP_OFFS;
> +
> +     acc_reset_fpga(&card->ov);
> +     acc_init_ov(&card->ov, &pdev->dev);
> +
> +     if (card->ov.version < PCI402_FPGA_VER_MIN) {
> +             dev_err(&pdev->dev,
> +                     "FPGA version (0x%.4x) outdated, please update\n",
> +                     card->ov.version);
> +             return -EINVAL;
> +     }
> +
> +     if (card->ov.active_cores > PCI402_MAX_CORES) {
> +             dev_warn(&pdev->dev, "Card has more active cores than supported by driver, %u core(s) will be ignored\n",
> +                      card->ov.active_cores - PCI402_MAX_CORES);
> +             card->ov.active_cores = PCI402_MAX_CORES;
> +     }
> +
> +     card->ov.deprecated_btr = card->ov.version < PCI402_FPGA_VER_NEWBTR;
> +
> +#ifdef __LITTLE_ENDIAN
> +     /* So card converts all busmastered data to LE for us: */
> +     acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
> +                     ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE);
> +#endif
> +
> +     return 0;
> +}
> +
> +static int pci402_init_interrupt(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +     int err;
> +
> +     err = pci_enable_msi(pdev);
> +     if (!err) {
> +             err = pci402_set_msiconfig(pdev);
> +             if (!err) {
> +                     card->msi_enabled = 1;

please use true/false as it's a bool

> +                     acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
> +                                     ACC_OV_REG_MODE_MASK_MSI_ENABLE);
> +                     dev_info(&pdev->dev, "MSI enabled\n");
> +             }
> +     }
> +
> +     err = request_irq(pdev->irq, pci402_interrupt,
> +                       IRQF_SHARED, DRV_NAME, pdev);

please use dev->name instead of DRV_NAME
please use devm_request_irq function

> +     if (err)
> +             goto failure_msidis;
> +
> +     iowrite32(1, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
> +
> +     return 0;
> +
> +failure_msidis:
> +     if (card->msi_enabled) {
> +             acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
> +                               ACC_OV_REG_MODE_MASK_MSI_ENABLE);
> +             pci_disable_msi(pdev);
> +             card->msi_enabled = 0;
> +     }
> +
> +     return err;
> +}
> +
> +static void pci402_finish_interrupt(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +
> +     iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_INT_ENABLE);
> +     free_irq(pdev->irq, pdev);
> +
> +     if (card->msi_enabled) {
> +             acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
> +                               ACC_OV_REG_MODE_MASK_MSI_ENABLE);
> +             pci_disable_msi(pdev);
> +             card->msi_enabled = 0;
> +     }
> +}
> +
> +static int pci402_init_dma(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +     int err;
> +
> +     err = pci_set_consistent_dma_mask(pdev, PCI402_DMA_MASK);
> +     if (err) {
> +             dev_err(&pdev->dev, "dma set mask failed!");
> +             return err;
> +     }
> +
> +     card->dma_buf = pci_alloc_consistent(pdev, PCI402_DMA_SIZE,
> +                                          &card->dma_hnd);
> +     if (!card->dma_buf) {
> +             dev_err(&pdev->dev, "dma alloc failed!");
> +             return -ENOMEM;
> +     }
> +
> +     acc_init_bm_ptr(&card->ov, card->core, card->dma_buf);
> +
> +     iowrite32((u32)card->dma_hnd,
> +               card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
> +     iowrite32((u32)(card->dma_hnd >> 32),
> +               card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);
> +
> +     acc_ov_set_bits(&card->ov, ACC_OV_OF_MODE,
> +                     ACC_OV_REG_MODE_MASK_BM_ENABLE);
> +
> +     return 0;
> +}
> +
> +static void pci402_finish_dma(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +     int i;
> +
> +     acc_ov_clear_bits(&card->ov, ACC_OV_OF_MODE,
> +                       ACC_OV_REG_MODE_MASK_BM_ENABLE);
> +
> +     iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_LO);
> +     iowrite32(0, card->addr_pciep + PCI402_PCIEP_OF_BM_ADDR_HI);
> +
> +     card->ov.bmfifo.messages = NULL;
> +     card->ov.bmfifo.irq_cnt = NULL;
> +     for (i = 0; i < card->ov.active_cores; ++i) {
> +             struct acc_core *core = &card->core[i];
> +
> +             core->bmfifo.messages = NULL;
> +             core->bmfifo.irq_cnt = NULL;
> +     }
> +
> +     pci_free_consistent(pdev, PCI402_DMA_SIZE, card->dma_buf,
> +                         card->dma_hnd);
> +     card->dma_buf = NULL;
> +}
> +
> +static int pci402_init_cores(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +     int num_register_ok = 0;
> +     int err;
> +     int i;
> +
> +     for (i = 0; i < card->ov.active_cores; ++i) {
i++ common style (and doesn't make a difference here)
> +             struct acc_core *core = &card->core[i];
> +             struct acc_net_priv *priv;
> +             struct net_device *netdev;
> +             u32 fifo_config;
> +
> +             core->addr = card->ov.addr + (i + 1) * PCI402_IO_LEN_CORE;
> +
> +             fifo_config = acc_read32(core, ACC_CORE_OF_TXFIFO_CONFIG);
> +             core->tx_fifo_size = (u8)(fifo_config >> 24);
cast not needed
> +             BUG_ON(core->tx_fifo_size <= 1);

better exit more gracefully here

> +
> +             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 = &acc_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->ov.core_frequency / 2;
> +             priv->can.bittiming_const = (card->ov.deprecated_btr) ?
> +                             &acc_bittiming_const_pci402_old :
> +                             &acc_bittiming_const_pci402;
> +             priv->can.do_set_bittiming = acc_set_bittiming;
> +             priv->can.do_set_mode = acc_set_mode;
> +             priv->can.do_get_berr_counter = acc_get_berr_counter;
> +
> +             priv->core = core;
> +             priv->ov = &card->ov;
> +
> +             err = register_candev(netdev);
> +             if (err)

free the candev here.....

> +                     goto failure;
> +
> +             netdev_info(netdev, "registered\n");
> +             num_register_ok++;
> +     }
> +
> +     return 0;
> +
> +failure:
> +     for (i = 0; i < card->ov.active_cores; ++i) {

...and iterate backwards: for(i--; i >= 0; i--)

then you don't need num_registered_ok anymore.

> +             struct acc_core *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 pci402_finish_cores(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +     int i;
> +
> +     for (i = 0; i < card->ov.active_cores; ++i) {
> +             struct acc_core *core = &card->core[i];
> +
> +             unregister_candev(core->net_dev);
> +             free_candev(core->net_dev);
> +             core->net_dev = NULL;
> +     }
> +}
> +
> +static int pci402_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +     struct pci402_card *card = NULL;
> +     int err;
> +
> +     BUILD_BUG_ON(PCI402_DMA_FIFO_ITEMSIZE != sizeof(struct acc_bmmsg));
> +
> +     err = pci_enable_device(pdev);
> +     if (err)
> +             goto failure;
> +
> +     pci_set_master(pdev);
> +
> +     card = kzalloc(sizeof(*card), GFP_KERNEL);
devm_kzalloc()
> +     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, PCI402_BAR, PCI402_IO_LEN_TOTAL);
> +     if (!card->addr) {
> +             err = -ENOMEM;
> +             goto failure_release_regions;
> +     }
> +
> +     err = pci402_init_card(pdev);
> +     if (err)
> +             goto failure_unmap;
> +
> +     err = pci402_init_dma(pdev);
> +     if (err)
> +             goto failure_unmap;
> +
> +     err = pci402_init_interrupt(pdev);
> +     if (err)
> +             goto failure_finish_dma;
> +
> +     err = pci402_init_cores(pdev);
> +     if (err)
> +             goto failure_finish_interrupt;
> +
> +     return 0;
> +
> +failure_finish_interrupt:
> +     pci402_finish_interrupt(pdev);
> +
> +failure_finish_dma:
> +     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 pci402_remove(struct pci_dev *pdev)
> +{
> +     struct pci402_card *card = pci_get_drvdata(pdev);
> +
> +     pci402_finish_interrupt(pdev);
> +     pci402_finish_cores(pdev);
> +     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 pci402_tbl[] = {
> +     { PCI_VENDOR_ID_ESDGMBH, ESD_PCI_DEVICE_ID_PCIE402,
> +                     PCI_ANY_ID, PCI_ANY_ID, },
> +     { 0, }
> +};
> +
> +static struct pci_driver pci402_driver = {
> +     .name = DRV_NAME,
> +     .id_table = pci402_tbl,
> +     .probe = pci402_probe,
> +     .remove = pci402_remove,
> +};
> +
> +module_pci_driver(pci402_driver);
> +
> +MODULE_DESCRIPTION("linux-can driver for esd CAN PCIe/402 cards");
> +MODULE_AUTHOR("Thomas Körper <thomas.koerper@esd.eu>");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c
> new file mode 100644
> index 0000000..46feae6
> --- /dev/null
> +++ b/drivers/net/can/esd/esdacc.c
> @@ -0,0 +1,600 @@
> +/* Copyright (C) 2015 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/ktime.h>
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include "esdacc.h"
> +
> +static void acc_resetmode_enter(struct acc_core *core)
> +{
> +     int i;
> +
> +     acc_set_bits(core, ACC_CORE_OF_CTRL_MODE,
> +                  ACC_REG_CONTROL_MASK_MODE_RESETMODE);
> +
> +     for (i = 0; i < 10; ++i) {
> +             if (acc_resetmode_entered(core))
> +                     return;
> +
> +             udelay(5);
> +     }
> +
> +     netdev_warn(core->net_dev, "Entering reset mode timed out\n");
> +}
> +
> +static void acc_resetmode_leave(struct acc_core *core)
> +{
> +     int i;
> +
> +     acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE,
> +                    ACC_REG_CONTROL_MASK_MODE_RESETMODE);
> +
> +     for (i = 0; i < 10; ++i) {
> +             if (!acc_resetmode_entered(core))
> +                     return;
> +
> +             udelay(5);
> +     }
> +
> +     netdev_warn(core->net_dev, "Leaving reset mode timed out\n");
> +}
> +
> +static int acc_txq_isready(struct acc_core *core)
> +{
> +     u8 fifo_level = (u8)(acc_read32(core, ACC_CORE_OF_TXFIFO_STATUS) >> 16);
> +
> +     return (fifo_level < core->tx_fifo_size - 1);
> +}
> +
> +static void acc_txq_put(struct acc_core *core, u32 esd_id, u8 esd_len,
> +                     const void *data)
> +{
> +     acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_0,
> +                        *((const u32 *)data));
> +     acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_1,
> +                        *((const u32 *)(data + 4)));
> +     acc_write32(core, ACC_CORE_OF_TXFIFO_DLC, esd_len);
> +     /* CAN id must be written at last: */
> +     acc_write32(core, ACC_CORE_OF_TXFIFO_ID, esd_id);
> +}
> +
> +static ktime_t acc_ts_to_ktime(struct acc_ov *ov, u64 ts)
> +{
> +     unsigned long long ns;
> +     unsigned int rem;
> +
> +     rem = do_div(ts, ov->timestamp_frequency);
> +     ns = ts * NSEC_PER_SEC;
> +
> +     ts = (unsigned long long)rem * NSEC_PER_SEC;
> +     do_div(ts, ov->timestamp_frequency);
> +     ns += ts;
> +
> +     return ns_to_ktime(ns);
> +}
> +
> +void acc_init_ov(struct acc_ov *ov, struct device *dev)
> +{
> +     u32 temp;
> +
> +     temp = acc_ov_read32(ov, ACC_OV_OF_VERSION);
> +     ov->features = (u16)(temp >> 16);
> +     ov->version = (u16)temp;
> +
> +     temp = acc_ov_read32(ov, ACC_OV_OF_INFO);
> +     ov->active_cores = (u8)(temp >> 8);
> +     ov->total_cores = (u8)temp;
> +
> +     ov->core_frequency = acc_ov_read32(ov, ACC_OV_OF_CANCORE_FREQ);
> +     ov->timestamp_frequency = acc_ov_read32(ov, ACC_OV_OF_TS_FREQ_LO);
> +
> +     dev_info(dev,
> +              "FPGA v%u, freq: %u/%u, feat/strap: 0x%x/0x%x, cores: %u/%u\n",
> +              ov->version, ov->core_frequency, ov->timestamp_frequency,
> +              ov->features, acc_ov_read32(ov, ACC_OV_OF_INFO) >> 16,
> +              ov->active_cores, ov->total_cores);
> +}
> +
> +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, const void *mem)
> +{
> +     int i;
> +
> +     /* DMA buffer layout:
> +      * +-----------------------+
> +      * | FIFO Card/Overview    |
> +      * |                       |
> +      * +-----------------------+
> +      * | FIFO Core0            |
> +      * |                       |
> +      * +-----------------------+
> +      * | FIFO Core1            |
> +      * |                       |
> +      * +-----------------------+
> +      * | ...                   |
> +      * |                       |
> +      * +-----------------------+
> +      * | irq_cnt Card/Overview |
> +      * +-----------------------+
> +      * | irq_cnt Core0         |
> +      * +-----------------------+
> +      * | irq_cnt Core1         |
> +      * +-----------------------+
> +      * | ...                   |
> +      * +-----------------------+
> +      */
> +     ov->bmfifo.messages = mem;
> +     ov->bmfifo.irq_cnt = mem + (ov->total_cores + 1) * ACC_CORE_DMABUF_SIZE;
> +
> +     for (i = 0; i < ov->active_cores; ++i) {
> +             struct acc_core *core = &cores[i];
> +
> +             core->bmfifo.messages = mem + (i + 1) * ACC_CORE_DMABUF_SIZE;
> +             core->bmfifo.irq_cnt = ov->bmfifo.irq_cnt + 1 + i;
> +     }
> +}
> +
> +int acc_open(struct net_device *netdev)
> +{
> +     struct acc_net_priv *priv = netdev_priv(netdev);
> +     u32 ctrl_mode;
> +     int err;
> +
> +     err = open_candev(netdev);
> +     if (err)
> +             return err;
> +
> +     ctrl_mode = ACC_REG_CONTROL_MASK_IE_RXTX |
> +                     ACC_REG_CONTROL_MASK_IE_TXERROR |
> +                     ACC_REG_CONTROL_MASK_IE_ERRPASS |
> +                     ACC_REG_CONTROL_MASK_IE_OVERRUN |
> +                     ACC_REG_CONTROL_MASK_IE_ERROR;
> +
> +     if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING)
> +             ctrl_mode |= ACC_REG_CONTROL_MASK_IE_BUSERR;
> +
> +     acc_set_bits(priv->core, ACC_CORE_OF_CTRL_MODE, ctrl_mode);
> +
> +     netif_start_queue(netdev);
> +     return 0;
> +}
> +
> +int acc_close(struct net_device *netdev)
> +{
> +     struct acc_net_priv *priv = netdev_priv(netdev);
> +
> +     acc_clear_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
> +                    ACC_REG_CONTROL_MASK_IE_RXTX |
> +                    ACC_REG_CONTROL_MASK_IE_TXERROR |
> +                    ACC_REG_CONTROL_MASK_IE_OVERRUN);
> +
> +     netif_stop_queue(netdev);
> +     priv->can.state = CAN_STATE_STOPPED;
> +
> +     close_candev(netdev);
> +     return 0;
> +}
> +
> +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev)
> +{
> +     struct acc_net_priv *priv = netdev_priv(netdev);
> +     struct acc_core *core = priv->core;
> +     struct can_frame *cf = (struct can_frame *)skb->data;
> +     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) || !acc_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 |= ACC_CAN_RTR_FLAG;
> +
> +     if (cf->can_id & CAN_EFF_FLAG) {
> +             esd_id = cf->can_id & CAN_EFF_MASK;
> +             esd_id |= ACC_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;
> +
> +     acc_txq_put(core, esd_id, esd_len, cf->data);
> +
> +     return NETDEV_TX_OK;
> +}
> +
> +int acc_get_berr_counter(const struct net_device *netdev,
> +                      struct can_berr_counter *bec)
> +{
> +     struct acc_net_priv *priv = netdev_priv(netdev);
> +     u32 core_status = acc_read32(priv->core, ACC_CORE_OF_STATUS);
> +
> +     bec->txerr = (u8)(core_status >> 8);
> +     bec->rxerr = (u8)core_status;
> +
> +     return 0;
> +}
> +
> +int acc_set_mode(struct net_device *netdev, enum can_mode mode)
> +{
> +     struct acc_net_priv *priv = netdev_priv(netdev);
> +
> +     switch (mode) {
> +     case CAN_MODE_START:
> +             acc_resetmode_leave(priv->core);
> +             netif_wake_queue(netdev);
> +             break;
> +
> +     default:
> +             return -EOPNOTSUPP;
> +     }
> +
> +     return 0;
> +}
> +
> +int acc_set_bittiming(struct net_device *netdev)
> +{
> +     struct acc_net_priv *priv = netdev_priv(netdev);
> +     struct can_bittiming *bt = &priv->can.bittiming;
> +     u32 btr;
> +     u8 brp;
> +
> +     brp = (u8)(bt->brp - 1);
> +     acc_resetmode_enter(priv->core);
> +
> +     if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY)
> +             acc_set_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
> +                          ACC_REG_CONTROL_MASK_MODE_LOM);
> +     else
> +             acc_clear_bits(priv->core, ACC_CORE_OF_CTRL_MODE,
> +                            ACC_REG_CONTROL_MASK_MODE_LOM);
> +
> +     if (priv->ov->deprecated_btr) {
> +             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;
> +
> +             acc_write32(priv->core, ACC_CORE_OF_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;
> +
> +             acc_write32(priv->core, ACC_CORE_OF_BTR, brp);
> +             acc_write32(priv->core, ACC_CORE_OF_BTR_FD, btr);
> +     }
> +
> +     acc_resetmode_leave(priv->core);
> +     priv->can.state = CAN_STATE_ERROR_ACTIVE;
> +
> +     return 0;
> +}
> +
> +static void handle_core_msg_rxtxdone(struct acc_core *core,
> +                                  const struct acc_bmmsg_rxtxdone *msg)
> +{
> +     struct acc_net_priv *priv = netdev_priv(core->net_dev);
> +     struct net_device_stats *stats = &core->net_dev->stats;
> +
> +     if (msg->dlc.rxtx.len & ACC_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 & ACC_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) {
> +                     stats->rx_dropped++;
> +                     return;
> +             }
> +
> +             cf->can_id = msg->id & CAN_EFF_MASK;
> +             if (msg->id & ACC_CAN_EFF_FLAG)
> +                     cf->can_id |= CAN_EFF_FLAG;
> +
> +             cf->can_dlc = get_can_dlc(msg->dlc.rx.len);
> +             if (msg->dlc.rx.len & ACC_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 = acc_ts_to_ktime(priv->ov, msg->timestamp);
> +             netif_rx(skb);
> +
> +             stats->rx_packets++;
> +             stats->rx_bytes += cf->can_dlc;
> +     }
> +}
> +
> +static void handle_core_msg_txabort(struct acc_core *core,
> +                                 const struct acc_bmmsg_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 (!acc_resetmode_entered(core))
> +             netif_wake_queue(core->net_dev);
> +}
> +
> +static void handle_core_msg_overrun(struct acc_core *core,
> +                                 const struct acc_bmmsg_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)
> +             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 handle_core_msg_buserr(struct acc_core *core,
> +                                const struct acc_bmmsg_buserr *msg)
> +{
> +     struct acc_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)
> +             return;
> +
> +     cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR;
> +
> +     /* msg->ecc acts like SJA1000's ECC register */
> +     switch (msg->ecc & ACC_ECC_MASK) {
> +     case ACC_ECC_BIT:
> +             cf->data[2] |= CAN_ERR_PROT_BIT;
> +             break;
> +     case ACC_ECC_FORM:
> +             cf->data[2] |= CAN_ERR_PROT_FORM;
> +             break;
> +     case ACC_ECC_STUFF:
> +             cf->data[2] |= CAN_ERR_PROT_STUFF;
> +             break;
> +     default:
> +             cf->data[2] |= CAN_ERR_PROT_UNSPEC;
> +             cf->data[3] = msg->ecc & ACC_ECC_SEG;
> +             break;
> +     }
> +
> +     if ((msg->ecc & ACC_ECC_DIR) == 0)
> +             cf->data[2] |= CAN_ERR_PROT_TX;
> +
> +     netif_rx(skb);
> +     stats->rx_packets++;
> +     stats->rx_bytes += cf->can_dlc;

the skb is't valid anymore after netif_rx()
> +}
> +
> +static void
> +handle_core_msg_errstatechange(struct acc_core *core,
> +                            const struct acc_bmmsg_errstatechange *msg)
> +{
> +     struct acc_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 & ACC_REG_STATUS_MASK_STATUS_ES) != 0;
> +     is_passive = (msg->reg_status & ACC_REG_STATUS_MASK_STATUS_EP) != 0;
> +     is_busoff = (msg->reg_status & ACC_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;
same here
> +     }
> +
> +     if (is_busoff) {
> +             acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff);
> +             can_bus_off(core->net_dev);
> +     }
> +}
> +
> +static void handle_core_interrupt(struct acc_core *core)
> +{
> +     u32 msg_fifo_head = core->bmfifo.local_irq_cnt & 0xff;
> +
> +     while (core->bmfifo.msg_fifo_tail != msg_fifo_head) {
> +             const struct acc_bmmsg *msg =
> +                     &core->bmfifo.messages[core->bmfifo.msg_fifo_tail];
> +
> +             switch (msg->u.msg_id) {
> +             case BM_MSG_ID_RXTXDONE:
> +                     handle_core_msg_rxtxdone(core, &msg->u.rxtxdone);
> +                     break;
> +
> +             case BM_MSG_ID_TXABORT:
> +                     handle_core_msg_txabort(core, &msg->u.txabort);
> +                     break;
> +
> +             case BM_MSG_ID_OVERRUN:
> +                     handle_core_msg_overrun(core, &msg->u.overrun);
> +                     break;
> +
> +             case BM_MSG_ID_BUSERR:
> +                     handle_core_msg_buserr(core, &msg->u.buserr);
> +                     break;
> +
> +             case BM_MSG_ID_ERRPASSIVE:
> +             case BM_MSG_ID_ERRWARN:
> +                     handle_core_msg_errstatechange(core,
> +                                                    &msg->u.errstatechange);
> +                     break;
> +
> +             default:
> +                     break;
> +             }
> +
> +             core->bmfifo.msg_fifo_tail =
> +                             (core->bmfifo.msg_fifo_tail + 1) & 0xff;
> +     }
> +}
> +
> +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores)
> +{
> +     u32 irqmask;
> +     int i;
> +
> +     /* 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 (*ov->bmfifo.irq_cnt != ov->bmfifo.local_irq_cnt) {
> +             irqmask |= ACC_BM_IRQ_MASK;
> +             ov->bmfifo.local_irq_cnt = *ov->bmfifo.irq_cnt;
> +     }
> +
> +     for (i = 0; i < ov->active_cores; ++i) {
> +             struct acc_core *core = &cores[i];
> +
> +             if (*core->bmfifo.irq_cnt != core->bmfifo.local_irq_cnt) {
> +                     irqmask |= (ACC_BM_IRQ_MASK << (2 * (i + 1)));
> +                     core->bmfifo.local_irq_cnt = *core->bmfifo.irq_cnt;
> +             }
> +     }
> +
> +     if (!irqmask)
> +             return IRQ_NONE;
> +
> +     /* At second we tell the card we're working on them by writing irqmask,
> +      * call handle_{ov|core}_interrupt and then acknowledge the
> +      * interrupts by writing irq_cnt:
> +      */
> +     acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, irqmask);
> +
> +     if (irqmask & ACC_BM_IRQ_MASK) {
> +             /* handle_ov_interrupt(); - no use yet. */
> +             acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_COUNTER,
> +                            ov->bmfifo.local_irq_cnt);
> +     }
> +
> +     for (i = 0; i < ov->active_cores; ++i) {
> +             struct acc_core *core = &cores[i];
> +
> +             if (irqmask & (ACC_BM_IRQ_MASK << (2 * (i + 1)))) {
> +                     handle_core_interrupt(core);
> +                     acc_write32(core, ACC_OV_OF_BM_IRQ_COUNTER,
> +                                 core->bmfifo.local_irq_cnt);
> +             }
> +     }
> +
> +     acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, ACC_BM_IRQ_UNMASK_ALL);
> +
> +     return IRQ_HANDLED;
> +}
> diff --git a/drivers/net/can/esd/esdacc.h b/drivers/net/can/esd/esdacc.h
> new file mode 100644
> index 0000000..97d0d3b
> --- /dev/null
> +++ b/drivers/net/can/esd/esdacc.h
> @@ -0,0 +1,428 @@
> +/* Copyright (C) 2015 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/netdevice.h>
> +#include <linux/can/dev.h>
> +
> +#define ACC_CAN_EFF_FLAG                     0x20000000
> +#define ACC_CAN_RTR_FLAG                     0x10
> +#define ACC_CAN_DLC_MASK                     0x0f
> +
> +#define ACC_OV_OF_PROBE                              0x0000
> +#define ACC_OV_OF_VERSION                    0x0004
> +#define ACC_OV_OF_INFO                               0x0008
> +#define ACC_OV_OF_CANCORE_FREQ                       0x000c
> +#define ACC_OV_OF_TS_FREQ_LO                 0x0010
> +#define ACC_OV_OF_TS_FREQ_HI                 0x0014
> +#define ACC_OV_OF_IRQ_STATUS_CORES           0x0018
> +#define ACC_OV_OF_TS_CURR_LO                 0x001c
> +#define ACC_OV_OF_TS_CURR_HI                 0x0020
> +#define ACC_OV_OF_IRQ_STATUS                 0x0028
> +#define ACC_OV_OF_MODE                               0x002c
> +#define ACC_OV_OF_BM_IRQ_COUNTER             0x0070
> +#define ACC_OV_OF_BM_IRQ_MASK                        0x0074
> +#define ACC_OV_OF_MSI_DATA                   0x0080
> +#define ACC_OV_OF_MSI_ADDRESSOFFSET          0x0084
> +
> +#define ACC_OV_REG_MODE_MASK_ENDIAN_LITTLE   0x00000001
> +#define ACC_OV_REG_MODE_MASK_BM_ENABLE               0x00000002
> +#define ACC_OV_REG_MODE_MASK_MODE_LED                0x00000004
> +#define ACC_OV_REG_MODE_MASK_TIMER           0x00000070
> +#define ACC_OV_REG_MODE_MASK_TIMER_ENABLE    0x00000010
> +#define ACC_OV_REG_MODE_MASK_TIMER_ONE_SHOT  0x00000020
> +#define ACC_OV_REG_MODE_MASK_TIMER_ABSOLUTE  0x00000040
> +#define ACC_OV_REG_MODE_MASK_TS_SRC          0x00000180
> +#define ACC_OV_REG_MODE_MASK_I2C_ENABLE              0x00000800
> +#define ACC_OV_REG_MODE_MASK_MSI_ENABLE              0x00004000
> +#define ACC_OV_REG_MODE_MASK_FPGA_RESET              0x80000000
> +
> +#define ACC_BM_IRQ_UNMASK_ALL                        0x55555555
> +#define ACC_BM_IRQ_MASK_ALL                  0xaaaaaaaa
> +#define ACC_BM_IRQ_MASK                              0x2
> +#define ACC_BM_IRQ_UNMASK                    0x1
> +#define ACC_BM_LENFLAG_TX                    0x20
> +
> +#define ACC_CORE_OF_CTRL_MODE                        0x0000
> +#define ACC_CORE_OF_STATUS_IRQ                       0x0008
> +#define ACC_CORE_OF_BTR                              0x000c
> +#define ACC_CORE_OF_BTR_FD                   0x0010
> +#define ACC_CORE_OF_BTR_FDF                  0x0014
> +#define ACC_CORE_OF_STATUS                   0x0030
> +#define ACC_CORE_OF_TXFIFO_CONFIG            0x0048
> +#define ACC_CORE_OF_TXFIFO_STATUS            0x004c
> +#define ACC_CORE_OF_TX_STATUS_IRQ            0x0050
> +#define ACC_CORE_OF_TX_ABORT_MASK            0x0054
> +#define ACC_CORE_OF_BM_IRQ_COUNTER           0x0070
> +#define ACC_CORE_OF_TXFIFO_ID                        0x00c0
> +#define ACC_CORE_OF_TXFIFO_DLC                       0x00c4
> +#define ACC_CORE_OF_TXFIFO_DATA_0            0x00c8
> +#define ACC_CORE_OF_TXFIFO_DATA_1            0x00cc
> +
> +/* ecc value of esdACC equals SJA1000's ECC register */
> +#define ACC_ECC_SEG                          0x1f
> +#define ACC_ECC_DIR                          0x20
> +#define ACC_ECC_BIT                          0x00
> +#define ACC_ECC_FORM                         0x40
> +#define ACC_ECC_STUFF                                0x80
> +#define ACC_ECC_MASK                         0xc0
> +
> +#define ACC_REG_CONTROL_IDX_MODE_RESETMODE   0
> +#define ACC_REG_CONTROL_IDX_MODE_LOM         1
> +#define ACC_REG_CONTROL_IDX_MODE_STM         2
> +#define ACC_REG_CONTROL_IDX_MODE_TRANSEN     5
> +#define ACC_REG_CONTROL_IDX_MODE_TS          6
> +#define ACC_REG_CONTROL_IDX_MODE_SCHEDULE    7
> +#define ACC_REG_CONTROL_MASK_MODE_RESETMODE  \
> +                             BIT(ACC_REG_CONTROL_IDX_MODE_RESETMODE)
> +#define ACC_REG_CONTROL_MASK_MODE_LOM                \
> +                             BIT(ACC_REG_CONTROL_IDX_MODE_LOM)
> +#define ACC_REG_CONTROL_MASK_MODE_STM                \
> +                             BIT(ACC_REG_CONTROL_IDX_MODE_STM)
> +#define ACC_REG_CONTROL_MASK_MODE_TRANSEN    \
> +                             BIT(ACC_REG_CONTROL_IDX_MODE_TRANSEN)
> +#define ACC_REG_CONTROL_MASK_MODE_TS         \
> +                             BIT(ACC_REG_CONTROL_IDX_MODE_TS)
> +#define ACC_REG_CONTROL_MASK_MODE_SCHEDULE   \
> +                             BIT(ACC_REG_CONTROL_IDX_MODE_SCHEDULE)
> +
> +#define ACC_REG_CONTROL_IDX_IE_RXTX  8
> +#define ACC_REG_CONTROL_IDX_IE_TXERROR       9
> +#define ACC_REG_CONTROL_IDX_IE_ERROR 10
> +#define ACC_REG_CONTROL_IDX_IE_OVERRUN       11
> +#define ACC_REG_CONTROL_IDX_IE_TSI   12
> +#define ACC_REG_CONTROL_IDX_IE_ERRPASS       13
> +#define ACC_REG_CONTROL_IDX_IE_BUSERR        15
> +#define ACC_REG_CONTROL_MASK_IE_RXTX BIT(ACC_REG_CONTROL_IDX_IE_RXTX)
> +#define ACC_REG_CONTROL_MASK_IE_TXERROR      BIT(ACC_REG_CONTROL_IDX_IE_TXERROR)
> +#define ACC_REG_CONTROL_MASK_IE_ERROR        BIT(ACC_REG_CONTROL_IDX_IE_ERROR)
> +#define ACC_REG_CONTROL_MASK_IE_OVERRUN      BIT(ACC_REG_CONTROL_IDX_IE_OVERRUN)
> +#define ACC_REG_CONTROL_MASK_IE_TSI  BIT(ACC_REG_CONTROL_IDX_IE_TSI)
> +#define ACC_REG_CONTROL_MASK_IE_ERRPASS      BIT(ACC_REG_CONTROL_IDX_IE_ERRPASS)
> +#define ACC_REG_CONTROL_MASK_IE_BUSERR       BIT(ACC_REG_CONTROL_IDX_IE_BUSERR)
> +
> +#define ACC_REG_STATUS_IDX_STATUS_DOS        16
> +#define ACC_REG_STATUS_IDX_STATUS_ES 17
> +#define ACC_REG_STATUS_IDX_STATUS_EP 18
> +#define ACC_REG_STATUS_IDX_STATUS_BS 19
> +#define ACC_REG_STATUS_IDX_STATUS_RBS        20
> +#define ACC_REG_STATUS_IDX_STATUS_RS 21
> +#define ACC_REG_STATUS_MASK_STATUS_DOS       BIT(ACC_REG_STATUS_IDX_STATUS_DOS)
> +#define ACC_REG_STATUS_MASK_STATUS_ES        BIT(ACC_REG_STATUS_IDX_STATUS_ES)
> +#define ACC_REG_STATUS_MASK_STATUS_EP        BIT(ACC_REG_STATUS_IDX_STATUS_EP)
> +#define ACC_REG_STATUS_MASK_STATUS_BS        BIT(ACC_REG_STATUS_IDX_STATUS_BS)
> +#define ACC_REG_STATUS_MASK_STATUS_RBS       BIT(ACC_REG_STATUS_IDX_STATUS_RBS)
> +#define ACC_REG_STATUS_MASK_STATUS_RS        BIT(ACC_REG_STATUS_IDX_STATUS_RS)
> +
> +#define ACC_CORE_DMABUF_SIZE         (256 * 32) /* 256 messages */
> +
> +enum acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_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 acc_bmmsg_timeslice {
> +     u8 msg_id;
> +     u8 txfifo_level;
> +     u8 reserved1[2];
> +     u8 txtsfifo_level;
> +     u8 reserved2[3];
> +     u64 ts;
> +     u32 reserved3[4];
> +} __packed;
> +
> +struct acc_bmmsg_hwtimer {
> +     u8 msg_id;
> +     u8 reserved1[3];
> +     u32 reserved2[1];
> +     u64 timer;
> +     u32 reserved3[4];
> +} __packed;
> +
> +struct acc_bmmsg_hotplug {
> +     u8 msg_id;
> +     u8 reserved1[3];
> +     u32 reserved2[7];
> +} __packed;
> +
> +struct acc_bmmsg_canfddata {
> +     u8 msg_id;
> +     u8 reserved1[3];
> +     union {
> +             u8 ui8[28];
> +             u32 ui32[7];
> +     } d;
> +} __packed;
> +
> +struct acc_bmmsg {
> +     union {
> +             u8 msg_id;
> +             struct acc_bmmsg_rxtxdone rxtxdone;
> +             struct acc_bmmsg_canfddata canfddata;
> +             struct acc_bmmsg_txabort txabort;
> +             struct acc_bmmsg_overrun overrun;
> +             struct acc_bmmsg_buserr buserr;
> +             struct acc_bmmsg_errstatechange errstatechange;
> +             struct acc_bmmsg_timeslice timeslice;
> +             struct acc_bmmsg_hwtimer hwtimer;
> +     } u;
> +};
> +
> +struct acc_bmfifo {
> +     const struct acc_bmmsg *messages;
> +     /* Bits0..7: bm_fifo head index */
> +     const u32 *irq_cnt;
> +     u32 local_irq_cnt;
> +     u32 msg_fifo_tail;
> +};
> +
> +struct acc_core {
> +     void __iomem *addr;
> +     struct net_device *net_dev;
> +     struct acc_bmfifo bmfifo;
> +     u8 tx_fifo_size;
> +     u8 tx_fifo_head;
> +     u8 tx_fifo_tail;
> +};
> +
> +struct acc_ov {
> +     void __iomem *addr;
> +     struct acc_bmfifo bmfifo;
> +     u32 timestamp_frequency;
> +     u32 core_frequency;
> +     u16 version;
> +     u16 features;
> +     u8 total_cores;
> +     u8 active_cores;
> +     bool deprecated_btr;
> +};
> +
> +struct acc_net_priv {
> +     struct can_priv can; /* must be the first member! */
> +     struct acc_core *core;
> +     struct acc_ov *ov;
> +};
> +
> +static inline u32 acc_read32(struct acc_core *core, unsigned short offs)
> +{
> +     return ioread32be(core->addr + offs);
> +}
> +
> +static inline void acc_write32(struct acc_core *core,
> +                            unsigned short offs, u32 v)
> +{
> +     iowrite32be(v, core->addr + offs);
> +}
> +
> +static inline void acc_write32_noswap(struct acc_core *core,
> +                                   unsigned short offs, u32 v)
> +{
> +     iowrite32(v, core->addr + offs);
> +}
> +
> +static inline void acc_set_bits(struct acc_core *core,
> +                             unsigned short offs, u32 mask)
> +{
> +     u32 v = acc_read32(core, offs);
> +
> +     v |= mask;
> +     acc_write32(core, offs, v);
> +}
> +
> +static inline void acc_clear_bits(struct acc_core *core,
> +                               unsigned short offs, u32 mask)
> +{
> +     u32 v = acc_read32(core, offs);
> +
> +     v &= ~mask;
> +     acc_write32(core, offs, v);
> +}
> +
> +static inline int acc_resetmode_entered(struct acc_core *core)
> +{
> +     u32 ctrl = acc_read32(core, ACC_CORE_OF_CTRL_MODE);
> +
> +     return (ctrl & ACC_REG_CONTROL_MASK_MODE_RESETMODE) != 0;
> +}
> +
> +static inline u32 acc_ov_read32(struct acc_ov *ov, unsigned short offs)
> +{
> +     return ioread32be(ov->addr + offs);
> +}
> +
> +static inline void acc_ov_write32(struct acc_ov *ov,
> +                               unsigned short offs, u32 v)
> +{
> +     iowrite32be(v, ov->addr + offs);
> +}
> +
> +static inline void acc_ov_set_bits(struct acc_ov *ov,
> +                                unsigned short offs, u32 b)
> +{
> +     u32 v = acc_ov_read32(ov, offs);
> +
> +     v |= b;
> +     acc_ov_write32(ov, offs, v);
> +}
> +
> +static inline void acc_ov_clear_bits(struct acc_ov *ov,
> +                                  unsigned short offs, u32 b)
> +{
> +     u32 v = acc_ov_read32(ov, offs);
> +
> +     v &= ~b;
> +     acc_ov_write32(ov, offs, v);
> +}
> +
> +static inline void acc_reset_fpga(struct acc_ov *ov)
> +{
> +     acc_ov_write32(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_FPGA_RESET);
> +
> +     /* Also reset I²C, to re-detect card addons at every driver start: */
> +     acc_ov_clear_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
> +     mdelay(2);
> +     acc_ov_set_bits(ov, ACC_OV_OF_MODE, ACC_OV_REG_MODE_MASK_I2C_ENABLE);
> +     mdelay(10);
> +}
> +
> +void acc_init_ov(struct acc_ov *ov, struct device *dev);
> +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores,
> +                  const void *mem);
> +int acc_open(struct net_device *netdev);
> +int acc_close(struct net_device *netdev);
> +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev);
> +int acc_get_berr_counter(const struct net_device *netdev,
> +                      struct can_berr_counter *bec);
> +int acc_set_mode(struct net_device *netdev, enum can_mode mode);
> +int acc_set_bittiming(struct net_device *netdev);
> +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores);
> +
> +/* Used in PCIe/402 cards with FPGA Version < 0x0032 */
> +static const struct can_bittiming_const acc_bittiming_const_pci402_old = {
> +     .name = "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 acc_bittiming_const_pci402 = {
> +     .name = "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 acc_netdev_ops = {
> +     .ndo_open = acc_open,
> +     .ndo_stop = acc_close,
> +     .ndo_start_xmit = acc_start_xmit,
> +     .ndo_change_mtu = can_change_mtu
> +};
>

Marc

--
Pengutronix e.K.                  | Marc Kleine-Budde           |
Industrial Linux Solutions        | Phone: +49-231-2826-924     |
Vertretung West/Dortmund          | Fax:   +49-5121-206917-5555 |
Amtsgericht Hildesheim, HRA 2686  | http://www.pengutronix.de   |



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

end of thread, other threads:[~2015-03-18  5:08 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-03-16 12:15 [PATCH V5 1/1] can: Add support for esd CAN PCIe/402 card Thomas Körper
2015-03-16 12:46 ` Andri Yngvason
2015-03-16 13:35   ` Marc Kleine-Budde
2015-03-17  6:30   ` AW: " Thomas Körper
2015-03-17  7:26     ` Ahmed S. Darwish
2015-03-17 10:10       ` Andri Yngvason
2015-03-17 10:33     ` AW: " Andri Yngvason
2015-03-17 21:27 ` Marc Kleine-Budde
2015-03-18  5:08   ` Thomas Körper

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).