Merge branch 'master' of git://git.denx.de/u-boot-net
This commit is contained in:
commit
62a09f45ab
@ -616,6 +616,7 @@ M: Joe Hershberger <joe.hershberger@ni.com>
|
||||
S: Maintained
|
||||
T: git https://gitlab.denx.de/u-boot/custodians/u-boot-net.git
|
||||
F: drivers/net/
|
||||
F: include/net.h
|
||||
F: net/
|
||||
|
||||
NIOS
|
||||
|
@ -125,6 +125,7 @@ config SANDBOX
|
||||
imply PCH
|
||||
imply PHYLIB
|
||||
imply DM_MDIO
|
||||
imply DM_MDIO_MUX
|
||||
|
||||
config SH
|
||||
bool "SuperH architecture"
|
||||
|
@ -824,7 +824,28 @@
|
||||
dma-names = "m2m", "tx0", "rx0";
|
||||
};
|
||||
|
||||
mdio-test {
|
||||
/*
|
||||
* keep mdio-mux ahead of mdio so that the mux is removed first at the
|
||||
* end of the test. If parent mdio is removed first, clean-up of the
|
||||
* mux will trigger a 2nd probe of parent-mdio, leaving parent-mdio
|
||||
* active at the end of the test. That it turn doesn't allow the mdio
|
||||
* class to be destroyed, triggering an error.
|
||||
*/
|
||||
mdio-mux-test {
|
||||
compatible = "sandbox,mdio-mux";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
mdio-parent-bus = <&mdio>;
|
||||
|
||||
mdio-ch-test@0 {
|
||||
reg = <0>;
|
||||
};
|
||||
mdio-ch-test@1 {
|
||||
reg = <1>;
|
||||
};
|
||||
};
|
||||
|
||||
mdio: mdio-test {
|
||||
compatible = "sandbox,mdio";
|
||||
};
|
||||
};
|
||||
|
@ -268,6 +268,11 @@ static int do_mdio(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bus) {
|
||||
puts("No MDIO bus found\n");
|
||||
return CMD_RET_FAILURE;
|
||||
}
|
||||
|
||||
if (op[0] == 'l') {
|
||||
mdio_list_devices();
|
||||
|
||||
|
138
doc/device-tree-bindings/net/mdio-mux.txt
Normal file
138
doc/device-tree-bindings/net/mdio-mux.txt
Normal file
@ -0,0 +1,138 @@
|
||||
The expected structure of an MDIO MUX device tree node is described here. This
|
||||
is heavily based on current Linux specification.
|
||||
One notable difference to Linux is that mdio-parent-bus is currently required
|
||||
by U-Boot, not optional as is in Linux. Current U-Boot MDIO MUX udevice class
|
||||
implementation does not have specific support for MDIOs with an integrated MUX,
|
||||
the property should be made optional if such support is added.
|
||||
|
||||
The MDIO buses downstream of the MUX should be described in the device tree as
|
||||
child nodes as indicated below.
|
||||
|
||||
Required properties:
|
||||
mdio-parent-bus = a phandle to the MDIO bus used to perform actual I/O. This is
|
||||
typically a real MDIO device, unless there are cascaded MUXes.
|
||||
#address-cells = <1>, each MDIO group is identified by one 32b value.
|
||||
#size-cells = <0>
|
||||
|
||||
Other properties:
|
||||
The properties described here are sufficient for MDIO MUX DM class code, but
|
||||
MUX drivers may define additional properties, either required or optional.
|
||||
|
||||
Required properties in child nodes:
|
||||
reg = value to be configured on the MUX to select the respective downstream
|
||||
MDIO.
|
||||
|
||||
Child nodes should normally contain PHY nodes, referenced by phandle from
|
||||
ethernet nodes of the eth interfaces using these PHYs.
|
||||
|
||||
Example structure, extracted from Linux bindings document:
|
||||
|
||||
/* The parent MDIO bus. */
|
||||
smi1: mdio@1180000001900 {
|
||||
compatible = "cavium,octeon-3860-mdio";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
reg = <0x11800 0x00001900 0x0 0x40>;
|
||||
};
|
||||
/*
|
||||
* An NXP sn74cbtlv3253 dual 1-of-4 switch controlled by a
|
||||
* pair of GPIO lines. Child busses 2 and 3 populated with 4
|
||||
* PHYs each.
|
||||
*/
|
||||
mdio-mux {
|
||||
compatible = "mdio-mux-gpio";
|
||||
gpios = <&gpio1 3 0>, <&gpio1 4 0>;
|
||||
mdio-parent-bus = <&smi1>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
mdio@2 {
|
||||
reg = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy11: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <10 8>; /* Pin 10, active low */
|
||||
};
|
||||
phy12: ethernet-phy@2 {
|
||||
reg = <2>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <10 8>; /* Pin 10, active low */
|
||||
};
|
||||
phy13: ethernet-phy@3 {
|
||||
reg = <3>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <10 8>; /* Pin 10, active low */
|
||||
};
|
||||
phy14: ethernet-phy@4 {
|
||||
reg = <4>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <10 8>; /* Pin 10, active low */
|
||||
};
|
||||
};
|
||||
mdio@3 {
|
||||
reg = <3>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
phy21: ethernet-phy@1 {
|
||||
reg = <1>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <12 8>; /* Pin 12, active low */
|
||||
};
|
||||
phy22: ethernet-phy@2 {
|
||||
reg = <2>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <12 8>; /* Pin 12, active low */
|
||||
};
|
||||
phy23: ethernet-phy@3 {
|
||||
reg = <3>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <12 8>; /* Pin 12, active low */
|
||||
};
|
||||
phy24: ethernet-phy@4 {
|
||||
reg = <4>;
|
||||
compatible = "marvell,88e1149r";
|
||||
marvell,reg-init = <3 0x10 0 0x5777>,
|
||||
<3 0x11 0 0x00aa>,
|
||||
<3 0x12 0 0x4105>,
|
||||
<3 0x13 0 0x0a60>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <12 8>; /* Pin 12, active low */
|
||||
};
|
||||
};
|
||||
};
|
@ -24,6 +24,18 @@ config DM_MDIO
|
||||
This is currently implemented in net/mdio-uclass.c
|
||||
Look in include/miiphy.h for details.
|
||||
|
||||
config DM_MDIO_MUX
|
||||
bool "Enable Driver Model for MDIO MUX devices"
|
||||
depends on DM_MDIO
|
||||
help
|
||||
Enable driver model for MDIO MUX devices
|
||||
|
||||
Adds UCLASS_MDIO_MUX DM class supporting MDIO MUXes. Useful for
|
||||
systems that support DM_MDIO and integrate one or multiple muxes on
|
||||
the MDIO bus.
|
||||
This is currently implemented in net/mdio-mux-uclass.c
|
||||
Look in include/miiphy.h for details.
|
||||
|
||||
config MDIO_SANDBOX
|
||||
depends on DM_MDIO && SANDBOX
|
||||
default y
|
||||
@ -34,6 +46,16 @@ config MDIO_SANDBOX
|
||||
|
||||
This driver is used in for testing in test/dm/mdio.c
|
||||
|
||||
config MDIO_MUX_SANDBOX
|
||||
depends on DM_MDIO_MUX && MDIO_SANDBOX
|
||||
default y
|
||||
bool "Sandbox: Mocked MDIO-MUX driver"
|
||||
help
|
||||
This driver implements dummy select/deselect ops mimicking a MUX on
|
||||
the MDIO bux. It uses mdio_sandbox driver as parent MDIO.
|
||||
|
||||
This driver is used for testing in test/dm/mdio.c
|
||||
|
||||
menuconfig NETDEVICES
|
||||
bool "Network device support"
|
||||
depends on NET
|
||||
|
@ -37,6 +37,7 @@ obj-$(CONFIG_LAN91C96) += lan91c96.o
|
||||
obj-$(CONFIG_LPC32XX_ETH) += lpc32xx_eth.o
|
||||
obj-$(CONFIG_MACB) += macb.o
|
||||
obj-$(CONFIG_MCFFEC) += mcffec.o mcfmii.o
|
||||
obj-$(CONFIG_MDIO_MUX_SANDBOX) += mdio_mux_sandbox.o
|
||||
obj-$(CONFIG_MPC8XX_FEC) += mpc8xx_fec.o
|
||||
obj-$(CONFIG_MT7628_ETH) += mt7628-eth.o
|
||||
obj-$(CONFIG_MVGBE) += mvgbe.o
|
||||
|
@ -677,10 +677,10 @@ int designware_eth_probe(struct udevice *dev)
|
||||
struct dw_eth_dev *priv = dev_get_priv(dev);
|
||||
u32 iobase = pdata->iobase;
|
||||
ulong ioaddr;
|
||||
int ret;
|
||||
int ret, err;
|
||||
struct reset_ctl_bulk reset_bulk;
|
||||
#ifdef CONFIG_CLK
|
||||
int i, err, clock_nb;
|
||||
int i, clock_nb;
|
||||
|
||||
priv->clock_count = 0;
|
||||
clock_nb = dev_count_phandle_with_args(dev, "clocks", "#clock-cells");
|
||||
@ -753,13 +753,23 @@ int designware_eth_probe(struct udevice *dev)
|
||||
priv->interface = pdata->phy_interface;
|
||||
priv->max_speed = pdata->max_speed;
|
||||
|
||||
dw_mdio_init(dev->name, dev);
|
||||
ret = dw_mdio_init(dev->name, dev);
|
||||
if (ret) {
|
||||
err = ret;
|
||||
goto mdio_err;
|
||||
}
|
||||
priv->bus = miiphy_get_dev_by_name(dev->name);
|
||||
|
||||
ret = dw_phy_init(priv, dev);
|
||||
debug("%s, ret=%d\n", __func__, ret);
|
||||
if (!ret)
|
||||
return 0;
|
||||
|
||||
return ret;
|
||||
/* continue here for cleanup if no PHY found */
|
||||
err = ret;
|
||||
mdio_unregister(priv->bus);
|
||||
mdio_free(priv->bus);
|
||||
mdio_err:
|
||||
|
||||
#ifdef CONFIG_CLK
|
||||
clk_err:
|
||||
@ -767,8 +777,8 @@ clk_err:
|
||||
if (ret)
|
||||
pr_err("failed to disable all clocks\n");
|
||||
|
||||
return err;
|
||||
#endif
|
||||
return err;
|
||||
}
|
||||
|
||||
static int designware_eth_remove(struct udevice *dev)
|
||||
|
97
drivers/net/mdio_mux_sandbox.c
Normal file
97
drivers/net/mdio_mux_sandbox.c
Normal file
@ -0,0 +1,97 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2019
|
||||
* Alex Marginean, NXP
|
||||
*/
|
||||
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <miiphy.h>
|
||||
|
||||
/* macros copied over from mdio_sandbox.c */
|
||||
#define SANDBOX_PHY_ADDR 5
|
||||
#define SANDBOX_PHY_REG_CNT 2
|
||||
|
||||
struct mdio_mux_sandbox_priv {
|
||||
int enabled;
|
||||
int sel;
|
||||
};
|
||||
|
||||
static int mdio_mux_sandbox_mark_selection(struct udevice *dev, int sel)
|
||||
{
|
||||
struct udevice *mdio;
|
||||
struct mdio_ops *ops;
|
||||
int err;
|
||||
|
||||
/*
|
||||
* find the sandbox parent mdio and write a register on the PHY there
|
||||
* so the mux test can verify selection.
|
||||
*/
|
||||
err = uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &mdio);
|
||||
if (err)
|
||||
return err;
|
||||
ops = mdio_get_ops(mdio);
|
||||
return ops->write(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
|
||||
SANDBOX_PHY_REG_CNT - 1, (u16)sel);
|
||||
}
|
||||
|
||||
static int mdio_mux_sandbox_select(struct udevice *dev, int cur, int sel)
|
||||
{
|
||||
struct mdio_mux_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (!priv->enabled)
|
||||
return -ENODEV;
|
||||
|
||||
if (cur != priv->sel)
|
||||
return -EINVAL;
|
||||
|
||||
priv->sel = sel;
|
||||
mdio_mux_sandbox_mark_selection(dev, priv->sel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mdio_mux_sandbox_deselect(struct udevice *dev, int sel)
|
||||
{
|
||||
struct mdio_mux_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
if (!priv->enabled)
|
||||
return -ENODEV;
|
||||
|
||||
if (sel != priv->sel)
|
||||
return -EINVAL;
|
||||
|
||||
priv->sel = -1;
|
||||
mdio_mux_sandbox_mark_selection(dev, priv->sel);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct mdio_mux_ops mdio_mux_sandbox_ops = {
|
||||
.select = mdio_mux_sandbox_select,
|
||||
.deselect = mdio_mux_sandbox_deselect,
|
||||
};
|
||||
|
||||
static int mdio_mux_sandbox_probe(struct udevice *dev)
|
||||
{
|
||||
struct mdio_mux_sandbox_priv *priv = dev_get_priv(dev);
|
||||
|
||||
priv->enabled = 1;
|
||||
priv->sel = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct udevice_id mdio_mux_sandbox_ids[] = {
|
||||
{ .compatible = "sandbox,mdio-mux" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mdio_mux_sandbox) = {
|
||||
.name = "mdio_mux_sandbox",
|
||||
.id = UCLASS_MDIO_MUX,
|
||||
.of_match = mdio_mux_sandbox_ids,
|
||||
.probe = mdio_mux_sandbox_probe,
|
||||
.ops = &mdio_mux_sandbox_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct mdio_mux_sandbox_priv),
|
||||
};
|
@ -9,11 +9,11 @@
|
||||
#include <miiphy.h>
|
||||
|
||||
#define SANDBOX_PHY_ADDR 5
|
||||
#define SANDBOX_PHY_REG 0
|
||||
#define SANDBOX_PHY_REG_CNT 2
|
||||
|
||||
struct mdio_sandbox_priv {
|
||||
int enabled;
|
||||
u16 reg;
|
||||
u16 reg[SANDBOX_PHY_REG_CNT];
|
||||
};
|
||||
|
||||
static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg)
|
||||
@ -27,10 +27,10 @@ static int mdio_sandbox_read(struct udevice *dev, int addr, int devad, int reg)
|
||||
return -ENODEV;
|
||||
if (devad != MDIO_DEVAD_NONE)
|
||||
return -ENODEV;
|
||||
if (reg != SANDBOX_PHY_REG)
|
||||
if (reg < 0 || reg > SANDBOX_PHY_REG_CNT)
|
||||
return -ENODEV;
|
||||
|
||||
return priv->reg;
|
||||
return priv->reg[reg];
|
||||
}
|
||||
|
||||
static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg,
|
||||
@ -45,10 +45,10 @@ static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg,
|
||||
return -ENODEV;
|
||||
if (devad != MDIO_DEVAD_NONE)
|
||||
return -ENODEV;
|
||||
if (reg != SANDBOX_PHY_REG)
|
||||
if (reg < 0 || reg > SANDBOX_PHY_REG_CNT)
|
||||
return -ENODEV;
|
||||
|
||||
priv->reg = val;
|
||||
priv->reg[reg] = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -56,8 +56,10 @@ static int mdio_sandbox_write(struct udevice *dev, int addr, int devad, int reg,
|
||||
static int mdio_sandbox_reset(struct udevice *dev)
|
||||
{
|
||||
struct mdio_sandbox_priv *priv = dev_get_priv(dev);
|
||||
int i;
|
||||
|
||||
priv->reg = 0;
|
||||
for (i = 0; i < SANDBOX_PHY_REG_CNT; i++)
|
||||
priv->reg[i] = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -461,6 +461,19 @@ struct phy_driver aqr107_driver = {
|
||||
.shutdown = &gen10g_shutdown,
|
||||
};
|
||||
|
||||
struct phy_driver aqr112_driver = {
|
||||
.name = "Aquantia AQR112",
|
||||
.uid = 0x3a1b660,
|
||||
.mask = 0xfffffff0,
|
||||
.features = PHY_10G_FEATURES,
|
||||
.mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS |
|
||||
MDIO_MMD_PHYXS | MDIO_MMD_AN |
|
||||
MDIO_MMD_VEND1),
|
||||
.config = &aquantia_config,
|
||||
.startup = &aquantia_startup,
|
||||
.shutdown = &gen10g_shutdown,
|
||||
};
|
||||
|
||||
struct phy_driver aqr405_driver = {
|
||||
.name = "Aquantia AQR405",
|
||||
.uid = 0x3a1b4b2,
|
||||
@ -474,6 +487,19 @@ struct phy_driver aqr405_driver = {
|
||||
.shutdown = &gen10g_shutdown,
|
||||
};
|
||||
|
||||
struct phy_driver aqr412_driver = {
|
||||
.name = "Aquantia AQR412",
|
||||
.uid = 0x3a1b710,
|
||||
.mask = 0xfffffff0,
|
||||
.features = PHY_10G_FEATURES,
|
||||
.mmds = (MDIO_MMD_PMAPMD | MDIO_MMD_PCS |
|
||||
MDIO_MMD_PHYXS | MDIO_MMD_AN |
|
||||
MDIO_MMD_VEND1),
|
||||
.config = &aquantia_config,
|
||||
.startup = &aquantia_startup,
|
||||
.shutdown = &gen10g_shutdown,
|
||||
};
|
||||
|
||||
int phy_aquantia_init(void)
|
||||
{
|
||||
phy_register(&aq1202_driver);
|
||||
@ -481,7 +507,9 @@ int phy_aquantia_init(void)
|
||||
phy_register(&aqr105_driver);
|
||||
phy_register(&aqr106_driver);
|
||||
phy_register(&aqr107_driver);
|
||||
phy_register(&aqr112_driver);
|
||||
phy_register(&aqr405_driver);
|
||||
phy_register(&aqr412_driver);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -727,12 +727,23 @@ static struct phy_device *create_phy_by_mask(struct mii_dev *bus,
|
||||
while (phy_mask) {
|
||||
int addr = ffs(phy_mask) - 1;
|
||||
int r = get_phy_id(bus, addr, devad, &phy_id);
|
||||
|
||||
/*
|
||||
* If the PHY ID is flat 0 we ignore it. There are C45 PHYs
|
||||
* that return all 0s for C22 reads (like Aquantia AQR112) and
|
||||
* there are C22 PHYs that return all 0s for C45 reads (like
|
||||
* Atheros AR8035).
|
||||
*/
|
||||
if (r == 0 && phy_id == 0)
|
||||
goto next;
|
||||
|
||||
/* If the PHY ID is mostly f's, we didn't find anything */
|
||||
if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff) {
|
||||
is_c45 = (devad == MDIO_DEVAD_NONE) ? false : true;
|
||||
return phy_device_create(bus, addr, phy_id, is_c45,
|
||||
interface);
|
||||
}
|
||||
next:
|
||||
phy_mask &= ~(1 << addr);
|
||||
}
|
||||
return NULL;
|
||||
|
@ -59,6 +59,7 @@ enum uclass_id {
|
||||
UCLASS_MAILBOX, /* Mailbox controller */
|
||||
UCLASS_MASS_STORAGE, /* Mass storage device */
|
||||
UCLASS_MDIO, /* MDIO bus */
|
||||
UCLASS_MDIO_MUX, /* MDIO MUX/switch */
|
||||
UCLASS_MISC, /* Miscellaneous device */
|
||||
UCLASS_MMC, /* SD / MMC card or chip */
|
||||
UCLASS_MOD_EXP, /* RSA Mod Exp device */
|
||||
|
@ -167,4 +167,24 @@ struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DM_MDIO_MUX
|
||||
|
||||
/* indicates none of the child buses is selected */
|
||||
#define MDIO_MUX_SELECT_NONE -1
|
||||
|
||||
/**
|
||||
* struct mdio_mux_ops - MDIO MUX operations
|
||||
*
|
||||
* @select: Selects a child bus
|
||||
* @deselect: Clean up selection. Optional, can be NULL
|
||||
*/
|
||||
struct mdio_mux_ops {
|
||||
int (*select)(struct udevice *mux, int cur, int sel);
|
||||
int (*deselect)(struct udevice *mux, int sel);
|
||||
};
|
||||
|
||||
#define mdio_mux_get_ops(dev) ((struct mdio_mux_ops *)(dev)->driver->ops)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -728,7 +728,7 @@ static inline struct in_addr net_read_ip(void *from)
|
||||
}
|
||||
|
||||
/* return ulong *in network byteorder* */
|
||||
static inline u32 net_read_u32(u32 *from)
|
||||
static inline u32 net_read_u32(void *from)
|
||||
{
|
||||
u32 l;
|
||||
|
||||
@ -749,7 +749,7 @@ static inline void net_copy_ip(void *to, void *from)
|
||||
}
|
||||
|
||||
/* copy ulong */
|
||||
static inline void net_copy_u32(u32 *to, u32 *from)
|
||||
static inline void net_copy_u32(void *to, void *from)
|
||||
{
|
||||
memcpy((void *)to, (void *)from, sizeof(u32));
|
||||
}
|
||||
|
@ -246,15 +246,71 @@ static inline int is_10g_interface(phy_interface_t interface)
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* phy_init() - Initializes the PHY drivers
|
||||
*
|
||||
* This function registers all available PHY drivers
|
||||
*
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int phy_init(void);
|
||||
|
||||
/**
|
||||
* phy_reset() - Resets the specified PHY
|
||||
*
|
||||
* Issues a reset of the PHY and waits for it to complete
|
||||
*
|
||||
* @phydev: PHY to reset
|
||||
* @return 0 if OK, -ve on error
|
||||
*/
|
||||
int phy_reset(struct phy_device *phydev);
|
||||
|
||||
/**
|
||||
* phy_find_by_mask() - Searches for a PHY on the specified MDIO bus
|
||||
*
|
||||
* The function checks the PHY addresses flagged in phy_mask and returns a
|
||||
* phy_device pointer if it detects a PHY.
|
||||
* This function should only be called if just one PHY is expected to be present
|
||||
* in the set of addresses flagged in phy_mask. If multiple PHYs are present,
|
||||
* it is undefined which of these PHYs is returned.
|
||||
*
|
||||
* @bus: MII/MDIO bus to scan
|
||||
* @phy_mask: bitmap of PYH addresses to scan
|
||||
* @interface: type of MAC-PHY interface
|
||||
* @return pointer to phy_device if a PHY is found, or NULL otherwise
|
||||
*/
|
||||
struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask,
|
||||
phy_interface_t interface);
|
||||
|
||||
#ifdef CONFIG_DM_ETH
|
||||
|
||||
/**
|
||||
* phy_connect_dev() - Associates the given pair of PHY and Ethernet devices
|
||||
* @phydev: PHY device
|
||||
* @dev: Ethernet device
|
||||
*/
|
||||
void phy_connect_dev(struct phy_device *phydev, struct udevice *dev);
|
||||
|
||||
/**
|
||||
* phy_connect() - Creates a PHY device for the Ethernet interface
|
||||
*
|
||||
* Creates a PHY device for the PHY at the given address, if one doesn't exist
|
||||
* already, and associates it with the Ethernet device.
|
||||
* The function may be called with addr <= 0, in this case addr value is ignored
|
||||
* and the bus is scanned to detect a PHY. Scanning should only be used if only
|
||||
* one PHY is expected to be present on the MDIO bus, otherwise it is undefined
|
||||
* which PHY is returned.
|
||||
*
|
||||
* @bus: MII/MDIO bus that hosts the PHY
|
||||
* @addr: PHY address on MDIO bus
|
||||
* @dev: Ethernet device to associate to the PHY
|
||||
* @interface: type of MAC-PHY interface
|
||||
* @return pointer to phy_device if a PHY is found, or NULL otherwise
|
||||
*/
|
||||
struct phy_device *phy_connect(struct mii_dev *bus, int addr,
|
||||
struct udevice *dev,
|
||||
phy_interface_t interface);
|
||||
|
||||
static inline ofnode phy_get_ofnode(struct phy_device *phydev)
|
||||
{
|
||||
if (ofnode_valid(phydev->node))
|
||||
@ -263,10 +319,34 @@ static inline ofnode phy_get_ofnode(struct phy_device *phydev)
|
||||
return dev_ofnode(phydev->dev);
|
||||
}
|
||||
#else
|
||||
|
||||
/**
|
||||
* phy_connect_dev() - Associates the given pair of PHY and Ethernet devices
|
||||
* @phydev: PHY device
|
||||
* @dev: Ethernet device
|
||||
*/
|
||||
void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev);
|
||||
|
||||
/**
|
||||
* phy_connect() - Creates a PHY device for the Ethernet interface
|
||||
*
|
||||
* Creates a PHY device for the PHY at the given address, if one doesn't exist
|
||||
* already, and associates it with the Ethernet device.
|
||||
* The function may be called with addr <= 0, in this case addr value is ignored
|
||||
* and the bus is scanned to detect a PHY. Scanning should only be used if only
|
||||
* one PHY is expected to be present on the MDIO bus, otherwise it is undefined
|
||||
* which PHY is returned.
|
||||
*
|
||||
* @bus: MII/MDIO bus that hosts the PHY
|
||||
* @addr: PHY address on MDIO bus
|
||||
* @dev: Ethernet device to associate to the PHY
|
||||
* @interface: type of MAC-PHY interface
|
||||
* @return pointer to phy_device if a PHY is found, or NULL otherwise
|
||||
*/
|
||||
struct phy_device *phy_connect(struct mii_dev *bus, int addr,
|
||||
struct eth_device *dev,
|
||||
phy_interface_t interface);
|
||||
|
||||
static inline ofnode phy_get_ofnode(struct phy_device *phydev)
|
||||
{
|
||||
return ofnode_null();
|
||||
|
@ -16,6 +16,7 @@ else
|
||||
obj-$(CONFIG_NET) += eth_legacy.o
|
||||
endif
|
||||
obj-$(CONFIG_DM_MDIO) += mdio-uclass.o
|
||||
obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o
|
||||
obj-$(CONFIG_NET) += eth_common.o
|
||||
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
|
||||
obj-$(CONFIG_NET) += net.o
|
||||
|
232
net/mdio-mux-uclass.c
Normal file
232
net/mdio-mux-uclass.c
Normal file
@ -0,0 +1,232 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2019
|
||||
* Alex Marginean, NXP
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <miiphy.h>
|
||||
#include <dm/device-internal.h>
|
||||
#include <dm/uclass-internal.h>
|
||||
#include <dm/lists.h>
|
||||
|
||||
#define MDIO_MUX_CHILD_DRV_NAME "mdio-mux-bus-drv"
|
||||
|
||||
/**
|
||||
* struct mdio_mux_perdev_priv - Per-device class data for MDIO MUX DM
|
||||
*
|
||||
* @parent_mdio: Parent DM MDIO device, this is called for actual MDIO I/O after
|
||||
* setting up the mux. Typically this is a real MDIO device,
|
||||
* unless there are cascaded muxes.
|
||||
* @selected: Current child bus selection. Defaults to -1
|
||||
*/
|
||||
struct mdio_mux_perdev_priv {
|
||||
struct udevice *mdio_parent;
|
||||
int selected;
|
||||
};
|
||||
|
||||
/*
|
||||
* This source file uses three types of devices, as follows:
|
||||
* - mux is the hardware MDIO MUX which selects between the existing child MDIO
|
||||
* buses, this is the device relevant for MDIO MUX class of drivers.
|
||||
* - ch is a child MDIO bus, this is just a representation of a mux selection,
|
||||
* not a real piece of hardware.
|
||||
* - mdio_parent is the actual MDIO bus called to perform reads/writes after
|
||||
* the MUX is configured. Typically this is a real MDIO device, unless there
|
||||
* are cascaded muxes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* struct mdio_mux_ch_data - Per-device data for child MDIOs
|
||||
*
|
||||
* @sel: Selection value used by the MDIO MUX to access this child MDIO bus
|
||||
*/
|
||||
struct mdio_mux_ch_data {
|
||||
int sel;
|
||||
};
|
||||
|
||||
static struct udevice *mmux_get_parent_mdio(struct udevice *mux)
|
||||
{
|
||||
struct mdio_mux_perdev_priv *pdata = dev_get_uclass_priv(mux);
|
||||
|
||||
return pdata->mdio_parent;
|
||||
}
|
||||
|
||||
static struct mdio_ops *mmux_get_mdio_parent_ops(struct udevice *mux)
|
||||
{
|
||||
return mdio_get_ops(mmux_get_parent_mdio(mux));
|
||||
}
|
||||
|
||||
/* call driver select function before performing MDIO r/w */
|
||||
static int mmux_change_sel(struct udevice *ch, bool sel)
|
||||
{
|
||||
struct udevice *mux = ch->parent;
|
||||
struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
|
||||
struct mdio_mux_ops *ops = mdio_mux_get_ops(mux);
|
||||
struct mdio_mux_ch_data *ch_data = dev_get_parent_platdata(ch);
|
||||
int err = 0;
|
||||
|
||||
if (sel) {
|
||||
err = ops->select(mux, priv->selected, ch_data->sel);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
priv->selected = ch_data->sel;
|
||||
} else {
|
||||
if (ops->deselect) {
|
||||
ops->deselect(mux, ch_data->sel);
|
||||
priv->selected = MDIO_MUX_SELECT_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read wrapper, sets up the mux before issuing a read on parent MDIO bus */
|
||||
static int mmux_read(struct udevice *ch, int addr, int devad,
|
||||
int reg)
|
||||
{
|
||||
struct udevice *mux = ch->parent;
|
||||
struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
|
||||
struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
|
||||
int err;
|
||||
|
||||
err = mmux_change_sel(ch, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = parent_ops->read(parent_mdio, addr, devad, reg);
|
||||
mmux_change_sel(ch, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Write wrapper, sets up the mux before issuing a write on parent MDIO bus */
|
||||
static int mmux_write(struct udevice *ch, int addr, int devad,
|
||||
int reg, u16 val)
|
||||
{
|
||||
struct udevice *mux = ch->parent;
|
||||
struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
|
||||
struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
|
||||
int err;
|
||||
|
||||
err = mmux_change_sel(ch, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = parent_ops->write(parent_mdio, addr, devad, reg, val);
|
||||
mmux_change_sel(ch, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Reset wrapper, sets up the mux before issuing a reset on parent MDIO bus */
|
||||
static int mmux_reset(struct udevice *ch)
|
||||
{
|
||||
struct udevice *mux = ch->parent;
|
||||
struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
|
||||
struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
|
||||
int err;
|
||||
|
||||
/* reset is optional, if it's not implemented just exit */
|
||||
if (!parent_ops->reset)
|
||||
return 0;
|
||||
|
||||
err = mmux_change_sel(ch, true);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = parent_ops->reset(parent_mdio);
|
||||
mmux_change_sel(ch, false);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
/* Picks up the mux selection value for each child */
|
||||
static int dm_mdio_mux_child_post_bind(struct udevice *ch)
|
||||
{
|
||||
struct mdio_mux_ch_data *ch_data = dev_get_parent_platdata(ch);
|
||||
|
||||
ch_data->sel = dev_read_u32_default(ch, "reg", MDIO_MUX_SELECT_NONE);
|
||||
|
||||
if (ch_data->sel == MDIO_MUX_SELECT_NONE)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Explicitly bind child MDIOs after binding the mux */
|
||||
static int dm_mdio_mux_post_bind(struct udevice *mux)
|
||||
{
|
||||
ofnode ch_node;
|
||||
int err, first_err = 0;
|
||||
|
||||
if (!ofnode_valid(mux->node)) {
|
||||
debug("%s: no mux node found, no child MDIO busses set up\n",
|
||||
__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* we're going by Linux bindings so the child nodes do not have
|
||||
* compatible strings. We're going through them here and binding to
|
||||
* them.
|
||||
*/
|
||||
dev_for_each_subnode(ch_node, mux) {
|
||||
struct udevice *ch_dev;
|
||||
const char *ch_name;
|
||||
|
||||
ch_name = ofnode_get_name(ch_node);
|
||||
|
||||
err = device_bind_driver_to_node(mux, MDIO_MUX_CHILD_DRV_NAME,
|
||||
ch_name, ch_node, &ch_dev);
|
||||
/* try to bind all, but keep 1st error */
|
||||
if (err && !first_err)
|
||||
first_err = err;
|
||||
}
|
||||
|
||||
return first_err;
|
||||
}
|
||||
|
||||
/* Get a reference to the parent MDIO bus, it should be bound by now */
|
||||
static int dm_mdio_mux_post_probe(struct udevice *mux)
|
||||
{
|
||||
struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
|
||||
int err;
|
||||
|
||||
priv->selected = MDIO_MUX_SELECT_NONE;
|
||||
|
||||
/* pick up mdio parent from device tree */
|
||||
err = uclass_get_device_by_phandle(UCLASS_MDIO, mux, "mdio-parent-bus",
|
||||
&priv->mdio_parent);
|
||||
if (err) {
|
||||
debug("%s: didn't find mdio-parent-bus\n", __func__);
|
||||
return err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct mdio_ops mmux_child_mdio_ops = {
|
||||
.read = mmux_read,
|
||||
.write = mmux_write,
|
||||
.reset = mmux_reset,
|
||||
};
|
||||
|
||||
/* MDIO class driver used for MUX child MDIO buses */
|
||||
U_BOOT_DRIVER(mdio_mux_child) = {
|
||||
.name = MDIO_MUX_CHILD_DRV_NAME,
|
||||
.id = UCLASS_MDIO,
|
||||
.ops = &mmux_child_mdio_ops,
|
||||
};
|
||||
|
||||
UCLASS_DRIVER(mdio_mux) = {
|
||||
.id = UCLASS_MDIO_MUX,
|
||||
.name = "mdio-mux",
|
||||
.child_post_bind = dm_mdio_mux_child_post_bind,
|
||||
.post_bind = dm_mdio_mux_post_bind,
|
||||
.post_probe = dm_mdio_mux_post_probe,
|
||||
.per_device_auto_alloc_size = sizeof(struct mdio_mux_perdev_priv),
|
||||
.per_child_platdata_auto_alloc_size = sizeof(struct mdio_mux_ch_data),
|
||||
};
|
@ -63,4 +63,5 @@ obj-$(CONFIG_TEE) += tee.o
|
||||
obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o
|
||||
obj-$(CONFIG_DMA) += dma.o
|
||||
obj-$(CONFIG_DM_MDIO) += mdio.o
|
||||
obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o
|
||||
endif
|
||||
|
@ -13,6 +13,9 @@
|
||||
|
||||
/* macros copied over from mdio_sandbox.c */
|
||||
#define SANDBOX_PHY_ADDR 5
|
||||
#define SANDBOX_PHY_REG_CNT 2
|
||||
|
||||
/* test using 1st register, 0 */
|
||||
#define SANDBOX_PHY_REG 0
|
||||
|
||||
#define TEST_REG_VALUE 0xabcd
|
||||
|
80
test/dm/mdio_mux.c
Normal file
80
test/dm/mdio_mux.c
Normal file
@ -0,0 +1,80 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* (C) Copyright 2019
|
||||
* Alex Marginean, NXP
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <dm/test.h>
|
||||
#include <misc.h>
|
||||
#include <test/ut.h>
|
||||
#include <miiphy.h>
|
||||
|
||||
/* macros copied over from mdio_sandbox.c */
|
||||
#define SANDBOX_PHY_ADDR 5
|
||||
#define SANDBOX_PHY_REG_CNT 2
|
||||
|
||||
#define TEST_REG_VALUE 0xabcd
|
||||
|
||||
static int dm_test_mdio_mux(struct unit_test_state *uts)
|
||||
{
|
||||
struct uclass *uc;
|
||||
struct udevice *mux;
|
||||
struct udevice *mdio_ch0, *mdio_ch1, *mdio;
|
||||
struct mdio_ops *ops, *ops_parent;
|
||||
struct mdio_mux_ops *mmops;
|
||||
u16 reg;
|
||||
|
||||
ut_assertok(uclass_get(UCLASS_MDIO_MUX, &uc));
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_MDIO_MUX, "mdio-mux-test",
|
||||
&mux));
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@0",
|
||||
&mdio_ch0));
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@1",
|
||||
&mdio_ch1));
|
||||
|
||||
ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &mdio));
|
||||
|
||||
ops = mdio_get_ops(mdio_ch0);
|
||||
ut_assertnonnull(ops);
|
||||
ut_assertnonnull(ops->read);
|
||||
ut_assertnonnull(ops->write);
|
||||
|
||||
mmops = mdio_mux_get_ops(mux);
|
||||
ut_assertnonnull(mmops);
|
||||
ut_assertnonnull(mmops->select);
|
||||
|
||||
ops_parent = mdio_get_ops(mdio);
|
||||
ut_assertnonnull(ops);
|
||||
ut_assertnonnull(ops->read);
|
||||
|
||||
/*
|
||||
* mux driver sets last register on the emulated PHY whenever a group
|
||||
* is selected to the selection #. Just reading that register from
|
||||
* either of the child buses should return the id of the child bus
|
||||
*/
|
||||
reg = ops->read(mdio_ch0, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
|
||||
SANDBOX_PHY_REG_CNT - 1);
|
||||
ut_asserteq(reg, 0);
|
||||
|
||||
reg = ops->read(mdio_ch1, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
|
||||
SANDBOX_PHY_REG_CNT - 1);
|
||||
ut_asserteq(reg, 1);
|
||||
|
||||
mmops->select(mux, MDIO_MUX_SELECT_NONE, 5);
|
||||
reg = ops_parent->read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
|
||||
SANDBOX_PHY_REG_CNT - 1);
|
||||
ut_asserteq(reg, 5);
|
||||
|
||||
mmops->deselect(mux, 5);
|
||||
reg = ops_parent->read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
|
||||
SANDBOX_PHY_REG_CNT - 1);
|
||||
ut_asserteq(reg, (u16)MDIO_MUX_SELECT_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
DM_TEST(dm_test_mdio_mux, DM_TESTF_SCAN_FDT);
|
Loading…
Reference in New Issue
Block a user