Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Marc Kleine-Budde | 1112 | 100.00% | 4 | 100.00% |
Total | 1112 | 4 |
// SPDX-License-Identifier: GPL-2.0 // // mcp251xfd - Microchip MCP251xFD Family CAN controller driver // // Copyright (c) 2019, 2020, 2021 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 int mcp251xfd_rx_head_get_from_chip(const struct mcp251xfd_priv *priv, const struct mcp251xfd_rx_ring *ring, u8 *rx_head, bool *fifo_empty) { u32 fifo_sta; int err; err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOSTA(ring->fifo_nr), &fifo_sta); if (err) return err; *rx_head = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta); *fifo_empty = !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF); return 0; } 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_rx_ring_update(const struct mcp251xfd_priv *priv, struct mcp251xfd_rx_ring *ring) { u32 new_head; u8 chip_rx_head; bool fifo_empty; int err; err = mcp251xfd_rx_head_get_from_chip(priv, ring, &chip_rx_head, &fifo_empty); if (err || fifo_empty) return err; /* chip_rx_head, is the next RX-Object filled by the HW. * The new RX head must be >= the old head. */ new_head = round_down(ring->head, ring->obj_num) + chip_rx_head; if (new_head <= ring->head) new_head += ring->obj_num; ring->head = new_head; return mcp251xfd_check_rx_tail(priv, ring); } 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); mcp251xfd_skb_set_timestamp(priv, skb, hw_rx_obj->ts); } 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; int err; 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_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(struct mcp251xfd_priv *priv, struct mcp251xfd_rx_ring *ring) { struct mcp251xfd_hw_rx_obj_canfd *hw_rx_obj = ring->obj; u8 rx_tail, len; int err, i; err = mcp251xfd_rx_ring_update(priv, ring); if (err) return err; while ((len = mcp251xfd_get_rx_linear_len(ring))) { int offset; rx_tail = mcp251xfd_get_rx_tail(ring); err = mcp251xfd_rx_obj_read(priv, ring, hw_rx_obj, rx_tail, len); if (err) return err; for (i = 0; i < len; i++) { err = mcp251xfd_handle_rxif_one(priv, ring, (void *)hw_rx_obj + i * ring->obj_size); if (err) return err; } /* 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; } 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; }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1