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>
This commit is contained in:
parent
c3f812cea0
commit
34c6adf197
7 changed files with 243 additions and 22 deletions
|
@ -50,3 +50,13 @@ config FSL_ENETC_HW_TIMESTAMPING
|
||||||
allocation has not been supported and it is too expensive to use
|
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 if timestamping is not used, this option enables
|
||||||
extended RX BDs in order to support hardware timestamping.
|
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).
|
||||||
|
|
|
@ -5,9 +5,11 @@ common-objs := enetc.o enetc_cbdr.o enetc_ethtool.o
|
||||||
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
|
obj-$(CONFIG_FSL_ENETC) += fsl-enetc.o
|
||||||
fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs)
|
fsl-enetc-y := enetc_pf.o enetc_mdio.o $(common-objs)
|
||||||
fsl-enetc-$(CONFIG_PCI_IOV) += enetc_msg.o
|
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
|
obj-$(CONFIG_FSL_ENETC_VF) += fsl-enetc-vf.o
|
||||||
fsl-enetc-vf-y := enetc_vf.o $(common-objs)
|
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
|
obj-$(CONFIG_FSL_ENETC_MDIO) += fsl-enetc-mdio.o
|
||||||
fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o
|
fsl-enetc-mdio-y := enetc_pci_mdio.o enetc_mdio.o
|
||||||
|
|
|
@ -1427,8 +1427,7 @@ int enetc_close(struct net_device *ndev)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
|
int enetc_setup_tc_mqprio(struct net_device *ndev, void *type_data)
|
||||||
void *type_data)
|
|
||||||
{
|
{
|
||||||
struct enetc_ndev_priv *priv = netdev_priv(ndev);
|
struct enetc_ndev_priv *priv = netdev_priv(ndev);
|
||||||
struct tc_mqprio_qopt *mqprio = type_data;
|
struct tc_mqprio_qopt *mqprio = type_data;
|
||||||
|
@ -1436,9 +1435,6 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
|
||||||
u8 num_tc;
|
u8 num_tc;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (type != TC_SETUP_QDISC_MQPRIO)
|
|
||||||
return -EOPNOTSUPP;
|
|
||||||
|
|
||||||
mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
|
mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS;
|
||||||
num_tc = mqprio->num_tc;
|
num_tc = mqprio->num_tc;
|
||||||
|
|
||||||
|
@ -1483,6 +1479,19 @@ int enetc_setup_tc(struct net_device *ndev, enum tc_setup_type type,
|
||||||
return 0;
|
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 net_device_stats *enetc_get_stats(struct net_device *ndev)
|
||||||
{
|
{
|
||||||
struct enetc_ndev_priv *priv = netdev_priv(ndev);
|
struct enetc_ndev_priv *priv = netdev_priv(ndev);
|
||||||
|
|
|
@ -244,3 +244,10 @@ int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,
|
||||||
void enetc_set_rss_key(struct enetc_hw *hw, const u8 *bytes);
|
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_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_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
|
||||||
|
|
|
@ -32,7 +32,7 @@ static int enetc_cbd_unused(struct enetc_cbdr *r)
|
||||||
r->bd_count;
|
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;
|
struct enetc_cbdr *ring = &si->cbd_ring;
|
||||||
int timeout = ENETC_CBDR_TIMEOUT;
|
int timeout = ENETC_CBDR_TIMEOUT;
|
||||||
|
@ -66,6 +66,9 @@ static int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)
|
||||||
if (!timeout)
|
if (!timeout)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
|
||||||
|
/* CBD may writeback data, feedback up level */
|
||||||
|
*cbd = *dest_cbd;
|
||||||
|
|
||||||
enetc_clean_cbdr(si);
|
enetc_clean_cbdr(si);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#define ENETC_SICTR0 0x18
|
#define ENETC_SICTR0 0x18
|
||||||
#define ENETC_SICTR1 0x1c
|
#define ENETC_SICTR1 0x1c
|
||||||
#define ENETC_SIPCAPR0 0x20
|
#define ENETC_SIPCAPR0 0x20
|
||||||
|
#define ENETC_SIPCAPR0_QBV BIT(4)
|
||||||
#define ENETC_SIPCAPR0_RSS BIT(8)
|
#define ENETC_SIPCAPR0_RSS BIT(8)
|
||||||
#define ENETC_SIPCAPR1 0x24
|
#define ENETC_SIPCAPR1 0x24
|
||||||
#define ENETC_SITGTGR 0x30
|
#define ENETC_SITGTGR 0x30
|
||||||
|
@ -440,22 +441,6 @@ union enetc_rx_bd {
|
||||||
#define EMETC_MAC_ADDR_FILT_RES 3 /* # of reserved entries at the beginning */
|
#define EMETC_MAC_ADDR_FILT_RES 3 /* # of reserved entries at the beginning */
|
||||||
#define ENETC_MAX_NUM_VFS 2
|
#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_FLAGS_SF BIT(7) /* short format */
|
||||||
#define ENETC_CBD_STATUS_MASK 0xf
|
#define ENETC_CBD_STATUS_MASK 0xf
|
||||||
|
|
||||||
|
@ -554,3 +539,70 @@ static inline void enetc_set_bdr_prio(struct enetc_hw *hw, int bdr_idx,
|
||||||
val |= ENETC_TBMR_SET_PRIO(prio);
|
val |= ENETC_TBMR_SET_PRIO(prio);
|
||||||
enetc_txbdr_wr(hw, bdr_idx, ENETC_TBMR, val);
|
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)
|
||||||
|
|
138
drivers/net/ethernet/freescale/enetc/enetc_qos.c
Normal file
138
drivers/net/ethernet/freescale/enetc/enetc_qos.c
Normal file
|
@ -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;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue