Commit de29aff9 authored by David S. Miller's avatar David S. Miller
Browse files

Merge tag 'linux-can-next-for-5.18-20220313' of...

Merge tag 'linux-can-next-for-5.18-20220313' of git://git.kernel.org/pub/scm/linux/kernel/git/mkl/linux-can-next



linux-can-next-for-5.18-20220313

Marc Kleine-Budde says:

====================
pull-request: can-next 2022-03-13

this is a pull request of 13 patches for net-next/master.

The 1st patch is by me and fixes the freeing of a skb in the vxcan
driver (initially added in this net-next window).

The remaining 12 patches are also by me and target the mcp251xfd
driver. The first patch fixes a printf modifier (initially added in
this net-next window). The remaining patches add ethtool based ring
and RX/TX IRQ coalescing support to the driver.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 97aeb877 aa66ae9b
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -6,6 +6,8 @@ mcp251xfd-objs :=
mcp251xfd-objs += mcp251xfd-chip-fifo.o
mcp251xfd-objs += mcp251xfd-core.o
mcp251xfd-objs += mcp251xfd-crc16.o
mcp251xfd-objs += mcp251xfd-ethtool.o
mcp251xfd-objs += mcp251xfd-ram.o
mcp251xfd-objs += mcp251xfd-regmap.o
mcp251xfd-objs += mcp251xfd-ring.o
mcp251xfd-objs += mcp251xfd-rx.o
+7 −0
Original line number Diff line number Diff line
@@ -1598,6 +1598,7 @@ static int mcp251xfd_open(struct net_device *ndev)
		goto out_transceiver_disable;

	mcp251xfd_timestamp_init(priv);
	clear_bit(MCP251XFD_FLAGS_DOWN, priv->flags);
	can_rx_offload_enable(&priv->offload);

	err = request_threaded_irq(spi->irq, NULL, mcp251xfd_irq,
@@ -1618,6 +1619,7 @@ static int mcp251xfd_open(struct net_device *ndev)
	free_irq(spi->irq, priv);
 out_can_rx_offload_disable:
	can_rx_offload_disable(&priv->offload);
	set_bit(MCP251XFD_FLAGS_DOWN, priv->flags);
	mcp251xfd_timestamp_stop(priv);
 out_transceiver_disable:
	mcp251xfd_transceiver_disable(priv);
@@ -1637,6 +1639,8 @@ static int mcp251xfd_stop(struct net_device *ndev)
	struct mcp251xfd_priv *priv = netdev_priv(ndev);

	netif_stop_queue(ndev);
	set_bit(MCP251XFD_FLAGS_DOWN, priv->flags);
	hrtimer_cancel(&priv->rx_irq_timer);
	mcp251xfd_chip_interrupts_disable(priv);
	free_irq(ndev->irq, priv);
	can_rx_offload_disable(&priv->offload);
@@ -1871,6 +1875,8 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv)
	if (err)
		goto out_chip_sleep;

	mcp251xfd_ethtool_init(priv);

	err = register_candev(ndev);
	if (err)
		goto out_chip_sleep;
@@ -2034,6 +2040,7 @@ static int mcp251xfd_probe(struct spi_device *spi)
		CAN_CTRLMODE_LISTENONLY | CAN_CTRLMODE_BERR_REPORTING |
		CAN_CTRLMODE_FD | CAN_CTRLMODE_FD_NON_ISO |
		CAN_CTRLMODE_CC_LEN8_DLC;
	set_bit(MCP251XFD_FLAGS_DOWN, priv->flags);
	priv->ndev = ndev;
	priv->spi = spi;
	priv->rx_int = rx_int;
+143 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
//
// mcp251xfd - Microchip MCP251xFD Family CAN controller driver
//
// Copyright (c) 2021, 2022 Pengutronix,
//               Marc Kleine-Budde <kernel@pengutronix.de>
//

#include <linux/ethtool.h>

#include "mcp251xfd.h"
#include "mcp251xfd-ram.h"

static void
mcp251xfd_ring_get_ringparam(struct net_device *ndev,
			     struct ethtool_ringparam *ring,
			     struct kernel_ethtool_ringparam *kernel_ring,
			     struct netlink_ext_ack *extack)
{
	const struct mcp251xfd_priv *priv = netdev_priv(ndev);
	const bool fd_mode = mcp251xfd_is_fd_mode(priv);
	struct can_ram_layout layout;

	can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, fd_mode);
	ring->rx_max_pending = layout.max_rx;
	ring->tx_max_pending = layout.max_tx;

	ring->rx_pending = priv->rx_obj_num;
	ring->tx_pending = priv->tx->obj_num;
}

static int
mcp251xfd_ring_set_ringparam(struct net_device *ndev,
			     struct ethtool_ringparam *ring,
			     struct kernel_ethtool_ringparam *kernel_ring,
			     struct netlink_ext_ack *extack)
{
	struct mcp251xfd_priv *priv = netdev_priv(ndev);
	const bool fd_mode = mcp251xfd_is_fd_mode(priv);
	struct can_ram_layout layout;

	can_ram_get_layout(&layout, &mcp251xfd_ram_config, ring, NULL, fd_mode);
	if ((layout.cur_rx != priv->rx_obj_num ||
	     layout.cur_tx != priv->tx->obj_num) &&
	    netif_running(ndev))
		return -EBUSY;

	priv->rx_obj_num = layout.cur_rx;
	priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
	priv->tx->obj_num = layout.cur_tx;

	return 0;
}

static int mcp251xfd_ring_get_coalesce(struct net_device *ndev,
				       struct ethtool_coalesce *ec,
				       struct kernel_ethtool_coalesce *kec,
				       struct netlink_ext_ack *ext_ack)
{
	struct mcp251xfd_priv *priv = netdev_priv(ndev);
	u32 rx_max_frames, tx_max_frames;

	/* The ethtool doc says:
	 * To disable coalescing, set usecs = 0 and max_frames = 1.
	 */
	if (priv->rx_obj_num_coalesce_irq == 0)
		rx_max_frames = 1;
	else
		rx_max_frames = priv->rx_obj_num_coalesce_irq;

	ec->rx_max_coalesced_frames_irq = rx_max_frames;
	ec->rx_coalesce_usecs_irq = priv->rx_coalesce_usecs_irq;

	if (priv->tx_obj_num_coalesce_irq == 0)
		tx_max_frames = 1;
	else
		tx_max_frames = priv->tx_obj_num_coalesce_irq;

	ec->tx_max_coalesced_frames_irq = tx_max_frames;
	ec->tx_coalesce_usecs_irq = priv->tx_coalesce_usecs_irq;

	return 0;
}

static int mcp251xfd_ring_set_coalesce(struct net_device *ndev,
				       struct ethtool_coalesce *ec,
				       struct kernel_ethtool_coalesce *kec,
				       struct netlink_ext_ack *ext_ack)
{
	struct mcp251xfd_priv *priv = netdev_priv(ndev);
	const bool fd_mode = mcp251xfd_is_fd_mode(priv);
	const struct ethtool_ringparam ring = {
		.rx_pending = priv->rx_obj_num,
		.tx_pending = priv->tx->obj_num,
	};
	struct can_ram_layout layout;

	can_ram_get_layout(&layout, &mcp251xfd_ram_config, &ring, ec, fd_mode);

	if ((layout.rx_coalesce != priv->rx_obj_num_coalesce_irq ||
	     ec->rx_coalesce_usecs_irq != priv->rx_coalesce_usecs_irq ||
	     layout.tx_coalesce != priv->tx_obj_num_coalesce_irq ||
	     ec->tx_coalesce_usecs_irq != priv->tx_coalesce_usecs_irq) &&
	    netif_running(ndev))
		return -EBUSY;

	priv->rx_obj_num = layout.cur_rx;
	priv->rx_obj_num_coalesce_irq = layout.rx_coalesce;
	priv->rx_coalesce_usecs_irq = ec->rx_coalesce_usecs_irq;

	priv->tx->obj_num = layout.cur_tx;
	priv->tx_obj_num_coalesce_irq = layout.tx_coalesce;
	priv->tx_coalesce_usecs_irq = ec->tx_coalesce_usecs_irq;

	return 0;
}

static const struct ethtool_ops mcp251xfd_ethtool_ops = {
	.supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS_IRQ |
		ETHTOOL_COALESCE_RX_MAX_FRAMES_IRQ |
		ETHTOOL_COALESCE_TX_USECS_IRQ |
		ETHTOOL_COALESCE_TX_MAX_FRAMES_IRQ,
	.get_ringparam = mcp251xfd_ring_get_ringparam,
	.set_ringparam = mcp251xfd_ring_set_ringparam,
	.get_coalesce = mcp251xfd_ring_get_coalesce,
	.set_coalesce = mcp251xfd_ring_set_coalesce,
};

void mcp251xfd_ethtool_init(struct mcp251xfd_priv *priv)
{
	struct can_ram_layout layout;

	priv->ndev->ethtool_ops = &mcp251xfd_ethtool_ops;

	can_ram_get_layout(&layout, &mcp251xfd_ram_config, NULL, NULL, false);
	priv->rx_obj_num = layout.default_rx;
	priv->tx->obj_num = layout.default_tx;

	priv->rx_obj_num_coalesce_irq = 0;
	priv->tx_obj_num_coalesce_irq = 0;
	priv->rx_coalesce_usecs_irq = 0;
	priv->tx_coalesce_usecs_irq = 0;
}
+153 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
//
// mcp251xfd - Microchip MCP251xFD Family CAN controller driver
//
// Copyright (c) 2021, 2022 Pengutronix,
//               Marc Kleine-Budde <kernel@pengutronix.de>
//

#include "mcp251xfd-ram.h"

static inline u8 can_ram_clamp(const struct can_ram_config *config,
			       const struct can_ram_obj_config *obj,
			       u8 val)
{
	u8 max;

	max = min_t(u8, obj->max, obj->fifo_num * config->fifo_depth);
	return clamp(val, obj->min, max);
}

static u8
can_ram_rounddown_pow_of_two(const struct can_ram_config *config,
			     const struct can_ram_obj_config *obj,
			     const u8 coalesce, u8 val)
{
	u8 fifo_num = obj->fifo_num;
	u8 ret = 0, i;

	val = can_ram_clamp(config, obj, val);

	if (coalesce) {
		/* Use 1st FIFO for coalescing, if requested.
		 *
		 * Either use complete FIFO (and FIFO Full IRQ) for
		 * coalescing or only half of FIFO (FIFO Half Full
		 * IRQ) and use remaining half for normal objects.
		 */
		ret = min_t(u8, coalesce * 2, config->fifo_depth);
		val -= ret;
		fifo_num--;
	}

	for (i = 0; i < fifo_num && val; i++) {
		u8 n;

		n = min_t(u8, rounddown_pow_of_two(val),
			  config->fifo_depth);

		/* skip small FIFOs */
		if (n < obj->fifo_depth_min)
			return ret;

		ret += n;
		val -= n;
	}

	return ret;
}

void can_ram_get_layout(struct can_ram_layout *layout,
			const struct can_ram_config *config,
			const struct ethtool_ringparam *ring,
			const struct ethtool_coalesce *ec,
			const bool fd_mode)
{
	u8 num_rx, num_tx;
	u16 ram_free;

	/* default CAN */

	num_tx = config->tx.def[fd_mode];
	num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);

	ram_free = config->size;
	ram_free -= config->tx.size[fd_mode] * num_tx;

	num_rx = ram_free / config->rx.size[fd_mode];

	layout->default_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
	layout->default_tx = num_tx;

	/* MAX CAN */

	ram_free = config->size;
	ram_free -= config->tx.size[fd_mode] * config->tx.min;
	num_rx = ram_free / config->rx.size[fd_mode];

	ram_free = config->size;
	ram_free -= config->rx.size[fd_mode] * config->rx.min;
	num_tx = ram_free / config->tx.size[fd_mode];

	layout->max_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, num_rx);
	layout->max_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);

	/* cur CAN */

	if (ring) {
		u8 num_rx_coalesce = 0, num_tx_coalesce = 0;

		num_rx = can_ram_rounddown_pow_of_two(config, &config->rx, 0, ring->rx_pending);

		/* The ethtool doc says:
		 * To disable coalescing, set usecs = 0 and max_frames = 1.
		 */
		if (ec && !(ec->rx_coalesce_usecs_irq == 0 &&
			    ec->rx_max_coalesced_frames_irq == 1)) {
			u8 max;

			/* use only max half of available objects for coalescing */
			max = min_t(u8, num_rx / 2, config->fifo_depth);
			num_rx_coalesce = clamp(ec->rx_max_coalesced_frames_irq,
						(u32)config->rx.fifo_depth_coalesce_min,
						(u32)max);
			num_rx_coalesce = rounddown_pow_of_two(num_rx_coalesce);

			num_rx = can_ram_rounddown_pow_of_two(config, &config->rx,
							      num_rx_coalesce, num_rx);
		}

		ram_free = config->size - config->rx.size[fd_mode] * num_rx;
		num_tx = ram_free / config->tx.size[fd_mode];
		num_tx = min_t(u8, ring->tx_pending, num_tx);
		num_tx = can_ram_rounddown_pow_of_two(config, &config->tx, 0, num_tx);

		/* The ethtool doc says:
		 * To disable coalescing, set usecs = 0 and max_frames = 1.
		 */
		if (ec && !(ec->tx_coalesce_usecs_irq == 0 &&
			    ec->tx_max_coalesced_frames_irq == 1)) {
			u8 max;

			/* use only max half of available objects for coalescing */
			max = min_t(u8, num_tx / 2, config->fifo_depth);
			num_tx_coalesce = clamp(ec->tx_max_coalesced_frames_irq,
						(u32)config->tx.fifo_depth_coalesce_min,
						(u32)max);
			num_tx_coalesce = rounddown_pow_of_two(num_tx_coalesce);

			num_tx = can_ram_rounddown_pow_of_two(config, &config->tx,
							      num_tx_coalesce, num_tx);
		}

		layout->cur_rx = num_rx;
		layout->cur_tx = num_tx;
		layout->rx_coalesce = num_rx_coalesce;
		layout->tx_coalesce = num_tx_coalesce;
	} else {
		layout->cur_rx = layout->default_rx;
		layout->cur_tx = layout->default_tx;
		layout->rx_coalesce = 0;
		layout->tx_coalesce = 0;
	}
}
+62 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0
 *
 * mcp251xfd - Microchip MCP251xFD Family CAN controller driver
 *
 * Copyright (c) 2021, 2022 Pengutronix,
 *               Marc Kleine-Budde <kernel@pengutronix.de>
 */

#ifndef _MCP251XFD_RAM_H
#define _MCP251XFD_RAM_H

#include <linux/ethtool.h>

#define CAN_RAM_NUM_MAX (-1)

enum can_ram_mode {
	CAN_RAM_MODE_CAN,
	CAN_RAM_MODE_CANFD,
	__CAN_RAM_MODE_MAX
};

struct can_ram_obj_config {
	u8 size[__CAN_RAM_MODE_MAX];

	u8 def[__CAN_RAM_MODE_MAX];
	u8 min;
	u8 max;

	u8 fifo_num;
	u8 fifo_depth_min;
	u8 fifo_depth_coalesce_min;
};

struct can_ram_config {
	const struct can_ram_obj_config rx;
	const struct can_ram_obj_config tx;

	u16 size;
	u8 fifo_depth;
};

struct can_ram_layout {
	u8 default_rx;
	u8 default_tx;

	u8 max_rx;
	u8 max_tx;

	u8 cur_rx;
	u8 cur_tx;

	u8 rx_coalesce;
	u8 tx_coalesce;
};

void can_ram_get_layout(struct can_ram_layout *layout,
			const struct can_ram_config *config,
			const struct ethtool_ringparam *ring,
			const struct ethtool_coalesce *ec,
			const bool fd_mode);

#endif
Loading