acpi_gpio.c revision 1.2 1 /* $NetBSD: acpi_gpio.c,v 1.2 2024/12/09 22:10:25 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 <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.2 2024/12/09 22:10:25 jmcneill Exp $");
38
39 #include <sys/param.h>
40 #include <sys/gpio.h>
41
42 #include <dev/acpi/acpireg.h>
43 #include <dev/acpi/acpivar.h>
44 #include <dev/acpi/acpi_gpio.h>
45
46 int
47 acpi_gpio_register(struct acpi_devnode *ad, device_t dev,
48 int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv)
49 {
50 if (ad->ad_gpiodev != NULL) {
51 device_printf(dev, "%s already registered\n",
52 device_xname(ad->ad_gpiodev));
53 return EBUSY;
54 }
55
56 ad->ad_gpiodev = dev;
57 ad->ad_gpio_translate = translate;
58 ad->ad_gpio_priv = priv;
59
60 return 0;
61 }
62
63 static ACPI_STATUS
64 acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin)
65 {
66 struct acpi_devnode *ad, *gpioad = NULL;
67 ACPI_HANDLE hdl;
68 ACPI_RESOURCE_SOURCE *rs;
69 ACPI_STATUS rv;
70 int xpin;
71
72 /* Find the device node providing the GPIO resource. */
73 rs = &res->ResourceSource;
74 if (rs->StringPtr == NULL) {
75 return AE_NOT_FOUND;
76 }
77 rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl);
78 if (ACPI_FAILURE(rv)) {
79 return rv;
80 }
81 SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) {
82 if (ad->ad_handle == hdl) {
83 gpioad = ad;
84 break;
85 }
86 }
87 if (gpioad == NULL) {
88 /* No device node found. */
89 return AE_NOT_FOUND;
90 }
91
92 if (gpioad->ad_gpiodev == NULL) {
93 /* No resource provider is registered. */
94 return AE_NO_HANDLER;
95 }
96
97 xpin = gpioad->ad_gpio_translate(gpioad->ad_gpio_priv,
98 res, gpiop);
99 if (xpin == -1) {
100 /* Pin could not be translated. */
101 return AE_NOT_IMPLEMENTED;
102 }
103
104 *pin = xpin;
105
106 return AE_OK;
107 }
108
109 struct acpi_gpio_resource_context {
110 u_int index;
111 u_int conntype;
112 u_int curindex;
113 ACPI_RESOURCE_GPIO *res;
114 };
115
116 static ACPI_STATUS
117 acpi_gpio_parse(ACPI_RESOURCE *res, void *context)
118 {
119 struct acpi_gpio_resource_context *ctx = context;
120
121 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) {
122 return AE_OK;
123 }
124 if (res->Data.Gpio.ConnectionType != ctx->conntype) {
125 return AE_OK;
126 }
127 if (ctx->curindex == ctx->index) {
128 ctx->res = &res->Data.Gpio;
129 return AE_CTRL_TERMINATE;
130 }
131 ctx->curindex++;
132 return AE_OK;
133
134 }
135
136 ACPI_STATUS
137 acpi_gpio_get_int(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin,
138 int *irqmode)
139 {
140 struct acpi_gpio_resource_context ctx = {
141 .index = index,
142 .conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
143 };
144 ACPI_RESOURCE_GPIO *gpio;
145 ACPI_STATUS rv;
146
147 rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
148 if (ACPI_FAILURE(rv)) {
149 return rv;
150 }
151 gpio = ctx.res;
152
153 rv = acpi_gpio_translate(gpio, gpiop, pin);
154 if (ACPI_FAILURE(rv)) {
155 printf("%s: translate failed: %s\n", __func__,
156 AcpiFormatException(rv));
157 return rv;
158 }
159
160 if (gpio->Triggering == ACPI_LEVEL_SENSITIVE) {
161 *irqmode = gpio->Polarity == ACPI_ACTIVE_HIGH ?
162 GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL;
163 } else {
164 KASSERT(gpio->Triggering == ACPI_EDGE_SENSITIVE);
165 if (gpio->Polarity == ACPI_ACTIVE_LOW) {
166 *irqmode = GPIO_INTR_NEG_EDGE;
167 } else if (gpio->Polarity == ACPI_ACTIVE_HIGH) {
168 *irqmode = GPIO_INTR_POS_EDGE;
169 } else {
170 KASSERT(gpio->Polarity == ACPI_ACTIVE_BOTH);
171 *irqmode = GPIO_INTR_DOUBLE_EDGE;
172 }
173 }
174
175 return AE_OK;
176 }
177
178 ACPI_STATUS
179 acpi_gpio_get_io(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin)
180 {
181 struct acpi_gpio_resource_context ctx = {
182 .index = index,
183 .conntype = ACPI_RESOURCE_GPIO_TYPE_INT,
184 };
185 ACPI_STATUS rv;
186
187 rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx);
188 if (ACPI_FAILURE(rv)) {
189 return rv;
190 }
191
192 return acpi_gpio_translate(ctx.res, gpiop, pin);
193 }
194