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