Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Marc Kleine-Budde | 1039 | 100.00% | 2 | 100.00% |
Total | 1039 | 2 |
// 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_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv, u8 *tef_tail) { u32 tef_ua; int err; err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFUA, &tef_ua); if (err) return err; *tef_tail = tef_ua / sizeof(struct mcp251xfd_hw_tef_obj); return 0; } static int mcp251xfd_check_tef_tail(const struct mcp251xfd_priv *priv) { u8 tef_tail_chip, tef_tail; int err; if (!IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY)) return 0; err = mcp251xfd_tef_tail_get_from_chip(priv, &tef_tail_chip); if (err) return err; tef_tail = mcp251xfd_get_tef_tail(priv); if (tef_tail_chip != tef_tail) { netdev_err(priv->ndev, "TEF tail of chip (0x%02x) and ours (0x%08x) inconsistent.\n", tef_tail_chip, tef_tail); return -EILSEQ; } return 0; } static int mcp251xfd_handle_tefif_recover(const struct mcp251xfd_priv *priv, const u32 seq) { const struct mcp251xfd_tx_ring *tx_ring = priv->tx; u32 tef_sta; int err; err = regmap_read(priv->map_reg, MCP251XFD_REG_TEFSTA, &tef_sta); if (err) return err; if (tef_sta & MCP251XFD_REG_TEFSTA_TEFOVIF) { netdev_err(priv->ndev, "Transmit Event FIFO buffer overflow.\n"); return -ENOBUFS; } netdev_info(priv->ndev, "Transmit Event FIFO buffer %s. (seq=0x%08x, tef_tail=0x%08x, tef_head=0x%08x, tx_head=0x%08x).\n", tef_sta & MCP251XFD_REG_TEFSTA_TEFFIF ? "full" : tef_sta & MCP251XFD_REG_TEFSTA_TEFNEIF ? "not empty" : "empty", seq, priv->tef->tail, priv->tef->head, tx_ring->head); /* The Sequence Number in the TEF doesn't match our tef_tail. */ return -EAGAIN; } static int mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv, const struct mcp251xfd_hw_tef_obj *hw_tef_obj, unsigned int *frame_len_ptr) { struct net_device_stats *stats = &priv->ndev->stats; struct sk_buff *skb; u32 seq, seq_masked, tef_tail_masked, tef_tail; seq = FIELD_GET(MCP251XFD_OBJ_FLAGS_SEQ_MCP2518FD_MASK, hw_tef_obj->flags); /* Use the MCP2517FD mask on the MCP2518FD, too. We only * compare 7 bits, this should be enough to detect * net-yet-completed, i.e. old TEF objects. */ seq_masked = seq & field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK); tef_tail_masked = priv->tef->tail & field_mask(MCP251XFD_OBJ_FLAGS_SEQ_MCP2517FD_MASK); if (seq_masked != tef_tail_masked) return mcp251xfd_handle_tefif_recover(priv, seq); tef_tail = mcp251xfd_get_tef_tail(priv); skb = priv->can.echo_skb[tef_tail]; if (skb) mcp251xfd_skb_set_timestamp(priv, skb, hw_tef_obj->ts); stats->tx_bytes += can_rx_offload_get_echo_skb(&priv->offload, tef_tail, hw_tef_obj->ts, frame_len_ptr); stats->tx_packets++; priv->tef->tail++; return 0; } static int mcp251xfd_tef_ring_update(struct mcp251xfd_priv *priv) { const struct mcp251xfd_tx_ring *tx_ring = priv->tx; unsigned int new_head; u8 chip_tx_tail; int err; err = mcp251xfd_tx_tail_get_from_chip(priv, &chip_tx_tail); if (err) return err; /* chip_tx_tail, is the next TX-Object send by the HW. * The new TEF head must be >= the old head, ... */ new_head = round_down(priv->tef->head, tx_ring->obj_num) + chip_tx_tail; if (new_head <= priv->tef->head) new_head += tx_ring->obj_num; /* ... but it cannot exceed the TX head. */ priv->tef->head = min(new_head, tx_ring->head); return mcp251xfd_check_tef_tail(priv); } static inline int mcp251xfd_tef_obj_read(const struct mcp251xfd_priv *priv, struct mcp251xfd_hw_tef_obj *hw_tef_obj, const u8 offset, const u8 len) { const struct mcp251xfd_tx_ring *tx_ring = priv->tx; const int val_bytes = regmap_get_val_bytes(priv->map_rx); if (IS_ENABLED(CONFIG_CAN_MCP251XFD_SANITY) && (offset > tx_ring->obj_num || len > tx_ring->obj_num || offset + len > tx_ring->obj_num)) { netdev_err(priv->ndev, "Trying to read too many TEF objects (max=%d, offset=%d, len=%d).\n", tx_ring->obj_num, offset, len); return -ERANGE; } return regmap_bulk_read(priv->map_rx, mcp251xfd_get_tef_obj_addr(offset), hw_tef_obj, sizeof(*hw_tef_obj) / val_bytes * len); } static inline void mcp251xfd_ecc_tefif_successful(struct mcp251xfd_priv *priv) { struct mcp251xfd_ecc *ecc = &priv->ecc; ecc->ecc_stat = 0; } int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) { struct mcp251xfd_hw_tef_obj hw_tef_obj[MCP251XFD_TX_OBJ_NUM_MAX]; unsigned int total_frame_len = 0; u8 tef_tail, len, l; int err, i; err = mcp251xfd_tef_ring_update(priv); if (err) return err; tef_tail = mcp251xfd_get_tef_tail(priv); len = mcp251xfd_get_tef_len(priv); l = mcp251xfd_get_tef_linear_len(priv); err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l); if (err) return err; if (l < len) { err = mcp251xfd_tef_obj_read(priv, &hw_tef_obj[l], 0, len - l); if (err) return err; } for (i = 0; i < len; i++) { unsigned int frame_len = 0; err = mcp251xfd_handle_tefif_one(priv, &hw_tef_obj[i], &frame_len); /* -EAGAIN means the Sequence Number in the TEF * doesn't match our tef_tail. This can happen if we * read the TEF objects too early. Leave loop let the * interrupt handler call us again. */ if (err == -EAGAIN) goto out_netif_wake_queue; if (err) return err; total_frame_len += frame_len; } out_netif_wake_queue: len = i; /* number of handled goods TEFs */ if (len) { struct mcp251xfd_tef_ring *ring = priv->tef; struct mcp251xfd_tx_ring *tx_ring = priv->tx; int offset; /* Increment the TEF 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; tx_ring->tail += len; netdev_completed_queue(priv->ndev, len, total_frame_len); err = mcp251xfd_check_tef_tail(priv); if (err) return err; } mcp251xfd_ecc_tefif_successful(priv); if (mcp251xfd_get_tx_free(priv->tx)) { /* Make sure that anybody stopping the queue after * this sees the new tx_ring->tail. */ smp_mb(); netif_wake_queue(priv->ndev); } if (priv->tx_coalesce_usecs_irq) hrtimer_start(&priv->tx_irq_timer, ns_to_ktime(priv->tx_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