mirror of
https://github.com/u-boot/u-boot.git
synced 2025-04-29 17:55:48 +00:00

As part of bringing the master branch back in to next, we need to allow for all of these changes to exist here. Reported-by: Jonas Karlman <jonas@kwiboo.se> Signed-off-by: Tom Rini <trini@konsulko.com>
609 lines
14 KiB
C
609 lines
14 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* CPSW MDIO generic driver for TI AMxx/K2x/EMAC devices.
|
|
*
|
|
* Copyright (C) 2018 Texas Instruments Incorporated - https://www.ti.com/
|
|
*/
|
|
|
|
#include <clk.h>
|
|
#include <dm/device_compat.h>
|
|
#include <log.h>
|
|
#include <malloc.h>
|
|
#include <phy.h>
|
|
#include <asm/io.h>
|
|
#include <miiphy.h>
|
|
#include <soc.h>
|
|
#include <wait_bit.h>
|
|
#include <linux/bitops.h>
|
|
#include <linux/delay.h>
|
|
|
|
struct cpsw_mdio_regs {
|
|
u32 version;
|
|
u32 control;
|
|
#define CONTROL_IDLE BIT(31)
|
|
#define CONTROL_ENABLE BIT(30)
|
|
#define CONTROL_FAULT BIT(19)
|
|
#define CONTROL_FAULT_ENABLE BIT(18)
|
|
#define CONTROL_DIV_MASK GENMASK(15, 0)
|
|
#define CONTROL_MAX_DIV CONTROL_DIV_MASK
|
|
|
|
#define MDIO_MAN_MDCLK_O BIT(2)
|
|
#define MDIO_MAN_OE BIT(1)
|
|
#define MDIO_MAN_PIN BIT(0)
|
|
#define MDIO_MANUALMODE BIT(31)
|
|
|
|
u32 alive;
|
|
u32 link;
|
|
u32 linkintraw;
|
|
u32 linkintmasked;
|
|
u32 __reserved_0[2];
|
|
u32 userintraw;
|
|
u32 userintmasked;
|
|
u32 userintmaskset;
|
|
u32 userintmaskclr;
|
|
u32 manualif;
|
|
u32 poll;
|
|
u32 __reserved_1[18];
|
|
|
|
struct {
|
|
u32 access;
|
|
u32 physel;
|
|
#define USERACCESS_GO BIT(31)
|
|
#define USERACCESS_WRITE BIT(30)
|
|
#define USERACCESS_ACK BIT(29)
|
|
#define USERACCESS_READ (0)
|
|
#define USERACCESS_PHY_REG_SHIFT (21)
|
|
#define USERACCESS_PHY_ADDR_SHIFT (16)
|
|
#define USERACCESS_DATA GENMASK(15, 0)
|
|
} user[2];
|
|
};
|
|
|
|
#define CPSW_MDIO_DIV_DEF 0xff
|
|
#define PHY_REG_MASK 0x1f
|
|
#define PHY_ID_MASK 0x1f
|
|
|
|
#define MDIO_BITRANGE 0x8000
|
|
#define C22_READ_PATTERN 0x6
|
|
#define C22_WRITE_PATTERN 0x5
|
|
#define C22_BITRANGE 0x8
|
|
#define PHY_BITRANGE 0x10
|
|
#define PHY_DATA_BITRANGE 0x8000
|
|
|
|
/*
|
|
* This timeout definition is a worst-case ultra defensive measure against
|
|
* unexpected controller lock ups. Ideally, we should never ever hit this
|
|
* scenario in practice.
|
|
*/
|
|
#define CPSW_MDIO_TIMEOUT 100 /* msecs */
|
|
|
|
#define CPSW_MDIO_DEF_BUS_FREQ 2200000 /* 2.2 MHz */
|
|
|
|
enum cpsw_mdio_manual {
|
|
MDIO_PIN = 0,
|
|
MDIO_OE,
|
|
MDIO_MDCLK,
|
|
};
|
|
|
|
struct cpsw_mdio {
|
|
struct cpsw_mdio_regs *regs;
|
|
struct mii_dev *bus;
|
|
int div;
|
|
bool manual_mode;
|
|
struct clk clk;
|
|
unsigned long bus_freq;
|
|
};
|
|
|
|
static int cpsw_mdio_enable(struct cpsw_mdio *data)
|
|
{
|
|
int ret;
|
|
|
|
/* set enable and clock divider */
|
|
writel(data->div | CONTROL_ENABLE, &data->regs->control);
|
|
ret = wait_for_bit_le32(&data->regs->control,
|
|
CONTROL_IDLE, false, CPSW_MDIO_TIMEOUT, true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
/*
|
|
* wait for scan logic to settle:
|
|
* the scan time consists of (a) a large fixed component, and (b) a
|
|
* small component that varies with the mii bus frequency. These
|
|
* were estimated using measurements at 1.1 and 2.2 MHz on tnetv107x
|
|
* silicon. Since the effect of (b) was found to be largely
|
|
* negligible, we keep things simple here.
|
|
*/
|
|
mdelay(1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void cpsw_mdio_disable(struct cpsw_mdio *mdio)
|
|
{
|
|
u32 reg;
|
|
/* Disable MDIO state machine */
|
|
reg = readl(&mdio->regs->control);
|
|
reg &= ~CONTROL_ENABLE;
|
|
|
|
writel(reg, &mdio->regs->control);
|
|
}
|
|
|
|
static void cpsw_mdio_enable_manual_mode(struct cpsw_mdio *mdio)
|
|
{
|
|
u32 reg;
|
|
|
|
/* set manual mode */
|
|
reg = readl(&mdio->regs->poll);
|
|
reg |= MDIO_MANUALMODE;
|
|
|
|
writel(reg, &mdio->regs->poll);
|
|
}
|
|
|
|
static void cpsw_mdio_sw_set_bit(struct cpsw_mdio *mdio,
|
|
enum cpsw_mdio_manual bit)
|
|
{
|
|
u32 reg;
|
|
|
|
reg = readl(&mdio->regs->manualif);
|
|
|
|
switch (bit) {
|
|
case MDIO_OE:
|
|
reg |= MDIO_MAN_OE;
|
|
writel(reg, &mdio->regs->manualif);
|
|
break;
|
|
case MDIO_PIN:
|
|
reg |= MDIO_MAN_PIN;
|
|
writel(reg, &mdio->regs->manualif);
|
|
break;
|
|
case MDIO_MDCLK:
|
|
reg |= MDIO_MAN_MDCLK_O;
|
|
writel(reg, &mdio->regs->manualif);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
static void cpsw_mdio_sw_clr_bit(struct cpsw_mdio *mdio,
|
|
enum cpsw_mdio_manual bit)
|
|
{
|
|
u32 reg;
|
|
|
|
reg = readl(&mdio->regs->manualif);
|
|
|
|
switch (bit) {
|
|
case MDIO_OE:
|
|
reg &= ~MDIO_MAN_OE;
|
|
writel(reg, &mdio->regs->manualif);
|
|
break;
|
|
case MDIO_PIN:
|
|
reg &= ~MDIO_MAN_PIN;
|
|
writel(reg, &mdio->regs->manualif);
|
|
break;
|
|
case MDIO_MDCLK:
|
|
reg = readl(&mdio->regs->manualif);
|
|
reg &= ~MDIO_MAN_MDCLK_O;
|
|
writel(reg, &mdio->regs->manualif);
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
}
|
|
|
|
static int cpsw_mdio_test_man_bit(struct cpsw_mdio *mdio,
|
|
enum cpsw_mdio_manual bit)
|
|
{
|
|
u32 reg;
|
|
|
|
reg = readl(&mdio->regs->manualif);
|
|
return test_bit(bit, ®);
|
|
}
|
|
|
|
static void cpsw_mdio_toggle_man_bit(struct cpsw_mdio *mdio,
|
|
enum cpsw_mdio_manual bit)
|
|
{
|
|
cpsw_mdio_sw_clr_bit(mdio, bit);
|
|
cpsw_mdio_sw_set_bit(mdio, bit);
|
|
}
|
|
|
|
static void cpsw_mdio_man_send_pattern(struct cpsw_mdio *mdio,
|
|
u32 bitrange, u32 val)
|
|
{
|
|
u32 i;
|
|
|
|
for (i = bitrange; i; i = i >> 1) {
|
|
if (i & val)
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_PIN);
|
|
else
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_PIN);
|
|
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
}
|
|
}
|
|
|
|
static void cpsw_mdio_sw_preamble(struct cpsw_mdio *mdio)
|
|
{
|
|
u32 i;
|
|
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_OE);
|
|
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_MDCLK);
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
}
|
|
}
|
|
|
|
#if defined(CONFIG_DM_MDIO)
|
|
#define MII_TO_CPSW_MDIO(bus) (dev_get_priv((struct udevice *)(bus)->priv))
|
|
#else
|
|
#define MII_TO_CPSW_MDIO(bus) ((bus)->priv)
|
|
#endif
|
|
|
|
static int cpsw_mdio_sw_read(struct mii_dev *bus, int phy_id,
|
|
int dev_addr, int phy_reg)
|
|
{
|
|
struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
|
|
u32 reg, i;
|
|
u8 ack;
|
|
|
|
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
|
|
return -EINVAL;
|
|
|
|
cpsw_mdio_disable(mdio);
|
|
cpsw_mdio_enable_manual_mode(mdio);
|
|
cpsw_mdio_sw_preamble(mdio);
|
|
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_OE);
|
|
|
|
/* Issue clause 22 MII read function {0,1,1,0} */
|
|
cpsw_mdio_man_send_pattern(mdio, C22_BITRANGE, C22_READ_PATTERN);
|
|
|
|
/* Send the device number MSB first */
|
|
cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_id);
|
|
|
|
/* Send the register number MSB first */
|
|
cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_reg);
|
|
|
|
/* Send turn around cycles */
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_OE);
|
|
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
|
|
ack = cpsw_mdio_test_man_bit(mdio, MDIO_PIN);
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
|
|
reg = 0;
|
|
if (ack == 0) {
|
|
for (i = MDIO_BITRANGE; i; i = i >> 1) {
|
|
if (cpsw_mdio_test_man_bit(mdio, MDIO_PIN))
|
|
reg |= i;
|
|
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
}
|
|
} else {
|
|
for (i = MDIO_BITRANGE; i; i = i >> 1)
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
|
|
reg = 0xFFFF;
|
|
}
|
|
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
|
|
return reg;
|
|
}
|
|
|
|
static int cpsw_mdio_sw_write(struct mii_dev *bus, int phy_id,
|
|
int dev_addr, int phy_reg, u16 phy_data)
|
|
{
|
|
struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
|
|
|
|
if ((phy_reg & ~PHY_REG_MASK) || (phy_id & ~PHY_ID_MASK))
|
|
return -EINVAL;
|
|
|
|
cpsw_mdio_disable(mdio);
|
|
cpsw_mdio_enable_manual_mode(mdio);
|
|
cpsw_mdio_sw_preamble(mdio);
|
|
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_OE);
|
|
|
|
/* Issue clause 22 MII write function {0,1,0,1} */
|
|
cpsw_mdio_man_send_pattern(mdio, C22_BITRANGE, C22_WRITE_PATTERN);
|
|
|
|
/* Send the device number MSB first */
|
|
cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_id);
|
|
|
|
/* Send the register number MSB first */
|
|
cpsw_mdio_man_send_pattern(mdio, PHY_BITRANGE, phy_reg);
|
|
|
|
/* set turn-around cycles */
|
|
cpsw_mdio_sw_set_bit(mdio, MDIO_PIN);
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_PIN);
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
|
|
/* Send Register data MSB first */
|
|
cpsw_mdio_man_send_pattern(mdio, PHY_DATA_BITRANGE, phy_data);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_OE);
|
|
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_sw_clr_bit(mdio, MDIO_MDCLK);
|
|
cpsw_mdio_toggle_man_bit(mdio, MDIO_MDCLK);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* wait until hardware is ready for another user access */
|
|
static int cpsw_mdio_wait_for_user_access(struct cpsw_mdio *mdio)
|
|
{
|
|
return wait_for_bit_le32(&mdio->regs->user[0].access,
|
|
USERACCESS_GO, false,
|
|
CPSW_MDIO_TIMEOUT, false);
|
|
}
|
|
|
|
static int cpsw_mdio_read(struct mii_dev *bus, int phy_id,
|
|
int dev_addr, int phy_reg)
|
|
{
|
|
struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
|
|
int data, ret;
|
|
u32 reg;
|
|
|
|
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
|
|
return -EINVAL;
|
|
|
|
ret = cpsw_mdio_wait_for_user_access(mdio);
|
|
if (ret)
|
|
return ret;
|
|
reg = (USERACCESS_GO | USERACCESS_READ |
|
|
(phy_reg << USERACCESS_PHY_REG_SHIFT) |
|
|
(phy_id << USERACCESS_PHY_ADDR_SHIFT));
|
|
writel(reg, &mdio->regs->user[0].access);
|
|
ret = cpsw_mdio_wait_for_user_access(mdio);
|
|
if (ret)
|
|
return ret;
|
|
|
|
reg = readl(&mdio->regs->user[0].access);
|
|
data = (reg & USERACCESS_ACK) ? (reg & USERACCESS_DATA) : -1;
|
|
return data;
|
|
}
|
|
|
|
static int cpsw_mdio_write(struct mii_dev *bus, int phy_id, int dev_addr,
|
|
int phy_reg, u16 data)
|
|
{
|
|
struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
|
|
u32 reg;
|
|
int ret;
|
|
|
|
if (phy_reg & ~PHY_REG_MASK || phy_id & ~PHY_ID_MASK)
|
|
return -EINVAL;
|
|
|
|
ret = cpsw_mdio_wait_for_user_access(mdio);
|
|
if (ret)
|
|
return ret;
|
|
reg = (USERACCESS_GO | USERACCESS_WRITE |
|
|
(phy_reg << USERACCESS_PHY_REG_SHIFT) |
|
|
(phy_id << USERACCESS_PHY_ADDR_SHIFT) |
|
|
(data & USERACCESS_DATA));
|
|
writel(reg, &mdio->regs->user[0].access);
|
|
|
|
return cpsw_mdio_wait_for_user_access(mdio);
|
|
}
|
|
|
|
#if !defined(CONFIG_MDIO_TI_CPSW)
|
|
u32 cpsw_mdio_get_alive(struct mii_dev *bus)
|
|
{
|
|
struct cpsw_mdio *mdio = MII_TO_CPSW_MDIO(bus);
|
|
u32 val;
|
|
|
|
val = readl(&mdio->regs->alive);
|
|
return val & GENMASK(7, 0);
|
|
}
|
|
|
|
struct mii_dev *cpsw_mdio_init(const char *name, phys_addr_t mdio_base,
|
|
u32 bus_freq, int fck_freq, bool manual_mode)
|
|
{
|
|
struct cpsw_mdio *cpsw_mdio;
|
|
int ret;
|
|
|
|
cpsw_mdio = calloc(1, sizeof(*cpsw_mdio));
|
|
if (!cpsw_mdio) {
|
|
debug("failed to alloc cpsw_mdio\n");
|
|
return NULL;
|
|
}
|
|
|
|
cpsw_mdio->bus = mdio_alloc();
|
|
if (!cpsw_mdio->bus) {
|
|
debug("failed to alloc mii bus\n");
|
|
free(cpsw_mdio);
|
|
return NULL;
|
|
}
|
|
|
|
cpsw_mdio->regs = (struct cpsw_mdio_regs *)(uintptr_t)mdio_base;
|
|
|
|
if (!bus_freq || !fck_freq)
|
|
cpsw_mdio->div = CPSW_MDIO_DIV_DEF;
|
|
else
|
|
cpsw_mdio->div = (fck_freq / bus_freq) - 1;
|
|
cpsw_mdio->div &= CONTROL_DIV_MASK;
|
|
ret = cpsw_mdio_enable(cpsw_mdio);
|
|
if (ret) {
|
|
debug("mdio_enable failed: %d\n", ret);
|
|
goto free_bus;
|
|
}
|
|
|
|
if (manual_mode) {
|
|
cpsw_mdio->bus->read = cpsw_mdio_sw_read;
|
|
cpsw_mdio->bus->write = cpsw_mdio_sw_write;
|
|
} else {
|
|
cpsw_mdio->bus->read = cpsw_mdio_read;
|
|
cpsw_mdio->bus->write = cpsw_mdio_write;
|
|
}
|
|
|
|
cpsw_mdio->bus->priv = cpsw_mdio;
|
|
snprintf(cpsw_mdio->bus->name, sizeof(cpsw_mdio->bus->name), name);
|
|
|
|
ret = mdio_register(cpsw_mdio->bus);
|
|
if (ret < 0) {
|
|
debug("failed to register mii bus\n");
|
|
goto free_bus;
|
|
}
|
|
|
|
return cpsw_mdio->bus;
|
|
|
|
free_bus:
|
|
mdio_free(cpsw_mdio->bus);
|
|
free(cpsw_mdio);
|
|
return NULL;
|
|
}
|
|
|
|
void cpsw_mdio_free(struct mii_dev *bus)
|
|
{
|
|
struct cpsw_mdio *mdio = bus->priv;
|
|
u32 reg;
|
|
|
|
/* disable mdio */
|
|
reg = readl(&mdio->regs->control);
|
|
reg &= ~CONTROL_ENABLE;
|
|
writel(reg, &mdio->regs->control);
|
|
|
|
mdio_unregister(bus);
|
|
mdio_free(bus);
|
|
free(mdio);
|
|
}
|
|
|
|
#else
|
|
|
|
static int cpsw_mdio_init_clk(struct cpsw_mdio *data)
|
|
{
|
|
u32 mdio_in, div;
|
|
|
|
mdio_in = clk_get_rate(&data->clk);
|
|
div = (mdio_in / data->bus_freq) - 1;
|
|
if (div > CONTROL_MAX_DIV)
|
|
div = CONTROL_MAX_DIV;
|
|
|
|
data->div = div;
|
|
return cpsw_mdio_enable(data);
|
|
}
|
|
|
|
static int cpsw_mdio_bus_read(struct udevice *dev, int addr,
|
|
int devad, int reg)
|
|
{
|
|
struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
|
|
NULL;
|
|
struct cpsw_mdio *priv = dev_get_priv(dev);
|
|
|
|
if (pdata && pdata->mii_bus) {
|
|
if (priv->manual_mode)
|
|
return cpsw_mdio_sw_read(pdata->mii_bus, addr, devad, reg);
|
|
else
|
|
return cpsw_mdio_read(pdata->mii_bus, addr, devad, reg);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int cpsw_mdio_bus_write(struct udevice *dev, int addr,
|
|
int devad, int reg, u16 val)
|
|
{
|
|
struct mdio_perdev_priv *pdata = (dev) ? dev_get_uclass_priv(dev) :
|
|
NULL;
|
|
struct cpsw_mdio *priv = dev_get_priv(dev);
|
|
|
|
if (pdata && pdata->mii_bus) {
|
|
if (priv->manual_mode)
|
|
return cpsw_mdio_sw_write(pdata->mii_bus, addr, devad, reg, val);
|
|
else
|
|
return cpsw_mdio_write(pdata->mii_bus, addr, devad, reg, val);
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static const struct mdio_ops cpsw_mdio_ops = {
|
|
.read = cpsw_mdio_bus_read,
|
|
.write = cpsw_mdio_bus_write,
|
|
};
|
|
|
|
static const struct soc_attr k3_mdio_soc_data[] = {
|
|
{ .family = "AM62X", .revision = "SR1.0" },
|
|
{ .family = "AM64X", .revision = "SR1.0" },
|
|
{ .family = "AM64X", .revision = "SR2.0" },
|
|
{ .family = "AM65X", .revision = "SR1.0" },
|
|
{ .family = "AM65X", .revision = "SR2.0" },
|
|
{ .family = "J7200", .revision = "SR1.0" },
|
|
{ .family = "J7200", .revision = "SR2.0" },
|
|
{ .family = "J721E", .revision = "SR1.0" },
|
|
{ .family = "J721E", .revision = "SR1.1" },
|
|
{ .family = "J721S2", .revision = "SR1.0" },
|
|
{ /* sentinel */ },
|
|
};
|
|
|
|
static const struct udevice_id cpsw_mdio_ids[] = {
|
|
{ .compatible = "ti,davinci_mdio", },
|
|
{ .compatible = "ti,cpsw-mdio", },
|
|
{ /* sentinel */ },
|
|
};
|
|
|
|
static int cpsw_mdio_probe(struct udevice *dev)
|
|
{
|
|
struct cpsw_mdio *priv = dev_get_priv(dev);
|
|
int ret;
|
|
|
|
if (!priv) {
|
|
dev_err(dev, "dev_get_priv(dev %p) = NULL\n", dev);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
priv->regs = dev_remap_addr(dev);
|
|
|
|
if (soc_device_match(k3_mdio_soc_data))
|
|
priv->manual_mode = true;
|
|
|
|
ret = clk_get_by_name(dev, "fck", &priv->clk);
|
|
if (ret) {
|
|
dev_err(dev, "failed to get clock %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
priv->bus_freq = dev_read_u32_default(dev, "bus_freq",
|
|
CPSW_MDIO_DEF_BUS_FREQ);
|
|
ret = cpsw_mdio_init_clk(priv);
|
|
if (ret) {
|
|
dev_err(dev, "init clock failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cpsw_mdio_remove(struct udevice *dev)
|
|
{
|
|
struct cpsw_mdio *priv = dev_get_priv(dev);
|
|
|
|
cpsw_mdio_disable(priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_DRIVER(cpsw_mdio) = {
|
|
.name = "cpsw_mdio",
|
|
.id = UCLASS_MDIO,
|
|
.of_match = cpsw_mdio_ids,
|
|
.probe = cpsw_mdio_probe,
|
|
.remove = cpsw_mdio_remove,
|
|
.ops = &cpsw_mdio_ops,
|
|
.priv_auto = sizeof(struct cpsw_mdio),
|
|
};
|
|
#endif /* CONFIG_MDIO_TI_CPSW */
|