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