1 // SPDX-License-Identifier: GPL-2.0
2 //
3 // mcp251xfd - Microchip MCP251xFD Family CAN controller driver
4 //
5 // Copyright (c) 2021, 2022 Pengutronix,
6 // Marc Kleine-Budde <kernel@pengutronix.de>
7 //
8
9 #include <linux/ethtool.h>
10
11 #include "mcp251xfd.h"
12 #include "mcp251xfd-ram.h"
13
14 static void
mcp251xfd_ring_get_ringparam(struct net_device * ndev,struct ethtool_ringparam * ring,struct kernel_ethtool_ringparam * kernel_ring,struct netlink_ext_ack * extack)15 mcp251xfd_ring_get_ringparam(struct net_device *ndev,
16 struct ethtool_ringparam *ring,
17 struct kernel_ethtool_ringparam *kernel_ring,
18 struct netlink_ext_ack *extack)
19 {
20 const struct mcp251xfd_priv *priv = netdev_priv(ndev);
21 const bool fd_mode = mcp251xfd_is_fd_mode(priv);
22 struct can_ram_layout layout;
23
24 can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode);
25 ring->rx_max_pending = layout.max_rx;
26 ring->tx_max_pending = layout.max_tx;
27
28 ring->rx_pending = priv->rx_obj_num;
29 ring->tx_pending = priv->tx->obj_num;
30 }
31
32 static int
mcp251xfd_ring_set_ringparam(struct net_device * ndev,struct ethtool_ringparam * ring,struct kernel_ethtool_ringparam * kernel_ring,struct netlink_ext_ack * extack)33 mcp251xfd_ring_set_ringparam(struct net_device *ndev,
34 struct ethtool_ringparam *ring,
35 struct kernel_ethtool_ringparam *kernel_ring,
36 struct netlink_ext_ack *extack)
37 {
38 struct mcp251xfd_priv *priv = netdev_priv(ndev);
39 const bool fd_mode = mcp251xfd_is_fd_mode(priv);
40 struct can_ram_layout layout;
41
42 can_ram_get_layout(&layout, &mcp251xfd_ram_config, ring, NULL, fd_mode);
43 if ((layout.cur_rx != priv->rx_obj_num ||
44 layout.cur_tx != priv->tx->obj_num) &&
45 netif_running(ndev))
46 return -EBUSY;
47
48 priv->rx_obj_num = layout.cur_rx;
49 priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
50 priv->tx->obj_num = layout.cur_tx;
51 priv->tx_obj_num_coalesce_irq = layout.tx_coalesce;
52
53 return 0;
54 }
55
mcp251xfd_ring_get_coalesce(struct net_device * ndev,struct ethtool_coalesce * ec,struct kernel_ethtool_coalesce * kec,struct netlink_ext_ack * ext_ack)56 static int mcp251xfd_ring_get_coalesce(struct net_device *ndev,
57 struct ethtool_coalesce *ec,
58 struct kernel_ethtool_coalesce *kec,
59 struct netlink_ext_ack *ext_ack)
60 {
61 struct mcp251xfd_priv *priv = netdev_priv(ndev);
62 u32 rx_max_frames, tx_max_frames;
63
64 /* The ethtool doc says:
65 * To disable coalescing, set usecs = 0 and max_frames = 1.
66 */
67 if (priv->rx_obj_num_coalesce_irq == 0)
68 rx_max_frames = 1;
69 else
70 rx_max_frames = priv->rx_obj_num_coalesce_irq;
71
72 ec->rx_max_coalesced_frames_irq = rx_max_frames;
73 ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq;
74
75 if (priv->tx_obj_num_coalesce_irq == 0)
76 tx_max_frames = 1;
77 else
78 tx_max_frames = priv->tx_obj_num_coalesce_irq;
79
80 ec->tx_max_coalesced_frames_irq = tx_max_frames;
81 ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq;
82
83 return 0;
84 }
85
mcp251xfd_ring_set_coalesce(struct net_device * ndev,struct ethtool_coalesce * ec,struct kernel_ethtool_coalesce * kec,struct netlink_ext_ack * ext_ack)86 static int mcp251xfd_ring_set_coalesce(struct net_device *ndev,
87 struct ethtool_coalesce *ec,
88 struct kernel_ethtool_coalesce *kec,
89 struct netlink_ext_ack *ext_ack)
90 {
91 struct mcp251xfd_priv *priv = netdev_priv(ndev);
92 const bool fd_mode = mcp251xfd_is_fd_mode(priv);
93 const struct ethtool_ringparam ring = {
94 .rx_pending = priv->rx_obj_num,
95 .tx_pending = priv->tx->obj_num,
96 };
97 struct can_ram_layout layout;
98
99 can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode);
100
101 if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq ||
102 ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq ||
103 layout.tx_coalesce != priv->tx_obj_num_coalesce_irq ||
104 ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) &&
105 netif_running(ndev))
106 return -EBUSY;
107
108 priv->rx_obj_num = layout.cur_rx;
109 priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
110 priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq;
111
112 priv->tx->obj_num = layout.cur_tx;
113 priv->tx_obj_num_coalesce_irq = layout.tx_coalesce;
114 priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq;
115
116 return 0;
117 }
118
119 static const struct ethtool_ops mcp251xfd_ethtool_ops = {
120 .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ |
121 ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |
122 ETHTOOL_COALESCE_TX_USECS_IRQ |
123 ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ,
124 .get_ringparam = mcp251xfd_ring_get_ringparam,
125 .set_ringparam = mcp251xfd_ring_set_ringparam,
126 .get_coalesce = mcp251xfd_ring_get_coalesce,
127 .set_coalesce = mcp251xfd_ring_set_coalesce,
128 .get_ts_info = can_ethtool_op_get_ts_info_hwts,
129 };
130
mcp251xfd_ethtool_init(struct mcp251xfd_priv * priv)131 void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv)
132 {
133 struct can_ram_layout layout;
134
135 priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops;
136
137 can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false);
138 priv->rx_obj_num = layout.default_rx;
139 priv->tx->obj_num = layout.default_tx;
140
141 priv->rx_obj_num_coalesce_irq = 0;
142 priv->tx_obj_num_coalesce_irq = 0;
143 priv->rx_coalesce_usecs_irq = 0;
144 priv->tx_coalesce_usecs_irq = 0;
145 }
146