xref: /openbmc/linux/drivers/net/dsa/sja1105/sja1105_mdio.c (revision ab324d8dfddad04bec0e8421242716504e31e204)
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