Files
u-boot/drivers/net/mdio-mscc-miim.c
Robert Marko dfc39f9caf net: add Microsemi/Microchip MDIO driver
Add Microsemi/Microchip MDIO driver for interfaces found in their network
switches.

Driver is based on the Linux version.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Acked-by: Jerome Forissier <jerome@forissier.org>
2026-01-15 11:09:28 +01:00

137 lines
3.3 KiB
C

// SPDX-License-Identifier: GPL-2.0+
#include <asm/io.h>
#include <dm.h>
#include <time.h>
#include <regmap.h>
#include <miiphy.h>
#include <linux/bitfield.h>
#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),
};