| // 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_SSTX_FLAG BIT(24) /* Single Shot TX */ | |
| /* esdACC DLC in struct acc_bmmsg_rxtxdone::acc_dlc.len only! */ | |
| #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, | |
| ACC_REG_CTRL_MASK_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, | |
| ACC_REG_CTRL_MASK_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, u32 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); | |
| } | |
| #undef ACC_TS_FACTOR | |
| #undef ACC_TS_80MHZ_SHIFT | |
| 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; | |
| 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 = ACC_REG_CTRL_MASK_IE_RXTX | | |
| ACC_REG_CTRL_MASK_IE_TXERROR | | |
| ACC_REG_CTRL_MASK_IE_ERRWARN | | |
| ACC_REG_CTRL_MASK_IE_OVERRUN | | |
| ACC_REG_CTRL_MASK_IE_ERRPASS; | |
| if (priv->can.ctrlmode & CAN_CTRLMODE_BERR_REPORTING) | |
| ctrl |= ACC_REG_CTRL_MASK_IE_BUSERR; | |
| if (priv->can.ctrlmode & CAN_CTRLMODE_LISTENONLY) | |
| ctrl |= ACC_REG_CTRL_MASK_LOM; | |
| acc_set_bits(core, ACC_CORE_OF_CTRL, ctrl); | |
| 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, | |
| ACC_REG_CTRL_MASK_IE_RXTX | | |
| ACC_REG_CTRL_MASK_IE_TXERROR | | |
| ACC_REG_CTRL_MASK_IE_ERRWARN | | |
| ACC_REG_CTRL_MASK_IE_OVERRUN | | |
| ACC_REG_CTRL_MASK_IE_ERRPASS | | |
| ACC_REG_CTRL_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_CTRL_MASK_LOM is only accessible in RESET mode */ | |
| acc_clear_bits(core, ACC_CORE_OF_CTRL, | |
| ACC_REG_CTRL_MASK_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; | |
| u32 acc_dlc; | |
| if (can_dev_dropped_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 (priv->can.ctrlmode & CAN_CTRLMODE_ONE_SHOT) | |
| acc_dlc |= ACC_DLC_SSTX_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 | |
| * | |
| * 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; | |
| } |