mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-06 13:14:27 +00:00
237 lines
5.5 KiB
C
237 lines
5.5 KiB
C
|
// SPDX-License-Identifier: GPL-2.0+
|
||
|
/*
|
||
|
* Copyright (C) 2018 Marvell International Ltd.
|
||
|
* Author: Ken Ma<make@marvell.com>
|
||
|
*/
|
||
|
|
||
|
#include <common.h>
|
||
|
#include <dm.h>
|
||
|
#include <dm/device-internal.h>
|
||
|
#include <dm/lists.h>
|
||
|
#include <miiphy.h>
|
||
|
#include <phy.h>
|
||
|
#include <asm/io.h>
|
||
|
#include <wait_bit.h>
|
||
|
|
||
|
#define MVMDIO_SMI_DATA_SHIFT 0
|
||
|
#define MVMDIO_SMI_PHY_ADDR_SHIFT 16
|
||
|
#define MVMDIO_SMI_PHY_REG_SHIFT 21
|
||
|
#define MVMDIO_SMI_READ_OPERATION BIT(26)
|
||
|
#define MVMDIO_SMI_WRITE_OPERATION 0
|
||
|
#define MVMDIO_SMI_READ_VALID BIT(27)
|
||
|
#define MVMDIO_SMI_BUSY BIT(28)
|
||
|
|
||
|
#define MVMDIO_XSMI_MGNT_REG 0x0
|
||
|
#define MVMDIO_XSMI_PHYADDR_SHIFT 16
|
||
|
#define MVMDIO_XSMI_DEVADDR_SHIFT 21
|
||
|
#define MVMDIO_XSMI_WRITE_OPERATION (0x5 << 26)
|
||
|
#define MVMDIO_XSMI_READ_OPERATION (0x7 << 26)
|
||
|
#define MVMDIO_XSMI_READ_VALID BIT(29)
|
||
|
#define MVMDIO_XSMI_BUSY BIT(30)
|
||
|
#define MVMDIO_XSMI_ADDR_REG 0x8
|
||
|
|
||
|
enum mvmdio_bus_type {
|
||
|
BUS_TYPE_SMI,
|
||
|
BUS_TYPE_XSMI
|
||
|
};
|
||
|
|
||
|
struct mvmdio_priv {
|
||
|
void *mdio_base;
|
||
|
enum mvmdio_bus_type type;
|
||
|
};
|
||
|
|
||
|
static int mvmdio_smi_read(struct udevice *dev, int addr,
|
||
|
int devad, int reg)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
u32 val;
|
||
|
int ret;
|
||
|
|
||
|
if (devad != MDIO_DEVAD_NONE)
|
||
|
return -EOPNOTSUPP;
|
||
|
|
||
|
ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_SMI_BUSY,
|
||
|
false, CONFIG_SYS_HZ, false);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
writel(((addr << MVMDIO_SMI_PHY_ADDR_SHIFT) |
|
||
|
(reg << MVMDIO_SMI_PHY_REG_SHIFT) |
|
||
|
MVMDIO_SMI_READ_OPERATION),
|
||
|
priv->mdio_base);
|
||
|
|
||
|
ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_SMI_BUSY,
|
||
|
false, CONFIG_SYS_HZ, false);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
val = readl(priv->mdio_base);
|
||
|
if (!(val & MVMDIO_SMI_READ_VALID)) {
|
||
|
pr_err("SMI bus read not valid\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
return val & GENMASK(15, 0);
|
||
|
}
|
||
|
|
||
|
static int mvmdio_smi_write(struct udevice *dev, int addr, int devad,
|
||
|
int reg, u16 value)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
int ret;
|
||
|
|
||
|
if (devad != MDIO_DEVAD_NONE)
|
||
|
return -EOPNOTSUPP;
|
||
|
|
||
|
ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_SMI_BUSY,
|
||
|
false, CONFIG_SYS_HZ, false);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
writel(((addr << MVMDIO_SMI_PHY_ADDR_SHIFT) |
|
||
|
(reg << MVMDIO_SMI_PHY_REG_SHIFT) |
|
||
|
MVMDIO_SMI_WRITE_OPERATION |
|
||
|
(value << MVMDIO_SMI_DATA_SHIFT)),
|
||
|
priv->mdio_base);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mvmdio_xsmi_read(struct udevice *dev, int addr,
|
||
|
int devad, int reg)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
int ret;
|
||
|
|
||
|
if (devad == MDIO_DEVAD_NONE)
|
||
|
return -EOPNOTSUPP;
|
||
|
|
||
|
ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_XSMI_BUSY,
|
||
|
false, CONFIG_SYS_HZ, false);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
writel(reg & GENMASK(15, 0), priv->mdio_base + MVMDIO_XSMI_ADDR_REG);
|
||
|
writel(((addr << MVMDIO_XSMI_PHYADDR_SHIFT) |
|
||
|
(devad << MVMDIO_XSMI_DEVADDR_SHIFT) |
|
||
|
MVMDIO_XSMI_READ_OPERATION),
|
||
|
priv->mdio_base + MVMDIO_XSMI_MGNT_REG);
|
||
|
|
||
|
ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_XSMI_BUSY,
|
||
|
false, CONFIG_SYS_HZ, false);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
if (!(readl(priv->mdio_base + MVMDIO_XSMI_MGNT_REG) &
|
||
|
MVMDIO_XSMI_READ_VALID)) {
|
||
|
pr_err("XSMI bus read not valid\n");
|
||
|
return -ENODEV;
|
||
|
}
|
||
|
|
||
|
return readl(priv->mdio_base + MVMDIO_XSMI_MGNT_REG) & GENMASK(15, 0);
|
||
|
}
|
||
|
|
||
|
static int mvmdio_xsmi_write(struct udevice *dev, int addr, int devad,
|
||
|
int reg, u16 value)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
int ret;
|
||
|
|
||
|
if (devad == MDIO_DEVAD_NONE)
|
||
|
return -EOPNOTSUPP;
|
||
|
|
||
|
ret = wait_for_bit_le32(priv->mdio_base, MVMDIO_XSMI_BUSY,
|
||
|
false, CONFIG_SYS_HZ, false);
|
||
|
if (ret < 0)
|
||
|
return ret;
|
||
|
|
||
|
writel(reg & GENMASK(15, 0), priv->mdio_base + MVMDIO_XSMI_ADDR_REG);
|
||
|
writel(((addr << MVMDIO_XSMI_PHYADDR_SHIFT) |
|
||
|
(devad << MVMDIO_XSMI_DEVADDR_SHIFT) |
|
||
|
MVMDIO_XSMI_WRITE_OPERATION | value),
|
||
|
priv->mdio_base + MVMDIO_XSMI_MGNT_REG);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int mvmdio_read(struct udevice *dev, int addr, int devad, int reg)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
int err = -ENOTSUPP;
|
||
|
|
||
|
switch (priv->type) {
|
||
|
case BUS_TYPE_SMI:
|
||
|
err = mvmdio_smi_read(dev, addr, devad, reg);
|
||
|
break;
|
||
|
case BUS_TYPE_XSMI:
|
||
|
err = mvmdio_xsmi_read(dev, addr, devad, reg);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
static int mvmdio_write(struct udevice *dev, int addr, int devad, int reg,
|
||
|
u16 value)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
int err = -ENOTSUPP;
|
||
|
|
||
|
switch (priv->type) {
|
||
|
case BUS_TYPE_SMI:
|
||
|
err = mvmdio_smi_write(dev, addr, devad, reg, value);
|
||
|
break;
|
||
|
case BUS_TYPE_XSMI:
|
||
|
err = mvmdio_xsmi_write(dev, addr, devad, reg, value);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Name the device, we use the device tree node name.
|
||
|
* This can be overwritten by MDIO class code if device-name property is
|
||
|
* present.
|
||
|
*/
|
||
|
static int mvmdio_bind(struct udevice *dev)
|
||
|
{
|
||
|
if (ofnode_valid(dev->node))
|
||
|
device_set_name(dev, ofnode_get_name(dev->node));
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Get device base address and type, either C22 SMII or C45 XSMI */
|
||
|
static int mvmdio_probe(struct udevice *dev)
|
||
|
{
|
||
|
struct mvmdio_priv *priv = dev_get_priv(dev);
|
||
|
|
||
|
priv->mdio_base = (void *)dev_read_addr(dev);
|
||
|
priv->type = (enum mvmdio_bus_type)dev_get_driver_data(dev);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static const struct mdio_ops mvmdio_ops = {
|
||
|
.read = mvmdio_read,
|
||
|
.write = mvmdio_write,
|
||
|
};
|
||
|
|
||
|
static const struct udevice_id mvmdio_ids[] = {
|
||
|
{ .compatible = "marvell,orion-mdio", .data = BUS_TYPE_SMI },
|
||
|
{ .compatible = "marvell,xmdio", .data = BUS_TYPE_XSMI },
|
||
|
{ }
|
||
|
};
|
||
|
|
||
|
U_BOOT_DRIVER(mvmdio) = {
|
||
|
.name = "mvmdio",
|
||
|
.id = UCLASS_MDIO,
|
||
|
.of_match = mvmdio_ids,
|
||
|
.bind = mvmdio_bind,
|
||
|
.probe = mvmdio_probe,
|
||
|
.ops = &mvmdio_ops,
|
||
|
.priv_auto_alloc_size = sizeof(struct mvmdio_priv),
|
||
|
};
|
||
|
|