6c96446859
On some boards MDIO may get stuck if it detects echo on the line. This is a know hardware issue, there is a board fix for it. In case we're running on a board that doesn't have the fix, we don't want to loop here forever and freeze U-Boot. Signed-off-by: Alex Marginean <alexandru.marginean@nxp.com> Acked-by: Joe Hershberger <joe.hershberger@ni.com>
156 lines
3.8 KiB
C
156 lines
3.8 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* ENETC ethernet controller driver
|
|
* Copyright 2019 NXP
|
|
*/
|
|
|
|
#include <common.h>
|
|
#include <dm.h>
|
|
#include <errno.h>
|
|
#include <pci.h>
|
|
#include <miiphy.h>
|
|
#include <asm/io.h>
|
|
#include <asm/processor.h>
|
|
#include <miiphy.h>
|
|
|
|
#include "fsl_enetc.h"
|
|
|
|
static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv)
|
|
{
|
|
int to = 10000;
|
|
|
|
while ((enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY) &&
|
|
--to)
|
|
cpu_relax();
|
|
if (!to)
|
|
printf("T");
|
|
}
|
|
|
|
int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad,
|
|
int reg)
|
|
{
|
|
if (devad == MDIO_DEVAD_NONE)
|
|
enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
|
|
else
|
|
enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
|
|
enetc_mdio_wait_bsy(priv);
|
|
|
|
if (devad == MDIO_DEVAD_NONE) {
|
|
enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
|
|
(addr << 5) | reg);
|
|
} else {
|
|
enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
|
|
enetc_mdio_wait_bsy(priv);
|
|
|
|
enetc_write(priv, ENETC_MDIO_STAT, reg);
|
|
enetc_mdio_wait_bsy(priv);
|
|
|
|
enetc_write(priv, ENETC_MDIO_CTL, ENETC_MDIO_CTL_READ |
|
|
(addr << 5) | devad);
|
|
}
|
|
|
|
enetc_mdio_wait_bsy(priv);
|
|
if (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_RD_ER)
|
|
return ENETC_MDIO_READ_ERR;
|
|
|
|
return enetc_read(priv, ENETC_MDIO_DATA);
|
|
}
|
|
|
|
int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad,
|
|
int reg, u16 val)
|
|
{
|
|
if (devad == MDIO_DEVAD_NONE)
|
|
enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);
|
|
else
|
|
enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C45);
|
|
enetc_mdio_wait_bsy(priv);
|
|
|
|
if (devad != MDIO_DEVAD_NONE) {
|
|
enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + devad);
|
|
enetc_write(priv, ENETC_MDIO_STAT, reg);
|
|
} else {
|
|
enetc_write(priv, ENETC_MDIO_CTL, (addr << 5) + reg);
|
|
}
|
|
enetc_mdio_wait_bsy(priv);
|
|
|
|
enetc_write(priv, ENETC_MDIO_DATA, val);
|
|
enetc_mdio_wait_bsy(priv);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* DM wrappers */
|
|
static int dm_enetc_mdio_read(struct udevice *dev, int addr, int devad, int reg)
|
|
{
|
|
struct enetc_mdio_priv *priv = dev_get_priv(dev);
|
|
|
|
return enetc_mdio_read_priv(priv, addr, devad, reg);
|
|
}
|
|
|
|
static int dm_enetc_mdio_write(struct udevice *dev, int addr, int devad,
|
|
int reg, u16 val)
|
|
{
|
|
struct enetc_mdio_priv *priv = dev_get_priv(dev);
|
|
|
|
return enetc_mdio_write_priv(priv, addr, devad, reg, val);
|
|
}
|
|
|
|
static const struct mdio_ops enetc_mdio_ops = {
|
|
.read = dm_enetc_mdio_read,
|
|
.write = dm_enetc_mdio_write,
|
|
};
|
|
|
|
static int enetc_mdio_bind(struct udevice *dev)
|
|
{
|
|
char name[16];
|
|
static int eth_num_devices;
|
|
|
|
/*
|
|
* prefer using PCI function numbers to number interfaces, but these
|
|
* are only available if dts nodes are present. For PCI they are
|
|
* optional, handle that case too. Just in case some nodes are present
|
|
* and some are not, use different naming scheme - enetc-N based on
|
|
* PCI function # and enetc#N based on interface count
|
|
*/
|
|
if (ofnode_valid(dev->node))
|
|
sprintf(name, "emdio-%u", PCI_FUNC(pci_get_devfn(dev)));
|
|
else
|
|
sprintf(name, "emdio#%u", eth_num_devices++);
|
|
device_set_name(dev, name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int enetc_mdio_probe(struct udevice *dev)
|
|
{
|
|
struct enetc_mdio_priv *priv = dev_get_priv(dev);
|
|
|
|
priv->regs_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0);
|
|
if (!priv->regs_base) {
|
|
enetc_dbg(dev, "failed to map BAR0\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
priv->regs_base += ENETC_MDIO_BASE;
|
|
|
|
dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_DRIVER(enetc_mdio) = {
|
|
.name = "enetc_mdio",
|
|
.id = UCLASS_MDIO,
|
|
.bind = enetc_mdio_bind,
|
|
.probe = enetc_mdio_probe,
|
|
.ops = &enetc_mdio_ops,
|
|
.priv_auto_alloc_size = sizeof(struct enetc_mdio_priv),
|
|
};
|
|
|
|
static struct pci_device_id enetc_mdio_ids[] = {
|
|
{ PCI_DEVICE(PCI_VENDOR_ID_FREESCALE, PCI_DEVICE_ID_ENETC_MDIO) },
|
|
{ }
|
|
};
|
|
|
|
U_BOOT_PCI_DEVICE(enetc_mdio, enetc_mdio_ids);
|