1*6505b680SVladimir Oltean // SPDX-License-Identifier: (GPL-2.0 OR MIT) 2*6505b680SVladimir Oltean /* 3*6505b680SVladimir Oltean * Hardware library for MAC Merge Layer and Frame Preemption on TSN-capable 4*6505b680SVladimir Oltean * switches (VSC9959) 5*6505b680SVladimir Oltean * 6*6505b680SVladimir Oltean * Copyright 2022-2023 NXP 7*6505b680SVladimir Oltean */ 8*6505b680SVladimir Oltean #include <linux/ethtool.h> 9*6505b680SVladimir Oltean #include <soc/mscc/ocelot.h> 10*6505b680SVladimir Oltean #include <soc/mscc/ocelot_dev.h> 11*6505b680SVladimir Oltean #include <soc/mscc/ocelot_qsys.h> 12*6505b680SVladimir Oltean 13*6505b680SVladimir Oltean #include "ocelot.h" 14*6505b680SVladimir Oltean 15*6505b680SVladimir Oltean static const char * 16*6505b680SVladimir Oltean mm_verify_state_to_string(enum ethtool_mm_verify_status state) 17*6505b680SVladimir Oltean { 18*6505b680SVladimir Oltean switch (state) { 19*6505b680SVladimir Oltean case ETHTOOL_MM_VERIFY_STATUS_INITIAL: 20*6505b680SVladimir Oltean return "INITIAL"; 21*6505b680SVladimir Oltean case ETHTOOL_MM_VERIFY_STATUS_VERIFYING: 22*6505b680SVladimir Oltean return "VERIFYING"; 23*6505b680SVladimir Oltean case ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED: 24*6505b680SVladimir Oltean return "SUCCEEDED"; 25*6505b680SVladimir Oltean case ETHTOOL_MM_VERIFY_STATUS_FAILED: 26*6505b680SVladimir Oltean return "FAILED"; 27*6505b680SVladimir Oltean case ETHTOOL_MM_VERIFY_STATUS_DISABLED: 28*6505b680SVladimir Oltean return "DISABLED"; 29*6505b680SVladimir Oltean default: 30*6505b680SVladimir Oltean return "UNKNOWN"; 31*6505b680SVladimir Oltean } 32*6505b680SVladimir Oltean } 33*6505b680SVladimir Oltean 34*6505b680SVladimir Oltean static enum ethtool_mm_verify_status ocelot_mm_verify_status(u32 val) 35*6505b680SVladimir Oltean { 36*6505b680SVladimir Oltean switch (DEV_MM_STAT_MM_STATUS_PRMPT_VERIFY_STATE_X(val)) { 37*6505b680SVladimir Oltean case 0: 38*6505b680SVladimir Oltean return ETHTOOL_MM_VERIFY_STATUS_INITIAL; 39*6505b680SVladimir Oltean case 1: 40*6505b680SVladimir Oltean return ETHTOOL_MM_VERIFY_STATUS_VERIFYING; 41*6505b680SVladimir Oltean case 2: 42*6505b680SVladimir Oltean return ETHTOOL_MM_VERIFY_STATUS_SUCCEEDED; 43*6505b680SVladimir Oltean case 3: 44*6505b680SVladimir Oltean return ETHTOOL_MM_VERIFY_STATUS_FAILED; 45*6505b680SVladimir Oltean case 4: 46*6505b680SVladimir Oltean return ETHTOOL_MM_VERIFY_STATUS_DISABLED; 47*6505b680SVladimir Oltean default: 48*6505b680SVladimir Oltean return ETHTOOL_MM_VERIFY_STATUS_UNKNOWN; 49*6505b680SVladimir Oltean } 50*6505b680SVladimir Oltean } 51*6505b680SVladimir Oltean 52*6505b680SVladimir Oltean void ocelot_port_mm_irq(struct ocelot *ocelot, int port) 53*6505b680SVladimir Oltean { 54*6505b680SVladimir Oltean struct ocelot_port *ocelot_port = ocelot->ports[port]; 55*6505b680SVladimir Oltean struct ocelot_mm_state *mm = &ocelot->mm[port]; 56*6505b680SVladimir Oltean enum ethtool_mm_verify_status verify_status; 57*6505b680SVladimir Oltean u32 val; 58*6505b680SVladimir Oltean 59*6505b680SVladimir Oltean mutex_lock(&mm->lock); 60*6505b680SVladimir Oltean 61*6505b680SVladimir Oltean val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); 62*6505b680SVladimir Oltean 63*6505b680SVladimir Oltean verify_status = ocelot_mm_verify_status(val); 64*6505b680SVladimir Oltean if (mm->verify_status != verify_status) { 65*6505b680SVladimir Oltean dev_dbg(ocelot->dev, 66*6505b680SVladimir Oltean "Port %d MAC Merge verification state %s\n", 67*6505b680SVladimir Oltean port, mm_verify_state_to_string(verify_status)); 68*6505b680SVladimir Oltean mm->verify_status = verify_status; 69*6505b680SVladimir Oltean } 70*6505b680SVladimir Oltean 71*6505b680SVladimir Oltean if (val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STICKY) { 72*6505b680SVladimir Oltean mm->tx_active = !!(val & DEV_MM_STAT_MM_STATUS_PRMPT_ACTIVE_STATUS); 73*6505b680SVladimir Oltean 74*6505b680SVladimir Oltean dev_dbg(ocelot->dev, "Port %d TX preemption %s\n", 75*6505b680SVladimir Oltean port, mm->tx_active ? "active" : "inactive"); 76*6505b680SVladimir Oltean } 77*6505b680SVladimir Oltean 78*6505b680SVladimir Oltean if (val & DEV_MM_STAT_MM_STATUS_UNEXP_RX_PFRM_STICKY) { 79*6505b680SVladimir Oltean dev_err(ocelot->dev, 80*6505b680SVladimir Oltean "Unexpected P-frame received on port %d while verification was unsuccessful or not yet verified\n", 81*6505b680SVladimir Oltean port); 82*6505b680SVladimir Oltean } 83*6505b680SVladimir Oltean 84*6505b680SVladimir Oltean if (val & DEV_MM_STAT_MM_STATUS_UNEXP_TX_PFRM_STICKY) { 85*6505b680SVladimir Oltean dev_err(ocelot->dev, 86*6505b680SVladimir Oltean "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*6505b680SVladimir Oltean port); 88*6505b680SVladimir Oltean } 89*6505b680SVladimir Oltean 90*6505b680SVladimir Oltean ocelot_port_writel(ocelot_port, val, DEV_MM_STATUS); 91*6505b680SVladimir Oltean 92*6505b680SVladimir Oltean mutex_unlock(&mm->lock); 93*6505b680SVladimir Oltean } 94*6505b680SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_mm_irq); 95*6505b680SVladimir Oltean 96*6505b680SVladimir Oltean int ocelot_port_set_mm(struct ocelot *ocelot, int port, 97*6505b680SVladimir Oltean struct ethtool_mm_cfg *cfg, 98*6505b680SVladimir Oltean struct netlink_ext_ack *extack) 99*6505b680SVladimir Oltean { 100*6505b680SVladimir Oltean struct ocelot_port *ocelot_port = ocelot->ports[port]; 101*6505b680SVladimir Oltean u32 mm_enable = 0, verify_disable = 0, add_frag_size; 102*6505b680SVladimir Oltean struct ocelot_mm_state *mm; 103*6505b680SVladimir Oltean int err; 104*6505b680SVladimir Oltean 105*6505b680SVladimir Oltean if (!ocelot->mm_supported) 106*6505b680SVladimir Oltean return -EOPNOTSUPP; 107*6505b680SVladimir Oltean 108*6505b680SVladimir Oltean mm = &ocelot->mm[port]; 109*6505b680SVladimir Oltean 110*6505b680SVladimir Oltean err = ethtool_mm_frag_size_min_to_add(cfg->tx_min_frag_size, 111*6505b680SVladimir Oltean &add_frag_size, extack); 112*6505b680SVladimir Oltean if (err) 113*6505b680SVladimir Oltean return err; 114*6505b680SVladimir Oltean 115*6505b680SVladimir Oltean if (cfg->pmac_enabled) 116*6505b680SVladimir Oltean mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA; 117*6505b680SVladimir Oltean 118*6505b680SVladimir Oltean if (cfg->tx_enabled) 119*6505b680SVladimir Oltean mm_enable |= DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA; 120*6505b680SVladimir Oltean 121*6505b680SVladimir Oltean if (!cfg->verify_enabled) 122*6505b680SVladimir Oltean verify_disable = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS; 123*6505b680SVladimir Oltean 124*6505b680SVladimir Oltean mutex_lock(&mm->lock); 125*6505b680SVladimir Oltean 126*6505b680SVladimir Oltean ocelot_port_rmwl(ocelot_port, mm_enable, 127*6505b680SVladimir Oltean DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA | 128*6505b680SVladimir Oltean DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA, 129*6505b680SVladimir Oltean DEV_MM_ENABLE_CONFIG); 130*6505b680SVladimir Oltean 131*6505b680SVladimir Oltean ocelot_port_rmwl(ocelot_port, verify_disable | 132*6505b680SVladimir Oltean DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME(cfg->verify_time), 133*6505b680SVladimir Oltean DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_DIS | 134*6505b680SVladimir Oltean DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_M, 135*6505b680SVladimir Oltean DEV_MM_VERIF_CONFIG); 136*6505b680SVladimir Oltean 137*6505b680SVladimir Oltean ocelot_rmw_rix(ocelot, 138*6505b680SVladimir Oltean QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE(add_frag_size), 139*6505b680SVladimir Oltean QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_M, 140*6505b680SVladimir Oltean QSYS_PREEMPTION_CFG, 141*6505b680SVladimir Oltean port); 142*6505b680SVladimir Oltean 143*6505b680SVladimir Oltean mutex_unlock(&mm->lock); 144*6505b680SVladimir Oltean 145*6505b680SVladimir Oltean return 0; 146*6505b680SVladimir Oltean } 147*6505b680SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_set_mm); 148*6505b680SVladimir Oltean 149*6505b680SVladimir Oltean int ocelot_port_get_mm(struct ocelot *ocelot, int port, 150*6505b680SVladimir Oltean struct ethtool_mm_state *state) 151*6505b680SVladimir Oltean { 152*6505b680SVladimir Oltean struct ocelot_port *ocelot_port = ocelot->ports[port]; 153*6505b680SVladimir Oltean struct ocelot_mm_state *mm; 154*6505b680SVladimir Oltean u32 val, add_frag_size; 155*6505b680SVladimir Oltean 156*6505b680SVladimir Oltean if (!ocelot->mm_supported) 157*6505b680SVladimir Oltean return -EOPNOTSUPP; 158*6505b680SVladimir Oltean 159*6505b680SVladimir Oltean mm = &ocelot->mm[port]; 160*6505b680SVladimir Oltean 161*6505b680SVladimir Oltean mutex_lock(&mm->lock); 162*6505b680SVladimir Oltean 163*6505b680SVladimir Oltean val = ocelot_port_readl(ocelot_port, DEV_MM_ENABLE_CONFIG); 164*6505b680SVladimir Oltean state->pmac_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_RX_ENA); 165*6505b680SVladimir Oltean state->tx_enabled = !!(val & DEV_MM_CONFIG_ENABLE_CONFIG_MM_TX_ENA); 166*6505b680SVladimir Oltean 167*6505b680SVladimir Oltean val = ocelot_port_readl(ocelot_port, DEV_MM_VERIF_CONFIG); 168*6505b680SVladimir Oltean state->verify_time = DEV_MM_CONFIG_VERIF_CONFIG_PRM_VERIFY_TIME_X(val); 169*6505b680SVladimir Oltean state->max_verify_time = 128; 170*6505b680SVladimir Oltean 171*6505b680SVladimir Oltean val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); 172*6505b680SVladimir Oltean add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); 173*6505b680SVladimir Oltean state->tx_min_frag_size = ethtool_mm_frag_size_add_to_min(add_frag_size); 174*6505b680SVladimir Oltean state->rx_min_frag_size = ETH_ZLEN; 175*6505b680SVladimir Oltean 176*6505b680SVladimir Oltean state->verify_status = mm->verify_status; 177*6505b680SVladimir Oltean state->tx_active = mm->tx_active; 178*6505b680SVladimir Oltean 179*6505b680SVladimir Oltean mutex_unlock(&mm->lock); 180*6505b680SVladimir Oltean 181*6505b680SVladimir Oltean return 0; 182*6505b680SVladimir Oltean } 183*6505b680SVladimir Oltean EXPORT_SYMBOL_GPL(ocelot_port_get_mm); 184*6505b680SVladimir Oltean 185*6505b680SVladimir Oltean int ocelot_mm_init(struct ocelot *ocelot) 186*6505b680SVladimir Oltean { 187*6505b680SVladimir Oltean struct ocelot_port *ocelot_port; 188*6505b680SVladimir Oltean struct ocelot_mm_state *mm; 189*6505b680SVladimir Oltean int port; 190*6505b680SVladimir Oltean 191*6505b680SVladimir Oltean if (!ocelot->mm_supported) 192*6505b680SVladimir Oltean return 0; 193*6505b680SVladimir Oltean 194*6505b680SVladimir Oltean ocelot->mm = devm_kcalloc(ocelot->dev, ocelot->num_phys_ports, 195*6505b680SVladimir Oltean sizeof(*ocelot->mm), GFP_KERNEL); 196*6505b680SVladimir Oltean if (!ocelot->mm) 197*6505b680SVladimir Oltean return -ENOMEM; 198*6505b680SVladimir Oltean 199*6505b680SVladimir Oltean for (port = 0; port < ocelot->num_phys_ports; port++) { 200*6505b680SVladimir Oltean u32 val; 201*6505b680SVladimir Oltean 202*6505b680SVladimir Oltean mm = &ocelot->mm[port]; 203*6505b680SVladimir Oltean mutex_init(&mm->lock); 204*6505b680SVladimir Oltean ocelot_port = ocelot->ports[port]; 205*6505b680SVladimir Oltean 206*6505b680SVladimir Oltean /* Update initial status variable for the 207*6505b680SVladimir Oltean * verification state machine 208*6505b680SVladimir Oltean */ 209*6505b680SVladimir Oltean val = ocelot_port_readl(ocelot_port, DEV_MM_STATUS); 210*6505b680SVladimir Oltean mm->verify_status = ocelot_mm_verify_status(val); 211*6505b680SVladimir Oltean } 212*6505b680SVladimir Oltean 213*6505b680SVladimir Oltean return 0; 214*6505b680SVladimir Oltean } 215