mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2025-01-21 17:51:17 +07:00
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
@ -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).
|
||||
|
@ -5,9 +5,11 @@ common-objs := enetc.o enetc_cbdr.o enetc_ethtool.o
|
||||
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
|
||||
|
@ -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 *ndev, enum tc_setup_type type,
|
||||
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 *ndev, enum tc_setup_type type,
|
||||
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);
|
||||
|
@ -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);
|
||||
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
|
||||
|
@ -32,7 +32,7 @@ static int enetc_cbd_unused(struct enetc_cbdr *r)
|
||||
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_si *si, struct enetc_cbd *cbd)
|
||||
if (!timeout)
|
||||
return -EBUSY;
|
||||
|
||||
/* CBD may writeback data, feedback up level */
|
||||
*cbd = *dest_cbd;
|
||||
|
||||
enetc_clean_cbdr(si);
|
||||
|
||||
return 0;
|
||||
|
@ -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
|
||||
@ -440,22 +441,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
|
||||
|
||||
@ -554,3 +539,70 @@ static inline void enetc_set_bdr_prio(struct enetc_hw *hw, int bdr_idx,
|
||||
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)
|
||||
|
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…
Reference in New Issue
Block a user