167 lines
4.4 KiB
C
167 lines
4.4 KiB
C
// 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_dropped_invalid_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++;
|
|
}
|