1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright © 2021-2022 Dmitry Salychev
5 * Copyright © 2022 Bjoern A. Zeeb
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 /*
31 * The DPAA2 Management Complex (MC) Bus Driver (FDT-based).
32 *
33 * MC is a hardware resource manager which can be found in several NXP
34 * SoCs (LX2160A, for example) and provides an access to the specialized
35 * hardware objects used in network-oriented packet processing applications.
36 */
37
38 #include <sys/param.h>
39 #include <sys/kernel.h>
40 #include <sys/bus.h>
41 #include <sys/rman.h>
42 #include <sys/module.h>
43 #include <sys/malloc.h>
44 #include <sys/mutex.h>
45
46 #include <machine/bus.h>
47 #include <machine/resource.h>
48
49 #include <dev/ofw/ofw_bus.h>
50 #include <dev/ofw/ofw_bus_subr.h>
51 #include <dev/fdt/simplebus.h>
52
53 #include "pcib_if.h"
54 #include "pci_if.h"
55 #include "ofw_bus_if.h"
56
57 #include "dpaa2_mcp.h"
58 #include "dpaa2_mc.h"
59 #include "dpaa2_mc_if.h"
60
61 struct dpaa2_mac_fdt_softc {
62 uint32_t reg;
63 phandle_t sfp;
64 phandle_t pcs_handle;
65 phandle_t phy_handle;
66 char managed[64];
67 char phy_conn_type[64];
68 };
69
70 #if 0
71 ethernet@1 {
72
73 compatible = "fsl,qoriq-mc-dpmac";
74 reg = <0x1>;
75 sfp = <0x14>;
76 pcs-handle = <0x15>;
77 phy-connection-type = "10gbase-r";
78 managed = "in-band-status";
79 };
80 ethernet@3 {
81
82 compatible = "fsl,qoriq-mc-dpmac";
83 reg = <0x3>;
84 phy-handle = <0x18>;
85 phy-connection-type = "qsgmii";
86 managed = "in-band-status";
87 pcs-handle = <0x19>;
88 };
89 #endif
90
91 static int
dpaa2_mac_dev_probe(device_t dev)92 dpaa2_mac_dev_probe(device_t dev)
93 {
94 phandle_t node;
95 uint64_t reg;
96 ssize_t s;
97
98 node = ofw_bus_get_node(dev);
99 if (!ofw_bus_node_is_compatible(node, "fsl,qoriq-mc-dpmac")) {
100 device_printf(dev, "'%s' not fsl,qoriq-mc-dpmac compatible\n",
101 ofw_bus_get_name(dev));
102 return (ENXIO);
103 }
104
105 s = device_get_property(dev, "reg", ®, sizeof(reg),
106 DEVICE_PROP_UINT32);
107 if (s == -1) {
108 device_printf(dev, "%s: '%s' has no 'reg' property, s %zd\n",
109 __func__, ofw_bus_get_name(dev), s);
110 return (ENXIO);
111 }
112
113 device_set_desc(dev, "DPAA2 MAC DEV");
114 return (BUS_PROBE_DEFAULT);
115 }
116
117 static int
dpaa2_mac_fdt_attach(device_t dev)118 dpaa2_mac_fdt_attach(device_t dev)
119 {
120 struct dpaa2_mac_fdt_softc *sc;
121 phandle_t node;
122 ssize_t s;
123
124 sc = device_get_softc(dev);
125 node = ofw_bus_get_node(dev);
126
127 s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg),
128 DEVICE_PROP_UINT32);
129 if (s == -1) {
130 device_printf(dev, "Cannot find 'reg' property: %zd\n", s);
131 return (ENXIO);
132 }
133
134 s = device_get_property(dev, "managed", sc->managed,
135 sizeof(sc->managed), DEVICE_PROP_ANY);
136 s = device_get_property(dev, "phy-connection-type", sc->phy_conn_type,
137 sizeof(sc->phy_conn_type), DEVICE_PROP_ANY);
138 s = device_get_property(dev, "pcs-handle", &sc->pcs_handle,
139 sizeof(sc->pcs_handle), DEVICE_PROP_HANDLE);
140
141 /* 'sfp' and 'phy-handle' are optional but we need one or the other. */
142 s = device_get_property(dev, "sfp", &sc->sfp, sizeof(sc->sfp),
143 DEVICE_PROP_HANDLE);
144 s = device_get_property(dev, "phy-handle", &sc->phy_handle,
145 sizeof(sc->phy_handle), DEVICE_PROP_HANDLE);
146
147 if (bootverbose)
148 device_printf(dev, "node %#x '%s': reg %#x sfp %#x pcs-handle "
149 "%#x phy-handle %#x managed '%s' phy-conn-type '%s'\n",
150 node, ofw_bus_get_name(dev),
151 sc->reg, sc->sfp, sc->pcs_handle, sc->phy_handle,
152 sc->managed, sc->phy_conn_type);
153
154 return (0);
155 }
156
157 static bool
dpaa2_mac_fdt_match_id(device_t dev,uint32_t id)158 dpaa2_mac_fdt_match_id(device_t dev, uint32_t id)
159 {
160 struct dpaa2_mac_fdt_softc *sc;
161
162 if (dev == NULL)
163 return (false);
164
165 sc = device_get_softc(dev);
166 if (sc->reg == id)
167 return (true);
168
169 return (false);
170 }
171
172 static device_t
dpaa2_mac_fdt_get_phy_dev(device_t dev)173 dpaa2_mac_fdt_get_phy_dev(device_t dev)
174 {
175 struct dpaa2_mac_fdt_softc *sc;
176
177 if (dev == NULL)
178 return (NULL);
179
180 sc = device_get_softc(dev);
181 if (sc->phy_handle == 0 && sc->sfp == 0)
182 return (NULL);
183
184 #ifdef __not_yet__ /* No sff,sfp support yet. */
185 if (sc->sfp != 0) {
186 device_t xdev;
187
188 xdev = OF_device_from_xref(OF_xref_from_node(sc->sfp));
189 if (xdev != NULL)
190 return (xdev);
191 }
192 #endif
193 return (OF_device_from_xref(OF_xref_from_node(sc->phy_handle)));
194 }
195
196 static device_method_t dpaa2_mac_fdt_methods[] = {
197 /* Device interface */
198 DEVMETHOD(device_probe, dpaa2_mac_dev_probe),
199 DEVMETHOD(device_attach, dpaa2_mac_fdt_attach),
200 DEVMETHOD(device_detach, bus_generic_detach),
201
202 DEVMETHOD_END
203 };
204
205 DEFINE_CLASS_0(dpaa2_mac_fdt, dpaa2_mac_fdt_driver, dpaa2_mac_fdt_methods,
206 sizeof(struct dpaa2_mac_fdt_softc));
207 DRIVER_MODULE(dpaa2_mac_fdt, dpaa2_mc, dpaa2_mac_fdt_driver, 0, 0);
208 MODULE_DEPEND(dpaa2_mac_fdt, memac_mdio_fdt, 1, 1, 1);
209
210 /*
211 * Device interface.
212 */
213
214 static int
dpaa2_mc_fdt_probe(device_t dev)215 dpaa2_mc_fdt_probe(device_t dev)
216 {
217 if (!ofw_bus_status_okay(dev))
218 return (ENXIO);
219
220 if (!ofw_bus_is_compatible(dev, "fsl,qoriq-mc"))
221 return (ENXIO);
222
223 device_set_desc(dev, "DPAA2 Management Complex");
224 return (BUS_PROBE_DEFAULT);
225 }
226
227 static int
dpaa2_mc_fdt_probe_child(device_t bus,phandle_t child)228 dpaa2_mc_fdt_probe_child(device_t bus, phandle_t child)
229 {
230 device_t childdev;
231
232 /* make sure we do not aliready have a device. */
233 childdev = ofw_bus_find_child_device_by_phandle(bus, child);
234 if (childdev != NULL)
235 return (0);
236
237 childdev = simplebus_add_device(bus, child, 0, "dpaa2_mac_fdt", -1,
238 NULL);
239 if (childdev == NULL)
240 return (ENXIO);
241
242 return (device_probe_and_attach(childdev));
243 }
244
245 static int
dpaa2_mc_fdt_attach(device_t dev)246 dpaa2_mc_fdt_attach(device_t dev)
247 {
248 struct dpaa2_mc_softc *sc;
249 phandle_t node;
250 phandle_t child;
251
252 sc = device_get_softc(dev);
253 sc->acpi_based = false;
254 sc->ofw_node = ofw_bus_get_node(dev);
255
256 bus_identify_children(dev);
257 bus_enumerate_hinted_children(dev);
258
259 bus_identify_children(dev);
260 bus_enumerate_hinted_children(dev);
261
262 /*
263 * Attach the children represented in the device tree.
264 */
265 /* fsl-mc -> dpamcs */
266 node = OF_child(sc->ofw_node);
267 simplebus_init(dev, node);
268
269 /* Attach the dpmac children represented in the device tree. */
270 child = ofw_bus_find_compatible(node, "fsl,qoriq-mc-dpmac");
271 for (; child > 0; child = OF_peer(child)) {
272 if (!ofw_bus_node_is_compatible(child, "fsl,qoriq-mc-dpmac"))
273 continue;
274 if (!OF_hasprop(child, "reg"))
275 continue;
276 if (dpaa2_mc_fdt_probe_child(dev, child) != 0)
277 continue;
278 }
279
280 return (dpaa2_mc_attach(dev));
281 }
282
283 /*
284 * FDT compat layer.
285 */
286 static device_t
dpaa2_mc_fdt_find_dpaa2_mac_dev(device_t dev,uint32_t id)287 dpaa2_mc_fdt_find_dpaa2_mac_dev(device_t dev, uint32_t id)
288 {
289 int devcount, error, i, len;
290 device_t *devlist, mdev;
291 const char *mdevname;
292
293 error = device_get_children(dev, &devlist, &devcount);
294 if (error != 0)
295 return (NULL);
296
297 for (i = 0; i < devcount; i++) {
298 mdev = devlist[i];
299 mdevname = device_get_name(mdev);
300 if (mdevname == NULL)
301 continue;
302 len = strlen(mdevname);
303 if (strncmp("dpaa2_mac_fdt", mdevname, len) != 0)
304 continue;
305 if (!device_is_attached(mdev))
306 continue;
307
308 if (dpaa2_mac_fdt_match_id(mdev, id))
309 return (mdev);
310 }
311
312 return (NULL);
313 }
314
315 static int
dpaa2_mc_fdt_get_phy_dev(device_t dev,device_t * phy_dev,uint32_t id)316 dpaa2_mc_fdt_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id)
317 {
318 device_t mdev, pdev;
319
320 mdev = dpaa2_mc_fdt_find_dpaa2_mac_dev(dev, id);
321 if (mdev == NULL) {
322 device_printf(dev, "%s: error finding dpmac device with id=%u\n",
323 __func__, id);
324 return (ENXIO);
325 }
326
327 pdev = dpaa2_mac_fdt_get_phy_dev(mdev);
328 if (pdev == NULL) {
329 device_printf(dev, "%s: error getting MDIO device for dpamc %s "
330 "(id=%u)\n", __func__, device_get_nameunit(mdev), id);
331 return (ENXIO);
332 }
333
334 if (phy_dev != NULL)
335 *phy_dev = pdev;
336
337 if (bootverbose)
338 device_printf(dev, "dpmac_id %u mdev %p (%s) pdev %p (%s)\n",
339 id, mdev, device_get_nameunit(mdev),
340 pdev, device_get_nameunit(pdev));
341
342 return (0);
343 }
344
345 static const struct ofw_bus_devinfo *
dpaa2_mc_simplebus_get_devinfo(device_t bus,device_t child)346 dpaa2_mc_simplebus_get_devinfo(device_t bus, device_t child)
347 {
348
349 return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child));
350 }
351
352 static device_method_t dpaa2_mc_fdt_methods[] = {
353 /* Device interface */
354 DEVMETHOD(device_probe, dpaa2_mc_fdt_probe),
355 DEVMETHOD(device_attach, dpaa2_mc_fdt_attach),
356 DEVMETHOD(device_detach, dpaa2_mc_detach),
357
358 /* Bus interface */
359 DEVMETHOD(bus_get_rman, dpaa2_mc_rman),
360 DEVMETHOD(bus_alloc_resource, dpaa2_mc_alloc_resource),
361 DEVMETHOD(bus_adjust_resource, dpaa2_mc_adjust_resource),
362 DEVMETHOD(bus_release_resource, dpaa2_mc_release_resource),
363 DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource),
364 DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource),
365 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
366 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
367
368 /* Pseudo-PCIB interface */
369 DEVMETHOD(pcib_alloc_msi, dpaa2_mc_alloc_msi),
370 DEVMETHOD(pcib_release_msi, dpaa2_mc_release_msi),
371 DEVMETHOD(pcib_map_msi, dpaa2_mc_map_msi),
372 DEVMETHOD(pcib_get_id, dpaa2_mc_get_id),
373
374 /* DPAA2 MC bus interface */
375 DEVMETHOD(dpaa2_mc_manage_dev, dpaa2_mc_manage_dev),
376 DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev),
377 DEVMETHOD(dpaa2_mc_get_dev, dpaa2_mc_get_dev),
378 DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev),
379 DEVMETHOD(dpaa2_mc_reserve_dev, dpaa2_mc_reserve_dev),
380 DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev),
381 DEVMETHOD(dpaa2_mc_get_phy_dev, dpaa2_mc_fdt_get_phy_dev),
382
383 /* OFW/simplebus */
384 DEVMETHOD(ofw_bus_get_devinfo, dpaa2_mc_simplebus_get_devinfo),
385 DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
386 DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
387 DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
388 DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
389 DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
390
391 DEVMETHOD_END
392 };
393
394 DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_fdt_driver, dpaa2_mc_fdt_methods,
395 sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver);
396
397 DRIVER_MODULE(dpaa2_mc, simplebus, dpaa2_mc_fdt_driver, 0, 0);
398