162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
262306a36Sopenharmony_ci/* Copyright (c) 2022 Amarula Solutions, Dario Binacchi <dario.binacchi@amarulasolutions.com>
362306a36Sopenharmony_ci * Copyright (c) 2022 Pengutronix, Marc Kleine-Budde <kernel@pengutronix.de>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/can/dev.h>
862306a36Sopenharmony_ci#include <linux/ethtool.h>
962306a36Sopenharmony_ci#include <linux/kernel.h>
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/platform_device.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include "flexcan.h"
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic const char flexcan_priv_flags_strings[][ETH_GSTRING_LEN] = {
1662306a36Sopenharmony_ci#define FLEXCAN_PRIV_FLAGS_RX_RTR BIT(0)
1762306a36Sopenharmony_ci	"rx-rtr",
1862306a36Sopenharmony_ci};
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void
2162306a36Sopenharmony_ciflexcan_get_ringparam(struct net_device *ndev, struct ethtool_ringparam *ring,
2262306a36Sopenharmony_ci		      struct kernel_ethtool_ringparam *kernel_ring,
2362306a36Sopenharmony_ci		      struct netlink_ext_ack *ext_ack)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	const struct flexcan_priv *priv = netdev_priv(ndev);
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	ring->rx_max_pending = priv->mb_count;
2862306a36Sopenharmony_ci	ring->tx_max_pending = priv->mb_count;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	if (priv->devtype_data.quirks & FLEXCAN_QUIRK_USE_RX_MAILBOX)
3162306a36Sopenharmony_ci		ring->rx_pending = priv->offload.mb_last -
3262306a36Sopenharmony_ci			priv->offload.mb_first + 1;
3362306a36Sopenharmony_ci	else
3462306a36Sopenharmony_ci		ring->rx_pending = 6;	/* RX-FIFO depth is fixed */
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* the drive currently supports only on TX buffer */
3762306a36Sopenharmony_ci	ring->tx_pending = 1;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void
4162306a36Sopenharmony_ciflexcan_get_strings(struct net_device *ndev, u32 stringset, u8 *data)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	switch (stringset) {
4462306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
4562306a36Sopenharmony_ci		memcpy(data, flexcan_priv_flags_strings,
4662306a36Sopenharmony_ci		       sizeof(flexcan_priv_flags_strings));
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic u32 flexcan_get_priv_flags(struct net_device *ndev)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	const struct flexcan_priv *priv = netdev_priv(ndev);
5362306a36Sopenharmony_ci	u32 priv_flags = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	if (flexcan_active_rx_rtr(priv))
5662306a36Sopenharmony_ci		priv_flags |= FLEXCAN_PRIV_FLAGS_RX_RTR;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return priv_flags;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int flexcan_set_priv_flags(struct net_device *ndev, u32 priv_flags)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	struct flexcan_priv *priv = netdev_priv(ndev);
6462306a36Sopenharmony_ci	u32 quirks = priv->devtype_data.quirks;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	if (priv_flags & FLEXCAN_PRIV_FLAGS_RX_RTR) {
6762306a36Sopenharmony_ci		if (flexcan_supports_rx_mailbox_rtr(priv))
6862306a36Sopenharmony_ci			quirks |= FLEXCAN_QUIRK_USE_RX_MAILBOX;
6962306a36Sopenharmony_ci		else if (flexcan_supports_rx_fifo(priv))
7062306a36Sopenharmony_ci			quirks &= ~FLEXCAN_QUIRK_USE_RX_MAILBOX;
7162306a36Sopenharmony_ci		else
7262306a36Sopenharmony_ci			quirks |= FLEXCAN_QUIRK_USE_RX_MAILBOX;
7362306a36Sopenharmony_ci	} else {
7462306a36Sopenharmony_ci		if (flexcan_supports_rx_mailbox(priv))
7562306a36Sopenharmony_ci			quirks |= FLEXCAN_QUIRK_USE_RX_MAILBOX;
7662306a36Sopenharmony_ci		else
7762306a36Sopenharmony_ci			quirks &= ~FLEXCAN_QUIRK_USE_RX_MAILBOX;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if (quirks != priv->devtype_data.quirks && netif_running(ndev))
8162306a36Sopenharmony_ci		return -EBUSY;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	priv->devtype_data.quirks = quirks;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	if (!(priv_flags & FLEXCAN_PRIV_FLAGS_RX_RTR) &&
8662306a36Sopenharmony_ci	    !flexcan_active_rx_rtr(priv))
8762306a36Sopenharmony_ci		netdev_info(ndev,
8862306a36Sopenharmony_ci			    "Activating RX mailbox mode, cannot receive RTR frames.\n");
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	return 0;
9162306a36Sopenharmony_ci}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_cistatic int flexcan_get_sset_count(struct net_device *netdev, int sset)
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	switch (sset) {
9662306a36Sopenharmony_ci	case ETH_SS_PRIV_FLAGS:
9762306a36Sopenharmony_ci		return ARRAY_SIZE(flexcan_priv_flags_strings);
9862306a36Sopenharmony_ci	default:
9962306a36Sopenharmony_ci		return -EOPNOTSUPP;
10062306a36Sopenharmony_ci	}
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciconst struct ethtool_ops flexcan_ethtool_ops = {
10462306a36Sopenharmony_ci	.get_ringparam = flexcan_get_ringparam,
10562306a36Sopenharmony_ci	.get_strings = flexcan_get_strings,
10662306a36Sopenharmony_ci	.get_priv_flags = flexcan_get_priv_flags,
10762306a36Sopenharmony_ci	.set_priv_flags = flexcan_set_priv_flags,
10862306a36Sopenharmony_ci	.get_sset_count = flexcan_get_sset_count,
10962306a36Sopenharmony_ci	.get_ts_info = ethtool_op_get_ts_info,
11062306a36Sopenharmony_ci};
111