Commit | Line | Data |
---|---|---|
97fb5e8d | 1 | // SPDX-License-Identifier: GPL-2.0-only |
3db80c23 SK |
2 | /* |
3 | * Generic Event Device for ACPI. | |
4 | * | |
5 | * Copyright (c) 2016, The Linux Foundation. All rights reserved. | |
6 | * | |
3db80c23 SK |
7 | * Generic Event Device allows platforms to handle interrupts in ACPI |
8 | * ASL statements. It follows very similar to _EVT method approach | |
9 | * from GPIO events. All interrupts are listed in _CRS and the handler | |
10 | * is written in _EVT method. Here is an example. | |
11 | * | |
12 | * Device (GED0) | |
13 | * { | |
14 | * | |
15 | * Name (_HID, "ACPI0013") | |
16 | * Name (_UID, 0) | |
17 | * Method (_CRS, 0x0, Serialized) | |
18 | * { | |
19 | * Name (RBUF, ResourceTemplate () | |
20 | * { | |
21 | * Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared, , , ) | |
22 | * {123} | |
23 | * } | |
24 | * }) | |
25 | * | |
26 | * Method (_EVT, 1) { | |
27 | * if (Lequal(123, Arg0)) | |
28 | * { | |
29 | * } | |
30 | * } | |
31 | * } | |
3db80c23 SK |
32 | */ |
33 | ||
34 | #include <linux/err.h> | |
35 | #include <linux/init.h> | |
36 | #include <linux/interrupt.h> | |
37 | #include <linux/list.h> | |
38 | #include <linux/platform_device.h> | |
39 | #include <linux/acpi.h> | |
40 | ||
41 | #define MODULE_NAME "acpi-ged" | |
42 | ||
099caa91 SK |
43 | struct acpi_ged_device { |
44 | struct device *dev; | |
45 | struct list_head event_list; | |
46 | }; | |
47 | ||
3db80c23 SK |
48 | struct acpi_ged_event { |
49 | struct list_head node; | |
50 | struct device *dev; | |
51 | unsigned int gsi; | |
52 | unsigned int irq; | |
53 | acpi_handle handle; | |
54 | }; | |
55 | ||
56 | static irqreturn_t acpi_ged_irq_handler(int irq, void *data) | |
57 | { | |
58 | struct acpi_ged_event *event = data; | |
59 | acpi_status acpi_ret; | |
60 | ||
61 | acpi_ret = acpi_execute_simple_method(event->handle, NULL, event->gsi); | |
62 | if (ACPI_FAILURE(acpi_ret)) | |
63 | dev_err_once(event->dev, "IRQ method execution failed\n"); | |
64 | ||
65 | return IRQ_HANDLED; | |
66 | } | |
67 | ||
68 | static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares, | |
69 | void *context) | |
70 | { | |
71 | struct acpi_ged_event *event; | |
72 | unsigned int irq; | |
73 | unsigned int gsi; | |
74 | unsigned int irqflags = IRQF_ONESHOT; | |
099caa91 SK |
75 | struct acpi_ged_device *geddev = context; |
76 | struct device *dev = geddev->dev; | |
3db80c23 SK |
77 | acpi_handle handle = ACPI_HANDLE(dev); |
78 | acpi_handle evt_handle; | |
79 | struct resource r; | |
80 | struct acpi_resource_irq *p = &ares->data.irq; | |
81 | struct acpi_resource_extended_irq *pext = &ares->data.extended_irq; | |
ea6f3af4 AB |
82 | char ev_name[5]; |
83 | u8 trigger; | |
3db80c23 SK |
84 | |
85 | if (ares->type == ACPI_RESOURCE_TYPE_END_TAG) | |
86 | return AE_OK; | |
87 | ||
88 | if (!acpi_dev_resource_interrupt(ares, 0, &r)) { | |
89 | dev_err(dev, "unable to parse IRQ resource\n"); | |
90 | return AE_ERROR; | |
91 | } | |
ea6f3af4 | 92 | if (ares->type == ACPI_RESOURCE_TYPE_IRQ) { |
3db80c23 | 93 | gsi = p->interrupts[0]; |
ea6f3af4 AB |
94 | trigger = p->triggering; |
95 | } else { | |
3db80c23 | 96 | gsi = pext->interrupts[0]; |
e5c399b0 | 97 | trigger = pext->triggering; |
ea6f3af4 | 98 | } |
3db80c23 SK |
99 | |
100 | irq = r.start; | |
101 | ||
ea6f3af4 AB |
102 | switch (gsi) { |
103 | case 0 ... 255: | |
9debfb81 | 104 | sprintf(ev_name, "_%c%02X", |
ea6f3af4 AB |
105 | trigger == ACPI_EDGE_SENSITIVE ? 'E' : 'L', gsi); |
106 | ||
107 | if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle))) | |
108 | break; | |
57d2dd4b | 109 | fallthrough; |
ea6f3af4 AB |
110 | default: |
111 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle))) | |
112 | break; | |
113 | ||
3db80c23 SK |
114 | dev_err(dev, "cannot locate _EVT method\n"); |
115 | return AE_ERROR; | |
116 | } | |
117 | ||
3db80c23 SK |
118 | event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL); |
119 | if (!event) | |
120 | return AE_ERROR; | |
121 | ||
122 | event->gsi = gsi; | |
123 | event->dev = dev; | |
124 | event->irq = irq; | |
125 | event->handle = evt_handle; | |
126 | ||
127 | if (r.flags & IORESOURCE_IRQ_SHAREABLE) | |
128 | irqflags |= IRQF_SHARED; | |
129 | ||
099caa91 SK |
130 | if (request_threaded_irq(irq, NULL, acpi_ged_irq_handler, |
131 | irqflags, "ACPI:Ged", event)) { | |
3db80c23 SK |
132 | dev_err(dev, "failed to setup event handler for irq %u\n", irq); |
133 | return AE_ERROR; | |
134 | } | |
135 | ||
099caa91 SK |
136 | dev_dbg(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq); |
137 | list_add_tail(&event->node, &geddev->event_list); | |
3db80c23 SK |
138 | return AE_OK; |
139 | } | |
140 | ||
141 | static int ged_probe(struct platform_device *pdev) | |
142 | { | |
099caa91 | 143 | struct acpi_ged_device *geddev; |
3db80c23 SK |
144 | acpi_status acpi_ret; |
145 | ||
099caa91 SK |
146 | geddev = devm_kzalloc(&pdev->dev, sizeof(*geddev), GFP_KERNEL); |
147 | if (!geddev) | |
148 | return -ENOMEM; | |
149 | ||
150 | geddev->dev = &pdev->dev; | |
151 | INIT_LIST_HEAD(&geddev->event_list); | |
3db80c23 | 152 | acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS", |
099caa91 | 153 | acpi_ged_request_interrupt, geddev); |
3db80c23 SK |
154 | if (ACPI_FAILURE(acpi_ret)) { |
155 | dev_err(&pdev->dev, "unable to parse the _CRS record\n"); | |
156 | return -EINVAL; | |
157 | } | |
099caa91 | 158 | platform_set_drvdata(pdev, geddev); |
3db80c23 SK |
159 | |
160 | return 0; | |
161 | } | |
162 | ||
099caa91 SK |
163 | static void ged_shutdown(struct platform_device *pdev) |
164 | { | |
165 | struct acpi_ged_device *geddev = platform_get_drvdata(pdev); | |
166 | struct acpi_ged_event *event, *next; | |
167 | ||
168 | list_for_each_entry_safe(event, next, &geddev->event_list, node) { | |
169 | free_irq(event->irq, event); | |
170 | list_del(&event->node); | |
171 | dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n", | |
172 | event->gsi, event->irq); | |
173 | } | |
174 | } | |
175 | ||
176 | static int ged_remove(struct platform_device *pdev) | |
177 | { | |
178 | ged_shutdown(pdev); | |
179 | return 0; | |
180 | } | |
181 | ||
3db80c23 SK |
182 | static const struct acpi_device_id ged_acpi_ids[] = { |
183 | {"ACPI0013"}, | |
184 | {}, | |
185 | }; | |
186 | ||
187 | static struct platform_driver ged_driver = { | |
188 | .probe = ged_probe, | |
099caa91 SK |
189 | .remove = ged_remove, |
190 | .shutdown = ged_shutdown, | |
3db80c23 SK |
191 | .driver = { |
192 | .name = MODULE_NAME, | |
193 | .acpi_match_table = ACPI_PTR(ged_acpi_ids), | |
194 | }, | |
195 | }; | |
437014bd | 196 | builtin_platform_driver(ged_driver); |