1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2024 Ahmad Khalifa <ahmadkhalifa570@gmail.com>
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 */
27
28 #include <sys/types.h>
29 #include <sys/bus.h>
30 #include <sys/kernel.h>
31 #include <sys/module.h>
32 #include <sys/gpio.h>
33
34 #include <contrib/dev/acpica/include/acpi.h>
35 #include <dev/acpica/acpivar.h>
36
37 #include <dev/gpio/gpiobusvar.h>
38 #include <dev/gpio/acpi_gpiobusvar.h>
39 #include <dev/gpio/gpiobus_internal.h>
40
41 #include "gpiobus_if.h"
42
43 struct acpi_gpiobus_softc {
44 struct gpiobus_softc super_sc;
45 ACPI_CONNECTION_INFO handler_info;
46 };
47
48 struct acpi_gpiobus_ctx {
49 struct gpiobus_softc *sc;
50 ACPI_HANDLE dev_handle;
51 };
52
53 struct acpi_gpiobus_ivar
54 {
55 struct gpiobus_ivar gpiobus; /* Must come first */
56 ACPI_HANDLE dev_handle; /* ACPI handle for bus */
57 uint32_t flags;
58 };
59
60 static uint32_t
acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO * gpio_res)61 acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res)
62 {
63 uint32_t flags = 0;
64
65 /* Figure out pin flags */
66 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_INT) {
67 switch (gpio_res->Polarity) {
68 case ACPI_ACTIVE_HIGH:
69 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ?
70 GPIO_INTR_LEVEL_HIGH : GPIO_INTR_EDGE_RISING;
71 break;
72 case ACPI_ACTIVE_LOW:
73 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ?
74 GPIO_INTR_LEVEL_LOW : GPIO_INTR_EDGE_FALLING;
75 break;
76 case ACPI_ACTIVE_BOTH:
77 flags = GPIO_INTR_EDGE_BOTH;
78 break;
79 }
80
81 flags |= GPIO_PIN_INPUT;
82 #ifdef NOT_YET
83 /* This is not currently implemented. */
84 if (gpio_res->Shareable == ACPI_SHARED)
85 flags |= GPIO_INTR_SHAREABLE;
86 #endif
87 }
88 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_IO) {
89 switch (gpio_res->IoRestriction) {
90 case ACPI_IO_RESTRICT_INPUT:
91 flags |= GPIO_PIN_INPUT;
92 break;
93 case ACPI_IO_RESTRICT_OUTPUT:
94 flags |= GPIO_PIN_OUTPUT;
95 break;
96 }
97 }
98
99 switch (gpio_res->PinConfig) {
100 case ACPI_PIN_CONFIG_PULLUP:
101 flags |= GPIO_PIN_PULLUP;
102 break;
103 case ACPI_PIN_CONFIG_PULLDOWN:
104 flags |= GPIO_PIN_PULLDOWN;
105 break;
106 }
107
108 return (flags);
109 }
110
111 static ACPI_STATUS
acpi_gpiobus_enumerate_res(ACPI_RESOURCE * res,void * context)112 acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context)
113 {
114 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
115 struct acpi_gpiobus_ctx *ctx = context;
116 struct gpiobus_softc *super_sc = ctx->sc;
117 ACPI_HANDLE handle;
118 uint32_t flags, i;
119
120 if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
121 return (AE_OK);
122
123 if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT,
124 gpio_res->ResourceSource.StringPtr, &handle)) ||
125 handle != ctx->dev_handle)
126 return (AE_OK);
127
128 if (__predict_false(gpio_res->PinTableLength > super_sc->sc_npins)) {
129 device_printf(super_sc->sc_busdev,
130 "invalid pin table length %hu, max: %d (bad ACPI tables?)\n",
131 gpio_res->PinTableLength, super_sc->sc_npins);
132 return (AE_LIMIT);
133 }
134
135 flags = acpi_gpiobus_convflags(gpio_res);
136 for (i = 0; i < gpio_res->PinTableLength; i++) {
137 UINT16 pin = gpio_res->PinTable[i];
138
139 if (__predict_false(pin >= super_sc->sc_npins)) {
140 device_printf(super_sc->sc_busdev,
141 "invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n",
142 pin, super_sc->sc_npins - 1);
143 return (AE_LIMIT);
144 }
145
146 GPIO_PIN_SETFLAGS(super_sc->sc_dev, pin, flags &
147 ~GPIO_INTR_MASK);
148 }
149
150 return (AE_OK);
151 }
152
153 static struct acpi_gpiobus_ivar *
acpi_gpiobus_setup_devinfo(device_t bus,device_t child,ACPI_RESOURCE_GPIO * gpio_res)154 acpi_gpiobus_setup_devinfo(device_t bus, device_t child,
155 ACPI_RESOURCE_GPIO *gpio_res)
156 {
157 struct acpi_gpiobus_ivar *devi;
158
159 devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO);
160 if (devi == NULL)
161 return (NULL);
162 resource_list_init(&devi->gpiobus.rl);
163
164 devi->flags = acpi_gpiobus_convflags(gpio_res);
165 if (acpi_quirks & ACPI_Q_AEI_NOPULL)
166 devi->flags &= ~GPIO_PIN_PULLUP;
167
168 devi->gpiobus.npins = 1;
169 if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) {
170 free(devi, M_DEVBUF);
171 return (NULL);
172 }
173
174 for (int i = 0; i < devi->gpiobus.npins; i++)
175 devi->gpiobus.pins[i] = gpio_res->PinTable[i];
176
177 return (devi);
178 }
179
180 static ACPI_STATUS
acpi_gpiobus_enumerate_aei(ACPI_RESOURCE * res,void * context)181 acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context)
182 {
183 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
184 struct acpi_gpiobus_ctx *ctx = context;
185 device_t bus = ctx->sc->sc_busdev;
186 device_t child;
187 struct acpi_gpiobus_ivar *devi;
188
189 /* Check that we have a GpioInt object. */
190 if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
191 return (AE_OK);
192 if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT)
193 return (AE_OK);
194
195 /* Add a child. */
196 child = device_add_child_ordered(bus, 0, "gpio_aei", DEVICE_UNIT_ANY);
197 if (child == NULL)
198 return (AE_OK);
199 devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res);
200 if (devi == NULL) {
201 device_delete_child(bus, child);
202 return (AE_OK);
203 }
204 device_set_ivars(child, devi);
205
206 for (int i = 0; i < devi->gpiobus.npins; i++) {
207 if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags &
208 ~GPIO_INTR_MASK)) {
209 device_delete_child(bus, child);
210 return (AE_OK);
211 }
212 }
213
214 /* Pass ACPI information to children. */
215 devi->dev_handle = ctx->dev_handle;
216
217 return (AE_OK);
218 }
219
220 static ACPI_STATUS
acpi_gpiobus_enumerate(ACPI_HANDLE handle,UINT32 depth,void * context,void ** result)221 acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context,
222 void **result)
223 {
224 UINT32 sta;
225
226 /*
227 * If no _STA method or if it failed, then assume that
228 * the device is present.
229 */
230 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
231 !ACPI_DEVICE_PRESENT(sta))
232 return (AE_OK);
233
234 if (!acpi_has_hid(handle))
235 return (AE_OK);
236
237 /* Look for GPIO resources */
238 AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context);
239
240 return (AE_OK);
241 }
242
243 static ACPI_STATUS
acpi_gpiobus_space_handler(UINT32 function,ACPI_PHYSICAL_ADDRESS address,UINT32 length,UINT64 * value,void * context,void * region_context)244 acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
245 UINT32 length, UINT64 *value, void *context, void *region_context)
246 {
247 ACPI_CONNECTION_INFO *info = context;
248 ACPI_RESOURCE_GPIO *gpio_res;
249 device_t controller;
250 ACPI_RESOURCE *res;
251 ACPI_STATUS status;
252
253 status = AcpiBufferToResource(info->Connection, info->Length, &res);
254 if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO)
255 goto err;
256
257 gpio_res = &res->Data.Gpio;
258 controller = __containerof(info, struct acpi_gpiobus_softc,
259 handler_info)->super_sc.sc_dev;
260
261 switch (function) {
262 case ACPI_WRITE:
263 if (__predict_false(
264 gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT))
265 goto err;
266
267 for (int i = 0; i < length; i++)
268 if (GPIO_PIN_SET(controller,
269 gpio_res->PinTable[address + i], (*value & 1 << i) ?
270 GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0)
271 goto err;
272 break;
273 case ACPI_READ:
274 if (__predict_false(
275 gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT))
276 goto err;
277
278 for (int i = 0; i < length; i++) {
279 uint32_t v;
280
281 if (GPIO_PIN_GET(controller,
282 gpio_res->PinTable[address + i], &v) != 0)
283 goto err;
284 *value |= v << i;
285 }
286 break;
287 default:
288 goto err;
289 }
290
291 ACPI_FREE(res);
292 return (AE_OK);
293
294 err:
295 ACPI_FREE(res);
296 return (AE_BAD_PARAMETER);
297 }
298
299 static int
acpi_gpiobus_probe(device_t dev)300 acpi_gpiobus_probe(device_t dev)
301 {
302 device_t controller;
303
304 if (acpi_disabled("gpiobus"))
305 return (ENXIO);
306
307 controller = device_get_parent(dev);
308 if (controller == NULL)
309 return (ENXIO);
310
311 if (acpi_get_handle(controller) == NULL)
312 return (ENXIO);
313
314 device_set_desc(dev, "GPIO bus (ACPI-hinted)");
315 return (BUS_PROBE_DEFAULT);
316 }
317
318 static int
acpi_gpiobus_attach(device_t dev)319 acpi_gpiobus_attach(device_t dev)
320 {
321 struct acpi_gpiobus_softc *sc;
322 struct acpi_gpiobus_ctx ctx;
323 ACPI_HANDLE handle;
324 ACPI_STATUS status;
325 int err;
326
327 if ((err = gpiobus_attach(dev)) != 0)
328 return (err);
329
330 sc = device_get_softc(dev);
331 handle = acpi_get_handle(sc->super_sc.sc_dev);
332 if (handle == NULL) {
333 gpiobus_detach(dev);
334 return (ENXIO);
335 }
336
337 status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
338 acpi_gpiobus_space_handler, NULL, &sc->handler_info);
339
340 if (ACPI_FAILURE(status)) {
341 device_printf(dev,
342 "Failed to install GPIO address space handler\n");
343 gpiobus_detach(dev);
344 return (ENXIO);
345 }
346
347 ctx.dev_handle = handle;
348 ctx.sc = &sc->super_sc;
349
350 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
351 ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL);
352
353 if (ACPI_FAILURE(status))
354 device_printf(dev, "Failed to enumerate GPIO resources\n");
355
356 /* Look for AEI children */
357 status = AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei,
358 &ctx);
359
360 if (ACPI_FAILURE(status))
361 device_printf(dev, "Failed to enumerate AEI resources\n");
362
363 return (0);
364 }
365
366 static int
acpi_gpiobus_detach(device_t dev)367 acpi_gpiobus_detach(device_t dev)
368 {
369 struct gpiobus_softc *super_sc;
370 ACPI_STATUS status;
371
372 super_sc = device_get_softc(dev);
373 status = AcpiRemoveAddressSpaceHandler(
374 acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO,
375 acpi_gpiobus_space_handler
376 );
377
378 if (ACPI_FAILURE(status))
379 device_printf(dev,
380 "Failed to remove GPIO address space handler\n");
381
382 return (gpiobus_detach(dev));
383 }
384
385 static int
acpi_gpiobus_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)386 acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
387 {
388 struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
389
390 switch (which) {
391 case ACPI_GPIOBUS_IVAR_HANDLE:
392 *result = (uintptr_t)devi->dev_handle;
393 break;
394 case ACPI_GPIOBUS_IVAR_FLAGS:
395 *result = (uintptr_t)devi->flags;
396 break;
397 default:
398 return (gpiobus_read_ivar(dev, child, which, result));
399 }
400
401 return (0);
402 }
403
404 static device_method_t acpi_gpiobus_methods[] = {
405 /* Device interface */
406 DEVMETHOD(device_probe, acpi_gpiobus_probe),
407 DEVMETHOD(device_attach, acpi_gpiobus_attach),
408 DEVMETHOD(device_detach, acpi_gpiobus_detach),
409
410 /* Bus interface */
411 DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar),
412
413 DEVMETHOD_END
414 };
415
416 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods,
417 sizeof(struct acpi_gpiobus_softc), gpiobus_driver);
418 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL,
419 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
420 MODULE_VERSION(acpi_gpiobus, 1);
421 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1);
422