]>
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; | |
82 | ||
83 | if (ares->type == ACPI_RESOURCE_TYPE_END_TAG) | |
84 | return AE_OK; | |
85 | ||
86 | if (!acpi_dev_resource_interrupt(ares, 0, &r)) { | |
87 | dev_err(dev, "unable to parse IRQ resource\n"); | |
88 | return AE_ERROR; | |
89 | } | |
90 | if (ares->type == ACPI_RESOURCE_TYPE_IRQ) | |
91 | gsi = p->interrupts[0]; | |
92 | else | |
93 | gsi = pext->interrupts[0]; | |
94 | ||
95 | irq = r.start; | |
96 | ||
97 | if (ACPI_FAILURE(acpi_get_handle(handle, "_EVT", &evt_handle))) { | |
98 | dev_err(dev, "cannot locate _EVT method\n"); | |
99 | return AE_ERROR; | |
100 | } | |
101 | ||
3db80c23 SK |
102 | event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL); |
103 | if (!event) | |
104 | return AE_ERROR; | |
105 | ||
106 | event->gsi = gsi; | |
107 | event->dev = dev; | |
108 | event->irq = irq; | |
109 | event->handle = evt_handle; | |
110 | ||
111 | if (r.flags & IORESOURCE_IRQ_SHAREABLE) | |
112 | irqflags |= IRQF_SHARED; | |
113 | ||
099caa91 SK |
114 | if (request_threaded_irq(irq, NULL, acpi_ged_irq_handler, |
115 | irqflags, "ACPI:Ged", event)) { | |
3db80c23 SK |
116 | dev_err(dev, "failed to setup event handler for irq %u\n", irq); |
117 | return AE_ERROR; | |
118 | } | |
119 | ||
099caa91 SK |
120 | dev_dbg(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq); |
121 | list_add_tail(&event->node, &geddev->event_list); | |
3db80c23 SK |
122 | return AE_OK; |
123 | } | |
124 | ||
125 | static int ged_probe(struct platform_device *pdev) | |
126 | { | |
099caa91 | 127 | struct acpi_ged_device *geddev; |
3db80c23 SK |
128 | acpi_status acpi_ret; |
129 | ||
099caa91 SK |
130 | geddev = devm_kzalloc(&pdev->dev, sizeof(*geddev), GFP_KERNEL); |
131 | if (!geddev) | |
132 | return -ENOMEM; | |
133 | ||
134 | geddev->dev = &pdev->dev; | |
135 | INIT_LIST_HEAD(&geddev->event_list); | |
3db80c23 | 136 | acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS", |
099caa91 | 137 | acpi_ged_request_interrupt, geddev); |
3db80c23 SK |
138 | if (ACPI_FAILURE(acpi_ret)) { |
139 | dev_err(&pdev->dev, "unable to parse the _CRS record\n"); | |
140 | return -EINVAL; | |
141 | } | |
099caa91 | 142 | platform_set_drvdata(pdev, geddev); |
3db80c23 SK |
143 | |
144 | return 0; | |
145 | } | |
146 | ||
099caa91 SK |
147 | static void ged_shutdown(struct platform_device *pdev) |
148 | { | |
149 | struct acpi_ged_device *geddev = platform_get_drvdata(pdev); | |
150 | struct acpi_ged_event *event, *next; | |
151 | ||
152 | list_for_each_entry_safe(event, next, &geddev->event_list, node) { | |
153 | free_irq(event->irq, event); | |
154 | list_del(&event->node); | |
155 | dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n", | |
156 | event->gsi, event->irq); | |
157 | } | |
158 | } | |
159 | ||
160 | static int ged_remove(struct platform_device *pdev) | |
161 | { | |
162 | ged_shutdown(pdev); | |
163 | return 0; | |
164 | } | |
165 | ||
3db80c23 SK |
166 | static const struct acpi_device_id ged_acpi_ids[] = { |
167 | {"ACPI0013"}, | |
168 | {}, | |
169 | }; | |
170 | ||
171 | static struct platform_driver ged_driver = { | |
172 | .probe = ged_probe, | |
099caa91 SK |
173 | .remove = ged_remove, |
174 | .shutdown = ged_shutdown, | |
3db80c23 SK |
175 | .driver = { |
176 | .name = MODULE_NAME, | |
177 | .acpi_match_table = ACPI_PTR(ged_acpi_ids), | |
178 | }, | |
179 | }; | |
437014bd | 180 | builtin_platform_driver(ged_driver); |