1*5a8f0974SVladimir Oltean // SPDX-License-Identifier: GPL-2.0 2*5a8f0974SVladimir Oltean /* Copyright 2021, NXP Semiconductors 3*5a8f0974SVladimir Oltean */ 4*5a8f0974SVladimir Oltean #include <linux/of_mdio.h> 5*5a8f0974SVladimir Oltean #include "sja1105.h" 6*5a8f0974SVladimir Oltean 7*5a8f0974SVladimir Oltean enum sja1105_mdio_opcode { 8*5a8f0974SVladimir Oltean SJA1105_C45_ADDR = 0, 9*5a8f0974SVladimir Oltean SJA1105_C22 = 1, 10*5a8f0974SVladimir Oltean SJA1105_C45_DATA = 2, 11*5a8f0974SVladimir Oltean SJA1105_C45_DATA_AUTOINC = 3, 12*5a8f0974SVladimir Oltean }; 13*5a8f0974SVladimir Oltean 14*5a8f0974SVladimir Oltean static u64 sja1105_base_t1_encode_addr(struct sja1105_private *priv, 15*5a8f0974SVladimir Oltean int phy, enum sja1105_mdio_opcode op, 16*5a8f0974SVladimir Oltean int xad) 17*5a8f0974SVladimir Oltean { 18*5a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 19*5a8f0974SVladimir Oltean 20*5a8f0974SVladimir Oltean return regs->mdio_100base_t1 | (phy << 7) | (op << 5) | (xad << 0); 21*5a8f0974SVladimir Oltean } 22*5a8f0974SVladimir Oltean 23*5a8f0974SVladimir Oltean static int sja1105_base_t1_mdio_read(struct mii_bus *bus, int phy, int reg) 24*5a8f0974SVladimir Oltean { 25*5a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 26*5a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 27*5a8f0974SVladimir Oltean u64 addr; 28*5a8f0974SVladimir Oltean u32 tmp; 29*5a8f0974SVladimir Oltean int rc; 30*5a8f0974SVladimir Oltean 31*5a8f0974SVladimir Oltean if (reg & MII_ADDR_C45) { 32*5a8f0974SVladimir Oltean u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f; 33*5a8f0974SVladimir Oltean 34*5a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR, 35*5a8f0974SVladimir Oltean mmd); 36*5a8f0974SVladimir Oltean 37*5a8f0974SVladimir Oltean tmp = reg & MII_REGADDR_C45_MASK; 38*5a8f0974SVladimir Oltean 39*5a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 40*5a8f0974SVladimir Oltean if (rc < 0) 41*5a8f0974SVladimir Oltean return rc; 42*5a8f0974SVladimir Oltean 43*5a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA, 44*5a8f0974SVladimir Oltean mmd); 45*5a8f0974SVladimir Oltean 46*5a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL); 47*5a8f0974SVladimir Oltean if (rc < 0) 48*5a8f0974SVladimir Oltean return rc; 49*5a8f0974SVladimir Oltean 50*5a8f0974SVladimir Oltean return tmp & 0xffff; 51*5a8f0974SVladimir Oltean } 52*5a8f0974SVladimir Oltean 53*5a8f0974SVladimir Oltean /* Clause 22 read */ 54*5a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f); 55*5a8f0974SVladimir Oltean 56*5a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL); 57*5a8f0974SVladimir Oltean if (rc < 0) 58*5a8f0974SVladimir Oltean return rc; 59*5a8f0974SVladimir Oltean 60*5a8f0974SVladimir Oltean return tmp & 0xffff; 61*5a8f0974SVladimir Oltean } 62*5a8f0974SVladimir Oltean 63*5a8f0974SVladimir Oltean static int sja1105_base_t1_mdio_write(struct mii_bus *bus, int phy, int reg, 64*5a8f0974SVladimir Oltean u16 val) 65*5a8f0974SVladimir Oltean { 66*5a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 67*5a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 68*5a8f0974SVladimir Oltean u64 addr; 69*5a8f0974SVladimir Oltean u32 tmp; 70*5a8f0974SVladimir Oltean int rc; 71*5a8f0974SVladimir Oltean 72*5a8f0974SVladimir Oltean if (reg & MII_ADDR_C45) { 73*5a8f0974SVladimir Oltean u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f; 74*5a8f0974SVladimir Oltean 75*5a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR, 76*5a8f0974SVladimir Oltean mmd); 77*5a8f0974SVladimir Oltean 78*5a8f0974SVladimir Oltean tmp = reg & MII_REGADDR_C45_MASK; 79*5a8f0974SVladimir Oltean 80*5a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 81*5a8f0974SVladimir Oltean if (rc < 0) 82*5a8f0974SVladimir Oltean return rc; 83*5a8f0974SVladimir Oltean 84*5a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA, 85*5a8f0974SVladimir Oltean mmd); 86*5a8f0974SVladimir Oltean 87*5a8f0974SVladimir Oltean tmp = val & 0xffff; 88*5a8f0974SVladimir Oltean 89*5a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 90*5a8f0974SVladimir Oltean if (rc < 0) 91*5a8f0974SVladimir Oltean return rc; 92*5a8f0974SVladimir Oltean 93*5a8f0974SVladimir Oltean return 0; 94*5a8f0974SVladimir Oltean } 95*5a8f0974SVladimir Oltean 96*5a8f0974SVladimir Oltean /* Clause 22 write */ 97*5a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f); 98*5a8f0974SVladimir Oltean 99*5a8f0974SVladimir Oltean tmp = val & 0xffff; 100*5a8f0974SVladimir Oltean 101*5a8f0974SVladimir Oltean return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 102*5a8f0974SVladimir Oltean } 103*5a8f0974SVladimir Oltean 104*5a8f0974SVladimir Oltean static int sja1105_base_tx_mdio_read(struct mii_bus *bus, int phy, int reg) 105*5a8f0974SVladimir Oltean { 106*5a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 107*5a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 108*5a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 109*5a8f0974SVladimir Oltean u32 tmp; 110*5a8f0974SVladimir Oltean int rc; 111*5a8f0974SVladimir Oltean 112*5a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_READ, regs->mdio_100base_tx + reg, 113*5a8f0974SVladimir Oltean &tmp, NULL); 114*5a8f0974SVladimir Oltean if (rc < 0) 115*5a8f0974SVladimir Oltean return rc; 116*5a8f0974SVladimir Oltean 117*5a8f0974SVladimir Oltean return tmp & 0xffff; 118*5a8f0974SVladimir Oltean } 119*5a8f0974SVladimir Oltean 120*5a8f0974SVladimir Oltean static int sja1105_base_tx_mdio_write(struct mii_bus *bus, int phy, int reg, 121*5a8f0974SVladimir Oltean u16 val) 122*5a8f0974SVladimir Oltean { 123*5a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 124*5a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 125*5a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 126*5a8f0974SVladimir Oltean u32 tmp = val; 127*5a8f0974SVladimir Oltean 128*5a8f0974SVladimir Oltean return sja1105_xfer_u32(priv, SPI_WRITE, regs->mdio_100base_tx + reg, 129*5a8f0974SVladimir Oltean &tmp, NULL); 130*5a8f0974SVladimir Oltean } 131*5a8f0974SVladimir Oltean 132*5a8f0974SVladimir Oltean static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv, 133*5a8f0974SVladimir Oltean struct device_node *mdio_node) 134*5a8f0974SVladimir Oltean { 135*5a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv; 136*5a8f0974SVladimir Oltean struct device_node *np; 137*5a8f0974SVladimir Oltean struct mii_bus *bus; 138*5a8f0974SVladimir Oltean int rc = 0; 139*5a8f0974SVladimir Oltean 140*5a8f0974SVladimir Oltean np = of_find_compatible_node(mdio_node, NULL, 141*5a8f0974SVladimir Oltean "nxp,sja1110-base-tx-mdio"); 142*5a8f0974SVladimir Oltean if (!np) 143*5a8f0974SVladimir Oltean return 0; 144*5a8f0974SVladimir Oltean 145*5a8f0974SVladimir Oltean if (!of_device_is_available(np)) 146*5a8f0974SVladimir Oltean goto out_put_np; 147*5a8f0974SVladimir Oltean 148*5a8f0974SVladimir Oltean bus = mdiobus_alloc_size(sizeof(*mdio_priv)); 149*5a8f0974SVladimir Oltean if (!bus) { 150*5a8f0974SVladimir Oltean rc = -ENOMEM; 151*5a8f0974SVladimir Oltean goto out_put_np; 152*5a8f0974SVladimir Oltean } 153*5a8f0974SVladimir Oltean 154*5a8f0974SVladimir Oltean bus->name = "SJA1110 100base-TX MDIO bus"; 155*5a8f0974SVladimir Oltean snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-tx", 156*5a8f0974SVladimir Oltean dev_name(priv->ds->dev)); 157*5a8f0974SVladimir Oltean bus->read = sja1105_base_tx_mdio_read; 158*5a8f0974SVladimir Oltean bus->write = sja1105_base_tx_mdio_write; 159*5a8f0974SVladimir Oltean bus->parent = priv->ds->dev; 160*5a8f0974SVladimir Oltean mdio_priv = bus->priv; 161*5a8f0974SVladimir Oltean mdio_priv->priv = priv; 162*5a8f0974SVladimir Oltean 163*5a8f0974SVladimir Oltean rc = of_mdiobus_register(bus, np); 164*5a8f0974SVladimir Oltean if (rc) { 165*5a8f0974SVladimir Oltean mdiobus_free(bus); 166*5a8f0974SVladimir Oltean goto out_put_np; 167*5a8f0974SVladimir Oltean } 168*5a8f0974SVladimir Oltean 169*5a8f0974SVladimir Oltean priv->mdio_base_tx = bus; 170*5a8f0974SVladimir Oltean 171*5a8f0974SVladimir Oltean out_put_np: 172*5a8f0974SVladimir Oltean of_node_put(np); 173*5a8f0974SVladimir Oltean 174*5a8f0974SVladimir Oltean return 0; 175*5a8f0974SVladimir Oltean } 176*5a8f0974SVladimir Oltean 177*5a8f0974SVladimir Oltean static void sja1105_mdiobus_base_tx_unregister(struct sja1105_private *priv) 178*5a8f0974SVladimir Oltean { 179*5a8f0974SVladimir Oltean if (!priv->mdio_base_tx) 180*5a8f0974SVladimir Oltean return; 181*5a8f0974SVladimir Oltean 182*5a8f0974SVladimir Oltean mdiobus_unregister(priv->mdio_base_tx); 183*5a8f0974SVladimir Oltean mdiobus_free(priv->mdio_base_tx); 184*5a8f0974SVladimir Oltean priv->mdio_base_tx = NULL; 185*5a8f0974SVladimir Oltean } 186*5a8f0974SVladimir Oltean 187*5a8f0974SVladimir Oltean static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv, 188*5a8f0974SVladimir Oltean struct device_node *mdio_node) 189*5a8f0974SVladimir Oltean { 190*5a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv; 191*5a8f0974SVladimir Oltean struct device_node *np; 192*5a8f0974SVladimir Oltean struct mii_bus *bus; 193*5a8f0974SVladimir Oltean int rc = 0; 194*5a8f0974SVladimir Oltean 195*5a8f0974SVladimir Oltean np = of_find_compatible_node(mdio_node, NULL, 196*5a8f0974SVladimir Oltean "nxp,sja1110-base-t1-mdio"); 197*5a8f0974SVladimir Oltean if (!np) 198*5a8f0974SVladimir Oltean return 0; 199*5a8f0974SVladimir Oltean 200*5a8f0974SVladimir Oltean if (!of_device_is_available(np)) 201*5a8f0974SVladimir Oltean goto out_put_np; 202*5a8f0974SVladimir Oltean 203*5a8f0974SVladimir Oltean bus = mdiobus_alloc_size(sizeof(*mdio_priv)); 204*5a8f0974SVladimir Oltean if (!bus) { 205*5a8f0974SVladimir Oltean rc = -ENOMEM; 206*5a8f0974SVladimir Oltean goto out_put_np; 207*5a8f0974SVladimir Oltean } 208*5a8f0974SVladimir Oltean 209*5a8f0974SVladimir Oltean bus->name = "SJA1110 100base-T1 MDIO bus"; 210*5a8f0974SVladimir Oltean snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-t1", 211*5a8f0974SVladimir Oltean dev_name(priv->ds->dev)); 212*5a8f0974SVladimir Oltean bus->read = sja1105_base_t1_mdio_read; 213*5a8f0974SVladimir Oltean bus->write = sja1105_base_t1_mdio_write; 214*5a8f0974SVladimir Oltean bus->parent = priv->ds->dev; 215*5a8f0974SVladimir Oltean mdio_priv = bus->priv; 216*5a8f0974SVladimir Oltean mdio_priv->priv = priv; 217*5a8f0974SVladimir Oltean 218*5a8f0974SVladimir Oltean rc = of_mdiobus_register(bus, np); 219*5a8f0974SVladimir Oltean if (rc) { 220*5a8f0974SVladimir Oltean mdiobus_free(bus); 221*5a8f0974SVladimir Oltean goto out_put_np; 222*5a8f0974SVladimir Oltean } 223*5a8f0974SVladimir Oltean 224*5a8f0974SVladimir Oltean priv->mdio_base_t1 = bus; 225*5a8f0974SVladimir Oltean 226*5a8f0974SVladimir Oltean out_put_np: 227*5a8f0974SVladimir Oltean of_node_put(np); 228*5a8f0974SVladimir Oltean 229*5a8f0974SVladimir Oltean return rc; 230*5a8f0974SVladimir Oltean } 231*5a8f0974SVladimir Oltean 232*5a8f0974SVladimir Oltean static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv) 233*5a8f0974SVladimir Oltean { 234*5a8f0974SVladimir Oltean if (!priv->mdio_base_t1) 235*5a8f0974SVladimir Oltean return; 236*5a8f0974SVladimir Oltean 237*5a8f0974SVladimir Oltean mdiobus_unregister(priv->mdio_base_t1); 238*5a8f0974SVladimir Oltean mdiobus_free(priv->mdio_base_t1); 239*5a8f0974SVladimir Oltean priv->mdio_base_t1 = NULL; 240*5a8f0974SVladimir Oltean } 241*5a8f0974SVladimir Oltean 242*5a8f0974SVladimir Oltean int sja1105_mdiobus_register(struct dsa_switch *ds) 243*5a8f0974SVladimir Oltean { 244*5a8f0974SVladimir Oltean struct sja1105_private *priv = ds->priv; 245*5a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 246*5a8f0974SVladimir Oltean struct device_node *switch_node = ds->dev->of_node; 247*5a8f0974SVladimir Oltean struct device_node *mdio_node; 248*5a8f0974SVladimir Oltean int rc; 249*5a8f0974SVladimir Oltean 250*5a8f0974SVladimir Oltean mdio_node = of_get_child_by_name(switch_node, "mdios"); 251*5a8f0974SVladimir Oltean if (!mdio_node) 252*5a8f0974SVladimir Oltean return 0; 253*5a8f0974SVladimir Oltean 254*5a8f0974SVladimir Oltean if (!of_device_is_available(mdio_node)) 255*5a8f0974SVladimir Oltean goto out_put_mdio_node; 256*5a8f0974SVladimir Oltean 257*5a8f0974SVladimir Oltean if (regs->mdio_100base_tx != SJA1105_RSV_ADDR) { 258*5a8f0974SVladimir Oltean rc = sja1105_mdiobus_base_tx_register(priv, mdio_node); 259*5a8f0974SVladimir Oltean if (rc) 260*5a8f0974SVladimir Oltean goto err_put_mdio_node; 261*5a8f0974SVladimir Oltean } 262*5a8f0974SVladimir Oltean 263*5a8f0974SVladimir Oltean if (regs->mdio_100base_t1 != SJA1105_RSV_ADDR) { 264*5a8f0974SVladimir Oltean rc = sja1105_mdiobus_base_t1_register(priv, mdio_node); 265*5a8f0974SVladimir Oltean if (rc) 266*5a8f0974SVladimir Oltean goto err_free_base_tx_mdiobus; 267*5a8f0974SVladimir Oltean } 268*5a8f0974SVladimir Oltean 269*5a8f0974SVladimir Oltean out_put_mdio_node: 270*5a8f0974SVladimir Oltean of_node_put(mdio_node); 271*5a8f0974SVladimir Oltean 272*5a8f0974SVladimir Oltean return 0; 273*5a8f0974SVladimir Oltean 274*5a8f0974SVladimir Oltean err_free_base_tx_mdiobus: 275*5a8f0974SVladimir Oltean sja1105_mdiobus_base_tx_unregister(priv); 276*5a8f0974SVladimir Oltean err_put_mdio_node: 277*5a8f0974SVladimir Oltean of_node_put(mdio_node); 278*5a8f0974SVladimir Oltean 279*5a8f0974SVladimir Oltean return rc; 280*5a8f0974SVladimir Oltean } 281*5a8f0974SVladimir Oltean 282*5a8f0974SVladimir Oltean void sja1105_mdiobus_unregister(struct dsa_switch *ds) 283*5a8f0974SVladimir Oltean { 284*5a8f0974SVladimir Oltean struct sja1105_private *priv = ds->priv; 285*5a8f0974SVladimir Oltean 286*5a8f0974SVladimir Oltean sja1105_mdiobus_base_t1_unregister(priv); 287*5a8f0974SVladimir Oltean sja1105_mdiobus_base_tx_unregister(priv); 288*5a8f0974SVladimir Oltean } 289