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
next prev parent 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.