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