Prepare v2024.10
[platform/kernel/u-boot.git] / net / mdio-mux-uclass.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2019
4  * Alex Marginean, NXP
5  */
6
7 #include <dm.h>
8 #include <log.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  */
24 struct 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  */
45 struct mdio_mux_ch_data {
46         int sel;
47 };
48
49 static 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
56 /* call driver select function before performing MDIO r/w */
57 static 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);
62         struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
63         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 */
82 static 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);
87         int err;
88
89         err = mmux_change_sel(ch, true);
90         if (err)
91                 return err;
92
93         err = dm_mdio_read(parent_mdio, addr, devad, reg);
94         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 */
100 static 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);
105         int err;
106
107         err = mmux_change_sel(ch, true);
108         if (err)
109                 return err;
110
111         err = dm_mdio_write(parent_mdio, addr, devad, reg, val);
112         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 */
118 static int mmux_reset(struct udevice *ch)
119 {
120         struct udevice *mux = ch->parent;
121         struct udevice *parent_mdio = mmux_get_parent_mdio(mux);
122         int err;
123
124         /* reset is optional, if it's not implemented just exit */
125         if (!mdio_get_ops(parent_mdio)->reset)
126                 return 0;
127
128         err = mmux_change_sel(ch, true);
129         if (err)
130                 return err;
131
132         err = dm_mdio_reset(parent_mdio);
133         mmux_change_sel(ch, false);
134
135         return err;
136 }
137
138 /* Picks up the mux selection value for each child */
139 static int dm_mdio_mux_child_post_bind(struct udevice *ch)
140 {
141         struct mdio_mux_ch_data *ch_data = dev_get_parent_plat(ch);
142
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 */
152 static int dm_mdio_mux_post_bind(struct udevice *mux)
153 {
154         ofnode ch_node;
155         int err, first_err = 0;
156
157         if (!dev_has_ofnode(mux)) {
158                 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 */
185 static 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
203 const 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 */
210 U_BOOT_DRIVER(mdio_mux_child) = {
211         .name           = MDIO_MUX_CHILD_DRV_NAME,
212         .id             = UCLASS_MDIO,
213         .ops            = &mmux_child_mdio_ops,
214 };
215
216 UCLASS_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,
222         .per_device_auto        = sizeof(struct mdio_mux_perdev_priv),
223         .per_child_plat_auto    = sizeof(struct mdio_mux_ch_data),
224 };