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
40 #include "gpiobus_if.h"
41
42 struct acpi_gpiobus_softc {
43 struct gpiobus_softc super_sc;
44 ACPI_CONNECTION_INFO handler_info;
45 };
46
47 struct acpi_gpiobus_ctx {
48 struct gpiobus_softc *sc;
49 ACPI_HANDLE dev_handle;
50 };
51
52 struct acpi_gpiobus_ivar
53 {
54 struct gpiobus_ivar gpiobus; /* Must come first */
55 ACPI_HANDLE dev_handle; /* ACPI handle for bus */
56 uint32_t flags;
57 };
58
59 static uint32_t
acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO * gpio_res)60 acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res)
61 {
62 uint32_t flags = 0;
63
64 /* Figure out pin flags */
65 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_INT) {
66 switch (gpio_res->Polarity) {
67 case ACPI_ACTIVE_HIGH:
68 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ?
69 GPIO_INTR_LEVEL_HIGH : GPIO_INTR_EDGE_RISING;
70 break;
71 case ACPI_ACTIVE_LOW:
72 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ?
73 GPIO_INTR_LEVEL_LOW : GPIO_INTR_EDGE_FALLING;
74 break;
75 case ACPI_ACTIVE_BOTH:
76 flags = GPIO_INTR_EDGE_BOTH;
77 break;
78 }
79
80 flags |= GPIO_PIN_INPUT;
81 #ifdef NOT_YET
82 /* This is not currently implemented. */
83 if (gpio_res->Shareable == ACPI_SHARED)
84 flags |= GPIO_INTR_SHAREABLE;
85 #endif
86 }
87 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_IO) {
88 switch (gpio_res->IoRestriction) {
89 case ACPI_IO_RESTRICT_INPUT:
90 flags |= GPIO_PIN_INPUT;
91 break;
92 case ACPI_IO_RESTRICT_OUTPUT:
93 flags |= GPIO_PIN_OUTPUT;
94 break;
95 }
96 }
97
98 switch (gpio_res->PinConfig) {
99 case ACPI_PIN_CONFIG_PULLUP:
100 flags |= GPIO_PIN_PULLUP;
101 break;
102 case ACPI_PIN_CONFIG_PULLDOWN:
103 flags |= GPIO_PIN_PULLDOWN;
104 break;
105 }
106
107 return (flags);
108 }
109
110 static ACPI_STATUS
acpi_gpiobus_enumerate_res(ACPI_RESOURCE * res,void * context)111 acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context)
112 {
113 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
114 struct acpi_gpiobus_ctx *ctx = context;
115 struct gpiobus_softc *super_sc = ctx->sc;
116 ACPI_HANDLE handle;
117 uint32_t flags, i;
118
119 if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
120 return (AE_OK);
121
122 if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT,
123 gpio_res->ResourceSource.StringPtr, &handle)) ||
124 handle != ctx->dev_handle)
125 return (AE_OK);
126
127 if (__predict_false(gpio_res->PinTableLength > super_sc->sc_npins)) {
128 device_printf(super_sc->sc_busdev,
129 "invalid pin table length %hu, max: %d (bad ACPI tables?)\n",
130 gpio_res->PinTableLength, super_sc->sc_npins);
131 return (AE_LIMIT);
132 }
133
134 flags = acpi_gpiobus_convflags(gpio_res);
135 for (i = 0; i < gpio_res->PinTableLength; i++) {
136 UINT16 pin = gpio_res->PinTable[i];
137
138 if (__predict_false(pin >= super_sc->sc_npins)) {
139 device_printf(super_sc->sc_busdev,
140 "invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n",
141 pin, super_sc->sc_npins - 1);
142 return (AE_LIMIT);
143 }
144
145 GPIO_PIN_SETFLAGS(super_sc->sc_dev, pin, flags &
146 ~GPIO_INTR_MASK);
147 }
148
149 return (AE_OK);
150 }
151
152 static struct acpi_gpiobus_ivar *
acpi_gpiobus_setup_devinfo(device_t bus,device_t child,ACPI_RESOURCE_GPIO * gpio_res)153 acpi_gpiobus_setup_devinfo(device_t bus, device_t child,
154 ACPI_RESOURCE_GPIO *gpio_res)
155 {
156 struct acpi_gpiobus_ivar *devi;
157
158 devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO);
159 if (devi == NULL)
160 return (NULL);
161 resource_list_init(&devi->gpiobus.rl);
162
163 devi->flags = acpi_gpiobus_convflags(gpio_res);
164 if (acpi_quirks & ACPI_Q_AEI_NOPULL)
165 devi->flags &= ~GPIO_PIN_PULLUP;
166
167 devi->gpiobus.npins = 1;
168 if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) {
169 free(devi, M_DEVBUF);
170 return (NULL);
171 }
172
173 for (int i = 0; i < devi->gpiobus.npins; i++)
174 devi->gpiobus.pins[i] = gpio_res->PinTable[i];
175
176 return (devi);
177 }
178
179 static ACPI_STATUS
acpi_gpiobus_enumerate_aei(ACPI_RESOURCE * res,void * context)180 acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context)
181 {
182 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
183 struct acpi_gpiobus_ctx *ctx = context;
184 device_t bus = ctx->sc->sc_busdev;
185 device_t child;
186 struct acpi_gpiobus_ivar *devi;
187
188 /* Check that we have a GpioInt object. */
189 if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
190 return (AE_OK);
191 if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT)
192 return (AE_OK);
193
194 /* Add a child. */
195 child = device_add_child_ordered(bus, 0, "gpio_aei", DEVICE_UNIT_ANY);
196 if (child == NULL)
197 return (AE_OK);
198 devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res);
199 if (devi == NULL) {
200 device_delete_child(bus, child);
201 return (AE_OK);
202 }
203 device_set_ivars(child, devi);
204
205 for (int i = 0; i < devi->gpiobus.npins; i++) {
206 if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags &
207 ~GPIO_INTR_MASK)) {
208 device_delete_child(bus, child);
209 return (AE_OK);
210 }
211 }
212
213 /* Pass ACPI information to children. */
214 devi->dev_handle = ctx->dev_handle;
215
216 return (AE_OK);
217 }
218
219 static ACPI_STATUS
acpi_gpiobus_enumerate(ACPI_HANDLE handle,UINT32 depth,void * context,void ** result)220 acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context,
221 void **result)
222 {
223 UINT32 sta;
224
225 /*
226 * If no _STA method or if it failed, then assume that
227 * the device is present.
228 */
229 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
230 !ACPI_DEVICE_PRESENT(sta))
231 return (AE_OK);
232
233 if (!acpi_has_hid(handle))
234 return (AE_OK);
235
236 /* Look for GPIO resources */
237 AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context);
238
239 return (AE_OK);
240 }
241
242 static ACPI_STATUS
acpi_gpiobus_space_handler(UINT32 function,ACPI_PHYSICAL_ADDRESS address,UINT32 length,UINT64 * value,void * context,void * region_context)243 acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
244 UINT32 length, UINT64 *value, void *context, void *region_context)
245 {
246 ACPI_CONNECTION_INFO *info = context;
247 ACPI_RESOURCE_GPIO *gpio_res;
248 device_t controller;
249 ACPI_RESOURCE *res;
250 ACPI_STATUS status;
251
252 status = AcpiBufferToResource(info->Connection, info->Length, &res);
253 if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO)
254 goto err;
255
256 gpio_res = &res->Data.Gpio;
257 controller = __containerof(info, struct acpi_gpiobus_softc,
258 handler_info)->super_sc.sc_dev;
259
260 switch (function) {
261 case ACPI_WRITE:
262 if (__predict_false(
263 gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT))
264 goto err;
265
266 for (int i = 0; i < length; i++)
267 if (GPIO_PIN_SET(controller,
268 gpio_res->PinTable[address + i], (*value & 1 << i) ?
269 GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0)
270 goto err;
271 break;
272 case ACPI_READ:
273 if (__predict_false(
274 gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT))
275 goto err;
276
277 for (int i = 0; i < length; i++) {
278 uint32_t v;
279
280 if (GPIO_PIN_GET(controller,
281 gpio_res->PinTable[address + i], &v) != 0)
282 goto err;
283 *value |= v << i;
284 }
285 break;
286 default:
287 goto err;
288 }
289
290 ACPI_FREE(res);
291 return (AE_OK);
292
293 err:
294 ACPI_FREE(res);
295 return (AE_BAD_PARAMETER);
296 }
297
298 static int
acpi_gpiobus_probe(device_t dev)299 acpi_gpiobus_probe(device_t dev)
300 {
301 device_t controller;
302
303 if (acpi_disabled("gpiobus"))
304 return (ENXIO);
305
306 controller = device_get_parent(dev);
307 if (controller == NULL)
308 return (ENXIO);
309
310 if (acpi_get_handle(controller) == NULL)
311 return (ENXIO);
312
313 device_set_desc(dev, "GPIO bus (ACPI-hinted)");
314 return (BUS_PROBE_DEFAULT);
315 }
316
317 static int
acpi_gpiobus_attach(device_t dev)318 acpi_gpiobus_attach(device_t dev)
319 {
320 struct acpi_gpiobus_softc *sc;
321 struct acpi_gpiobus_ctx ctx;
322 ACPI_HANDLE handle;
323 ACPI_STATUS status;
324 int err;
325
326 if ((err = gpiobus_attach(dev)) != 0)
327 return (err);
328
329 sc = device_get_softc(dev);
330 handle = acpi_get_handle(sc->super_sc.sc_dev);
331 if (handle == NULL) {
332 gpiobus_detach(dev);
333 return (ENXIO);
334 }
335
336 status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
337 acpi_gpiobus_space_handler, NULL, &sc->handler_info);
338
339 if (ACPI_FAILURE(status)) {
340 device_printf(dev,
341 "Failed to install GPIO address space handler\n");
342 gpiobus_detach(dev);
343 return (ENXIO);
344 }
345
346 ctx.dev_handle = handle;
347 ctx.sc = &sc->super_sc;
348
349 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
350 ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL);
351
352 if (ACPI_FAILURE(status))
353 device_printf(dev, "Failed to enumerate GPIO resources\n");
354
355 /* Look for AEI children */
356 status = AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei,
357 &ctx);
358
359 if (ACPI_FAILURE(status))
360 device_printf(dev, "Failed to enumerate AEI resources\n");
361
362 return (0);
363 }
364
365 static int
acpi_gpiobus_detach(device_t dev)366 acpi_gpiobus_detach(device_t dev)
367 {
368 struct gpiobus_softc *super_sc;
369 ACPI_STATUS status;
370
371 super_sc = device_get_softc(dev);
372 status = AcpiRemoveAddressSpaceHandler(
373 acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO,
374 acpi_gpiobus_space_handler
375 );
376
377 if (ACPI_FAILURE(status))
378 device_printf(dev,
379 "Failed to remove GPIO address space handler\n");
380
381 return (gpiobus_detach(dev));
382 }
383
384 static int
acpi_gpiobus_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)385 acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
386 {
387 struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
388
389 switch (which) {
390 case ACPI_GPIOBUS_IVAR_HANDLE:
391 *result = (uintptr_t)devi->dev_handle;
392 break;
393 case ACPI_GPIOBUS_IVAR_FLAGS:
394 *result = (uintptr_t)devi->flags;
395 break;
396 default:
397 return (gpiobus_read_ivar(dev, child, which, result));
398 }
399
400 return (0);
401 }
402
403 static device_method_t acpi_gpiobus_methods[] = {
404 /* Device interface */
405 DEVMETHOD(device_probe, acpi_gpiobus_probe),
406 DEVMETHOD(device_attach, acpi_gpiobus_attach),
407 DEVMETHOD(device_detach, acpi_gpiobus_detach),
408
409 /* Bus interface */
410 DEVMETHOD(bus_read_ivar, acpi_gpiobus_read_ivar),
411
412 DEVMETHOD_END
413 };
414
415 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods,
416 sizeof(struct acpi_gpiobus_softc), gpiobus_driver);
417 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL,
418 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
419 MODULE_VERSION(acpi_gpiobus, 1);
420 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1);
421