Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: mscc: ocelot: add MAC Merge layer support for VSC9959

Felix (VSC9959) has a DEV_GMII:MM_CONFIG block composed of 2 registers
(ENABLE_CONFIG and VERIF_CONFIG). Because the MAC Merge statistics and
pMAC statistics are already in the Ocelot switch lib even if just Felix
supports them, I'm adding support for the whole MAC Merge layer in the
common Ocelot library too.

There is an interrupt (shared with the PTP interrupt) which signals
changes to the MM verification state. This is done because the
preemptible traffic classes should be committed to hardware only once
the verification procedure has declared the link partner of being
capable of receiving preemptible frames.

We implement ethtool getters and setters for the MAC Merge layer state.
The "TX enabled" and "verify status" are taken from the IRQ handler,
using a mutex to ensure serialized access.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Vladimir Oltean and committed by
David S. Miller
6505b680 ab3f97a9

+302 -12
+19
drivers/net/dsa/ocelot/felix.c
··· 2024 2024 return ocelot_port_del_dscp_prio(ocelot, port, dscp, prio); 2025 2025 } 2026 2026 2027 + static int felix_get_mm(struct dsa_switch *ds, int port, 2028 + struct ethtool_mm_state *state) 2029 + { 2030 + struct ocelot *ocelot = ds->priv; 2031 + 2032 + return ocelot_port_get_mm(ocelot, port, state); 2033 + } 2034 + 2035 + static int felix_set_mm(struct dsa_switch *ds, int port, 2036 + struct ethtool_mm_cfg *cfg, 2037 + struct netlink_ext_ack *extack) 2038 + { 2039 + struct ocelot *ocelot = ds->priv; 2040 + 2041 + return ocelot_port_set_mm(ocelot, port, cfg, extack); 2042 + } 2043 + 2027 2044 static void felix_get_mm_stats(struct dsa_switch *ds, int port, 2028 2045 struct ethtool_mm_stats *stats) 2029 2046 { ··· 2056 2039 .setup = felix_setup, 2057 2040 .teardown = felix_teardown, 2058 2041 .set_ageing_time = felix_set_ageing_time, 2042 + .get_mm = felix_get_mm, 2043 + .set_mm = felix_set_mm, 2059 2044 .get_mm_stats = felix_get_mm_stats, 2060 2045 .get_stats64 = felix_get_stats64, 2061 2046 .get_pause_stats = felix_get_pause_stats,
+11 -8
drivers/net/dsa/ocelot/felix_vsc9959.c
··· 6 6 #include <soc/mscc/ocelot_qsys.h> 7 7 #include <soc/mscc/ocelot_vcap.h> 8 8 #include <soc/mscc/ocelot_ana.h> 9 + #include <soc/mscc/ocelot_dev.h> 9 10 #include <soc/mscc/ocelot_ptp.h> 10 11 #include <soc/mscc/ocelot_sys.h> 11 12 #include <net/tc_act/tc_gate.h> ··· 477 476 REG(DEV_MAC_FC_MAC_LOW_CFG, 0x3c), 478 477 REG(DEV_MAC_FC_MAC_HIGH_CFG, 0x40), 479 478 REG(DEV_MAC_STICKY, 0x44), 479 + REG(DEV_MM_ENABLE_CONFIG, 0x48), 480 + REG(DEV_MM_VERIF_CONFIG, 0x4C), 481 + REG(DEV_MM_STATUS, 0x50), 480 482 REG_RESERVED(PCS1G_CFG), 481 483 REG_RESERVED(PCS1G_MODE_CFG), 482 484 REG_RESERVED(PCS1G_SD_CFG), ··· 2603 2599 .tas_guard_bands_update = vsc9959_tas_guard_bands_update, 2604 2600 }; 2605 2601 2602 + /* The INTB interrupt is shared between for PTP TX timestamp availability 2603 + * notification and MAC Merge status change on each port. 2604 + */ 2606 2605 static irqreturn_t felix_irq_handler(int irq, void *data) 2607 2606 { 2608 2607 struct ocelot *ocelot = (struct ocelot *)data; 2609 - 2610 - /* The INTB interrupt is used for both PTP TX timestamp interrupt 2611 - * and preemption status change interrupt on each port. 2612 - * 2613 - * - Get txtstamp if have 2614 - * - TODO: handle preemption. Without handling it, driver may get 2615 - * interrupt storm. 2616 - */ 2608 + int port; 2617 2609 2618 2610 ocelot_get_txtstamp(ocelot); 2611 + 2612 + for (port = 0; port < ocelot->num_phys_ports; port++) 2613 + ocelot_port_mm_irq(ocelot, port); 2619 2614 2620 2615 return IRQ_HANDLED; 2621 2616 }
+1
drivers/net/ethernet/mscc/Makefile
··· 5 5 ocelot_devlink.o \ 6 6 ocelot_flower.o \ 7 7 ocelot_io.o \ 8 + ocelot_mm.o \ 8 9 ocelot_police.o \ 9 10 ocelot_ptp.o \ 10 11 ocelot_stats.o \
+14 -4
drivers/net/ethernet/mscc/ocelot.c
··· 2738 2738 return -ENOMEM; 2739 2739 2740 2740 ret = ocelot_stats_init(ocelot); 2741 - if (ret) { 2742 - destroy_workqueue(ocelot->owq); 2743 - return ret; 2744 - } 2741 + if (ret) 2742 + goto err_stats_init; 2745 2743 2746 2744 INIT_LIST_HEAD(&ocelot->multicast); 2747 2745 INIT_LIST_HEAD(&ocelot->pgids); ··· 2753 2755 2754 2756 if (ocelot->ops->psfp_init) 2755 2757 ocelot->ops->psfp_init(ocelot); 2758 + 2759 + if (ocelot->mm_supported) { 2760 + ret = ocelot_mm_init(ocelot); 2761 + if (ret) 2762 + goto err_mm_init; 2763 + } 2756 2764 2757 2765 for (port = 0; port < ocelot->num_phys_ports; port++) { 2758 2766 /* Clear all counters (5 groups) */ ··· 2857 2853 ANA_CPUQ_8021_CFG, i); 2858 2854 2859 2855 return 0; 2856 + 2857 + err_mm_init: 2858 + ocelot_stats_deinit(ocelot); 2859 + err_stats_init: 2860 + destroy_workqueue(ocelot->owq); 2861 + return ret; 2860 2862 } 2861 2863 EXPORT_SYMBOL(ocelot_init); 2862 2864
+2
drivers/net/ethernet/mscc/ocelot.h
··· 109 109 int ocelot_stats_init(struct ocelot *ocelot); 110 110 void ocelot_stats_deinit(struct ocelot *ocelot); 111 111 112 + int ocelot_mm_init(struct ocelot *ocelot); 113 + 112 114 extern struct notifier_block ocelot_netdevice_nb; 113 115 extern struct notifier_block ocelot_switchdev_nb; 114 116 extern struct notifier_block ocelot_switchdev_blocking_nb;
+214
drivers/net/ethernet/mscc/ocelot_mm.c
··· 1 + // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2 + /* 3 + * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable 4 + * switches (VSC9959) 5 + * 6 + * Copyright 2022-2023 NXP 7 + */ 8 + #include <linux/ethtool.h> 9 + #include <soc/mscc/ocelot.h> 10 + #include <soc/mscc/ocelot_dev.h> 11 + #include <soc/mscc/ocelot_qsys.h> 12 + 13 + #include "ocelot.h" 14 + 15 + static const char * 16 + mm_verify_state_to_string(enum ethtool_mm_verify_status state) 17 + { 18 + switch (state) { 19 + case ETHTOOL_MM_VERIFY_STATUS_INITIAL: 20 + return "INITIAL"; 21 + case ETHTOOL_MM_VERIFY_STATUS_VERIFYING: 22 + return "VERIFYING"; 23 + case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: 24 + return "SUCCEEDED"; 25 + case ETHTOOL_MM_VERIFY_STATUS_FAILED: 26 + return "FAILED"; 27 + case ETHTOOL_MM_VERIFY_STATUS_DISABLED: 28 + return "DISABLED"; 29 + default: 30 + return "UNKNOWN"; 31 + } 32 + } 33 + 34 + static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val) 35 + { 36 + switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) { 37 + case 0: 38 + return ETHTOOL_MM_VERIFY_STATUS_INITIAL; 39 + case 1: 40 + return ETHTOOL_MM_VERIFY_STATUS_VERIFYING; 41 + case 2: 42 + return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED; 43 + case 3: 44 + return ETHTOOL_MM_VERIFY_STATUS_FAILED; 45 + case 4: 46 + return ETHTOOL_MM_VERIFY_STATUS_DISABLED; 47 + default: 48 + return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN; 49 + } 50 + } 51 + 52 + void ocelot_port_mm_irq(struct ocelot *ocelot, int port) 53 + { 54 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 55 + struct ocelot_mm_state *mm = &ocelot->mm[port]; 56 + enum ethtool_mm_verify_status verify_status; 57 + u32 val; 58 + 59 + mutex_lock(&mm->lock); 60 + 61 + val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); 62 + 63 + verify_status = ocelot_mm_verify_status(val); 64 + if (mm->verify_status != verify_status) { 65 + dev_dbg(ocelot->dev, 66 + "Port %d MAC Merge verification state %s\n", 67 + port, mm_verify_state_to_string(verify_status)); 68 + mm->verify_status = verify_status; 69 + } 70 + 71 + if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) { 72 + mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS); 73 + 74 + dev_dbg(ocelot->dev, "Port %d TX preemption %s\n", 75 + port, mm->tx_active ? "active" : "inactive"); 76 + } 77 + 78 + if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) { 79 + dev_err(ocelot->dev, 80 + "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n", 81 + port); 82 + } 83 + 84 + if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) { 85 + dev_err(ocelot->dev, 86 + "Unexpected P-frame requested to be transmitted on port %d while verification was unsuccessful or not yet verified, or MM_TX_ENA=0\n", 87 + port); 88 + } 89 + 90 + ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS); 91 + 92 + mutex_unlock(&mm->lock); 93 + } 94 + EXPORT_SYMBOL_GPL(ocelot_port_mm_irq); 95 + 96 + int ocelot_port_set_mm(struct ocelot *ocelot, int port, 97 + struct ethtool_mm_cfg *cfg, 98 + struct netlink_ext_ack *extack) 99 + { 100 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 101 + u32 mm_enable = 0, verify_disable = 0, add_frag_size; 102 + struct ocelot_mm_state *mm; 103 + int err; 104 + 105 + if (!ocelot->mm_supported) 106 + return -EOPNOTSUPP; 107 + 108 + mm = &ocelot->mm[port]; 109 + 110 + err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size, 111 + &add_frag_size, extack); 112 + if (err) 113 + return err; 114 + 115 + if (cfg->pmac_enabled) 116 + mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA; 117 + 118 + if (cfg->tx_enabled) 119 + mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA; 120 + 121 + if (!cfg->verify_enabled) 122 + verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS; 123 + 124 + mutex_lock(&mm->lock); 125 + 126 + ocelot_port_rmwl(ocelot_port, mm_enable, 127 + DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA | 128 + DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA, 129 + DEV_MM_ENABLE_CONFIG); 130 + 131 + ocelot_port_rmwl(ocelot_port, verify_disable | 132 + DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time), 133 + DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS | 134 + DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M, 135 + DEV_MM_VERIF_CONFIG); 136 + 137 + ocelot_rmw_rix(ocelot, 138 + QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size), 139 + QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M, 140 + QSYS_PREEMPTION_CFG, 141 + port); 142 + 143 + mutex_unlock(&mm->lock); 144 + 145 + return 0; 146 + } 147 + EXPORT_SYMBOL_GPL(ocelot_port_set_mm); 148 + 149 + int ocelot_port_get_mm(struct ocelot *ocelot, int port, 150 + struct ethtool_mm_state *state) 151 + { 152 + struct ocelot_port *ocelot_port = ocelot->ports[port]; 153 + struct ocelot_mm_state *mm; 154 + u32 val, add_frag_size; 155 + 156 + if (!ocelot->mm_supported) 157 + return -EOPNOTSUPP; 158 + 159 + mm = &ocelot->mm[port]; 160 + 161 + mutex_lock(&mm->lock); 162 + 163 + val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG); 164 + state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA); 165 + state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA); 166 + 167 + val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG); 168 + state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val); 169 + state->max_verify_time = 128; 170 + 171 + val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); 172 + add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); 173 + state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size); 174 + state->rx_min_frag_size = ETH_ZLEN; 175 + 176 + state->verify_status = mm->verify_status; 177 + state->tx_active = mm->tx_active; 178 + 179 + mutex_unlock(&mm->lock); 180 + 181 + return 0; 182 + } 183 + EXPORT_SYMBOL_GPL(ocelot_port_get_mm); 184 + 185 + int ocelot_mm_init(struct ocelot *ocelot) 186 + { 187 + struct ocelot_port *ocelot_port; 188 + struct ocelot_mm_state *mm; 189 + int port; 190 + 191 + if (!ocelot->mm_supported) 192 + return 0; 193 + 194 + ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports, 195 + sizeof(*ocelot->mm), GFP_KERNEL); 196 + if (!ocelot->mm) 197 + return -ENOMEM; 198 + 199 + for (port = 0; port < ocelot->num_phys_ports; port++) { 200 + u32 val; 201 + 202 + mm = &ocelot->mm[port]; 203 + mutex_init(&mm->lock); 204 + ocelot_port = ocelot->ports[port]; 205 + 206 + /* Update initial status variable for the 207 + * verification state machine 208 + */ 209 + val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); 210 + mm->verify_status = ocelot_mm_verify_status(val); 211 + } 212 + 213 + return 0; 214 + }
+18
include/soc/mscc/ocelot.h
··· 515 515 DEV_MAC_FC_MAC_LOW_CFG, 516 516 DEV_MAC_FC_MAC_HIGH_CFG, 517 517 DEV_MAC_STICKY, 518 + DEV_MM_ENABLE_CONFIG, 519 + DEV_MM_VERIF_CONFIG, 520 + DEV_MM_STATUS, 518 521 PCS1G_CFG, 519 522 PCS1G_MODE_CFG, 520 523 PCS1G_SD_CFG, ··· 742 739 int to; 743 740 }; 744 741 742 + struct ocelot_mm_state { 743 + struct mutex lock; 744 + enum ethtool_mm_verify_status verify_status; 745 + bool tx_active; 746 + }; 747 + 745 748 struct ocelot_port; 746 749 747 750 struct ocelot_port { ··· 872 863 /* Protects the PTP clock */ 873 864 spinlock_t ptp_clock_lock; 874 865 struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM]; 866 + 867 + struct ocelot_mm_state *mm; 875 868 876 869 struct ocelot_fdma *fdma; 877 870 }; ··· 1132 1121 int ocelot_vcap_policer_add(struct ocelot *ocelot, u32 pol_ix, 1133 1122 struct ocelot_policer *pol); 1134 1123 int ocelot_vcap_policer_del(struct ocelot *ocelot, u32 pol_ix); 1124 + 1125 + void ocelot_port_mm_irq(struct ocelot *ocelot, int port); 1126 + int ocelot_port_set_mm(struct ocelot *ocelot, int port, 1127 + struct ethtool_mm_cfg *cfg, 1128 + struct netlink_ext_ack *extack); 1129 + int ocelot_port_get_mm(struct ocelot *ocelot, int port, 1130 + struct ethtool_mm_state *state); 1135 1131 1136 1132 #if IS_ENABLED(CONFIG_BRIDGE_MRP) 1137 1133 int ocelot_mrp_add(struct ocelot *ocelot, int port,
+23
include/soc/mscc/ocelot_dev.h
··· 93 93 #define DEV_MAC_STICKY_TX_FRM_LEN_OVR_STICKY BIT(1) 94 94 #define DEV_MAC_STICKY_TX_ABORT_STICKY BIT(0) 95 95 96 + #define DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA BIT(0) 97 + #define DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA BIT(4) 98 + #define DEV_MM_CONFIG_ENABLE_CONFIG_KEEP_S_AFTER_D BIT(8) 99 + 100 + #define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS BIT(0) 101 + #define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(x) (((x) << 4) & GENMASK(11, 4)) 102 + #define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M GENMASK(11, 4) 103 + #define DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(x) (((x) & GENMASK(11, 4)) >> 4) 104 + #define DEV_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS(x) (((x) << 12) & GENMASK(13, 12)) 105 + #define DEV_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS_M GENMASK(13, 12) 106 + #define DEV_MM_CONFIG_VERIF_CONFIG_VERIF_TIMER_UNITS_X(x) (((x) & GENMASK(13, 12)) >> 12) 107 + 108 + #define DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS BIT(0) 109 + #define DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY BIT(4) 110 + #define DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE(x) (((x) << 8) & GENMASK(10, 8)) 111 + #define DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_M GENMASK(10, 8) 112 + #define DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(x) (((x) & GENMASK(10, 8)) >> 8) 113 + #define DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY BIT(12) 114 + #define DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY BIT(16) 115 + #define DEV_MM_STAT_MM_STATUS_MM_RX_FRAME_STATUS BIT(20) 116 + #define DEV_MM_STAT_MM_STATUS_MM_TX_FRAME_STATUS BIT(24) 117 + #define DEV_MM_STAT_MM_STATUS_MM_TX_PRMPT_STATUS BIT(28) 118 + 96 119 #define PCS1G_CFG_LINK_STATUS_TYPE BIT(4) 97 120 #define PCS1G_CFG_AN_LINK_CTRL_ENA BIT(1) 98 121 #define PCS1G_CFG_PCS_ENA BIT(0)