xref: /freebsd/sys/dev/gpio/acpi_gpiobus.c (revision c5c02a131a0e2ef52771e683269bc8778fe511f3)
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
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 #ifdef NOT_YET
81 		/* This is not currently implemented. */
82 		if (gpio_res->Shareable == ACPI_SHARED)
83 			flags |= GPIO_INTR_SHAREABLE;
84 #endif
85 	}
86 	if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_IO) {
87 		switch (gpio_res->IoRestriction) {
88 		case ACPI_IO_RESTRICT_INPUT:
89 			flags |= GPIO_PIN_INPUT;
90 			break;
91 		case ACPI_IO_RESTRICT_OUTPUT:
92 			flags |= GPIO_PIN_OUTPUT;
93 			break;
94 		}
95 	}
96 
97 	switch (gpio_res->PinConfig) {
98 	case ACPI_PIN_CONFIG_PULLUP:
99 		flags |= GPIO_PIN_PULLUP;
100 		break;
101 	case ACPI_PIN_CONFIG_PULLDOWN:
102 		flags |= GPIO_PIN_PULLDOWN;
103 		break;
104 	}
105 
106 	return (flags);
107 }
108 
109 static ACPI_STATUS
110 acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context)
111 {
112 	ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
113 	struct acpi_gpiobus_ctx *ctx = context;
114 	struct gpiobus_softc *super_sc = ctx->sc;
115 	ACPI_HANDLE handle;
116 	uint32_t flags, i;
117 
118 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
119 		return (AE_OK);
120 
121 	if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT,
122 	    gpio_res->ResourceSource.StringPtr, &handle)) ||
123 	    handle != ctx->dev_handle)
124 		return (AE_OK);
125 
126 	if (__predict_false(gpio_res->PinTableLength > super_sc->sc_npins)) {
127 		device_printf(super_sc->sc_busdev,
128 		    "invalid pin table length %hu, max: %d (bad ACPI tables?)\n",
129 		    gpio_res->PinTableLength, super_sc->sc_npins);
130 		return (AE_LIMIT);
131 	}
132 
133 	flags = acpi_gpiobus_convflags(gpio_res);
134 	for (i = 0; i < gpio_res->PinTableLength; i++) {
135 		UINT16 pin = gpio_res->PinTable[i];
136 
137 		if (__predict_false(pin >= super_sc->sc_npins)) {
138 			device_printf(super_sc->sc_busdev,
139 			    "invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n",
140 			    pin, super_sc->sc_npins - 1);
141 			return (AE_LIMIT);
142 		}
143 
144 		GPIO_PIN_SETFLAGS(super_sc->sc_dev, pin, flags &
145 		    ~GPIO_INTR_MASK);
146 	}
147 
148 	return (AE_OK);
149 }
150 
151 static struct acpi_gpiobus_ivar *
152 acpi_gpiobus_setup_devinfo(device_t bus, device_t child,
153     ACPI_RESOURCE_GPIO *gpio_res)
154 {
155 	struct acpi_gpiobus_ivar *devi;
156 
157 	devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO);
158 	if (devi == NULL)
159 		return (NULL);
160 	resource_list_init(&devi->gpiobus.rl);
161 
162 	devi->flags = acpi_gpiobus_convflags(gpio_res);
163 	if (acpi_quirks & ACPI_Q_AEI_NOPULL)
164 		devi->flags &= ~GPIO_PIN_PULLUP;
165 
166 	devi->gpiobus.npins = 1;
167 	if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) {
168 		free(devi, M_DEVBUF);
169 		return (NULL);
170 	}
171 
172 	for (int i = 0; i < devi->gpiobus.npins; i++)
173 		devi->gpiobus.pins[i] = gpio_res->PinTable[i];
174 
175 	return (devi);
176 }
177 
178 static ACPI_STATUS
179 acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context)
180 {
181 	ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
182 	struct acpi_gpiobus_ctx *ctx = context;
183 	device_t bus = ctx->sc->sc_busdev;
184 	device_t child;
185 	struct acpi_gpiobus_ivar *devi;
186 
187 	/* Check that we have a GpioInt object. */
188 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
189 		return (AE_OK);
190 	if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT)
191 		return (AE_OK);
192 
193 	/* Add a child. */
194 	child = device_add_child_ordered(bus, 0, "gpio_aei", DEVICE_UNIT_ANY);
195 	if (child == NULL)
196 		return (AE_OK);
197 	devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res);
198 	if (devi == NULL) {
199 		device_delete_child(bus, child);
200 		return (AE_OK);
201 	}
202 	device_set_ivars(child, devi);
203 
204 	for (int i = 0; i < devi->gpiobus.npins; i++) {
205 		if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags)) {
206 			gpiobus_free_ivars(&devi->gpiobus);
207 			free(devi, M_DEVBUF);
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
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
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
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
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 GPIO resources\n");
361 
362 	return (0);
363 }
364 
365 static int
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 int
385 gpio_pin_get_by_acpi_index(device_t consumer, uint32_t idx,
386     gpio_pin_t *out_pin)
387 {
388 	struct acpi_gpiobus_ivar *devi;
389 	int rv;
390 
391 	rv = gpio_pin_get_by_child_index(consumer, idx, out_pin);
392 	if (rv != 0)
393 		return (rv);
394 
395 	devi = device_get_ivars(consumer);
396 	(*out_pin)->flags = devi->flags;
397 
398 	return (0);
399 }
400 
401 static int
402 acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
403 {
404 	struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
405 
406 	switch (which) {
407 	case ACPI_GPIOBUS_IVAR_HANDLE:
408 		*result = (uintptr_t)devi->dev_handle;
409 		break;
410 	default:
411 		return (gpiobus_read_ivar(dev, child, which, result));
412 	}
413 
414 	return (0);
415 }
416 
417 static device_method_t acpi_gpiobus_methods[] = {
418 	/* Device interface */
419 	DEVMETHOD(device_probe,		acpi_gpiobus_probe),
420 	DEVMETHOD(device_attach,	acpi_gpiobus_attach),
421 	DEVMETHOD(device_detach,	acpi_gpiobus_detach),
422 
423 	/* Bus interface */
424 	DEVMETHOD(bus_read_ivar,	acpi_gpiobus_read_ivar),
425 
426 	DEVMETHOD_END
427 };
428 
429 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods,
430     sizeof(struct acpi_gpiobus_softc), gpiobus_driver);
431 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL,
432     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
433 MODULE_VERSION(acpi_gpiobus, 1);
434 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1);
435