gpio_intel: Support integrated GPIO controllers of the Cherry Trail SoC.
[dragonfly.git] / sys / bus / gpio / gpio_acpi / gpio_acpi.c
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 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/module.h>
39 #include <sys/errno.h>
40 #include <sys/lock.h>
41 #include <sys/mutex.h>
42 #include <sys/bus.h>
43
44 #include "acpi.h"
45 #include "opt_acpi.h"
46 #include <dev/acpica/acpivar.h>
47
48 #include "gpio_acpivar.h"
49
50 #include "gpio_if.h"
51
52 struct acpi_event_info {
53         device_t dev;
54         uint16_t pin;
55         int trigger;
56 };
57
58 struct acpi_gpio_handler_data {
59         struct acpi_connection_info info;
60         device_t dev;
61 };
62
63 struct gpio_acpi_data {
64         struct acpi_event_info *infos;
65         int num_aei;
66         struct acpi_gpio_handler_data space_handler_data;
67 };
68
69 /* GPIO Address Space Handler */
70 static void             gpio_acpi_install_address_space_handler(device_t dev,
71                             struct acpi_gpio_handler_data *data);
72 static void             gpio_acpi_remove_address_space_handler(device_t dev,
73                             struct acpi_gpio_handler_data *data);
74 static ACPI_STATUS      gpio_acpi_space_handler(UINT32 Function,
75                             ACPI_PHYSICAL_ADDRESS Address, UINT32 BitWidth,
76                             UINT64 *Value, void *HandlerContext,
77                             void *RegionContext);
78
79 /* ACPI Event Interrupts */
80 static void     gpio_acpi_do_map_aei(device_t dev,
81                     struct acpi_event_info *info, ACPI_RESOURCE_GPIO *gpio);
82 static void     *gpio_acpi_map_aei(device_t dev, int *num_aei);
83 static void     gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data);
84 static void     gpio_acpi_handle_event(void *Context);
85 static void     gpio_acpi_aei_handler(void *arg);
86
87 /* Register GPIO device with ACPICA for ACPI-5.0 GPIO functionality */
88 void *
89 gpio_acpi_register(device_t dev)
90 {
91         struct gpio_acpi_data *data;
92
93         data = kmalloc(sizeof(*data), M_DEVBUF, M_WAITOK | M_ZERO);
94
95         gpio_acpi_install_address_space_handler(dev,
96             &data->space_handler_data);
97
98         data->infos = gpio_acpi_map_aei(dev, &data->num_aei);
99
100         return data;
101 }
102
103 void
104 gpio_acpi_unregister(device_t dev, void *arg)
105 {
106         struct gpio_acpi_data *data = (struct gpio_acpi_data *)arg;
107
108         if (data->infos != NULL)
109                 gpio_acpi_unmap_aei(dev, data);
110
111         gpio_acpi_remove_address_space_handler(dev, &data->space_handler_data);
112
113         kfree(data, M_DEVBUF);
114 }
115
116 /*
117  * GPIO Address space handler
118  */
119
120 static void
121 gpio_acpi_install_address_space_handler(device_t dev,
122     struct acpi_gpio_handler_data *data)
123 {
124         ACPI_HANDLE handle;
125         ACPI_STATUS s;
126
127         handle = acpi_get_handle(dev);
128         data->dev = dev;
129         /*
130          * XXX Should use the Address Space Setup Handler to check and
131          *     reserve the GPIO pins, to avoid checking on each READ/WRITE
132          *     call into the Adress Space Handler.
133          */
134         s = AcpiInstallAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
135             &gpio_acpi_space_handler, NULL, data);
136         if (!ACPI_SUCCESS(s)) {
137                 device_printf(dev,
138                     "Failed to install GPIO Address Space Handler in ACPI\n");
139         }
140 }
141
142 static void
143 gpio_acpi_remove_address_space_handler(device_t dev,
144     struct acpi_gpio_handler_data *data)
145 {
146         ACPI_HANDLE handle;
147         ACPI_STATUS s;
148
149         handle = acpi_get_handle(dev);
150         s = AcpiRemoveAddressSpaceHandler(handle, ACPI_ADR_SPACE_GPIO,
151             &gpio_acpi_space_handler);
152         if (!ACPI_SUCCESS(s)) {
153                 device_printf(dev,
154                     "Failed to remove GPIO Address Space Handler from ACPI\n");
155         }
156 }
157
158 static ACPI_STATUS
159 gpio_acpi_space_handler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address,
160     UINT32 BitWidth, UINT64 *Value, void *HandlerContext, void *RegionContext)
161 {
162         struct acpi_gpio_handler_data *data = HandlerContext;
163         device_t dev = data->dev;
164         struct acpi_connection_info *info = &data->info;
165         struct acpi_resource_gpio *gpio;
166         UINT64 val;
167         UINT8 action = Function & ACPI_IO_MASK;
168         ACPI_RESOURCE *Resource;
169         ACPI_STATUS s = AE_OK;
170         int i;
171
172         s = AcpiBufferToResource(info->Connection, info->Length, &Resource);
173         if (ACPI_FAILURE(s))
174                 return (s);
175         if (Value == NULL || Resource->Type != ACPI_RESOURCE_TYPE_GPIO) {
176                 s = AE_BAD_PARAMETER;
177                 goto err;
178         }
179
180         gpio = &Resource->Data.Gpio;
181         if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_IO) {
182                 s = AE_BAD_PARAMETER;
183                 goto err;
184         }
185
186         /* XXX probably unnecessary */
187         if (BitWidth == 0 || BitWidth > 64) {
188                 s = AE_BAD_PARAMETER;
189                 goto err;
190         }
191
192         if (Address + BitWidth > gpio->PinTableLength) {
193                 s = AE_BAD_ADDRESS;
194                 goto err;
195         }
196
197         if (action == ACPI_READ) {
198                 if (gpio->IoRestriction == ACPI_IO_RESTRICT_OUTPUT) {
199                         s = AE_BAD_PARAMETER;
200                         goto err;
201                 }
202                 *Value = 0;
203                 for (i = 0; i < BitWidth; i++) {
204                         val = GPIO_READ_PIN(dev, gpio->PinTable[Address + i]);
205                         *Value |= val << i;
206                 }
207         } else {
208                 if (gpio->IoRestriction == ACPI_IO_RESTRICT_INPUT) {
209                         s = AE_BAD_PARAMETER;
210                         goto err;
211                 }
212                 for (i = 0; i < BitWidth; i++) {
213                         GPIO_WRITE_PIN(dev, gpio->PinTable[Address + i],
214                             *Value & (1ULL << i) ? 1 : 0);
215                 }
216         }
217
218 err:
219         ACPI_FREE(Resource);
220
221         return (s);
222 }
223
224 /*
225  * ACPI Event Interrupts
226  */
227
228 static void
229 gpio_acpi_handle_event(void *Context)
230 {
231         struct acpi_event_info *info = (struct acpi_event_info *)Context;
232         ACPI_HANDLE handle, h;
233         ACPI_STATUS s;
234         char buf[5];
235
236         handle = acpi_get_handle(info->dev);
237         if (info->trigger == ACPI_EDGE_SENSITIVE) {
238                 ksnprintf(buf, sizeof(buf), "_E%02X", info->pin);
239         } else {
240                 ksnprintf(buf, sizeof(buf), "_L%02X", info->pin);
241         }
242         if (info->pin <= 255 && ACPI_SUCCESS(AcpiGetHandle(handle, buf, &h))) {
243                 s = AcpiEvaluateObject(handle, buf, NULL, NULL);
244                 if (!ACPI_SUCCESS(s)) {
245                         device_printf(info->dev, "evaluating %s failed\n", buf);
246                 }
247         } else {
248                 ACPI_OBJECT_LIST arglist;
249                 ACPI_OBJECT arg;
250
251                 arglist.Pointer = &arg;
252                 arglist.Count = 1;
253                 arg.Type = ACPI_TYPE_INTEGER;
254                 arg.Integer.Value = info->pin;
255                 s = AcpiEvaluateObject(handle, "_EVT", &arglist, NULL);
256                 if (!ACPI_SUCCESS(s)) {
257                         device_printf(info->dev, "evaluating _EVT failed\n");
258                 }
259         }
260 }
261
262 static void
263 gpio_acpi_aei_handler(void *arg)
264 {
265         struct acpi_event_info *info = (struct acpi_event_info *)arg;
266         ACPI_STATUS s;
267
268         s = AcpiOsExecute(OSL_GPE_HANDLER, gpio_acpi_handle_event, arg);
269         if (!ACPI_SUCCESS(s)) {
270                 device_printf(info->dev,
271                     "AcpiOsExecute for Acpi Event handler failed\n");
272         }
273 }
274
275 static void
276 gpio_acpi_do_map_aei(device_t dev, struct acpi_event_info *info,
277     ACPI_RESOURCE_GPIO *gpio)
278 {
279         uint16_t pin;
280
281         if (gpio->ConnectionType != ACPI_RESOURCE_GPIO_TYPE_INT) {
282                 device_printf(dev, "Unexpected gpio type %d\n",
283                     gpio->ConnectionType);
284                 return;
285         }
286         if (gpio->PinTableLength != 1) {
287                 device_printf(dev,
288                     "Unexepcted gpio PinTableLength expected 1 got %d\n",
289                     gpio->PinTableLength);
290                 return;
291         }
292         pin = gpio->PinTable[0];
293
294         /* sanity checks */
295         switch (gpio->Triggering) {
296         case ACPI_LEVEL_SENSITIVE:
297         case ACPI_EDGE_SENSITIVE:
298                 break;
299         default:
300                 device_printf(dev, "Invalid Trigger: %d\n", gpio->Triggering);
301                 return;
302         }
303         switch (gpio->Polarity) {
304         case ACPI_ACTIVE_HIGH:
305         case ACPI_ACTIVE_LOW:
306         case ACPI_ACTIVE_BOTH:
307                 break;
308         default:
309                 device_printf(dev, "Invalid Polarity: %d\n", gpio->Polarity);
310                 return;
311         }
312
313         info->dev = dev;
314         info->pin = pin;
315         info->trigger = gpio->Triggering;
316
317         if (GPIO_ALLOC_INTR(dev, pin, gpio->Triggering, gpio->Polarity,
318             gpio->PinConfig, info, gpio_acpi_aei_handler) != 0) {
319                 device_printf(dev,
320                     "Failed to map AEI interrupt on pin %d\n", pin);
321                 memset(info, 0, sizeof(*info));
322         }
323 }
324
325 /* Map ACPI events */
326 static void *
327 gpio_acpi_map_aei(device_t dev, int *num_aei)
328 {
329         ACPI_HANDLE handle = acpi_get_handle(dev);
330         ACPI_RESOURCE_GPIO *gpio;
331         ACPI_RESOURCE *res, *end;
332         ACPI_BUFFER b;
333         ACPI_STATUS s;
334         struct acpi_event_info *infos = NULL;
335         int n;
336
337         *num_aei = 0;
338
339         b.Pointer = NULL;
340         b.Length = ACPI_ALLOCATE_BUFFER;
341         s = AcpiGetEventResources(handle, &b);
342         if (ACPI_SUCCESS(s)) {
343                 end = (ACPI_RESOURCE *)((char *)b.Pointer + b.Length);
344                 /* Count Gpio connections */
345                 n = 0;
346                 for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
347                     res = ACPI_NEXT_RESOURCE(res)) {
348                         if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
349                                 break;
350                         switch (res->Type) {
351                         case ACPI_RESOURCE_TYPE_GPIO:
352                                 n++;
353                                 break;
354                         default:
355                                 break;
356                         }
357                 }
358                 if (n <= 0) {
359                         AcpiOsFree(b.Pointer);
360                         return (infos);
361                 }
362                 infos = kmalloc(n*sizeof(*infos), M_DEVBUF, M_WAITOK | M_ZERO);
363                 *num_aei = n;
364                 n = 0;
365                 for (res = (ACPI_RESOURCE *)b.Pointer; res < end;
366                     res = ACPI_NEXT_RESOURCE(res)) {
367                         if (res->Type == ACPI_RESOURCE_TYPE_END_TAG)
368                                 break;
369                         switch (res->Type) {
370                         case ACPI_RESOURCE_TYPE_GPIO:
371                                 gpio = (ACPI_RESOURCE_GPIO *)&res->Data;
372                                 gpio_acpi_do_map_aei(dev, &infos[n++], gpio);
373                                 break;
374                         default:
375                                 device_printf(dev,
376                                     "Unexpected resource type %d\n",
377                                     res->Type);
378                                 break;
379                         };
380                 }
381                 AcpiOsFree(b.Pointer);
382         }
383         return (infos);
384 }
385
386 /*  Unmap ACPI events */
387 static void
388 gpio_acpi_unmap_aei(device_t dev, struct gpio_acpi_data *data)
389 {
390         struct acpi_event_info *info;
391         int i;
392
393         for (i = 0; i < data->num_aei; i++) {
394                 info = &data->infos[i];
395                 if (info->dev != NULL) {
396                         GPIO_FREE_INTR(dev, info->pin);
397                         /* XXX Wait until ACPI Event handler has finished */
398                         memset(info, 0, sizeof(*info));
399                 }
400         }
401         kfree(data->infos, M_DEVBUF);
402 }
403
404 MODULE_DEPEND(gpio_acpi, acpi, 1, 1, 1);
405 MODULE_VERSION(gpio_acpi, 1);