| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 6ee2331a3a5627b062daf76aa5ed9f64fbbfa303 Mon Sep 17 00:00:00 2001 |
| 2 | From: Po Liu <po.liu@nxp.com> |
| 3 | Date: Fri, 15 Nov 2019 03:33:33 +0000 |
| 4 | Subject: [PATCH] enetc: Configure the Time-Aware Scheduler via tc-taprio |
| 5 | offload |
| 6 | |
| 7 | ENETC supports in hardware for time-based egress shaping according |
| 8 | to IEEE 802.1Qbv. This patch implement the Qbv enablement by the |
| 9 | hardware offload method qdisc tc-taprio method. |
| 10 | Also update cbdr writeback to up level since control bd ring may |
| 11 | writeback data to control bd ring. |
| 12 | |
| 13 | Signed-off-by: Po Liu <Po.Liu@nxp.com> |
| 14 | Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> |
| 15 | Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com> |
| 16 | Signed-off-by: David S. Miller <davem@davemloft.net> |
| 17 | --- |
| 18 | drivers/net/ethernet/freescale/enetc/Kconfig | 10 ++ |
| 19 | drivers/net/ethernet/freescale/enetc/Makefile | 2 + |
| 20 | drivers/net/ethernet/freescale/enetc/enetc.c | 19 ++- |
| 21 | drivers/net/ethernet/freescale/enetc/enetc.h | 7 ++ |
| 22 | drivers/net/ethernet/freescale/enetc/enetc_cbdr.c | 5 +- |
| 23 | drivers/net/ethernet/freescale/enetc/enetc_hw.h | 84 ++++++++++--- |
| 24 | drivers/net/ethernet/freescale/enetc/enetc_qos.c | 138 ++++++++++++++++++++++ |
| 25 | 7 files changed, 243 insertions(+), 22 deletions(-) |
| 26 | create mode 100644 drivers/net/ethernet/freescale/enetc/enetc_qos.c |
| 27 | |
| 28 | --- a/drivers/net/ethernet/freescale/enetc/Kconfig |
| 29 | +++ b/drivers/net/ethernet/freescale/enetc/Kconfig |
| 30 | @@ -50,3 +50,13 @@ config FSL_ENETC_HW_TIMESTAMPING |
| 31 | allocation has not been supported and it is too expensive to use |
| 32 | extended RX BDs if timestamping is not used, this option enables |
| 33 | extended RX BDs in order to support hardware timestamping. |
| 34 | + |
| 35 | +config FSL_ENETC_QOS |
| 36 | + bool "ENETC hardware Time-sensitive Network support" |
| 37 | + depends on (FSL_ENETC || FSL_ENETC_VF) && NET_SCH_TAPRIO |
| 38 | + help |
| 39 | + There are Time-Sensitive Network(TSN) capabilities(802.1Qbv/802.1Qci |
| 40 | + /802.1Qbu etc.) supported by ENETC. These TSN capabilities can be set |
| 41 | + enable/disable from user space via Qos commands(tc). In the kernel |
| 42 | + side, it can be loaded by Qos driver. Currently, it is only support |
| 43 | + taprio(802.1Qbv). |
| 44 | --- a/drivers/net/ethernet/freescale/enetc/Makefile |
| 45 | +++ b/drivers/net/ethernet/freescale/enetc/Makefile |
| 46 | @@ -5,9 +5,11 @@ common-objs := enetc.o enetc_cbdr.o enet |
| 47 | obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o |
| 48 | fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs) |
| 49 | fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o |
| 50 | +fsl-enetc-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o |
| 51 | |
| 52 | obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o |
| 53 | fsl-enetc-vf-y := enetc_vf.o $(common-objs) |
| 54 | +fsl-enetc-vf-$(CONFIG_FSL_ENETC_QOS) += enetc_qos.o |
| 55 | |
| 56 | obj-$(CONFIG_FSL_ENETC_MDIO) += fsl-enetc-mdio.o |
| 57 | fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o |
| 58 | --- a/drivers/net/ethernet/freescale/enetc/enetc.c |
| 59 | +++ b/drivers/net/ethernet/freescale/enetc/enetc.c |
| 60 | @@ -1424,8 +1424,7 @@ int enetc_close(struct net_device *ndev) |
| 61 | return 0; |
| 62 | } |
| 63 | |
| 64 | -int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type, |
| 65 | - void *type_data) |
| 66 | +int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data) |
| 67 | { |
| 68 | struct enetc_ndev_priv *priv = netdev_priv(ndev); |
| 69 | struct tc_mqprio_qopt *mqprio = type_data; |
| 70 | @@ -1433,9 +1432,6 @@ int enetc_setup_tc(struct net_device *nd |
| 71 | u8 num_tc; |
| 72 | int i; |
| 73 | |
| 74 | - if (type != TC_SETUP_QDISC_MQPRIO) |
| 75 | - return -EOPNOTSUPP; |
| 76 | - |
| 77 | mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; |
| 78 | num_tc = mqprio->num_tc; |
| 79 | |
| 80 | @@ -1480,6 +1476,19 @@ int enetc_setup_tc(struct net_device *nd |
| 81 | return 0; |
| 82 | } |
| 83 | |
| 84 | +int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type, |
| 85 | + void *type_data) |
| 86 | +{ |
| 87 | + switch (type) { |
| 88 | + case TC_SETUP_QDISC_MQPRIO: |
| 89 | + return enetc_setup_tc_mqprio(ndev, type_data); |
| 90 | + case TC_SETUP_QDISC_TAPRIO: |
| 91 | + return enetc_setup_tc_taprio(ndev, type_data); |
| 92 | + default: |
| 93 | + return -EOPNOTSUPP; |
| 94 | + } |
| 95 | +} |
| 96 | + |
| 97 | struct net_device_stats *enetc_get_stats(struct net_device *ndev) |
| 98 | { |
| 99 | struct enetc_ndev_priv *priv = netdev_priv(ndev); |
| 100 | --- a/drivers/net/ethernet/freescale/enetc/enetc.h |
| 101 | +++ b/drivers/net/ethernet/freescale/enetc/enetc.h |
| 102 | @@ -249,3 +249,10 @@ int enetc_set_fs_entry(struct enetc_si * |
| 103 | void enetc_set_rss_key(struct enetc_hw *hw, const u8 *bytes); |
| 104 | int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count); |
| 105 | int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count); |
| 106 | +int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd); |
| 107 | + |
| 108 | +#ifdef CONFIG_FSL_ENETC_QOS |
| 109 | +int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data); |
| 110 | +#else |
| 111 | +#define enetc_setup_tc_taprio(ndev, type_data) -EOPNOTSUPP |
| 112 | +#endif |
| 113 | --- a/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c |
| 114 | +++ b/drivers/net/ethernet/freescale/enetc/enetc_cbdr.c |
| 115 | @@ -32,7 +32,7 @@ static int enetc_cbd_unused(struct enetc |
| 116 | r->bd_count; |
| 117 | } |
| 118 | |
| 119 | -static int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd) |
| 120 | +int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd) |
| 121 | { |
| 122 | struct enetc_cbdr *ring = &si->cbd_ring; |
| 123 | int timeout = ENETC_CBDR_TIMEOUT; |
| 124 | @@ -66,6 +66,9 @@ static int enetc_send_cmd(struct enetc_s |
| 125 | if (!timeout) |
| 126 | return -EBUSY; |
| 127 | |
| 128 | + /* CBD may writeback data, feedback up level */ |
| 129 | + *cbd = *dest_cbd; |
| 130 | + |
| 131 | enetc_clean_cbdr(si); |
| 132 | |
| 133 | return 0; |
| 134 | --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h |
| 135 | +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h |
| 136 | @@ -18,6 +18,7 @@ |
| 137 | #define ENETC_SICTR0 0x18 |
| 138 | #define ENETC_SICTR1 0x1c |
| 139 | #define ENETC_SIPCAPR0 0x20 |
| 140 | +#define ENETC_SIPCAPR0_QBV BIT(4) |
| 141 | #define ENETC_SIPCAPR0_RSS BIT(8) |
| 142 | #define ENETC_SIPCAPR1 0x24 |
| 143 | #define ENETC_SITGTGR 0x30 |
| 144 | @@ -446,22 +447,6 @@ union enetc_rx_bd { |
| 145 | #define EMETC_MAC_ADDR_FILT_RES 3 /* # of reserved entries at the beginning */ |
| 146 | #define ENETC_MAX_NUM_VFS 2 |
| 147 | |
| 148 | -struct enetc_cbd { |
| 149 | - union { |
| 150 | - struct { |
| 151 | - __le32 addr[2]; |
| 152 | - __le32 opt[4]; |
| 153 | - }; |
| 154 | - __le32 data[6]; |
| 155 | - }; |
| 156 | - __le16 index; |
| 157 | - __le16 length; |
| 158 | - u8 cmd; |
| 159 | - u8 cls; |
| 160 | - u8 _res; |
| 161 | - u8 status_flags; |
| 162 | -}; |
| 163 | - |
| 164 | #define ENETC_CBD_FLAGS_SF BIT(7) /* short format */ |
| 165 | #define ENETC_CBD_STATUS_MASK 0xf |
| 166 | |
| 167 | @@ -560,3 +545,70 @@ static inline void enetc_set_bdr_prio(st |
| 168 | val |= ENETC_TBMR_SET_PRIO(prio); |
| 169 | enetc_txbdr_wr(hw, bdr_idx, ENETC_TBMR, val); |
| 170 | } |
| 171 | + |
| 172 | +enum bdcr_cmd_class { |
| 173 | + BDCR_CMD_UNSPEC = 0, |
| 174 | + BDCR_CMD_MAC_FILTER, |
| 175 | + BDCR_CMD_VLAN_FILTER, |
| 176 | + BDCR_CMD_RSS, |
| 177 | + BDCR_CMD_RFS, |
| 178 | + BDCR_CMD_PORT_GCL, |
| 179 | + BDCR_CMD_RECV_CLASSIFIER, |
| 180 | + __BDCR_CMD_MAX_LEN, |
| 181 | + BDCR_CMD_MAX_LEN = __BDCR_CMD_MAX_LEN - 1, |
| 182 | +}; |
| 183 | + |
| 184 | +/* class 5, command 0 */ |
| 185 | +struct tgs_gcl_conf { |
| 186 | + u8 atc; /* init gate value */ |
| 187 | + u8 res[7]; |
| 188 | + struct { |
| 189 | + u8 res1[4]; |
| 190 | + __le16 acl_len; |
| 191 | + u8 res2[2]; |
| 192 | + }; |
| 193 | +}; |
| 194 | + |
| 195 | +/* gate control list entry */ |
| 196 | +struct gce { |
| 197 | + __le32 period; |
| 198 | + u8 gate; |
| 199 | + u8 res[3]; |
| 200 | +}; |
| 201 | + |
| 202 | +/* tgs_gcl_conf address point to this data space */ |
| 203 | +struct tgs_gcl_data { |
| 204 | + __le32 btl; |
| 205 | + __le32 bth; |
| 206 | + __le32 ct; |
| 207 | + __le32 cte; |
| 208 | + struct gce entry[0]; |
| 209 | +}; |
| 210 | + |
| 211 | +struct enetc_cbd { |
| 212 | + union{ |
| 213 | + struct { |
| 214 | + __le32 addr[2]; |
| 215 | + union { |
| 216 | + __le32 opt[4]; |
| 217 | + struct tgs_gcl_conf gcl_conf; |
| 218 | + }; |
| 219 | + }; /* Long format */ |
| 220 | + __le32 data[6]; |
| 221 | + }; |
| 222 | + __le16 index; |
| 223 | + __le16 length; |
| 224 | + u8 cmd; |
| 225 | + u8 cls; |
| 226 | + u8 _res; |
| 227 | + u8 status_flags; |
| 228 | +}; |
| 229 | + |
| 230 | +/* port time gating control register */ |
| 231 | +#define ENETC_QBV_PTGCR_OFFSET 0x11a00 |
| 232 | +#define ENETC_QBV_TGE BIT(31) |
| 233 | +#define ENETC_QBV_TGPE BIT(30) |
| 234 | + |
| 235 | +/* Port time gating capability register */ |
| 236 | +#define ENETC_QBV_PTGCAPR_OFFSET 0x11a08 |
| 237 | +#define ENETC_QBV_MAX_GCL_LEN_MASK GENMASK(15, 0) |
| 238 | --- /dev/null |
| 239 | +++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c |
| 240 | @@ -0,0 +1,138 @@ |
| 241 | +// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) |
| 242 | +/* Copyright 2019 NXP */ |
| 243 | + |
| 244 | +#include "enetc.h" |
| 245 | + |
| 246 | +#include <net/pkt_sched.h> |
| 247 | + |
| 248 | +static u16 enetc_get_max_gcl_len(struct enetc_hw *hw) |
| 249 | +{ |
| 250 | + return enetc_rd(hw, ENETC_QBV_PTGCAPR_OFFSET) |
| 251 | + & ENETC_QBV_MAX_GCL_LEN_MASK; |
| 252 | +} |
| 253 | + |
| 254 | +static int enetc_setup_taprio(struct net_device *ndev, |
| 255 | + struct tc_taprio_qopt_offload *admin_conf) |
| 256 | +{ |
| 257 | + struct enetc_ndev_priv *priv = netdev_priv(ndev); |
| 258 | + struct enetc_cbd cbd = {.cmd = 0}; |
| 259 | + struct tgs_gcl_conf *gcl_config; |
| 260 | + struct tgs_gcl_data *gcl_data; |
| 261 | + struct gce *gce; |
| 262 | + dma_addr_t dma; |
| 263 | + u16 data_size; |
| 264 | + u16 gcl_len; |
| 265 | + u32 tge; |
| 266 | + int err; |
| 267 | + int i; |
| 268 | + |
| 269 | + if (admin_conf->num_entries > enetc_get_max_gcl_len(&priv->si->hw)) |
| 270 | + return -EINVAL; |
| 271 | + gcl_len = admin_conf->num_entries; |
| 272 | + |
| 273 | + tge = enetc_rd(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET); |
| 274 | + if (!admin_conf->enable) { |
| 275 | + enetc_wr(&priv->si->hw, |
| 276 | + ENETC_QBV_PTGCR_OFFSET, |
| 277 | + tge & (~ENETC_QBV_TGE)); |
| 278 | + return 0; |
| 279 | + } |
| 280 | + |
| 281 | + if (admin_conf->cycle_time > U32_MAX || |
| 282 | + admin_conf->cycle_time_extension > U32_MAX) |
| 283 | + return -EINVAL; |
| 284 | + |
| 285 | + /* Configure the (administrative) gate control list using the |
| 286 | + * control BD descriptor. |
| 287 | + */ |
| 288 | + gcl_config = &cbd.gcl_conf; |
| 289 | + |
| 290 | + data_size = struct_size(gcl_data, entry, gcl_len); |
| 291 | + gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL); |
| 292 | + if (!gcl_data) |
| 293 | + return -ENOMEM; |
| 294 | + |
| 295 | + gce = (struct gce *)(gcl_data + 1); |
| 296 | + |
| 297 | + /* Set all gates open as default */ |
| 298 | + gcl_config->atc = 0xff; |
| 299 | + gcl_config->acl_len = cpu_to_le16(gcl_len); |
| 300 | + |
| 301 | + if (!admin_conf->base_time) { |
| 302 | + gcl_data->btl = |
| 303 | + cpu_to_le32(enetc_rd(&priv->si->hw, ENETC_SICTR0)); |
| 304 | + gcl_data->bth = |
| 305 | + cpu_to_le32(enetc_rd(&priv->si->hw, ENETC_SICTR1)); |
| 306 | + } else { |
| 307 | + gcl_data->btl = |
| 308 | + cpu_to_le32(lower_32_bits(admin_conf->base_time)); |
| 309 | + gcl_data->bth = |
| 310 | + cpu_to_le32(upper_32_bits(admin_conf->base_time)); |
| 311 | + } |
| 312 | + |
| 313 | + gcl_data->ct = cpu_to_le32(admin_conf->cycle_time); |
| 314 | + gcl_data->cte = cpu_to_le32(admin_conf->cycle_time_extension); |
| 315 | + |
| 316 | + for (i = 0; i < gcl_len; i++) { |
| 317 | + struct tc_taprio_sched_entry *temp_entry; |
| 318 | + struct gce *temp_gce = gce + i; |
| 319 | + |
| 320 | + temp_entry = &admin_conf->entries[i]; |
| 321 | + |
| 322 | + temp_gce->gate = (u8)temp_entry->gate_mask; |
| 323 | + temp_gce->period = cpu_to_le32(temp_entry->interval); |
| 324 | + } |
| 325 | + |
| 326 | + cbd.length = cpu_to_le16(data_size); |
| 327 | + cbd.status_flags = 0; |
| 328 | + |
| 329 | + dma = dma_map_single(&priv->si->pdev->dev, gcl_data, |
| 330 | + data_size, DMA_TO_DEVICE); |
| 331 | + if (dma_mapping_error(&priv->si->pdev->dev, dma)) { |
| 332 | + netdev_err(priv->si->ndev, "DMA mapping failed!\n"); |
| 333 | + kfree(gcl_data); |
| 334 | + return -ENOMEM; |
| 335 | + } |
| 336 | + |
| 337 | + cbd.addr[0] = lower_32_bits(dma); |
| 338 | + cbd.addr[1] = upper_32_bits(dma); |
| 339 | + cbd.cls = BDCR_CMD_PORT_GCL; |
| 340 | + cbd.status_flags = 0; |
| 341 | + |
| 342 | + enetc_wr(&priv->si->hw, ENETC_QBV_PTGCR_OFFSET, |
| 343 | + tge | ENETC_QBV_TGE); |
| 344 | + |
| 345 | + err = enetc_send_cmd(priv->si, &cbd); |
| 346 | + if (err) |
| 347 | + enetc_wr(&priv->si->hw, |
| 348 | + ENETC_QBV_PTGCR_OFFSET, |
| 349 | + tge & (~ENETC_QBV_TGE)); |
| 350 | + |
| 351 | + dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE); |
| 352 | + kfree(gcl_data); |
| 353 | + |
| 354 | + return err; |
| 355 | +} |
| 356 | + |
| 357 | +int enetc_setup_tc_taprio(struct net_device *ndev, void *type_data) |
| 358 | +{ |
| 359 | + struct tc_taprio_qopt_offload *taprio = type_data; |
| 360 | + struct enetc_ndev_priv *priv = netdev_priv(ndev); |
| 361 | + int err; |
| 362 | + int i; |
| 363 | + |
| 364 | + for (i = 0; i < priv->num_tx_rings; i++) |
| 365 | + enetc_set_bdr_prio(&priv->si->hw, |
| 366 | + priv->tx_ring[i]->index, |
| 367 | + taprio->enable ? i : 0); |
| 368 | + |
| 369 | + err = enetc_setup_taprio(ndev, taprio); |
| 370 | + |
| 371 | + if (err) |
| 372 | + for (i = 0; i < priv->num_tx_rings; i++) |
| 373 | + enetc_set_bdr_prio(&priv->si->hw, |
| 374 | + priv->tx_ring[i]->index, |
| 375 | + taprio->enable ? 0 : i); |
| 376 | + |
| 377 | + return err; |
| 378 | +} |