xref: /freebsd/sys/dev/gpio/acpi_gpiobus.c (revision 9709bda03cd0f20eba0ba4276fc3c2e06354a54f)
192adaa58SAhmad Khalifa /*-
292adaa58SAhmad Khalifa  * SPDX-License-Identifier: BSD-2-Clause
392adaa58SAhmad Khalifa  *
492adaa58SAhmad Khalifa  * Copyright (c) 2024 Ahmad Khalifa <ahmadkhalifa570@gmail.com>
592adaa58SAhmad Khalifa  *
692adaa58SAhmad Khalifa  * Redistribution and use in source and binary forms, with or without
792adaa58SAhmad Khalifa  * modification, are permitted provided that the following conditions
892adaa58SAhmad Khalifa  * are met:
992adaa58SAhmad Khalifa  * 1. Redistributions of source code must retain the above copyright
1092adaa58SAhmad Khalifa  *    notice, this list of conditions and the following disclaimer.
1192adaa58SAhmad Khalifa  * 2. Redistributions in binary form must reproduce the above copyright
1292adaa58SAhmad Khalifa  *    notice, this list of conditions and the following disclaimer in the
1392adaa58SAhmad Khalifa  *    documentation and/or other materials provided with the distribution.
1492adaa58SAhmad Khalifa  *
1592adaa58SAhmad Khalifa  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
1692adaa58SAhmad Khalifa  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1792adaa58SAhmad Khalifa  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1892adaa58SAhmad Khalifa  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
1992adaa58SAhmad Khalifa  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2092adaa58SAhmad Khalifa  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2192adaa58SAhmad Khalifa  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2292adaa58SAhmad Khalifa  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2392adaa58SAhmad Khalifa  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2492adaa58SAhmad Khalifa  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2592adaa58SAhmad Khalifa  * SUCH DAMAGE.
2692adaa58SAhmad Khalifa  */
2792adaa58SAhmad Khalifa 
2892adaa58SAhmad Khalifa #include <sys/types.h>
2992adaa58SAhmad Khalifa #include <sys/bus.h>
3092adaa58SAhmad Khalifa #include <sys/kernel.h>
3192adaa58SAhmad Khalifa #include <sys/module.h>
3292adaa58SAhmad Khalifa #include <sys/gpio.h>
3392adaa58SAhmad Khalifa 
3492adaa58SAhmad Khalifa #include <contrib/dev/acpica/include/acpi.h>
3592adaa58SAhmad Khalifa #include <dev/acpica/acpivar.h>
3692adaa58SAhmad Khalifa 
3792adaa58SAhmad Khalifa #include <dev/gpio/gpiobusvar.h>
38*9709bda0SColin Percival #include <dev/gpio/acpi_gpiobusvar.h>
39*9709bda0SColin Percival 
40*9709bda0SColin Percival #include "gpiobus_if.h"
4192adaa58SAhmad Khalifa 
4292adaa58SAhmad Khalifa struct acpi_gpiobus_softc {
4392adaa58SAhmad Khalifa 	struct gpiobus_softc	super_sc;
4492adaa58SAhmad Khalifa 	ACPI_CONNECTION_INFO	handler_info;
4592adaa58SAhmad Khalifa };
4692adaa58SAhmad Khalifa 
4792adaa58SAhmad Khalifa struct acpi_gpiobus_ctx {
4892adaa58SAhmad Khalifa 	struct gpiobus_softc	*sc;
4992adaa58SAhmad Khalifa 	ACPI_HANDLE		dev_handle;
5092adaa58SAhmad Khalifa };
5192adaa58SAhmad Khalifa 
52*9709bda0SColin Percival struct acpi_gpiobus_ivar
53*9709bda0SColin Percival {
54*9709bda0SColin Percival 	struct gpiobus_ivar	gpiobus;	/* Must come first */
55*9709bda0SColin Percival 	ACPI_HANDLE		dev_handle;	/* ACPI handle for bus */
56*9709bda0SColin Percival 	uint32_t		flags;
57*9709bda0SColin Percival };
58*9709bda0SColin Percival 
5992adaa58SAhmad Khalifa static uint32_t
acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO * gpio_res)6092adaa58SAhmad Khalifa acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res)
6192adaa58SAhmad Khalifa {
6292adaa58SAhmad Khalifa 	uint32_t flags = 0;
6392adaa58SAhmad Khalifa 
6492adaa58SAhmad Khalifa 	/* Figure out pin flags */
6592adaa58SAhmad Khalifa 	if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_INT) {
6692adaa58SAhmad Khalifa 		switch (gpio_res->Polarity) {
6792adaa58SAhmad Khalifa 		case ACPI_ACTIVE_HIGH:
6892adaa58SAhmad Khalifa 			flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ?
6992adaa58SAhmad Khalifa 			    GPIO_INTR_LEVEL_HIGH : GPIO_INTR_EDGE_RISING;
7092adaa58SAhmad Khalifa 			break;
7192adaa58SAhmad Khalifa 		case ACPI_ACTIVE_LOW:
7292adaa58SAhmad Khalifa 			flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ?
7392adaa58SAhmad Khalifa 			    GPIO_INTR_LEVEL_LOW : GPIO_INTR_EDGE_FALLING;
7492adaa58SAhmad Khalifa 			break;
7592adaa58SAhmad Khalifa 		case ACPI_ACTIVE_BOTH:
7692adaa58SAhmad Khalifa 			flags = GPIO_INTR_EDGE_BOTH;
7792adaa58SAhmad Khalifa 			break;
7892adaa58SAhmad Khalifa 		}
7992adaa58SAhmad Khalifa 
802d421991SColin Percival #ifdef NOT_YET
812d421991SColin Percival 		/* This is not currently implemented. */
8292adaa58SAhmad Khalifa 		if (gpio_res->Shareable == ACPI_SHARED)
8392adaa58SAhmad Khalifa 			flags |= GPIO_INTR_SHAREABLE;
8492adaa58SAhmad Khalifa #endif
852d421991SColin Percival 	}
860ffd7d4dSColin Percival 	if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_IO) {
8792adaa58SAhmad Khalifa 		switch (gpio_res->IoRestriction) {
8892adaa58SAhmad Khalifa 		case ACPI_IO_RESTRICT_INPUT:
89c8081327SColin Percival 			flags |= GPIO_PIN_INPUT;
9092adaa58SAhmad Khalifa 			break;
9192adaa58SAhmad Khalifa 		case ACPI_IO_RESTRICT_OUTPUT:
92c8081327SColin Percival 			flags |= GPIO_PIN_OUTPUT;
9392adaa58SAhmad Khalifa 			break;
9492adaa58SAhmad Khalifa 		}
950ffd7d4dSColin Percival 	}
9692adaa58SAhmad Khalifa 
9792adaa58SAhmad Khalifa 	switch (gpio_res->PinConfig) {
9892adaa58SAhmad Khalifa 	case ACPI_PIN_CONFIG_PULLUP:
9992adaa58SAhmad Khalifa 		flags |= GPIO_PIN_PULLUP;
10092adaa58SAhmad Khalifa 		break;
10192adaa58SAhmad Khalifa 	case ACPI_PIN_CONFIG_PULLDOWN:
10292adaa58SAhmad Khalifa 		flags |= GPIO_PIN_PULLDOWN;
10392adaa58SAhmad Khalifa 		break;
10492adaa58SAhmad Khalifa 	}
10592adaa58SAhmad Khalifa 
10692adaa58SAhmad Khalifa 	return (flags);
10792adaa58SAhmad Khalifa }
10892adaa58SAhmad Khalifa 
10992adaa58SAhmad Khalifa static ACPI_STATUS
acpi_gpiobus_enumerate_res(ACPI_RESOURCE * res,void * context)11092adaa58SAhmad Khalifa acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context)
11192adaa58SAhmad Khalifa {
11292adaa58SAhmad Khalifa 	ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
11392adaa58SAhmad Khalifa 	struct acpi_gpiobus_ctx *ctx = context;
11492adaa58SAhmad Khalifa 	struct gpiobus_softc *super_sc = ctx->sc;
11592adaa58SAhmad Khalifa 	ACPI_HANDLE handle;
11692adaa58SAhmad Khalifa 	uint32_t flags, i;
11792adaa58SAhmad Khalifa 
11892adaa58SAhmad Khalifa 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
11992adaa58SAhmad Khalifa 		return (AE_OK);
12092adaa58SAhmad Khalifa 
12192adaa58SAhmad Khalifa 	if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT,
12292adaa58SAhmad Khalifa 	    gpio_res->ResourceSource.StringPtr, &handle)) ||
12392adaa58SAhmad Khalifa 	    handle != ctx->dev_handle)
12492adaa58SAhmad Khalifa 		return (AE_OK);
12592adaa58SAhmad Khalifa 
12692adaa58SAhmad Khalifa 	if (__predict_false(gpio_res->PinTableLength > super_sc->sc_npins)) {
12792adaa58SAhmad Khalifa 		device_printf(super_sc->sc_busdev,
12892adaa58SAhmad Khalifa 		    "invalid pin table length %hu, max: %d (bad ACPI tables?)\n",
12992adaa58SAhmad Khalifa 		    gpio_res->PinTableLength, super_sc->sc_npins);
13092adaa58SAhmad Khalifa 		return (AE_LIMIT);
13192adaa58SAhmad Khalifa 	}
13292adaa58SAhmad Khalifa 
13392adaa58SAhmad Khalifa 	flags = acpi_gpiobus_convflags(gpio_res);
13492adaa58SAhmad Khalifa 	for (i = 0; i < gpio_res->PinTableLength; i++) {
13592adaa58SAhmad Khalifa 		UINT16 pin = gpio_res->PinTable[i];
13692adaa58SAhmad Khalifa 
13792adaa58SAhmad Khalifa 		if (__predict_false(pin >= super_sc->sc_npins)) {
13892adaa58SAhmad Khalifa 			device_printf(super_sc->sc_busdev,
13992adaa58SAhmad Khalifa 			    "invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n",
14092adaa58SAhmad Khalifa 			    pin, super_sc->sc_npins - 1);
14192adaa58SAhmad Khalifa 			return (AE_LIMIT);
14292adaa58SAhmad Khalifa 		}
14392adaa58SAhmad Khalifa 
14492adaa58SAhmad Khalifa 		GPIO_PIN_SETFLAGS(super_sc->sc_dev, pin, flags &
14592adaa58SAhmad Khalifa 		    ~GPIO_INTR_MASK);
14692adaa58SAhmad Khalifa 	}
14792adaa58SAhmad Khalifa 
14892adaa58SAhmad Khalifa 	return (AE_OK);
14992adaa58SAhmad Khalifa }
15092adaa58SAhmad Khalifa 
151*9709bda0SColin Percival static struct acpi_gpiobus_ivar *
acpi_gpiobus_setup_devinfo(device_t bus,device_t child,ACPI_RESOURCE_GPIO * gpio_res)152*9709bda0SColin Percival acpi_gpiobus_setup_devinfo(device_t bus, device_t child,
153*9709bda0SColin Percival     ACPI_RESOURCE_GPIO *gpio_res)
154*9709bda0SColin Percival {
155*9709bda0SColin Percival 	struct acpi_gpiobus_ivar *devi;
156*9709bda0SColin Percival 
157*9709bda0SColin Percival 	devi = malloc(sizeof(*devi), M_DEVBUF, M_NOWAIT | M_ZERO);
158*9709bda0SColin Percival 	if (devi == NULL)
159*9709bda0SColin Percival 		return (NULL);
160*9709bda0SColin Percival 	resource_list_init(&devi->gpiobus.rl);
161*9709bda0SColin Percival 
162*9709bda0SColin Percival 	devi->flags = acpi_gpiobus_convflags(gpio_res);
163*9709bda0SColin Percival 	if (acpi_quirks & ACPI_Q_AEI_NOPULL)
164*9709bda0SColin Percival 		devi->flags &= ~GPIO_PIN_PULLUP;
165*9709bda0SColin Percival 
166*9709bda0SColin Percival 	devi->gpiobus.npins = 1;
167*9709bda0SColin Percival 	if (gpiobus_alloc_ivars(&devi->gpiobus) != 0) {
168*9709bda0SColin Percival 		free(devi, M_DEVBUF);
169*9709bda0SColin Percival 		return (NULL);
170*9709bda0SColin Percival 	}
171*9709bda0SColin Percival 
172*9709bda0SColin Percival 	for (int i = 0; i < devi->gpiobus.npins; i++)
173*9709bda0SColin Percival 		devi->gpiobus.pins[i] = gpio_res->PinTable[i];
174*9709bda0SColin Percival 
175*9709bda0SColin Percival 	return (devi);
176*9709bda0SColin Percival }
177*9709bda0SColin Percival 
178*9709bda0SColin Percival static ACPI_STATUS
acpi_gpiobus_enumerate_aei(ACPI_RESOURCE * res,void * context)179*9709bda0SColin Percival acpi_gpiobus_enumerate_aei(ACPI_RESOURCE *res, void *context)
180*9709bda0SColin Percival {
181*9709bda0SColin Percival 	ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio;
182*9709bda0SColin Percival 	struct acpi_gpiobus_ctx *ctx = context;
183*9709bda0SColin Percival 	device_t bus = ctx->sc->sc_busdev;
184*9709bda0SColin Percival 	device_t child;
185*9709bda0SColin Percival 	struct acpi_gpiobus_ivar *devi;
186*9709bda0SColin Percival 
187*9709bda0SColin Percival 	/* Check that we have a GpioInt object. */
188*9709bda0SColin Percival 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO)
189*9709bda0SColin Percival 		return (AE_OK);
190*9709bda0SColin Percival 	if (gpio_res->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT)
191*9709bda0SColin Percival 		return (AE_OK);
192*9709bda0SColin Percival 
193*9709bda0SColin Percival 	/* Add a child. */
194*9709bda0SColin Percival 	child = device_add_child_ordered(bus, 0, "gpio_aei", DEVICE_UNIT_ANY);
195*9709bda0SColin Percival 	if (child == NULL)
196*9709bda0SColin Percival 		return (AE_OK);
197*9709bda0SColin Percival 	devi = acpi_gpiobus_setup_devinfo(bus, child, gpio_res);
198*9709bda0SColin Percival 	if (devi == NULL) {
199*9709bda0SColin Percival 		device_delete_child(bus, child);
200*9709bda0SColin Percival 		return (AE_OK);
201*9709bda0SColin Percival 	}
202*9709bda0SColin Percival 	device_set_ivars(child, devi);
203*9709bda0SColin Percival 
204*9709bda0SColin Percival 	for (int i = 0; i < devi->gpiobus.npins; i++) {
205*9709bda0SColin Percival 		if (GPIOBUS_PIN_SETFLAGS(bus, child, 0, devi->flags)) {
206*9709bda0SColin Percival 			gpiobus_free_ivars(&devi->gpiobus);
207*9709bda0SColin Percival 			free(devi, M_DEVBUF);
208*9709bda0SColin Percival 			device_delete_child(bus, child);
209*9709bda0SColin Percival 			return (AE_OK);
210*9709bda0SColin Percival 		}
211*9709bda0SColin Percival 	}
212*9709bda0SColin Percival 
213*9709bda0SColin Percival 	/* Pass ACPI information to children. */
214*9709bda0SColin Percival 	devi->dev_handle = ctx->dev_handle;
215*9709bda0SColin Percival 
216*9709bda0SColin Percival 	return (AE_OK);
217*9709bda0SColin Percival }
218*9709bda0SColin Percival 
21992adaa58SAhmad Khalifa static ACPI_STATUS
acpi_gpiobus_enumerate(ACPI_HANDLE handle,UINT32 depth,void * context,void ** result)22092adaa58SAhmad Khalifa acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context,
22192adaa58SAhmad Khalifa     void **result)
22292adaa58SAhmad Khalifa {
22392adaa58SAhmad Khalifa 	UINT32 sta;
22492adaa58SAhmad Khalifa 
22592adaa58SAhmad Khalifa 	/*
22692adaa58SAhmad Khalifa 	 * If no _STA method or if it failed, then assume that
22792adaa58SAhmad Khalifa 	 * the device is present.
22892adaa58SAhmad Khalifa 	 */
22992adaa58SAhmad Khalifa 	if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) &&
23092adaa58SAhmad Khalifa 	    !ACPI_DEVICE_PRESENT(sta))
23192adaa58SAhmad Khalifa 		return (AE_OK);
23292adaa58SAhmad Khalifa 
23392adaa58SAhmad Khalifa 	if (!acpi_has_hid(handle))
23492adaa58SAhmad Khalifa 		return (AE_OK);
23592adaa58SAhmad Khalifa 
23692adaa58SAhmad Khalifa 	/* Look for GPIO resources */
23792adaa58SAhmad Khalifa 	AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context);
23892adaa58SAhmad Khalifa 
23992adaa58SAhmad Khalifa 	return (AE_OK);
24092adaa58SAhmad Khalifa }
24192adaa58SAhmad Khalifa 
24292adaa58SAhmad Khalifa static ACPI_STATUS
acpi_gpiobus_space_handler(UINT32 function,ACPI_PHYSICAL_ADDRESS address,UINT32 length,UINT64 * value,void * context,void * region_context)24392adaa58SAhmad Khalifa acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address,
24492adaa58SAhmad Khalifa     UINT32 length, UINT64 *value, void *context, void *region_context)
24592adaa58SAhmad Khalifa {
24692adaa58SAhmad Khalifa 	ACPI_CONNECTION_INFO *info = context;
24792adaa58SAhmad Khalifa 	ACPI_RESOURCE_GPIO *gpio_res;
24892adaa58SAhmad Khalifa 	device_t controller;
24992adaa58SAhmad Khalifa 	ACPI_RESOURCE *res;
25092adaa58SAhmad Khalifa 	ACPI_STATUS status;
25192adaa58SAhmad Khalifa 
25292adaa58SAhmad Khalifa 	status = AcpiBufferToResource(info->Connection, info->Length, &res);
25392adaa58SAhmad Khalifa 	if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO)
25492adaa58SAhmad Khalifa 		goto err;
25592adaa58SAhmad Khalifa 
25692adaa58SAhmad Khalifa 	gpio_res = &res->Data.Gpio;
25792adaa58SAhmad Khalifa 	controller = __containerof(info, struct acpi_gpiobus_softc,
25892adaa58SAhmad Khalifa 	    handler_info)->super_sc.sc_dev;
25992adaa58SAhmad Khalifa 
26092adaa58SAhmad Khalifa 	switch (function) {
26192adaa58SAhmad Khalifa 	case ACPI_WRITE:
26292adaa58SAhmad Khalifa 		if (__predict_false(
26392adaa58SAhmad Khalifa 		    gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT))
26492adaa58SAhmad Khalifa 			goto err;
26592adaa58SAhmad Khalifa 
26692adaa58SAhmad Khalifa 		for (int i = 0; i < length; i++)
26792adaa58SAhmad Khalifa 			if (GPIO_PIN_SET(controller,
26892adaa58SAhmad Khalifa 			    gpio_res->PinTable[address + i], (*value & 1 << i) ?
26992adaa58SAhmad Khalifa 			    GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0)
27092adaa58SAhmad Khalifa 				goto err;
27192adaa58SAhmad Khalifa 		break;
27292adaa58SAhmad Khalifa 	case ACPI_READ:
27392adaa58SAhmad Khalifa 		if (__predict_false(
27492adaa58SAhmad Khalifa 		    gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT))
27592adaa58SAhmad Khalifa 			goto err;
27692adaa58SAhmad Khalifa 
27792adaa58SAhmad Khalifa 		for (int i = 0; i < length; i++) {
27892adaa58SAhmad Khalifa 			uint32_t v;
27992adaa58SAhmad Khalifa 
28092adaa58SAhmad Khalifa 			if (GPIO_PIN_GET(controller,
28192adaa58SAhmad Khalifa 			    gpio_res->PinTable[address + i], &v) != 0)
28292adaa58SAhmad Khalifa 				goto err;
28392adaa58SAhmad Khalifa 			*value |= v << i;
28492adaa58SAhmad Khalifa 		}
28592adaa58SAhmad Khalifa 		break;
28692adaa58SAhmad Khalifa 	default:
28792adaa58SAhmad Khalifa 		goto err;
28892adaa58SAhmad Khalifa 	}
28992adaa58SAhmad Khalifa 
29092adaa58SAhmad Khalifa 	ACPI_FREE(res);
29192adaa58SAhmad Khalifa 	return (AE_OK);
29292adaa58SAhmad Khalifa 
29392adaa58SAhmad Khalifa err:
29492adaa58SAhmad Khalifa 	ACPI_FREE(res);
29592adaa58SAhmad Khalifa 	return (AE_BAD_PARAMETER);
29692adaa58SAhmad Khalifa }
29792adaa58SAhmad Khalifa 
29892adaa58SAhmad Khalifa static int
acpi_gpiobus_probe(device_t dev)29992adaa58SAhmad Khalifa acpi_gpiobus_probe(device_t dev)
30092adaa58SAhmad Khalifa {
30192adaa58SAhmad Khalifa 	device_t controller;
30292adaa58SAhmad Khalifa 
30392adaa58SAhmad Khalifa 	if (acpi_disabled("gpiobus"))
30492adaa58SAhmad Khalifa 		return (ENXIO);
30592adaa58SAhmad Khalifa 
30692adaa58SAhmad Khalifa 	controller = device_get_parent(dev);
30792adaa58SAhmad Khalifa 	if (controller == NULL)
30892adaa58SAhmad Khalifa 		return (ENXIO);
30992adaa58SAhmad Khalifa 
31092adaa58SAhmad Khalifa 	if (acpi_get_handle(controller) == NULL)
31192adaa58SAhmad Khalifa 		return (ENXIO);
31292adaa58SAhmad Khalifa 
31392adaa58SAhmad Khalifa 	device_set_desc(dev, "GPIO bus (ACPI-hinted)");
31492adaa58SAhmad Khalifa 	return (BUS_PROBE_DEFAULT);
31592adaa58SAhmad Khalifa }
31692adaa58SAhmad Khalifa 
31792adaa58SAhmad Khalifa static int
acpi_gpiobus_attach(device_t dev)31892adaa58SAhmad Khalifa acpi_gpiobus_attach(device_t dev)
31992adaa58SAhmad Khalifa {
32092adaa58SAhmad Khalifa 	struct acpi_gpiobus_softc *sc;
32192adaa58SAhmad Khalifa 	struct acpi_gpiobus_ctx ctx;
32292adaa58SAhmad Khalifa 	ACPI_HANDLE handle;
32392adaa58SAhmad Khalifa 	ACPI_STATUS status;
32492adaa58SAhmad Khalifa 	int err;
32592adaa58SAhmad Khalifa 
32692adaa58SAhmad Khalifa 	if ((err = gpiobus_attach(dev)) != 0)
32792adaa58SAhmad Khalifa 		return (err);
32892adaa58SAhmad Khalifa 
32992adaa58SAhmad Khalifa 	sc = device_get_softc(dev);
33092adaa58SAhmad Khalifa 	handle = acpi_get_handle(sc->super_sc.sc_dev);
33192adaa58SAhmad Khalifa 	if (handle == NULL) {
33292adaa58SAhmad Khalifa 		gpiobus_detach(dev);
33392adaa58SAhmad Khalifa 		return (ENXIO);
33492adaa58SAhmad Khalifa 	}
33592adaa58SAhmad Khalifa 
33692adaa58SAhmad Khalifa 	status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
33792adaa58SAhmad Khalifa 	    acpi_gpiobus_space_handler, NULL, &sc->handler_info);
33892adaa58SAhmad Khalifa 
33992adaa58SAhmad Khalifa 	if (ACPI_FAILURE(status)) {
34092adaa58SAhmad Khalifa 		device_printf(dev,
34192adaa58SAhmad Khalifa 		    "Failed to install GPIO address space handler\n");
34292adaa58SAhmad Khalifa 		gpiobus_detach(dev);
34392adaa58SAhmad Khalifa 		return (ENXIO);
34492adaa58SAhmad Khalifa 	}
34592adaa58SAhmad Khalifa 
34692adaa58SAhmad Khalifa 	ctx.dev_handle = handle;
34792adaa58SAhmad Khalifa 	ctx.sc = &sc->super_sc;
34892adaa58SAhmad Khalifa 
34992adaa58SAhmad Khalifa 	status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
35092adaa58SAhmad Khalifa 	    ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL);
35192adaa58SAhmad Khalifa 
35292adaa58SAhmad Khalifa 	if (ACPI_FAILURE(status))
35392adaa58SAhmad Khalifa 		device_printf(dev, "Failed to enumerate GPIO resources\n");
35492adaa58SAhmad Khalifa 
355*9709bda0SColin Percival 	/* Look for AEI children */
356*9709bda0SColin Percival 	status = AcpiWalkResources(handle, "_AEI", acpi_gpiobus_enumerate_aei,
357*9709bda0SColin Percival 	    &ctx);
358*9709bda0SColin Percival 
359*9709bda0SColin Percival 	if (ACPI_FAILURE(status))
360*9709bda0SColin Percival 		device_printf(dev, "Failed to enumerate GPIO resources\n");
361*9709bda0SColin Percival 
36292adaa58SAhmad Khalifa 	return (0);
36392adaa58SAhmad Khalifa }
36492adaa58SAhmad Khalifa 
36592adaa58SAhmad Khalifa static int
acpi_gpiobus_detach(device_t dev)36692adaa58SAhmad Khalifa acpi_gpiobus_detach(device_t dev)
36792adaa58SAhmad Khalifa {
36892adaa58SAhmad Khalifa 	struct gpiobus_softc *super_sc;
36992adaa58SAhmad Khalifa 	ACPI_STATUS status;
37092adaa58SAhmad Khalifa 
37192adaa58SAhmad Khalifa 	super_sc = device_get_softc(dev);
37292adaa58SAhmad Khalifa 	status = AcpiRemoveAddressSpaceHandler(
37392adaa58SAhmad Khalifa 	    acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO,
37492adaa58SAhmad Khalifa 	    acpi_gpiobus_space_handler
37592adaa58SAhmad Khalifa 	);
37692adaa58SAhmad Khalifa 
37792adaa58SAhmad Khalifa 	if (ACPI_FAILURE(status))
37892adaa58SAhmad Khalifa 		device_printf(dev,
37992adaa58SAhmad Khalifa 		    "Failed to remove GPIO address space handler\n");
38092adaa58SAhmad Khalifa 
38192adaa58SAhmad Khalifa 	return (gpiobus_detach(dev));
38292adaa58SAhmad Khalifa }
38392adaa58SAhmad Khalifa 
384*9709bda0SColin Percival int
gpio_pin_get_by_acpi_index(device_t consumer,uint32_t idx,gpio_pin_t * out_pin)385*9709bda0SColin Percival gpio_pin_get_by_acpi_index(device_t consumer, uint32_t idx,
386*9709bda0SColin Percival     gpio_pin_t *out_pin)
387*9709bda0SColin Percival {
388*9709bda0SColin Percival 	struct acpi_gpiobus_ivar *devi;
389*9709bda0SColin Percival 	int rv;
390*9709bda0SColin Percival 
391*9709bda0SColin Percival 	rv = gpio_pin_get_by_child_index(consumer, idx, out_pin);
392*9709bda0SColin Percival 	if (rv != 0)
393*9709bda0SColin Percival 		return (rv);
394*9709bda0SColin Percival 
395*9709bda0SColin Percival 	devi = device_get_ivars(consumer);
396*9709bda0SColin Percival 	(*out_pin)->flags = devi->flags;
397*9709bda0SColin Percival 
398*9709bda0SColin Percival 	return (0);
399*9709bda0SColin Percival }
400*9709bda0SColin Percival 
401*9709bda0SColin Percival static int
acpi_gpiobus_read_ivar(device_t dev,device_t child,int which,uintptr_t * result)402*9709bda0SColin Percival acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result)
403*9709bda0SColin Percival {
404*9709bda0SColin Percival 	struct acpi_gpiobus_ivar *devi = device_get_ivars(child);
405*9709bda0SColin Percival 
406*9709bda0SColin Percival 	switch (which) {
407*9709bda0SColin Percival 	case ACPI_GPIOBUS_IVAR_HANDLE:
408*9709bda0SColin Percival 		*result = (uintptr_t)devi->dev_handle;
409*9709bda0SColin Percival 		break;
410*9709bda0SColin Percival 	default:
411*9709bda0SColin Percival 		return (gpiobus_read_ivar(dev, child, which, result));
412*9709bda0SColin Percival 	}
413*9709bda0SColin Percival 
414*9709bda0SColin Percival 	return (0);
415*9709bda0SColin Percival }
416*9709bda0SColin Percival 
41792adaa58SAhmad Khalifa static device_method_t acpi_gpiobus_methods[] = {
41892adaa58SAhmad Khalifa 	/* Device interface */
41992adaa58SAhmad Khalifa 	DEVMETHOD(device_probe,		acpi_gpiobus_probe),
42092adaa58SAhmad Khalifa 	DEVMETHOD(device_attach,	acpi_gpiobus_attach),
42192adaa58SAhmad Khalifa 	DEVMETHOD(device_detach,	acpi_gpiobus_detach),
42292adaa58SAhmad Khalifa 
423*9709bda0SColin Percival 	/* Bus interface */
424*9709bda0SColin Percival 	DEVMETHOD(bus_read_ivar,	acpi_gpiobus_read_ivar),
425*9709bda0SColin Percival 
42692adaa58SAhmad Khalifa 	DEVMETHOD_END
42792adaa58SAhmad Khalifa };
42892adaa58SAhmad Khalifa 
42992adaa58SAhmad Khalifa DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods,
43092adaa58SAhmad Khalifa     sizeof(struct acpi_gpiobus_softc), gpiobus_driver);
43192adaa58SAhmad Khalifa EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL,
43292adaa58SAhmad Khalifa     BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
43392adaa58SAhmad Khalifa MODULE_VERSION(acpi_gpiobus, 1);
43492adaa58SAhmad Khalifa MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1);
435