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@xxxxxxx>
Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
Signed-off-by: Claudiu Manoil <claudiu.manoil@xxxxxxx>
---
changes:
- introduce a local define CONFIG_FSL_ENETC_QOS to fix the various
configurations will result in link errors.
Since the CONFIG_NET_SCH_TAPRIO depends on many Qos configs. Not
to use it directly in driver. Add it to CONFIG_FSL_ENETC_QOS depends
on list, so only CONFIG_NET_SCH_TAPRIO enabled, user can enable this
tsn feature, or else, return not support.
drivers/net/ethernet/freescale/enetc/Kconfig | 10 ++
drivers/net/ethernet/freescale/enetc/Makefile | 1 +
drivers/net/ethernet/freescale/enetc/enetc.c | 19 ++-
drivers/net/ethernet/freescale/enetc/enetc.h | 7 +
.../net/ethernet/freescale/enetc/enetc_cbdr.c | 5 +-
.../net/ethernet/freescale/enetc/enetc_hw.h | 150 ++++++++++++++++--
.../net/ethernet/freescale/enetc/enetc_qos.c | 130 +++++++++++++++
7 files changed, 300 insertions(+), 22 deletions(-)
create mode 100644 drivers/net/ethernet/freescale/enetc/enetc_qos.c
Sorry didn't see v2, so i duplicate my question here:
@@ -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;
+ }
+}
+
diff --git a/drivers/net/ethernet/freescale/enetc/enetc_qos.c b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
new file mode 100644
index 000000000000..036bb39c7a0b
--- /dev/null
+++ b/drivers/net/ethernet/freescale/enetc/enetc_qos.c
+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 temp;
+ int i;
+
+ gcl_len = admin_conf->num_entries;
+ if (gcl_len > enetc_get_max_gcl_len(&priv->si->hw))
+ return -EINVAL;
+
+ if (admin_conf->enable) {
+ enetc_wr(&priv->si->hw,
+ ENETC_QBV_PTGCR_OFFSET,
+ temp & (~ENETC_QBV_TGE));
+ usleep_range(10, 20);
+ enetc_wr(&priv->si->hw,
+ ENETC_QBV_PTGCR_OFFSET,
+ temp | ENETC_QBV_TGE);
+ } else {
+ enetc_wr(&priv->si->hw,
+ ENETC_QBV_PTGCR_OFFSET,
+ temp & (~ENETC_QBV_TGE));
+ return 0;
+ }
+tc-taprio and IEEE Qbv allows to change configuration in flight, so that oper
+ /* Configure the (administrative) gate control list using the
+ * control BD descriptor.
+ */
+ gcl_config = &cbd.gcl_conf;
+
+ data_size = sizeof(struct tgs_gcl_data) + gcl_len * sizeof(struct gce);
+
+ gcl_data = kzalloc(data_size, __GFP_DMA | GFP_KERNEL);
+ if (!gcl_data)
+ return -ENOMEM;
+
+ gce = (struct gce *)(gcl_data + 1);
+
+ /* Since no initial state config in taprio, set 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 = cpu_to_le32(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;
+
+ /* Updated by ENETC on completion of the configuration
+ * command. A zero value indicates success.
+ */
+ cbd.status_flags = 0;
+
+ enetc_send_cmd(priv->si, &cbd);
+
+ dma_unmap_single(&priv->si->pdev->dev, dma, data_size, DMA_TO_DEVICE);
+ kfree(gcl_data);
+
+ return 0;
+}
+
+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 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);
+
+ return enetc_setup_taprio(ndev, taprio);
+}
--
2.17.1