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
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
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 
52 	return 0;
53 }
54 
55 static int mcp251xfd_ring_get_coalesce(struct net_device *ndev,
56 				       struct ethtool_coalesce *ec,
57 				       struct kernel_ethtool_coalesce *kec,
58 				       struct netlink_ext_ack *ext_ack)
59 {
60 	struct mcp251xfd_priv *priv = netdev_priv(ndev);
61 	u32 rx_max_frames, tx_max_frames;
62 
63 	/* The ethtool doc says:
64 	 * To disable coalescing, set usecs = 0 and max_frames = 1.
65 	 */
66 	if (priv->rx_obj_num_coalesce_irq == 0)
67 		rx_max_frames = 1;
68 	else
69 		rx_max_frames = priv->rx_obj_num_coalesce_irq;
70 
71 	ec->rx_max_coalesced_frames_irq = rx_max_frames;
72 	ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq;
73 
74 	if (priv->tx_obj_num_coalesce_irq == 0)
75 		tx_max_frames = 1;
76 	else
77 		tx_max_frames = priv->tx_obj_num_coalesce_irq;
78 
79 	ec->tx_max_coalesced_frames_irq = tx_max_frames;
80 	ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq;
81 
82 	return 0;
83 }
84 
85 static int mcp251xfd_ring_set_coalesce(struct net_device *ndev,
86 				       struct ethtool_coalesce *ec,
87 				       struct kernel_ethtool_coalesce *kec,
88 				       struct netlink_ext_ack *ext_ack)
89 {
90 	struct mcp251xfd_priv *priv = netdev_priv(ndev);
91 	const bool fd_mode = mcp251xfd_is_fd_mode(priv);
92 	const struct ethtool_ringparam ring = {
93 		.rx_pending = priv->rx_obj_num,
94 		.tx_pending = priv->tx->obj_num,
95 	};
96 	struct can_ram_layout layout;
97 
98 	can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode);
99 
100 	if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq ||
101 	     ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq ||
102 	     layout.tx_coalesce != priv->tx_obj_num_coalesce_irq ||
103 	     ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) &&
104 	    netif_running(ndev))
105 		return -EBUSY;
106 
107 	priv->rx_obj_num = layout.cur_rx;
108 	priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
109 	priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq;
110 
111 	priv->tx->obj_num = layout.cur_tx;
112 	priv->tx_obj_num_coalesce_irq = layout.tx_coalesce;
113 	priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq;
114 
115 	return 0;
116 }
117 
118 static const struct ethtool_ops mcp251xfd_ethtool_ops = {
119 	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ |
120 		ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |
121 		ETHTOOL_COALESCE_TX_USECS_IRQ |
122 		ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ,
123 	.get_ringparam = mcp251xfd_ring_get_ringparam,
124 	.set_ringparam = mcp251xfd_ring_set_ringparam,
125 	.get_coalesce = mcp251xfd_ring_get_coalesce,
126 	.set_coalesce = mcp251xfd_ring_set_coalesce,
127 };
128 
129 void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv)
130 {
131 	struct can_ram_layout layout;
132 
133 	priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops;
134 
135 	can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false);
136 	priv->rx_obj_num = layout.default_rx;
137 	priv->tx->obj_num = layout.default_tx;
138 
139 	priv->rx_obj_num_coalesce_irq = 0;
140 	priv->tx_obj_num_coalesce_irq = 0;
141 	priv->rx_coalesce_usecs_irq = 0;
142 	priv->tx_coalesce_usecs_irq = 0;
143 }
144