1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Generic Event Device for ACPI. |
4 | * |
5 | * Copyright (c) 2016, The Linux Foundation. All rights reserved. |
6 | * |
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 | * } |
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 | |
43 | struct acpi_ged_device { |
44 | struct device *dev; |
45 | struct list_head event_list; |
46 | }; |
47 | |
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(handle: event->handle, NULL, arg: 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; |
75 | struct acpi_ged_device *geddev = context; |
76 | struct device *dev = geddev->dev; |
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 | char ev_name[5]; |
83 | u8 trigger; |
84 | |
85 | if (ares->type == ACPI_RESOURCE_TYPE_END_TAG) |
86 | return AE_OK; |
87 | |
88 | if (!acpi_dev_resource_interrupt(ares, index: 0, res: &r)) { |
89 | dev_err(dev, "unable to parse IRQ resource\n" ); |
90 | return AE_ERROR; |
91 | } |
92 | if (ares->type == ACPI_RESOURCE_TYPE_IRQ) { |
93 | gsi = p->interrupts[0]; |
94 | trigger = p->triggering; |
95 | } else { |
96 | gsi = pext->interrupts[0]; |
97 | trigger = pext->triggering; |
98 | } |
99 | |
100 | irq = r.start; |
101 | |
102 | switch (gsi) { |
103 | case 0 ... 255: |
104 | sprintf(buf: ev_name, fmt: "_%c%02X" , |
105 | trigger == ACPI_EDGE_SENSITIVE ? 'E' : 'L', gsi); |
106 | |
107 | if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle))) |
108 | break; |
109 | fallthrough; |
110 | default: |
111 | if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT" , &evt_handle))) |
112 | break; |
113 | |
114 | dev_err(dev, "cannot locate _EVT method\n" ); |
115 | return AE_ERROR; |
116 | } |
117 | |
118 | event = devm_kzalloc(dev, size: 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 | |
130 | if (request_threaded_irq(irq, NULL, thread_fn: acpi_ged_irq_handler, |
131 | flags: irqflags, name: "ACPI:Ged" , dev: event)) { |
132 | dev_err(dev, "failed to setup event handler for irq %u\n" , irq); |
133 | return AE_ERROR; |
134 | } |
135 | |
136 | dev_dbg(dev, "GED listening GSI %u @ IRQ %u\n" , gsi, irq); |
137 | list_add_tail(new: &event->node, head: &geddev->event_list); |
138 | return AE_OK; |
139 | } |
140 | |
141 | static int ged_probe(struct platform_device *pdev) |
142 | { |
143 | struct acpi_ged_device *geddev; |
144 | acpi_status acpi_ret; |
145 | |
146 | geddev = devm_kzalloc(dev: &pdev->dev, size: sizeof(*geddev), GFP_KERNEL); |
147 | if (!geddev) |
148 | return -ENOMEM; |
149 | |
150 | geddev->dev = &pdev->dev; |
151 | INIT_LIST_HEAD(list: &geddev->event_list); |
152 | acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), name: "_CRS" , |
153 | user_function: acpi_ged_request_interrupt, context: geddev); |
154 | if (ACPI_FAILURE(acpi_ret)) { |
155 | dev_err(&pdev->dev, "unable to parse the _CRS record\n" ); |
156 | return -EINVAL; |
157 | } |
158 | platform_set_drvdata(pdev, data: geddev); |
159 | |
160 | return 0; |
161 | } |
162 | |
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(entry: &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 | |
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, |
189 | .remove = ged_remove, |
190 | .shutdown = ged_shutdown, |
191 | .driver = { |
192 | .name = MODULE_NAME, |
193 | .acpi_match_table = ACPI_PTR(ged_acpi_ids), |
194 | }, |
195 | }; |
196 | builtin_platform_driver(ged_driver); |
197 | |