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
61struct 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
91static int
92dpaa2_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", &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
117static int
118dpaa2_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
157static bool
158dpaa2_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
172static device_t
173dpaa2_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
196static 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
205DEFINE_CLASS_0(dpaa2_mac_fdt, dpaa2_mac_fdt_driver, dpaa2_mac_fdt_methods,
206    sizeof(struct dpaa2_mac_fdt_softc));
207DRIVER_MODULE(dpaa2_mac_fdt, dpaa2_mc, dpaa2_mac_fdt_driver, 0, 0);
208MODULE_DEPEND(dpaa2_mac_fdt, memac_mdio_fdt, 1, 1, 1);
209
210/*
211 * Device interface.
212 */
213
214static int
215dpaa2_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
227static int
228dpaa2_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
245static int
246dpaa2_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_generic_probe(dev);
257	bus_enumerate_hinted_children(dev);
258
259	bus_generic_probe(dev);
260	bus_enumerate_hinted_children(dev);
261	/*
262	 * Attach the children represented in the device tree.
263	 */
264	/* fsl-mc -> dpamcs */
265	node = OF_child(sc->ofw_node);
266	simplebus_init(dev, node);
267
268	/* Attach the dpmac children represented in the device tree. */
269	child = ofw_bus_find_compatible(node, "fsl,qoriq-mc-dpmac");
270	for (; child > 0; child = OF_peer(child)) {
271		if (!ofw_bus_node_is_compatible(child, "fsl,qoriq-mc-dpmac"))
272			continue;
273		if (!OF_hasprop(child, "reg"))
274			continue;
275		if (dpaa2_mc_fdt_probe_child(dev, child) != 0)
276			continue;
277	}
278
279	return (dpaa2_mc_attach(dev));
280}
281
282/*
283 * FDT compat layer.
284 */
285static device_t
286dpaa2_mc_fdt_find_dpaa2_mac_dev(device_t dev, uint32_t id)
287{
288	int devcount, error, i, len;
289	device_t *devlist, mdev;
290	const char *mdevname;
291
292	error = device_get_children(dev, &devlist, &devcount);
293	if (error != 0)
294		return (NULL);
295
296	for (i = 0; i < devcount; i++) {
297		mdev = devlist[i];
298		mdevname = device_get_name(mdev);
299		if (mdevname == NULL)
300			continue;
301		len = strlen(mdevname);
302		if (strncmp("dpaa2_mac_fdt", mdevname, len) != 0)
303			continue;
304		if (!device_is_attached(mdev))
305			continue;
306
307		if (dpaa2_mac_fdt_match_id(mdev, id))
308			return (mdev);
309	}
310
311	return (NULL);
312}
313
314static int
315dpaa2_mc_fdt_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id)
316{
317	device_t mdev, pdev;
318
319	mdev = dpaa2_mc_fdt_find_dpaa2_mac_dev(dev, id);
320	if (mdev == NULL) {
321		device_printf(dev, "%s: error finding dpmac device with id=%u\n",
322		    __func__, id);
323		return (ENXIO);
324	}
325
326	pdev = dpaa2_mac_fdt_get_phy_dev(mdev);
327	if (pdev == NULL) {
328		device_printf(dev, "%s: error getting MDIO device for dpamc %s "
329		    "(id=%u)\n", __func__, device_get_nameunit(mdev), id);
330		return (ENXIO);
331	}
332
333	if (phy_dev != NULL)
334		*phy_dev = pdev;
335
336	if (bootverbose)
337		device_printf(dev, "dpmac_id %u mdev %p (%s) pdev %p (%s)\n",
338		    id, mdev, device_get_nameunit(mdev),
339		    pdev, device_get_nameunit(pdev));
340
341	return (0);
342}
343
344static const struct ofw_bus_devinfo *
345dpaa2_mc_simplebus_get_devinfo(device_t bus, device_t child)
346{
347
348	return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child));
349}
350
351static device_method_t dpaa2_mc_fdt_methods[] = {
352	/* Device interface */
353	DEVMETHOD(device_probe,		dpaa2_mc_fdt_probe),
354	DEVMETHOD(device_attach,	dpaa2_mc_fdt_attach),
355	DEVMETHOD(device_detach,	dpaa2_mc_detach),
356
357	/* Bus interface */
358	DEVMETHOD(bus_get_rman,		dpaa2_mc_rman),
359	DEVMETHOD(bus_alloc_resource,	dpaa2_mc_alloc_resource),
360	DEVMETHOD(bus_adjust_resource,	dpaa2_mc_adjust_resource),
361	DEVMETHOD(bus_release_resource,	dpaa2_mc_release_resource),
362	DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource),
363	DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource),
364	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
365	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
366
367	/* Pseudo-PCIB interface */
368	DEVMETHOD(pcib_alloc_msi,	dpaa2_mc_alloc_msi),
369	DEVMETHOD(pcib_release_msi,	dpaa2_mc_release_msi),
370	DEVMETHOD(pcib_map_msi,		dpaa2_mc_map_msi),
371	DEVMETHOD(pcib_get_id,		dpaa2_mc_get_id),
372
373	/* DPAA2 MC bus interface */
374	DEVMETHOD(dpaa2_mc_manage_dev,	dpaa2_mc_manage_dev),
375	DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev),
376	DEVMETHOD(dpaa2_mc_get_dev,	dpaa2_mc_get_dev),
377	DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev),
378	DEVMETHOD(dpaa2_mc_reserve_dev,	dpaa2_mc_reserve_dev),
379	DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev),
380	DEVMETHOD(dpaa2_mc_get_phy_dev,	dpaa2_mc_fdt_get_phy_dev),
381
382	/* OFW/simplebus */
383	DEVMETHOD(ofw_bus_get_devinfo,	dpaa2_mc_simplebus_get_devinfo),
384	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
385	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
386	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
387	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
388	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
389
390	DEVMETHOD_END
391};
392
393DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_fdt_driver, dpaa2_mc_fdt_methods,
394    sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver);
395
396DRIVER_MODULE(dpaa2_mc, simplebus, dpaa2_mc_fdt_driver, 0, 0);
397