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

Merge branch 'am65-cpsw-rx-mq'



Roger Quadros says:

====================
net: ethernet: ti: am65-cpsw: Add multi queue RX support

am65-cpsw can support up to 8 queues at Rx. So far we have
been using only one queue (i.e. default flow) for all RX traffic.

This series adds multi-queue support. The driver starts with
1 RX queue by default. User can increase the RX queues via ethtool,
e.g. 'ethtool -L ethx rx <N>'

The series also adds regmap and regfield support to some of the
ALE registers. It adds Policer/Classifier registers and fields.

Converting the existing ALE control APIs to regfields can be a separate
exercise.

Some helper functions are added to read/write to the Policer/Classifier
registers and a default Classifier setup function is added that
routes packets based on their PCP/DSCP priority to different RX queues.

Signed-off-by: default avatarRoger Quadros <rogerq@kernel.org>
---
Changes in v4:
- Use single macro AM65_CPSW_MAX_QUEUES for both TX and RX queues
  to simplify code
- reuse am65_cpsw_get/set_per_queue_coalesce for am65_cpsw_get/set_coalesce.
- return -EINVAL if unsupported tx/rx_coalesce_usecs in
  am65_cpsw_set_coalesce.
- reverse Xmas tree declaration order fixes in cpsw_ale
- Link to v3: https://lore.kernel.org/r/20240703-am65-cpsw-multi-rx-v3-0-f11cd860fd72@kernel.org

Changes in v3:
- code style fixes
- squashed patches 5 and 6
- added comment about priority to thread mapping table.
- Added Reviewed-by Simon Horman.
- Link to v2: https://lore.kernel.org/r/20240628-am65-cpsw-multi-rx-v2-0-c399cb77db56@kernel.org

Changes in v2:
- rebase to net/next
- fixed RX stall issue during iperf
- Link to v1: https://lore.kernel.org/r/20240606-am65-cpsw-multi-rx-v1-0-0704b0cb6fdc@kernel.org


====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 52fa3b65 b7468c0f
Loading
Loading
Loading
Loading
+29 −46
Original line number Diff line number Diff line
@@ -427,9 +427,9 @@ static void am65_cpsw_get_channels(struct net_device *ndev,
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);

	ch->max_rx = AM65_CPSW_MAX_RX_QUEUES;
	ch->max_tx = AM65_CPSW_MAX_TX_QUEUES;
	ch->rx_count = AM65_CPSW_MAX_RX_QUEUES;
	ch->max_rx = AM65_CPSW_MAX_QUEUES;
	ch->max_tx = AM65_CPSW_MAX_QUEUES;
	ch->rx_count = common->rx_ch_num_flows;
	ch->tx_count = common->tx_ch_num;
}

@@ -447,9 +447,8 @@ static int am65_cpsw_set_channels(struct net_device *ndev,
	if (common->usage_count)
		return -EBUSY;

	am65_cpsw_nuss_remove_tx_chns(common);

	return am65_cpsw_nuss_update_tx_chns(common, chs->tx_count);
	return am65_cpsw_nuss_update_tx_rx_chns(common, chs->tx_count,
						chs->rx_count);
}

static void
@@ -913,80 +912,64 @@ static void am65_cpsw_get_mm_stats(struct net_device *ndev,
	s->MACMergeHoldCount = readl(base + AM65_CPSW_STATN_IET_TX_HOLD);
}

static int am65_cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal,
				  struct kernel_ethtool_coalesce *kernel_coal,
				  struct netlink_ext_ack *extack)
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
	struct am65_cpsw_tx_chn *tx_chn;

	tx_chn = &common->tx_chns[0];

	coal->rx_coalesce_usecs = common->rx_pace_timeout / 1000;
	coal->tx_coalesce_usecs = tx_chn->tx_pace_timeout / 1000;

	return 0;
}

static int am65_cpsw_get_per_queue_coalesce(struct net_device *ndev, u32 queue,
					    struct ethtool_coalesce *coal)
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
	struct am65_cpsw_rx_flow *rx_flow;
	struct am65_cpsw_tx_chn *tx_chn;

	if (queue >= AM65_CPSW_MAX_TX_QUEUES)
	if (queue >= AM65_CPSW_MAX_QUEUES)
		return -EINVAL;

	tx_chn = &common->tx_chns[queue];

	coal->tx_coalesce_usecs = tx_chn->tx_pace_timeout / 1000;

	rx_flow = &common->rx_chns.flows[queue];
	coal->rx_coalesce_usecs = rx_flow->rx_pace_timeout / 1000;

	return 0;
}

static int am65_cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal,
static int am65_cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal,
				  struct kernel_ethtool_coalesce *kernel_coal,
				  struct netlink_ext_ack *extack)
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
	struct am65_cpsw_tx_chn *tx_chn;

	tx_chn = &common->tx_chns[0];

	if (coal->rx_coalesce_usecs && coal->rx_coalesce_usecs < 20)
		return -EINVAL;

	if (coal->tx_coalesce_usecs && coal->tx_coalesce_usecs < 20)
		return -EINVAL;

	common->rx_pace_timeout = coal->rx_coalesce_usecs * 1000;
	tx_chn->tx_pace_timeout = coal->tx_coalesce_usecs * 1000;

	return 0;
	return am65_cpsw_get_per_queue_coalesce(ndev, 0, coal);
}

static int am65_cpsw_set_per_queue_coalesce(struct net_device *ndev, u32 queue,
					    struct ethtool_coalesce *coal)
{
	struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
	struct am65_cpsw_rx_flow *rx_flow;
	struct am65_cpsw_tx_chn *tx_chn;

	if (queue >= AM65_CPSW_MAX_TX_QUEUES)
	if (queue >= AM65_CPSW_MAX_QUEUES)
		return -EINVAL;

	tx_chn = &common->tx_chns[queue];

	if (coal->tx_coalesce_usecs && coal->tx_coalesce_usecs < 20) {
		dev_info(common->dev, "defaulting to min value of 20us for tx-usecs for tx-%u\n",
			 queue);
		coal->tx_coalesce_usecs = 20;
	}
	if (coal->tx_coalesce_usecs && coal->tx_coalesce_usecs < 20)
		return -EINVAL;

	tx_chn->tx_pace_timeout = coal->tx_coalesce_usecs * 1000;

	rx_flow = &common->rx_chns.flows[queue];
	if (coal->rx_coalesce_usecs && coal->rx_coalesce_usecs < 20)
		return -EINVAL;

	rx_flow->rx_pace_timeout = coal->rx_coalesce_usecs * 1000;

	return 0;
}

static int am65_cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal,
				  struct kernel_ethtool_coalesce *kernel_coal,
				  struct netlink_ext_ack *extack)
{
	return am65_cpsw_set_per_queue_coalesce(ndev, 0, coal);
}

const struct ethtool_ops am65_cpsw_ethtool_ops_slave = {
	.begin			= am65_cpsw_ethtool_op_begin,
	.complete		= am65_cpsw_ethtool_op_complete,
+221 −167

File changed.

Preview size limit exceeded, changes collapsed.

+22 −17

File changed.

Preview size limit exceeded, changes collapsed.

+264 −23

File changed.

Preview size limit exceeded, changes collapsed.

+58 −4
Original line number Diff line number Diff line
@@ -8,11 +8,14 @@
#ifndef __TI_CPSW_ALE_H__
#define __TI_CPSW_ALE_H__

struct reg_fields;

struct cpsw_ale_params {
	struct device		*dev;
	void __iomem		*ale_regs;
	unsigned long		ale_ageout;	/* in secs */
	unsigned long		ale_entries;
	unsigned long		num_policers;
	unsigned long		ale_ports;
	/* NU Switch has specific handling as number of bits in ALE entries
	 * are different than other versions of ALE. Also there are specific
@@ -20,19 +23,69 @@ struct cpsw_ale_params {
	 * to identify this hardware.
	 */
	bool			nu_switch_ale;
	/* mask bit used in NU Switch ALE is 3 bits instead of 8 bits. So
	 * pass it from caller.
	 */
	u32			major_ver_mask;
	const struct reg_field *reg_fields;
	const char		*dev_id;
	unsigned long		bus_freq;
};

struct ale_entry_fld;
struct regmap;

enum ale_fields {
	MINOR_VER,
	MAJOR_VER,
	ALE_ENTRIES,
	ALE_POLICERS,
	POL_PORT_MEN,
	POL_TRUNK_ID,
	POL_PORT_NUM,
	POL_PRI_MEN,
	POL_PRI_VAL,
	POL_OUI_MEN,
	POL_OUI_INDEX,
	POL_DST_MEN,
	POL_DST_INDEX,
	POL_SRC_MEN,
	POL_SRC_INDEX,
	POL_OVLAN_MEN,
	POL_OVLAN_INDEX,
	POL_IVLAN_MEN,
	POL_IVLAN_INDEX,
	POL_ETHERTYPE_MEN,
	POL_ETHERTYPE_INDEX,
	POL_IPSRC_MEN,
	POL_IPSRC_INDEX,
	POL_IPDST_MEN,
	POL_IPDST_INDEX,
	POL_EN,
	POL_RED_DROP_EN,
	POL_YELLOW_DROP_EN,
	POL_YELLOW_THRESH,
	POL_POL_MATCH_MODE,
	POL_PRIORITY_THREAD_EN,
	POL_MAC_ONLY_DEF_DIS,
	POL_TEST_CLR,
	POL_TEST_CLR_RED,
	POL_TEST_CLR_YELLOW,
	POL_TEST_CLR_SELECTED,
	POL_TEST_ENTRY,
	POL_STATUS_HIT,
	POL_STATUS_HIT_RED,
	POL_STATUS_HIT_YELLOW,
	ALE_DEFAULT_THREAD_EN,
	ALE_DEFAULT_THREAD_VAL,
	ALE_THREAD_CLASS_INDEX,
	ALE_THREAD_ENABLE,
	ALE_THREAD_VALUE,
	/* terminator */
	ALE_FIELDS_MAX,
};

struct cpsw_ale {
	struct cpsw_ale_params	params;
	struct timer_list	timer;
	struct regmap		*regmap;
	struct regmap_field	*fields[ALE_FIELDS_MAX];
	unsigned long		ageout;
	u32			version;
	u32			features;
@@ -140,5 +193,6 @@ int cpsw_ale_vlan_add_modify(struct cpsw_ale *ale, u16 vid, int port_mask,
int cpsw_ale_vlan_del_modify(struct cpsw_ale *ale, u16 vid, int port_mask);
void cpsw_ale_set_unreg_mcast(struct cpsw_ale *ale, int unreg_mcast_mask,
			      bool add);
void cpsw_ale_classifier_setup_default(struct cpsw_ale *ale, int num_rx_ch);

#endif