1*9313f6b0SJohn Baldwin /*-
2*9313f6b0SJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause
3*9313f6b0SJohn Baldwin *
4*9313f6b0SJohn Baldwin * Copyright (c) 2020 Alexander Motin <mav@FreeBSD.org>
5*9313f6b0SJohn Baldwin * Copyright (c) 2025-2026 Netflix, Inc.
6*9313f6b0SJohn Baldwin *
7*9313f6b0SJohn Baldwin * Redistribution and use in source and binary forms, with or without
8*9313f6b0SJohn Baldwin * modification, are permitted provided that the following conditions
9*9313f6b0SJohn Baldwin * are met:
10*9313f6b0SJohn Baldwin * 1. Redistributions of source code must retain the above copyright
11*9313f6b0SJohn Baldwin * notice, this list of conditions and the following disclaimer.
12*9313f6b0SJohn Baldwin * 2. Redistributions in binary form must reproduce the above copyright
13*9313f6b0SJohn Baldwin * notice, this list of conditions and the following disclaimer in the
14*9313f6b0SJohn Baldwin * documentation and/or other materials provided with the distribution.
15*9313f6b0SJohn Baldwin *
16*9313f6b0SJohn Baldwin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*9313f6b0SJohn Baldwin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*9313f6b0SJohn Baldwin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*9313f6b0SJohn Baldwin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*9313f6b0SJohn Baldwin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*9313f6b0SJohn Baldwin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*9313f6b0SJohn Baldwin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*9313f6b0SJohn Baldwin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*9313f6b0SJohn Baldwin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*9313f6b0SJohn Baldwin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*9313f6b0SJohn Baldwin * SUCH DAMAGE.
27*9313f6b0SJohn Baldwin */
28*9313f6b0SJohn Baldwin
29*9313f6b0SJohn Baldwin #include <sys/types.h>
30*9313f6b0SJohn Baldwin #include <sys/bus.h>
31*9313f6b0SJohn Baldwin #include <sys/kernel.h>
32*9313f6b0SJohn Baldwin #include <sys/malloc.h>
33*9313f6b0SJohn Baldwin #include <sys/module.h>
34*9313f6b0SJohn Baldwin #include <sys/rman.h>
35*9313f6b0SJohn Baldwin
36*9313f6b0SJohn Baldwin #include <contrib/dev/acpica/include/acpi.h>
37*9313f6b0SJohn Baldwin
38*9313f6b0SJohn Baldwin #include <dev/acpica/acpivar.h>
39*9313f6b0SJohn Baldwin #include <dev/acpica/apeivar.h>
40*9313f6b0SJohn Baldwin
41*9313f6b0SJohn Baldwin /*
42*9313f6b0SJohn Baldwin * Different APEI tables can reuse the same registers (and sometimes
43*9313f6b0SJohn Baldwin * different views of the same register, e.g. 32- vs 64-bit mappings
44*9313f6b0SJohn Baldwin * of the same register). To enable this sharing, apei0 acts as a bus
45*9313f6b0SJohn Baldwin * device managing a pool of allocated resources and hands out
46*9313f6b0SJohn Baldwin * mappings to child devices. Child devices handle individual tables.
47*9313f6b0SJohn Baldwin */
48*9313f6b0SJohn Baldwin
49*9313f6b0SJohn Baldwin struct apei_register {
50*9313f6b0SJohn Baldwin struct resource *res;
51*9313f6b0SJohn Baldwin TAILQ_ENTRY(apei_register) link;
52*9313f6b0SJohn Baldwin };
53*9313f6b0SJohn Baldwin
54*9313f6b0SJohn Baldwin struct apei_register_mapping {
55*9313f6b0SJohn Baldwin struct resource_map map;
56*9313f6b0SJohn Baldwin struct apei_register *reg;
57*9313f6b0SJohn Baldwin TAILQ_ENTRY(apei_register_mapping) link;
58*9313f6b0SJohn Baldwin };
59*9313f6b0SJohn Baldwin
60*9313f6b0SJohn Baldwin struct apei_ivars {
61*9313f6b0SJohn Baldwin TAILQ_HEAD(, apei_register_mapping) mappings;
62*9313f6b0SJohn Baldwin };
63*9313f6b0SJohn Baldwin
64*9313f6b0SJohn Baldwin TAILQ_HEAD(apei_register_list, apei_register);
65*9313f6b0SJohn Baldwin
66*9313f6b0SJohn Baldwin struct apei_softc {
67*9313f6b0SJohn Baldwin device_t dev;
68*9313f6b0SJohn Baldwin struct apei_register_list mem;
69*9313f6b0SJohn Baldwin struct apei_register_list io;
70*9313f6b0SJohn Baldwin };
71*9313f6b0SJohn Baldwin
72*9313f6b0SJohn Baldwin static char *apei_ids[] = { "PNP0C33", NULL };
73*9313f6b0SJohn Baldwin
74*9313f6b0SJohn Baldwin static MALLOC_DEFINE(M_APEI, "apei", "ACPI platform error interface");
75*9313f6b0SJohn Baldwin
76*9313f6b0SJohn Baldwin static ACPI_STATUS
apei_find(ACPI_HANDLE handle,UINT32 level,void * context,void ** status)77*9313f6b0SJohn Baldwin apei_find(ACPI_HANDLE handle, UINT32 level, void *context,
78*9313f6b0SJohn Baldwin void **status)
79*9313f6b0SJohn Baldwin {
80*9313f6b0SJohn Baldwin int *found = (int *)status;
81*9313f6b0SJohn Baldwin char **ids;
82*9313f6b0SJohn Baldwin
83*9313f6b0SJohn Baldwin for (ids = apei_ids; *ids != NULL; ids++) {
84*9313f6b0SJohn Baldwin if (acpi_MatchHid(handle, *ids)) {
85*9313f6b0SJohn Baldwin *found = 1;
86*9313f6b0SJohn Baldwin break;
87*9313f6b0SJohn Baldwin }
88*9313f6b0SJohn Baldwin }
89*9313f6b0SJohn Baldwin return (AE_OK);
90*9313f6b0SJohn Baldwin }
91*9313f6b0SJohn Baldwin
92*9313f6b0SJohn Baldwin static void
apei_identify(driver_t * driver,device_t parent)93*9313f6b0SJohn Baldwin apei_identify(driver_t *driver, device_t parent)
94*9313f6b0SJohn Baldwin {
95*9313f6b0SJohn Baldwin device_t child;
96*9313f6b0SJohn Baldwin int found;
97*9313f6b0SJohn Baldwin
98*9313f6b0SJohn Baldwin if (acpi_disabled("apei"))
99*9313f6b0SJohn Baldwin return;
100*9313f6b0SJohn Baldwin
101*9313f6b0SJohn Baldwin /* Only one APEI device can exist. */
102*9313f6b0SJohn Baldwin if (device_find_child(parent, "apei", DEVICE_UNIT_ANY) != NULL)
103*9313f6b0SJohn Baldwin return;
104*9313f6b0SJohn Baldwin
105*9313f6b0SJohn Baldwin /* Search for ACPI error device to be used. */
106*9313f6b0SJohn Baldwin found = 0;
107*9313f6b0SJohn Baldwin AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
108*9313f6b0SJohn Baldwin 100, apei_find, NULL, NULL, (void *)&found);
109*9313f6b0SJohn Baldwin if (found)
110*9313f6b0SJohn Baldwin return;
111*9313f6b0SJohn Baldwin
112*9313f6b0SJohn Baldwin /* If not found - create a fake one. */
113*9313f6b0SJohn Baldwin child = BUS_ADD_CHILD(parent, 2, "apei", 0);
114*9313f6b0SJohn Baldwin if (child == NULL)
115*9313f6b0SJohn Baldwin printf("%s: can't add child\n", __func__);
116*9313f6b0SJohn Baldwin }
117*9313f6b0SJohn Baldwin
118*9313f6b0SJohn Baldwin static int
apei_probe(device_t dev)119*9313f6b0SJohn Baldwin apei_probe(device_t dev)
120*9313f6b0SJohn Baldwin {
121*9313f6b0SJohn Baldwin int rv;
122*9313f6b0SJohn Baldwin
123*9313f6b0SJohn Baldwin if (acpi_disabled("apei"))
124*9313f6b0SJohn Baldwin return (ENXIO);
125*9313f6b0SJohn Baldwin
126*9313f6b0SJohn Baldwin if (acpi_get_handle(dev) != NULL) {
127*9313f6b0SJohn Baldwin rv = ACPI_ID_PROBE(device_get_parent(dev), dev, apei_ids, NULL);
128*9313f6b0SJohn Baldwin if (rv > 0)
129*9313f6b0SJohn Baldwin return (rv);
130*9313f6b0SJohn Baldwin }
131*9313f6b0SJohn Baldwin
132*9313f6b0SJohn Baldwin device_set_desc(dev, "ACPI Platform Error Interface");
133*9313f6b0SJohn Baldwin return (0);
134*9313f6b0SJohn Baldwin }
135*9313f6b0SJohn Baldwin
136*9313f6b0SJohn Baldwin static int
apei_attach(device_t dev)137*9313f6b0SJohn Baldwin apei_attach(device_t dev)
138*9313f6b0SJohn Baldwin {
139*9313f6b0SJohn Baldwin struct apei_softc *sc = device_get_softc(dev);
140*9313f6b0SJohn Baldwin
141*9313f6b0SJohn Baldwin sc->dev = dev;
142*9313f6b0SJohn Baldwin TAILQ_INIT(&sc->mem);
143*9313f6b0SJohn Baldwin TAILQ_INIT(&sc->io);
144*9313f6b0SJohn Baldwin bus_identify_children(dev);
145*9313f6b0SJohn Baldwin bus_attach_children(dev);
146*9313f6b0SJohn Baldwin return (0);
147*9313f6b0SJohn Baldwin }
148*9313f6b0SJohn Baldwin
149*9313f6b0SJohn Baldwin static device_t
apei_add_child(device_t dev,u_int order,const char * name,int unit)150*9313f6b0SJohn Baldwin apei_add_child(device_t dev, u_int order, const char *name, int unit)
151*9313f6b0SJohn Baldwin {
152*9313f6b0SJohn Baldwin struct apei_ivars *ai;
153*9313f6b0SJohn Baldwin device_t child;
154*9313f6b0SJohn Baldwin
155*9313f6b0SJohn Baldwin ai = malloc(sizeof(*ai), M_APEI, M_WAITOK);
156*9313f6b0SJohn Baldwin TAILQ_INIT(&ai->mappings);
157*9313f6b0SJohn Baldwin child = device_add_child_ordered(dev, order, name, unit);
158*9313f6b0SJohn Baldwin if (child != NULL)
159*9313f6b0SJohn Baldwin device_set_ivars(child, ai);
160*9313f6b0SJohn Baldwin else
161*9313f6b0SJohn Baldwin free(ai, M_APEI);
162*9313f6b0SJohn Baldwin return (child);
163*9313f6b0SJohn Baldwin }
164*9313f6b0SJohn Baldwin
165*9313f6b0SJohn Baldwin static void
apei_child_deleted(device_t dev,device_t child)166*9313f6b0SJohn Baldwin apei_child_deleted(device_t dev, device_t child)
167*9313f6b0SJohn Baldwin {
168*9313f6b0SJohn Baldwin struct apei_ivars *ai = device_get_ivars(child);
169*9313f6b0SJohn Baldwin
170*9313f6b0SJohn Baldwin MPASS(TAILQ_EMPTY(&ai->mappings));
171*9313f6b0SJohn Baldwin free(ai, M_APEI);
172*9313f6b0SJohn Baldwin }
173*9313f6b0SJohn Baldwin
174*9313f6b0SJohn Baldwin static void
apei_child_detached(device_t dev,device_t child)175*9313f6b0SJohn Baldwin apei_child_detached(device_t dev, device_t child)
176*9313f6b0SJohn Baldwin {
177*9313f6b0SJohn Baldwin struct apei_ivars *ai = device_get_ivars(child);
178*9313f6b0SJohn Baldwin
179*9313f6b0SJohn Baldwin while (!TAILQ_EMPTY(&ai->mappings)) {
180*9313f6b0SJohn Baldwin struct apei_register_mapping *m = TAILQ_FIRST(&ai->mappings);
181*9313f6b0SJohn Baldwin
182*9313f6b0SJohn Baldwin device_printf(dev, "child leaked mapping of %s %#jx-%#jx\n",
183*9313f6b0SJohn Baldwin rman_get_type(m->reg->res) == SYS_RES_MEMORY ? "iomem" :
184*9313f6b0SJohn Baldwin "port", rman_get_start(m->reg->res),
185*9313f6b0SJohn Baldwin rman_get_end(m->reg->res));
186*9313f6b0SJohn Baldwin
187*9313f6b0SJohn Baldwin (void) apei_unmap_register(child, &m->map);
188*9313f6b0SJohn Baldwin }
189*9313f6b0SJohn Baldwin }
190*9313f6b0SJohn Baldwin
191*9313f6b0SJohn Baldwin static int
apei_read_ivar(device_t dev,device_t child,int index,uintptr_t * result)192*9313f6b0SJohn Baldwin apei_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
193*9313f6b0SJohn Baldwin {
194*9313f6b0SJohn Baldwin switch (index) {
195*9313f6b0SJohn Baldwin case ACPI_IVAR_HANDLE:
196*9313f6b0SJohn Baldwin *(ACPI_HANDLE *)result = acpi_get_handle(dev);
197*9313f6b0SJohn Baldwin break;
198*9313f6b0SJohn Baldwin default:
199*9313f6b0SJohn Baldwin return (ENOENT);
200*9313f6b0SJohn Baldwin }
201*9313f6b0SJohn Baldwin
202*9313f6b0SJohn Baldwin return (0);
203*9313f6b0SJohn Baldwin }
204*9313f6b0SJohn Baldwin
205*9313f6b0SJohn Baldwin static struct apei_register *
apei_alloc_register(struct apei_softc * sc,int type,rman_res_t start,rman_res_t count)206*9313f6b0SJohn Baldwin apei_alloc_register(struct apei_softc *sc, int type, rman_res_t start,
207*9313f6b0SJohn Baldwin rman_res_t count)
208*9313f6b0SJohn Baldwin {
209*9313f6b0SJohn Baldwin struct apei_register_list *list;
210*9313f6b0SJohn Baldwin struct apei_register *reg, *prev;
211*9313f6b0SJohn Baldwin rman_res_t end;
212*9313f6b0SJohn Baldwin const char *descr;
213*9313f6b0SJohn Baldwin int error, next_rid;
214*9313f6b0SJohn Baldwin
215*9313f6b0SJohn Baldwin switch (type) {
216*9313f6b0SJohn Baldwin case SYS_RES_MEMORY:
217*9313f6b0SJohn Baldwin list = &sc->mem;
218*9313f6b0SJohn Baldwin descr = "iomem";
219*9313f6b0SJohn Baldwin break;
220*9313f6b0SJohn Baldwin case SYS_RES_IOPORT:
221*9313f6b0SJohn Baldwin list = &sc->io;
222*9313f6b0SJohn Baldwin descr = "port";
223*9313f6b0SJohn Baldwin break;
224*9313f6b0SJohn Baldwin default:
225*9313f6b0SJohn Baldwin return (NULL);
226*9313f6b0SJohn Baldwin }
227*9313f6b0SJohn Baldwin
228*9313f6b0SJohn Baldwin end = start + (count - 1);
229*9313f6b0SJohn Baldwin
230*9313f6b0SJohn Baldwin /* First, look for an existing resource. */
231*9313f6b0SJohn Baldwin prev = NULL;
232*9313f6b0SJohn Baldwin TAILQ_FOREACH(reg, list, link) {
233*9313f6b0SJohn Baldwin /* Does the existing register overlap with this request? */
234*9313f6b0SJohn Baldwin if (start > rman_get_end(reg->res) ||
235*9313f6b0SJohn Baldwin end < rman_get_start(reg->res)) {
236*9313f6b0SJohn Baldwin
237*9313f6b0SJohn Baldwin /* prev will be the location of the first free rid */
238*9313f6b0SJohn Baldwin if (prev == NULL)
239*9313f6b0SJohn Baldwin next_rid = 0;
240*9313f6b0SJohn Baldwin else
241*9313f6b0SJohn Baldwin next_rid = rman_get_rid(prev->res) + 1;
242*9313f6b0SJohn Baldwin if (rman_get_rid(reg->res) == next_rid)
243*9313f6b0SJohn Baldwin prev = reg;
244*9313f6b0SJohn Baldwin continue;
245*9313f6b0SJohn Baldwin }
246*9313f6b0SJohn Baldwin
247*9313f6b0SJohn Baldwin /* Do we need to extend the existing register? */
248*9313f6b0SJohn Baldwin if (start < rman_get_start(reg->res) ||
249*9313f6b0SJohn Baldwin end > rman_get_end(reg->res)) {
250*9313f6b0SJohn Baldwin rman_res_t new_end, new_start;
251*9313f6b0SJohn Baldwin
252*9313f6b0SJohn Baldwin new_start = rman_get_start(reg->res);
253*9313f6b0SJohn Baldwin if (start < new_start)
254*9313f6b0SJohn Baldwin new_start = start;
255*9313f6b0SJohn Baldwin new_end = rman_get_end(reg->res);
256*9313f6b0SJohn Baldwin if (end > new_end)
257*9313f6b0SJohn Baldwin new_end = end;
258*9313f6b0SJohn Baldwin error = bus_adjust_resource(sc->dev, reg->res,
259*9313f6b0SJohn Baldwin new_start, new_end);
260*9313f6b0SJohn Baldwin if (error != 0) {
261*9313f6b0SJohn Baldwin if (bootverbose)
262*9313f6b0SJohn Baldwin device_printf(sc->dev,
263*9313f6b0SJohn Baldwin "failed to grow %s %#jx-%#jx for %#jx-%#jx: %d\n",
264*9313f6b0SJohn Baldwin descr, rman_get_start(reg->res),
265*9313f6b0SJohn Baldwin rman_get_end(reg->res), start, end,
266*9313f6b0SJohn Baldwin error);
267*9313f6b0SJohn Baldwin return (NULL);
268*9313f6b0SJohn Baldwin }
269*9313f6b0SJohn Baldwin }
270*9313f6b0SJohn Baldwin
271*9313f6b0SJohn Baldwin return (reg);
272*9313f6b0SJohn Baldwin }
273*9313f6b0SJohn Baldwin
274*9313f6b0SJohn Baldwin /* Allocate a new resource. */
275*9313f6b0SJohn Baldwin if (prev == NULL)
276*9313f6b0SJohn Baldwin next_rid = 0;
277*9313f6b0SJohn Baldwin else
278*9313f6b0SJohn Baldwin next_rid = rman_get_rid(prev->res) + 1;
279*9313f6b0SJohn Baldwin error = bus_set_resource(sc->dev, type, next_rid, start, count);
280*9313f6b0SJohn Baldwin if (error != 0) {
281*9313f6b0SJohn Baldwin if (bootverbose)
282*9313f6b0SJohn Baldwin device_printf(sc->dev,
283*9313f6b0SJohn Baldwin "failed to add %s %#jx-%#jx: %d\n", descr, start,
284*9313f6b0SJohn Baldwin end, error);
285*9313f6b0SJohn Baldwin return (NULL);
286*9313f6b0SJohn Baldwin }
287*9313f6b0SJohn Baldwin
288*9313f6b0SJohn Baldwin reg = malloc(sizeof(*reg), M_APEI, M_WAITOK);
289*9313f6b0SJohn Baldwin reg->res = bus_alloc_resource_any(sc->dev, type, next_rid, RF_ACTIVE |
290*9313f6b0SJohn Baldwin RF_UNMAPPED);
291*9313f6b0SJohn Baldwin if (reg->res == NULL) {
292*9313f6b0SJohn Baldwin if (bootverbose)
293*9313f6b0SJohn Baldwin device_printf(sc->dev,
294*9313f6b0SJohn Baldwin "failed to allocate %s %#jx-%#jx\n", descr, start,
295*9313f6b0SJohn Baldwin end);
296*9313f6b0SJohn Baldwin free(reg, M_APEI);
297*9313f6b0SJohn Baldwin bus_delete_resource(sc->dev, type, next_rid);
298*9313f6b0SJohn Baldwin return (NULL);
299*9313f6b0SJohn Baldwin }
300*9313f6b0SJohn Baldwin
301*9313f6b0SJohn Baldwin if (prev == NULL)
302*9313f6b0SJohn Baldwin TAILQ_INSERT_HEAD(list, reg, link);
303*9313f6b0SJohn Baldwin else
304*9313f6b0SJohn Baldwin TAILQ_INSERT_AFTER(list, prev, reg, link);
305*9313f6b0SJohn Baldwin return (reg);
306*9313f6b0SJohn Baldwin }
307*9313f6b0SJohn Baldwin
308*9313f6b0SJohn Baldwin static struct resource_map *
apei_map_register_method(device_t dev,device_t child,int type,rman_res_t start,rman_res_t count)309*9313f6b0SJohn Baldwin apei_map_register_method(device_t dev, device_t child, int type,
310*9313f6b0SJohn Baldwin rman_res_t start, rman_res_t count)
311*9313f6b0SJohn Baldwin {
312*9313f6b0SJohn Baldwin struct apei_softc *sc = device_get_softc(dev);
313*9313f6b0SJohn Baldwin struct apei_ivars *ai = device_get_ivars(child);
314*9313f6b0SJohn Baldwin struct apei_register_mapping *m;
315*9313f6b0SJohn Baldwin struct apei_register *reg;
316*9313f6b0SJohn Baldwin struct resource_map_request req;
317*9313f6b0SJohn Baldwin int error;
318*9313f6b0SJohn Baldwin
319*9313f6b0SJohn Baldwin reg = apei_alloc_register(sc, type, start, count);
320*9313f6b0SJohn Baldwin if (reg == NULL)
321*9313f6b0SJohn Baldwin return (NULL);
322*9313f6b0SJohn Baldwin
323*9313f6b0SJohn Baldwin m = malloc(sizeof(*m), M_APEI, M_WAITOK | M_ZERO);
324*9313f6b0SJohn Baldwin m->reg = reg;
325*9313f6b0SJohn Baldwin resource_init_map_request(&req);
326*9313f6b0SJohn Baldwin req.offset = start - rman_get_start(reg->res);
327*9313f6b0SJohn Baldwin req.length = count;
328*9313f6b0SJohn Baldwin error = bus_map_resource(dev, reg->res, &req, &m->map);
329*9313f6b0SJohn Baldwin if (error != 0) {
330*9313f6b0SJohn Baldwin if (bootverbose)
331*9313f6b0SJohn Baldwin device_printf(dev, "failed to map %s %#jx-%#jx: %d\n",
332*9313f6b0SJohn Baldwin type == SYS_RES_MEMORY ? "iomem" : "port",
333*9313f6b0SJohn Baldwin start,
334*9313f6b0SJohn Baldwin start + (count - 1), error);
335*9313f6b0SJohn Baldwin free(m, M_APEI);
336*9313f6b0SJohn Baldwin return (NULL);
337*9313f6b0SJohn Baldwin }
338*9313f6b0SJohn Baldwin TAILQ_INSERT_TAIL(&ai->mappings, m, link);
339*9313f6b0SJohn Baldwin return (&m->map);
340*9313f6b0SJohn Baldwin }
341*9313f6b0SJohn Baldwin
342*9313f6b0SJohn Baldwin static int
apei_unmap_register_method(device_t dev,device_t child,struct resource_map * map)343*9313f6b0SJohn Baldwin apei_unmap_register_method(device_t dev, device_t child,
344*9313f6b0SJohn Baldwin struct resource_map *map)
345*9313f6b0SJohn Baldwin {
346*9313f6b0SJohn Baldwin struct apei_ivars *ai = device_get_ivars(child);
347*9313f6b0SJohn Baldwin struct apei_register_mapping *m;
348*9313f6b0SJohn Baldwin int error;
349*9313f6b0SJohn Baldwin
350*9313f6b0SJohn Baldwin if (map == NULL)
351*9313f6b0SJohn Baldwin return (0);
352*9313f6b0SJohn Baldwin
353*9313f6b0SJohn Baldwin TAILQ_FOREACH(m, &ai->mappings, link) {
354*9313f6b0SJohn Baldwin if (&m->map == map)
355*9313f6b0SJohn Baldwin break;
356*9313f6b0SJohn Baldwin }
357*9313f6b0SJohn Baldwin
358*9313f6b0SJohn Baldwin if (m == NULL)
359*9313f6b0SJohn Baldwin return (EINVAL);
360*9313f6b0SJohn Baldwin
361*9313f6b0SJohn Baldwin TAILQ_REMOVE(&ai->mappings, m, link);
362*9313f6b0SJohn Baldwin error = bus_unmap_resource(dev, m->reg->res, &m->map);
363*9313f6b0SJohn Baldwin if (error != 0 && bootverbose)
364*9313f6b0SJohn Baldwin device_printf(dev, "failed to unmap %s %#jx-%#jx: %d\n",
365*9313f6b0SJohn Baldwin rman_get_type(m->reg->res) == SYS_RES_MEMORY ? "iomem" :
366*9313f6b0SJohn Baldwin "port", rman_get_start(m->reg->res),
367*9313f6b0SJohn Baldwin rman_get_end(m->reg->res), error);
368*9313f6b0SJohn Baldwin free(m, M_APEI);
369*9313f6b0SJohn Baldwin return (0);
370*9313f6b0SJohn Baldwin }
371*9313f6b0SJohn Baldwin
372*9313f6b0SJohn Baldwin struct resource_map *
apei_map_register(device_t dev,ACPI_GENERIC_ADDRESS * gas)373*9313f6b0SJohn Baldwin apei_map_register(device_t dev, ACPI_GENERIC_ADDRESS *gas)
374*9313f6b0SJohn Baldwin {
375*9313f6b0SJohn Baldwin int type;
376*9313f6b0SJohn Baldwin
377*9313f6b0SJohn Baldwin if (gas->Address == 0 || gas->BitWidth == 0 || gas->BitWidth % 8 != 0)
378*9313f6b0SJohn Baldwin return (NULL);
379*9313f6b0SJohn Baldwin
380*9313f6b0SJohn Baldwin switch (gas->SpaceId) {
381*9313f6b0SJohn Baldwin case ACPI_ADR_SPACE_SYSTEM_MEMORY:
382*9313f6b0SJohn Baldwin type = SYS_RES_MEMORY;
383*9313f6b0SJohn Baldwin break;
384*9313f6b0SJohn Baldwin case ACPI_ADR_SPACE_SYSTEM_IO:
385*9313f6b0SJohn Baldwin type = SYS_RES_IOPORT;
386*9313f6b0SJohn Baldwin break;
387*9313f6b0SJohn Baldwin default:
388*9313f6b0SJohn Baldwin return (NULL);
389*9313f6b0SJohn Baldwin }
390*9313f6b0SJohn Baldwin
391*9313f6b0SJohn Baldwin return (APEI_MAP_REGISTER(device_get_parent(dev), dev, type,
392*9313f6b0SJohn Baldwin gas->Address, gas->BitWidth / 8));
393*9313f6b0SJohn Baldwin }
394*9313f6b0SJohn Baldwin
395*9313f6b0SJohn Baldwin struct resource_map *
apei_map_memory(device_t dev,rman_res_t start,rman_res_t count)396*9313f6b0SJohn Baldwin apei_map_memory(device_t dev, rman_res_t start, rman_res_t count)
397*9313f6b0SJohn Baldwin {
398*9313f6b0SJohn Baldwin return (APEI_MAP_REGISTER(device_get_parent(dev), dev, SYS_RES_MEMORY,
399*9313f6b0SJohn Baldwin start, count));
400*9313f6b0SJohn Baldwin }
401*9313f6b0SJohn Baldwin
402*9313f6b0SJohn Baldwin int
apei_unmap_register(device_t dev,struct resource_map * map)403*9313f6b0SJohn Baldwin apei_unmap_register(device_t dev, struct resource_map *map)
404*9313f6b0SJohn Baldwin {
405*9313f6b0SJohn Baldwin return (APEI_UNMAP_REGISTER(device_get_parent(dev), dev, map));
406*9313f6b0SJohn Baldwin }
407*9313f6b0SJohn Baldwin
408*9313f6b0SJohn Baldwin static device_method_t apei_methods[] = {
409*9313f6b0SJohn Baldwin /* Device interface */
410*9313f6b0SJohn Baldwin DEVMETHOD(device_identify, apei_identify),
411*9313f6b0SJohn Baldwin DEVMETHOD(device_probe, apei_probe),
412*9313f6b0SJohn Baldwin DEVMETHOD(device_attach, apei_attach),
413*9313f6b0SJohn Baldwin
414*9313f6b0SJohn Baldwin /* Bus interface */
415*9313f6b0SJohn Baldwin DEVMETHOD(bus_add_child, apei_add_child),
416*9313f6b0SJohn Baldwin DEVMETHOD(bus_print_child, bus_generic_print_child),
417*9313f6b0SJohn Baldwin DEVMETHOD(bus_child_deleted, apei_child_deleted),
418*9313f6b0SJohn Baldwin DEVMETHOD(bus_child_detached, apei_child_detached),
419*9313f6b0SJohn Baldwin DEVMETHOD(bus_read_ivar, apei_read_ivar),
420*9313f6b0SJohn Baldwin
421*9313f6b0SJohn Baldwin /* APEI interface */
422*9313f6b0SJohn Baldwin DEVMETHOD(apei_map_register, apei_map_register_method),
423*9313f6b0SJohn Baldwin DEVMETHOD(apei_unmap_register, apei_unmap_register_method),
424*9313f6b0SJohn Baldwin
425*9313f6b0SJohn Baldwin DEVMETHOD_END
426*9313f6b0SJohn Baldwin };
427*9313f6b0SJohn Baldwin
428*9313f6b0SJohn Baldwin static driver_t apei_driver = {
429*9313f6b0SJohn Baldwin "apei",
430*9313f6b0SJohn Baldwin apei_methods,
431*9313f6b0SJohn Baldwin sizeof(struct apei_softc),
432*9313f6b0SJohn Baldwin };
433*9313f6b0SJohn Baldwin
434*9313f6b0SJohn Baldwin DRIVER_MODULE(apei, acpi, apei_driver, NULL, NULL);
435*9313f6b0SJohn Baldwin MODULE_DEPEND(apei, acpi, 1, 1, 1);
436