--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <asm/io.h>
+#include <dm.h>
+#include <linux/bitfield.h>
+#include <linux/iopoll.h>
+#include <miiphy.h>
+
+#define MT7531_PHY_IAC 0x701c
+#define MT7531_PHY_ACS_ST BIT(31)
+#define MT7531_MDIO_REG_ADDR_CL22 GENMASK(29, 25)
+#define MT7531_MDIO_DEV_ADDR MT7531_MDIO_REG_ADDR_CL22
+#define MT7531_MDIO_PHY_ADDR GENMASK(24, 20)
+#define MT7531_MDIO_CMD GENMASK(19, 18)
+#define MT7531_MDIO_CMD_READ_CL45 FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x3)
+#define MT7531_MDIO_CMD_READ_CL22 FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x2)
+#define MT7531_MDIO_CMD_WRITE FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x1)
+#define MT7531_MDIO_CMD_ADDR FIELD_PREP_CONST(MT7531_MDIO_CMD, 0x0)
+#define MT7531_MDIO_ST GENMASK(17, 16)
+#define MT7531_MDIO_ST_CL22 FIELD_PREP_CONST(MT7531_MDIO_ST, 0x1)
+#define MT7531_MDIO_ST_CL45 FIELD_PREP_CONST(MT7531_MDIO_ST, 0x0)
+#define MT7531_MDIO_RW_DATA GENMASK(15, 0)
+#define MT7531_MDIO_REG_ADDR_CL45 MT7531_MDIO_RW_DATA
+
+#define MT7531_MDIO_TIMEOUT 100000
+#define MT7531_MDIO_SLEEP 20
+
+struct mt7531_mdio_priv {
+ phys_addr_t switch_regs;
+};
+
+static int mt7531_mdio_wait_busy(struct mt7531_mdio_priv *priv)
+{
+ unsigned int busy;
+
+ return readl_poll_sleep_timeout(priv->switch_regs + MT7531_PHY_IAC,
+ busy, (busy & MT7531_PHY_ACS_ST) == 0,
+ MT7531_MDIO_SLEEP, MT7531_MDIO_TIMEOUT);
+}
+
+static int mt7531_mdio_read(struct mt7531_mdio_priv *priv, int addr, int devad, int reg)
+{
+ u32 val;
+
+ if (devad != MDIO_DEVAD_NONE) {
+ if (mt7531_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ val = MT7531_PHY_ACS_ST |
+ MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_ADDR |
+ FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
+ FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad) |
+ FIELD_PREP(MT7531_MDIO_REG_ADDR_CL45, reg);
+
+ writel(val, priv->switch_regs + MT7531_PHY_IAC);
+ }
+
+ if (mt7531_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ val = MT7531_PHY_ACS_ST | FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr);
+ if (devad != MDIO_DEVAD_NONE)
+ val |= MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_READ_CL45 |
+ FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad);
+ else
+ val |= MT7531_MDIO_ST_CL22 | MT7531_MDIO_CMD_READ_CL22 |
+ FIELD_PREP(MT7531_MDIO_REG_ADDR_CL22, reg);
+
+ writel(val, priv->switch_regs + MT7531_PHY_IAC);
+
+ if (mt7531_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ val = readl(priv->switch_regs + MT7531_PHY_IAC);
+ return val & MT7531_MDIO_RW_DATA;
+}
+
+static int mt7531_mdio_write(struct mt7531_mdio_priv *priv, int addr, int devad,
+ int reg, u16 value)
+{
+ u32 val;
+
+ if (devad != MDIO_DEVAD_NONE) {
+ if (mt7531_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ val = MT7531_PHY_ACS_ST |
+ MT7531_MDIO_ST_CL45 | MT7531_MDIO_CMD_ADDR |
+ FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
+ FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad) |
+ FIELD_PREP(MT7531_MDIO_REG_ADDR_CL45, reg);
+
+ writel(val, priv->switch_regs + MT7531_PHY_IAC);
+ }
+
+ if (mt7531_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ val = MT7531_PHY_ACS_ST | FIELD_PREP(MT7531_MDIO_PHY_ADDR, addr) |
+ MT7531_MDIO_CMD_WRITE | FIELD_PREP(MT7531_MDIO_RW_DATA, value);
+ if (devad != MDIO_DEVAD_NONE)
+ val |= MT7531_MDIO_ST_CL45 |
+ FIELD_PREP(MT7531_MDIO_DEV_ADDR, devad);
+ else
+ val |= MT7531_MDIO_ST_CL22 |
+ FIELD_PREP(MT7531_MDIO_REG_ADDR_CL22, reg);
+
+ writel(val, priv->switch_regs + MT7531_PHY_IAC);
+
+ if (mt7531_mdio_wait_busy(priv))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+int mt7531_mdio_mmio_read(struct mii_dev *bus, int addr, int devad, int reg)
+{
+ struct mt7531_mdio_priv *priv = bus->priv;
+
+ return mt7531_mdio_read(priv, addr, devad, reg);
+}
+
+int mt7531_mdio_mmio_write(struct mii_dev *bus, int addr, int devad,
+ int reg, u16 value)
+{
+ struct mt7531_mdio_priv *priv = bus->priv;
+
+ return mt7531_mdio_write(priv, addr, devad, reg, value);
+}
+
+static int dm_mt7531_mdio_read(struct udevice *dev, int addr, int devad, int reg)
+{
+ struct mt7531_mdio_priv *priv = dev_get_priv(dev);
+
+ return mt7531_mdio_read(priv, addr, devad, reg);
+}
+
+static int dm_mt7531_mdio_write(struct udevice *dev, int addr, int devad,
+ int reg, u16 value)
+{
+ struct mt7531_mdio_priv *priv = dev_get_priv(dev);
+
+ return mt7531_mdio_write(priv, addr, devad, reg, value);
+}
+
+static const struct mdio_ops mt7531_mdio_ops = {
+ .read = dm_mt7531_mdio_read,
+ .write = dm_mt7531_mdio_write,
+};
+
+static int mt7531_mdio_probe(struct udevice *dev)
+{
+ struct mt7531_mdio_priv *priv = dev_get_priv(dev);
+
+ priv->switch_regs = dev_read_addr(dev);
+ if (priv->switch_regs == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(mt7531_mdio) = {
+ .name = "mt7531-mdio-mmio",
+ .id = UCLASS_MDIO,
+ .probe = mt7531_mdio_probe,
+ .ops = &mt7531_mdio_ops,
+ .priv_auto = sizeof(struct mt7531_mdio_priv),
+};
* Author: Mark Lee <mark-mc.lee@mediatek.com>
*/
+#include <malloc.h>
#include <miiphy.h>
#include <linux/delay.h>
#include <linux/mdio.h>
#include "mtk_eth.h"
#include "mt753x.h"
+#include "../mdio-mt7531-mmio.h"
+
static int mt7988_reg_read(struct mt753x_switch_priv *priv, u32 reg, u32 *data)
{
*data = readl(priv->epriv.ethsys_base + GSW_BASE + reg);
static void mt7988_phy_setting(struct mt753x_switch_priv *priv)
{
+ struct mii_dev *mdio_bus = priv->mdio_bus;
u16 val;
u32 i;
for (i = 0; i < MT753X_NUM_PHYS; i++) {
+ u16 addr = MT753X_PHY_ADDR(priv->phy_base, i);
+
/* Set PHY to PHY page 1 */
- mt7531_mii_write(priv, i, 0x1f, 0x1);
+ mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+ 0x1f, 0x1);
/* Enable HW auto downshift */
- val = mt7531_mii_read(priv, i, PHY_EXT_REG_14);
+ val = mt7531_mdio_mmio_read(mdio_bus, addr, MDIO_DEVAD_NONE,
+ PHY_EXT_REG_14);
val |= PHY_EN_DOWN_SHFIT;
- mt7531_mii_write(priv, i, PHY_EXT_REG_14, val);
+ mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+ PHY_EXT_REG_14, val);
/* PHY link down power saving enable */
- val = mt7531_mii_read(priv, i, PHY_EXT_REG_17);
+ val = mt7531_mdio_mmio_read(mdio_bus, addr, MDIO_DEVAD_NONE,
+ PHY_EXT_REG_17);
val |= PHY_LINKDOWN_POWER_SAVING_EN;
- mt7531_mii_write(priv, i, PHY_EXT_REG_17, val);
+ mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+ PHY_EXT_REG_17, val);
/* Restore PHY to PHY page 0 */
- mt7531_mii_write(priv, i, 0x1f, 0x0);
+ mt7531_mdio_mmio_write(mdio_bus, addr, MDIO_DEVAD_NONE,
+ 0x1f, 0x0);
}
}
mt7988_reg_write(priv, PMCR_REG(6), pmcr);
}
+static int mt7988_mdio_register(struct mt753x_switch_priv *priv)
+{
+ struct mt7531_mdio_mmio_priv *mdio_priv;
+ struct mii_dev *mdio_bus = mdio_alloc();
+ int ret;
+
+ if (!mdio_bus)
+ return -ENOMEM;
+
+ mdio_priv = malloc(sizeof(*mdio_priv));
+ if (!mdio_priv)
+ return -ENOMEM;
+
+ mdio_priv->switch_regs = (phys_addr_t)priv->epriv.ethsys_base + GSW_BASE;
+
+ mdio_bus->read = mt7531_mdio_mmio_read;
+ mdio_bus->write = mt7531_mdio_mmio_write;
+ snprintf(mdio_bus->name, sizeof(mdio_bus->name), priv->epriv.sw->name);
+
+ mdio_bus->priv = mdio_priv;
+
+ ret = mdio_register(mdio_bus);
+ if (ret) {
+ free(mdio_bus->priv);
+ mdio_free(mdio_bus);
+ return ret;
+ }
+
+ priv->mdio_bus = mdio_bus;
+
+ return 0;
+}
+
static int mt7988_setup(struct mtk_eth_switch_priv *swpriv)
{
struct mt753x_switch_priv *priv = (struct mt753x_switch_priv *)swpriv;
- u16 phy_val;
+ struct mii_dev *mdio_bus;
+ u16 phy_addr, phy_val;
+ int ret, i;
u32 pmcr;
- int i;
priv->smi_addr = MT753X_DFL_SMI_ADDR;
priv->phy_base = (priv->smi_addr + 1) & MT753X_SMI_ADDR_MASK;
priv->reg_read = mt7988_reg_read;
priv->reg_write = mt7988_reg_write;
+ ret = mt7988_mdio_register(priv);
+ if (ret)
+ return ret;
+
+ mdio_bus = priv->mdio_bus;
+
/* Turn off PHYs */
for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_val = mt7531_mii_read(priv, i, MII_BMCR);
+ phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
+ phy_val = mt7531_mdio_mmio_read(mdio_bus, phy_addr,
+ MDIO_DEVAD_NONE, MII_BMCR);
phy_val |= BMCR_PDOWN;
- mt7531_mii_write(priv, i, MII_BMCR, phy_val);
+ mt7531_mdio_mmio_write(mdio_bus, phy_addr, MDIO_DEVAD_NONE,
+ MII_BMCR, phy_val);
}
switch (priv->epriv.phy_interface) {
/* Turn on PHYs */
for (i = 0; i < MT753X_NUM_PHYS; i++) {
- phy_val = mt7531_mii_read(priv, i, MII_BMCR);
+ phy_addr = MT753X_PHY_ADDR(priv->phy_base, i);
+ phy_val = mt7531_mdio_mmio_read(mdio_bus, phy_addr,
+ MDIO_DEVAD_NONE, MII_BMCR);
phy_val &= ~BMCR_PDOWN;
- mt7531_mii_write(priv, i, MII_BMCR, phy_val);
+ mt7531_mdio_mmio_write(mdio_bus, phy_addr, MDIO_DEVAD_NONE,
+ MII_BMCR, phy_val);
}
mt7988_phy_setting(priv);
- return mt7531_mdio_register(priv);
+ return 0;
}
static int mt7988_cleanup(struct mtk_eth_switch_priv *swpriv)
struct mii_dev *mdio_bus = priv->mdio_bus;
mdio_unregister(mdio_bus);
+ free(mdio_bus->priv);
mdio_free(mdio_bus);
return 0;