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 57/* call driver select function before performing MDIO r/w */ 58static int mmux_change_sel(struct udevice *ch, bool sel) 59{ 60 struct udevice *mux = ch->parent; 61 struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux); 62 struct mdio_mux_ops *ops = mdio_mux_get_ops(mux); 63 struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch); 64 int err = 0; 65 66 if (sel) { 67 err = ops->select(mux, priv->selected, ch_data->sel); 68 if (err) 69 return err; 70 71 priv->selected = ch_data->sel; 72 } else { 73 if (ops->deselect) { 74 ops->deselect(mux, ch_data->sel); 75 priv->selected = MDIO_MUX_SELECT_NONE; 76 } 77 } 78 79 return 0; 80} 81 82/* Read wrapper, sets up the mux before issuing a read on parent MDIO bus */ 83static int mmux_read(struct udevice *ch, int addr, int devad, 84 int reg) 85{ 86 struct udevice *mux = ch->parent; 87 struct udevice *parent_mdio = mmux_get_parent_mdio(mux); 88 int err; 89 90 err = mmux_change_sel(ch, true); 91 if (err) 92 return err; 93 94 err = dm_mdio_read(parent_mdio, addr, devad, reg); 95 mmux_change_sel(ch, false); 96 97 return err; 98} 99 100/* Write wrapper, sets up the mux before issuing a write on parent MDIO bus */ 101static int mmux_write(struct udevice *ch, int addr, int devad, 102 int reg, u16 val) 103{ 104 struct udevice *mux = ch->parent; 105 struct udevice *parent_mdio = mmux_get_parent_mdio(mux); 106 int err; 107 108 err = mmux_change_sel(ch, true); 109 if (err) 110 return err; 111 112 err = dm_mdio_write(parent_mdio, addr, devad, reg, val); 113 mmux_change_sel(ch, false); 114 115 return err; 116} 117 118/* Reset wrapper, sets up the mux before issuing a reset on parent MDIO bus */ 119static int mmux_reset(struct udevice *ch) 120{ 121 struct udevice *mux = ch->parent; 122 struct udevice *parent_mdio = mmux_get_parent_mdio(mux); 123 int err; 124 125 /* reset is optional, if it's not implemented just exit */ 126 if (!mdio_get_ops(parent_mdio)->reset) 127 return 0; 128 129 err = mmux_change_sel(ch, true); 130 if (err) 131 return err; 132 133 err = dm_mdio_reset(parent_mdio); 134 mmux_change_sel(ch, false); 135 136 return err; 137} 138 139/* Picks up the mux selection value for each child */ 140static int dm_mdio_mux_child_post_bind(struct udevice *ch) 141{ 142 struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch); 143 144 ch_data->sel = dev_read_u32_default(ch, "reg", MDIO_MUX_SELECT_NONE); 145 146 if (ch_data->sel == MDIO_MUX_SELECT_NONE) 147 return -EINVAL; 148 149 return 0; 150} 151 152/* Explicitly bind child MDIOs after binding the mux */ 153static int dm_mdio_mux_post_bind(struct udevice *mux) 154{ 155 ofnode ch_node; 156 int err, first_err = 0; 157 158 if (!dev_has_ofnode(mux)) { 159 debug("%s: no mux node found, no child MDIO busses set up\n", 160 __func__); 161 return 0; 162 } 163 164 /* 165 * we're going by Linux bindings so the child nodes do not have 166 * compatible strings. We're going through them here and binding to 167 * them. 168 */ 169 dev_for_each_subnode(ch_node, mux) { 170 struct udevice *ch_dev; 171 const char *ch_name; 172 173 ch_name = ofnode_get_name(ch_node); 174 175 err = device_bind_driver_to_node(mux, MDIO_MUX_CHILD_DRV_NAME, 176 ch_name, ch_node, &ch_dev); 177 /* try to bind all, but keep 1st error */ 178 if (err && !first_err) 179 first_err = err; 180 } 181 182 return first_err; 183} 184 185/* Get a reference to the parent MDIO bus, it should be bound by now */ 186static int dm_mdio_mux_post_probe(struct udevice *mux) 187{ 188 struct mdio_mux_perdev_priv *priv = dev_get_uclass_priv(mux); 189 int err; 190 191 priv->selected = MDIO_MUX_SELECT_NONE; 192 193 /* pick up mdio parent from device tree */ 194 err = uclass_get_device_by_phandle(UCLASS_MDIO, mux, "mdio-parent-bus", 195 &priv->mdio_parent); 196 if (err) { 197 debug("%s: didn't find mdio-parent-bus\n", __func__); 198 return err; 199 } 200 201 return 0; 202} 203 204const struct mdio_ops mmux_child_mdio_ops = { 205 .read = mmux_read, 206 .write = mmux_write, 207 .reset = mmux_reset, 208}; 209 210/* MDIO class driver used for MUX child MDIO buses */ 211U_BOOT_DRIVER(mdio_mux_child) = { 212 .name = MDIO_MUX_CHILD_DRV_NAME, 213 .id = UCLASS_MDIO, 214 .ops = &mmux_child_mdio_ops, 215}; 216 217UCLASS_DRIVER(mdio_mux) = { 218 .id = UCLASS_MDIO_MUX, 219 .name = "mdio-mux", 220 .child_post_bind = dm_mdio_mux_child_post_bind, 221 .post_bind = dm_mdio_mux_post_bind, 222 .post_probe = dm_mdio_mux_post_probe, 223 .per_device_auto = sizeof(struct mdio_mux_perdev_priv), 224 .per_child_plat_auto = sizeof(struct mdio_mux_ch_data), 225}; 226