mirror of
https://xff.cz/git/u-boot/
synced 2025-09-03 17:52:07 +02:00
net: add MDIO_MUX DM class
Adds a class for MDIO MUXes, which control access to a series of downstream child MDIOs. MDIO MUX drivers are required to implement a select function used to switch between child buses. MUX children are registered as MDIO buses and they can be used just like regular MDIOs. 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:
committed by
Joe Hershberger
parent
c38ac2893c
commit
8880edba06
@@ -24,6 +24,18 @@ config DM_MDIO
|
|||||||
This is currently implemented in net/mdio-uclass.c
|
This is currently implemented in net/mdio-uclass.c
|
||||||
Look in include/miiphy.h for details.
|
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
|
config MDIO_SANDBOX
|
||||||
depends on DM_MDIO && SANDBOX
|
depends on DM_MDIO && SANDBOX
|
||||||
default y
|
default y
|
||||||
|
@@ -59,6 +59,7 @@ enum uclass_id {
|
|||||||
UCLASS_MAILBOX, /* Mailbox controller */
|
UCLASS_MAILBOX, /* Mailbox controller */
|
||||||
UCLASS_MASS_STORAGE, /* Mass storage device */
|
UCLASS_MASS_STORAGE, /* Mass storage device */
|
||||||
UCLASS_MDIO, /* MDIO bus */
|
UCLASS_MDIO, /* MDIO bus */
|
||||||
|
UCLASS_MDIO_MUX, /* MDIO MUX/switch */
|
||||||
UCLASS_MISC, /* Miscellaneous device */
|
UCLASS_MISC, /* Miscellaneous device */
|
||||||
UCLASS_MMC, /* SD / MMC card or chip */
|
UCLASS_MMC, /* SD / MMC card or chip */
|
||||||
UCLASS_MOD_EXP, /* RSA Mod Exp device */
|
UCLASS_MOD_EXP, /* RSA Mod Exp device */
|
||||||
|
@@ -167,4 +167,24 @@ struct phy_device *dm_mdio_phy_connect(struct udevice *dev, int addr,
|
|||||||
|
|
||||||
#endif
|
#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
|
#endif
|
||||||
|
@@ -16,6 +16,7 @@ else
|
|||||||
obj-$(CONFIG_NET) += eth_legacy.o
|
obj-$(CONFIG_NET) += eth_legacy.o
|
||||||
endif
|
endif
|
||||||
obj-$(CONFIG_DM_MDIO) += mdio-uclass.o
|
obj-$(CONFIG_DM_MDIO) += mdio-uclass.o
|
||||||
|
obj-$(CONFIG_DM_MDIO_MUX) += mdio-mux-uclass.o
|
||||||
obj-$(CONFIG_NET) += eth_common.o
|
obj-$(CONFIG_NET) += eth_common.o
|
||||||
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
|
obj-$(CONFIG_CMD_LINK_LOCAL) += link_local.o
|
||||||
obj-$(CONFIG_NET) += net.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),
|
||||||
|
};
|
Reference in New Issue
Block a user