xref: /netbsd-src/sys/dev/acpi/acpi_gpio.c (revision 3a6467bcec8ff54b5598625160966db748a810e0)
1 /* $NetBSD: acpi_gpio.c,v 1.5 2024/12/15 10:15:55 hannken Exp $ */
2 
3 /*-
4  * Copyright (c) 2024 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jared McNeill <jmcneill@invisible.ca>.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 /*
33  * ACPI GPIO resource support.
34  */
35 
36 #include "gpio.h"
37 
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.5 2024/12/15 10:15:55 hannken Exp $");
40 
41 #include <sys/param.h>
42 #include <sys/kmem.h>
43 #include <sys/gpio.h>
44 
45 #include <dev/gpio/gpiovar.h>
46 
47 #include <dev/acpi/acpireg.h>
48 #include <dev/acpi/acpivar.h>
49 #include <dev/acpi/acpi_gpio.h>
50 
51 #if NGPIO > 0
52 
53 #define _COMPONENT	ACPI_RESOURCE_COMPONENT
54 ACPI_MODULE_NAME	("acpi_gpio")
55 
56 struct acpi_gpio_address_space_context {
57 	ACPI_CONNECTION_INFO conn_info;	/* must be first */
58 	struct acpi_devnode *ad;
59 };
60 
61 static ACPI_STATUS
62 acpi_gpio_address_space_init(ACPI_HANDLE region_hdl, UINT32 function,
63     void *handler_ctx, void **region_ctx)
64 {
65 	if (function == ACPI_REGION_DEACTIVATE) {
66 		*region_ctx = NULL;
67 	} else {
68 		*region_ctx = region_hdl;
69 	}
70 	return AE_OK;
71 }
72 
73 static ACPI_STATUS
74 acpi_gpio_address_space_handler(UINT32 function,
75     ACPI_PHYSICAL_ADDRESS address, UINT32 bit_width, UINT64 *value,
76     void *handler_ctx, void *region_ctx)
77 {
78 	ACPI_OPERAND_OBJECT *region_obj = region_ctx;
79 	struct acpi_gpio_address_space_context *context = handler_ctx;
80 	ACPI_CONNECTION_INFO *conn_info = &context->conn_info;
81 	struct acpi_devnode *ad = context->ad;
82 	ACPI_RESOURCE *res;
83 	ACPI_STATUS rv;
84 	struct gpio_pinmap pinmap;
85 	int pins[1];
86 	void *gpiop;
87 	int pin;
88 
89 	if (region_obj->Region.Type != ACPI_TYPE_REGION) {
90 		return AE_OK;
91 	}
92 
93 	if (ad->ad_gpiodev == NULL) {
94 		return AE_NO_HANDLER;
95 	}
96 
97 	rv = AcpiBufferToResource(conn_info->Connection,
98 	    conn_info->Length, &res);
99 	if (ACPI_FAILURE(rv)) {
100 		return rv;
101 	}
102 
103 	if (res->Data.Gpio.PinTableLength != 1) {
104 		/* TODO */
105 		aprint_debug_dev(ad->ad_gpiodev,
106 		    "Pin table length %u not implemented\n",
107 		    res->Data.Gpio.PinTableLength);
108 		rv = AE_NOT_IMPLEMENTED;
109 		goto done;
110 	}
111 
112 	pin = ad->ad_gpio_translate(ad->ad_gpio_priv,
113 	    &res->Data.Gpio, &gpiop);
114 	if (pin == -1) {
115 		/* Pin could not be translated. */
116 		rv = AE_SUPPORT;
117 		goto done;
118 	}
119 
120 	pinmap.pm_map = pins;
121 	if (gpio_pin_map(gpiop, pin, 1, &pinmap) != 0) {
122 		rv = AE_NOT_ACQUIRED;
123 		goto done;
124 	}
125 	if (function & ACPI_IO_MASK) {
126 		gpio_pin_write(gpiop, &pinmap, 0, *value & 1);
127 	} else {
128 		*value = gpio_pin_read(gpiop, &pinmap, 0);
129 	}
130 	gpio_pin_unmap(gpiop, &pinmap);
131 
132 done:
133 	ACPI_FREE(res);
134 
135 	return rv;
136 }
137 #endif
138 
139 ACPI_STATUS
140 acpi_gpio_register(struct acpi_devnode *ad, device_t dev,
141     int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv)
142 {
143 #if NGPIO > 0
144 	struct acpi_gpio_address_space_context *context;
145 	ACPI_STATUS rv;
146 
147 	if (ad->ad_gpiodev != NULL) {
148 		device_printf(dev, "%s already registered\n",
149 		    device_xname(ad->ad_gpiodev));
150 		return AE_ALREADY_EXISTS;
151 	}
152 
153 	context = kmem_zalloc(sizeof(*context), KM_SLEEP);
154 	context->ad = ad;
155 
156 	rv = AcpiInstallAddressSpaceHandler(ad->ad_handle,
157 	    ACPI_ADR_SPACE_GPIO,
158 	    acpi_gpio_address_space_handler,
159 	    acpi_gpio_address_space_init,
160 	    context);
161 	if (ACPI_FAILURE(rv)) {
162 		aprint_error_dev(dev,
163 		    "couldn't install address space handler: %s",
164 		    AcpiFormatException(rv));
165 		return rv;
166 	}
167 
168 	ad->ad_gpiodev = dev;
169 	ad->ad_gpio_translate = translate;
170 	ad->ad_gpio_priv = priv;
171 
172 	return AE_OK;
173 #else
174 	return AE_NOT_CONFIGURED;
175 #endif
176 }
177 
178 static ACPI_STATUS
179 acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin)
180 {
181 	struct acpi_devnode *ad, *gpioad = NULL;
182 	ACPI_HANDLE hdl;
183 	ACPI_RESOURCE_SOURCE *rs;
184 	ACPI_STATUS rv;
185 	int xpin;
186 
187 	/* Find the device node providing the GPIO resource. */
188 	rs = &res->ResourceSource;
189 	if (rs->StringPtr == NULL) {
190 		return AE_NOT_FOUND;
191 	}
192 	rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
193 	if (ACPI_FAILURE(rv)) {
194 		return rv;
195 	}
196 	SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
197 		if (ad->ad_handle == hdl) {
198 			gpioad = ad;
199 			break;
200 		}
201 	}
202 	if (gpioad == NULL) {
203 		/* No device node found. */
204 		return AE_NOT_FOUND;
205 	}
206 
207 	if (gpioad->ad_gpiodev == NULL) {
208 		/* No resource provider is registered. */
209 		return AE_NO_HANDLER;
210 	}
211 
212 	xpin = gpioad->ad_gpio_translate(gpioad->ad_gpio_priv,
213 	    res, gpiop);
214 	if (xpin == -1) {
215 		/* Pin could not be translated. */
216 		return AE_SUPPORT;
217 	}
218 
219 	*pin = xpin;
220 
221 	return AE_OK;
222 }
223 
224 struct acpi_gpio_resource_context {
225 	u_int index;
226 	u_int conntype;
227 	u_int curindex;
228 	ACPI_RESOURCE_GPIO *res;
229 };
230 
231 static ACPI_STATUS
232 acpi_gpio_parse(ACPI_RESOURCE *res, void *context)
233 {
234 	struct acpi_gpio_resource_context *ctx = context;
235 
236 	if (res->Type != ACPI_RESOURCE_TYPE_GPIO) {
237 		return AE_OK;
238 	}
239 	if (res->Data.Gpio.ConnectionType != ctx->conntype) {
240 		return AE_OK;
241 	}
242 	if (ctx->curindex == ctx->index) {
243 		ctx->res = &res->Data.Gpio;
244 		return AE_CTRL_TERMINATE;
245 	}
246 	ctx->curindex++;
247 	return AE_OK;
248 
249 }
250 
251 ACPI_STATUS
252 acpi_gpio_get_int(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin,
253     int *irqmode)
254 {
255 	struct acpi_gpio_resource_context ctx = {
256 		.index = index,
257 		.conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
258 	};
259 	ACPI_RESOURCE_GPIO *gpio;
260 	ACPI_STATUS rv;
261 
262 	rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
263 	if (ACPI_FAILURE(rv)) {
264 		return rv;
265 	}
266 	gpio = ctx.res;
267 
268 	rv = acpi_gpio_translate(gpio, gpiop, pin);
269 	if (ACPI_FAILURE(rv)) {
270 		printf("%s: translate failed: %s\n", __func__,
271 		    AcpiFormatException(rv));
272 		return rv;
273 	}
274 
275         if (gpio->Triggering == ACPI_LEVEL_SENSITIVE) {
276                 *irqmode = gpio->Polarity == ACPI_ACTIVE_HIGH ?
277                     GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
278         } else {
279                 KASSERT(gpio->Triggering == ACPI_EDGE_SENSITIVE);
280                 if (gpio->Polarity == ACPI_ACTIVE_LOW) {
281                         *irqmode = GPIO_INTR_NEG_EDGE;
282                 } else if (gpio->Polarity == ACPI_ACTIVE_HIGH) {
283                         *irqmode = GPIO_INTR_POS_EDGE;
284                 } else {
285                         KASSERT(gpio->Polarity == ACPI_ACTIVE_BOTH);
286                         *irqmode = GPIO_INTR_DOUBLE_EDGE;
287                 }
288         }
289 
290 	return AE_OK;
291 }
292 
293 ACPI_STATUS
294 acpi_gpio_get_io(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin)
295 {
296 	struct acpi_gpio_resource_context ctx = {
297 		.index = index,
298 		.conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
299 	};
300 	ACPI_STATUS rv;
301 
302 	rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
303 	if (ACPI_FAILURE(rv)) {
304 		return rv;
305 	}
306 
307 	return acpi_gpio_translate(ctx.res, gpiop, pin);
308 }
309