| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 3426e5e4339f124f00eef8815b56a80481364550 Mon Sep 17 00:00:00 2001 | 
 | 2 | From: Po Liu <po.liu@nxp.com> | 
 | 3 | Date: Mon, 25 Nov 2019 05:56:56 +0000 | 
 | 4 | Subject: [PATCH] enetc: add support Credit Based Shaper(CBS) for hardware | 
 | 5 |  offload | 
 | 6 |  | 
 | 7 | The ENETC hardware support the Credit Based Shaper(CBS) which part | 
 | 8 | of the IEEE-802.1Qav. The CBS driver was loaded by the sch_cbs | 
 | 9 | interface when set in the QOS in the kernel. | 
 | 10 |  | 
 | 11 | Here is an example command to set 20Mbits bandwidth in 1Gbits port | 
 | 12 | for taffic class 7: | 
 | 13 |  | 
 | 14 | tc qdisc add dev eth0 root handle 1: mqprio \ | 
 | 15 | 	   num_tc 8 map 0 1 2 3 4 5 6 7 hw 1 | 
 | 16 |  | 
 | 17 | tc qdisc replace dev eth0 parent 1:8 cbs \ | 
 | 18 | 	   locredit -1470 hicredit 30 \ | 
 | 19 | 	   sendslope -980000 idleslope 20000 offload 1 | 
 | 20 |  | 
 | 21 | Signed-off-by: Po Liu <Po.Liu@nxp.com> | 
 | 22 | Reviewed-by: Claudiu Manoil <claudiu.manoil@nxp.com> | 
 | 23 | Reviewed-by: Vladimir Oltean <vladimir.oltean@nxp.com> | 
 | 24 | Signed-off-by: David S. Miller <davem@davemloft.net> | 
 | 25 | --- | 
 | 26 |  drivers/net/ethernet/freescale/enetc/Kconfig     |   4 +- | 
 | 27 |  drivers/net/ethernet/freescale/enetc/enetc.c     |   2 + | 
 | 28 |  drivers/net/ethernet/freescale/enetc/enetc.h     |   2 + | 
 | 29 |  drivers/net/ethernet/freescale/enetc/enetc_hw.h  |   4 + | 
 | 30 |  drivers/net/ethernet/freescale/enetc/enetc_qos.c | 128 +++++++++++++++++++++++ | 
 | 31 |  5 files changed, 138 insertions(+), 2 deletions(-) | 
 | 32 |  | 
 | 33 | --- a/drivers/net/ethernet/freescale/enetc/Kconfig | 
 | 34 | +++ b/drivers/net/ethernet/freescale/enetc/Kconfig | 
 | 35 | @@ -53,10 +53,10 @@ config FSL_ENETC_HW_TIMESTAMPING | 
 | 36 |   | 
 | 37 |  config FSL_ENETC_QOS | 
 | 38 |  	bool "ENETC hardware Time-sensitive Network support" | 
 | 39 | -	depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO | 
 | 40 | +	depends on (FSL_ENETC || FSL_ENETC_VF) && (NET_SCH_TAPRIO || NET_SCH_CBS) | 
 | 41 |  	help | 
 | 42 |  	  There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci | 
 | 43 |  	  /802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set | 
 | 44 |  	  enable/disable from user space via Qos commands(tc). In the kernel | 
 | 45 |  	  side, it can be loaded by Qos driver. Currently, it is only support | 
 | 46 | -	  taprio(802.1Qbv). | 
 | 47 | +	  taprio(802.1Qbv) and Credit Based Shaper(802.1Qbu). | 
 | 48 | --- a/drivers/net/ethernet/freescale/enetc/enetc.c | 
 | 49 | +++ b/drivers/net/ethernet/freescale/enetc/enetc.c | 
 | 50 | @@ -1521,6 +1521,8 @@ int enetc_setup_tc(struct net_device *nd | 
 | 51 |  		return enetc_setup_tc_mqprio(ndev, type_data); | 
 | 52 |  	case TC_SETUP_QDISC_TAPRIO: | 
 | 53 |  		return enetc_setup_tc_taprio(ndev, type_data); | 
 | 54 | +	case TC_SETUP_QDISC_CBS: | 
 | 55 | +		return enetc_setup_tc_cbs(ndev, type_data); | 
 | 56 |  	default: | 
 | 57 |  		return -EOPNOTSUPP; | 
 | 58 |  	} | 
 | 59 | --- a/drivers/net/ethernet/freescale/enetc/enetc.h | 
 | 60 | +++ b/drivers/net/ethernet/freescale/enetc/enetc.h | 
 | 61 | @@ -260,7 +260,9 @@ int enetc_send_cmd(struct enetc_si *si, | 
 | 62 |  #ifdef CONFIG_FSL_ENETC_QOS | 
 | 63 |  int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); | 
 | 64 |  void enetc_sched_speed_set(struct net_device *ndev); | 
 | 65 | +int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data); | 
 | 66 |  #else | 
 | 67 |  #define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP | 
 | 68 |  #define enetc_sched_speed_set(ndev) (void)0 | 
 | 69 | +#define enetc_setup_tc_cbs(ndev, type_data) -EOPNOTSUPP | 
 | 70 |  #endif | 
 | 71 | --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h | 
 | 72 | +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h | 
 | 73 | @@ -185,6 +185,8 @@ enum enetc_bdr_type {TX, RX}; | 
 | 74 |  #define ENETC_PSICFGR0_SIVC(bmp)	(((bmp) & 0xff) << 24) /* VLAN_TYPE */ | 
 | 75 |   | 
 | 76 |  #define ENETC_PTCCBSR0(n)	(0x1110 + (n) * 8) /* n = 0 to 7*/ | 
 | 77 | +#define ENETC_CBSE		BIT(31) | 
 | 78 | +#define ENETC_CBS_BW_MASK	GENMASK(6, 0) | 
 | 79 |  #define ENETC_PTCCBSR1(n)	(0x1114 + (n) * 8) /* n = 0 to 7*/ | 
 | 80 |  #define ENETC_RSSHASH_KEY_SIZE	40 | 
 | 81 |  #define ENETC_PRSSCAPR		0x1404 | 
 | 82 | @@ -679,6 +681,8 @@ struct enetc_cbd { | 
 | 83 |  	u8 status_flags; | 
 | 84 |  }; | 
 | 85 |   | 
 | 86 | +#define ENETC_CLK  400000000ULL | 
 | 87 | + | 
 | 88 |  /* port time gating control register */ | 
 | 89 |  #define ENETC_QBV_PTGCR_OFFSET		0x11a00 | 
 | 90 |  #define ENETC_QBV_TGE			BIT(31) | 
 | 91 | --- a/drivers/net/ethernet/freescale/enetc/enetc_qos.c | 
 | 92 | +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c | 
 | 93 | @@ -4,6 +4,7 @@ | 
 | 94 |  #include "enetc.h" | 
 | 95 |   | 
 | 96 |  #include <net/pkt_sched.h> | 
 | 97 | +#include <linux/math64.h> | 
 | 98 |   | 
 | 99 |  static u16 enetc_get_max_gcl_len(struct enetc_hw *hw) | 
 | 100 |  { | 
 | 101 | @@ -170,3 +171,130 @@ int enetc_setup_tc_taprio(struct net_dev | 
 | 102 |   | 
 | 103 |  	return err; | 
 | 104 |  } | 
 | 105 | + | 
 | 106 | +static u32 enetc_get_cbs_enable(struct enetc_hw *hw, u8 tc) | 
 | 107 | +{ | 
 | 108 | +	return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBSE; | 
 | 109 | +} | 
 | 110 | + | 
 | 111 | +static u8 enetc_get_cbs_bw(struct enetc_hw *hw, u8 tc) | 
 | 112 | +{ | 
 | 113 | +	return enetc_port_rd(hw, ENETC_PTCCBSR0(tc)) & ENETC_CBS_BW_MASK; | 
 | 114 | +} | 
 | 115 | + | 
 | 116 | +int enetc_setup_tc_cbs(struct net_device *ndev, void *type_data) | 
 | 117 | +{ | 
 | 118 | +	struct enetc_ndev_priv *priv = netdev_priv(ndev); | 
 | 119 | +	struct tc_cbs_qopt_offload *cbs = type_data; | 
 | 120 | +	u32 port_transmit_rate = priv->speed; | 
 | 121 | +	u8 tc_nums = netdev_get_num_tc(ndev); | 
 | 122 | +	struct enetc_si *si = priv->si; | 
 | 123 | +	u32 hi_credit_bit, hi_credit_reg; | 
 | 124 | +	u32 max_interference_size; | 
 | 125 | +	u32 port_frame_max_size; | 
 | 126 | +	u32 tc_max_sized_frame; | 
 | 127 | +	u8 tc = cbs->queue; | 
 | 128 | +	u8 prio_top, prio_next; | 
 | 129 | +	int bw_sum = 0; | 
 | 130 | +	u8 bw; | 
 | 131 | + | 
 | 132 | +	prio_top = netdev_get_prio_tc_map(ndev, tc_nums - 1); | 
 | 133 | +	prio_next = netdev_get_prio_tc_map(ndev, tc_nums - 2); | 
 | 134 | + | 
 | 135 | +	/* Support highest prio and second prio tc in cbs mode */ | 
 | 136 | +	if (tc != prio_top && tc != prio_next) | 
 | 137 | +		return -EOPNOTSUPP; | 
 | 138 | + | 
 | 139 | +	if (!cbs->enable) { | 
 | 140 | +		/* Make sure the other TC that are numerically | 
 | 141 | +		 * lower than this TC have been disabled. | 
 | 142 | +		 */ | 
 | 143 | +		if (tc == prio_top && | 
 | 144 | +		    enetc_get_cbs_enable(&si->hw, prio_next)) { | 
 | 145 | +			dev_err(&ndev->dev, | 
 | 146 | +				"Disable TC%d before disable TC%d\n", | 
 | 147 | +				prio_next, tc); | 
 | 148 | +			return -EINVAL; | 
 | 149 | +		} | 
 | 150 | + | 
 | 151 | +		enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), 0); | 
 | 152 | +		enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), 0); | 
 | 153 | + | 
 | 154 | +		return 0; | 
 | 155 | +	} | 
 | 156 | + | 
 | 157 | +	if (cbs->idleslope - cbs->sendslope != port_transmit_rate * 1000L || | 
 | 158 | +	    cbs->idleslope < 0 || cbs->sendslope > 0) | 
 | 159 | +		return -EOPNOTSUPP; | 
 | 160 | + | 
 | 161 | +	port_frame_max_size = ndev->mtu + VLAN_ETH_HLEN + ETH_FCS_LEN; | 
 | 162 | + | 
 | 163 | +	bw = cbs->idleslope / (port_transmit_rate * 10UL); | 
 | 164 | + | 
 | 165 | +	/* Make sure the other TC that are numerically | 
 | 166 | +	 * higher than this TC have been enabled. | 
 | 167 | +	 */ | 
 | 168 | +	if (tc == prio_next) { | 
 | 169 | +		if (!enetc_get_cbs_enable(&si->hw, prio_top)) { | 
 | 170 | +			dev_err(&ndev->dev, | 
 | 171 | +				"Enable TC%d first before enable TC%d\n", | 
 | 172 | +				prio_top, prio_next); | 
 | 173 | +			return -EINVAL; | 
 | 174 | +		} | 
 | 175 | +		bw_sum += enetc_get_cbs_bw(&si->hw, prio_top); | 
 | 176 | +	} | 
 | 177 | + | 
 | 178 | +	if (bw_sum + bw >= 100) { | 
 | 179 | +		dev_err(&ndev->dev, | 
 | 180 | +			"The sum of all CBS Bandwidth can't exceed 100\n"); | 
 | 181 | +		return -EINVAL; | 
 | 182 | +	} | 
 | 183 | + | 
 | 184 | +	tc_max_sized_frame = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(tc)); | 
 | 185 | + | 
 | 186 | +	/* For top prio TC, the max_interfrence_size is maxSizedFrame. | 
 | 187 | +	 * | 
 | 188 | +	 * For next prio TC, the max_interfrence_size is calculated as below: | 
 | 189 | +	 * | 
 | 190 | +	 *      max_interference_size = M0 + Ma + Ra * M0 / (R0 - Ra) | 
 | 191 | +	 * | 
 | 192 | +	 *	- RA: idleSlope for AVB Class A | 
 | 193 | +	 *	- R0: port transmit rate | 
 | 194 | +	 *	- M0: maximum sized frame for the port | 
 | 195 | +	 *	- MA: maximum sized frame for AVB Class A | 
 | 196 | +	 */ | 
 | 197 | + | 
 | 198 | +	if (tc == prio_top) { | 
 | 199 | +		max_interference_size = port_frame_max_size * 8; | 
 | 200 | +	} else { | 
 | 201 | +		u32 m0, ma, r0, ra; | 
 | 202 | + | 
 | 203 | +		m0 = port_frame_max_size * 8; | 
 | 204 | +		ma = enetc_port_rd(&si->hw, ENETC_PTCMSDUR(prio_top)) * 8; | 
 | 205 | +		ra = enetc_get_cbs_bw(&si->hw, prio_top) * | 
 | 206 | +			port_transmit_rate * 10000ULL; | 
 | 207 | +		r0 = port_transmit_rate * 1000000ULL; | 
 | 208 | +		max_interference_size = m0 + ma + | 
 | 209 | +			(u32)div_u64((u64)ra * m0, r0 - ra); | 
 | 210 | +	} | 
 | 211 | + | 
 | 212 | +	/* hiCredit bits calculate by: | 
 | 213 | +	 * | 
 | 214 | +	 * maxSizedFrame * (idleSlope/portTxRate) | 
 | 215 | +	 */ | 
 | 216 | +	hi_credit_bit = max_interference_size * bw / 100; | 
 | 217 | + | 
 | 218 | +	/* hiCredit bits to hiCredit register need to calculated as: | 
 | 219 | +	 * | 
 | 220 | +	 * (enetClockFrequency / portTransmitRate) * 100 | 
 | 221 | +	 */ | 
 | 222 | +	hi_credit_reg = (u32)div_u64((ENETC_CLK * 100ULL) * hi_credit_bit, | 
 | 223 | +				     port_transmit_rate * 1000000ULL); | 
 | 224 | + | 
 | 225 | +	enetc_port_wr(&si->hw, ENETC_PTCCBSR1(tc), hi_credit_reg); | 
 | 226 | + | 
 | 227 | +	/* Set bw register and enable this traffic class */ | 
 | 228 | +	enetc_port_wr(&si->hw, ENETC_PTCCBSR0(tc), bw | ENETC_CBSE); | 
 | 229 | + | 
 | 230 | +	return 0; | 
 | 231 | +} |