Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright Sunplus Technology Co., Ltd.
0003  *       All rights reserved.
0004  */
0005 
0006 #include <linux/platform_device.h>
0007 #include <linux/netdevice.h>
0008 #include <linux/bitfield.h>
0009 #include <linux/of_mdio.h>
0010 
0011 #include "spl2sw_register.h"
0012 #include "spl2sw_define.h"
0013 #include "spl2sw_mdio.h"
0014 
0015 #define SPL2SW_MDIO_READ_CMD           0x02
0016 #define SPL2SW_MDIO_WRITE_CMD          0x01
0017 
0018 static int spl2sw_mdio_access(struct spl2sw_common *comm, u8 cmd, u8 addr, u8 regnum, u16 wdata)
0019 {
0020     u32 reg, reg2;
0021     u32 val;
0022     int ret;
0023 
0024     /* Note that addr (of phy) should match either ext_phy0_addr
0025      * or ext_phy1_addr, or mdio commands won't be sent out.
0026      */
0027     reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
0028     reg &= ~MAC_EXT_PHY0_ADDR;
0029     reg |= FIELD_PREP(MAC_EXT_PHY0_ADDR, addr);
0030 
0031     reg2 = FIELD_PREP(MAC_CPU_PHY_WT_DATA, wdata) | FIELD_PREP(MAC_CPU_PHY_CMD, cmd) |
0032            FIELD_PREP(MAC_CPU_PHY_REG_ADDR, regnum) | FIELD_PREP(MAC_CPU_PHY_ADDR, addr);
0033 
0034     /* Set ext_phy0_addr and then issue mdio command.
0035      * No interrupt is allowed in between.
0036      */
0037     spin_lock_irq(&comm->mdio_lock);
0038     writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
0039     writel(reg2, comm->l2sw_reg_base + L2SW_PHY_CNTL_REG0);
0040     spin_unlock_irq(&comm->mdio_lock);
0041 
0042     ret = read_poll_timeout(readl, val, val & cmd, 1, 1000, true,
0043                 comm->l2sw_reg_base + L2SW_PHY_CNTL_REG1);
0044 
0045     /* Set ext_phy0_addr back to 31 to prevent
0046      * from sending mdio command to phy by
0047      * hardware auto-mdio function.
0048      */
0049     reg = readl(comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
0050     reg &= ~MAC_EXT_PHY0_ADDR;
0051     reg |= FIELD_PREP(MAC_EXT_PHY0_ADDR, 31);
0052     writel(reg, comm->l2sw_reg_base + L2SW_MAC_FORCE_MODE);
0053 
0054     if (ret == 0)
0055         return val >> 16;
0056     else
0057         return ret;
0058 }
0059 
0060 static int spl2sw_mii_read(struct mii_bus *bus, int addr, int regnum)
0061 {
0062     struct spl2sw_common *comm = bus->priv;
0063 
0064     if (regnum & MII_ADDR_C45)
0065         return -EOPNOTSUPP;
0066 
0067     return spl2sw_mdio_access(comm, SPL2SW_MDIO_READ_CMD, addr, regnum, 0);
0068 }
0069 
0070 static int spl2sw_mii_write(struct mii_bus *bus, int addr, int regnum, u16 val)
0071 {
0072     struct spl2sw_common *comm = bus->priv;
0073     int ret;
0074 
0075     if (regnum & MII_ADDR_C45)
0076         return -EOPNOTSUPP;
0077 
0078     ret = spl2sw_mdio_access(comm, SPL2SW_MDIO_WRITE_CMD, addr, regnum, val);
0079     if (ret < 0)
0080         return ret;
0081 
0082     return 0;
0083 }
0084 
0085 u32 spl2sw_mdio_init(struct spl2sw_common *comm)
0086 {
0087     struct device_node *mdio_np;
0088     struct mii_bus *mii_bus;
0089     int ret;
0090 
0091     /* Get mdio child node. */
0092     mdio_np = of_get_child_by_name(comm->pdev->dev.of_node, "mdio");
0093     if (!mdio_np) {
0094         dev_err(&comm->pdev->dev, "No mdio child node found!\n");
0095         return -ENODEV;
0096     }
0097 
0098     /* Allocate and register mdio bus. */
0099     mii_bus = devm_mdiobus_alloc(&comm->pdev->dev);
0100     if (!mii_bus) {
0101         ret = -ENOMEM;
0102         goto out;
0103     }
0104 
0105     mii_bus->name = "sunplus_mii_bus";
0106     mii_bus->parent = &comm->pdev->dev;
0107     mii_bus->priv = comm;
0108     mii_bus->read = spl2sw_mii_read;
0109     mii_bus->write = spl2sw_mii_write;
0110     snprintf(mii_bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(&comm->pdev->dev));
0111 
0112     ret = of_mdiobus_register(mii_bus, mdio_np);
0113     if (ret) {
0114         dev_err(&comm->pdev->dev, "Failed to register mdiobus!\n");
0115         goto out;
0116     }
0117 
0118     comm->mii_bus = mii_bus;
0119 
0120 out:
0121     of_node_put(mdio_np);
0122     return ret;
0123 }
0124 
0125 void spl2sw_mdio_remove(struct spl2sw_common *comm)
0126 {
0127     if (comm->mii_bus) {
0128         mdiobus_unregister(comm->mii_bus);
0129         comm->mii_bus = NULL;
0130     }
0131 }