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