plgpio_acpi.c revision 1.2
1/* $NetBSD: plgpio_acpi.c,v 1.2 2018/10/21 18:31:58 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2018 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#include <sys/cdefs.h> 33__KERNEL_RCSID(0, "$NetBSD: plgpio_acpi.c,v 1.2 2018/10/21 18:31:58 jmcneill Exp $"); 34 35#include <sys/param.h> 36#include <sys/bus.h> 37#include <sys/cpu.h> 38#include <sys/device.h> 39#include <sys/gpio.h> 40 41#include <dev/acpi/acpireg.h> 42#include <dev/acpi/acpivar.h> 43 44#include <dev/gpio/gpiovar.h> 45#include <dev/ic/pl061var.h> 46#include <dev/ic/pl061reg.h> 47 48struct plgpio_acpi_softc; 49 50struct plgpio_acpi_event { 51 struct plgpio_acpi_softc *ev_sc; 52 u_int ev_pin; 53 ACPI_HANDLE ev_method; 54 bool ev_method_evt; 55}; 56 57struct plgpio_acpi_softc { 58 struct plgpio_softc sc_base; 59 60 ACPI_HANDLE sc_handle; 61 uint32_t sc_aei_pins; /* bitmask */ 62 ACPI_RESOURCE_GPIO sc_aei_res[8]; 63 64 struct plgpio_acpi_event sc_event[8]; 65}; 66 67static int plgpio_acpi_match(device_t, cfdata_t, void *); 68static void plgpio_acpi_attach(device_t, device_t, void *); 69 70static void plgpio_acpi_init(struct plgpio_acpi_softc *); 71static void plgpio_acpi_notify(void *); 72static int plgpio_acpi_intr(void *); 73 74CFATTACH_DECL_NEW(plgpio_acpi, sizeof(struct plgpio_acpi_softc), plgpio_acpi_match, plgpio_acpi_attach, NULL, NULL); 75 76static const char * const compatible[] = { 77 "ARMH0061", 78 NULL 79}; 80 81static int 82plgpio_acpi_match(device_t parent, cfdata_t cf, void *aux) 83{ 84 struct acpi_attach_args *aa = aux; 85 86 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 87 return 0; 88 89 return acpi_match_hid(aa->aa_node->ad_devinfo, compatible); 90} 91 92static void 93plgpio_acpi_attach(device_t parent, device_t self, void *aux) 94{ 95 struct plgpio_acpi_softc * const asc = device_private(self); 96 struct plgpio_softc * const sc = &asc->sc_base; 97 struct acpi_attach_args *aa = aux; 98 struct acpi_resources res; 99 struct acpi_mem *mem; 100 struct acpi_irq *irq; 101 ACPI_STATUS rv; 102 int error; 103 void *ih; 104 105 sc->sc_dev = self; 106 asc->sc_handle = aa->aa_node->ad_handle; 107 108 rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS", 109 &res, &acpi_resource_parse_ops_default); 110 if (ACPI_FAILURE(rv)) 111 return; 112 113 mem = acpi_res_mem(&res, 0); 114 if (mem == NULL) { 115 aprint_error_dev(self, "couldn't find mem resource\n"); 116 goto done; 117 } 118 119 irq = acpi_res_irq(&res, 0); 120 if (mem == NULL) { 121 aprint_error_dev(self, "couldn't find irq resource\n"); 122 goto done; 123 } 124 125 sc->sc_dev = self; 126 sc->sc_bst = aa->aa_memt; 127 error = bus_space_map(sc->sc_bst, mem->ar_base, mem->ar_length, 0, &sc->sc_bsh); 128 if (error) { 129 aprint_error_dev(self, "couldn't map registers\n"); 130 return; 131 } 132 133 plgpio_attach(sc); 134 135 const int type = (irq->ar_type == ACPI_EDGE_SENSITIVE) ? IST_EDGE : IST_LEVEL; 136 ih = intr_establish(irq->ar_irq, IPL_VM, type, plgpio_acpi_intr, asc); 137 if (ih == NULL) 138 aprint_error_dev(self, "couldn't establish interrupt\n"); 139 140 plgpio_acpi_init(asc); 141 142done: 143 acpi_resource_cleanup(&res); 144} 145 146static ACPI_STATUS 147plgpio_acpi_resource_cb(ACPI_RESOURCE *res, void *ctx) 148{ 149 struct plgpio_acpi_softc * const asc = ctx; 150 ACPI_RESOURCE_GPIO *gpio; 151 UINT16 pin; 152 153 if (res->Type != ACPI_RESOURCE_TYPE_GPIO) 154 return AE_OK; 155 156 gpio = &res->Data.Gpio; 157 if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT || 158 gpio->PinTableLength != 1) 159 return AE_OK; 160 161 pin = gpio->PinTable[0]; 162 if (pin >= __arraycount(asc->sc_aei_res)) { 163 aprint_error_dev(asc->sc_base.sc_dev, "_AEI pin %u out of range\n", pin); 164 return AE_OK; 165 } 166 167 asc->sc_aei_pins |= __BIT(pin); 168 asc->sc_aei_res[pin] = *gpio; 169 170 return AE_OK; 171} 172 173static void 174plgpio_acpi_init(struct plgpio_acpi_softc *asc) 175{ 176 struct plgpio_softc * const sc = &asc->sc_base; 177 ACPI_RESOURCE_GPIO *gpio; 178 ACPI_HANDLE handle; 179 char namebuf[5]; 180 ACPI_STATUS rv; 181 uint32_t ibe, iev, is, ie; 182 int pin; 183 184 rv = AcpiWalkResources(asc->sc_handle, "_AEI", plgpio_acpi_resource_cb, asc); 185 if (ACPI_FAILURE(rv)) { 186 if (rv != AE_NOT_FOUND) 187 aprint_error_dev(asc->sc_base.sc_dev, "failed to parse _AEI: %s\n", 188 AcpiFormatException(rv)); 189 return; 190 } 191 192 if (!asc->sc_aei_pins) 193 return; 194 195 aprint_verbose_dev(asc->sc_base.sc_dev, "ACPI event pins: %#x\n", asc->sc_aei_pins); 196 197 sc->sc_reserved_mask = asc->sc_aei_pins; 198 199 /* 200 * For each event pin, find the corresponding event method (_Exx, _Lxx, or _EVT). 201 */ 202 for (pin = 0; pin < __arraycount(asc->sc_aei_res); pin++) { 203 if ((asc->sc_aei_pins & __BIT(pin)) == 0) 204 continue; 205 206 gpio = &asc->sc_aei_res[pin]; 207 208 const char trig = gpio->Triggering == ACPI_LEVEL_SENSITIVE ? 'L' : 'E'; 209 handle = NULL; 210 snprintf(namebuf, sizeof(namebuf), "_%c%02X", trig, pin); 211 if (ACPI_FAILURE(AcpiGetHandle(asc->sc_handle, namebuf, &handle))) { 212 (void)AcpiGetHandle(asc->sc_handle, "_EVT", &handle); 213 if (handle != NULL) 214 asc->sc_event[pin].ev_method_evt = true; 215 } 216 217 if (handle == NULL) 218 continue; 219 220 asc->sc_event[pin].ev_sc = asc; 221 asc->sc_event[pin].ev_pin = pin; 222 asc->sc_event[pin].ev_method = handle; 223 224 /* 225 * Configure and enable interrupts for this pin. 226 */ 227 228 ibe = PLGPIO_READ(sc, PL061_GPIOIBE_REG); 229 iev = PLGPIO_READ(sc, PL061_GPIOIEV_REG); 230 switch (gpio->Polarity) { 231 case ACPI_ACTIVE_HIGH: 232 ibe &= ~__BIT(pin); 233 iev |= __BIT(pin); 234 break; 235 case ACPI_ACTIVE_LOW: 236 ibe &= ~__BIT(pin); 237 iev &= ~__BIT(pin); 238 break; 239 case ACPI_ACTIVE_BOTH: 240 ibe |= __BIT(pin); 241 break; 242 } 243 PLGPIO_WRITE(sc, PL061_GPIOIBE_REG, ibe); 244 PLGPIO_WRITE(sc, PL061_GPIOIEV_REG, iev); 245 246 is = PLGPIO_READ(sc, PL061_GPIOIS_REG); 247 switch (gpio->Triggering) { 248 case ACPI_LEVEL_SENSITIVE: 249 is |= __BIT(pin); 250 break; 251 case ACPI_EDGE_SENSITIVE: 252 is &= ~__BIT(pin); 253 break; 254 } 255 PLGPIO_WRITE(sc, PL061_GPIOIS_REG, is); 256 257 delay(20); 258 259 PLGPIO_WRITE(sc, PL061_GPIOIC_REG, __BIT(pin)); 260 261 ie = PLGPIO_READ(sc, PL061_GPIOIE_REG); 262 ie |= __BIT(pin); 263 PLGPIO_WRITE(sc, PL061_GPIOIE_REG, ie); 264 } 265} 266 267static void 268plgpio_acpi_notify(void *priv) 269{ 270 struct plgpio_acpi_event * const ev = priv; 271 struct plgpio_acpi_softc * const asc = ev->ev_sc; 272 struct plgpio_softc * const sc = &asc->sc_base; 273 ACPI_STATUS rv; 274 275 if (ev->ev_method_evt) { 276 ACPI_OBJECT_LIST objs; 277 ACPI_OBJECT obj[1]; 278 objs.Count = 1; 279 objs.Pointer = obj; 280 obj[0].Type = ACPI_TYPE_INTEGER; 281 obj[0].Integer.Value = ev->ev_pin; 282 rv = AcpiEvaluateObject(ev->ev_method, NULL, &objs, NULL); 283 } else { 284 rv = AcpiEvaluateObject(ev->ev_method, NULL, NULL, NULL); 285 } 286 287 if (ACPI_FAILURE(rv)) 288 device_printf(sc->sc_dev, "failed to handle %s event: %s\n", 289 acpi_name(ev->ev_method), AcpiFormatException(rv)); 290} 291 292static int 293plgpio_acpi_intr(void *priv) 294{ 295 struct plgpio_acpi_softc * const asc = priv; 296 struct plgpio_softc * const sc = &asc->sc_base; 297 uint32_t mis; 298 int bit; 299 300 mis = PLGPIO_READ(sc, PL061_GPIOMIS_REG); 301 PLGPIO_WRITE(sc, PL061_GPIOIC_REG, mis); 302 303 while ((bit = __builtin_ffs(mis)) != 0) { 304 const int pin = bit - 1; 305 struct plgpio_acpi_event * const ev = &asc->sc_event[pin]; 306 307 KASSERT(ev->ev_method != NULL); 308 309 AcpiOsExecute(OSL_NOTIFY_HANDLER, plgpio_acpi_notify, ev); 310 311 mis &= ~__BIT(pin); 312 } 313 314 return 1; 315} 316