uboot/net/mdio-mux-uclass.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2019
   4 * Alex Marginean, NXP
   5 */
   6
   7#include <common.h>
   8#include <dm.h>
   9#include <log.h>
  10#include <miiphy.h>
  11#include <dm/device-internal.h>
  12#include <dm/uclass-internal.h>
  13#include <dm/lists.h>
  14
  15#define MDIO_MUX_CHILD_DRV_NAME "mdio-mux-bus-drv"
  16
  17/**
  18 * struct mdio_mux_perdev_priv - Per-device class data for MDIO MUX DM
  19 *
  20 * @parent_mdio: Parent DM MDIO device, this is called for actual MDIO I/O after
  21 *               setting up the mux.  Typically this is a real MDIO device,
  22 *               unless there are cascaded muxes.
  23 * @selected:    Current child bus selection.  Defaults to -1
  24 */
  25struct mdio_mux_perdev_priv {
  26        struct udevice *mdio_parent;
  27        int selected;
  28};
  29
  30/*
  31 * This source file uses three types of devices, as follows:
  32 * - mux is the hardware MDIO MUX which selects between the existing child MDIO
  33 * buses, this is the device relevant for MDIO MUX class of drivers.
  34 * - ch is a child MDIO bus, this is just a representation of a mux selection,
  35 * not a real piece of hardware.
  36 * - mdio_parent is the actual MDIO bus called to perform reads/writes after
  37 * the MUX is configured.  Typically this is a real MDIO device, unless there
  38 * are cascaded muxes.
  39 */
  40
  41/**
  42 * struct mdio_mux_ch_data - Per-device data for child MDIOs
  43 *
  44 * @sel: Selection value used by the MDIO MUX to access this child MDIO bus
  45 */
  46struct mdio_mux_ch_data {
  47        int sel;
  48};
  49
  50static struct udevice *mmux_get_parent_mdio(struct udevice *mux)
  51{
  52        struct mdio_mux_perdev_priv *pdata = dev_get_uclass_priv(mux);
  53
  54        return pdata->mdio_parent;
  55}
  56
  57static struct mdio_ops *mmux_get_mdio_parent_ops(struct udevice *mux)
  58{
  59        return mdio_get_ops(mmux_get_parent_mdio(mux));
  60}
  61
  62/* call driver select function before performing MDIO r/w */
  63static int mmux_change_sel(struct udevice *ch, bool sel)
  64{
  65        struct udevice *mux = ch->parent;
  66        struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
  67        struct mdio_mux_ops *ops = mdio_mux_get_ops(mux);
  68        struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
  69        int err = 0;
  70
  71        if (sel) {
  72                err = ops->select(mux, priv->selected, ch_data->sel);
  73                if (err)
  74                        return err;
  75
  76                priv->selected = ch_data->sel;
  77        } else {
  78                if (ops->deselect) {
  79                        ops->deselect(mux, ch_data->sel);
  80                        priv->selected = MDIO_MUX_SELECT_NONE;
  81                }
  82        }
  83
  84        return 0;
  85}
  86
  87/* Read wrapper, sets up the mux before issuing a read on parent MDIO bus */
  88static int mmux_read(struct udevice *ch, int addr, int devad,
  89                     int reg)
  90{
  91        struct udevice *mux = ch->parent;
  92        struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
  93        struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
  94        int err;
  95
  96        err = mmux_change_sel(ch, true);
  97        if (err)
  98                return err;
  99
 100        err = parent_ops->read(parent_mdio, addr, devad, reg);
 101        mmux_change_sel(ch, false);
 102
 103        return err;
 104}
 105
 106/* Write wrapper, sets up the mux before issuing a write on parent MDIO bus */
 107static int mmux_write(struct udevice *ch, int addr, int devad,
 108                      int reg, u16 val)
 109{
 110        struct udevice *mux = ch->parent;
 111        struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
 112        struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
 113        int err;
 114
 115        err = mmux_change_sel(ch, true);
 116        if (err)
 117                return err;
 118
 119        err = parent_ops->write(parent_mdio, addr, devad, reg, val);
 120        mmux_change_sel(ch, false);
 121
 122        return err;
 123}
 124
 125/* Reset wrapper, sets up the mux before issuing a reset on parent MDIO bus */
 126static int mmux_reset(struct udevice *ch)
 127{
 128        struct udevice *mux = ch->parent;
 129        struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
 130        struct mdio_ops *parent_ops = mmux_get_mdio_parent_ops(mux);
 131        int err;
 132
 133        /* reset is optional, if it's not implemented just exit */
 134        if (!parent_ops->reset)
 135                return 0;
 136
 137        err = mmux_change_sel(ch, true);
 138        if (err)
 139                return err;
 140
 141        err = parent_ops->reset(parent_mdio);
 142        mmux_change_sel(ch, false);
 143
 144        return err;
 145}
 146
 147/* Picks up the mux selection value for each child */
 148static int dm_mdio_mux_child_post_bind(struct udevice *ch)
 149{
 150        struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
 151
 152        ch_data->sel = dev_read_u32_default(ch, "reg", MDIO_MUX_SELECT_NONE);
 153
 154        if (ch_data->sel == MDIO_MUX_SELECT_NONE)
 155                return -EINVAL;
 156
 157        return 0;
 158}
 159
 160/* Explicitly bind child MDIOs after binding the mux */
 161static int dm_mdio_mux_post_bind(struct udevice *mux)
 162{
 163        ofnode ch_node;
 164        int err, first_err = 0;
 165
 166        if (!dev_has_ofnode(mux)) {
 167                debug("%s: no mux node found, no child MDIO busses set up\n",
 168                      __func__);
 169                return 0;
 170        }
 171
 172        /*
 173         * we're going by Linux bindings so the child nodes do not have
 174         * compatible strings.  We're going through them here and binding to
 175         * them.
 176         */
 177        dev_for_each_subnode(ch_node, mux) {
 178                struct udevice *ch_dev;
 179                const char *ch_name;
 180
 181                ch_name = ofnode_get_name(ch_node);
 182
 183                err = device_bind_driver_to_node(mux, MDIO_MUX_CHILD_DRV_NAME,
 184                                                 ch_name, ch_node, &ch_dev);
 185                /* try to bind all, but keep 1st error */
 186                if (err && !first_err)
 187                        first_err = err;
 188        }
 189
 190        return first_err;
 191}
 192
 193/* Get a reference to the parent MDIO bus, it should be bound by now */
 194static int dm_mdio_mux_post_probe(struct udevice *mux)
 195{
 196        struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux);
 197        int err;
 198
 199        priv->selected = MDIO_MUX_SELECT_NONE;
 200
 201        /* pick up mdio parent from device tree */
 202        err = uclass_get_device_by_phandle(UCLASS_MDIO, mux, "mdio-parent-bus",
 203                                           &priv->mdio_parent);
 204        if (err) {
 205                debug("%s: didn't find mdio-parent-bus\n", __func__);
 206                return err;
 207        }
 208
 209        return 0;
 210}
 211
 212const struct mdio_ops mmux_child_mdio_ops = {
 213        .read = mmux_read,
 214        .write = mmux_write,
 215        .reset = mmux_reset,
 216};
 217
 218/* MDIO class driver used for MUX child MDIO buses */
 219U_BOOT_DRIVER(mdio_mux_child) = {
 220        .name           = MDIO_MUX_CHILD_DRV_NAME,
 221        .id             = UCLASS_MDIO,
 222        .ops            = &mmux_child_mdio_ops,
 223};
 224
 225UCLASS_DRIVER(mdio_mux) = {
 226        .id = UCLASS_MDIO_MUX,
 227        .name = "mdio-mux",
 228        .child_post_bind = dm_mdio_mux_child_post_bind,
 229        .post_bind  = dm_mdio_mux_post_bind,
 230        .post_probe = dm_mdio_mux_post_probe,
 231        .per_device_auto        = sizeof(struct mdio_mux_perdev_priv),
 232        .per_child_plat_auto    = sizeof(struct mdio_mux_ch_data),
 233};
 234