15a8f0974SVladimir Oltean // SPDX-License-Identifier: GPL-2.0 25a8f0974SVladimir Oltean /* Copyright 2021, NXP Semiconductors 35a8f0974SVladimir Oltean */ 45a8f0974SVladimir Oltean #include <linux/of_mdio.h> 55a8f0974SVladimir Oltean #include "sja1105.h" 65a8f0974SVladimir Oltean 75a8f0974SVladimir Oltean enum sja1105_mdio_opcode { 85a8f0974SVladimir Oltean SJA1105_C45_ADDR = 0, 95a8f0974SVladimir Oltean SJA1105_C22 = 1, 105a8f0974SVladimir Oltean SJA1105_C45_DATA = 2, 115a8f0974SVladimir Oltean SJA1105_C45_DATA_AUTOINC = 3, 125a8f0974SVladimir Oltean }; 135a8f0974SVladimir Oltean 145a8f0974SVladimir Oltean static u64 sja1105_base_t1_encode_addr(struct sja1105_private *priv, 155a8f0974SVladimir Oltean int phy, enum sja1105_mdio_opcode op, 165a8f0974SVladimir Oltean int xad) 175a8f0974SVladimir Oltean { 185a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 195a8f0974SVladimir Oltean 205a8f0974SVladimir Oltean return regs->mdio_100base_t1 | (phy << 7) | (op << 5) | (xad << 0); 215a8f0974SVladimir Oltean } 225a8f0974SVladimir Oltean 235a8f0974SVladimir Oltean static int sja1105_base_t1_mdio_read(struct mii_bus *bus, int phy, int reg) 245a8f0974SVladimir Oltean { 255a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 265a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 275a8f0974SVladimir Oltean u64 addr; 285a8f0974SVladimir Oltean u32 tmp; 295a8f0974SVladimir Oltean int rc; 305a8f0974SVladimir Oltean 315a8f0974SVladimir Oltean if (reg & MII_ADDR_C45) { 325a8f0974SVladimir Oltean u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f; 335a8f0974SVladimir Oltean 345a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR, 355a8f0974SVladimir Oltean mmd); 365a8f0974SVladimir Oltean 375a8f0974SVladimir Oltean tmp = reg & MII_REGADDR_C45_MASK; 385a8f0974SVladimir Oltean 395a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 405a8f0974SVladimir Oltean if (rc < 0) 415a8f0974SVladimir Oltean return rc; 425a8f0974SVladimir Oltean 435a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA, 445a8f0974SVladimir Oltean mmd); 455a8f0974SVladimir Oltean 465a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL); 475a8f0974SVladimir Oltean if (rc < 0) 485a8f0974SVladimir Oltean return rc; 495a8f0974SVladimir Oltean 505a8f0974SVladimir Oltean return tmp & 0xffff; 515a8f0974SVladimir Oltean } 525a8f0974SVladimir Oltean 535a8f0974SVladimir Oltean /* Clause 22 read */ 545a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f); 555a8f0974SVladimir Oltean 565a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL); 575a8f0974SVladimir Oltean if (rc < 0) 585a8f0974SVladimir Oltean return rc; 595a8f0974SVladimir Oltean 605a8f0974SVladimir Oltean return tmp & 0xffff; 615a8f0974SVladimir Oltean } 625a8f0974SVladimir Oltean 635a8f0974SVladimir Oltean static int sja1105_base_t1_mdio_write(struct mii_bus *bus, int phy, int reg, 645a8f0974SVladimir Oltean u16 val) 655a8f0974SVladimir Oltean { 665a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 675a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 685a8f0974SVladimir Oltean u64 addr; 695a8f0974SVladimir Oltean u32 tmp; 705a8f0974SVladimir Oltean int rc; 715a8f0974SVladimir Oltean 725a8f0974SVladimir Oltean if (reg & MII_ADDR_C45) { 735a8f0974SVladimir Oltean u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f; 745a8f0974SVladimir Oltean 755a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR, 765a8f0974SVladimir Oltean mmd); 775a8f0974SVladimir Oltean 785a8f0974SVladimir Oltean tmp = reg & MII_REGADDR_C45_MASK; 795a8f0974SVladimir Oltean 805a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 815a8f0974SVladimir Oltean if (rc < 0) 825a8f0974SVladimir Oltean return rc; 835a8f0974SVladimir Oltean 845a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA, 855a8f0974SVladimir Oltean mmd); 865a8f0974SVladimir Oltean 875a8f0974SVladimir Oltean tmp = val & 0xffff; 885a8f0974SVladimir Oltean 895a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 905a8f0974SVladimir Oltean if (rc < 0) 915a8f0974SVladimir Oltean return rc; 925a8f0974SVladimir Oltean 935a8f0974SVladimir Oltean return 0; 945a8f0974SVladimir Oltean } 955a8f0974SVladimir Oltean 965a8f0974SVladimir Oltean /* Clause 22 write */ 975a8f0974SVladimir Oltean addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f); 985a8f0974SVladimir Oltean 995a8f0974SVladimir Oltean tmp = val & 0xffff; 1005a8f0974SVladimir Oltean 1015a8f0974SVladimir Oltean return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL); 1025a8f0974SVladimir Oltean } 1035a8f0974SVladimir Oltean 1045a8f0974SVladimir Oltean static int sja1105_base_tx_mdio_read(struct mii_bus *bus, int phy, int reg) 1055a8f0974SVladimir Oltean { 1065a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 1075a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 1085a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 1095a8f0974SVladimir Oltean u32 tmp; 1105a8f0974SVladimir Oltean int rc; 1115a8f0974SVladimir Oltean 1125a8f0974SVladimir Oltean rc = sja1105_xfer_u32(priv, SPI_READ, regs->mdio_100base_tx + reg, 1135a8f0974SVladimir Oltean &tmp, NULL); 1145a8f0974SVladimir Oltean if (rc < 0) 1155a8f0974SVladimir Oltean return rc; 1165a8f0974SVladimir Oltean 1175a8f0974SVladimir Oltean return tmp & 0xffff; 1185a8f0974SVladimir Oltean } 1195a8f0974SVladimir Oltean 1205a8f0974SVladimir Oltean static int sja1105_base_tx_mdio_write(struct mii_bus *bus, int phy, int reg, 1215a8f0974SVladimir Oltean u16 val) 1225a8f0974SVladimir Oltean { 1235a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv = bus->priv; 1245a8f0974SVladimir Oltean struct sja1105_private *priv = mdio_priv->priv; 1255a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 1265a8f0974SVladimir Oltean u32 tmp = val; 1275a8f0974SVladimir Oltean 1285a8f0974SVladimir Oltean return sja1105_xfer_u32(priv, SPI_WRITE, regs->mdio_100base_tx + reg, 1295a8f0974SVladimir Oltean &tmp, NULL); 1305a8f0974SVladimir Oltean } 1315a8f0974SVladimir Oltean 1325a8f0974SVladimir Oltean static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv, 1335a8f0974SVladimir Oltean struct device_node *mdio_node) 1345a8f0974SVladimir Oltean { 1355a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv; 1365a8f0974SVladimir Oltean struct device_node *np; 1375a8f0974SVladimir Oltean struct mii_bus *bus; 1385a8f0974SVladimir Oltean int rc = 0; 1395a8f0974SVladimir Oltean 1405a8f0974SVladimir Oltean np = of_find_compatible_node(mdio_node, NULL, 1415a8f0974SVladimir Oltean "nxp,sja1110-base-tx-mdio"); 1425a8f0974SVladimir Oltean if (!np) 1435a8f0974SVladimir Oltean return 0; 1445a8f0974SVladimir Oltean 1455a8f0974SVladimir Oltean if (!of_device_is_available(np)) 1465a8f0974SVladimir Oltean goto out_put_np; 1475a8f0974SVladimir Oltean 1485a8f0974SVladimir Oltean bus = mdiobus_alloc_size(sizeof(*mdio_priv)); 1495a8f0974SVladimir Oltean if (!bus) { 1505a8f0974SVladimir Oltean rc = -ENOMEM; 1515a8f0974SVladimir Oltean goto out_put_np; 1525a8f0974SVladimir Oltean } 1535a8f0974SVladimir Oltean 1545a8f0974SVladimir Oltean bus->name = "SJA1110 100base-TX MDIO bus"; 1555a8f0974SVladimir Oltean snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-tx", 1565a8f0974SVladimir Oltean dev_name(priv->ds->dev)); 1575a8f0974SVladimir Oltean bus->read = sja1105_base_tx_mdio_read; 1585a8f0974SVladimir Oltean bus->write = sja1105_base_tx_mdio_write; 1595a8f0974SVladimir Oltean bus->parent = priv->ds->dev; 1605a8f0974SVladimir Oltean mdio_priv = bus->priv; 1615a8f0974SVladimir Oltean mdio_priv->priv = priv; 1625a8f0974SVladimir Oltean 1635a8f0974SVladimir Oltean rc = of_mdiobus_register(bus, np); 1645a8f0974SVladimir Oltean if (rc) { 1655a8f0974SVladimir Oltean mdiobus_free(bus); 1665a8f0974SVladimir Oltean goto out_put_np; 1675a8f0974SVladimir Oltean } 1685a8f0974SVladimir Oltean 1695a8f0974SVladimir Oltean priv->mdio_base_tx = bus; 1705a8f0974SVladimir Oltean 1715a8f0974SVladimir Oltean out_put_np: 1725a8f0974SVladimir Oltean of_node_put(np); 1735a8f0974SVladimir Oltean 174*ab324d8dSColin Ian King return rc; 1755a8f0974SVladimir Oltean } 1765a8f0974SVladimir Oltean 1775a8f0974SVladimir Oltean static void sja1105_mdiobus_base_tx_unregister(struct sja1105_private *priv) 1785a8f0974SVladimir Oltean { 1795a8f0974SVladimir Oltean if (!priv->mdio_base_tx) 1805a8f0974SVladimir Oltean return; 1815a8f0974SVladimir Oltean 1825a8f0974SVladimir Oltean mdiobus_unregister(priv->mdio_base_tx); 1835a8f0974SVladimir Oltean mdiobus_free(priv->mdio_base_tx); 1845a8f0974SVladimir Oltean priv->mdio_base_tx = NULL; 1855a8f0974SVladimir Oltean } 1865a8f0974SVladimir Oltean 1875a8f0974SVladimir Oltean static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv, 1885a8f0974SVladimir Oltean struct device_node *mdio_node) 1895a8f0974SVladimir Oltean { 1905a8f0974SVladimir Oltean struct sja1105_mdio_private *mdio_priv; 1915a8f0974SVladimir Oltean struct device_node *np; 1925a8f0974SVladimir Oltean struct mii_bus *bus; 1935a8f0974SVladimir Oltean int rc = 0; 1945a8f0974SVladimir Oltean 1955a8f0974SVladimir Oltean np = of_find_compatible_node(mdio_node, NULL, 1965a8f0974SVladimir Oltean "nxp,sja1110-base-t1-mdio"); 1975a8f0974SVladimir Oltean if (!np) 1985a8f0974SVladimir Oltean return 0; 1995a8f0974SVladimir Oltean 2005a8f0974SVladimir Oltean if (!of_device_is_available(np)) 2015a8f0974SVladimir Oltean goto out_put_np; 2025a8f0974SVladimir Oltean 2035a8f0974SVladimir Oltean bus = mdiobus_alloc_size(sizeof(*mdio_priv)); 2045a8f0974SVladimir Oltean if (!bus) { 2055a8f0974SVladimir Oltean rc = -ENOMEM; 2065a8f0974SVladimir Oltean goto out_put_np; 2075a8f0974SVladimir Oltean } 2085a8f0974SVladimir Oltean 2095a8f0974SVladimir Oltean bus->name = "SJA1110 100base-T1 MDIO bus"; 2105a8f0974SVladimir Oltean snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-t1", 2115a8f0974SVladimir Oltean dev_name(priv->ds->dev)); 2125a8f0974SVladimir Oltean bus->read = sja1105_base_t1_mdio_read; 2135a8f0974SVladimir Oltean bus->write = sja1105_base_t1_mdio_write; 2145a8f0974SVladimir Oltean bus->parent = priv->ds->dev; 2155a8f0974SVladimir Oltean mdio_priv = bus->priv; 2165a8f0974SVladimir Oltean mdio_priv->priv = priv; 2175a8f0974SVladimir Oltean 2185a8f0974SVladimir Oltean rc = of_mdiobus_register(bus, np); 2195a8f0974SVladimir Oltean if (rc) { 2205a8f0974SVladimir Oltean mdiobus_free(bus); 2215a8f0974SVladimir Oltean goto out_put_np; 2225a8f0974SVladimir Oltean } 2235a8f0974SVladimir Oltean 2245a8f0974SVladimir Oltean priv->mdio_base_t1 = bus; 2255a8f0974SVladimir Oltean 2265a8f0974SVladimir Oltean out_put_np: 2275a8f0974SVladimir Oltean of_node_put(np); 2285a8f0974SVladimir Oltean 2295a8f0974SVladimir Oltean return rc; 2305a8f0974SVladimir Oltean } 2315a8f0974SVladimir Oltean 2325a8f0974SVladimir Oltean static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv) 2335a8f0974SVladimir Oltean { 2345a8f0974SVladimir Oltean if (!priv->mdio_base_t1) 2355a8f0974SVladimir Oltean return; 2365a8f0974SVladimir Oltean 2375a8f0974SVladimir Oltean mdiobus_unregister(priv->mdio_base_t1); 2385a8f0974SVladimir Oltean mdiobus_free(priv->mdio_base_t1); 2395a8f0974SVladimir Oltean priv->mdio_base_t1 = NULL; 2405a8f0974SVladimir Oltean } 2415a8f0974SVladimir Oltean 2425a8f0974SVladimir Oltean int sja1105_mdiobus_register(struct dsa_switch *ds) 2435a8f0974SVladimir Oltean { 2445a8f0974SVladimir Oltean struct sja1105_private *priv = ds->priv; 2455a8f0974SVladimir Oltean const struct sja1105_regs *regs = priv->info->regs; 2465a8f0974SVladimir Oltean struct device_node *switch_node = ds->dev->of_node; 2475a8f0974SVladimir Oltean struct device_node *mdio_node; 2485a8f0974SVladimir Oltean int rc; 2495a8f0974SVladimir Oltean 2505a8f0974SVladimir Oltean mdio_node = of_get_child_by_name(switch_node, "mdios"); 2515a8f0974SVladimir Oltean if (!mdio_node) 2525a8f0974SVladimir Oltean return 0; 2535a8f0974SVladimir Oltean 2545a8f0974SVladimir Oltean if (!of_device_is_available(mdio_node)) 2555a8f0974SVladimir Oltean goto out_put_mdio_node; 2565a8f0974SVladimir Oltean 2575a8f0974SVladimir Oltean if (regs->mdio_100base_tx != SJA1105_RSV_ADDR) { 2585a8f0974SVladimir Oltean rc = sja1105_mdiobus_base_tx_register(priv, mdio_node); 2595a8f0974SVladimir Oltean if (rc) 2605a8f0974SVladimir Oltean goto err_put_mdio_node; 2615a8f0974SVladimir Oltean } 2625a8f0974SVladimir Oltean 2635a8f0974SVladimir Oltean if (regs->mdio_100base_t1 != SJA1105_RSV_ADDR) { 2645a8f0974SVladimir Oltean rc = sja1105_mdiobus_base_t1_register(priv, mdio_node); 2655a8f0974SVladimir Oltean if (rc) 2665a8f0974SVladimir Oltean goto err_free_base_tx_mdiobus; 2675a8f0974SVladimir Oltean } 2685a8f0974SVladimir Oltean 2695a8f0974SVladimir Oltean out_put_mdio_node: 2705a8f0974SVladimir Oltean of_node_put(mdio_node); 2715a8f0974SVladimir Oltean 2725a8f0974SVladimir Oltean return 0; 2735a8f0974SVladimir Oltean 2745a8f0974SVladimir Oltean err_free_base_tx_mdiobus: 2755a8f0974SVladimir Oltean sja1105_mdiobus_base_tx_unregister(priv); 2765a8f0974SVladimir Oltean err_put_mdio_node: 2775a8f0974SVladimir Oltean of_node_put(mdio_node); 2785a8f0974SVladimir Oltean 2795a8f0974SVladimir Oltean return rc; 2805a8f0974SVladimir Oltean } 2815a8f0974SVladimir Oltean 2825a8f0974SVladimir Oltean void sja1105_mdiobus_unregister(struct dsa_switch *ds) 2835a8f0974SVladimir Oltean { 2845a8f0974SVladimir Oltean struct sja1105_private *priv = ds->priv; 2855a8f0974SVladimir Oltean 2865a8f0974SVladimir Oltean sja1105_mdiobus_base_t1_unregister(priv); 2875a8f0974SVladimir Oltean sja1105_mdiobus_base_tx_unregister(priv); 2885a8f0974SVladimir Oltean } 289