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 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(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(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 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