Merge branch 'master' of git://git.denx.de/u-boot-net

This commit is contained in:
Tom Rini 2019-07-18 20:32:07 -04:00
commit 62a09f45ab
21 changed files with 770 additions and 15 deletions

View file

@ -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

View file

@ -125,6 +125,7 @@ config SANDBOX
imply PCH
imply PHYLIB
imply DM_MDIO
imply DM_MDIO_MUX
config SH
bool "SuperH architecture"

View file

@ -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";
};
};

View file

@ -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();

View 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 */
};
};
};

View file

@ -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

View file

@ -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

View file

@ -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)

View 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),
};

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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 */

View file

@ -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

View file

@ -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));
}

View file

@ -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();

View file

@ -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
View 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),
};

View file

@ -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

View file

@ -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
View 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);