// SPDX-License-Identifier: GPL-2.0+ #include #include #include #include #include #include #define MSCC_MIIM_REG_STATUS 0x0 #define MSCC_MIIM_STATUS_STAT_PENDING BIT(2) #define MSCC_MIIM_STATUS_STAT_BUSY BIT(3) #define MSCC_MIIM_REG_CMD 0x8 #define MSCC_MIIM_CMD_OPR_WRITE BIT(1) #define MSCC_MIIM_CMD_OPR_READ BIT(2) #define MSCC_MIIM_CMD_WRDATA_SHIFT 4 #define MSCC_MIIM_CMD_REGAD_SHIFT 20 #define MSCC_MIIM_CMD_PHYAD_SHIFT 25 #define MSCC_MIIM_CMD_VLD BIT(31) #define MSCC_MIIM_REG_DATA 0xC #define MSCC_MIIM_DATA_ERROR (BIT(16) | BIT(17)) #define MSCC_MIIM_DATA_MASK GENMASK(15, 0) #define MSCC_MIIM_REG_CFG 0x10 #define MSCC_MIIM_CFG_PRESCALE_MASK GENMASK(7, 0) /* 01 = Clause 22, 00 = Clause 45 */ #define MSCC_MIIM_CFG_ST_CFG_MASK GENMASK(10, 9) #define MSCC_MIIM_C22 1 #define MSCC_MIIM_C45 0 #define MSCC_MDIO_TIMEOUT 10000 #define MSCC_MDIO_SLEEP 50 struct mscc_mdio_priv { struct regmap *map; }; static int mscc_mdio_wait_busy(struct mscc_mdio_priv *priv) { u32 busy; return regmap_read_poll_timeout(priv->map, MSCC_MIIM_REG_STATUS, busy, (busy & MSCC_MIIM_STATUS_STAT_BUSY) == 0, MSCC_MDIO_SLEEP, MSCC_MDIO_TIMEOUT); } static int mscc_mdio_read(struct udevice *dev, int addr, int devad, int reg) { struct mscc_mdio_priv *priv = dev_get_priv(dev); u32 val; int ret; if (mscc_mdio_wait_busy(priv)) return -ETIMEDOUT; ret = regmap_write(priv->map, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD | (addr << MSCC_MIIM_CMD_PHYAD_SHIFT) | (reg << MSCC_MIIM_CMD_REGAD_SHIFT) | MSCC_MIIM_CMD_OPR_READ); if (ret) return ret; if (mscc_mdio_wait_busy(priv)) return -ETIMEDOUT; regmap_read(priv->map, MSCC_MIIM_REG_DATA, &val); if (val & MSCC_MIIM_DATA_ERROR) return -EIO; return FIELD_GET(MSCC_MIIM_DATA_MASK, val); } int mscc_mdio_write(struct udevice *dev, int addr, int devad, int reg, u16 val) { struct mscc_mdio_priv *priv = dev_get_priv(dev); int ret; if (mscc_mdio_wait_busy(priv)) return -ETIMEDOUT; ret = regmap_write(priv->map, MSCC_MIIM_REG_CMD, MSCC_MIIM_CMD_VLD | (addr << MSCC_MIIM_CMD_PHYAD_SHIFT) | (reg << MSCC_MIIM_CMD_REGAD_SHIFT) | (val << MSCC_MIIM_CMD_WRDATA_SHIFT) | MSCC_MIIM_CMD_OPR_WRITE); return ret; } static const struct mdio_ops mscc_mdio_ops = { .read = mscc_mdio_read, .write = mscc_mdio_write, }; static int mscc_mdio_bind(struct udevice *dev) { if (ofnode_valid(dev_ofnode(dev))) device_set_name(dev, ofnode_get_name(dev_ofnode(dev))); return 0; } static int mscc_mdio_probe(struct udevice *dev) { struct mscc_mdio_priv *priv = dev_get_priv(dev); int ret; ret = regmap_init_mem(dev_ofnode(dev), &priv->map); if (ret) return -EINVAL; /* Enter Clause 22 mode */ ret = regmap_update_bits(priv->map, MSCC_MIIM_REG_CFG, MSCC_MIIM_CFG_ST_CFG_MASK, FIELD_PREP(MSCC_MIIM_CFG_ST_CFG_MASK, MSCC_MIIM_C22)); return ret; } static const struct udevice_id mscc_mdio_ids[] = { { .compatible = "mscc,ocelot-miim", }, { } }; U_BOOT_DRIVER(mscc_mdio) = { .name = "mscc_mdio", .id = UCLASS_MDIO, .of_match = mscc_mdio_ids, .bind = mscc_mdio_bind, .probe = mscc_mdio_probe, .ops = &mscc_mdio_ops, .priv_auto = sizeof(struct mscc_mdio_priv), };