1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2024 Ahmad Khalifa <ahmadkhalifa570@gmail.com> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/bus.h> 30 #include <sys/kernel.h> 31 #include <sys/module.h> 32 #include <sys/gpio.h> 33 34 #include <contrib/dev/acpica/include/acpi.h> 35 #include <dev/acpica/acpivar.h> 36 37 #include <dev/gpio/gpiobusvar.h> 38 39 struct acpi_gpiobus_softc { 40 struct gpiobus_softc super_sc; 41 ACPI_CONNECTION_INFO handler_info; 42 }; 43 44 struct acpi_gpiobus_ctx { 45 struct gpiobus_softc *sc; 46 ACPI_HANDLE dev_handle; 47 }; 48 49 static uint32_t 50 acpi_gpiobus_convflags(ACPI_RESOURCE_GPIO *gpio_res) 51 { 52 uint32_t flags = 0; 53 54 /* Figure out pin flags */ 55 #ifdef NOT_YET 56 /* These are currently unused. */ 57 if (gpio_res->ConnectionType == ACPI_RESOURCE_GPIO_TYPE_INT) { 58 switch (gpio_res->Polarity) { 59 case ACPI_ACTIVE_HIGH: 60 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ? 61 GPIO_INTR_LEVEL_HIGH : GPIO_INTR_EDGE_RISING; 62 break; 63 case ACPI_ACTIVE_LOW: 64 flags = gpio_res->Triggering == ACPI_LEVEL_SENSITIVE ? 65 GPIO_INTR_LEVEL_LOW : GPIO_INTR_EDGE_FALLING; 66 break; 67 case ACPI_ACTIVE_BOTH: 68 flags = GPIO_INTR_EDGE_BOTH; 69 break; 70 } 71 72 if (gpio_res->Shareable == ACPI_SHARED) 73 flags |= GPIO_INTR_SHAREABLE; 74 } 75 #endif 76 switch (gpio_res->IoRestriction) { 77 case ACPI_IO_RESTRICT_INPUT: 78 flags |= GPIO_PIN_INPUT; 79 break; 80 case ACPI_IO_RESTRICT_OUTPUT: 81 flags |= GPIO_PIN_OUTPUT; 82 break; 83 } 84 85 switch (gpio_res->PinConfig) { 86 case ACPI_PIN_CONFIG_PULLUP: 87 flags |= GPIO_PIN_PULLUP; 88 break; 89 case ACPI_PIN_CONFIG_PULLDOWN: 90 flags |= GPIO_PIN_PULLDOWN; 91 break; 92 } 93 94 return (flags); 95 } 96 97 static ACPI_STATUS 98 acpi_gpiobus_enumerate_res(ACPI_RESOURCE *res, void *context) 99 { 100 ACPI_RESOURCE_GPIO *gpio_res = &res->Data.Gpio; 101 struct acpi_gpiobus_ctx *ctx = context; 102 struct gpiobus_softc *super_sc = ctx->sc; 103 ACPI_HANDLE handle; 104 uint32_t flags, i; 105 106 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) 107 return (AE_OK); 108 109 if (ACPI_FAILURE(AcpiGetHandle(ACPI_ROOT_OBJECT, 110 gpio_res->ResourceSource.StringPtr, &handle)) || 111 handle != ctx->dev_handle) 112 return (AE_OK); 113 114 if (__predict_false(gpio_res->PinTableLength > super_sc->sc_npins)) { 115 device_printf(super_sc->sc_busdev, 116 "invalid pin table length %hu, max: %d (bad ACPI tables?)\n", 117 gpio_res->PinTableLength, super_sc->sc_npins); 118 return (AE_LIMIT); 119 } 120 121 flags = acpi_gpiobus_convflags(gpio_res); 122 for (i = 0; i < gpio_res->PinTableLength; i++) { 123 UINT16 pin = gpio_res->PinTable[i]; 124 125 if (__predict_false(pin >= super_sc->sc_npins)) { 126 device_printf(super_sc->sc_busdev, 127 "invalid pin 0x%x, max: 0x%x (bad ACPI tables?)\n", 128 pin, super_sc->sc_npins - 1); 129 return (AE_LIMIT); 130 } 131 132 GPIO_PIN_SETFLAGS(super_sc->sc_dev, pin, flags & 133 ~GPIO_INTR_MASK); 134 } 135 136 return (AE_OK); 137 } 138 139 static ACPI_STATUS 140 acpi_gpiobus_enumerate(ACPI_HANDLE handle, UINT32 depth, void *context, 141 void **result) 142 { 143 UINT32 sta; 144 145 /* 146 * If no _STA method or if it failed, then assume that 147 * the device is present. 148 */ 149 if (!ACPI_FAILURE(acpi_GetInteger(handle, "_STA", &sta)) && 150 !ACPI_DEVICE_PRESENT(sta)) 151 return (AE_OK); 152 153 if (!acpi_has_hid(handle)) 154 return (AE_OK); 155 156 /* Look for GPIO resources */ 157 AcpiWalkResources(handle, "_CRS", acpi_gpiobus_enumerate_res, context); 158 159 return (AE_OK); 160 } 161 162 static ACPI_STATUS 163 acpi_gpiobus_space_handler(UINT32 function, ACPI_PHYSICAL_ADDRESS address, 164 UINT32 length, UINT64 *value, void *context, void *region_context) 165 { 166 ACPI_CONNECTION_INFO *info = context; 167 ACPI_RESOURCE_GPIO *gpio_res; 168 device_t controller; 169 ACPI_RESOURCE *res; 170 ACPI_STATUS status; 171 172 status = AcpiBufferToResource(info->Connection, info->Length, &res); 173 if (ACPI_FAILURE(status) || res->Type != ACPI_RESOURCE_TYPE_GPIO) 174 goto err; 175 176 gpio_res = &res->Data.Gpio; 177 controller = __containerof(info, struct acpi_gpiobus_softc, 178 handler_info)->super_sc.sc_dev; 179 180 switch (function) { 181 case ACPI_WRITE: 182 if (__predict_false( 183 gpio_res->IoRestriction == ACPI_IO_RESTRICT_INPUT)) 184 goto err; 185 186 for (int i = 0; i < length; i++) 187 if (GPIO_PIN_SET(controller, 188 gpio_res->PinTable[address + i], (*value & 1 << i) ? 189 GPIO_PIN_HIGH : GPIO_PIN_LOW) != 0) 190 goto err; 191 break; 192 case ACPI_READ: 193 if (__predict_false( 194 gpio_res->IoRestriction == ACPI_IO_RESTRICT_OUTPUT)) 195 goto err; 196 197 for (int i = 0; i < length; i++) { 198 uint32_t v; 199 200 if (GPIO_PIN_GET(controller, 201 gpio_res->PinTable[address + i], &v) != 0) 202 goto err; 203 *value |= v << i; 204 } 205 break; 206 default: 207 goto err; 208 } 209 210 ACPI_FREE(res); 211 return (AE_OK); 212 213 err: 214 ACPI_FREE(res); 215 return (AE_BAD_PARAMETER); 216 } 217 218 static int 219 acpi_gpiobus_probe(device_t dev) 220 { 221 device_t controller; 222 223 if (acpi_disabled("gpiobus")) 224 return (ENXIO); 225 226 controller = device_get_parent(dev); 227 if (controller == NULL) 228 return (ENXIO); 229 230 if (acpi_get_handle(controller) == NULL) 231 return (ENXIO); 232 233 device_set_desc(dev, "GPIO bus (ACPI-hinted)"); 234 return (BUS_PROBE_DEFAULT); 235 } 236 237 static int 238 acpi_gpiobus_attach(device_t dev) 239 { 240 struct acpi_gpiobus_softc *sc; 241 struct acpi_gpiobus_ctx ctx; 242 ACPI_HANDLE handle; 243 ACPI_STATUS status; 244 int err; 245 246 if ((err = gpiobus_attach(dev)) != 0) 247 return (err); 248 249 sc = device_get_softc(dev); 250 handle = acpi_get_handle(sc->super_sc.sc_dev); 251 if (handle == NULL) { 252 gpiobus_detach(dev); 253 return (ENXIO); 254 } 255 256 status = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO, 257 acpi_gpiobus_space_handler, NULL, &sc->handler_info); 258 259 if (ACPI_FAILURE(status)) { 260 device_printf(dev, 261 "Failed to install GPIO address space handler\n"); 262 gpiobus_detach(dev); 263 return (ENXIO); 264 } 265 266 ctx.dev_handle = handle; 267 ctx.sc = &sc->super_sc; 268 269 status = AcpiWalkNamespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, 270 ACPI_UINT32_MAX, acpi_gpiobus_enumerate, NULL, &ctx, NULL); 271 272 if (ACPI_FAILURE(status)) 273 device_printf(dev, "Failed to enumerate GPIO resources\n"); 274 275 return (0); 276 } 277 278 static int 279 acpi_gpiobus_detach(device_t dev) 280 { 281 struct gpiobus_softc *super_sc; 282 ACPI_STATUS status; 283 284 super_sc = device_get_softc(dev); 285 status = AcpiRemoveAddressSpaceHandler( 286 acpi_get_handle(super_sc->sc_dev), ACPI_ADR_SPACE_GPIO, 287 acpi_gpiobus_space_handler 288 ); 289 290 if (ACPI_FAILURE(status)) 291 device_printf(dev, 292 "Failed to remove GPIO address space handler\n"); 293 294 return (gpiobus_detach(dev)); 295 } 296 297 static device_method_t acpi_gpiobus_methods[] = { 298 /* Device interface */ 299 DEVMETHOD(device_probe, acpi_gpiobus_probe), 300 DEVMETHOD(device_attach, acpi_gpiobus_attach), 301 DEVMETHOD(device_detach, acpi_gpiobus_detach), 302 303 DEVMETHOD_END 304 }; 305 306 DEFINE_CLASS_1(gpiobus, acpi_gpiobus_driver, acpi_gpiobus_methods, 307 sizeof(struct acpi_gpiobus_softc), gpiobus_driver); 308 EARLY_DRIVER_MODULE(acpi_gpiobus, gpio, acpi_gpiobus_driver, NULL, NULL, 309 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 310 MODULE_VERSION(acpi_gpiobus, 1); 311 MODULE_DEPEND(acpi_gpiobus, acpi, 1, 1, 1); 312