1 1.5 hannken /* $NetBSD: acpi_gpio.c,v 1.5 2024/12/15 10:15:55 hannken Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /*- 4 1.1 jmcneill * Copyright (c) 2024 The NetBSD Foundation, Inc. 5 1.1 jmcneill * All rights reserved. 6 1.1 jmcneill * 7 1.1 jmcneill * This code is derived from software contributed to The NetBSD Foundation 8 1.1 jmcneill * by Jared McNeill <jmcneill (at) invisible.ca>. 9 1.1 jmcneill * 10 1.1 jmcneill * Redistribution and use in source and binary forms, with or without 11 1.1 jmcneill * modification, are permitted provided that the following conditions 12 1.1 jmcneill * are met: 13 1.1 jmcneill * 1. Redistributions of source code must retain the above copyright 14 1.1 jmcneill * notice, this list of conditions and the following disclaimer. 15 1.1 jmcneill * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 jmcneill * notice, this list of conditions and the following disclaimer in the 17 1.1 jmcneill * documentation and/or other materials provided with the distribution. 18 1.1 jmcneill * 19 1.1 jmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 jmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 jmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 jmcneill * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 jmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 jmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 jmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 jmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 jmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 jmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 jmcneill * POSSIBILITY OF SUCH DAMAGE. 30 1.1 jmcneill */ 31 1.1 jmcneill 32 1.1 jmcneill /* 33 1.1 jmcneill * ACPI GPIO resource support. 34 1.1 jmcneill */ 35 1.1 jmcneill 36 1.4 jmcneill #include "gpio.h" 37 1.4 jmcneill 38 1.1 jmcneill #include <sys/cdefs.h> 39 1.5 hannken __KERNEL_RCSID(0, "$NetBSD: acpi_gpio.c,v 1.5 2024/12/15 10:15:55 hannken Exp $"); 40 1.1 jmcneill 41 1.1 jmcneill #include <sys/param.h> 42 1.3 jmcneill #include <sys/kmem.h> 43 1.1 jmcneill #include <sys/gpio.h> 44 1.1 jmcneill 45 1.3 jmcneill #include <dev/gpio/gpiovar.h> 46 1.3 jmcneill 47 1.1 jmcneill #include <dev/acpi/acpireg.h> 48 1.1 jmcneill #include <dev/acpi/acpivar.h> 49 1.1 jmcneill #include <dev/acpi/acpi_gpio.h> 50 1.1 jmcneill 51 1.4 jmcneill #if NGPIO > 0 52 1.5 hannken 53 1.5 hannken #define _COMPONENT ACPI_RESOURCE_COMPONENT 54 1.5 hannken ACPI_MODULE_NAME ("acpi_gpio") 55 1.5 hannken 56 1.3 jmcneill struct acpi_gpio_address_space_context { 57 1.3 jmcneill ACPI_CONNECTION_INFO conn_info; /* must be first */ 58 1.3 jmcneill struct acpi_devnode *ad; 59 1.3 jmcneill }; 60 1.3 jmcneill 61 1.3 jmcneill static ACPI_STATUS 62 1.3 jmcneill acpi_gpio_address_space_init(ACPI_HANDLE region_hdl, UINT32 function, 63 1.3 jmcneill void *handler_ctx, void **region_ctx) 64 1.3 jmcneill { 65 1.3 jmcneill if (function == ACPI_REGION_DEACTIVATE) { 66 1.3 jmcneill *region_ctx = NULL; 67 1.3 jmcneill } else { 68 1.3 jmcneill *region_ctx = region_hdl; 69 1.3 jmcneill } 70 1.3 jmcneill return AE_OK; 71 1.3 jmcneill } 72 1.3 jmcneill 73 1.3 jmcneill static ACPI_STATUS 74 1.3 jmcneill acpi_gpio_address_space_handler(UINT32 function, 75 1.3 jmcneill ACPI_PHYSICAL_ADDRESS address, UINT32 bit_width, UINT64 *value, 76 1.3 jmcneill void *handler_ctx, void *region_ctx) 77 1.3 jmcneill { 78 1.3 jmcneill ACPI_OPERAND_OBJECT *region_obj = region_ctx; 79 1.3 jmcneill struct acpi_gpio_address_space_context *context = handler_ctx; 80 1.3 jmcneill ACPI_CONNECTION_INFO *conn_info = &context->conn_info; 81 1.3 jmcneill struct acpi_devnode *ad = context->ad; 82 1.3 jmcneill ACPI_RESOURCE *res; 83 1.3 jmcneill ACPI_STATUS rv; 84 1.3 jmcneill struct gpio_pinmap pinmap; 85 1.3 jmcneill int pins[1]; 86 1.3 jmcneill void *gpiop; 87 1.3 jmcneill int pin; 88 1.3 jmcneill 89 1.3 jmcneill if (region_obj->Region.Type != ACPI_TYPE_REGION) { 90 1.3 jmcneill return AE_OK; 91 1.3 jmcneill } 92 1.3 jmcneill 93 1.3 jmcneill if (ad->ad_gpiodev == NULL) { 94 1.3 jmcneill return AE_NO_HANDLER; 95 1.3 jmcneill } 96 1.3 jmcneill 97 1.3 jmcneill rv = AcpiBufferToResource(conn_info->Connection, 98 1.3 jmcneill conn_info->Length, &res); 99 1.3 jmcneill if (ACPI_FAILURE(rv)) { 100 1.3 jmcneill return rv; 101 1.3 jmcneill } 102 1.3 jmcneill 103 1.3 jmcneill if (res->Data.Gpio.PinTableLength != 1) { 104 1.3 jmcneill /* TODO */ 105 1.3 jmcneill aprint_debug_dev(ad->ad_gpiodev, 106 1.3 jmcneill "Pin table length %u not implemented\n", 107 1.3 jmcneill res->Data.Gpio.PinTableLength); 108 1.3 jmcneill rv = AE_NOT_IMPLEMENTED; 109 1.3 jmcneill goto done; 110 1.3 jmcneill } 111 1.3 jmcneill 112 1.3 jmcneill pin = ad->ad_gpio_translate(ad->ad_gpio_priv, 113 1.3 jmcneill &res->Data.Gpio, &gpiop); 114 1.3 jmcneill if (pin == -1) { 115 1.3 jmcneill /* Pin could not be translated. */ 116 1.3 jmcneill rv = AE_SUPPORT; 117 1.3 jmcneill goto done; 118 1.3 jmcneill } 119 1.3 jmcneill 120 1.3 jmcneill pinmap.pm_map = pins; 121 1.3 jmcneill if (gpio_pin_map(gpiop, pin, 1, &pinmap) != 0) { 122 1.3 jmcneill rv = AE_NOT_ACQUIRED; 123 1.3 jmcneill goto done; 124 1.3 jmcneill } 125 1.3 jmcneill if (function & ACPI_IO_MASK) { 126 1.3 jmcneill gpio_pin_write(gpiop, &pinmap, 0, *value & 1); 127 1.3 jmcneill } else { 128 1.3 jmcneill *value = gpio_pin_read(gpiop, &pinmap, 0); 129 1.3 jmcneill } 130 1.3 jmcneill gpio_pin_unmap(gpiop, &pinmap); 131 1.3 jmcneill 132 1.3 jmcneill done: 133 1.3 jmcneill ACPI_FREE(res); 134 1.3 jmcneill 135 1.3 jmcneill return rv; 136 1.3 jmcneill } 137 1.4 jmcneill #endif 138 1.3 jmcneill 139 1.3 jmcneill ACPI_STATUS 140 1.1 jmcneill acpi_gpio_register(struct acpi_devnode *ad, device_t dev, 141 1.2 jmcneill int (*translate)(void *, ACPI_RESOURCE_GPIO *, void **), void *priv) 142 1.1 jmcneill { 143 1.4 jmcneill #if NGPIO > 0 144 1.3 jmcneill struct acpi_gpio_address_space_context *context; 145 1.3 jmcneill ACPI_STATUS rv; 146 1.3 jmcneill 147 1.1 jmcneill if (ad->ad_gpiodev != NULL) { 148 1.1 jmcneill device_printf(dev, "%s already registered\n", 149 1.1 jmcneill device_xname(ad->ad_gpiodev)); 150 1.3 jmcneill return AE_ALREADY_EXISTS; 151 1.3 jmcneill } 152 1.3 jmcneill 153 1.3 jmcneill context = kmem_zalloc(sizeof(*context), KM_SLEEP); 154 1.3 jmcneill context->ad = ad; 155 1.3 jmcneill 156 1.3 jmcneill rv = AcpiInstallAddressSpaceHandler(ad->ad_handle, 157 1.3 jmcneill ACPI_ADR_SPACE_GPIO, 158 1.3 jmcneill acpi_gpio_address_space_handler, 159 1.3 jmcneill acpi_gpio_address_space_init, 160 1.3 jmcneill context); 161 1.3 jmcneill if (ACPI_FAILURE(rv)) { 162 1.3 jmcneill aprint_error_dev(dev, 163 1.3 jmcneill "couldn't install address space handler: %s", 164 1.3 jmcneill AcpiFormatException(rv)); 165 1.3 jmcneill return rv; 166 1.1 jmcneill } 167 1.1 jmcneill 168 1.1 jmcneill ad->ad_gpiodev = dev; 169 1.1 jmcneill ad->ad_gpio_translate = translate; 170 1.1 jmcneill ad->ad_gpio_priv = priv; 171 1.1 jmcneill 172 1.3 jmcneill return AE_OK; 173 1.4 jmcneill #else 174 1.4 jmcneill return AE_NOT_CONFIGURED; 175 1.4 jmcneill #endif 176 1.1 jmcneill } 177 1.1 jmcneill 178 1.1 jmcneill static ACPI_STATUS 179 1.1 jmcneill acpi_gpio_translate(ACPI_RESOURCE_GPIO *res, void **gpiop, int *pin) 180 1.1 jmcneill { 181 1.1 jmcneill struct acpi_devnode *ad, *gpioad = NULL; 182 1.1 jmcneill ACPI_HANDLE hdl; 183 1.1 jmcneill ACPI_RESOURCE_SOURCE *rs; 184 1.1 jmcneill ACPI_STATUS rv; 185 1.1 jmcneill int xpin; 186 1.1 jmcneill 187 1.1 jmcneill /* Find the device node providing the GPIO resource. */ 188 1.1 jmcneill rs = &res->ResourceSource; 189 1.1 jmcneill if (rs->StringPtr == NULL) { 190 1.1 jmcneill return AE_NOT_FOUND; 191 1.1 jmcneill } 192 1.1 jmcneill rv = AcpiGetHandle(NULL, rs->StringPtr, &hdl); 193 1.1 jmcneill if (ACPI_FAILURE(rv)) { 194 1.1 jmcneill return rv; 195 1.1 jmcneill } 196 1.1 jmcneill SIMPLEQ_FOREACH(ad, &acpi_softc->sc_head, ad_list) { 197 1.1 jmcneill if (ad->ad_handle == hdl) { 198 1.1 jmcneill gpioad = ad; 199 1.1 jmcneill break; 200 1.1 jmcneill } 201 1.1 jmcneill } 202 1.1 jmcneill if (gpioad == NULL) { 203 1.1 jmcneill /* No device node found. */ 204 1.1 jmcneill return AE_NOT_FOUND; 205 1.1 jmcneill } 206 1.1 jmcneill 207 1.1 jmcneill if (gpioad->ad_gpiodev == NULL) { 208 1.1 jmcneill /* No resource provider is registered. */ 209 1.1 jmcneill return AE_NO_HANDLER; 210 1.1 jmcneill } 211 1.1 jmcneill 212 1.1 jmcneill xpin = gpioad->ad_gpio_translate(gpioad->ad_gpio_priv, 213 1.2 jmcneill res, gpiop); 214 1.1 jmcneill if (xpin == -1) { 215 1.1 jmcneill /* Pin could not be translated. */ 216 1.3 jmcneill return AE_SUPPORT; 217 1.1 jmcneill } 218 1.1 jmcneill 219 1.1 jmcneill *pin = xpin; 220 1.1 jmcneill 221 1.1 jmcneill return AE_OK; 222 1.1 jmcneill } 223 1.1 jmcneill 224 1.1 jmcneill struct acpi_gpio_resource_context { 225 1.1 jmcneill u_int index; 226 1.1 jmcneill u_int conntype; 227 1.1 jmcneill u_int curindex; 228 1.1 jmcneill ACPI_RESOURCE_GPIO *res; 229 1.1 jmcneill }; 230 1.1 jmcneill 231 1.1 jmcneill static ACPI_STATUS 232 1.1 jmcneill acpi_gpio_parse(ACPI_RESOURCE *res, void *context) 233 1.1 jmcneill { 234 1.1 jmcneill struct acpi_gpio_resource_context *ctx = context; 235 1.1 jmcneill 236 1.1 jmcneill if (res->Type != ACPI_RESOURCE_TYPE_GPIO) { 237 1.1 jmcneill return AE_OK; 238 1.1 jmcneill } 239 1.1 jmcneill if (res->Data.Gpio.ConnectionType != ctx->conntype) { 240 1.1 jmcneill return AE_OK; 241 1.1 jmcneill } 242 1.1 jmcneill if (ctx->curindex == ctx->index) { 243 1.1 jmcneill ctx->res = &res->Data.Gpio; 244 1.1 jmcneill return AE_CTRL_TERMINATE; 245 1.1 jmcneill } 246 1.1 jmcneill ctx->curindex++; 247 1.1 jmcneill return AE_OK; 248 1.1 jmcneill 249 1.1 jmcneill } 250 1.1 jmcneill 251 1.1 jmcneill ACPI_STATUS 252 1.1 jmcneill acpi_gpio_get_int(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin, 253 1.1 jmcneill int *irqmode) 254 1.1 jmcneill { 255 1.1 jmcneill struct acpi_gpio_resource_context ctx = { 256 1.1 jmcneill .index = index, 257 1.1 jmcneill .conntype = ACPI_RESOURCE_GPIO_TYPE_INT, 258 1.1 jmcneill }; 259 1.1 jmcneill ACPI_RESOURCE_GPIO *gpio; 260 1.1 jmcneill ACPI_STATUS rv; 261 1.1 jmcneill 262 1.1 jmcneill rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx); 263 1.1 jmcneill if (ACPI_FAILURE(rv)) { 264 1.1 jmcneill return rv; 265 1.1 jmcneill } 266 1.2 jmcneill gpio = ctx.res; 267 1.1 jmcneill 268 1.2 jmcneill rv = acpi_gpio_translate(gpio, gpiop, pin); 269 1.1 jmcneill if (ACPI_FAILURE(rv)) { 270 1.1 jmcneill printf("%s: translate failed: %s\n", __func__, 271 1.1 jmcneill AcpiFormatException(rv)); 272 1.1 jmcneill return rv; 273 1.1 jmcneill } 274 1.1 jmcneill 275 1.1 jmcneill if (gpio->Triggering == ACPI_LEVEL_SENSITIVE) { 276 1.1 jmcneill *irqmode = gpio->Polarity == ACPI_ACTIVE_HIGH ? 277 1.1 jmcneill GPIO_INTR_HIGH_LEVEL : GPIO_INTR_LOW_LEVEL; 278 1.1 jmcneill } else { 279 1.1 jmcneill KASSERT(gpio->Triggering == ACPI_EDGE_SENSITIVE); 280 1.1 jmcneill if (gpio->Polarity == ACPI_ACTIVE_LOW) { 281 1.1 jmcneill *irqmode = GPIO_INTR_NEG_EDGE; 282 1.1 jmcneill } else if (gpio->Polarity == ACPI_ACTIVE_HIGH) { 283 1.1 jmcneill *irqmode = GPIO_INTR_POS_EDGE; 284 1.1 jmcneill } else { 285 1.1 jmcneill KASSERT(gpio->Polarity == ACPI_ACTIVE_BOTH); 286 1.1 jmcneill *irqmode = GPIO_INTR_DOUBLE_EDGE; 287 1.1 jmcneill } 288 1.1 jmcneill } 289 1.1 jmcneill 290 1.1 jmcneill return AE_OK; 291 1.1 jmcneill } 292 1.1 jmcneill 293 1.1 jmcneill ACPI_STATUS 294 1.2 jmcneill acpi_gpio_get_io(ACPI_HANDLE hdl, u_int index, void **gpiop, int *pin) 295 1.1 jmcneill { 296 1.1 jmcneill struct acpi_gpio_resource_context ctx = { 297 1.1 jmcneill .index = index, 298 1.1 jmcneill .conntype = ACPI_RESOURCE_GPIO_TYPE_INT, 299 1.1 jmcneill }; 300 1.1 jmcneill ACPI_STATUS rv; 301 1.1 jmcneill 302 1.1 jmcneill rv = AcpiWalkResources(hdl, "_CRS", acpi_gpio_parse, &ctx); 303 1.1 jmcneill if (ACPI_FAILURE(rv)) { 304 1.1 jmcneill return rv; 305 1.1 jmcneill } 306 1.1 jmcneill 307 1.2 jmcneill return acpi_gpio_translate(ctx.res, gpiop, pin); 308 1.1 jmcneill } 309