diff --git a/drivers/net/dsa/ocelot/Kconfig b/drivers/net/dsa/ocelot/Kconfig index 0031ca814346..7d24d55badd6 100644 --- a/drivers/net/dsa/ocelot/Kconfig +++ b/drivers/net/dsa/ocelot/Kconfig @@ -9,3 +9,11 @@ config NET_DSA_MSCC_FELIX the Vitesse / Microsemi / Microchip Ocelot family of switching cores. It is embedded as a PCIe function of the NXP LS1028A ENETC integrated endpoint. + +config MSCC_FELIX_SWITCH_TSN + tristate "TSN on FELIX switch driver" + depends on NET_DSA_MSCC_FELIX + depends on TSN + help + This driver supports TSN on felix switch. + diff --git a/drivers/net/dsa/ocelot/Makefile b/drivers/net/dsa/ocelot/Makefile index 37ad403e0b2a..c8db4cd0b0f7 100644 --- a/drivers/net/dsa/ocelot/Makefile +++ b/drivers/net/dsa/ocelot/Makefile @@ -4,3 +4,5 @@ obj-$(CONFIG_NET_DSA_MSCC_FELIX) += mscc_felix.o mscc_felix-objs := \ felix.o \ felix_vsc9959.o + +obj-$(CONFIG_MSCC_FELIX_SWITCH_TSN) += felix_tsn.o diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 167e41549cdd..57b4421037e0 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -9,6 +9,38 @@ #include #include #include "felix.h" +#include "felix_tsn.h" + +#ifdef CONFIG_MSCC_FELIX_SWITCH_TSN +const struct tsn_ops switch_tsn_ops = { + .device_init = felix_tsn_init, + .get_capability = felix_tsn_get_cap, + .qbv_set = felix_qbv_set, + .qbv_get = felix_qbv_get, + .qbv_get_status = felix_qbv_get_status, + .qbu_set = felix_qbu_set, + .qbu_get = felix_qbu_get, + .cb_streamid_set = felix_cb_streamid_set, + .cb_streamid_get = felix_cb_streamid_get, + .cb_streamid_counters_get = felix_cb_streamid_counters_get, + .qci_sfi_set = felix_qci_sfi_set, + .qci_sfi_get = felix_qci_sfi_get, + .qci_sfi_counters_get = felix_qci_sfi_counters_get, + .qci_get_maxcap = felix_qci_max_cap_get, + .qci_sgi_set = felix_qci_sgi_set, + .qci_sgi_get = felix_qci_sgi_get, + .qci_sgi_status_get = felix_qci_sgi_status_get, + .qci_fmi_set = felix_qci_fmi_set, + .qci_fmi_get = felix_qci_fmi_get, + .cbs_set = felix_cbs_set, + .cbs_get = felix_cbs_get, + .ct_set = felix_cut_thru_set, + .cbgen_set = felix_seq_gen_set, + .cbrec_set = felix_seq_rec_set, + .cb_get = felix_cb_get, + .dscp_set = felix_dscp_set, +}; +#endif static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, int port) @@ -138,6 +170,21 @@ static int felix_vlan_del(struct dsa_switch *ds, int port, return 0; } +#ifdef CONFIG_MSCC_FELIX_SWITCH_TSN +static int felix_tsn_enable(struct dsa_port *dp) +{ + struct net_device *dev; + + if (dp->type == DSA_PORT_TYPE_USER) { + dev = dp->slave; + tsn_port_register(dev, + (struct tsn_ops *)&switch_tsn_ops, + GROUP_OFFSET_SWITCH); + } + return 0; +} +#endif + static int felix_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) { @@ -386,6 +433,9 @@ static const struct dsa_switch_ops felix_switch_ops = { .port_hwtstamp_set = felix_hwtstamp_set, .port_rxtstamp = felix_rxtstamp, .port_txtstamp = felix_txtstamp, +#ifdef CONFIG_MSCC_FELIX_SWITCH_TSN + .port_tsn_enable = felix_tsn_enable, +#endif }; static struct felix_info *felix_instance_tbl[] = { diff --git a/drivers/net/dsa/ocelot/felix_tsn.c b/drivers/net/dsa/ocelot/felix_tsn.c new file mode 100644 index 000000000000..2e446a165f13 --- /dev/null +++ b/drivers/net/dsa/ocelot/felix_tsn.c @@ -0,0 +1,432 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Felix Switch TSN driver + * + * Copyright 2018-2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include "felix.h" + +static struct ocelot *felix_dev_to_ocelot(struct net_device *ndev) +{ + struct pci_dev *pdev; + struct felix *felix; + + pdev = list_entry(ndev->dev.parent, struct pci_dev, dev); + felix = pci_get_drvdata(pdev); + if (!felix) + return NULL; + + return &felix->ocelot; +} + +static int felix_dev_to_port(struct net_device *ndev, struct ocelot *ocelot) +{ + struct felix *felix = ocelot_to_felix(ocelot); + struct dsa_switch *ds = felix->ds; + struct dsa_port *dp; + int i; + + for (i = 0; i < ds->num_ports; i++) { + dp = &ds->ports[i]; + if (dp->dn == ndev->dev.of_node) + return dp->index; + } + + return -ENODEV; +} + +u32 felix_tsn_get_cap(struct net_device *ndev) +{ + u32 cap = 0; + + cap = (TSN_CAP_QBV | TSN_CAP_QCI | TSN_CAP_QBU | TSN_CAP_CBS | + TSN_CAP_CB | TSN_CAP_TBS | TSN_CAP_CTH); + + return cap; +} + +int felix_qbv_set(struct net_device *ndev, + struct tsn_qbv_conf *shaper_config) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qbv_set(ocelot, port, shaper_config); +} + +int felix_qbv_get(struct net_device *ndev, + struct tsn_qbv_conf *shaper_config) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qbv_get(ocelot, port, shaper_config); +} + +int felix_qbv_get_status(struct net_device *ndev, + struct tsn_qbv_status *qbvstatus) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qbv_get_status(ocelot, port, qbvstatus); +} + +int felix_qbu_set(struct net_device *ndev, u8 preemptible) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qbu_set(ocelot, port, preemptible); +} + +int felix_qbu_get(struct net_device *ndev, struct tsn_preempt_status *c) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qbu_get(ocelot, port, c); +} + +int felix_cb_streamid_set(struct net_device *ndev, u32 index, bool enable, + struct tsn_cb_streamid *streamid) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_cb_streamid_set(ocelot, port, index, enable, streamid); +} + +int felix_cb_streamid_get(struct net_device *ndev, u32 index, + struct tsn_cb_streamid *streamid) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_cb_streamid_get(ocelot, port, index, streamid); +} + +int felix_cb_streamid_counters_get(struct net_device *ndev, u32 index, + struct tsn_cb_streamid_counters *sc) +{ + return 0; +} + +int felix_qci_sfi_set(struct net_device *ndev, u32 index, bool enable, + struct tsn_qci_psfp_sfi_conf *sfi) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_sfi_set(ocelot, port, index, enable, sfi); +} + +int felix_qci_sfi_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sfi_conf *sfi) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_sfi_get(ocelot, port, index, sfi); +} + +int felix_qci_sfi_counters_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sfi_counters *sfi_cnt) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_sfi_counters_get(ocelot, port, index, sfi_cnt); +} + +int felix_qci_max_cap_get(struct net_device *ndev, + struct tsn_qci_psfp_stream_param *stream_para) +{ + struct ocelot *ocelot; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + + return ocelot_qci_max_cap_get(ocelot, stream_para); +} + +int felix_qci_sgi_set(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sgi_conf *sgi_conf) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_sgi_set(ocelot, port, index, sgi_conf); +} + +int felix_qci_sgi_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sgi_conf *sgi_conf) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_sgi_get(ocelot, port, index, sgi_conf); +} + +int felix_qci_sgi_status_get(struct net_device *ndev, u32 index, + struct tsn_psfp_sgi_status *sgi_status) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_sgi_status_get(ocelot, port, index, sgi_status); +} + +int felix_qci_fmi_set(struct net_device *ndev, u32 index, + bool enable, struct tsn_qci_psfp_fmi *fmi) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_fmi_set(ocelot, port, index, enable, fmi); +} + +int felix_qci_fmi_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_fmi *fmi, + struct tsn_qci_psfp_fmi_counters *counters) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_qci_fmi_get(ocelot, port, index, fmi, counters); +} + +int felix_cbs_set(struct net_device *ndev, u8 tc, u8 bw) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_cbs_set(ocelot, port, tc, bw); +} + +int felix_cbs_get(struct net_device *ndev, u8 tc) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_cbs_get(ocelot, port, tc); +} + +int felix_cut_thru_set(struct net_device *ndev, u8 cut_thru) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_cut_thru_set(ocelot, port, cut_thru); +} + +int felix_seq_gen_set(struct net_device *ndev, u32 index, + struct tsn_seq_gen_conf *sg_conf) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_seq_gen_set(ocelot, port, index, sg_conf); +} + +int felix_seq_rec_set(struct net_device *ndev, u32 index, + struct tsn_seq_rec_conf *sr_conf) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_seq_rec_set(ocelot, port, index, sr_conf); +} + +int felix_cb_get(struct net_device *ndev, u32 index, + struct tsn_cb_status *c) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_cb_get(ocelot, port, index, c); +} + +int felix_dscp_set(struct net_device *ndev, bool enable, const u8 dscp_ix, + struct tsn_qos_switch_dscp_conf *c) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return -ENODEV; + port = felix_dev_to_port(ndev, ocelot); + if (port < 0) + return -ENODEV; + + return ocelot_dscp_set(ocelot, port, enable, dscp_ix, c); +} + +void felix_tsn_init(struct net_device *ndev) +{ + struct ocelot *ocelot; + int port; + + ocelot = felix_dev_to_ocelot(ndev); + if (!ocelot) + return; + port = felix_dev_to_port(ndev, ocelot); + + ocelot_pcp_map_enable(ocelot, port); + ocelot_rtag_parse_enable(ocelot, port); +} diff --git a/drivers/net/dsa/ocelot/felix_tsn.h b/drivers/net/dsa/ocelot/felix_tsn.h new file mode 100644 index 000000000000..f5d8a0defea7 --- /dev/null +++ b/drivers/net/dsa/ocelot/felix_tsn.h @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) + * + * TSN_SWITCH driver + * + * Copyright 2018-2019 NXP + */ + +#ifndef _MSCC_FELIX_SWITCH_TSN_H_ +#define _MSCC_FELIX_SWITCH_TSN_H_ +#include + +u32 felix_tsn_get_cap(struct net_device *ndev); +int felix_qbv_set(struct net_device *ndev, + struct tsn_qbv_conf *shaper_config); +int felix_qbv_get(struct net_device *ndev, + struct tsn_qbv_conf *shaper_config); +int felix_qbv_get_status(struct net_device *ndev, + struct tsn_qbv_status *qbvstatus); +int felix_cut_thru_set(struct net_device *ndev, u8 cut_thru); +int felix_cbs_set(struct net_device *ndev, u8 tc, u8 bw); +int felix_cbs_get(struct net_device *ndev, u8 tc); +int felix_qbu_set(struct net_device *ndev, u8 preemptible); +int felix_qbu_get(struct net_device *ndev, struct tsn_preempt_status *c); +int felix_cb_streamid_get(struct net_device *ndev, u32 index, + struct tsn_cb_streamid *streamid); +int felix_cb_streamid_set(struct net_device *ndev, u32 index, + bool enable, struct tsn_cb_streamid *streamid); +int felix_cb_streamid_counters_get(struct net_device *ndev, u32 index, + struct tsn_cb_streamid_counters *sc); +int felix_qci_sfi_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sfi_conf *sfi); +int felix_qci_sfi_set(struct net_device *ndev, u32 index, + bool enable, struct tsn_qci_psfp_sfi_conf *sfi); +int felix_cb_streamid_counters_get(struct net_device *ndev, u32 index, + struct tsn_cb_streamid_counters *s_counters); +int felix_qci_sfi_counters_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sfi_counters *sfi_counters); +int felix_qci_max_cap_get(struct net_device *ndev, + struct tsn_qci_psfp_stream_param *stream_para); +int felix_qci_sgi_set(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sgi_conf *sgi_conf); +int felix_qci_sgi_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_sgi_conf *sgi_conf); +int felix_qci_sgi_status_get(struct net_device *ndev, u16 index, + struct tsn_psfp_sgi_status *sgi_status); +int felix_qci_fmi_set(struct net_device *ndev, u32 index, + bool enable, struct tsn_qci_psfp_fmi *fmi); +int felix_qci_fmi_get(struct net_device *ndev, u32 index, + struct tsn_qci_psfp_fmi *fmi, + struct tsn_qci_psfp_fmi_counters *counters); +int felix_seq_gen_set(struct net_device *ndev, u32 index, + struct tsn_seq_gen_conf *sg_conf); +int felix_seq_rec_set(struct net_device *ndev, u32 index, + struct tsn_seq_rec_conf *sr_conf); +int felix_cb_get(struct net_device *ndev, u32 index, + struct tsn_cb_status *c); +int felix_dscp_set(struct net_device *ndev, bool enable, const u8 dscp_ix, + struct tsn_qos_switch_dscp_conf *c); + +void felix_tsn_init(struct net_device *ndev); +#endif diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index b9758b0d18c7..ac91ede54178 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -176,7 +176,7 @@ static const u32 vsc9959_qsys_regmap[] = { REG(QSYS_QMAXSDU_CFG_6, 0x00f62c), REG(QSYS_QMAXSDU_CFG_7, 0x00f648), REG(QSYS_PREEMPTION_CFG, 0x00f664), - REG_RESERVED(QSYS_CIR_CFG), + REG(QSYS_CIR_CFG, 0x000000), REG(QSYS_EIR_CFG, 0x000004), REG(QSYS_SE_CFG, 0x000008), REG(QSYS_SE_DWRR_CFG, 0x00000c), @@ -269,7 +269,7 @@ static const u32 vsc9959_sys_regmap[] = { REG_RESERVED(SYS_MMGT_FAST), REG_RESERVED(SYS_EVENTS_DIF), REG_RESERVED(SYS_EVENTS_CORE), - REG_RESERVED(SYS_CNT), + REG(SYS_CNT, 0x000000), REG(SYS_PTP_STATUS, 0x000f14), REG(SYS_PTP_TXSTAMP, 0x000f18), REG(SYS_PTP_NXT, 0x000f1c), @@ -290,6 +290,10 @@ static const u32 vsc9959_ptp_regmap[] = { REG(PTP_CFG_MISC, 0x0000a0), REG(PTP_CLK_CFG_ADJ_CFG, 0x0000a4), REG(PTP_CLK_CFG_ADJ_FREQ, 0x0000a8), + REG(PTP_CUR_NSF, 0x0000bc), + REG(PTP_CUR_NSEC, 0x0000c0), + REG(PTP_CUR_SEC_LSB, 0x0000c4), + REG(PTP_CUR_SEC_MSB, 0x0000c8), }; static const u32 vsc9959_gcb_regmap[] = { diff --git a/include/net/dsa.h b/include/net/dsa.h index e40c2077dc2b..f6c9457c99bf 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -545,6 +545,7 @@ struct dsa_switch_ops { */ netdev_tx_t (*port_deferred_xmit)(struct dsa_switch *ds, int port, struct sk_buff *skb); + int (*port_tsn_enable)(struct dsa_port *dp); }; struct dsa_switch_driver { diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 716d265ba8ca..26c2a8917118 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -323,6 +323,10 @@ static int dsa_port_setup(struct dsa_port *dp) if (err) break; + /* Enable TSN function on switch port */ + if (ds->ops->port_tsn_enable) + ds->ops->port_tsn_enable(dp); + devlink_port_type_eth_set(dlp, dp->slave); break; }