Commit | Line | Data |
---|---|---|
3fe50208 BM |
1 | /****************************************************************************** |
2 | * | |
3 | * Module Name: evgpeutil - GPE utilities | |
4 | * | |
5 | *****************************************************************************/ | |
6 | ||
7 | /* | |
77848130 | 8 | * Copyright (C) 2000 - 2012, Intel Corp. |
3fe50208 BM |
9 | * All rights reserved. |
10 | * | |
11 | * Redistribution and use in source and binary forms, with or without | |
12 | * modification, are permitted provided that the following conditions | |
13 | * are met: | |
14 | * 1. Redistributions of source code must retain the above copyright | |
15 | * notice, this list of conditions, and the following disclaimer, | |
16 | * without modification. | |
17 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer | |
18 | * substantially similar to the "NO WARRANTY" disclaimer below | |
19 | * ("Disclaimer") and any redistribution must be conditioned upon | |
20 | * including a substantially similar Disclaimer requirement for further | |
21 | * binary redistribution. | |
22 | * 3. Neither the names of the above-listed copyright holders nor the names | |
23 | * of any contributors may be used to endorse or promote products derived | |
24 | * from this software without specific prior written permission. | |
25 | * | |
26 | * Alternatively, this software may be distributed under the terms of the | |
27 | * GNU General Public License ("GPL") version 2 as published by the Free | |
28 | * Software Foundation. | |
29 | * | |
30 | * NO WARRANTY | |
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR | |
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
35 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING | |
40 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
41 | * POSSIBILITY OF SUCH DAMAGES. | |
42 | */ | |
43 | ||
44 | #include <acpi/acpi.h> | |
45 | #include "accommon.h" | |
46 | #include "acevents.h" | |
47 | ||
48 | #define _COMPONENT ACPI_EVENTS | |
49 | ACPI_MODULE_NAME("evgpeutil") | |
50 | ||
51 | /******************************************************************************* | |
52 | * | |
53 | * FUNCTION: acpi_ev_walk_gpe_list | |
54 | * | |
55 | * PARAMETERS: gpe_walk_callback - Routine called for each GPE block | |
56 | * Context - Value passed to callback | |
57 | * | |
58 | * RETURN: Status | |
59 | * | |
60 | * DESCRIPTION: Walk the GPE lists. | |
61 | * | |
62 | ******************************************************************************/ | |
63 | acpi_status | |
64 | acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context) | |
65 | { | |
66 | struct acpi_gpe_block_info *gpe_block; | |
67 | struct acpi_gpe_xrupt_info *gpe_xrupt_info; | |
68 | acpi_status status = AE_OK; | |
69 | acpi_cpu_flags flags; | |
70 | ||
71 | ACPI_FUNCTION_TRACE(ev_walk_gpe_list); | |
72 | ||
73 | flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); | |
74 | ||
75 | /* Walk the interrupt level descriptor list */ | |
76 | ||
77 | gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; | |
78 | while (gpe_xrupt_info) { | |
79 | ||
80 | /* Walk all Gpe Blocks attached to this interrupt level */ | |
81 | ||
82 | gpe_block = gpe_xrupt_info->gpe_block_list_head; | |
83 | while (gpe_block) { | |
84 | ||
85 | /* One callback per GPE block */ | |
86 | ||
87 | status = | |
88 | gpe_walk_callback(gpe_xrupt_info, gpe_block, | |
89 | context); | |
90 | if (ACPI_FAILURE(status)) { | |
91 | if (status == AE_CTRL_END) { /* Callback abort */ | |
92 | status = AE_OK; | |
93 | } | |
94 | goto unlock_and_exit; | |
95 | } | |
96 | ||
97 | gpe_block = gpe_block->next; | |
98 | } | |
99 | ||
100 | gpe_xrupt_info = gpe_xrupt_info->next; | |
101 | } | |
102 | ||
103 | unlock_and_exit: | |
104 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | |
105 | return_ACPI_STATUS(status); | |
106 | } | |
107 | ||
108 | /******************************************************************************* | |
109 | * | |
110 | * FUNCTION: acpi_ev_valid_gpe_event | |
111 | * | |
112 | * PARAMETERS: gpe_event_info - Info for this GPE | |
113 | * | |
114 | * RETURN: TRUE if the gpe_event is valid | |
115 | * | |
116 | * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL. | |
117 | * Should be called only when the GPE lists are semaphore locked | |
118 | * and not subject to change. | |
119 | * | |
120 | ******************************************************************************/ | |
121 | ||
122 | u8 acpi_ev_valid_gpe_event(struct acpi_gpe_event_info *gpe_event_info) | |
123 | { | |
124 | struct acpi_gpe_xrupt_info *gpe_xrupt_block; | |
125 | struct acpi_gpe_block_info *gpe_block; | |
126 | ||
127 | ACPI_FUNCTION_ENTRY(); | |
128 | ||
129 | /* No need for spin lock since we are not changing any list elements */ | |
130 | ||
131 | /* Walk the GPE interrupt levels */ | |
132 | ||
133 | gpe_xrupt_block = acpi_gbl_gpe_xrupt_list_head; | |
134 | while (gpe_xrupt_block) { | |
135 | gpe_block = gpe_xrupt_block->gpe_block_list_head; | |
136 | ||
137 | /* Walk the GPE blocks on this interrupt level */ | |
138 | ||
139 | while (gpe_block) { | |
140 | if ((&gpe_block->event_info[0] <= gpe_event_info) && | |
141 | (&gpe_block->event_info[gpe_block->gpe_count] > | |
142 | gpe_event_info)) { | |
143 | return (TRUE); | |
144 | } | |
145 | ||
146 | gpe_block = gpe_block->next; | |
147 | } | |
148 | ||
149 | gpe_xrupt_block = gpe_xrupt_block->next; | |
150 | } | |
151 | ||
152 | return (FALSE); | |
153 | } | |
154 | ||
3cfd53d5 LM |
155 | /******************************************************************************* |
156 | * | |
157 | * FUNCTION: acpi_ev_get_gpe_device | |
158 | * | |
159 | * PARAMETERS: GPE_WALK_CALLBACK | |
160 | * | |
161 | * RETURN: Status | |
162 | * | |
163 | * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE | |
164 | * block device. NULL if the GPE is one of the FADT-defined GPEs. | |
165 | * | |
166 | ******************************************************************************/ | |
167 | ||
168 | acpi_status | |
169 | acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | |
170 | struct acpi_gpe_block_info *gpe_block, void *context) | |
171 | { | |
172 | struct acpi_gpe_device_info *info = context; | |
173 | ||
174 | /* Increment Index by the number of GPEs in this block */ | |
175 | ||
176 | info->next_block_base_index += gpe_block->gpe_count; | |
177 | ||
178 | if (info->index < info->next_block_base_index) { | |
179 | /* | |
180 | * The GPE index is within this block, get the node. Leave the node | |
181 | * NULL for the FADT-defined GPEs | |
182 | */ | |
183 | if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) { | |
184 | info->gpe_device = gpe_block->node; | |
185 | } | |
186 | ||
187 | info->status = AE_OK; | |
188 | return (AE_CTRL_END); | |
189 | } | |
190 | ||
191 | return (AE_OK); | |
192 | } | |
193 | ||
3fe50208 BM |
194 | /******************************************************************************* |
195 | * | |
196 | * FUNCTION: acpi_ev_get_gpe_xrupt_block | |
197 | * | |
198 | * PARAMETERS: interrupt_number - Interrupt for a GPE block | |
199 | * | |
200 | * RETURN: A GPE interrupt block | |
201 | * | |
202 | * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt | |
203 | * block per unique interrupt level used for GPEs. Should be | |
204 | * called only when the GPE lists are semaphore locked and not | |
205 | * subject to change. | |
206 | * | |
207 | ******************************************************************************/ | |
208 | ||
209 | struct acpi_gpe_xrupt_info *acpi_ev_get_gpe_xrupt_block(u32 interrupt_number) | |
210 | { | |
211 | struct acpi_gpe_xrupt_info *next_gpe_xrupt; | |
212 | struct acpi_gpe_xrupt_info *gpe_xrupt; | |
213 | acpi_status status; | |
214 | acpi_cpu_flags flags; | |
215 | ||
216 | ACPI_FUNCTION_TRACE(ev_get_gpe_xrupt_block); | |
217 | ||
218 | /* No need for lock since we are not changing any list elements here */ | |
219 | ||
220 | next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; | |
221 | while (next_gpe_xrupt) { | |
222 | if (next_gpe_xrupt->interrupt_number == interrupt_number) { | |
223 | return_PTR(next_gpe_xrupt); | |
224 | } | |
225 | ||
226 | next_gpe_xrupt = next_gpe_xrupt->next; | |
227 | } | |
228 | ||
229 | /* Not found, must allocate a new xrupt descriptor */ | |
230 | ||
231 | gpe_xrupt = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_xrupt_info)); | |
232 | if (!gpe_xrupt) { | |
233 | return_PTR(NULL); | |
234 | } | |
235 | ||
236 | gpe_xrupt->interrupt_number = interrupt_number; | |
237 | ||
238 | /* Install new interrupt descriptor with spin lock */ | |
239 | ||
240 | flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); | |
241 | if (acpi_gbl_gpe_xrupt_list_head) { | |
242 | next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; | |
243 | while (next_gpe_xrupt->next) { | |
244 | next_gpe_xrupt = next_gpe_xrupt->next; | |
245 | } | |
246 | ||
247 | next_gpe_xrupt->next = gpe_xrupt; | |
248 | gpe_xrupt->previous = next_gpe_xrupt; | |
249 | } else { | |
250 | acpi_gbl_gpe_xrupt_list_head = gpe_xrupt; | |
251 | } | |
252 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | |
253 | ||
254 | /* Install new interrupt handler if not SCI_INT */ | |
255 | ||
256 | if (interrupt_number != acpi_gbl_FADT.sci_interrupt) { | |
257 | status = acpi_os_install_interrupt_handler(interrupt_number, | |
258 | acpi_ev_gpe_xrupt_handler, | |
259 | gpe_xrupt); | |
260 | if (ACPI_FAILURE(status)) { | |
261 | ACPI_ERROR((AE_INFO, | |
262 | "Could not install GPE interrupt handler at level 0x%X", | |
263 | interrupt_number)); | |
264 | return_PTR(NULL); | |
265 | } | |
266 | } | |
267 | ||
268 | return_PTR(gpe_xrupt); | |
269 | } | |
270 | ||
271 | /******************************************************************************* | |
272 | * | |
273 | * FUNCTION: acpi_ev_delete_gpe_xrupt | |
274 | * | |
275 | * PARAMETERS: gpe_xrupt - A GPE interrupt info block | |
276 | * | |
277 | * RETURN: Status | |
278 | * | |
279 | * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated | |
280 | * interrupt handler if not the SCI interrupt. | |
281 | * | |
282 | ******************************************************************************/ | |
283 | ||
284 | acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt) | |
285 | { | |
286 | acpi_status status; | |
287 | acpi_cpu_flags flags; | |
288 | ||
289 | ACPI_FUNCTION_TRACE(ev_delete_gpe_xrupt); | |
290 | ||
291 | /* We never want to remove the SCI interrupt handler */ | |
292 | ||
293 | if (gpe_xrupt->interrupt_number == acpi_gbl_FADT.sci_interrupt) { | |
294 | gpe_xrupt->gpe_block_list_head = NULL; | |
295 | return_ACPI_STATUS(AE_OK); | |
296 | } | |
297 | ||
298 | /* Disable this interrupt */ | |
299 | ||
300 | status = | |
301 | acpi_os_remove_interrupt_handler(gpe_xrupt->interrupt_number, | |
302 | acpi_ev_gpe_xrupt_handler); | |
303 | if (ACPI_FAILURE(status)) { | |
304 | return_ACPI_STATUS(status); | |
305 | } | |
306 | ||
307 | /* Unlink the interrupt block with lock */ | |
308 | ||
309 | flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); | |
310 | if (gpe_xrupt->previous) { | |
311 | gpe_xrupt->previous->next = gpe_xrupt->next; | |
312 | } else { | |
313 | /* No previous, update list head */ | |
314 | ||
315 | acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next; | |
316 | } | |
317 | ||
318 | if (gpe_xrupt->next) { | |
319 | gpe_xrupt->next->previous = gpe_xrupt->previous; | |
320 | } | |
321 | acpi_os_release_lock(acpi_gbl_gpe_lock, flags); | |
322 | ||
323 | /* Free the block */ | |
324 | ||
325 | ACPI_FREE(gpe_xrupt); | |
326 | return_ACPI_STATUS(AE_OK); | |
327 | } | |
328 | ||
329 | /******************************************************************************* | |
330 | * | |
331 | * FUNCTION: acpi_ev_delete_gpe_handlers | |
332 | * | |
333 | * PARAMETERS: gpe_xrupt_info - GPE Interrupt info | |
334 | * gpe_block - Gpe Block info | |
335 | * | |
336 | * RETURN: Status | |
337 | * | |
338 | * DESCRIPTION: Delete all Handler objects found in the GPE data structs. | |
339 | * Used only prior to termination. | |
340 | * | |
341 | ******************************************************************************/ | |
342 | ||
343 | acpi_status | |
344 | acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info, | |
345 | struct acpi_gpe_block_info *gpe_block, | |
346 | void *context) | |
347 | { | |
348 | struct acpi_gpe_event_info *gpe_event_info; | |
349 | u32 i; | |
350 | u32 j; | |
351 | ||
352 | ACPI_FUNCTION_TRACE(ev_delete_gpe_handlers); | |
353 | ||
354 | /* Examine each GPE Register within the block */ | |
355 | ||
356 | for (i = 0; i < gpe_block->register_count; i++) { | |
357 | ||
358 | /* Now look at the individual GPEs in this byte register */ | |
359 | ||
360 | for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { | |
361 | gpe_event_info = &gpe_block->event_info[((acpi_size) i * | |
362 | ACPI_GPE_REGISTER_WIDTH) | |
363 | + j]; | |
364 | ||
365 | if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == | |
366 | ACPI_GPE_DISPATCH_HANDLER) { | |
367 | ACPI_FREE(gpe_event_info->dispatch.handler); | |
368 | gpe_event_info->dispatch.handler = NULL; | |
369 | gpe_event_info->flags &= | |
370 | ~ACPI_GPE_DISPATCH_MASK; | |
371 | } | |
372 | } | |
373 | } | |
374 | ||
375 | return_ACPI_STATUS(AE_OK); | |
376 | } |