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