xref: /OK3568_Linux_fs/kernel/drivers/acpi/acpica/evgpeutil.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /******************************************************************************
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Module Name: evgpeutil - GPE utilities
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 2000 - 2020, Intel Corp.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  *****************************************************************************/
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <acpi/acpi.h>
11*4882a593Smuzhiyun #include "accommon.h"
12*4882a593Smuzhiyun #include "acevents.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #define _COMPONENT          ACPI_EVENTS
15*4882a593Smuzhiyun ACPI_MODULE_NAME("evgpeutil")
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun #if (!ACPI_REDUCED_HARDWARE)	/* Entire module */
18*4882a593Smuzhiyun /*******************************************************************************
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  * FUNCTION:    acpi_ev_walk_gpe_list
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * PARAMETERS:  gpe_walk_callback   - Routine called for each GPE block
23*4882a593Smuzhiyun  *              context             - Value passed to callback
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  * RETURN:      Status
26*4882a593Smuzhiyun  *
27*4882a593Smuzhiyun  * DESCRIPTION: Walk the GPE lists.
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  ******************************************************************************/
30*4882a593Smuzhiyun acpi_status
acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback,void * context)31*4882a593Smuzhiyun acpi_ev_walk_gpe_list(acpi_gpe_callback gpe_walk_callback, void *context)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun 	struct acpi_gpe_block_info *gpe_block;
34*4882a593Smuzhiyun 	struct acpi_gpe_xrupt_info *gpe_xrupt_info;
35*4882a593Smuzhiyun 	acpi_status status = AE_OK;
36*4882a593Smuzhiyun 	acpi_cpu_flags flags;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	ACPI_FUNCTION_TRACE(ev_walk_gpe_list);
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun 	/* Walk the interrupt level descriptor list */
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head;
45*4882a593Smuzhiyun 	while (gpe_xrupt_info) {
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun 		/* Walk all Gpe Blocks attached to this interrupt level */
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 		gpe_block = gpe_xrupt_info->gpe_block_list_head;
50*4882a593Smuzhiyun 		while (gpe_block) {
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 			/* One callback per GPE block */
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 			status =
55*4882a593Smuzhiyun 			    gpe_walk_callback(gpe_xrupt_info, gpe_block,
56*4882a593Smuzhiyun 					      context);
57*4882a593Smuzhiyun 			if (ACPI_FAILURE(status)) {
58*4882a593Smuzhiyun 				if (status == AE_CTRL_END) {	/* Callback abort */
59*4882a593Smuzhiyun 					status = AE_OK;
60*4882a593Smuzhiyun 				}
61*4882a593Smuzhiyun 				goto unlock_and_exit;
62*4882a593Smuzhiyun 			}
63*4882a593Smuzhiyun 
64*4882a593Smuzhiyun 			gpe_block = gpe_block->next;
65*4882a593Smuzhiyun 		}
66*4882a593Smuzhiyun 
67*4882a593Smuzhiyun 		gpe_xrupt_info = gpe_xrupt_info->next;
68*4882a593Smuzhiyun 	}
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun unlock_and_exit:
71*4882a593Smuzhiyun 	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
72*4882a593Smuzhiyun 	return_ACPI_STATUS(status);
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /*******************************************************************************
76*4882a593Smuzhiyun  *
77*4882a593Smuzhiyun  * FUNCTION:    acpi_ev_get_gpe_device
78*4882a593Smuzhiyun  *
79*4882a593Smuzhiyun  * PARAMETERS:  GPE_WALK_CALLBACK
80*4882a593Smuzhiyun  *
81*4882a593Smuzhiyun  * RETURN:      Status
82*4882a593Smuzhiyun  *
83*4882a593Smuzhiyun  * DESCRIPTION: Matches the input GPE index (0-current_gpe_count) with a GPE
84*4882a593Smuzhiyun  *              block device. NULL if the GPE is one of the FADT-defined GPEs.
85*4882a593Smuzhiyun  *
86*4882a593Smuzhiyun  ******************************************************************************/
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun acpi_status
acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info * gpe_xrupt_info,struct acpi_gpe_block_info * gpe_block,void * context)89*4882a593Smuzhiyun acpi_ev_get_gpe_device(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
90*4882a593Smuzhiyun 		       struct acpi_gpe_block_info *gpe_block, void *context)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun 	struct acpi_gpe_device_info *info = context;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	/* Increment Index by the number of GPEs in this block */
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	info->next_block_base_index += gpe_block->gpe_count;
97*4882a593Smuzhiyun 
98*4882a593Smuzhiyun 	if (info->index < info->next_block_base_index) {
99*4882a593Smuzhiyun 		/*
100*4882a593Smuzhiyun 		 * The GPE index is within this block, get the node. Leave the node
101*4882a593Smuzhiyun 		 * NULL for the FADT-defined GPEs
102*4882a593Smuzhiyun 		 */
103*4882a593Smuzhiyun 		if ((gpe_block->node)->type == ACPI_TYPE_DEVICE) {
104*4882a593Smuzhiyun 			info->gpe_device = gpe_block->node;
105*4882a593Smuzhiyun 		}
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 		info->status = AE_OK;
108*4882a593Smuzhiyun 		return (AE_CTRL_END);
109*4882a593Smuzhiyun 	}
110*4882a593Smuzhiyun 
111*4882a593Smuzhiyun 	return (AE_OK);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /*******************************************************************************
115*4882a593Smuzhiyun  *
116*4882a593Smuzhiyun  * FUNCTION:    acpi_ev_get_gpe_xrupt_block
117*4882a593Smuzhiyun  *
118*4882a593Smuzhiyun  * PARAMETERS:  interrupt_number            - Interrupt for a GPE block
119*4882a593Smuzhiyun  *              gpe_xrupt_block             - Where the block is returned
120*4882a593Smuzhiyun  *
121*4882a593Smuzhiyun  * RETURN:      Status
122*4882a593Smuzhiyun  *
123*4882a593Smuzhiyun  * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt
124*4882a593Smuzhiyun  *              block per unique interrupt level used for GPEs. Should be
125*4882a593Smuzhiyun  *              called only when the GPE lists are semaphore locked and not
126*4882a593Smuzhiyun  *              subject to change.
127*4882a593Smuzhiyun  *
128*4882a593Smuzhiyun  ******************************************************************************/
129*4882a593Smuzhiyun 
130*4882a593Smuzhiyun acpi_status
acpi_ev_get_gpe_xrupt_block(u32 interrupt_number,struct acpi_gpe_xrupt_info ** gpe_xrupt_block)131*4882a593Smuzhiyun acpi_ev_get_gpe_xrupt_block(u32 interrupt_number,
132*4882a593Smuzhiyun 			    struct acpi_gpe_xrupt_info **gpe_xrupt_block)
133*4882a593Smuzhiyun {
134*4882a593Smuzhiyun 	struct acpi_gpe_xrupt_info *next_gpe_xrupt;
135*4882a593Smuzhiyun 	struct acpi_gpe_xrupt_info *gpe_xrupt;
136*4882a593Smuzhiyun 	acpi_status status;
137*4882a593Smuzhiyun 	acpi_cpu_flags flags;
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun 	ACPI_FUNCTION_TRACE(ev_get_gpe_xrupt_block);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	/* No need for lock since we are not changing any list elements here */
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head;
144*4882a593Smuzhiyun 	while (next_gpe_xrupt) {
145*4882a593Smuzhiyun 		if (next_gpe_xrupt->interrupt_number == interrupt_number) {
146*4882a593Smuzhiyun 			*gpe_xrupt_block = next_gpe_xrupt;
147*4882a593Smuzhiyun 			return_ACPI_STATUS(AE_OK);
148*4882a593Smuzhiyun 		}
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 		next_gpe_xrupt = next_gpe_xrupt->next;
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	/* Not found, must allocate a new xrupt descriptor */
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	gpe_xrupt = ACPI_ALLOCATE_ZEROED(sizeof(struct acpi_gpe_xrupt_info));
156*4882a593Smuzhiyun 	if (!gpe_xrupt) {
157*4882a593Smuzhiyun 		return_ACPI_STATUS(AE_NO_MEMORY);
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	gpe_xrupt->interrupt_number = interrupt_number;
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/* Install new interrupt descriptor with spin lock */
163*4882a593Smuzhiyun 
164*4882a593Smuzhiyun 	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
165*4882a593Smuzhiyun 	if (acpi_gbl_gpe_xrupt_list_head) {
166*4882a593Smuzhiyun 		next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head;
167*4882a593Smuzhiyun 		while (next_gpe_xrupt->next) {
168*4882a593Smuzhiyun 			next_gpe_xrupt = next_gpe_xrupt->next;
169*4882a593Smuzhiyun 		}
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 		next_gpe_xrupt->next = gpe_xrupt;
172*4882a593Smuzhiyun 		gpe_xrupt->previous = next_gpe_xrupt;
173*4882a593Smuzhiyun 	} else {
174*4882a593Smuzhiyun 		acpi_gbl_gpe_xrupt_list_head = gpe_xrupt;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 	/* Install new interrupt handler if not SCI_INT */
180*4882a593Smuzhiyun 
181*4882a593Smuzhiyun 	if (interrupt_number != acpi_gbl_FADT.sci_interrupt) {
182*4882a593Smuzhiyun 		status = acpi_os_install_interrupt_handler(interrupt_number,
183*4882a593Smuzhiyun 							   acpi_ev_gpe_xrupt_handler,
184*4882a593Smuzhiyun 							   gpe_xrupt);
185*4882a593Smuzhiyun 		if (ACPI_FAILURE(status)) {
186*4882a593Smuzhiyun 			ACPI_EXCEPTION((AE_INFO, status,
187*4882a593Smuzhiyun 					"Could not install GPE interrupt handler at level 0x%X",
188*4882a593Smuzhiyun 					interrupt_number));
189*4882a593Smuzhiyun 			return_ACPI_STATUS(status);
190*4882a593Smuzhiyun 		}
191*4882a593Smuzhiyun 	}
192*4882a593Smuzhiyun 
193*4882a593Smuzhiyun 	*gpe_xrupt_block = gpe_xrupt;
194*4882a593Smuzhiyun 	return_ACPI_STATUS(AE_OK);
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /*******************************************************************************
198*4882a593Smuzhiyun  *
199*4882a593Smuzhiyun  * FUNCTION:    acpi_ev_delete_gpe_xrupt
200*4882a593Smuzhiyun  *
201*4882a593Smuzhiyun  * PARAMETERS:  gpe_xrupt       - A GPE interrupt info block
202*4882a593Smuzhiyun  *
203*4882a593Smuzhiyun  * RETURN:      Status
204*4882a593Smuzhiyun  *
205*4882a593Smuzhiyun  * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated
206*4882a593Smuzhiyun  *              interrupt handler if not the SCI interrupt.
207*4882a593Smuzhiyun  *
208*4882a593Smuzhiyun  ******************************************************************************/
209*4882a593Smuzhiyun 
acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info * gpe_xrupt)210*4882a593Smuzhiyun acpi_status acpi_ev_delete_gpe_xrupt(struct acpi_gpe_xrupt_info *gpe_xrupt)
211*4882a593Smuzhiyun {
212*4882a593Smuzhiyun 	acpi_status status;
213*4882a593Smuzhiyun 	acpi_cpu_flags flags;
214*4882a593Smuzhiyun 
215*4882a593Smuzhiyun 	ACPI_FUNCTION_TRACE(ev_delete_gpe_xrupt);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	/* We never want to remove the SCI interrupt handler */
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	if (gpe_xrupt->interrupt_number == acpi_gbl_FADT.sci_interrupt) {
220*4882a593Smuzhiyun 		gpe_xrupt->gpe_block_list_head = NULL;
221*4882a593Smuzhiyun 		return_ACPI_STATUS(AE_OK);
222*4882a593Smuzhiyun 	}
223*4882a593Smuzhiyun 
224*4882a593Smuzhiyun 	/* Disable this interrupt */
225*4882a593Smuzhiyun 
226*4882a593Smuzhiyun 	status =
227*4882a593Smuzhiyun 	    acpi_os_remove_interrupt_handler(gpe_xrupt->interrupt_number,
228*4882a593Smuzhiyun 					     acpi_ev_gpe_xrupt_handler);
229*4882a593Smuzhiyun 	if (ACPI_FAILURE(status)) {
230*4882a593Smuzhiyun 		return_ACPI_STATUS(status);
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	/* Unlink the interrupt block with lock */
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock);
236*4882a593Smuzhiyun 	if (gpe_xrupt->previous) {
237*4882a593Smuzhiyun 		gpe_xrupt->previous->next = gpe_xrupt->next;
238*4882a593Smuzhiyun 	} else {
239*4882a593Smuzhiyun 		/* No previous, update list head */
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 		acpi_gbl_gpe_xrupt_list_head = gpe_xrupt->next;
242*4882a593Smuzhiyun 	}
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	if (gpe_xrupt->next) {
245*4882a593Smuzhiyun 		gpe_xrupt->next->previous = gpe_xrupt->previous;
246*4882a593Smuzhiyun 	}
247*4882a593Smuzhiyun 	acpi_os_release_lock(acpi_gbl_gpe_lock, flags);
248*4882a593Smuzhiyun 
249*4882a593Smuzhiyun 	/* Free the block */
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun 	ACPI_FREE(gpe_xrupt);
252*4882a593Smuzhiyun 	return_ACPI_STATUS(AE_OK);
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun /*******************************************************************************
256*4882a593Smuzhiyun  *
257*4882a593Smuzhiyun  * FUNCTION:    acpi_ev_delete_gpe_handlers
258*4882a593Smuzhiyun  *
259*4882a593Smuzhiyun  * PARAMETERS:  gpe_xrupt_info      - GPE Interrupt info
260*4882a593Smuzhiyun  *              gpe_block           - Gpe Block info
261*4882a593Smuzhiyun  *
262*4882a593Smuzhiyun  * RETURN:      Status
263*4882a593Smuzhiyun  *
264*4882a593Smuzhiyun  * DESCRIPTION: Delete all Handler objects found in the GPE data structs.
265*4882a593Smuzhiyun  *              Used only prior to termination.
266*4882a593Smuzhiyun  *
267*4882a593Smuzhiyun  ******************************************************************************/
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun acpi_status
acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info * gpe_xrupt_info,struct acpi_gpe_block_info * gpe_block,void * context)270*4882a593Smuzhiyun acpi_ev_delete_gpe_handlers(struct acpi_gpe_xrupt_info *gpe_xrupt_info,
271*4882a593Smuzhiyun 			    struct acpi_gpe_block_info *gpe_block,
272*4882a593Smuzhiyun 			    void *context)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun 	struct acpi_gpe_event_info *gpe_event_info;
275*4882a593Smuzhiyun 	struct acpi_gpe_notify_info *notify;
276*4882a593Smuzhiyun 	struct acpi_gpe_notify_info *next;
277*4882a593Smuzhiyun 	u32 i;
278*4882a593Smuzhiyun 	u32 j;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	ACPI_FUNCTION_TRACE(ev_delete_gpe_handlers);
281*4882a593Smuzhiyun 
282*4882a593Smuzhiyun 	/* Examine each GPE Register within the block */
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 	for (i = 0; i < gpe_block->register_count; i++) {
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 		/* Now look at the individual GPEs in this byte register */
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 		for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) {
289*4882a593Smuzhiyun 			gpe_event_info = &gpe_block->event_info[((acpi_size)i *
290*4882a593Smuzhiyun 								 ACPI_GPE_REGISTER_WIDTH)
291*4882a593Smuzhiyun 								+ j];
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 			if ((ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
294*4882a593Smuzhiyun 			     ACPI_GPE_DISPATCH_HANDLER) ||
295*4882a593Smuzhiyun 			    (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags) ==
296*4882a593Smuzhiyun 			     ACPI_GPE_DISPATCH_RAW_HANDLER)) {
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 				/* Delete an installed handler block */
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 				ACPI_FREE(gpe_event_info->dispatch.handler);
301*4882a593Smuzhiyun 				gpe_event_info->dispatch.handler = NULL;
302*4882a593Smuzhiyun 				gpe_event_info->flags &=
303*4882a593Smuzhiyun 				    ~ACPI_GPE_DISPATCH_MASK;
304*4882a593Smuzhiyun 			} else if (ACPI_GPE_DISPATCH_TYPE(gpe_event_info->flags)
305*4882a593Smuzhiyun 				   == ACPI_GPE_DISPATCH_NOTIFY) {
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 				/* Delete the implicit notification device list */
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun 				notify = gpe_event_info->dispatch.notify_list;
310*4882a593Smuzhiyun 				while (notify) {
311*4882a593Smuzhiyun 					next = notify->next;
312*4882a593Smuzhiyun 					ACPI_FREE(notify);
313*4882a593Smuzhiyun 					notify = next;
314*4882a593Smuzhiyun 				}
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 				gpe_event_info->dispatch.notify_list = NULL;
317*4882a593Smuzhiyun 				gpe_event_info->flags &=
318*4882a593Smuzhiyun 				    ~ACPI_GPE_DISPATCH_MASK;
319*4882a593Smuzhiyun 			}
320*4882a593Smuzhiyun 		}
321*4882a593Smuzhiyun 	}
322*4882a593Smuzhiyun 
323*4882a593Smuzhiyun 	return_ACPI_STATUS(AE_OK);
324*4882a593Smuzhiyun }
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun #endif				/* !ACPI_REDUCED_HARDWARE */
327