123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright 2021 NXP
- */
- #include <linux/pcs/pcs-xpcs.h>
- #include <linux/of_mdio.h>
- #include "sja1105.h"
- #define SJA1110_PCS_BANK_REG SJA1110_SPI_ADDR(0x3fc)
- int sja1105_pcs_mdio_read(struct mii_bus *bus, int phy, int reg)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- u16 mmd;
- int rc;
- if (!(reg & MII_ADDR_C45))
- return -EINVAL;
- mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
- addr = (mmd << 16) | (reg & GENMASK(15, 0));
- if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
- return 0xffff;
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
- return NXP_SJA1105_XPCS_ID >> 16;
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
- return NXP_SJA1105_XPCS_ID & GENMASK(15, 0);
- rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
- return tmp & 0xffff;
- }
- int sja1105_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- u16 mmd;
- if (!(reg & MII_ADDR_C45))
- return -EINVAL;
- mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
- addr = (mmd << 16) | (reg & GENMASK(15, 0));
- tmp = val;
- if (mmd != MDIO_MMD_VEND1 && mmd != MDIO_MMD_VEND2)
- return -EINVAL;
- return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
- }
- int sja1110_pcs_mdio_read(struct mii_bus *bus, int phy, int reg)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- int offset, bank;
- u64 addr;
- u32 tmp;
- u16 mmd;
- int rc;
- if (!(reg & MII_ADDR_C45))
- return -EINVAL;
- if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
- return -ENODEV;
- mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
- addr = (mmd << 16) | (reg & GENMASK(15, 0));
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID1)
- return NXP_SJA1110_XPCS_ID >> 16;
- if (mmd == MDIO_MMD_VEND2 && (reg & GENMASK(15, 0)) == MII_PHYSID2)
- return NXP_SJA1110_XPCS_ID & GENMASK(15, 0);
- bank = addr >> 8;
- offset = addr & GENMASK(7, 0);
- /* This addressing scheme reserves register 0xff for the bank address
- * register, so that can never be addressed.
- */
- if (WARN_ON(offset == 0xff))
- return -ENODEV;
- tmp = bank;
- rc = sja1105_xfer_u32(priv, SPI_WRITE,
- regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
- &tmp, NULL);
- if (rc < 0)
- return rc;
- rc = sja1105_xfer_u32(priv, SPI_READ, regs->pcs_base[phy] + offset,
- &tmp, NULL);
- if (rc < 0)
- return rc;
- return tmp & 0xffff;
- }
- int sja1110_pcs_mdio_write(struct mii_bus *bus, int phy, int reg, u16 val)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- int offset, bank;
- u64 addr;
- u32 tmp;
- u16 mmd;
- int rc;
- if (!(reg & MII_ADDR_C45))
- return -EINVAL;
- if (regs->pcs_base[phy] == SJA1105_RSV_ADDR)
- return -ENODEV;
- mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
- addr = (mmd << 16) | (reg & GENMASK(15, 0));
- bank = addr >> 8;
- offset = addr & GENMASK(7, 0);
- /* This addressing scheme reserves register 0xff for the bank address
- * register, so that can never be addressed.
- */
- if (WARN_ON(offset == 0xff))
- return -ENODEV;
- tmp = bank;
- rc = sja1105_xfer_u32(priv, SPI_WRITE,
- regs->pcs_base[phy] + SJA1110_PCS_BANK_REG,
- &tmp, NULL);
- if (rc < 0)
- return rc;
- tmp = val;
- return sja1105_xfer_u32(priv, SPI_WRITE, regs->pcs_base[phy] + offset,
- &tmp, NULL);
- }
- enum sja1105_mdio_opcode {
- SJA1105_C45_ADDR = 0,
- SJA1105_C22 = 1,
- SJA1105_C45_DATA = 2,
- SJA1105_C45_DATA_AUTOINC = 3,
- };
- static u64 sja1105_base_t1_encode_addr(struct sja1105_private *priv,
- int phy, enum sja1105_mdio_opcode op,
- int xad)
- {
- const struct sja1105_regs *regs = priv->info->regs;
- return regs->mdio_100base_t1 | (phy << 7) | (op << 5) | (xad << 0);
- }
- static int sja1105_base_t1_mdio_read(struct mii_bus *bus, int phy, int reg)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- int rc;
- if (reg & MII_ADDR_C45) {
- u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR,
- mmd);
- tmp = reg & MII_REGADDR_C45_MASK;
- rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA,
- mmd);
- rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
- return tmp & 0xffff;
- }
- /* Clause 22 read */
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);
- rc = sja1105_xfer_u32(priv, SPI_READ, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
- return tmp & 0xffff;
- }
- static int sja1105_base_t1_mdio_write(struct mii_bus *bus, int phy, int reg,
- u16 val)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- u64 addr;
- u32 tmp;
- int rc;
- if (reg & MII_ADDR_C45) {
- u16 mmd = (reg >> MII_DEVADDR_C45_SHIFT) & 0x1f;
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_ADDR,
- mmd);
- tmp = reg & MII_REGADDR_C45_MASK;
- rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C45_DATA,
- mmd);
- tmp = val & 0xffff;
- rc = sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
- if (rc < 0)
- return rc;
- return 0;
- }
- /* Clause 22 write */
- addr = sja1105_base_t1_encode_addr(priv, phy, SJA1105_C22, reg & 0x1f);
- tmp = val & 0xffff;
- return sja1105_xfer_u32(priv, SPI_WRITE, addr, &tmp, NULL);
- }
- static int sja1105_base_tx_mdio_read(struct mii_bus *bus, int phy, int reg)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- u32 tmp;
- int rc;
- if (reg & MII_ADDR_C45)
- return -EOPNOTSUPP;
- rc = sja1105_xfer_u32(priv, SPI_READ, regs->mdio_100base_tx + reg,
- &tmp, NULL);
- if (rc < 0)
- return rc;
- return tmp & 0xffff;
- }
- static int sja1105_base_tx_mdio_write(struct mii_bus *bus, int phy, int reg,
- u16 val)
- {
- struct sja1105_mdio_private *mdio_priv = bus->priv;
- struct sja1105_private *priv = mdio_priv->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- u32 tmp = val;
- if (reg & MII_ADDR_C45)
- return -EOPNOTSUPP;
- return sja1105_xfer_u32(priv, SPI_WRITE, regs->mdio_100base_tx + reg,
- &tmp, NULL);
- }
- static int sja1105_mdiobus_base_tx_register(struct sja1105_private *priv,
- struct device_node *mdio_node)
- {
- struct sja1105_mdio_private *mdio_priv;
- struct device_node *np;
- struct mii_bus *bus;
- int rc = 0;
- np = of_get_compatible_child(mdio_node, "nxp,sja1110-base-tx-mdio");
- if (!np)
- return 0;
- if (!of_device_is_available(np))
- goto out_put_np;
- bus = mdiobus_alloc_size(sizeof(*mdio_priv));
- if (!bus) {
- rc = -ENOMEM;
- goto out_put_np;
- }
- bus->name = "SJA1110 100base-TX MDIO bus";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-tx",
- dev_name(priv->ds->dev));
- bus->read = sja1105_base_tx_mdio_read;
- bus->write = sja1105_base_tx_mdio_write;
- bus->parent = priv->ds->dev;
- mdio_priv = bus->priv;
- mdio_priv->priv = priv;
- rc = of_mdiobus_register(bus, np);
- if (rc) {
- mdiobus_free(bus);
- goto out_put_np;
- }
- priv->mdio_base_tx = bus;
- out_put_np:
- of_node_put(np);
- return rc;
- }
- static void sja1105_mdiobus_base_tx_unregister(struct sja1105_private *priv)
- {
- if (!priv->mdio_base_tx)
- return;
- mdiobus_unregister(priv->mdio_base_tx);
- mdiobus_free(priv->mdio_base_tx);
- priv->mdio_base_tx = NULL;
- }
- static int sja1105_mdiobus_base_t1_register(struct sja1105_private *priv,
- struct device_node *mdio_node)
- {
- struct sja1105_mdio_private *mdio_priv;
- struct device_node *np;
- struct mii_bus *bus;
- int rc = 0;
- np = of_get_compatible_child(mdio_node, "nxp,sja1110-base-t1-mdio");
- if (!np)
- return 0;
- if (!of_device_is_available(np))
- goto out_put_np;
- bus = mdiobus_alloc_size(sizeof(*mdio_priv));
- if (!bus) {
- rc = -ENOMEM;
- goto out_put_np;
- }
- bus->name = "SJA1110 100base-T1 MDIO bus";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-base-t1",
- dev_name(priv->ds->dev));
- bus->read = sja1105_base_t1_mdio_read;
- bus->write = sja1105_base_t1_mdio_write;
- bus->parent = priv->ds->dev;
- mdio_priv = bus->priv;
- mdio_priv->priv = priv;
- rc = of_mdiobus_register(bus, np);
- if (rc) {
- mdiobus_free(bus);
- goto out_put_np;
- }
- priv->mdio_base_t1 = bus;
- out_put_np:
- of_node_put(np);
- return rc;
- }
- static void sja1105_mdiobus_base_t1_unregister(struct sja1105_private *priv)
- {
- if (!priv->mdio_base_t1)
- return;
- mdiobus_unregister(priv->mdio_base_t1);
- mdiobus_free(priv->mdio_base_t1);
- priv->mdio_base_t1 = NULL;
- }
- static int sja1105_mdiobus_pcs_register(struct sja1105_private *priv)
- {
- struct sja1105_mdio_private *mdio_priv;
- struct dsa_switch *ds = priv->ds;
- struct mii_bus *bus;
- int rc = 0;
- int port;
- if (!priv->info->pcs_mdio_read || !priv->info->pcs_mdio_write)
- return 0;
- bus = mdiobus_alloc_size(sizeof(*mdio_priv));
- if (!bus)
- return -ENOMEM;
- bus->name = "SJA1105 PCS MDIO bus";
- snprintf(bus->id, MII_BUS_ID_SIZE, "%s-pcs",
- dev_name(ds->dev));
- bus->read = priv->info->pcs_mdio_read;
- bus->write = priv->info->pcs_mdio_write;
- bus->parent = ds->dev;
- /* There is no PHY on this MDIO bus => mask out all PHY addresses
- * from auto probing.
- */
- bus->phy_mask = ~0;
- mdio_priv = bus->priv;
- mdio_priv->priv = priv;
- rc = mdiobus_register(bus);
- if (rc) {
- mdiobus_free(bus);
- return rc;
- }
- for (port = 0; port < ds->num_ports; port++) {
- struct mdio_device *mdiodev;
- struct dw_xpcs *xpcs;
- if (dsa_is_unused_port(ds, port))
- continue;
- if (priv->phy_mode[port] != PHY_INTERFACE_MODE_SGMII &&
- priv->phy_mode[port] != PHY_INTERFACE_MODE_2500BASEX)
- continue;
- mdiodev = mdio_device_create(bus, port);
- if (IS_ERR(mdiodev)) {
- rc = PTR_ERR(mdiodev);
- goto out_pcs_free;
- }
- xpcs = xpcs_create(mdiodev, priv->phy_mode[port]);
- if (IS_ERR(xpcs)) {
- rc = PTR_ERR(xpcs);
- goto out_pcs_free;
- }
- priv->xpcs[port] = xpcs;
- }
- priv->mdio_pcs = bus;
- return 0;
- out_pcs_free:
- for (port = 0; port < ds->num_ports; port++) {
- if (!priv->xpcs[port])
- continue;
- mdio_device_free(priv->xpcs[port]->mdiodev);
- xpcs_destroy(priv->xpcs[port]);
- priv->xpcs[port] = NULL;
- }
- mdiobus_unregister(bus);
- mdiobus_free(bus);
- return rc;
- }
- static void sja1105_mdiobus_pcs_unregister(struct sja1105_private *priv)
- {
- struct dsa_switch *ds = priv->ds;
- int port;
- if (!priv->mdio_pcs)
- return;
- for (port = 0; port < ds->num_ports; port++) {
- if (!priv->xpcs[port])
- continue;
- mdio_device_free(priv->xpcs[port]->mdiodev);
- xpcs_destroy(priv->xpcs[port]);
- priv->xpcs[port] = NULL;
- }
- mdiobus_unregister(priv->mdio_pcs);
- mdiobus_free(priv->mdio_pcs);
- priv->mdio_pcs = NULL;
- }
- int sja1105_mdiobus_register(struct dsa_switch *ds)
- {
- struct sja1105_private *priv = ds->priv;
- const struct sja1105_regs *regs = priv->info->regs;
- struct device_node *switch_node = ds->dev->of_node;
- struct device_node *mdio_node;
- int rc;
- rc = sja1105_mdiobus_pcs_register(priv);
- if (rc)
- return rc;
- mdio_node = of_get_child_by_name(switch_node, "mdios");
- if (!mdio_node)
- return 0;
- if (!of_device_is_available(mdio_node))
- goto out_put_mdio_node;
- if (regs->mdio_100base_tx != SJA1105_RSV_ADDR) {
- rc = sja1105_mdiobus_base_tx_register(priv, mdio_node);
- if (rc)
- goto err_put_mdio_node;
- }
- if (regs->mdio_100base_t1 != SJA1105_RSV_ADDR) {
- rc = sja1105_mdiobus_base_t1_register(priv, mdio_node);
- if (rc)
- goto err_free_base_tx_mdiobus;
- }
- out_put_mdio_node:
- of_node_put(mdio_node);
- return 0;
- err_free_base_tx_mdiobus:
- sja1105_mdiobus_base_tx_unregister(priv);
- err_put_mdio_node:
- of_node_put(mdio_node);
- sja1105_mdiobus_pcs_unregister(priv);
- return rc;
- }
- void sja1105_mdiobus_unregister(struct dsa_switch *ds)
- {
- struct sja1105_private *priv = ds->priv;
- sja1105_mdiobus_base_t1_unregister(priv);
- sja1105_mdiobus_base_tx_unregister(priv);
- sja1105_mdiobus_pcs_unregister(priv);
- }
|