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
acpi_gpio_address_space_init(ACPI_HANDLE region_hdl,UINT32 function,void * handler_ctx,void ** region_ctx)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
acpi_gpio_address_space_handler(UINT32 function,ACPI_PHYSICAL_ADDRESS address,UINT32 bit_width,UINT64 * value,void * handler_ctx,void * region_ctx)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
acpi_gpio_register(struct acpi_devnode * ad,device_t dev,int (* translate)(void *,ACPI_RESOURCE_GPIO *,void **),void * priv)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
acpi_gpio_translate(ACPI_RESOURCE_GPIO * res,void ** gpiop,int * pin)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
acpi_gpio_parse(ACPI_RESOURCE * res,void * context)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
acpi_gpio_get_int(ACPI_HANDLE hdl,u_int index,void ** gpiop,int * pin,int * irqmode)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
acpi_gpio_get_io(ACPI_HANDLE hdl,u_int index,void ** gpiop,int * pin)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