xref: /freebsd/sys/dev/dpaa2/memac_mdio_acpi.c (revision 18250ec6c089c0c50cbd9fd87d78e03ff89916df)
1ba7319e9SDmitry Salychev /*-
2ba7319e9SDmitry Salychev  * SPDX-License-Identifier: BSD-2-Clause
3ba7319e9SDmitry Salychev  *
4ba7319e9SDmitry Salychev  * Copyright © 2021-2022 Bjoern A. Zeeb
5ba7319e9SDmitry Salychev  *
6ba7319e9SDmitry Salychev  * Redistribution and use in source and binary forms, with or without
7ba7319e9SDmitry Salychev  * modification, are permitted provided that the following conditions
8ba7319e9SDmitry Salychev  * are met:
9ba7319e9SDmitry Salychev  * 1. Redistributions of source code must retain the above copyright
10ba7319e9SDmitry Salychev  *    notice, this list of conditions and the following disclaimer.
11ba7319e9SDmitry Salychev  * 2. Redistributions in binary form must reproduce the above copyright
12ba7319e9SDmitry Salychev  *    notice, this list of conditions and the following disclaimer in the
13ba7319e9SDmitry Salychev  *    documentation and/or other materials provided with the distribution.
14ba7319e9SDmitry Salychev  *
15ba7319e9SDmitry Salychev  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16ba7319e9SDmitry Salychev  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17ba7319e9SDmitry Salychev  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ba7319e9SDmitry Salychev  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19ba7319e9SDmitry Salychev  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20ba7319e9SDmitry Salychev  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21ba7319e9SDmitry Salychev  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ba7319e9SDmitry Salychev  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ba7319e9SDmitry Salychev  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ba7319e9SDmitry Salychev  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ba7319e9SDmitry Salychev  * SUCH DAMAGE.
26ba7319e9SDmitry Salychev  */
27ba7319e9SDmitry Salychev 
28ba7319e9SDmitry Salychev #include <sys/param.h>
29ba7319e9SDmitry Salychev #include <sys/kernel.h>
30ba7319e9SDmitry Salychev #include <sys/bus.h>
31ba7319e9SDmitry Salychev #include <sys/rman.h>
32ba7319e9SDmitry Salychev #include <sys/malloc.h>
33ba7319e9SDmitry Salychev #include <sys/module.h>
34ba7319e9SDmitry Salychev #include <sys/endian.h>
35ba7319e9SDmitry Salychev #include <sys/socket.h>
36ba7319e9SDmitry Salychev 
37ba7319e9SDmitry Salychev #include <machine/bus.h>
38ba7319e9SDmitry Salychev #include <machine/resource.h>
39ba7319e9SDmitry Salychev 
40ba7319e9SDmitry Salychev #include <contrib/dev/acpica/include/acpi.h>
41ba7319e9SDmitry Salychev #include <dev/acpica/acpivar.h>
42ba7319e9SDmitry Salychev 
43ba7319e9SDmitry Salychev #include <net/if.h>
44ba7319e9SDmitry Salychev #include <net/if_var.h>
45ba7319e9SDmitry Salychev #include <net/if_media.h>
46ba7319e9SDmitry Salychev 
47ba7319e9SDmitry Salychev #include <dev/mii/mii.h>
48ba7319e9SDmitry Salychev #include <dev/mii/miivar.h>
49ba7319e9SDmitry Salychev 
50ba7319e9SDmitry Salychev #include "memac_mdio.h"
51ba7319e9SDmitry Salychev #include "memac_mdio_if.h"
52ba7319e9SDmitry Salychev #include "acpi_bus_if.h"
53ba7319e9SDmitry Salychev #include "miibus_if.h"
54ba7319e9SDmitry Salychev 
55ba7319e9SDmitry Salychev /* -------------------------------------------------------------------------- */
56ba7319e9SDmitry Salychev 
57ba7319e9SDmitry Salychev struct memacphy_softc_acpi {
58ba7319e9SDmitry Salychev 	struct memacphy_softc_common	scc;
59ba7319e9SDmitry Salychev 	int				uid;
60ba7319e9SDmitry Salychev 	uint64_t			phy_channel;
61ba7319e9SDmitry Salychev 	char				compatible[64];
62ba7319e9SDmitry Salychev };
63ba7319e9SDmitry Salychev 
64ba7319e9SDmitry Salychev static void
memacphy_acpi_miibus_statchg(device_t dev)65ba7319e9SDmitry Salychev memacphy_acpi_miibus_statchg(device_t dev)
66ba7319e9SDmitry Salychev {
67ba7319e9SDmitry Salychev 	struct memacphy_softc_acpi *sc;
68ba7319e9SDmitry Salychev 
69ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
70ba7319e9SDmitry Salychev 	memacphy_miibus_statchg(&sc->scc);
71ba7319e9SDmitry Salychev }
72ba7319e9SDmitry Salychev 
73ba7319e9SDmitry Salychev static int
memacphy_acpi_set_ni_dev(device_t dev,device_t nidev)74ba7319e9SDmitry Salychev memacphy_acpi_set_ni_dev(device_t dev, device_t nidev)
75ba7319e9SDmitry Salychev {
76ba7319e9SDmitry Salychev 	struct memacphy_softc_acpi *sc;
77ba7319e9SDmitry Salychev 
78ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
79ba7319e9SDmitry Salychev 	return (memacphy_set_ni_dev(&sc->scc, nidev));
80ba7319e9SDmitry Salychev }
81ba7319e9SDmitry Salychev 
82ba7319e9SDmitry Salychev static int
memacphy_acpi_get_phy_loc(device_t dev,int * phy_loc)83ba7319e9SDmitry Salychev memacphy_acpi_get_phy_loc(device_t dev, int *phy_loc)
84ba7319e9SDmitry Salychev {
85ba7319e9SDmitry Salychev 	struct memacphy_softc_acpi *sc;
86ba7319e9SDmitry Salychev 
87ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
88ba7319e9SDmitry Salychev 	return (memacphy_get_phy_loc(&sc->scc, phy_loc));
89ba7319e9SDmitry Salychev }
90ba7319e9SDmitry Salychev 
91ba7319e9SDmitry Salychev static int
memacphy_acpi_probe(device_t dev)92ba7319e9SDmitry Salychev memacphy_acpi_probe(device_t dev)
93ba7319e9SDmitry Salychev {
94ba7319e9SDmitry Salychev 
95ba7319e9SDmitry Salychev 	device_set_desc(dev, "MEMAC PHY (acpi)");
96ba7319e9SDmitry Salychev 	return (BUS_PROBE_DEFAULT);
97ba7319e9SDmitry Salychev }
98ba7319e9SDmitry Salychev 
99ba7319e9SDmitry Salychev static int
memacphy_acpi_attach(device_t dev)100ba7319e9SDmitry Salychev memacphy_acpi_attach(device_t dev)
101ba7319e9SDmitry Salychev {
102ba7319e9SDmitry Salychev 	struct memacphy_softc_acpi *sc;
103ba7319e9SDmitry Salychev 	ACPI_HANDLE h;
104ba7319e9SDmitry Salychev 	ssize_t s;
105ba7319e9SDmitry Salychev 
106ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
107ba7319e9SDmitry Salychev 	sc->scc.dev = dev;
108ba7319e9SDmitry Salychev 	h = acpi_get_handle(dev);
109ba7319e9SDmitry Salychev 
110ba7319e9SDmitry Salychev 	s = acpi_GetInteger(h, "_UID", &sc->uid);
111ba7319e9SDmitry Salychev 	if (ACPI_FAILURE(s)) {
112ba7319e9SDmitry Salychev 		device_printf(dev, "Cannot get '_UID' property: %zd\n", s);
113ba7319e9SDmitry Salychev 		return (ENXIO);
114ba7319e9SDmitry Salychev 	}
115ba7319e9SDmitry Salychev 
116ba7319e9SDmitry Salychev 	s = device_get_property(dev, "phy-channel",
117ba7319e9SDmitry Salychev 	    &sc->phy_channel, sizeof(sc->phy_channel), DEVICE_PROP_UINT64);
118ba7319e9SDmitry Salychev 	if (s != -1)
119ba7319e9SDmitry Salychev 		sc->scc.phy = sc->phy_channel;
120ba7319e9SDmitry Salychev 	else
121ba7319e9SDmitry Salychev 		sc->scc.phy = -1;
122ba7319e9SDmitry Salychev 	s = device_get_property(dev, "compatible",
123ba7319e9SDmitry Salychev 	    sc->compatible, sizeof(sc->compatible), DEVICE_PROP_ANY);
124ba7319e9SDmitry Salychev 
125ba7319e9SDmitry Salychev 	if (bootverbose)
126ba7319e9SDmitry Salychev 		device_printf(dev, "UID %#04x phy-channel %ju compatible '%s' phy %u\n",
127ba7319e9SDmitry Salychev 		    sc->uid, sc->phy_channel,
128ba7319e9SDmitry Salychev 		    sc->compatible[0] != '\0' ? sc->compatible : "", sc->scc.phy);
129ba7319e9SDmitry Salychev 
130ba7319e9SDmitry Salychev 	if (sc->scc.phy == -1)
131ba7319e9SDmitry Salychev 		return (ENXIO);
132ba7319e9SDmitry Salychev 	return (0);
133ba7319e9SDmitry Salychev }
134ba7319e9SDmitry Salychev 
135ba7319e9SDmitry Salychev static device_method_t memacphy_acpi_methods[] = {
136ba7319e9SDmitry Salychev 	/* Device interface */
137ba7319e9SDmitry Salychev 	DEVMETHOD(device_probe,		memacphy_acpi_probe),
138ba7319e9SDmitry Salychev 	DEVMETHOD(device_attach,	memacphy_acpi_attach),
139ba7319e9SDmitry Salychev 	DEVMETHOD(device_detach,	bus_generic_detach),
140ba7319e9SDmitry Salychev 
141ba7319e9SDmitry Salychev 	/* MII interface */
142ba7319e9SDmitry Salychev 	DEVMETHOD(miibus_readreg,	memacphy_miibus_readreg),
143ba7319e9SDmitry Salychev 	DEVMETHOD(miibus_writereg,	memacphy_miibus_writereg),
144ba7319e9SDmitry Salychev 	DEVMETHOD(miibus_statchg,	memacphy_acpi_miibus_statchg),
145ba7319e9SDmitry Salychev 
146ba7319e9SDmitry Salychev 	/* memac */
147ba7319e9SDmitry Salychev 	DEVMETHOD(memac_mdio_set_ni_dev, memacphy_acpi_set_ni_dev),
148ba7319e9SDmitry Salychev 	DEVMETHOD(memac_mdio_get_phy_loc, memacphy_acpi_get_phy_loc),
149ba7319e9SDmitry Salychev 
150ba7319e9SDmitry Salychev 	DEVMETHOD_END
151ba7319e9SDmitry Salychev };
152ba7319e9SDmitry Salychev 
153ba7319e9SDmitry Salychev DEFINE_CLASS_0(memacphy_acpi, memacphy_acpi_driver, memacphy_acpi_methods,
154ba7319e9SDmitry Salychev     sizeof(struct memacphy_softc_acpi));
155ba7319e9SDmitry Salychev 
156ba7319e9SDmitry Salychev EARLY_DRIVER_MODULE(memacphy_acpi, memac_mdio_acpi, memacphy_acpi_driver, 0, 0,
157ba7319e9SDmitry Salychev     BUS_PASS_SUPPORTDEV);
158ba7319e9SDmitry Salychev DRIVER_MODULE(miibus, memacphy_acpi, miibus_driver, 0, 0);
159ba7319e9SDmitry Salychev MODULE_DEPEND(memacphy_acpi, miibus, 1, 1, 1);
160ba7319e9SDmitry Salychev 
161ba7319e9SDmitry Salychev /* -------------------------------------------------------------------------- */
162ba7319e9SDmitry Salychev 
163ba7319e9SDmitry Salychev struct memac_mdio_softc_acpi {
164ba7319e9SDmitry Salychev 	struct memac_mdio_softc_common	scc;
165ba7319e9SDmitry Salychev };
166ba7319e9SDmitry Salychev 
167ba7319e9SDmitry Salychev static int
memac_acpi_miibus_readreg(device_t dev,int phy,int reg)168ba7319e9SDmitry Salychev memac_acpi_miibus_readreg(device_t dev, int phy, int reg)
169ba7319e9SDmitry Salychev {
170ba7319e9SDmitry Salychev 	struct memac_mdio_softc_acpi *sc;
171ba7319e9SDmitry Salychev 
172ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
173ba7319e9SDmitry Salychev 	return (memac_miibus_readreg(&sc->scc, phy, reg));
174ba7319e9SDmitry Salychev }
175ba7319e9SDmitry Salychev 
176ba7319e9SDmitry Salychev static int
memac_acpi_miibus_writereg(device_t dev,int phy,int reg,int data)177ba7319e9SDmitry Salychev memac_acpi_miibus_writereg(device_t dev, int phy, int reg, int data)
178ba7319e9SDmitry Salychev {
179ba7319e9SDmitry Salychev 	struct memac_mdio_softc_acpi *sc;
180ba7319e9SDmitry Salychev 
181ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
182ba7319e9SDmitry Salychev 	return (memac_miibus_writereg(&sc->scc, phy, reg, data));
183ba7319e9SDmitry Salychev }
184ba7319e9SDmitry Salychev 
185ba7319e9SDmitry Salychev /* Context for walking PHY child devices. */
186ba7319e9SDmitry Salychev struct memac_mdio_walk_ctx {
187ba7319e9SDmitry Salychev 	device_t	dev;
188ba7319e9SDmitry Salychev 	int		count;
189ba7319e9SDmitry Salychev 	int		countok;
190ba7319e9SDmitry Salychev };
191ba7319e9SDmitry Salychev 
192ba7319e9SDmitry Salychev static char *memac_mdio_ids[] = {
193ba7319e9SDmitry Salychev 	"NXP0006",
194ba7319e9SDmitry Salychev 	NULL
195ba7319e9SDmitry Salychev };
196ba7319e9SDmitry Salychev 
197ba7319e9SDmitry Salychev static int
memac_mdio_acpi_probe(device_t dev)198ba7319e9SDmitry Salychev memac_mdio_acpi_probe(device_t dev)
199ba7319e9SDmitry Salychev {
200ba7319e9SDmitry Salychev 	int rc;
201ba7319e9SDmitry Salychev 
202ba7319e9SDmitry Salychev 	if (acpi_disabled("fsl_memac_mdio"))
203ba7319e9SDmitry Salychev 		return (ENXIO);
204ba7319e9SDmitry Salychev 
205ba7319e9SDmitry Salychev 	rc = ACPI_ID_PROBE(device_get_parent(dev), dev, memac_mdio_ids, NULL);
206ba7319e9SDmitry Salychev 	if (rc <= 0)
207ba7319e9SDmitry Salychev 		device_set_desc(dev, "Freescale XGMAC MDIO Bus");
208ba7319e9SDmitry Salychev 
209ba7319e9SDmitry Salychev 	return (rc);
210ba7319e9SDmitry Salychev }
211ba7319e9SDmitry Salychev 
212ba7319e9SDmitry Salychev static ACPI_STATUS
memac_mdio_acpi_probe_child(ACPI_HANDLE h,device_t * dev,int level,void * arg)213ba7319e9SDmitry Salychev memac_mdio_acpi_probe_child(ACPI_HANDLE h, device_t *dev, int level, void *arg)
214ba7319e9SDmitry Salychev {
215ba7319e9SDmitry Salychev 	struct memac_mdio_walk_ctx *ctx;
216ba7319e9SDmitry Salychev 	struct acpi_device *ad;
217ba7319e9SDmitry Salychev 	device_t child;
218ba7319e9SDmitry Salychev 	uint32_t adr;
219ba7319e9SDmitry Salychev 
220ba7319e9SDmitry Salychev 	ctx = (struct memac_mdio_walk_ctx *)arg;
221ba7319e9SDmitry Salychev 	ctx->count++;
222ba7319e9SDmitry Salychev 
223ba7319e9SDmitry Salychev 	if (ACPI_FAILURE(acpi_GetInteger(h, "_ADR", &adr)))
224ba7319e9SDmitry Salychev 		return (AE_OK);
225ba7319e9SDmitry Salychev 
226ba7319e9SDmitry Salychev 	/* Technically M_ACPIDEV */
227ba7319e9SDmitry Salychev 	if ((ad = malloc(sizeof(*ad), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL)
228ba7319e9SDmitry Salychev 		return (AE_OK);
229ba7319e9SDmitry Salychev 
230ba7319e9SDmitry Salychev 	child = device_add_child(ctx->dev, "memacphy_acpi", -1);
231ba7319e9SDmitry Salychev 	if (child == NULL) {
232ba7319e9SDmitry Salychev 		free(ad, M_DEVBUF);
233ba7319e9SDmitry Salychev 		return (AE_OK);
234ba7319e9SDmitry Salychev 	}
235ba7319e9SDmitry Salychev 	ad->ad_handle = h;
236ba7319e9SDmitry Salychev 	ad->ad_cls_class = 0xffffff;
237ba7319e9SDmitry Salychev 	resource_list_init(&ad->ad_rl);
238ba7319e9SDmitry Salychev 	device_set_ivars(child, ad);
239ba7319e9SDmitry Salychev 	*dev = child;
240ba7319e9SDmitry Salychev 
241ba7319e9SDmitry Salychev 	ctx->countok++;
242ba7319e9SDmitry Salychev 	return (AE_OK);
243ba7319e9SDmitry Salychev }
244ba7319e9SDmitry Salychev 
245ba7319e9SDmitry Salychev static int
memac_mdio_acpi_attach(device_t dev)246ba7319e9SDmitry Salychev memac_mdio_acpi_attach(device_t dev)
247ba7319e9SDmitry Salychev {
248ba7319e9SDmitry Salychev 	struct memac_mdio_softc_acpi *sc;
249ba7319e9SDmitry Salychev 	struct memac_mdio_walk_ctx ctx;
250ba7319e9SDmitry Salychev 	int error;
251ba7319e9SDmitry Salychev 
252ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
253ba7319e9SDmitry Salychev 	sc->scc.dev = dev;
254ba7319e9SDmitry Salychev 
255ba7319e9SDmitry Salychev 	error = memac_mdio_generic_attach(&sc->scc);
256ba7319e9SDmitry Salychev 	if (error != 0)
257ba7319e9SDmitry Salychev 		return (error);
258ba7319e9SDmitry Salychev 
259ba7319e9SDmitry Salychev 	ctx.dev = dev;
260ba7319e9SDmitry Salychev 	ctx.count = 0;
261ba7319e9SDmitry Salychev 	ctx.countok = 0;
262ba7319e9SDmitry Salychev 	ACPI_SCAN_CHILDREN(device_get_parent(dev), dev, 1,
263ba7319e9SDmitry Salychev 	    memac_mdio_acpi_probe_child, &ctx);
264ba7319e9SDmitry Salychev 	if (ctx.countok > 0) {
265723da5d9SJohn Baldwin 		bus_identify_children(dev);
266*18250ec6SJohn Baldwin 		bus_attach_children(dev);
267ba7319e9SDmitry Salychev 	}
268ba7319e9SDmitry Salychev 
269ba7319e9SDmitry Salychev 	return (0);
270ba7319e9SDmitry Salychev }
271ba7319e9SDmitry Salychev 
272ba7319e9SDmitry Salychev static int
memac_mdio_acpi_detach(device_t dev)273ba7319e9SDmitry Salychev memac_mdio_acpi_detach(device_t dev)
274ba7319e9SDmitry Salychev {
275ba7319e9SDmitry Salychev 	struct memac_mdio_softc_acpi *sc;
276ba7319e9SDmitry Salychev 
277ba7319e9SDmitry Salychev 	sc = device_get_softc(dev);
278ba7319e9SDmitry Salychev 	return (memac_mdio_generic_detach(&sc->scc));
279ba7319e9SDmitry Salychev }
280ba7319e9SDmitry Salychev 
281ba7319e9SDmitry Salychev static device_method_t memac_mdio_acpi_methods[] = {
282ba7319e9SDmitry Salychev 	/* Device interface */
283ba7319e9SDmitry Salychev 	DEVMETHOD(device_probe,		memac_mdio_acpi_probe),
284ba7319e9SDmitry Salychev 	DEVMETHOD(device_attach,	memac_mdio_acpi_attach),
285ba7319e9SDmitry Salychev 	DEVMETHOD(device_detach,	memac_mdio_acpi_detach),
286ba7319e9SDmitry Salychev 
287ba7319e9SDmitry Salychev 	/* MII interface */
288ba7319e9SDmitry Salychev 	DEVMETHOD(miibus_readreg,	memac_acpi_miibus_readreg),
289ba7319e9SDmitry Salychev 	DEVMETHOD(miibus_writereg,	memac_acpi_miibus_writereg),
290ba7319e9SDmitry Salychev 
291ba7319e9SDmitry Salychev 	/* .. */
292ba7319e9SDmitry Salychev 	DEVMETHOD(bus_add_child,	bus_generic_add_child),
293ba7319e9SDmitry Salychev 	DEVMETHOD(bus_read_ivar,	memac_mdio_read_ivar),
294ba7319e9SDmitry Salychev 	DEVMETHOD(bus_get_property,	memac_mdio_get_property),
295ba7319e9SDmitry Salychev 
296ba7319e9SDmitry Salychev 	DEVMETHOD_END
297ba7319e9SDmitry Salychev };
298ba7319e9SDmitry Salychev 
299ba7319e9SDmitry Salychev DEFINE_CLASS_0(memac_mdio_acpi, memac_mdio_acpi_driver, memac_mdio_acpi_methods,
300ba7319e9SDmitry Salychev     sizeof(struct memac_mdio_softc_acpi));
301ba7319e9SDmitry Salychev 
302ba7319e9SDmitry Salychev EARLY_DRIVER_MODULE(memac_mdio_acpi, acpi, memac_mdio_acpi_driver, 0, 0,
303ba7319e9SDmitry Salychev     BUS_PASS_SUPPORTDEV);
304ba7319e9SDmitry Salychev 
305ba7319e9SDmitry Salychev DRIVER_MODULE(miibus, memac_mdio_acpi, miibus_driver, 0, 0);
306ba7319e9SDmitry Salychev MODULE_DEPEND(memac_mdio_acpi, miibus, 1, 1, 1);
307ba7319e9SDmitry Salychev MODULE_VERSION(memac_mdio_acpi, 1);
308