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