| // SPDX-License-Identifier: GPL-2.0 |
| // |
| // mcp251xfd - Microchip MCP251xFD Family CAN controller driver |
| // |
| // Copyright (c) 2019, 2020, 2021, 2023 Pengutronix, |
| // Marc Kleine-Budde <kernel@pengutronix.de> |
| // |
| // Based on: |
| // |
| // CAN bus driver for Microchip 25XXFD CAN Controller with SPI Interface |
| // |
| // Copyright (c) 2019 Martin Sperl <kernel@martin.sperl.org> |
| // |
| |
| #include <linux/bitfield.h> |
| |
| #include "mcp251xfd.h" |
| |
| static inline bool mcp251xfd_rx_fifo_sta_empty(const u32 fifo_sta) |
| { |
| return !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF); |
| } |
| |
| static inline bool mcp251xfd_rx_fifo_sta_full(const u32 fifo_sta) |
| { |
| return fifo_sta & MCP251XFD_REG_FIFOSTA_TFERFFIF; |
| } |
| |
| static inline int |
| mcp251xfd_rx_tail_get_from_chip(const struct mcp251xfd_priv *priv, |
| const struct mcp251xfd_rx_ring *ring, |
| u8 *rx_tail) |
| { |
| u32 fifo_ua; |
| int err; |
| |
| err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOUA(ring->fifo_nr), |
| &fifo_ua); |
| if (err) |
| return err; |
| |
| fifo_ua -= ring->base - MCP251XFD_RAM_START; |
| *rx_tail = fifo_ua / ring->obj_size; |
| |
| return 0; |
| } |
| |
| static int |
| mcp251xfd_check_rx_tail(const struct mcp251xfd_priv *priv, |
| const struct mcp251xfd_rx_ring *ring) |
| { |
| u8 rx_tail_chip, rx_tail; |
| int err; |
| |
| if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY)) |
| return 0; |
| |
| err = mcp251xfd_rx_tail_get_from_chip(priv, ring, &rx_tail_chip); |
| if (err) |
| return err; |
| |
| rx_tail = mcp251xfd_get_rx_tail(ring); |
| if (rx_tail_chip != rx_tail) { |
| netdev_err(priv->ndev, |
| "RX tail of chip (%d) and ours (%d) inconsistent.\n", |
| rx_tail_chip, rx_tail); |
| return -EILSEQ; |
| } |
| |
| return 0; |
| } |
| |
| static int |
| mcp251xfd_get_rx_len(const struct mcp251xfd_priv *priv, |
| const struct mcp251xfd_rx_ring *ring, |
| u8 *len_p) |
| { |
| const u8 shift = ring->obj_num_shift_to_u8; |
| u8 chip_head, tail, len; |
| u32 fifo_sta; |
| int err; |
| |
| err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOSTA(ring->fifo_nr), |
| &fifo_sta); |
| if (err) |
| return err; |
| |
| if (mcp251xfd_rx_fifo_sta_empty(fifo_sta)) { |
| *len_p = 0; |
| return 0; |
| } |
| |
| if (mcp251xfd_rx_fifo_sta_full(fifo_sta)) { |
| *len_p = ring->obj_num; |
| return 0; |
| } |
| |
| chip_head = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta); |
| |
| err = mcp251xfd_check_rx_tail(priv, ring); |
| if (err) |
| return err; |
| tail = mcp251xfd_get_rx_tail(ring); |
| |
| /* First shift to full u8. The subtraction works on signed |
| * values, that keeps the difference steady around the u8 |
| * overflow. The right shift acts on len, which is an u8. |
| */ |
| BUILD_BUG_ON(sizeof(ring->obj_num) != sizeof(chip_head)); |
| BUILD_BUG_ON(sizeof(ring->obj_num) != sizeof(tail)); |
| BUILD_BUG_ON(sizeof(ring->obj_num) != sizeof(len)); |
| |
| len = (chip_head << shift) - (tail << shift); |
| *len_p = len >> shift; |
| |
| return 0; |
| } |
| |
| static void |
| mcp251xfd_hw_rx_obj_to_skb(const struct mcp251xfd_priv *priv, |
| const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, |
| struct sk_buff *skb) |
| { |
| struct canfd_frame *cfd = (struct canfd_frame *)skb->data; |
| u8 dlc; |
| |
| if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_IDE) { |
| u32 sid, eid; |
| |
| eid = FIELD_GET(MCP251XFD_OBJ_ID_EID_MASK, hw_rx_obj->id); |
| sid = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, hw_rx_obj->id); |
| |
| cfd->can_id = CAN_EFF_FLAG | |
| FIELD_PREP(MCP251XFD_REG_FRAME_EFF_EID_MASK, eid) | |
| FIELD_PREP(MCP251XFD_REG_FRAME_EFF_SID_MASK, sid); |
| } else { |
| cfd->can_id = FIELD_GET(MCP251XFD_OBJ_ID_SID_MASK, |
| hw_rx_obj->id); |
| } |
| |
| dlc = FIELD_GET(MCP251XFD_OBJ_FLAGS_DLC_MASK, hw_rx_obj->flags); |
| |
| /* CANFD */ |
| if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) { |
| if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_ESI) |
| cfd->flags |= CANFD_ESI; |
| |
| if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_BRS) |
| cfd->flags |= CANFD_BRS; |
| |
| cfd->len = can_fd_dlc2len(dlc); |
| } else { |
| if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR) |
| cfd->can_id |= CAN_RTR_FLAG; |
| |
| can_frame_set_cc_len((struct can_frame *)cfd, dlc, |
| priv->can.ctrlmode); |
| } |
| |
| if (!(hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_RTR)) |
| memcpy(cfd->data, hw_rx_obj->data, cfd->len); |
| } |
| |
| static int |
| mcp251xfd_handle_rxif_one(struct mcp251xfd_priv *priv, |
| struct mcp251xfd_rx_ring *ring, |
| const struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj) |
| { |
| struct net_device_stats *stats = &priv->ndev->stats; |
| struct sk_buff *skb; |
| struct canfd_frame *cfd; |
| u64 timestamp; |
| int err; |
| |
| /* According to mcp2518fd erratum DS80000789E 6. the FIFOCI |
| * bits of a FIFOSTA register, here the RX FIFO head index |
| * might be corrupted and we might process past the RX FIFO's |
| * head into old CAN frames. |
| * |
| * Compare the timestamp of currently processed CAN frame with |
| * last valid frame received. Abort with -EBADMSG if an old |
| * CAN frame is detected. |
| */ |
| timestamp = timecounter_cyc2time(&priv->tc, hw_rx_obj->ts); |
| if (timestamp <= ring->last_valid) { |
| stats->rx_fifo_errors++; |
| |
| return -EBADMSG; |
| } |
| ring->last_valid = timestamp; |
| |
| if (hw_rx_obj->flags & MCP251XFD_OBJ_FLAGS_FDF) |
| skb = alloc_canfd_skb(priv->ndev, &cfd); |
| else |
| skb = alloc_can_skb(priv->ndev, (struct can_frame **)&cfd); |
| |
| if (!skb) { |
| stats->rx_dropped++; |
| return 0; |
| } |
| |
| mcp251xfd_skb_set_timestamp(skb, timestamp); |
| mcp251xfd_hw_rx_obj_to_skb(priv, hw_rx_obj, skb); |
| err = can_rx_offload_queue_timestamp(&priv->offload, skb, hw_rx_obj->ts); |
| if (err) |
| stats->rx_fifo_errors++; |
| |
| return 0; |
| } |
| |
| static inline int |
| mcp251xfd_rx_obj_read(const struct mcp251xfd_priv *priv, |
| const struct mcp251xfd_rx_ring *ring, |
| struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj, |
| const u8 offset, const u8 len) |
| { |
| const int val_bytes = regmap_get_val_bytes(priv->map_rx); |
| int err; |
| |
| err = regmap_bulk_read(priv->map_rx, |
| mcp251xfd_get_rx_obj_addr(ring, offset), |
| hw_rx_obj, |
| len * ring->obj_size / val_bytes); |
| |
| return err; |
| } |
| |
| static int |
| mcp251xfd_handle_rxif_ring_uinc(const struct mcp251xfd_priv *priv, |
| struct mcp251xfd_rx_ring *ring, |
| u8 len) |
| { |
| int offset; |
| int err; |
| |
| if (!len) |
| return 0; |
| |
| ring->head += len; |
| |
| /* Increment the RX FIFO tail pointer 'len' times in a |
| * single SPI message. |
| * |
| * Note: |
| * Calculate offset, so that the SPI transfer ends on |
| * the last message of the uinc_xfer array, which has |
| * "cs_change == 0", to properly deactivate the chip |
| * select. |
| */ |
| offset = ARRAY_SIZE(ring->uinc_xfer) - len; |
| err = spi_sync_transfer(priv->spi, |
| ring->uinc_xfer + offset, len); |
| if (err) |
| return err; |
| |
| ring->tail += len; |
| |
| return 0; |
| } |
| |
| static int |
| mcp251xfd_handle_rxif_ring(struct mcp251xfd_priv *priv, |
| struct mcp251xfd_rx_ring *ring) |
| { |
| struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj = ring->obj; |
| u8 rx_tail, len, l; |
| int err, i; |
| |
| err = mcp251xfd_get_rx_len(priv, ring, &len); |
| if (err) |
| return err; |
| |
| while ((l = mcp251xfd_get_rx_linear_len(ring, len))) { |
| rx_tail = mcp251xfd_get_rx_tail(ring); |
| |
| err = mcp251xfd_rx_obj_read(priv, ring, hw_rx_obj, |
| rx_tail, l); |
| if (err) |
| return err; |
| |
| for (i = 0; i < l; i++) { |
| err = mcp251xfd_handle_rxif_one(priv, ring, |
| (void *)hw_rx_obj + |
| i * ring->obj_size); |
| |
| /* -EBADMSG means we're affected by mcp2518fd |
| * erratum DS80000789E 6., i.e. the timestamp |
| * in the RX object is older that the last |
| * valid received CAN frame. Don't process any |
| * further and mark processed frames as good. |
| */ |
| if (err == -EBADMSG) |
| return mcp251xfd_handle_rxif_ring_uinc(priv, ring, i); |
| else if (err) |
| return err; |
| } |
| |
| err = mcp251xfd_handle_rxif_ring_uinc(priv, ring, l); |
| if (err) |
| return err; |
| |
| len -= l; |
| } |
| |
| return 0; |
| } |
| |
| int mcp251xfd_handle_rxif(struct mcp251xfd_priv *priv) |
| { |
| struct mcp251xfd_rx_ring *ring; |
| int err, n; |
| |
| mcp251xfd_for_each_rx_ring(priv, ring, n) { |
| /* - if RX IRQ coalescing is active always handle ring 0 |
| * - only handle rings if RX IRQ is active |
| */ |
| if ((ring->nr > 0 || !priv->rx_obj_num_coalesce_irq) && |
| !(priv->regs_status.rxif & BIT(ring->fifo_nr))) |
| continue; |
| |
| err = mcp251xfd_handle_rxif_ring(priv, ring); |
| if (err) |
| return err; |
| } |
| |
| if (priv->rx_coalesce_usecs_irq) |
| hrtimer_start(&priv->rx_irq_timer, |
| ns_to_ktime(priv->rx_coalesce_usecs_irq * |
| NSEC_PER_USEC), |
| HRTIMER_MODE_REL); |
| |
| return 0; |
| } |