mirror of
https://github.com/brain-hackers/u-boot-brain
synced 2024-09-30 00:20:44 +09:00
drivers: net: add NXP ENETC MDIO driver
Adds a driver for the MDIO interface currently integrated in LS1028A SoC. This MDIO interface is shared by multiple ethernet interfaces and is presented as a stand-alone PCI function on the SoC ECAM. Ethernet has a functional dependency on MDIO, for simplicity there is a single config option for both. Signed-off-by: Alex Marginean <alexm.osslist@gmail.com> Reviewed-by: Bin Meng <bmeng.cn@gmail.com> Acked-by: Joe Hershberger <joe.hershberger@ni.com>
This commit is contained in:
parent
120b5ef287
commit
1d99534bef
@ -590,7 +590,7 @@ config HIGMACV300_ETH
|
|||||||
|
|
||||||
config FSL_ENETC
|
config FSL_ENETC
|
||||||
bool "NXP ENETC Ethernet controller"
|
bool "NXP ENETC Ethernet controller"
|
||||||
depends on DM_PCI && DM_ETH
|
depends on DM_PCI && DM_ETH && DM_MDIO
|
||||||
help
|
help
|
||||||
This driver supports the NXP ENETC Ethernet controller found on some
|
This driver supports the NXP ENETC Ethernet controller found on some
|
||||||
of the NXP SoCs.
|
of the NXP SoCs.
|
||||||
|
@ -79,4 +79,4 @@ obj-$(CONFIG_MEDIATEK_ETH) += mtk_eth.o
|
|||||||
obj-y += mscc_eswitch/
|
obj-y += mscc_eswitch/
|
||||||
obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
|
obj-$(CONFIG_HIGMACV300_ETH) += higmacv300.o
|
||||||
obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o
|
obj-$(CONFIG_MDIO_SANDBOX) += mdio_sandbox.o
|
||||||
obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o
|
obj-$(CONFIG_FSL_ENETC) += fsl_enetc.o fsl_enetc_mdio.o
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include <memalign.h>
|
#include <memalign.h>
|
||||||
#include <asm/io.h>
|
#include <asm/io.h>
|
||||||
#include <pci.h>
|
#include <pci.h>
|
||||||
|
#include <miiphy.h>
|
||||||
|
|
||||||
#include "fsl_enetc.h"
|
#include "fsl_enetc.h"
|
||||||
|
|
||||||
@ -38,6 +39,59 @@ static int enetc_bind(struct udevice *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Configure the actual/external ethernet PHY, if one is found */
|
||||||
|
static void enetc_start_phy(struct udevice *dev)
|
||||||
|
{
|
||||||
|
struct enetc_priv *priv = dev_get_priv(dev);
|
||||||
|
struct udevice *miidev;
|
||||||
|
struct phy_device *phy;
|
||||||
|
u32 phandle, phy_id;
|
||||||
|
ofnode phy_node;
|
||||||
|
int supported;
|
||||||
|
|
||||||
|
if (!ofnode_valid(dev->node)) {
|
||||||
|
enetc_dbg(dev, "no enetc ofnode found, skipping PHY set-up\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ofnode_read_u32(dev->node, "phy-handle", &phandle)) {
|
||||||
|
enetc_dbg(dev, "phy-handle not found, skipping PHY set-up\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
phy_node = ofnode_get_by_phandle(phandle);
|
||||||
|
if (!ofnode_valid(phy_node)) {
|
||||||
|
enetc_dbg(dev, "invalid phy node, skipping PHY set-up\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
enetc_dbg(dev, "phy node: %s\n", ofnode_get_name(phy_node));
|
||||||
|
|
||||||
|
if (ofnode_read_u32(phy_node, "reg", &phy_id)) {
|
||||||
|
enetc_dbg(dev,
|
||||||
|
"missing reg in PHY node, skipping PHY set-up\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uclass_get_device_by_ofnode(UCLASS_MDIO,
|
||||||
|
ofnode_get_parent(phy_node),
|
||||||
|
&miidev)) {
|
||||||
|
enetc_dbg(dev, "can't find MDIO bus for node %s\n",
|
||||||
|
ofnode_get_name(ofnode_get_parent(phy_node)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
phy = dm_mdio_phy_connect(miidev, phy_id, dev, priv->if_type);
|
||||||
|
if (!phy) {
|
||||||
|
enetc_dbg(dev, "dm_mdio_phy_connect returned null\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
supported = GENMASK(6, 0); /* speeds up to 1G & AN */
|
||||||
|
phy->advertising = phy->supported & supported;
|
||||||
|
phy_config(phy);
|
||||||
|
phy_startup(phy);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Probe ENETC driver:
|
* Probe ENETC driver:
|
||||||
* - initialize port and station interface BARs
|
* - initialize port and station interface BARs
|
||||||
@ -249,6 +303,9 @@ static int enetc_start(struct udevice *dev)
|
|||||||
enetc_setup_tx_bdr(dev);
|
enetc_setup_tx_bdr(dev);
|
||||||
enetc_setup_rx_bdr(dev);
|
enetc_setup_rx_bdr(dev);
|
||||||
|
|
||||||
|
priv->if_type = PHY_INTERFACE_MODE_NONE;
|
||||||
|
enetc_start_phy(dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
/* PCI function IDs */
|
/* PCI function IDs */
|
||||||
#define PCI_DEVICE_ID_ENETC_ETH 0xE100
|
#define PCI_DEVICE_ID_ENETC_ETH 0xE100
|
||||||
|
#define PCI_DEVICE_ID_ENETC_MDIO 0xEE01
|
||||||
|
|
||||||
/* ENETC Ethernet controller registers */
|
/* ENETC Ethernet controller registers */
|
||||||
/* Station interface register offsets */
|
/* Station interface register offsets */
|
||||||
@ -143,6 +144,8 @@ struct enetc_priv {
|
|||||||
/* Rx/Tx buffer descriptor rings info */
|
/* Rx/Tx buffer descriptor rings info */
|
||||||
struct bd_ring tx_bdr;
|
struct bd_ring tx_bdr;
|
||||||
struct bd_ring rx_bdr;
|
struct bd_ring rx_bdr;
|
||||||
|
|
||||||
|
int if_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* register accessors */
|
/* register accessors */
|
||||||
@ -165,4 +168,22 @@ struct enetc_priv {
|
|||||||
#define enetc_bdr_write(priv, t, n, off, val) \
|
#define enetc_bdr_write(priv, t, n, off, val) \
|
||||||
enetc_write(priv, ENETC_BDR(t, n, off), val)
|
enetc_write(priv, ENETC_BDR(t, n, off), val)
|
||||||
|
|
||||||
|
/* ENETC external MDIO registers */
|
||||||
|
#define ENETC_MDIO_BASE 0x1c00
|
||||||
|
#define ENETC_MDIO_CFG 0x00
|
||||||
|
#define ENETC_EMDIO_CFG_C22 0x00809508
|
||||||
|
#define ENETC_EMDIO_CFG_C45 0x00809548
|
||||||
|
#define ENETC_EMDIO_CFG_RD_ER BIT(1)
|
||||||
|
#define ENETC_EMDIO_CFG_BSY BIT(0)
|
||||||
|
#define ENETC_MDIO_CTL 0x04
|
||||||
|
#define ENETC_MDIO_CTL_READ BIT(15)
|
||||||
|
#define ENETC_MDIO_DATA 0x08
|
||||||
|
#define ENETC_MDIO_STAT 0x0c
|
||||||
|
|
||||||
|
#define ENETC_MDIO_READ_ERR 0xffff
|
||||||
|
|
||||||
|
struct enetc_mdio_priv {
|
||||||
|
void *regs_base;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* _ENETC_H */
|
#endif /* _ENETC_H */
|
||||||
|
149
drivers/net/fsl_enetc_mdio.c
Normal file
149
drivers/net/fsl_enetc_mdio.c
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
// 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)
|
||||||
|
{
|
||||||
|
while (enetc_read(priv, ENETC_MDIO_CFG) & ENETC_EMDIO_CFG_BSY)
|
||||||
|
cpu_relax();
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
static 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);
|
Loading…
Reference in New Issue
Block a user