| 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 |
| @@ -1424,8 +1424,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; |
| @@ -1433,9 +1432,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; |
| |
| @@ -1480,6 +1476,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 |
| @@ -249,3 +249,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 |
| @@ -446,22 +447,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 |
| |
| @@ -560,3 +545,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; |
| +} |