162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci// 362306a36Sopenharmony_ci// mcp251xfd - Microchip MCP251xFD Family CAN controller driver 462306a36Sopenharmony_ci// 562306a36Sopenharmony_ci// Copyright (c) 2021, 2022 Pengutronix, 662306a36Sopenharmony_ci// Marc Kleine-Budde <kernel@pengutronix.de> 762306a36Sopenharmony_ci// 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/ethtool.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "mcp251xfd.h" 1262306a36Sopenharmony_ci#include "mcp251xfd-ram.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_cistatic void 1562306a36Sopenharmony_cimcp251xfd_ring_get_ringparam(struct net_device *ndev, 1662306a36Sopenharmony_ci struct ethtool_ringparam *ring, 1762306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 1862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 1962306a36Sopenharmony_ci{ 2062306a36Sopenharmony_ci const struct mcp251xfd_priv *priv = netdev_priv(ndev); 2162306a36Sopenharmony_ci const bool fd_mode = mcp251xfd_is_fd_mode(priv); 2262306a36Sopenharmony_ci struct can_ram_layout layout; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode); 2562306a36Sopenharmony_ci ring->rx_max_pending = layout.max_rx; 2662306a36Sopenharmony_ci ring->tx_max_pending = layout.max_tx; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci ring->rx_pending = priv->rx_obj_num; 2962306a36Sopenharmony_ci ring->tx_pending = priv->tx->obj_num; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int 3362306a36Sopenharmony_cimcp251xfd_ring_set_ringparam(struct net_device *ndev, 3462306a36Sopenharmony_ci struct ethtool_ringparam *ring, 3562306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 3662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci struct mcp251xfd_priv *priv = netdev_priv(ndev); 3962306a36Sopenharmony_ci const bool fd_mode = mcp251xfd_is_fd_mode(priv); 4062306a36Sopenharmony_ci struct can_ram_layout layout; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci can_ram_get_layout(&layout, &mcp251xfd_ram_config, ring, NULL, fd_mode); 4362306a36Sopenharmony_ci if ((layout.cur_rx != priv->rx_obj_num || 4462306a36Sopenharmony_ci layout.cur_tx != priv->tx->obj_num) && 4562306a36Sopenharmony_ci netif_running(ndev)) 4662306a36Sopenharmony_ci return -EBUSY; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci priv->rx_obj_num = layout.cur_rx; 4962306a36Sopenharmony_ci priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; 5062306a36Sopenharmony_ci priv->tx->obj_num = layout.cur_tx; 5162306a36Sopenharmony_ci priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return 0; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int mcp251xfd_ring_get_coalesce(struct net_device *ndev, 5762306a36Sopenharmony_ci struct ethtool_coalesce *ec, 5862306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kec, 5962306a36Sopenharmony_ci struct netlink_ext_ack *ext_ack) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct mcp251xfd_priv *priv = netdev_priv(ndev); 6262306a36Sopenharmony_ci u32 rx_max_frames, tx_max_frames; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci /* The ethtool doc says: 6562306a36Sopenharmony_ci * To disable coalescing, set usecs = 0 and max_frames = 1. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_ci if (priv->rx_obj_num_coalesce_irq == 0) 6862306a36Sopenharmony_ci rx_max_frames = 1; 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci rx_max_frames = priv->rx_obj_num_coalesce_irq; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci ec->rx_max_coalesced_frames_irq = rx_max_frames; 7362306a36Sopenharmony_ci ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (priv->tx_obj_num_coalesce_irq == 0) 7662306a36Sopenharmony_ci tx_max_frames = 1; 7762306a36Sopenharmony_ci else 7862306a36Sopenharmony_ci tx_max_frames = priv->tx_obj_num_coalesce_irq; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci ec->tx_max_coalesced_frames_irq = tx_max_frames; 8162306a36Sopenharmony_ci ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci} 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cistatic int mcp251xfd_ring_set_coalesce(struct net_device *ndev, 8762306a36Sopenharmony_ci struct ethtool_coalesce *ec, 8862306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kec, 8962306a36Sopenharmony_ci struct netlink_ext_ack *ext_ack) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct mcp251xfd_priv *priv = netdev_priv(ndev); 9262306a36Sopenharmony_ci const bool fd_mode = mcp251xfd_is_fd_mode(priv); 9362306a36Sopenharmony_ci const struct ethtool_ringparam ring = { 9462306a36Sopenharmony_ci .rx_pending = priv->rx_obj_num, 9562306a36Sopenharmony_ci .tx_pending = priv->tx->obj_num, 9662306a36Sopenharmony_ci }; 9762306a36Sopenharmony_ci struct can_ram_layout layout; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq || 10262306a36Sopenharmony_ci ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq || 10362306a36Sopenharmony_ci layout.tx_coalesce != priv->tx_obj_num_coalesce_irq || 10462306a36Sopenharmony_ci ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) && 10562306a36Sopenharmony_ci netif_running(ndev)) 10662306a36Sopenharmony_ci return -EBUSY; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci priv->rx_obj_num = layout.cur_rx; 10962306a36Sopenharmony_ci priv->rx_obj_num_coalesce_irq = layout.rx_coalesce; 11062306a36Sopenharmony_ci priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci priv->tx->obj_num = layout.cur_tx; 11362306a36Sopenharmony_ci priv->tx_obj_num_coalesce_irq = layout.tx_coalesce; 11462306a36Sopenharmony_ci priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic const struct ethtool_ops mcp251xfd_ethtool_ops = { 12062306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ | 12162306a36Sopenharmony_ci ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ | 12262306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_USECS_IRQ | 12362306a36Sopenharmony_ci ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ, 12462306a36Sopenharmony_ci .get_ringparam = mcp251xfd_ring_get_ringparam, 12562306a36Sopenharmony_ci .set_ringparam = mcp251xfd_ring_set_ringparam, 12662306a36Sopenharmony_ci .get_coalesce = mcp251xfd_ring_get_coalesce, 12762306a36Sopenharmony_ci .set_coalesce = mcp251xfd_ring_set_coalesce, 12862306a36Sopenharmony_ci .get_ts_info = can_ethtool_op_get_ts_info_hwts, 12962306a36Sopenharmony_ci}; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_civoid mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct can_ram_layout layout; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false); 13862306a36Sopenharmony_ci priv->rx_obj_num = layout.default_rx; 13962306a36Sopenharmony_ci priv->tx->obj_num = layout.default_tx; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci priv->rx_obj_num_coalesce_irq = 0; 14262306a36Sopenharmony_ci priv->tx_obj_num_coalesce_irq = 0; 14362306a36Sopenharmony_ci priv->rx_coalesce_usecs_irq = 0; 14462306a36Sopenharmony_ci priv->tx_coalesce_usecs_irq = 0; 14562306a36Sopenharmony_ci} 146