| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- // SPDX-License-Identifier: GPL-2.0
- //
- // Copyright (c) 2023, 2024 Pengutronix,
- // Marc Kleine-Budde <kernel@pengutronix.de>
- //
- #include <net/netdev_queues.h>
- #include "rockchip_canfd.h"
- static bool rkcanfd_tx_tail_is_eff(const struct rkcanfd_priv *priv)
- {
- const struct canfd_frame *cfd;
- const struct sk_buff *skb;
- unsigned int tx_tail;
- if (!rkcanfd_get_tx_pending(priv))
- return false;
- tx_tail = rkcanfd_get_tx_tail(priv);
- skb = priv->can.echo_skb[tx_tail];
- if (!skb) {
- netdev_err(priv->ndev,
- "%s: echo_skb[%u]=NULL tx_head=0x%08x tx_tail=0x%08x\n",
- __func__, tx_tail,
- priv->tx_head, priv->tx_tail);
- return false;
- }
- cfd = (struct canfd_frame *)skb->data;
- return cfd->can_id & CAN_EFF_FLAG;
- }
- unsigned int rkcanfd_get_effective_tx_free(const struct rkcanfd_priv *priv)
- {
- if (priv->devtype_data.quirks & RKCANFD_QUIRK_RK3568_ERRATUM_6 &&
- rkcanfd_tx_tail_is_eff(priv))
- return 0;
- return rkcanfd_get_tx_free(priv);
- }
- static void rkcanfd_start_xmit_write_cmd(const struct rkcanfd_priv *priv,
- const u32 reg_cmd)
- {
- if (priv->devtype_data.quirks & RKCANFD_QUIRK_RK3568_ERRATUM_12)
- rkcanfd_write(priv, RKCANFD_REG_MODE, priv->reg_mode_default |
- RKCANFD_REG_MODE_SPACE_RX_MODE);
- rkcanfd_write(priv, RKCANFD_REG_CMD, reg_cmd);
- if (priv->devtype_data.quirks & RKCANFD_QUIRK_RK3568_ERRATUM_12)
- rkcanfd_write(priv, RKCANFD_REG_MODE, priv->reg_mode_default);
- }
- void rkcanfd_xmit_retry(struct rkcanfd_priv *priv)
- {
- const unsigned int tx_head = rkcanfd_get_tx_head(priv);
- const u32 reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head);
- rkcanfd_start_xmit_write_cmd(priv, reg_cmd);
- }
- netdev_tx_t rkcanfd_start_xmit(struct sk_buff *skb, struct net_device *ndev)
- {
- struct rkcanfd_priv *priv = netdev_priv(ndev);
- u32 reg_frameinfo, reg_id, reg_cmd;
- unsigned int tx_head, frame_len;
- const struct canfd_frame *cfd;
- int err;
- u8 i;
- if (can_dev_dropped_skb(ndev, skb))
- return NETDEV_TX_OK;
- if (!netif_subqueue_maybe_stop(priv->ndev, 0,
- rkcanfd_get_effective_tx_free(priv),
- RKCANFD_TX_STOP_THRESHOLD,
- RKCANFD_TX_START_THRESHOLD)) {
- if (net_ratelimit())
- netdev_info(priv->ndev,
- "Stopping tx-queue (tx_head=0x%08x, tx_tail=0x%08x, tx_pending=%d)\n",
- priv->tx_head, priv->tx_tail,
- rkcanfd_get_tx_pending(priv));
- return NETDEV_TX_BUSY;
- }
- cfd = (struct canfd_frame *)skb->data;
- if (cfd->can_id & CAN_EFF_FLAG) {
- reg_frameinfo = RKCANFD_REG_FD_FRAMEINFO_FRAME_FORMAT;
- reg_id = FIELD_PREP(RKCANFD_REG_FD_ID_EFF, cfd->can_id);
- } else {
- reg_frameinfo = 0;
- reg_id = FIELD_PREP(RKCANFD_REG_FD_ID_SFF, cfd->can_id);
- }
- if (cfd->can_id & CAN_RTR_FLAG)
- reg_frameinfo |= RKCANFD_REG_FD_FRAMEINFO_RTR;
- if (can_is_canfd_skb(skb)) {
- reg_frameinfo |= RKCANFD_REG_FD_FRAMEINFO_FDF;
- if (cfd->flags & CANFD_BRS)
- reg_frameinfo |= RKCANFD_REG_FD_FRAMEINFO_BRS;
- reg_frameinfo |= FIELD_PREP(RKCANFD_REG_FD_FRAMEINFO_DATA_LENGTH,
- can_fd_len2dlc(cfd->len));
- } else {
- reg_frameinfo |= FIELD_PREP(RKCANFD_REG_FD_FRAMEINFO_DATA_LENGTH,
- cfd->len);
- }
- tx_head = rkcanfd_get_tx_head(priv);
- reg_cmd = RKCANFD_REG_CMD_TX_REQ(tx_head);
- rkcanfd_write(priv, RKCANFD_REG_FD_TXFRAMEINFO, reg_frameinfo);
- rkcanfd_write(priv, RKCANFD_REG_FD_TXID, reg_id);
- for (i = 0; i < cfd->len; i += 4)
- rkcanfd_write(priv, RKCANFD_REG_FD_TXDATA0 + i,
- *(u32 *)(cfd->data + i));
- frame_len = can_skb_get_frame_len(skb);
- err = can_put_echo_skb(skb, ndev, tx_head, frame_len);
- if (!err)
- netdev_sent_queue(priv->ndev, frame_len);
- WRITE_ONCE(priv->tx_head, priv->tx_head + 1);
- rkcanfd_start_xmit_write_cmd(priv, reg_cmd);
- netif_subqueue_maybe_stop(priv->ndev, 0,
- rkcanfd_get_effective_tx_free(priv),
- RKCANFD_TX_STOP_THRESHOLD,
- RKCANFD_TX_START_THRESHOLD);
- return NETDEV_TX_OK;
- }
- void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts,
- unsigned int *frame_len_p)
- {
- struct net_device_stats *stats = &priv->ndev->stats;
- unsigned int tx_tail;
- struct sk_buff *skb;
- tx_tail = rkcanfd_get_tx_tail(priv);
- skb = priv->can.echo_skb[tx_tail];
- /* Manual handling of CAN Bus Error counters. See
- * rkcanfd_get_corrected_berr_counter() for detailed
- * explanation.
- */
- if (priv->bec.txerr)
- priv->bec.txerr--;
- if (skb)
- rkcanfd_skb_set_timestamp(priv, skb, ts);
- stats->tx_bytes +=
- can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
- tx_tail, ts,
- frame_len_p);
- stats->tx_packets++;
- }
|