acpi_gpio.c revision 1.5 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 (at) 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