All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oliver Hartkopp <socketcan@hartkopp.net>
To: "Thomas Körper" <Thomas.Koerper@esd.eu>, linux-can@vger.kernel.org
Subject: Re: [PATCH 1/1] can: Add support for esd CAN PCIe/402 card
Date: Mon, 20 Oct 2014 18:26:22 +0200	[thread overview]
Message-ID: <544537AE.6030607@hartkopp.net> (raw)
In-Reply-To: <1413199410-42938-1-git-send-email-Thomas.Koerper@esd.eu>

Hello Thomas,

thanks for contributing the first escACC-based driver!

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

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

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

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

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

Capital letters in mail addresses - ugh.

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

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

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

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

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

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

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

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

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

GmbH ??

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


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

This is obsolete now.

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

esd402_pcie

?

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

A candidate for esdacc.h ...


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

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

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

Looks generic too.
Move to esdacc.c?

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

acc specific or card specific?

-> esdacc.h ??

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

Is this PCI402 or esdAAC specific?

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

esd_pci402_cancore == esd_aac_core ??

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

-> esdaac.h

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

Regards,
Oliver

  reply	other threads:[~2014-10-20 16:26 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-13 11:23 [PATCH 1/1] can: Add support for esd CAN PCIe/402 card Thomas Körper
2014-10-20 16:26 ` Oliver Hartkopp [this message]
2014-10-21  7:46   ` AW: " Thomas Körper
2014-10-21  8:27     ` Oliver Hartkopp
2014-10-21  9:41       ` AW: " Thomas Körper
2014-10-21 18:39       ` Robert Schwebel

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=544537AE.6030607@hartkopp.net \
    --to=socketcan@hartkopp.net \
    --cc=Thomas.Koerper@esd.eu \
    --cc=linux-can@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.