xref: /freebsd/sys/dev/gpio/acpi_gpiobus.c (revision 3bbdc0223a7c1efaf734d3d9f3cb818e79e11d54)
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 #include <sys/sbuf.h>
41 
42 #include "gpiobus_if.h"
43 
44 struct acpi_gpiobus_softc {
45 	struct gpiobus_softc	super_sc;
46 	ACPI_CONNECTION_INFO	handler_info;
47 };
48 
49 struct acpi_gpiobus_ctx {
50 	struct gpiobus_softc	*sc;
51 	ACPI_HANDLE		dev_handle;
52 };
53 
54 struct acpi_gpiobus_ivar
55 {
56 	struct gpiobus_ivar	gpiobus;
57 	ACPI_HANDLE		handle;
58 };
59 
60 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 ACPI_STATUS
acpi_gpiobus_enumerate_aei(ACPI_RESOURCE * res,void * context)154 acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context)
155 {
156 	ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
157 	uint32_t *npins = context, *pins = npins + 1;
158 
159 	/*
160 	 * Check that we have a GpioInt object.
161 	 * Note that according to the spec this
162 	 * should always be the case.
163 	 */
164 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
165 		return (AE_OK);
166 	if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT)
167 		return (AE_OK);
168 
169 	for (int i = 0; i < gpio_res->PinTableLength; i++)
170 		pins[(*npins)++] = gpio_res->PinTable[i];
171 	return (AE_OK);
172 }
173 
174 static ACPI_STATUS
acpi_gpiobus_enumerate(ACPI_HANDLE handle,UINT32 depth,void * context,void ** result)175 acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context,
176     void **result)
177 {
178 	UINT32 sta;
179 
180 	/*
181 	 * If no _STA method or if it failed, then assume that
182 	 * the device is present.
183 	 */
184 	if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
185 	    !ACPI_DEVICE_PRESENT(sta))
186 		return (AE_OK);
187 
188 	if (!acpi_has_hid(handle))
189 		return (AE_OK);
190 
191 	/* Look for GPIO resources */
192 	AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context);
193 
194 	return (AE_OK);
195 }
196 
197 static ACPI_STATUS
acpi_gpiobus_space_handler(UINT32 function,ACPI_PHYSICAL_ADDRESS address,UINT32 length,UINT64 * value,void * context,void * region_context)198 acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
199     UINT32 length, UINT64 *value, void *context, void *region_context)
200 {
201 	ACPI_CONNECTION_INFO *info = context;
202 	ACPI_RESOURCE_GPIO *gpio_res;
203 	device_t controller;
204 	ACPI_RESOURCE *res;
205 	ACPI_STATUS status;
206 
207 	status = AcpiBufferToResource(info->Connection, info->Length, &res);
208 	if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO)
209 		goto err;
210 
211 	gpio_res = &res->Data.Gpio;
212 	controller = __containerof(info, struct acpi_gpiobus_softc,
213 	    handler_info)->super_sc.sc_dev;
214 
215 	switch (function) {
216 	case ACPI_WRITE:
217 		if (__predict_false(
218 		    gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT))
219 			goto err;
220 
221 		for (int i = 0; i < length; i++)
222 			if (GPIO_PIN_SET(controller,
223 			    gpio_res->PinTable[address + i], (*value & 1 << i) ?
224 			    GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0)
225 				goto err;
226 		break;
227 	case ACPI_READ:
228 		if (__predict_false(
229 		    gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT))
230 			goto err;
231 
232 		for (int i = 0; i < length; i++) {
233 			uint32_t v;
234 
235 			if (GPIO_PIN_GET(controller,
236 			    gpio_res->PinTable[address + i], &v) != 0)
237 				goto err;
238 			*value |= v << i;
239 		}
240 		break;
241 	default:
242 		goto err;
243 	}
244 
245 	ACPI_FREE(res);
246 	return (AE_OK);
247 
248 err:
249 	ACPI_FREE(res);
250 	return (AE_BAD_PARAMETER);
251 }
252 
253 static void
acpi_gpiobus_attach_aei(struct acpi_gpiobus_softc * sc,ACPI_HANDLE handle)254 acpi_gpiobus_attach_aei(struct acpi_gpiobus_softc *sc, ACPI_HANDLE handle)
255 {
256 	struct acpi_gpiobus_ivar *devi;
257 	ACPI_HANDLE aei_handle;
258 	device_t child;
259 	uint32_t *pins;
260 	ACPI_STATUS status;
261 	int err;
262 
263 	status = AcpiGetHandle(handle, "_AEI", &aei_handle);
264 	if (ACPI_FAILURE(status))
265 		return;
266 
267 	/* pins[0] specifies the length of the array. */
268 	pins = mallocarray(sc->super_sc.sc_npins + 1,
269 	    sizeof(uint32_t), M_DEVBUF, M_WAITOK);
270 	pins[0] = 0;
271 
272 	status = AcpiWalkResources(handle, "_AEI",
273 	    acpi_gpiobus_enumerate_aei, pins);
274 	if (ACPI_FAILURE(status)) {
275 		device_printf(sc->super_sc.sc_busdev,
276 		    "Failed to enumerate AEI resources\n");
277 		free(pins, M_DEVBUF);
278 		return;
279 	}
280 
281 	child = BUS_ADD_CHILD(sc->super_sc.sc_busdev, 0, "gpio_aei",
282 	    DEVICE_UNIT_ANY);
283 	if (child == NULL) {
284 		device_printf(sc->super_sc.sc_busdev,
285 		    "Failed to add gpio_aei child\n");
286 		free(pins, M_DEVBUF);
287 		return;
288 	}
289 
290 	devi = device_get_ivars(child);
291 	devi->gpiobus.npins = pins[0];
292 	devi->handle = aei_handle;
293 
294 	err = gpiobus_alloc_ivars(&devi->gpiobus);
295 	if (err != 0) {
296 		device_printf(sc->super_sc.sc_busdev,
297 		    "Failed to allocate gpio_aei ivars\n");
298 		device_delete_child(sc->super_sc.sc_busdev, child);
299 		free(pins, M_DEVBUF);
300 		return;
301 	}
302 
303 	for (int i = 0; i < pins[0]; i++)
304 		devi->gpiobus.pins[i] = pins[i + 1];
305 	free(pins, M_DEVBUF);
306 
307 	status = AcpiAttachData(aei_handle, acpi_fake_objhandler, child);
308 	if (ACPI_FAILURE(status)) {
309 		printf("WARNING: Unable to attach object data to %s - %s\n",
310 		    acpi_name(aei_handle), AcpiFormatException(status));
311 	}
312 
313 	bus_attach_children(sc->super_sc.sc_busdev);
314 }
315 
316 static int
acpi_gpiobus_probe(device_t dev)317 acpi_gpiobus_probe(device_t dev)
318 {
319 	device_t controller;
320 
321 	if (acpi_disabled("gpiobus"))
322 		return (ENXIO);
323 
324 	controller = device_get_parent(dev);
325 	if (controller == NULL)
326 		return (ENXIO);
327 
328 	if (acpi_get_handle(controller) == NULL)
329 		return (ENXIO);
330 
331 	device_set_desc(dev, "GPIO bus (ACPI-hinted)");
332 	return (BUS_PROBE_DEFAULT);
333 }
334 
335 static int
acpi_gpiobus_attach(device_t dev)336 acpi_gpiobus_attach(device_t dev)
337 {
338 	struct acpi_gpiobus_softc *sc;
339 	struct acpi_gpiobus_ctx ctx;
340 	ACPI_HANDLE handle;
341 	ACPI_STATUS status;
342 	int err;
343 
344 	if ((err = gpiobus_attach(dev)) != 0)
345 		return (err);
346 
347 	sc = device_get_softc(dev);
348 	handle = acpi_get_handle(sc->super_sc.sc_dev);
349 	if (handle == NULL) {
350 		gpiobus_detach(dev);
351 		return (ENXIO);
352 	}
353 
354 	status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
355 	    acpi_gpiobus_space_handler, NULL, &sc->handler_info);
356 
357 	if (ACPI_FAILURE(status)) {
358 		device_printf(dev,
359 		    "Failed to install GPIO address space handler\n");
360 		gpiobus_detach(dev);
361 		return (ENXIO);
362 	}
363 
364 	ctx.dev_handle = handle;
365 	ctx.sc = &sc->super_sc;
366 
367 	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
368 	    ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL);
369 
370 	if (ACPI_FAILURE(status))
371 		device_printf(dev, "Failed to enumerate GPIO resources\n");
372 
373 	/* Look for AEI child */
374 	acpi_gpiobus_attach_aei(sc, handle);
375 	return (0);
376 }
377 
378 static int
acpi_gpiobus_detach(device_t dev)379 acpi_gpiobus_detach(device_t dev)
380 {
381 	struct gpiobus_softc *super_sc;
382 	ACPI_STATUS status;
383 
384 	super_sc = device_get_softc(dev);
385 	status = AcpiRemoveAddressSpaceHandler(
386 	    acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO,
387 	    acpi_gpiobus_space_handler
388 	);
389 
390 	if (ACPI_FAILURE(status))
391 		device_printf(dev,
392 		    "Failed to remove GPIO address space handler\n");
393 
394 	return (gpiobus_detach(dev));
395 }
396 
397 static int
acpi_gpiobus_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)398 acpi_gpiobus_read_ivar(device_t dev, device_t child, int which,
399     uintptr_t *result)
400 {
401 	struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
402 
403 	switch (which) {
404 	case ACPI_GPIOBUS_IVAR_HANDLE:
405 		*result = (uintptr_t)devi->handle;
406 		break;
407 	default:
408 		return (gpiobus_read_ivar(dev, child, which, result));
409 	}
410 
411 	return (0);
412 }
413 
414 static device_t
acpi_gpiobus_add_child(device_t dev,u_int order,const char * name,int unit)415 acpi_gpiobus_add_child(device_t dev, u_int order, const char *name, int unit)
416 {
417 	return (gpiobus_add_child_common(dev, order, name, unit,
418 	    sizeof(struct acpi_gpiobus_ivar)));
419 }
420 
421 static int
acpi_gpiobus_child_location(device_t bus,device_t child,struct sbuf * sb)422 acpi_gpiobus_child_location(device_t bus, device_t child, struct sbuf *sb)
423 {
424 	struct acpi_gpiobus_ivar *devi;
425 	int err;
426 
427 	err = gpiobus_child_location(bus, child, sb);
428 	if (err != 0)
429 		return (err);
430 
431 	devi = device_get_ivars(child);
432 	sbuf_printf(sb, " handle=%s", acpi_name(devi->handle));
433 	return (0);
434 }
435 
436 static void
acpi_gpiobus_child_deleted(device_t bus,device_t child)437 acpi_gpiobus_child_deleted(device_t bus, device_t child)
438 {
439 	struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
440 
441 	if (acpi_get_device(devi->handle) == child)
442 		AcpiDetachData(devi->handle, acpi_fake_objhandler);
443 	gpiobus_child_deleted(bus, child);
444 }
445 
446 static device_method_t acpi_gpiobus_methods[] = {
447 	/* Device interface */
448 	DEVMETHOD(device_probe,		acpi_gpiobus_probe),
449 	DEVMETHOD(device_attach,	acpi_gpiobus_attach),
450 	DEVMETHOD(device_detach,	acpi_gpiobus_detach),
451 
452 	/* Bus interface */
453 	DEVMETHOD(bus_read_ivar,	acpi_gpiobus_read_ivar),
454 	DEVMETHOD(bus_add_child,	acpi_gpiobus_add_child),
455 	DEVMETHOD(bus_child_location,	acpi_gpiobus_child_location),
456 	DEVMETHOD(bus_child_deleted,	acpi_gpiobus_child_deleted),
457 
458 	DEVMETHOD_END
459 };
460 
461 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods,
462     sizeof(struct acpi_gpiobus_softc), gpiobus_driver);
463 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL,
464     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
465 MODULE_VERSION(acpi_gpiobus, 1);
466 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1);
467