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