1*d0ce9fd7SAlexander Duyck // SPDX-License-Identifier: GPL-2.0 2*d0ce9fd7SAlexander Duyck /* Copyright (c) Meta Platforms, Inc. and affiliates. */ 3*d0ce9fd7SAlexander Duyck 4*d0ce9fd7SAlexander Duyck #include <linux/mdio.h> 5*d0ce9fd7SAlexander Duyck #include <linux/pcs/pcs-xpcs.h> 6*d0ce9fd7SAlexander Duyck 7*d0ce9fd7SAlexander Duyck #include "fbnic.h" 8*d0ce9fd7SAlexander Duyck #include "fbnic_netdev.h" 9*d0ce9fd7SAlexander Duyck 10*d0ce9fd7SAlexander Duyck #define DW_VENDOR BIT(15) 11*d0ce9fd7SAlexander Duyck #define FBNIC_PCS_VENDOR BIT(9) 12*d0ce9fd7SAlexander Duyck #define FBNIC_PCS_ZERO_MASK (DW_VENDOR - FBNIC_PCS_VENDOR) 13*d0ce9fd7SAlexander Duyck 14*d0ce9fd7SAlexander Duyck static int 15*d0ce9fd7SAlexander Duyck fbnic_mdio_read_pmd(struct fbnic_dev *fbd, int addr, int regnum) 16*d0ce9fd7SAlexander Duyck { 17*d0ce9fd7SAlexander Duyck u8 aui = FBNIC_AUI_UNKNOWN; 18*d0ce9fd7SAlexander Duyck struct fbnic_net *fbn; 19*d0ce9fd7SAlexander Duyck int ret = 0; 20*d0ce9fd7SAlexander Duyck 21*d0ce9fd7SAlexander Duyck /* We don't need a second PMD, just one can handle both lanes */ 22*d0ce9fd7SAlexander Duyck if (addr) 23*d0ce9fd7SAlexander Duyck return 0; 24*d0ce9fd7SAlexander Duyck 25*d0ce9fd7SAlexander Duyck if (fbd->netdev) { 26*d0ce9fd7SAlexander Duyck fbn = netdev_priv(fbd->netdev); 27*d0ce9fd7SAlexander Duyck if (fbn->aui < FBNIC_AUI_UNKNOWN) 28*d0ce9fd7SAlexander Duyck aui = fbn->aui; 29*d0ce9fd7SAlexander Duyck } 30*d0ce9fd7SAlexander Duyck 31*d0ce9fd7SAlexander Duyck switch (regnum) { 32*d0ce9fd7SAlexander Duyck case MDIO_DEVID1: 33*d0ce9fd7SAlexander Duyck ret = MP_FBNIC_XPCS_PMA_100G_ID >> 16; 34*d0ce9fd7SAlexander Duyck break; 35*d0ce9fd7SAlexander Duyck case MDIO_DEVID2: 36*d0ce9fd7SAlexander Duyck ret = MP_FBNIC_XPCS_PMA_100G_ID & 0xffff; 37*d0ce9fd7SAlexander Duyck break; 38*d0ce9fd7SAlexander Duyck case MDIO_DEVS1: 39*d0ce9fd7SAlexander Duyck ret = MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; 40*d0ce9fd7SAlexander Duyck break; 41*d0ce9fd7SAlexander Duyck case MDIO_STAT2: 42*d0ce9fd7SAlexander Duyck ret = MDIO_STAT2_DEVPRST_VAL; 43*d0ce9fd7SAlexander Duyck break; 44*d0ce9fd7SAlexander Duyck case MDIO_PMA_RXDET: 45*d0ce9fd7SAlexander Duyck /* If training isn't complete default to 0 */ 46*d0ce9fd7SAlexander Duyck if (fbd->pmd_state != FBNIC_PMD_SEND_DATA) 47*d0ce9fd7SAlexander Duyck break; 48*d0ce9fd7SAlexander Duyck /* Report either 1 or 2 lanes detected depending on config */ 49*d0ce9fd7SAlexander Duyck ret = (MDIO_PMD_RXDET_GLOBAL | MDIO_PMD_RXDET_0) | 50*d0ce9fd7SAlexander Duyck ((aui & FBNIC_AUI_MODE_R2) * 51*d0ce9fd7SAlexander Duyck (MDIO_PMD_RXDET_1 / FBNIC_AUI_MODE_R2)); 52*d0ce9fd7SAlexander Duyck break; 53*d0ce9fd7SAlexander Duyck default: 54*d0ce9fd7SAlexander Duyck break; 55*d0ce9fd7SAlexander Duyck } 56*d0ce9fd7SAlexander Duyck 57*d0ce9fd7SAlexander Duyck dev_dbg(fbd->dev, 58*d0ce9fd7SAlexander Duyck "SWMII PMD Rd: Addr: %d RegNum: %d Value: 0x%04x\n", 59*d0ce9fd7SAlexander Duyck addr, regnum, ret); 60*d0ce9fd7SAlexander Duyck 61*d0ce9fd7SAlexander Duyck return ret; 62*d0ce9fd7SAlexander Duyck } 63*d0ce9fd7SAlexander Duyck 64*d0ce9fd7SAlexander Duyck static int 65*d0ce9fd7SAlexander Duyck fbnic_mdio_read_pcs(struct fbnic_dev *fbd, int addr, int regnum) 66*d0ce9fd7SAlexander Duyck { 67*d0ce9fd7SAlexander Duyck int ret, offset = 0; 68*d0ce9fd7SAlexander Duyck 69*d0ce9fd7SAlexander Duyck /* We will need access to both PCS instances to get config info */ 70*d0ce9fd7SAlexander Duyck if (addr >= 2) 71*d0ce9fd7SAlexander Duyck return 0; 72*d0ce9fd7SAlexander Duyck 73*d0ce9fd7SAlexander Duyck /* Report 0 for reserved registers */ 74*d0ce9fd7SAlexander Duyck if (regnum & FBNIC_PCS_ZERO_MASK) 75*d0ce9fd7SAlexander Duyck return 0; 76*d0ce9fd7SAlexander Duyck 77*d0ce9fd7SAlexander Duyck /* Intercept and return correct ID for PCS */ 78*d0ce9fd7SAlexander Duyck if (regnum == MDIO_DEVID1) 79*d0ce9fd7SAlexander Duyck return DW_XPCS_ID >> 16; 80*d0ce9fd7SAlexander Duyck if (regnum == MDIO_DEVID2) 81*d0ce9fd7SAlexander Duyck return DW_XPCS_ID & 0xffff; 82*d0ce9fd7SAlexander Duyck if (regnum == MDIO_DEVS1) 83*d0ce9fd7SAlexander Duyck return MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS; 84*d0ce9fd7SAlexander Duyck 85*d0ce9fd7SAlexander Duyck /* Swap vendor page bit for FBNIC PCS vendor page bit */ 86*d0ce9fd7SAlexander Duyck if (regnum & DW_VENDOR) 87*d0ce9fd7SAlexander Duyck offset ^= DW_VENDOR | FBNIC_PCS_VENDOR; 88*d0ce9fd7SAlexander Duyck 89*d0ce9fd7SAlexander Duyck ret = fbnic_rd32(fbd, FBNIC_PCS_PAGE(addr) + (regnum ^ offset)); 90*d0ce9fd7SAlexander Duyck 91*d0ce9fd7SAlexander Duyck dev_dbg(fbd->dev, 92*d0ce9fd7SAlexander Duyck "SWMII PCS Rd: Addr: %d RegNum: %d Value: 0x%04x\n", 93*d0ce9fd7SAlexander Duyck addr, regnum, ret); 94*d0ce9fd7SAlexander Duyck 95*d0ce9fd7SAlexander Duyck return ret; 96*d0ce9fd7SAlexander Duyck } 97*d0ce9fd7SAlexander Duyck 98*d0ce9fd7SAlexander Duyck static int 99*d0ce9fd7SAlexander Duyck fbnic_mdio_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum) 100*d0ce9fd7SAlexander Duyck { 101*d0ce9fd7SAlexander Duyck struct fbnic_dev *fbd = bus->priv; 102*d0ce9fd7SAlexander Duyck 103*d0ce9fd7SAlexander Duyck if (devnum == MDIO_MMD_PMAPMD) 104*d0ce9fd7SAlexander Duyck return fbnic_mdio_read_pmd(fbd, addr, regnum); 105*d0ce9fd7SAlexander Duyck 106*d0ce9fd7SAlexander Duyck if (devnum == MDIO_MMD_PCS) 107*d0ce9fd7SAlexander Duyck return fbnic_mdio_read_pcs(fbd, addr, regnum); 108*d0ce9fd7SAlexander Duyck 109*d0ce9fd7SAlexander Duyck return 0; 110*d0ce9fd7SAlexander Duyck } 111*d0ce9fd7SAlexander Duyck 112*d0ce9fd7SAlexander Duyck static void 113*d0ce9fd7SAlexander Duyck fbnic_mdio_write_pmd(struct fbnic_dev *fbd, int addr, int regnum, u16 val) 114*d0ce9fd7SAlexander Duyck { 115*d0ce9fd7SAlexander Duyck dev_dbg(fbd->dev, 116*d0ce9fd7SAlexander Duyck "SWMII PMD Wr: Addr: %d RegNum: %d Value: 0x%04x\n", 117*d0ce9fd7SAlexander Duyck addr, regnum, val); 118*d0ce9fd7SAlexander Duyck } 119*d0ce9fd7SAlexander Duyck 120*d0ce9fd7SAlexander Duyck static void 121*d0ce9fd7SAlexander Duyck fbnic_mdio_write_pcs(struct fbnic_dev *fbd, int addr, int regnum, u16 val) 122*d0ce9fd7SAlexander Duyck { 123*d0ce9fd7SAlexander Duyck dev_dbg(fbd->dev, 124*d0ce9fd7SAlexander Duyck "SWMII PCS Wr: Addr: %d RegNum: %d Value: 0x%04x\n", 125*d0ce9fd7SAlexander Duyck addr, regnum, val); 126*d0ce9fd7SAlexander Duyck 127*d0ce9fd7SAlexander Duyck /* Allow access to both halves of PCS for 50R2 config */ 128*d0ce9fd7SAlexander Duyck if (addr > 2) 129*d0ce9fd7SAlexander Duyck return; 130*d0ce9fd7SAlexander Duyck 131*d0ce9fd7SAlexander Duyck /* Skip write for reserved registers */ 132*d0ce9fd7SAlexander Duyck if (regnum & FBNIC_PCS_ZERO_MASK) 133*d0ce9fd7SAlexander Duyck return; 134*d0ce9fd7SAlexander Duyck 135*d0ce9fd7SAlexander Duyck /* Swap vendor page bit for FBNIC PCS vendor page bit */ 136*d0ce9fd7SAlexander Duyck if (regnum & DW_VENDOR) 137*d0ce9fd7SAlexander Duyck regnum ^= DW_VENDOR | FBNIC_PCS_VENDOR; 138*d0ce9fd7SAlexander Duyck 139*d0ce9fd7SAlexander Duyck fbnic_wr32(fbd, FBNIC_PCS_PAGE(addr) + regnum, val); 140*d0ce9fd7SAlexander Duyck } 141*d0ce9fd7SAlexander Duyck 142*d0ce9fd7SAlexander Duyck static int 143*d0ce9fd7SAlexander Duyck fbnic_mdio_write_c45(struct mii_bus *bus, int addr, int devnum, 144*d0ce9fd7SAlexander Duyck int regnum, u16 val) 145*d0ce9fd7SAlexander Duyck { 146*d0ce9fd7SAlexander Duyck struct fbnic_dev *fbd = bus->priv; 147*d0ce9fd7SAlexander Duyck 148*d0ce9fd7SAlexander Duyck if (devnum == MDIO_MMD_PMAPMD) 149*d0ce9fd7SAlexander Duyck fbnic_mdio_write_pmd(fbd, addr, regnum, val); 150*d0ce9fd7SAlexander Duyck 151*d0ce9fd7SAlexander Duyck if (devnum == MDIO_MMD_PCS) 152*d0ce9fd7SAlexander Duyck fbnic_mdio_write_pcs(fbd, addr, regnum, val); 153*d0ce9fd7SAlexander Duyck 154*d0ce9fd7SAlexander Duyck return 0; 155*d0ce9fd7SAlexander Duyck } 156*d0ce9fd7SAlexander Duyck 157*d0ce9fd7SAlexander Duyck /** 158*d0ce9fd7SAlexander Duyck * fbnic_mdiobus_create - Create an MDIO bus to allow interfacing w/ PHYs 159*d0ce9fd7SAlexander Duyck * @fbd: Pointer to FBNIC device structure to populate bus on 160*d0ce9fd7SAlexander Duyck * 161*d0ce9fd7SAlexander Duyck * Initialize an MDIO bus and place a pointer to it on the fbd struct. This bus 162*d0ce9fd7SAlexander Duyck * will be used to interface with the PMA/PMD and PCS. 163*d0ce9fd7SAlexander Duyck * 164*d0ce9fd7SAlexander Duyck * Return: 0 on success, negative on failure 165*d0ce9fd7SAlexander Duyck **/ 166*d0ce9fd7SAlexander Duyck int fbnic_mdiobus_create(struct fbnic_dev *fbd) 167*d0ce9fd7SAlexander Duyck { 168*d0ce9fd7SAlexander Duyck struct mii_bus *bus; 169*d0ce9fd7SAlexander Duyck int err; 170*d0ce9fd7SAlexander Duyck 171*d0ce9fd7SAlexander Duyck bus = devm_mdiobus_alloc(fbd->dev); 172*d0ce9fd7SAlexander Duyck if (!bus) 173*d0ce9fd7SAlexander Duyck return -ENOMEM; 174*d0ce9fd7SAlexander Duyck 175*d0ce9fd7SAlexander Duyck bus->name = "fbnic_mii_bus"; 176*d0ce9fd7SAlexander Duyck bus->read_c45 = &fbnic_mdio_read_c45; 177*d0ce9fd7SAlexander Duyck bus->write_c45 = &fbnic_mdio_write_c45; 178*d0ce9fd7SAlexander Duyck 179*d0ce9fd7SAlexander Duyck /* Disable PHY auto probing. We will add PCS manually */ 180*d0ce9fd7SAlexander Duyck bus->phy_mask = ~0; 181*d0ce9fd7SAlexander Duyck 182*d0ce9fd7SAlexander Duyck bus->parent = fbd->dev; 183*d0ce9fd7SAlexander Duyck bus->priv = fbd; 184*d0ce9fd7SAlexander Duyck snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(fbd->dev)); 185*d0ce9fd7SAlexander Duyck 186*d0ce9fd7SAlexander Duyck err = devm_mdiobus_register(fbd->dev, bus); 187*d0ce9fd7SAlexander Duyck if (err) { 188*d0ce9fd7SAlexander Duyck dev_err(fbd->dev, "Failed to create MDIO bus: %d\n", err); 189*d0ce9fd7SAlexander Duyck return err; 190*d0ce9fd7SAlexander Duyck } 191*d0ce9fd7SAlexander Duyck 192*d0ce9fd7SAlexander Duyck fbd->mdio_bus = bus; 193*d0ce9fd7SAlexander Duyck 194*d0ce9fd7SAlexander Duyck return 0; 195*d0ce9fd7SAlexander Duyck } 196