xref: /dragonfly/sys/bus/gpio/gpio_acpi/gpio_acpi.c (revision e2164e291b6b73561de02c50274ff8cc7fc528fe)
1 /*
2  * Copyright (c) 2016 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Imre Vadász <imre@vdsz.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 
35 /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/kernel.h>
40 #include <sys/malloc.h>
41 #include <sys/module.h>
42 #include <sys/errno.h>
43 #include <sys/lock.h>
44 #include <sys/bus.h>
45 
46 #include "opt_acpi.h"
47 #include "acpi.h"
48 #include <dev/acpica/acpivar.h>
49 
50 #include "gpio_acpivar.h"
51 
52 #include "gpio_if.h"
53 
54 struct acpi_event_info {
55           device_t dev;
56           u_int pin;
57           void *cookie;
58           int trigger;
59 };
60 
61 struct acpi_gpio_handler_data {
62           struct acpi_connection_info info;
63           device_t dev;
64 };
65 
66 struct gpio_acpi_softc {
67           device_t dev;
68           device_t parent;
69           struct acpi_event_info *infos;
70           int num_aei;
71           struct acpi_gpio_handler_data space_handler_data;
72 };
73 
74 static int          gpio_acpi_probe(device_t dev);
75 static int          gpio_acpi_attach(device_t dev);
76 static int          gpio_acpi_detach(device_t dev);
77 
78 static BOOLEAN      gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio);
79 static void         **gpioio_alloc_pins(device_t dev, device_t provider,
80                         ACPI_RESOURCE_GPIO *gpio, uint16_t idx, uint16_t length,
81                         void **buf);
82 
83 /* GPIO Address Space Handler */
84 static void                   gpio_acpi_install_address_space_handler(
85                                   struct gpio_acpi_softc *sc);
86 static void                   gpio_acpi_remove_address_space_handler(
87                                   struct gpio_acpi_softc *sc);
88 static ACPI_STATUS  gpio_acpi_space_handler(UINT32 Function,
89                                   ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
90                                   UINT64 *Value, void *HandlerContext,
91                                   void *RegionContext);
92 
93 /* ACPI Event Interrupts */
94 static void         gpio_acpi_do_map_aei(struct gpio_acpi_softc *sc,
95                         ACPI_RESOURCE_GPIO *gpio);
96 static void         gpio_acpi_map_aei(struct gpio_acpi_softc *sc);
97 static void         gpio_acpi_unmap_aei(struct gpio_acpi_softc *sc);
98 static void         gpio_acpi_handle_event(void *Context);
99 static void         gpio_acpi_aei_handler(void *arg);
100 
101 /* Sanity-Check for GpioInt resources */
102 static BOOLEAN
gpio_acpi_check_gpioint(device_t dev,ACPI_RESOURCE_GPIO * gpio)103 gpio_acpi_check_gpioint(device_t dev, ACPI_RESOURCE_GPIO *gpio)
104 {
105           if (gpio->PinTableLength != 1) {
106                     device_printf(dev,
107                         "Unexepcted GpioInt resource PinTableLength %d\n",
108                         gpio->PinTableLength);
109                     return (FALSE);
110           }
111           switch (gpio->Triggering) {
112           case ACPI_LEVEL_SENSITIVE:
113           case ACPI_EDGE_SENSITIVE:
114                     break;
115           default:
116                     device_printf(dev, "Invalid GpioInt resource Triggering: %d\n",
117                         gpio->Triggering);
118                     return (FALSE);
119           }
120           switch (gpio->Polarity) {
121           case ACPI_ACTIVE_HIGH:
122           case ACPI_ACTIVE_LOW:
123           case ACPI_ACTIVE_BOTH:
124                     break;
125           default:
126                     device_printf(dev, "Invalid GpioInt resource Polarity: %d\n",
127                         gpio->Polarity);
128                     return (FALSE);
129           }
130 
131           return (TRUE);
132 }
133 
134 /*
135  * GpioIo ACPI resource handling
136  */
137 
138 static void **
gpioio_alloc_pins(device_t dev,device_t provider,ACPI_RESOURCE_GPIO * gpio,uint16_t idx,uint16_t length,void ** buf)139 gpioio_alloc_pins(device_t dev, device_t provider, ACPI_RESOURCE_GPIO *gpio,
140     uint16_t idx, uint16_t length, void **buf)
141 {
142           void **pins;
143           int flags, i, j;
144 
145           if (buf == NULL) {
146                     pins = kmalloc(sizeof(*pins) * length, M_DEVBUF,
147                         M_WAITOK | M_ZERO);
148           } else {
149                     pins = buf;
150           }
151 
152           if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) {
153                     flags = (1U << 0);
154           } else if (gpio->IoRestriction ==
155               ACPI_IO_RESTRICT_OUTPUT) {
156                     flags = (1U << 1);
157           } else {
158                     flags = (1U << 0) | (1U << 1);
159           }
160           for (i = 0; i < length; i++) {
161                     if (GPIO_ALLOC_IO_PIN(provider, gpio->PinTable[idx + i], flags,
162                         &pins[i]) != 0) {
163                               device_printf(dev, "Failed to alloc GpioIo pin %u on "
164                                   "ResourceSource \"%s\"\n", gpio->PinTable[idx + i],
165                                   gpio->ResourceSource.StringPtr);
166                               /* Release already alloc-ed pins */
167                               for (j = 0; j < i; j++)
168                                         GPIO_RELEASE_IO_PIN(provider, pins[j]);
169                               goto err;
170                     }
171           }
172 
173           return (pins);
174 
175 err:
176           if (buf == NULL)
177                     kfree(pins, M_DEVBUF);
178           return (NULL);
179 }
180 
181 /*
182  * GPIO Address space handler
183  */
184 
185 static void
gpio_acpi_install_address_space_handler(struct gpio_acpi_softc * sc)186 gpio_acpi_install_address_space_handler(struct gpio_acpi_softc *sc)
187 {
188           struct acpi_gpio_handler_data *data = &sc->space_handler_data;
189           ACPI_HANDLE handle;
190           ACPI_STATUS s;
191 
192           handle = acpi_get_handle(sc->parent);
193           data->dev = sc->parent;
194           s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
195               &gpio_acpi_space_handler, NULL, data);
196           if (ACPI_FAILURE(s)) {
197                     device_printf(sc->dev,
198                         "Failed to install GPIO Address Space Handler in ACPI\n");
199           }
200 }
201 
202 static void
gpio_acpi_remove_address_space_handler(struct gpio_acpi_softc * sc)203 gpio_acpi_remove_address_space_handler(struct gpio_acpi_softc *sc)
204 {
205           ACPI_HANDLE handle;
206           ACPI_STATUS s;
207 
208           handle = acpi_get_handle(sc->parent);
209           s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
210               &gpio_acpi_space_handler);
211           if (ACPI_FAILURE(s)) {
212                     device_printf(sc->dev,
213                         "Failed to remove GPIO Address Space Handler from ACPI\n");
214           }
215 }
216 
217 static ACPI_STATUS
gpio_acpi_space_handler(UINT32 Function,ACPI_PHYSICAL_ADDRESS Address,UINT32 BitWidth,UINT64 * Value,void * HandlerContext,void * RegionContext)218 gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
219     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
220 {
221           struct acpi_gpio_handler_data *data = HandlerContext;
222           device_t dev = data->dev;
223           struct acpi_connection_info *info = &data->info;
224           struct acpi_resource_gpio *gpio;
225           UINT64 val;
226           UINT8 action = Function & ACPI_IO_MASK;
227           ACPI_RESOURCE *Resource;
228           ACPI_STATUS s = AE_OK;
229           void **pins;
230           int i;
231 
232           if (Value == NULL)
233                     return (AE_BAD_PARAMETER);
234 
235           /* XXX probably unnecessary */
236           if (BitWidth == 0 || BitWidth > 64)
237                     return (AE_BAD_PARAMETER);
238 
239           s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
240           if (ACPI_FAILURE(s)) {
241                     device_printf(dev, "AcpiBufferToResource failed\n");
242                     return (s);
243           }
244           if (Resource->Type != ACPI_RESOURCE_TYPE_GPIO) {
245                     device_printf(dev, "Resource->Type is wrong\n");
246                     s = AE_BAD_PARAMETER;
247                     goto err;
248           }
249           gpio = &Resource->Data.Gpio;
250           if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) {
251                     device_printf(dev, "gpio->ConnectionType is wrong\n");
252                     s = AE_BAD_PARAMETER;
253                     goto err;
254           }
255 
256           if (Address + BitWidth > gpio->PinTableLength) {
257                     device_printf(dev, "Address + BitWidth out of range\n");
258                     s = AE_BAD_ADDRESS;
259                     goto err;
260           }
261 
262           if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT &&
263               action == ACPI_READ) {
264                     device_printf(dev,
265                         "IoRestriction is output only, but action is ACPI_READ\n");
266                     s = AE_BAD_PARAMETER;
267                     goto err;
268           }
269           if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT &&
270               action == ACPI_WRITE) {
271                     device_printf(dev,
272                         "IoRestriction is input only, but action is ACPI_WRITE\n");
273                     s = AE_BAD_PARAMETER;
274                     goto err;
275           }
276 
277           /* Make sure we can access all pins, before trying actual read/write */
278           pins = gpioio_alloc_pins(dev, dev, gpio, Address, BitWidth, NULL);
279           if (pins == NULL) {
280                     s = AE_BAD_PARAMETER;
281                     goto err;
282           }
283 
284           if (action == ACPI_READ) {
285                     *Value = 0;
286                     for (i = 0; i < BitWidth; i++) {
287                               val = GPIO_READ_PIN(dev, pins[i]);
288                               *Value |= val << i;
289                     }
290           } else {
291                     for (i = 0; i < BitWidth; i++) {
292                               GPIO_WRITE_PIN(dev, pins[i],
293                                   (*Value & (1ULL << i)) ? 1 : 0);
294                     }
295           }
296           for (i = 0; i < BitWidth; i++)
297                     GPIO_RELEASE_IO_PIN(dev, pins[i]);
298           kfree(pins, M_DEVBUF);
299 
300 err:
301           ACPI_FREE(Resource);
302           return (s);
303 }
304 
305 /*
306  * ACPI Event Interrupts
307  */
308 
309 static void
gpio_acpi_handle_event(void * Context)310 gpio_acpi_handle_event(void *Context)
311 {
312           struct acpi_event_info *info = (struct acpi_event_info *)Context;
313           ACPI_HANDLE handle, h;
314           ACPI_STATUS s;
315           char buf[5];
316 
317           handle = acpi_get_handle(device_get_parent(info->dev));
318           if (info->trigger == ACPI_EDGE_SENSITIVE) {
319                     ksnprintf(buf, sizeof(buf), "_E%02X", info->pin);
320           } else {
321                     ksnprintf(buf, sizeof(buf), "_L%02X", info->pin);
322           }
323           if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) {
324                     s = AcpiEvaluateObject(handle, buf, NULL, NULL);
325                     if (ACPI_FAILURE(s))
326                               device_printf(info->dev, "evaluating %s failed\n", buf);
327           } else {
328                     ACPI_OBJECT_LIST arglist;
329                     ACPI_OBJECT arg;
330 
331                     arglist.Pointer = &arg;
332                     arglist.Count = 1;
333                     arg.Type = ACPI_TYPE_INTEGER;
334                     arg.Integer.Value = info->pin;
335                     s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL);
336                     if (ACPI_FAILURE(s))
337                               device_printf(info->dev, "evaluating _EVT failed\n");
338           }
339 }
340 
341 static void
gpio_acpi_aei_handler(void * arg)342 gpio_acpi_aei_handler(void *arg)
343 {
344           struct acpi_event_info *info = (struct acpi_event_info *)arg;
345           ACPI_STATUS s;
346 
347           s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg);
348           if (ACPI_FAILURE(s)) {
349                     device_printf(info->dev,
350                         "AcpiOsExecute for Acpi Event handler failed\n");
351           }
352 }
353 
354 static void
gpio_acpi_do_map_aei(struct gpio_acpi_softc * sc,ACPI_RESOURCE_GPIO * gpio)355 gpio_acpi_do_map_aei(struct gpio_acpi_softc *sc, ACPI_RESOURCE_GPIO *gpio)
356 {
357           struct acpi_event_info *info = &sc->infos[sc->num_aei];
358           uint16_t pin;
359           void *cookie;
360 
361           if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
362                     device_printf(sc->dev, "Unexpected gpio type %d\n",
363                         gpio->ConnectionType);
364                     return;
365           }
366 
367           /* sc->dev is correct here, since it's only used for device_printf */
368           if (!gpio_acpi_check_gpioint(sc->dev, gpio))
369                     return;
370 
371           pin = gpio->PinTable[0];
372 
373           if (GPIO_ALLOC_INTR(sc->parent, pin, gpio->Triggering, gpio->Polarity,
374               gpio->PinConfig, &cookie) != 0) {
375                     device_printf(sc->dev,
376                         "Failed to allocate AEI interrupt on pin %d\n", pin);
377                     return;
378           }
379 
380           info->dev = sc->dev;
381           info->pin = pin;
382           info->trigger = gpio->Triggering;
383           info->cookie = cookie;
384 
385           GPIO_SETUP_INTR(sc->parent, cookie, info, gpio_acpi_aei_handler);
386           sc->num_aei++;
387 }
388 
389 /* Map ACPI events */
390 static void
gpio_acpi_map_aei(struct gpio_acpi_softc * sc)391 gpio_acpi_map_aei(struct gpio_acpi_softc *sc)
392 {
393           ACPI_HANDLE handle = acpi_get_handle(sc->parent);
394           ACPI_RESOURCE_GPIO *gpio;
395           ACPI_RESOURCE *res, *end;
396           ACPI_BUFFER b;
397           ACPI_STATUS s;
398           int n;
399 
400           sc->infos = NULL;
401           sc->num_aei = 0;
402 
403           b.Pointer = NULL;
404           b.Length = ACPI_ALLOCATE_BUFFER;
405           s = AcpiGetEventResources(handle, &b);
406           if (ACPI_FAILURE(s))
407                     return;
408 
409           end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length);
410           /* Count Gpio connections */
411           n = 0;
412           for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
413               res = ACPI_NEXT_RESOURCE(res)) {
414                     if (res->Type == ACPI_RESOURCE_TYPE_END_TAG) {
415                               break;
416                     } else if (res->Type == ACPI_RESOURCE_TYPE_GPIO) {
417                               n++;
418                     } else {
419                               device_printf(sc->dev, "Unexpected resource type %d\n",
420                                   res->Type);
421                     }
422           }
423           if (n <= 0) {
424                     AcpiOsFree(b.Pointer);
425                     return;
426           }
427           sc->infos = kmalloc(n*sizeof(*sc->infos), M_DEVBUF, M_WAITOK | M_ZERO);
428           for (res = (ACPI_RESOURCE *)b.Pointer; res < end && sc->num_aei < n;
429               res = ACPI_NEXT_RESOURCE(res)) {
430                     if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
431                               break;
432                     if (res->Type == ACPI_RESOURCE_TYPE_GPIO) {
433                               gpio = (ACPI_RESOURCE_GPIO *)&res->Data;
434                               gpio_acpi_do_map_aei(sc, gpio);
435                     }
436           }
437           AcpiOsFree(b.Pointer);
438 }
439 
440 /*  Unmap ACPI events */
441 static void
gpio_acpi_unmap_aei(struct gpio_acpi_softc * sc)442 gpio_acpi_unmap_aei(struct gpio_acpi_softc *sc)
443 {
444           struct acpi_event_info *info;
445           int i;
446 
447           for (i = 0; i < sc->num_aei; i++) {
448                     info = &sc->infos[i];
449                     KKASSERT(info->dev != NULL);
450                     GPIO_TEARDOWN_INTR(sc->parent, info->cookie);
451                     GPIO_FREE_INTR(sc->parent, info->cookie);
452                     /* XXX Wait until ACPI Event handler has finished */
453                     memset(info, 0, sizeof(*info));
454           }
455           kfree(sc->infos, M_DEVBUF);
456           sc->infos = NULL;
457           sc->num_aei = 0;
458 }
459 
460 static int
gpio_acpi_probe(device_t dev)461 gpio_acpi_probe(device_t dev)
462 {
463           if (acpi_get_handle(device_get_parent(dev)) == NULL)
464                     return (ENXIO);
465 
466           device_set_desc(dev, "ACPI GeneralPurposeIo backend");
467 
468           return (0);
469 }
470 
471 static int
gpio_acpi_attach(device_t dev)472 gpio_acpi_attach(device_t dev)
473 {
474           struct gpio_acpi_softc *sc = device_get_softc(dev);
475 
476           sc->dev = dev;
477           sc->parent = device_get_parent(dev);
478 
479           gpio_acpi_install_address_space_handler(sc);
480 
481           gpio_acpi_map_aei(sc);
482 
483           return (0);
484 }
485 
486 static int
gpio_acpi_detach(device_t dev)487 gpio_acpi_detach(device_t dev)
488 {
489           struct gpio_acpi_softc *sc = device_get_softc(dev);
490 
491           if (sc->infos != NULL)
492                     gpio_acpi_unmap_aei(sc);
493 
494           gpio_acpi_remove_address_space_handler(sc);
495 
496           return (0);
497 }
498 
499 
500 static device_method_t gpio_acpi_methods[] = {
501           /* Device interface */
502           DEVMETHOD(device_probe, gpio_acpi_probe),
503           DEVMETHOD(device_attach, gpio_acpi_attach),
504           DEVMETHOD(device_detach, gpio_acpi_detach),
505 
506           DEVMETHOD_END
507 };
508 
509 static driver_t gpio_acpi_driver = {
510           "gpio_acpi",
511           gpio_acpi_methods,
512           sizeof(struct gpio_acpi_softc)
513 };
514 
515 static devclass_t gpio_acpi_devclass;
516 
517 DRIVER_MODULE(gpio_acpi, gpio_intel, gpio_acpi_driver, gpio_acpi_devclass,
518     NULL, NULL);
519 MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1);
520 MODULE_VERSION(gpio_acpi, 1);
521