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