summaryrefslogtreecommitdiff
path: root/drivers/net/can/rockchip/rockchip_canfd-tx.c
blob: 668a902f4c2a4e0fa1b93af66f740ce92025a463 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 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 void rkcanfd_start_xmit_write_cmd(const struct rkcanfd_priv *priv,
					 const u32 reg_cmd)
{
	rkcanfd_write(priv, RKCANFD_REG_CMD, reg_cmd);
}

int 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_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_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;

	tx_tail = rkcanfd_get_tx_tail(priv);
	stats->tx_bytes +=
		can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
							    tx_tail, ts,
							    frame_len_p);
	stats->tx_packets++;
}