mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-30 13:34:27 -04:00 
			
		
		
		
	All modifications made by update_kernel.sh run in a fresh clone without any existing toolchains. Build system: x86_64 Build-tested: ipq806x/R7800, ath79/generic, bcm27xx/bcm2711 Run-tested: ipq806x/R7800 No dmesg regressions, everything functional Signed-off-by: John Audia <graysky@archlinux.us>
		
			
				
	
	
		
			379 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			379 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| From 6ee2331a3a5627b062daf76aa5ed9f64fbbfa303 Mon Sep 17 00:00:00 2001
 | |
| From: Po Liu <po.liu@nxp.com>
 | |
| Date: Fri, 15 Nov 2019 03:33:33 +0000
 | |
| Subject: [PATCH] enetc: Configure the Time-Aware Scheduler via tc-taprio
 | |
|  offload
 | |
| 
 | |
| ENETC supports in hardware for time-based egress shaping according
 | |
| to IEEE 802.1Qbv. This patch implement the Qbv enablement by the
 | |
| hardware offload method qdisc tc-taprio method.
 | |
| Also update cbdr writeback to up level since control bd ring may
 | |
| writeback data to control bd ring.
 | |
| 
 | |
| Signed-off-by: Po Liu <Po.Liu@nxp.com>
 | |
| Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
 | |
| Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
 | |
| Signed-off-by: David S. Miller <davem@davemloft.net>
 | |
| ---
 | |
|  drivers/net/ethernet/freescale/enetc/Kconfig      |  10 ++
 | |
|  drivers/net/ethernet/freescale/enetc/Makefile     |   2 +
 | |
|  drivers/net/ethernet/freescale/enetc/enetc.c      |  19 ++-
 | |
|  drivers/net/ethernet/freescale/enetc/enetc.h      |   7 ++
 | |
|  drivers/net/ethernet/freescale/enetc/enetc_cbdr.c |   5 +-
 | |
|  drivers/net/ethernet/freescale/enetc/enetc_hw.h   |  84 ++++++++++---
 | |
|  drivers/net/ethernet/freescale/enetc/enetc_qos.c  | 138 ++++++++++++++++++++++
 | |
|  7 files changed, 243 insertions(+), 22 deletions(-)
 | |
|  create mode 100644 drivers/net/ethernet/freescale/enetc/enetc_qos.c
 | |
| 
 | |
| --- a/drivers/net/ethernet/freescale/enetc/Kconfig
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/Kconfig
 | |
| @@ -50,3 +50,13 @@ config FSL_ENETC_HW_TIMESTAMPING
 | |
|  	  allocation has not been supported and it is too expensive to use
 | |
|  	  extended RX BDs if timestamping is not used, this option enables
 | |
|  	  extended RX BDs in order to support hardware timestamping.
 | |
| +
 | |
| +config FSL_ENETC_QOS
 | |
| +	bool "ENETC hardware Time-sensitive Network support"
 | |
| +	depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO
 | |
| +	help
 | |
| +	  There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci
 | |
| +	  /802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set
 | |
| +	  enable/disable from user space via Qos commands(tc). In the kernel
 | |
| +	  side, it can be loaded by Qos driver. Currently, it is only support
 | |
| +	  taprio(802.1Qbv).
 | |
| --- a/drivers/net/ethernet/freescale/enetc/Makefile
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/Makefile
 | |
| @@ -5,9 +5,11 @@ common-objs := enetc.o enetc_cbdr.o enet
 | |
|  obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
 | |
|  fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs)
 | |
|  fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
 | |
| +fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
 | |
|  
 | |
|  obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o
 | |
|  fsl-enetc-vf-y := enetc_vf.o $(common-objs)
 | |
| +fsl-enetc-vf-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o
 | |
|  
 | |
|  obj-$(CONFIG_FSL_ENETC_MDIO) += fsl-enetc-mdio.o
 | |
|  fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o
 | |
| --- a/drivers/net/ethernet/freescale/enetc/enetc.c
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/enetc.c
 | |
| @@ -1427,8 +1427,7 @@ int enetc_close(struct net_device *ndev)
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| -int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
 | |
| -		   void *type_data)
 | |
| +int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data)
 | |
|  {
 | |
|  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 | |
|  	struct tc_mqprio_qopt *mqprio = type_data;
 | |
| @@ -1436,9 +1435,6 @@ int enetc_setup_tc(struct net_device *nd
 | |
|  	u8 num_tc;
 | |
|  	int i;
 | |
|  
 | |
| -	if (type != TC_SETUP_QDISC_MQPRIO)
 | |
| -		return -EOPNOTSUPP;
 | |
| -
 | |
|  	mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
 | |
|  	num_tc = mqprio->num_tc;
 | |
|  
 | |
| @@ -1483,6 +1479,19 @@ int enetc_setup_tc(struct net_device *nd
 | |
|  	return 0;
 | |
|  }
 | |
|  
 | |
| +int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
 | |
| +		   void *type_data)
 | |
| +{
 | |
| +	switch (type) {
 | |
| +	case TC_SETUP_QDISC_MQPRIO:
 | |
| +		return enetc_setup_tc_mqprio(ndev, type_data);
 | |
| +	case TC_SETUP_QDISC_TAPRIO:
 | |
| +		return enetc_setup_tc_taprio(ndev, type_data);
 | |
| +	default:
 | |
| +		return -EOPNOTSUPP;
 | |
| +	}
 | |
| +}
 | |
| +
 | |
|  struct net_device_stats *enetc_get_stats(struct net_device *ndev)
 | |
|  {
 | |
|  	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 | |
| --- a/drivers/net/ethernet/freescale/enetc/enetc.h
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/enetc.h
 | |
| @@ -244,3 +244,10 @@ int enetc_set_fs_entry(struct enetc_si *
 | |
|  void enetc_set_rss_key(struct enetc_hw *hw, const u8 *bytes);
 | |
|  int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count);
 | |
|  int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count);
 | |
| +int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd);
 | |
| +
 | |
| +#ifdef CONFIG_FSL_ENETC_QOS
 | |
| +int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data);
 | |
| +#else
 | |
| +#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP
 | |
| +#endif
 | |
| --- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c
 | |
| @@ -32,7 +32,7 @@ static int enetc_cbd_unused(struct enetc
 | |
|  		r->bd_count;
 | |
|  }
 | |
|  
 | |
| -static int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
 | |
| +int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
 | |
|  {
 | |
|  	struct enetc_cbdr *ring = &si->cbd_ring;
 | |
|  	int timeout = ENETC_CBDR_TIMEOUT;
 | |
| @@ -66,6 +66,9 @@ static int enetc_send_cmd(struct enetc_s
 | |
|  	if (!timeout)
 | |
|  		return -EBUSY;
 | |
|  
 | |
| +	/* CBD may writeback data, feedback up level */
 | |
| +	*cbd = *dest_cbd;
 | |
| +
 | |
|  	enetc_clean_cbdr(si);
 | |
|  
 | |
|  	return 0;
 | |
| --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
 | |
| @@ -18,6 +18,7 @@
 | |
|  #define ENETC_SICTR0	0x18
 | |
|  #define ENETC_SICTR1	0x1c
 | |
|  #define ENETC_SIPCAPR0	0x20
 | |
| +#define ENETC_SIPCAPR0_QBV	BIT(4)
 | |
|  #define ENETC_SIPCAPR0_RSS	BIT(8)
 | |
|  #define ENETC_SIPCAPR1	0x24
 | |
|  #define ENETC_SITGTGR	0x30
 | |
| @@ -444,22 +445,6 @@ union enetc_rx_bd {
 | |
|  #define EMETC_MAC_ADDR_FILT_RES	3 /* # of reserved entries at the beginning */
 | |
|  #define ENETC_MAX_NUM_VFS	2
 | |
|  
 | |
| -struct enetc_cbd {
 | |
| -	union {
 | |
| -		struct {
 | |
| -			__le32 addr[2];
 | |
| -			__le32 opt[4];
 | |
| -		};
 | |
| -		__le32 data[6];
 | |
| -	};
 | |
| -	__le16 index;
 | |
| -	__le16 length;
 | |
| -	u8 cmd;
 | |
| -	u8 cls;
 | |
| -	u8 _res;
 | |
| -	u8 status_flags;
 | |
| -};
 | |
| -
 | |
|  #define ENETC_CBD_FLAGS_SF	BIT(7) /* short format */
 | |
|  #define ENETC_CBD_STATUS_MASK	0xf
 | |
|  
 | |
| @@ -558,3 +543,70 @@ static inline void enetc_set_bdr_prio(st
 | |
|  	val |= ENETC_TBMR_SET_PRIO(prio);
 | |
|  	enetc_txbdr_wr(hw, bdr_idx, ENETC_TBMR, val);
 | |
|  }
 | |
| +
 | |
| +enum bdcr_cmd_class {
 | |
| +	BDCR_CMD_UNSPEC = 0,
 | |
| +	BDCR_CMD_MAC_FILTER,
 | |
| +	BDCR_CMD_VLAN_FILTER,
 | |
| +	BDCR_CMD_RSS,
 | |
| +	BDCR_CMD_RFS,
 | |
| +	BDCR_CMD_PORT_GCL,
 | |
| +	BDCR_CMD_RECV_CLASSIFIER,
 | |
| +	__BDCR_CMD_MAX_LEN,
 | |
| +	BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1,
 | |
| +};
 | |
| +
 | |
| +/* class 5, command 0 */
 | |
| +struct tgs_gcl_conf {
 | |
| +	u8	atc;	/* init gate value */
 | |
| +	u8	res[7];
 | |
| +	struct {
 | |
| +		u8	res1[4];
 | |
| +		__le16	acl_len;
 | |
| +		u8	res2[2];
 | |
| +	};
 | |
| +};
 | |
| +
 | |
| +/* gate control list entry */
 | |
| +struct gce {
 | |
| +	__le32	period;
 | |
| +	u8	gate;
 | |
| +	u8	res[3];
 | |
| +};
 | |
| +
 | |
| +/* tgs_gcl_conf address point to this data space */
 | |
| +struct tgs_gcl_data {
 | |
| +	__le32		btl;
 | |
| +	__le32		bth;
 | |
| +	__le32		ct;
 | |
| +	__le32		cte;
 | |
| +	struct gce	entry[0];
 | |
| +};
 | |
| +
 | |
| +struct enetc_cbd {
 | |
| +	union{
 | |
| +		struct {
 | |
| +			__le32	addr[2];
 | |
| +			union {
 | |
| +				__le32	opt[4];
 | |
| +				struct tgs_gcl_conf	gcl_conf;
 | |
| +			};
 | |
| +		};	/* Long format */
 | |
| +		__le32 data[6];
 | |
| +	};
 | |
| +	__le16 index;
 | |
| +	__le16 length;
 | |
| +	u8 cmd;
 | |
| +	u8 cls;
 | |
| +	u8 _res;
 | |
| +	u8 status_flags;
 | |
| +};
 | |
| +
 | |
| +/* port time gating control register */
 | |
| +#define ENETC_QBV_PTGCR_OFFSET		0x11a00
 | |
| +#define ENETC_QBV_TGE			BIT(31)
 | |
| +#define ENETC_QBV_TGPE			BIT(30)
 | |
| +
 | |
| +/* Port time gating capability register */
 | |
| +#define ENETC_QBV_PTGCAPR_OFFSET	0x11a08
 | |
| +#define ENETC_QBV_MAX_GCL_LEN_MASK	GENMASK(15, 0)
 | |
| --- /dev/null
 | |
| +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
 | |
| @@ -0,0 +1,138 @@
 | |
| +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 | |
| +/* Copyright 2019 NXP */
 | |
| +
 | |
| +#include "enetc.h"
 | |
| +
 | |
| +#include <net/pkt_sched.h>
 | |
| +
 | |
| +static u16 enetc_get_max_gcl_len(struct enetc_hw *hw)
 | |
| +{
 | |
| +	return enetc_rd(hw, ENETC_QBV_PTGCAPR_OFFSET)
 | |
| +		& ENETC_QBV_MAX_GCL_LEN_MASK;
 | |
| +}
 | |
| +
 | |
| +static int enetc_setup_taprio(struct net_device *ndev,
 | |
| +			      struct tc_taprio_qopt_offload *admin_conf)
 | |
| +{
 | |
| +	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 | |
| +	struct enetc_cbd cbd = {.cmd = 0};
 | |
| +	struct tgs_gcl_conf *gcl_config;
 | |
| +	struct tgs_gcl_data *gcl_data;
 | |
| +	struct gce *gce;
 | |
| +	dma_addr_t dma;
 | |
| +	u16 data_size;
 | |
| +	u16 gcl_len;
 | |
| +	u32 tge;
 | |
| +	int err;
 | |
| +	int i;
 | |
| +
 | |
| +	if (admin_conf->num_entries > enetc_get_max_gcl_len(&priv->si->hw))
 | |
| +		return -EINVAL;
 | |
| +	gcl_len = admin_conf->num_entries;
 | |
| +
 | |
| +	tge = enetc_rd(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET);
 | |
| +	if (!admin_conf->enable) {
 | |
| +		enetc_wr(&priv->si->hw,
 | |
| +			 ENETC_QBV_PTGCR_OFFSET,
 | |
| +			 tge & (~ENETC_QBV_TGE));
 | |
| +		return 0;
 | |
| +	}
 | |
| +
 | |
| +	if (admin_conf->cycle_time > U32_MAX ||
 | |
| +	    admin_conf->cycle_time_extension > U32_MAX)
 | |
| +		return -EINVAL;
 | |
| +
 | |
| +	/* Configure the (administrative) gate control list using the
 | |
| +	 * control BD descriptor.
 | |
| +	 */
 | |
| +	gcl_config = &cbd.gcl_conf;
 | |
| +
 | |
| +	data_size = struct_size(gcl_data, entry, gcl_len);
 | |
| +	gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL);
 | |
| +	if (!gcl_data)
 | |
| +		return -ENOMEM;
 | |
| +
 | |
| +	gce = (struct gce *)(gcl_data + 1);
 | |
| +
 | |
| +	/* Set all gates open as default */
 | |
| +	gcl_config->atc = 0xff;
 | |
| +	gcl_config->acl_len = cpu_to_le16(gcl_len);
 | |
| +
 | |
| +	if (!admin_conf->base_time) {
 | |
| +		gcl_data->btl =
 | |
| +			cpu_to_le32(enetc_rd(&priv->si->hw, ENETC_SICTR0));
 | |
| +		gcl_data->bth =
 | |
| +			cpu_to_le32(enetc_rd(&priv->si->hw, ENETC_SICTR1));
 | |
| +	} else {
 | |
| +		gcl_data->btl =
 | |
| +			cpu_to_le32(lower_32_bits(admin_conf->base_time));
 | |
| +		gcl_data->bth =
 | |
| +			cpu_to_le32(upper_32_bits(admin_conf->base_time));
 | |
| +	}
 | |
| +
 | |
| +	gcl_data->ct = cpu_to_le32(admin_conf->cycle_time);
 | |
| +	gcl_data->cte = cpu_to_le32(admin_conf->cycle_time_extension);
 | |
| +
 | |
| +	for (i = 0; i < gcl_len; i++) {
 | |
| +		struct tc_taprio_sched_entry *temp_entry;
 | |
| +		struct gce *temp_gce = gce + i;
 | |
| +
 | |
| +		temp_entry = &admin_conf->entries[i];
 | |
| +
 | |
| +		temp_gce->gate = (u8)temp_entry->gate_mask;
 | |
| +		temp_gce->period = cpu_to_le32(temp_entry->interval);
 | |
| +	}
 | |
| +
 | |
| +	cbd.length = cpu_to_le16(data_size);
 | |
| +	cbd.status_flags = 0;
 | |
| +
 | |
| +	dma = dma_map_single(&priv->si->pdev->dev, gcl_data,
 | |
| +			     data_size, DMA_TO_DEVICE);
 | |
| +	if (dma_mapping_error(&priv->si->pdev->dev, dma)) {
 | |
| +		netdev_err(priv->si->ndev, "DMA mapping failed!\n");
 | |
| +		kfree(gcl_data);
 | |
| +		return -ENOMEM;
 | |
| +	}
 | |
| +
 | |
| +	cbd.addr[0] = lower_32_bits(dma);
 | |
| +	cbd.addr[1] = upper_32_bits(dma);
 | |
| +	cbd.cls = BDCR_CMD_PORT_GCL;
 | |
| +	cbd.status_flags = 0;
 | |
| +
 | |
| +	enetc_wr(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET,
 | |
| +		 tge | ENETC_QBV_TGE);
 | |
| +
 | |
| +	err = enetc_send_cmd(priv->si, &cbd);
 | |
| +	if (err)
 | |
| +		enetc_wr(&priv->si->hw,
 | |
| +			 ENETC_QBV_PTGCR_OFFSET,
 | |
| +			 tge & (~ENETC_QBV_TGE));
 | |
| +
 | |
| +	dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE);
 | |
| +	kfree(gcl_data);
 | |
| +
 | |
| +	return err;
 | |
| +}
 | |
| +
 | |
| +int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data)
 | |
| +{
 | |
| +	struct tc_taprio_qopt_offload *taprio = type_data;
 | |
| +	struct enetc_ndev_priv *priv = netdev_priv(ndev);
 | |
| +	int err;
 | |
| +	int i;
 | |
| +
 | |
| +	for (i = 0; i < priv->num_tx_rings; i++)
 | |
| +		enetc_set_bdr_prio(&priv->si->hw,
 | |
| +				   priv->tx_ring[i]->index,
 | |
| +				   taprio->enable ? i : 0);
 | |
| +
 | |
| +	err = enetc_setup_taprio(ndev, taprio);
 | |
| +
 | |
| +	if (err)
 | |
| +		for (i = 0; i < priv->num_tx_rings; i++)
 | |
| +			enetc_set_bdr_prio(&priv->si->hw,
 | |
| +					   priv->tx_ring[i]->index,
 | |
| +					   taprio->enable ? 0 : i);
 | |
| +
 | |
| +	return err;
 | |
| +}
 |