Commit | Line | Data |
---|---|---|
3db80c23 SK |
1 | /* |
2 | * Generic Event Device for ACPI. | |
3 | * | |
4 | * Copyright (c) 2016, The Linux Foundation. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License version 2 and | |
8 | * only version 2 as published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * Generic Event Device allows platforms to handle interrupts in ACPI | |
16 | * ASL statements. It follows very similar to _EVT method approach | |
17 | * from GPIO events. All interrupts are listed in _CRS and the handler | |
18 | * is written in _EVT method. Here is an example. | |
19 | * | |
20 | * Device (GED0) | |
21 | * { | |
22 | * | |
23 | * Name (_HID, "ACPI0013") | |
24 | * Name (_UID, 0) | |
25 | * Method (_CRS, 0x0, Serialized) | |
26 | * { | |
27 | * Name (RBUF, ResourceTemplate () | |
28 | * { | |
29 | * Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared, , , ) | |
30 | * {123} | |
31 | * } | |
32 | * }) | |
33 | * | |
34 | * Method (_EVT, 1) { | |
35 | * if (Lequal(123, Arg0)) | |
36 | * { | |
37 | * } | |
38 | * } | |
39 | * } | |
40 | * | |
41 | */ | |
42 | ||
43 | #include <linux/err.h> | |
44 | #include <linux/init.h> | |
45 | #include <linux/interrupt.h> | |
46 | #include <linux/list.h> | |
47 | #include <linux/platform_device.h> | |
48 | #include <linux/acpi.h> | |
49 | ||
50 | #define MODULE_NAME "acpi-ged" | |
51 | ||
52 | struct acpi_ged_event { | |
53 | struct list_head node; | |
54 | struct device *dev; | |
55 | unsigned int gsi; | |
56 | unsigned int irq; | |
57 | acpi_handle handle; | |
58 | }; | |
59 | ||
60 | static irqreturn_t acpi_ged_irq_handler(int irq, void *data) | |
61 | { | |
62 | struct acpi_ged_event *event = data; | |
63 | acpi_status acpi_ret; | |
64 | ||
65 | acpi_ret = acpi_execute_simple_method(event->handle, NULL, event->gsi); | |
66 | if (ACPI_FAILURE(acpi_ret)) | |
67 | dev_err_once(event->dev, "IRQ method execution failed\n"); | |
68 | ||
69 | return IRQ_HANDLED; | |
70 | } | |
71 | ||
72 | static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, | |
73 | void *context) | |
74 | { | |
75 | struct acpi_ged_event *event; | |
76 | unsigned int irq; | |
77 | unsigned int gsi; | |
78 | unsigned int irqflags = IRQF_ONESHOT; | |
79 | struct device *dev = context; | |
80 | acpi_handle handle = ACPI_HANDLE(dev); | |
81 | acpi_handle evt_handle; | |
82 | struct resource r; | |
83 | struct acpi_resource_irq *p = &ares->data.irq; | |
84 | struct acpi_resource_extended_irq *pext = &ares->data.extended_irq; | |
85 | ||
86 | if (ares->type == ACPI_RESOURCE_TYPE_END_TAG) | |
87 | return AE_OK; | |
88 | ||
89 | if (!acpi_dev_resource_interrupt(ares, 0, &r)) { | |
90 | dev_err(dev, "unable to parse IRQ resource\n"); | |
91 | return AE_ERROR; | |
92 | } | |
93 | if (ares->type == ACPI_RESOURCE_TYPE_IRQ) | |
94 | gsi = p->interrupts[0]; | |
95 | else | |
96 | gsi = pext->interrupts[0]; | |
97 | ||
98 | irq = r.start; | |
99 | ||
100 | if (ACPI_FAILURE(acpi_get_handle(handle, "_EVT", &evt_handle))) { | |
101 | dev_err(dev, "cannot locate _EVT method\n"); | |
102 | return AE_ERROR; | |
103 | } | |
104 | ||
105 | dev_info(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq); | |
106 | ||
107 | event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL); | |
108 | if (!event) | |
109 | return AE_ERROR; | |
110 | ||
111 | event->gsi = gsi; | |
112 | event->dev = dev; | |
113 | event->irq = irq; | |
114 | event->handle = evt_handle; | |
115 | ||
116 | if (r.flags & IORESOURCE_IRQ_SHAREABLE) | |
117 | irqflags |= IRQF_SHARED; | |
118 | ||
119 | if (devm_request_threaded_irq(dev, irq, NULL, acpi_ged_irq_handler, | |
120 | irqflags, "ACPI:Ged", event)) { | |
121 | dev_err(dev, "failed to setup event handler for irq %u\n", irq); | |
122 | return AE_ERROR; | |
123 | } | |
124 | ||
125 | return AE_OK; | |
126 | } | |
127 | ||
128 | static int ged_probe(struct platform_device *pdev) | |
129 | { | |
130 | acpi_status acpi_ret; | |
131 | ||
132 | acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS", | |
133 | acpi_ged_request_interrupt, &pdev->dev); | |
134 | if (ACPI_FAILURE(acpi_ret)) { | |
135 | dev_err(&pdev->dev, "unable to parse the _CRS record\n"); | |
136 | return -EINVAL; | |
137 | } | |
138 | ||
139 | return 0; | |
140 | } | |
141 | ||
142 | static const struct acpi_device_id ged_acpi_ids[] = { | |
143 | {"ACPI0013"}, | |
144 | {}, | |
145 | }; | |
146 | ||
147 | static struct platform_driver ged_driver = { | |
148 | .probe = ged_probe, | |
149 | .driver = { | |
150 | .name = MODULE_NAME, | |
151 | .acpi_match_table = ACPI_PTR(ged_acpi_ids), | |
152 | }, | |
153 | }; | |
437014bd | 154 | builtin_platform_driver(ged_driver); |