blob: 94b90e0657626b2cdbe342ac9d94f2893acb48a6 [file] [log] [blame]
Alex Marginean8880edb2019-07-12 10:13:50 +03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * (C) Copyright 2019
4 * Alex Marginean, NXP
5 */
6
Tom Rinid678a592024-05-18 20:20:43 -06007#include <common.h>
Alex Marginean8880edb2019-07-12 10:13:50 +03008#include <dm.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -06009#include <log.h>
Alex Marginean8880edb2019-07-12 10:13:50 +030010#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
Alex Marginean8880edb2019-07-12 10:13:50 +030057/* 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);
Simon Glasscaa4daa2020-12-03 16:55:18 -070063 struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
Alex Marginean8880edb2019-07-12 10:13:50 +030064 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);
Alex Marginean8880edb2019-07-12 10:13:50 +030088 int err;
89
90 err = mmux_change_sel(ch, true);
91 if (err)
92 return err;
93
Marek Behún1776a242022-04-07 00:32:59 +020094 err = dm_mdio_read(parent_mdio, addr, devad, reg);
Alex Marginean8880edb2019-07-12 10:13:50 +030095 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);
Alex Marginean8880edb2019-07-12 10:13:50 +0300106 int err;
107
108 err = mmux_change_sel(ch, true);
109 if (err)
110 return err;
111
Marek Behún1776a242022-04-07 00:32:59 +0200112 err = dm_mdio_write(parent_mdio, addr, devad, reg, val);
Alex Marginean8880edb2019-07-12 10:13:50 +0300113 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);
Alex Marginean8880edb2019-07-12 10:13:50 +0300123 int err;
124
125 /* reset is optional, if it's not implemented just exit */
Marek Behún1776a242022-04-07 00:32:59 +0200126 if (!mdio_get_ops(parent_mdio)->reset)
Alex Marginean8880edb2019-07-12 10:13:50 +0300127 return 0;
128
129 err = mmux_change_sel(ch, true);
130 if (err)
131 return err;
132
Marek Behún1776a242022-04-07 00:32:59 +0200133 err = dm_mdio_reset(parent_mdio);
Alex Marginean8880edb2019-07-12 10:13:50 +0300134 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{
Simon Glasscaa4daa2020-12-03 16:55:18 -0700142 struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
Alex Marginean8880edb2019-07-12 10:13:50 +0300143
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
Simon Glassf10643c2020-12-19 10:40:14 -0700158 if (!dev_has_ofnode(mux)) {
Alex Marginean8880edb2019-07-12 10:13:50 +0300159 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,
Simon Glass41575d82020-12-03 16:55:17 -0700223 .per_device_auto = sizeof(struct mdio_mux_perdev_priv),
Simon Glasscaa4daa2020-12-03 16:55:18 -0700224 .per_child_plat_auto = sizeof(struct mdio_mux_ch_data),
Alex Marginean8880edb2019-07-12 10:13:50 +0300225};