acpi_gpio.c revision 1.4 1 /* $NetBSD: acpi_gpio.c,v 1.4 2024/12/13 12:25:39 jmcneill 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.4 2024/12/13 12:25:39 jmcneill 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 struct acpi_gpio_address_space_context {
53 ACPI_CONNECTION_INFO conn_info; /* must be first */
54 struct acpi_devnode *ad;
55 };
56
57 static ACPI_STATUS
58 acpi_gpio_address_space_init(ACPI_HANDLE region_hdl, UINT32 function,
59 void *handler_ctx, void **region_ctx)
60 {
61 if (function == ACPI_REGION_DEACTIVATE) {
62 *region_ctx = NULL;
63 } else {
64 *region_ctx = region_hdl;
65 }
66 return AE_OK;
67 }
68
69 static ACPI_STATUS
70 acpi_gpio_address_space_handler(UINT32 function,
71 ACPI_PHYSICAL_ADDRESS address, UINT32 bit_width, UINT64 *value,
72 void *handler_ctx, void *region_ctx)
73 {
74 ACPI_OPERAND_OBJECT *region_obj = region_ctx;
75 struct acpi_gpio_address_space_context *context = handler_ctx;
76 ACPI_CONNECTION_INFO *conn_info = &context->conn_info;
77 struct acpi_devnode *ad = context->ad;
78 ACPI_RESOURCE *res;
79 ACPI_STATUS rv;
80 struct gpio_pinmap pinmap;
81 int pins[1];
82 void *gpiop;
83 int pin;
84
85 if (region_obj->Region.Type != ACPI_TYPE_REGION) {
86 return AE_OK;
87 }
88
89 if (ad->ad_gpiodev == NULL) {
90 return AE_NO_HANDLER;
91 }
92
93 rv = AcpiBufferToResource(conn_info->Connection,
94 conn_info->Length, &res);
95 if (ACPI_FAILURE(rv)) {
96 return rv;
97 }
98
99 if (res->Data.Gpio.PinTableLength != 1) {
100 /* TODO */
101 aprint_debug_dev(ad->ad_gpiodev,
102 "Pin table length %u not implemented\n",
103 res->Data.Gpio.PinTableLength);
104 rv = AE_NOT_IMPLEMENTED;
105 goto done;
106 }
107
108 pin = ad->ad_gpio_translate(ad->ad_gpio_priv,
109 &res->Data.Gpio, &gpiop);
110 if (pin == -1) {
111 /* Pin could not be translated. */
112 rv = AE_SUPPORT;
113 goto done;
114 }
115
116 pinmap.pm_map = pins;
117 if (gpio_pin_map(gpiop, pin, 1, &pinmap) != 0) {
118 rv = AE_NOT_ACQUIRED;
119 goto done;
120 }
121 if (function & ACPI_IO_MASK) {
122 gpio_pin_write(gpiop, &pinmap, 0, *value & 1);
123 } else {
124 *value = gpio_pin_read(gpiop, &pinmap, 0);
125 }
126 gpio_pin_unmap(gpiop, &pinmap);
127
128 done:
129 ACPI_FREE(res);
130
131 return rv;
132 }
133 #endif
134
135 ACPI_STATUS
136 acpi_gpio_register(struct acpi_devnode *ad, device_t dev,
137 int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv)
138 {
139 #if NGPIO > 0
140 struct acpi_gpio_address_space_context *context;
141 ACPI_STATUS rv;
142
143 if (ad->ad_gpiodev != NULL) {
144 device_printf(dev, "%s already registered\n",
145 device_xname(ad->ad_gpiodev));
146 return AE_ALREADY_EXISTS;
147 }
148
149 context = kmem_zalloc(sizeof(*context), KM_SLEEP);
150 context->ad = ad;
151
152 rv = AcpiInstallAddressSpaceHandler(ad->ad_handle,
153 ACPI_ADR_SPACE_GPIO,
154 acpi_gpio_address_space_handler,
155 acpi_gpio_address_space_init,
156 context);
157 if (ACPI_FAILURE(rv)) {
158 aprint_error_dev(dev,
159 "couldn't install address space handler: %s",
160 AcpiFormatException(rv));
161 return rv;
162 }
163
164 ad->ad_gpiodev = dev;
165 ad->ad_gpio_translate = translate;
166 ad->ad_gpio_priv = priv;
167
168 return AE_OK;
169 #else
170 return AE_NOT_CONFIGURED;
171 #endif
172 }
173
174 static ACPI_STATUS
175 acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin)
176 {
177 struct acpi_devnode *ad, *gpioad = NULL;
178 ACPI_HANDLE hdl;
179 ACPI_RESOURCE_SOURCE *rs;
180 ACPI_STATUS rv;
181 int xpin;
182
183 /* Find the device node providing the GPIO resource. */
184 rs = &res->ResourceSource;
185 if (rs->StringPtr == NULL) {
186 return AE_NOT_FOUND;
187 }
188 rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
189 if (ACPI_FAILURE(rv)) {
190 return rv;
191 }
192 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
193 if (ad->ad_handle == hdl) {
194 gpioad = ad;
195 break;
196 }
197 }
198 if (gpioad == NULL) {
199 /* No device node found. */
200 return AE_NOT_FOUND;
201 }
202
203 if (gpioad->ad_gpiodev == NULL) {
204 /* No resource provider is registered. */
205 return AE_NO_HANDLER;
206 }
207
208 xpin = gpioad->ad_gpio_translate(gpioad->ad_gpio_priv,
209 res, gpiop);
210 if (xpin == -1) {
211 /* Pin could not be translated. */
212 return AE_SUPPORT;
213 }
214
215 *pin = xpin;
216
217 return AE_OK;
218 }
219
220 struct acpi_gpio_resource_context {
221 u_int index;
222 u_int conntype;
223 u_int curindex;
224 ACPI_RESOURCE_GPIO *res;
225 };
226
227 static ACPI_STATUS
228 acpi_gpio_parse(ACPI_RESOURCE *res, void *context)
229 {
230 struct acpi_gpio_resource_context *ctx = context;
231
232 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) {
233 return AE_OK;
234 }
235 if (res->Data.Gpio.ConnectionType != ctx->conntype) {
236 return AE_OK;
237 }
238 if (ctx->curindex == ctx->index) {
239 ctx->res = &res->Data.Gpio;
240 return AE_CTRL_TERMINATE;
241 }
242 ctx->curindex++;
243 return AE_OK;
244
245 }
246
247 ACPI_STATUS
248 acpi_gpio_get_int(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin,
249 int *irqmode)
250 {
251 struct acpi_gpio_resource_context ctx = {
252 .index = index,
253 .conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
254 };
255 ACPI_RESOURCE_GPIO *gpio;
256 ACPI_STATUS rv;
257
258 rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
259 if (ACPI_FAILURE(rv)) {
260 return rv;
261 }
262 gpio = ctx.res;
263
264 rv = acpi_gpio_translate(gpio, gpiop, pin);
265 if (ACPI_FAILURE(rv)) {
266 printf("%s: translate failed: %s\n", __func__,
267 AcpiFormatException(rv));
268 return rv;
269 }
270
271 if (gpio->Triggering == ACPI_LEVEL_SENSITIVE) {
272 *irqmode = gpio->Polarity == ACPI_ACTIVE_HIGH ?
273 GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
274 } else {
275 KASSERT(gpio->Triggering == ACPI_EDGE_SENSITIVE);
276 if (gpio->Polarity == ACPI_ACTIVE_LOW) {
277 *irqmode = GPIO_INTR_NEG_EDGE;
278 } else if (gpio->Polarity == ACPI_ACTIVE_HIGH) {
279 *irqmode = GPIO_INTR_POS_EDGE;
280 } else {
281 KASSERT(gpio->Polarity == ACPI_ACTIVE_BOTH);
282 *irqmode = GPIO_INTR_DOUBLE_EDGE;
283 }
284 }
285
286 return AE_OK;
287 }
288
289 ACPI_STATUS
290 acpi_gpio_get_io(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin)
291 {
292 struct acpi_gpio_resource_context ctx = {
293 .index = index,
294 .conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
295 };
296 ACPI_STATUS rv;
297
298 rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
299 if (ACPI_FAILURE(rv)) {
300 return rv;
301 }
302
303 return acpi_gpio_translate(ctx.res, gpiop, pin);
304 }
305