Will change the patch to follow your last comment below. Am Mittwoch, den 22.11.2023, 00:04 +0900 schrieb Vincent Mailhol: > Two last comments. These are not withstanding. > > On Tue. 21 Nov. 2023 at 02:57, Stefan Mätje <stefan.maetje@xxxxxx> wrote: > > This patch adds support for the PCI based PCIe/402 CAN interface family > > from esd GmbH that is available with various form factors > > (https://esd.eu/en/products/402-series-can-interfaces). > > > > All boards utilize a FPGA based CAN controller solution developed > > by esd (esdACC). For more information on the esdACC see > > https://esd.eu/en/products/esdacc. > > > > This driver detects all available CAN interface board variants of > > the family but atm. operates the CAN-FD capable devices in > > Classic-CAN mode only! A later patch will introduce the CAN-FD > > functionality in this driver. > > > > Co-developed-by: Thomas Körper <thomas.koerper@xxxxxx> > > Signed-off-by: Thomas Körper <thomas.koerper@xxxxxx> > > Signed-off-by: Stefan Mätje <stefan.maetje@xxxxxx> > > --- > > (...) > > > diff --git a/drivers/net/can/esd/esdacc.c b/drivers/net/can/esd/esdacc.c > > new file mode 100644 > > index 000000000000..c990e60c09f1 > > --- /dev/null > > +++ b/drivers/net/can/esd/esdacc.c > > @@ -0,0 +1,761 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +/* Copyright (C) 2015 - 2016 Thomas Körper, esd electronic system design gmbh > > + * Copyright (C) 2017 - 2023 Stefan Mätje, esd electronics gmbh > > + */ > > + > > +#include "esdacc.h" > > + > > +#include <linux/bitfield.h> > > +#include <linux/delay.h> > > +#include <linux/io.h> > > +#include <linux/ktime.h> > > + > > +/* esdACC ID register layout */ > > +#define ACC_ID_ID_MASK GENMASK(28, 0) > > +#define ACC_ID_EFF_FLAG BIT(29) > > + > > +/* esdACC DLC register layout */ > > +#define ACC_DLC_DLC_MASK GENMASK(3, 0) > > +#define ACC_DLC_RTR_FLAG BIT(4) > > +#define ACC_DLC_TXD_FLAG BIT(5) > > + > > +/* ecc value of esdACC equals SJA1000's ECC register */ > > +#define ACC_ECC_SEG 0x1f > > +#define ACC_ECC_DIR 0x20 > > +#define ACC_ECC_BIT 0x00 > > +#define ACC_ECC_FORM 0x40 > > +#define ACC_ECC_STUFF 0x80 > > +#define ACC_ECC_MASK 0xc0 > > + > > +/* esdACC Status Register bits. Unused bits not documented. */ > > +#define ACC_REG_STATUS_MASK_STATUS_ES BIT(17) > > +#define ACC_REG_STATUS_MASK_STATUS_EP BIT(18) > > +#define ACC_REG_STATUS_MASK_STATUS_BS BIT(19) > > + > > +/* esdACC Overview Module BM_IRQ_Mask register related defines */ > > +/* Two bit wide command masks to mask or unmask a single core IRQ */ > > +#define ACC_BM_IRQ_UNMASK BIT(0) > > +#define ACC_BM_IRQ_MASK (ACC_BM_IRQ_UNMASK << 1) > > +/* Command to unmask all IRQ sources. Created by shifting > > + * and oring the two bit wide ACC_BM_IRQ_UNMASK 16 times. > > + */ > > +#define ACC_BM_IRQ_UNMASK_ALL 0x55555555U > > + > > +static void acc_resetmode_enter(struct acc_core *core) > > +{ > > + acc_set_bits(core, ACC_CORE_OF_CTRL_MODE, > > + ACC_REG_CONTROL_MASK_MODE_RESETMODE); > > + > > + /* Read back reset mode bit to flush PCI write posting */ > > + acc_resetmode_entered(core); > > +} > > + > > +static void acc_resetmode_leave(struct acc_core *core) > > +{ > > + acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE, > > + ACC_REG_CONTROL_MASK_MODE_RESETMODE); > > + > > + /* Read back reset mode bit to flush PCI write posting */ > > + acc_resetmode_entered(core); > > +} > > + > > +static void acc_txq_put(struct acc_core *core, u32 acc_id, u8 acc_dlc, > > + const void *data) > > +{ > > + acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_1, > > + *((const u32 *)(data + 4))); > > + acc_write32_noswap(core, ACC_CORE_OF_TXFIFO_DATA_0, > > + *((const u32 *)data)); > > + acc_write32(core, ACC_CORE_OF_TXFIFO_DLC, acc_dlc); > > + /* CAN id must be written at last. This write starts TX. */ > > + acc_write32(core, ACC_CORE_OF_TXFIFO_ID, acc_id); > > +} > > + > > +static u8 acc_tx_fifo_next(struct acc_core *core, u8 tx_fifo_idx) > > +{ > > + ++tx_fifo_idx; > > + if (tx_fifo_idx >= core->tx_fifo_size) > > + tx_fifo_idx = 0U; > > + return tx_fifo_idx; > > +} > > +/* Convert timestamp from esdACC time stamp ticks to ns > > + * > > + * The conversion factor ts2ns from time stamp counts to ns is basically > > + * ts2ns = NSEC_PER_SEC / timestamp_frequency > > + * > > + * We handle here only a fixed timestamp frequency of 80MHz. The > > + * resulting ts2ns factor would be 12.5. > > + * > > + * At the end we multiply by 12 and add the half of the HW timestamp > > + * to get a multiplication by 12.5. This way any overflow is > > + * avoided until ktime_t itself overflows. > > + */ > > +#define ACC_TS_FACTOR (NSEC_PER_SEC / ACC_TS_FREQ_80MHZ) > > +#define ACC_TS_80MHZ_SHIFT 1 > > + > > +static ktime_t acc_ts2ktime(struct acc_ov *ov, u64 ts) > > +{ > > + u64 ns; > > + > > + ns = (ts * ACC_TS_FACTOR) + (ts >> ACC_TS_80MHZ_SHIFT); > > + > > + return ns_to_ktime(ns); > > +} > > Nitpick: if the macro is meant to have a global scope, better to put > it at the beginning with other macros. If the scope is only > acc_ts2ktime(), then undefine them once it is out of scope. > > #undef ACC_TS_FACTOR > #undef ACC_TS_80MHZ_SHIFT This should really have only local scope. So I will #undef them here ... > > +void acc_init_ov(struct acc_ov *ov, struct device *dev) > > +{ > > + u32 temp; > > + > > + temp = acc_ov_read32(ov, ACC_OV_OF_VERSION); > > + ov->version = temp; > > + ov->features = (temp >> 16); > > + > > + temp = acc_ov_read32(ov, ACC_OV_OF_INFO); > > + ov->total_cores = temp; > > + ov->active_cores = (temp >> 8); > > + > > + ov->core_frequency = acc_ov_read32(ov, ACC_OV_OF_CANCORE_FREQ); > > + ov->timestamp_frequency = acc_ov_read32(ov, ACC_OV_OF_TS_FREQ_LO); > > + > > + /* Depending on esdACC feature NEW_PSC enable the new prescaler > > + * or adjust core_frequency according to the implicit division by 2. > > + */ > > + if (ov->features & ACC_OV_REG_FEAT_MASK_NEW_PSC) { > > + acc_ov_set_bits(ov, ACC_OV_OF_MODE, > > + ACC_OV_REG_MODE_MASK_NEW_PSC_ENABLE); > > + } else { > > + ov->core_frequency /= 2; > > + } > > + > > + dev_dbg(dev, > > + "esdACC v%u, freq: %u/%u, feat/strap: 0x%x/0x%x, cores: %u/%u\n", > > + ov->version, ov->core_frequency, ov->timestamp_frequency, > > + ov->features, acc_ov_read32(ov, ACC_OV_OF_INFO) >> 16, > > + ov->active_cores, ov->total_cores); > > +} > > + > > +void acc_init_bm_ptr(struct acc_ov *ov, struct acc_core *cores, const void *mem) > > +{ > > + unsigned int u; > > + > > + /* DMA buffer layout as follows where N is the number of CAN cores > > + * implemented in the FPGA, i.e. N = ov->total_cores > > + * > > + * Section Layout Section size > > + * ---------------------------------------------- > > + * FIFO Card/Overview ACC_CORE_DMABUF_SIZE > > + * FIFO Core0 ACC_CORE_DMABUF_SIZE > > + * ... ... > > + * FIFO CoreN ACC_CORE_DMABUF_SIZE > > + * irq_cnt Card/Overview sizeof(u32) > > + * irq_cnt Core0 sizeof(u32) > > + * ... ... > > + * irq_cnt CoreN sizeof(u32) > > + */ > > + ov->bmfifo.messages = mem; > > + ov->bmfifo.irq_cnt = mem + (ov->total_cores + 1U) * ACC_CORE_DMABUF_SIZE; > > + > > + for (u = 0U; u < ov->active_cores; u++) { > > + struct acc_core *core = &cores[u]; > > + > > + core->bmfifo.messages = mem + (u + 1U) * ACC_CORE_DMABUF_SIZE; > > + core->bmfifo.irq_cnt = ov->bmfifo.irq_cnt + (u + 1U); > > + } > > +} > > + > > +int acc_open(struct net_device *netdev) > > +{ > > + struct acc_net_priv *priv = netdev_priv(netdev); > > + struct acc_core *core = priv->core; > > + u32 tx_fifo_status; > > + u32 ctrl_mode; > > + int err; > > + > > + /* Retry to enter RESET mode if out of sync. */ > > + if (priv->can.state != CAN_STATE_STOPPED) { > > + netdev_warn(netdev, "Entered %s() with bad can.state: %s\n", > > + __func__, can_get_state_str(priv->can.state)); > > + acc_resetmode_enter(core); > > + priv->can.state = CAN_STATE_STOPPED; > > + } > > + > > + err = open_candev(netdev); > > + if (err) > > + return err; > > + > > + ctrl_mode = ACC_REG_CONTROL_MASK_IE_RXTX | > > + ACC_REG_CONTROL_MASK_IE_TXERROR | > > + ACC_REG_CONTROL_MASK_IE_ERRWARN | > > + ACC_REG_CONTROL_MASK_IE_OVERRUN | > > + ACC_REG_CONTROL_MASK_IE_ERRPASS; > > + > > + if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) > > + ctrl_mode |= ACC_REG_CONTROL_MASK_IE_BUSERR; > > + > > + if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) > > + ctrl_mode |= ACC_REG_CONTROL_MASK_MODE_LOM; > > + > > + acc_set_bits(core, ACC_CORE_OF_CTRL_MODE, ctrl_mode); > > + > > + acc_resetmode_leave(core); > > + priv->can.state = CAN_STATE_ERROR_ACTIVE; > > + > > + /* Resync TX FIFO indices to HW state after (re-)start. */ > > + tx_fifo_status = acc_read32(core, ACC_CORE_OF_TXFIFO_STATUS); > > + core->tx_fifo_head = tx_fifo_status & 0xff; > > + core->tx_fifo_tail = (tx_fifo_status >> 8) & 0xff; > > + > > + netif_start_queue(netdev); > > + return 0; > > +} > > + > > +int acc_close(struct net_device *netdev) > > +{ > > + struct acc_net_priv *priv = netdev_priv(netdev); > > + struct acc_core *core = priv->core; > > + > > + acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE, > > + ACC_REG_CONTROL_MASK_IE_RXTX | > > + ACC_REG_CONTROL_MASK_IE_TXERROR | > > + ACC_REG_CONTROL_MASK_IE_ERRWARN | > > + ACC_REG_CONTROL_MASK_IE_OVERRUN | > > + ACC_REG_CONTROL_MASK_IE_ERRPASS | > > + ACC_REG_CONTROL_MASK_IE_BUSERR); > > + > > + netif_stop_queue(netdev); > > + acc_resetmode_enter(core); > > + priv->can.state = CAN_STATE_STOPPED; > > + > > + /* Mark pending TX requests to be aborted after controller restart. */ > > + acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff); > > + > > + /* ACC_REG_CONTROL_MASK_MODE_LOM is only accessible in RESET mode */ > > + acc_clear_bits(core, ACC_CORE_OF_CTRL_MODE, > > + ACC_REG_CONTROL_MASK_MODE_LOM); > > + > > + close_candev(netdev); > > + return 0; > > +} > > + > > +netdev_tx_t acc_start_xmit(struct sk_buff *skb, struct net_device *netdev) > > +{ > > + struct acc_net_priv *priv = netdev_priv(netdev); > > + struct acc_core *core = priv->core; > > + struct can_frame *cf = (struct can_frame *)skb->data; > > + u8 tx_fifo_head = core->tx_fifo_head; > > + int fifo_usage; > > + u32 acc_id; > > + u8 acc_dlc; > > + > > + if (can_dropped_invalid_skb(netdev, skb)) > > + return NETDEV_TX_OK; > > + > > + /* Access core->tx_fifo_tail only once because it may be changed > > + * from the interrupt level. > > + */ > > + fifo_usage = tx_fifo_head - core->tx_fifo_tail; > > + if (fifo_usage < 0) > > + fifo_usage += core->tx_fifo_size; > > + > > + if (fifo_usage >= core->tx_fifo_size - 1) { > > + netdev_err(core->netdev, > > + "BUG: TX ring full when queue awake!\n"); > > + netif_stop_queue(netdev); > > + return NETDEV_TX_BUSY; > > + } > > + > > + if (fifo_usage == core->tx_fifo_size - 2) > > + netif_stop_queue(netdev); > > + > > + acc_dlc = can_get_cc_dlc(cf, priv->can.ctrlmode); > > + if (cf->can_id & CAN_RTR_FLAG) > > + acc_dlc |= ACC_DLC_RTR_FLAG; > > + > > + if (cf->can_id & CAN_EFF_FLAG) { > > + acc_id = cf->can_id & CAN_EFF_MASK; > > + acc_id |= ACC_ID_EFF_FLAG; > > + } else { > > + acc_id = cf->can_id & CAN_SFF_MASK; > > + } > > + > > + can_put_echo_skb(skb, netdev, core->tx_fifo_head, 0); > > + > > + core->tx_fifo_head = acc_tx_fifo_next(core, tx_fifo_head); > > + > > + acc_txq_put(core, acc_id, acc_dlc, cf->data); > > + > > + return NETDEV_TX_OK; > > +} > > + > > +int acc_get_berr_counter(const struct net_device *netdev, > > + struct can_berr_counter *bec) > > +{ > > + struct acc_net_priv *priv = netdev_priv(netdev); > > + u32 core_status = acc_read32(priv->core, ACC_CORE_OF_STATUS); > > + > > + bec->txerr = (core_status >> 8) & 0xff; > > + bec->rxerr = core_status & 0xff; > > + > > + return 0; > > +} > > + > > +int acc_set_mode(struct net_device *netdev, enum can_mode mode) > > +{ > > + struct acc_net_priv *priv = netdev_priv(netdev); > > + > > + switch (mode) { > > + case CAN_MODE_START: > > + /* Paranoid FIFO index check. */ > > + { > > + const u32 tx_fifo_status = > > + acc_read32(priv->core, ACC_CORE_OF_TXFIFO_STATUS); > > + const u8 hw_fifo_head = tx_fifo_status; > > + > > + if (hw_fifo_head != priv->core->tx_fifo_head || > > + hw_fifo_head != priv->core->tx_fifo_tail) { > > + netdev_warn(netdev, > > + "TX FIFO mismatch: T %2u H %2u; TFHW %#08x\n", > > + priv->core->tx_fifo_tail, > > + priv->core->tx_fifo_head, > > + tx_fifo_status); > > + } > > + } > > + acc_resetmode_leave(priv->core); > > + /* To leave the bus-off state the esdACC controller begins > > + * here a grace period where it counts 128 "idle conditions" (each > > + * of 11 consecutive recessive bits) on the bus as required > > + * by the CAN spec. > > + * > > + * During this time the TX FIFO may still contain already > > + * aborted "zombie" frames that are only drained from the FIFO > > + * at the end of the grace period. > > + * > > + * To not to interfere with this drain process we don't > > + * call netif_wake_queue() here. When the controller reaches > > + * the error-active state again, it informs us about that > > + * with an acc_bmmsg_errstatechange message. Then > > + * netif_wake_queue() is called from > > + * handle_core_msg_errstatechange() instead. > > + */ > > + break; > > + > > + default: > > + return -EOPNOTSUPP; > > + } > > + > > + return 0; > > +} > > + > > +int acc_set_bittiming(struct net_device *netdev) > > +{ > > + struct acc_net_priv *priv = netdev_priv(netdev); > > + const struct can_bittiming *bt = &priv->can.bittiming; > > + u32 brp; > > + u32 btr; > > + > > + if (priv->ov->features & ACC_OV_REG_FEAT_MASK_CANFD) { > > + u32 fbtr = 0; > > + > > + netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n", > > + bt->brp, bt->prop_seg, > > + bt->phase_seg1, bt->phase_seg2, bt->sjw); > > + > > + brp = FIELD_PREP(ACC_REG_BRP_FD_MASK_BRP, bt->brp - 1); > > + > > + btr = FIELD_PREP(ACC_REG_BTR_FD_MASK_TSEG1, bt->phase_seg1 + bt->prop_seg - 1); > > + btr |= FIELD_PREP(ACC_REG_BTR_FD_MASK_TSEG2, bt->phase_seg2 - 1); > > + btr |= FIELD_PREP(ACC_REG_BTR_FD_MASK_SJW, bt->sjw - 1); > > + > > + /* Keep order of accesses to ACC_CORE_OF_BRP and ACC_CORE_OF_BTR. */ > > + acc_write32(priv->core, ACC_CORE_OF_BRP, brp); > > + acc_write32(priv->core, ACC_CORE_OF_BTR, btr); > > + > > + netdev_dbg(netdev, "esdACC: BRP %u, NBTR 0x%08x, DBTR 0x%08x", > > + brp, btr, fbtr); > > + } else { > > + netdev_dbg(netdev, "bit timing: brp %u, prop %u, ph1 %u ph2 %u, sjw %u\n", > > + bt->brp, bt->prop_seg, > > + bt->phase_seg1, bt->phase_seg2, bt->sjw); > > + > > + brp = FIELD_PREP(ACC_REG_BRP_CL_MASK_BRP, bt->brp - 1); > > + > > + btr = FIELD_PREP(ACC_REG_BTR_CL_MASK_TSEG1, bt->phase_seg1 + bt->prop_seg - 1); > > + btr |= FIELD_PREP(ACC_REG_BTR_CL_MASK_TSEG2, bt->phase_seg2 - 1); > > + btr |= FIELD_PREP(ACC_REG_BTR_CL_MASK_SJW, bt->sjw - 1); > > + > > + /* Keep order of accesses to ACC_CORE_OF_BRP and ACC_CORE_OF_BTR. */ > > + acc_write32(priv->core, ACC_CORE_OF_BRP, brp); > > + acc_write32(priv->core, ACC_CORE_OF_BTR, btr); > > + > > + netdev_dbg(netdev, "esdACC: BRP %u, BTR 0x%08x", brp, btr); > > + } > > + > > + return 0; > > +} > > + > > +static void handle_core_msg_rxtxdone(struct acc_core *core, > > + const struct acc_bmmsg_rxtxdone *msg) > > +{ > > + struct acc_net_priv *priv = netdev_priv(core->netdev); > > + struct net_device_stats *stats = &core->netdev->stats; > > + struct sk_buff *skb; > > + > > + if (msg->acc_dlc.len & ACC_DLC_TXD_FLAG) { > > + u8 tx_fifo_tail = core->tx_fifo_tail; > > + > > + if (core->tx_fifo_head == tx_fifo_tail) { > > + netdev_warn(core->netdev, > > + "TX interrupt, but queue is empty!?\n"); > > + return; > > + } > > + > > + /* Direct access echo skb to attach HW time stamp. */ > > + skb = priv->can.echo_skb[tx_fifo_tail]; > > + if (skb) { > > + skb_hwtstamps(skb)->hwtstamp = > > + acc_ts2ktime(priv->ov, msg->ts); > > + } > > + > > + stats->tx_packets++; > > + stats->tx_bytes += can_get_echo_skb(core->netdev, tx_fifo_tail, > > + NULL); > > + > > + core->tx_fifo_tail = acc_tx_fifo_next(core, tx_fifo_tail); > > + > > + netif_wake_queue(core->netdev); > > + > > + } else { > > + struct can_frame *cf; > > + > > + skb = alloc_can_skb(core->netdev, &cf); > > + if (!skb) { > > + stats->rx_dropped++; > > + return; > > + } > > + > > + cf->can_id = msg->id & ACC_ID_ID_MASK; > > + if (msg->id & ACC_ID_EFF_FLAG) > > + cf->can_id |= CAN_EFF_FLAG; > > + > > + can_frame_set_cc_len(cf, msg->acc_dlc.len & ACC_DLC_DLC_MASK, > > + priv->can.ctrlmode); > > + > > + if (msg->acc_dlc.len & ACC_DLC_RTR_FLAG) { > > + cf->can_id |= CAN_RTR_FLAG; > > + } else { > > + memcpy(cf->data, msg->data, cf->len); > > + stats->rx_bytes += cf->len; > > + } > > + stats->rx_packets++; > > + > > + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); > > + > > + netif_rx(skb); > > + } > > +} > > + > > +static void handle_core_msg_txabort(struct acc_core *core, > > + const struct acc_bmmsg_txabort *msg) > > +{ > > + struct net_device_stats *stats = &core->netdev->stats; > > + u8 tx_fifo_tail = core->tx_fifo_tail; > > + u32 abort_mask = msg->abort_mask; /* u32 extend to avoid warnings later */ > > + > > + /* The abort_mask shows which frames were aborted in esdACC's FIFO. */ > > + while (tx_fifo_tail != core->tx_fifo_head && (abort_mask)) { > > + const u32 tail_mask = (1U << tx_fifo_tail); > > + > > + if (!(abort_mask & tail_mask)) > > + break; > > + abort_mask &= ~tail_mask; > > + > > + can_free_echo_skb(core->netdev, tx_fifo_tail, NULL); > > + stats->tx_dropped++; > > + stats->tx_aborted_errors++; > > + > > + tx_fifo_tail = acc_tx_fifo_next(core, tx_fifo_tail); > > + } > > + core->tx_fifo_tail = tx_fifo_tail; > > + if (abort_mask) > > + netdev_warn(core->netdev, "Unhandled aborted messages\n"); > > + > > + if (!acc_resetmode_entered(core)) > > + netif_wake_queue(core->netdev); > > +} > > + > > +static void handle_core_msg_overrun(struct acc_core *core, > > + const struct acc_bmmsg_overrun *msg) > > +{ > > + struct acc_net_priv *priv = netdev_priv(core->netdev); > > + struct net_device_stats *stats = &core->netdev->stats; > > + struct can_frame *cf; > > + struct sk_buff *skb; > > + > > + /* lost_cnt may be 0 if not supported by esdACC version */ > > + if (msg->lost_cnt) { > > + stats->rx_errors += msg->lost_cnt; > > + stats->rx_over_errors += msg->lost_cnt; > > + } else { > > + stats->rx_errors++; > > + stats->rx_over_errors++; > > + } > > + > > + skb = alloc_can_err_skb(core->netdev, &cf); > > + if (!skb) > > + return; > > + > > + cf->can_id |= CAN_ERR_CRTL; > > + cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW; > > + > > + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); > > + > > + netif_rx(skb); > > +} > > + > > +static void handle_core_msg_buserr(struct acc_core *core, > > + const struct acc_bmmsg_buserr *msg) > > +{ > > + struct acc_net_priv *priv = netdev_priv(core->netdev); > > + struct net_device_stats *stats = &core->netdev->stats; > > + struct can_frame *cf; > > + struct sk_buff *skb; > > + const u32 reg_status = msg->reg_status; > > + const u8 rxerr = reg_status; > > + const u8 txerr = (reg_status >> 8); > > + u8 can_err_prot_type = 0U; > > + > > + priv->can.can_stats.bus_error++; > > + > > + /* Error occurred during transmission? */ > > + if (msg->ecc & ACC_ECC_DIR) { > > + stats->rx_errors++; > > + } else { > > + can_err_prot_type |= CAN_ERR_PROT_TX; > > + stats->tx_errors++; > > + } > > + /* Determine error type */ > > + switch (msg->ecc & ACC_ECC_MASK) { > > + case ACC_ECC_BIT: > > + can_err_prot_type |= CAN_ERR_PROT_BIT; > > + break; > > + case ACC_ECC_FORM: > > + can_err_prot_type |= CAN_ERR_PROT_FORM; > > + break; > > + case ACC_ECC_STUFF: > > + can_err_prot_type |= CAN_ERR_PROT_STUFF; > > + break; > > + default: > > + can_err_prot_type |= CAN_ERR_PROT_UNSPEC; > > + break; > > + } > > + > > + skb = alloc_can_err_skb(core->netdev, &cf); > > + if (!skb) > > + return; > > + > > + cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT; > > + > > + /* Set protocol error type */ > > + cf->data[2] = can_err_prot_type; > > + /* Set error location */ > > + cf->data[3] = msg->ecc & ACC_ECC_SEG; > > + > > + /* Insert CAN TX and RX error counters. */ > > + cf->data[6] = txerr; > > + cf->data[7] = rxerr; > > + > > + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); > > + > > + netif_rx(skb); > > +} > > + > > +static void > > +handle_core_msg_errstatechange(struct acc_core *core, > > + const struct acc_bmmsg_errstatechange *msg) > > +{ > > + struct acc_net_priv *priv = netdev_priv(core->netdev); > > + struct can_frame *cf = NULL; > > + struct sk_buff *skb; > > + const u32 reg_status = msg->reg_status; > > + const u8 rxerr = reg_status; > > + const u8 txerr = (reg_status >> 8); > > + enum can_state new_state; > > + > > + if (reg_status & ACC_REG_STATUS_MASK_STATUS_BS) { > > + new_state = CAN_STATE_BUS_OFF; > > + } else if (reg_status & ACC_REG_STATUS_MASK_STATUS_EP) { > > + new_state = CAN_STATE_ERROR_PASSIVE; > > + } else if (reg_status & ACC_REG_STATUS_MASK_STATUS_ES) { > > + new_state = CAN_STATE_ERROR_WARNING; > > + } else { > > + new_state = CAN_STATE_ERROR_ACTIVE; > > + if (priv->can.state == CAN_STATE_BUS_OFF) { > > + /* See comment in acc_set_mode() for CAN_MODE_START */ > > + netif_wake_queue(core->netdev); > > + } > > + } > > + > > + skb = alloc_can_err_skb(core->netdev, &cf); > > + > > + if (new_state != priv->can.state) { > > + enum can_state tx_state, rx_state; > > + > > + tx_state = (txerr >= rxerr) ? > > + new_state : CAN_STATE_ERROR_ACTIVE; > > + rx_state = (rxerr >= txerr) ? > > + new_state : CAN_STATE_ERROR_ACTIVE; > > + > > + /* Always call can_change_state() to update the state > > + * even if alloc_can_err_skb() may have failed. > > + * can_change_state() can cope with a NULL cf pointer. > > + */ > > + can_change_state(core->netdev, cf, tx_state, rx_state); > > + } > > + > > + if (skb) { > > + cf->can_id |= CAN_ERR_CNT; > > + cf->data[6] = txerr; > > + cf->data[7] = rxerr; > > + > > + skb_hwtstamps(skb)->hwtstamp = acc_ts2ktime(priv->ov, msg->ts); > > + > > + netif_rx(skb); > > + } > > + > > + if (new_state == CAN_STATE_BUS_OFF) { > > + acc_write32(core, ACC_CORE_OF_TX_ABORT_MASK, 0xffff); > > + can_bus_off(core->netdev); > > + } > > +} > > + > > +static void handle_core_interrupt(struct acc_core *core) > > +{ > > + u32 msg_fifo_head = core->bmfifo.local_irq_cnt & 0xff; > > + > > + while (core->bmfifo.msg_fifo_tail != msg_fifo_head) { > > + const union acc_bmmsg *msg = > > + &core->bmfifo.messages[core->bmfifo.msg_fifo_tail]; > > + > > + switch (msg->msg_id) { > > + case BM_MSG_ID_RXTXDONE: > > + handle_core_msg_rxtxdone(core, &msg->rxtxdone); > > + break; > > + > > + case BM_MSG_ID_TXABORT: > > + handle_core_msg_txabort(core, &msg->txabort); > > + break; > > + > > + case BM_MSG_ID_OVERRUN: > > + handle_core_msg_overrun(core, &msg->overrun); > > + break; > > + > > + case BM_MSG_ID_BUSERR: > > + handle_core_msg_buserr(core, &msg->buserr); > > + break; > > + > > + case BM_MSG_ID_ERRPASSIVE: > > + case BM_MSG_ID_ERRWARN: > > + handle_core_msg_errstatechange(core, > > + &msg->errstatechange); > > + break; > > + > > + default: > > + /* Ignore all other BM messages (like the CAN-FD messages) */ > > + break; > > + } > > + > > + core->bmfifo.msg_fifo_tail = > > + (core->bmfifo.msg_fifo_tail + 1) & 0xff; > > + } > > +} > > + > > +/** > > + * acc_card_interrupt() - handle the interrupts of an esdACC FPGA > > + * > > + * @ov: overview module structure > > + * @cores: array of core structures > > + * > > + * This function handles all interrupts pending for the overview module and the > > + * CAN cores of the esdACC FPGA. > > + * > > + * It examines for all cores (the overview module core and the CAN cores) > > + * the bmfifo.irq_cnt and compares it with the previously saved > > + * bmfifo.local_irq_cnt. An IRQ is pending if they differ. The esdACC FPGA > > + * updates the bmfifo.irq_cnt values by DMA. > > + * > > + * The pending interrupts are masked by writing to the IRQ mask register at > > + * ACC_OV_OF_BM_IRQ_MASK. This register has for each core a two bit command > > + * field evaluated as follows: > > + * > > + * Define, bit pattern: meaning > > + * 00: no action > > + * ACC_BM_IRQ_UNMASK, 01: unmask interrupt > > + * ACC_BM_IRQ_MASK, 10: mask interrupt > > + * 11: no action > > Nitpick: consider this format > > * Define bit pattern meaning > * ------------------------------------------------------ > * 00 no action > * ACC_BM_IRQ_UNMASK 01 unmask interrupt > * ACC_BM_IRQ_MASK 10 mask interrupt > * 11 no action > > (I am using spaces due to a limitation of my email client, you can use > tab instead) > > > + * For each CAN core with a pending IRQ handle_core_interrupt() handles all > > + * busmaster messages from the message FIFO. The last handled message (FIFO > > + * index) is written to the CAN core to acknowledge its handling. > > + * > > + * Last step is to unmask all interrupts in the FPGA using > > + * ACC_BM_IRQ_UNMASK_ALL. > > + * > > + * Return: > > + * IRQ_HANDLED, if card generated an interrupt that was handled > > + * IRQ_NONE, if the interrupt is not ours > > + */ > > +irqreturn_t acc_card_interrupt(struct acc_ov *ov, struct acc_core *cores) > > +{ > > + u32 irqmask; > > + int i; > > + > > + /* First we look for whom interrupts are pending, card/overview > > + * or any of the cores. Two bits in irqmask are used for each; > > + * Each two bit field is set to ACC_BM_IRQ_MASK if an IRQ is > > + * pending. > > + */ > > + irqmask = 0U; > > + if (READ_ONCE(*ov->bmfifo.irq_cnt) != ov->bmfifo.local_irq_cnt) { > > + irqmask |= ACC_BM_IRQ_MASK; > > + ov->bmfifo.local_irq_cnt = READ_ONCE(*ov->bmfifo.irq_cnt); > > + } > > + > > + for (i = 0; i < ov->active_cores; i++) { > > + struct acc_core *core = &cores[i]; > > + > > + if (READ_ONCE(*core->bmfifo.irq_cnt) != core->bmfifo.local_irq_cnt) { > > + irqmask |= (ACC_BM_IRQ_MASK << (2 * (i + 1))); > > + core->bmfifo.local_irq_cnt = READ_ONCE(*core->bmfifo.irq_cnt); > > + } > > + } > > + > > + if (!irqmask) > > + return IRQ_NONE; > > + > > + /* At second we tell the card we're working on them by writing irqmask, > > + * call handle_{ov|core}_interrupt and then acknowledge the > > + * interrupts by writing irq_cnt: > > + */ > > + acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, irqmask); > > + > > + if (irqmask & ACC_BM_IRQ_MASK) { > > + /* handle_ov_interrupt(); - no use yet. */ > > + acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_COUNTER, > > + ov->bmfifo.local_irq_cnt); > > + } > > + > > + for (i = 0; i < ov->active_cores; i++) { > > + struct acc_core *core = &cores[i]; > > + > > + if (irqmask & (ACC_BM_IRQ_MASK << (2 * (i + 1)))) { > > + handle_core_interrupt(core); > > + acc_write32(core, ACC_OV_OF_BM_IRQ_COUNTER, > > + core->bmfifo.local_irq_cnt); > > + } > > + } > > + > > + acc_ov_write32(ov, ACC_OV_OF_BM_IRQ_MASK, ACC_BM_IRQ_UNMASK_ALL); > > + > > + return IRQ_HANDLED; > > +} > > (...) > > Yours sincerely, > Vincent Mailhol