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(dev: ndev); |
21 | const bool fd_mode = mcp251xfd_is_fd_mode(priv); |
22 | struct can_ram_layout layout; |
23 | |
24 | can_ram_get_layout(layout: &layout, config: &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(dev: ndev); |
39 | const bool fd_mode = mcp251xfd_is_fd_mode(priv); |
40 | struct can_ram_layout layout; |
41 | |
42 | can_ram_get_layout(layout: &layout, config: &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(dev: 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 | |
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(dev: 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 | |
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(dev: 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: &layout, config: &mcp251xfd_ram_config, ring: &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(dev: 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 | |
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: &layout, config: &mcp251xfd_ram_config, NULL, NULL, fd_mode: 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 | |