xref: /OK3568_Linux_fs/kernel/drivers/acpi/irq.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * ACPI GSI IRQ layer
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2015 ARM Ltd.
6*4882a593Smuzhiyun  * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
7*4882a593Smuzhiyun  */
8*4882a593Smuzhiyun #include <linux/acpi.h>
9*4882a593Smuzhiyun #include <linux/irq.h>
10*4882a593Smuzhiyun #include <linux/irqdomain.h>
11*4882a593Smuzhiyun #include <linux/of.h>
12*4882a593Smuzhiyun 
13*4882a593Smuzhiyun enum acpi_irq_model_id acpi_irq_model;
14*4882a593Smuzhiyun 
15*4882a593Smuzhiyun static struct fwnode_handle *acpi_gsi_domain_id;
16*4882a593Smuzhiyun 
17*4882a593Smuzhiyun /**
18*4882a593Smuzhiyun  * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
19*4882a593Smuzhiyun  * @gsi: GSI IRQ number to map
20*4882a593Smuzhiyun  * @irq: pointer where linux IRQ number is stored
21*4882a593Smuzhiyun  *
22*4882a593Smuzhiyun  * irq location updated with irq value [>0 on success, 0 on failure]
23*4882a593Smuzhiyun  *
24*4882a593Smuzhiyun  * Returns: 0 on success
25*4882a593Smuzhiyun  *          -EINVAL on failure
26*4882a593Smuzhiyun  */
acpi_gsi_to_irq(u32 gsi,unsigned int * irq)27*4882a593Smuzhiyun int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
28*4882a593Smuzhiyun {
29*4882a593Smuzhiyun 	struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
30*4882a593Smuzhiyun 							DOMAIN_BUS_ANY);
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 	*irq = irq_find_mapping(d, gsi);
33*4882a593Smuzhiyun 	/*
34*4882a593Smuzhiyun 	 * *irq == 0 means no mapping, that should
35*4882a593Smuzhiyun 	 * be reported as a failure
36*4882a593Smuzhiyun 	 */
37*4882a593Smuzhiyun 	return (*irq > 0) ? 0 : -EINVAL;
38*4882a593Smuzhiyun }
39*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
40*4882a593Smuzhiyun 
41*4882a593Smuzhiyun /**
42*4882a593Smuzhiyun  * acpi_register_gsi() - Map a GSI to a linux IRQ number
43*4882a593Smuzhiyun  * @dev: device for which IRQ has to be mapped
44*4882a593Smuzhiyun  * @gsi: GSI IRQ number
45*4882a593Smuzhiyun  * @trigger: trigger type of the GSI number to be mapped
46*4882a593Smuzhiyun  * @polarity: polarity of the GSI to be mapped
47*4882a593Smuzhiyun  *
48*4882a593Smuzhiyun  * Returns: a valid linux IRQ number on success
49*4882a593Smuzhiyun  *          -EINVAL on failure
50*4882a593Smuzhiyun  */
acpi_register_gsi(struct device * dev,u32 gsi,int trigger,int polarity)51*4882a593Smuzhiyun int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
52*4882a593Smuzhiyun 		      int polarity)
53*4882a593Smuzhiyun {
54*4882a593Smuzhiyun 	struct irq_fwspec fwspec;
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	if (WARN_ON(!acpi_gsi_domain_id)) {
57*4882a593Smuzhiyun 		pr_warn("GSI: No registered irqchip, giving up\n");
58*4882a593Smuzhiyun 		return -EINVAL;
59*4882a593Smuzhiyun 	}
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	fwspec.fwnode = acpi_gsi_domain_id;
62*4882a593Smuzhiyun 	fwspec.param[0] = gsi;
63*4882a593Smuzhiyun 	fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
64*4882a593Smuzhiyun 	fwspec.param_count = 2;
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun 	return irq_create_fwspec_mapping(&fwspec);
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(acpi_register_gsi);
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun /**
71*4882a593Smuzhiyun  * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping
72*4882a593Smuzhiyun  * @gsi: GSI IRQ number
73*4882a593Smuzhiyun  */
acpi_unregister_gsi(u32 gsi)74*4882a593Smuzhiyun void acpi_unregister_gsi(u32 gsi)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
77*4882a593Smuzhiyun 							DOMAIN_BUS_ANY);
78*4882a593Smuzhiyun 	int irq = irq_find_mapping(d, gsi);
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	irq_dispose_mapping(irq);
81*4882a593Smuzhiyun }
82*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun /**
85*4882a593Smuzhiyun  * acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source.
86*4882a593Smuzhiyun  * @source: acpi_resource_source to use for the lookup.
87*4882a593Smuzhiyun  *
88*4882a593Smuzhiyun  * Description:
89*4882a593Smuzhiyun  * Retrieve the fwhandle of the device referenced by the given IRQ resource
90*4882a593Smuzhiyun  * source.
91*4882a593Smuzhiyun  *
92*4882a593Smuzhiyun  * Return:
93*4882a593Smuzhiyun  * The referenced device fwhandle or NULL on failure
94*4882a593Smuzhiyun  */
95*4882a593Smuzhiyun static struct fwnode_handle *
acpi_get_irq_source_fwhandle(const struct acpi_resource_source * source)96*4882a593Smuzhiyun acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun 	struct fwnode_handle *result;
99*4882a593Smuzhiyun 	struct acpi_device *device;
100*4882a593Smuzhiyun 	acpi_handle handle;
101*4882a593Smuzhiyun 	acpi_status status;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	if (!source->string_length)
104*4882a593Smuzhiyun 		return acpi_gsi_domain_id;
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	status = acpi_get_handle(NULL, source->string_ptr, &handle);
107*4882a593Smuzhiyun 	if (WARN_ON(ACPI_FAILURE(status)))
108*4882a593Smuzhiyun 		return NULL;
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 	device = acpi_bus_get_acpi_device(handle);
111*4882a593Smuzhiyun 	if (WARN_ON(!device))
112*4882a593Smuzhiyun 		return NULL;
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	result = &device->fwnode;
115*4882a593Smuzhiyun 	acpi_bus_put_acpi_device(device);
116*4882a593Smuzhiyun 	return result;
117*4882a593Smuzhiyun }
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun /*
120*4882a593Smuzhiyun  * Context for the resource walk used to lookup IRQ resources.
121*4882a593Smuzhiyun  * Contains a return code, the lookup index, and references to the flags
122*4882a593Smuzhiyun  * and fwspec where the result is returned.
123*4882a593Smuzhiyun  */
124*4882a593Smuzhiyun struct acpi_irq_parse_one_ctx {
125*4882a593Smuzhiyun 	int rc;
126*4882a593Smuzhiyun 	unsigned int index;
127*4882a593Smuzhiyun 	unsigned long *res_flags;
128*4882a593Smuzhiyun 	struct irq_fwspec *fwspec;
129*4882a593Smuzhiyun };
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun /**
132*4882a593Smuzhiyun  * acpi_irq_parse_one_match - Handle a matching IRQ resource.
133*4882a593Smuzhiyun  * @fwnode: matching fwnode
134*4882a593Smuzhiyun  * @hwirq: hardware IRQ number
135*4882a593Smuzhiyun  * @triggering: triggering attributes of hwirq
136*4882a593Smuzhiyun  * @polarity: polarity attributes of hwirq
137*4882a593Smuzhiyun  * @polarity: polarity attributes of hwirq
138*4882a593Smuzhiyun  * @shareable: shareable attributes of hwirq
139*4882a593Smuzhiyun  * @ctx: acpi_irq_parse_one_ctx updated by this function
140*4882a593Smuzhiyun  *
141*4882a593Smuzhiyun  * Description:
142*4882a593Smuzhiyun  * Handle a matching IRQ resource by populating the given ctx with
143*4882a593Smuzhiyun  * the information passed.
144*4882a593Smuzhiyun  */
acpi_irq_parse_one_match(struct fwnode_handle * fwnode,u32 hwirq,u8 triggering,u8 polarity,u8 shareable,struct acpi_irq_parse_one_ctx * ctx)145*4882a593Smuzhiyun static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode,
146*4882a593Smuzhiyun 					    u32 hwirq, u8 triggering,
147*4882a593Smuzhiyun 					    u8 polarity, u8 shareable,
148*4882a593Smuzhiyun 					    struct acpi_irq_parse_one_ctx *ctx)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	if (!fwnode)
151*4882a593Smuzhiyun 		return;
152*4882a593Smuzhiyun 	ctx->rc = 0;
153*4882a593Smuzhiyun 	*ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable);
154*4882a593Smuzhiyun 	ctx->fwspec->fwnode = fwnode;
155*4882a593Smuzhiyun 	ctx->fwspec->param[0] = hwirq;
156*4882a593Smuzhiyun 	ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);
157*4882a593Smuzhiyun 	ctx->fwspec->param_count = 2;
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun /**
161*4882a593Smuzhiyun  * acpi_irq_parse_one_cb - Handle the given resource.
162*4882a593Smuzhiyun  * @ares: resource to handle
163*4882a593Smuzhiyun  * @context: context for the walk
164*4882a593Smuzhiyun  *
165*4882a593Smuzhiyun  * Description:
166*4882a593Smuzhiyun  * This is called by acpi_walk_resources passing each resource returned by
167*4882a593Smuzhiyun  * the _CRS method. We only inspect IRQ resources. Since IRQ resources
168*4882a593Smuzhiyun  * might contain multiple interrupts we check if the index is within this
169*4882a593Smuzhiyun  * one's interrupt array, otherwise we subtract the current resource IRQ
170*4882a593Smuzhiyun  * count from the lookup index to prepare for the next resource.
171*4882a593Smuzhiyun  * Once a match is found we call acpi_irq_parse_one_match to populate
172*4882a593Smuzhiyun  * the result and end the walk by returning AE_CTRL_TERMINATE.
173*4882a593Smuzhiyun  *
174*4882a593Smuzhiyun  * Return:
175*4882a593Smuzhiyun  * AE_OK if the walk should continue, AE_CTRL_TERMINATE if a matching
176*4882a593Smuzhiyun  * IRQ resource was found.
177*4882a593Smuzhiyun  */
acpi_irq_parse_one_cb(struct acpi_resource * ares,void * context)178*4882a593Smuzhiyun static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
179*4882a593Smuzhiyun 					 void *context)
180*4882a593Smuzhiyun {
181*4882a593Smuzhiyun 	struct acpi_irq_parse_one_ctx *ctx = context;
182*4882a593Smuzhiyun 	struct acpi_resource_irq *irq;
183*4882a593Smuzhiyun 	struct acpi_resource_extended_irq *eirq;
184*4882a593Smuzhiyun 	struct fwnode_handle *fwnode;
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	switch (ares->type) {
187*4882a593Smuzhiyun 	case ACPI_RESOURCE_TYPE_IRQ:
188*4882a593Smuzhiyun 		irq = &ares->data.irq;
189*4882a593Smuzhiyun 		if (ctx->index >= irq->interrupt_count) {
190*4882a593Smuzhiyun 			ctx->index -= irq->interrupt_count;
191*4882a593Smuzhiyun 			return AE_OK;
192*4882a593Smuzhiyun 		}
193*4882a593Smuzhiyun 		fwnode = acpi_gsi_domain_id;
194*4882a593Smuzhiyun 		acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
195*4882a593Smuzhiyun 					 irq->triggering, irq->polarity,
196*4882a593Smuzhiyun 					 irq->shareable, ctx);
197*4882a593Smuzhiyun 		return AE_CTRL_TERMINATE;
198*4882a593Smuzhiyun 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
199*4882a593Smuzhiyun 		eirq = &ares->data.extended_irq;
200*4882a593Smuzhiyun 		if (eirq->producer_consumer == ACPI_PRODUCER)
201*4882a593Smuzhiyun 			return AE_OK;
202*4882a593Smuzhiyun 		if (ctx->index >= eirq->interrupt_count) {
203*4882a593Smuzhiyun 			ctx->index -= eirq->interrupt_count;
204*4882a593Smuzhiyun 			return AE_OK;
205*4882a593Smuzhiyun 		}
206*4882a593Smuzhiyun 		fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source);
207*4882a593Smuzhiyun 		acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
208*4882a593Smuzhiyun 					 eirq->triggering, eirq->polarity,
209*4882a593Smuzhiyun 					 eirq->shareable, ctx);
210*4882a593Smuzhiyun 		return AE_CTRL_TERMINATE;
211*4882a593Smuzhiyun 	}
212*4882a593Smuzhiyun 
213*4882a593Smuzhiyun 	return AE_OK;
214*4882a593Smuzhiyun }
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun /**
217*4882a593Smuzhiyun  * acpi_irq_parse_one - Resolve an interrupt for a device
218*4882a593Smuzhiyun  * @handle: the device whose interrupt is to be resolved
219*4882a593Smuzhiyun  * @index: index of the interrupt to resolve
220*4882a593Smuzhiyun  * @fwspec: structure irq_fwspec filled by this function
221*4882a593Smuzhiyun  * @flags: resource flags filled by this function
222*4882a593Smuzhiyun  *
223*4882a593Smuzhiyun  * Description:
224*4882a593Smuzhiyun  * Resolves an interrupt for a device by walking its CRS resources to find
225*4882a593Smuzhiyun  * the appropriate ACPI IRQ resource and populating the given struct irq_fwspec
226*4882a593Smuzhiyun  * and flags.
227*4882a593Smuzhiyun  *
228*4882a593Smuzhiyun  * Return:
229*4882a593Smuzhiyun  * The result stored in ctx.rc by the callback, or the default -EINVAL value
230*4882a593Smuzhiyun  * if an error occurs.
231*4882a593Smuzhiyun  */
acpi_irq_parse_one(acpi_handle handle,unsigned int index,struct irq_fwspec * fwspec,unsigned long * flags)232*4882a593Smuzhiyun static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
233*4882a593Smuzhiyun 			      struct irq_fwspec *fwspec, unsigned long *flags)
234*4882a593Smuzhiyun {
235*4882a593Smuzhiyun 	struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 	acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
238*4882a593Smuzhiyun 	return ctx.rc;
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun /**
242*4882a593Smuzhiyun  * acpi_irq_get - Lookup an ACPI IRQ resource and use it to initialize resource.
243*4882a593Smuzhiyun  * @handle: ACPI device handle
244*4882a593Smuzhiyun  * @index:  ACPI IRQ resource index to lookup
245*4882a593Smuzhiyun  * @res:    Linux IRQ resource to initialize
246*4882a593Smuzhiyun  *
247*4882a593Smuzhiyun  * Description:
248*4882a593Smuzhiyun  * Look for the ACPI IRQ resource with the given index and use it to initialize
249*4882a593Smuzhiyun  * the given Linux IRQ resource.
250*4882a593Smuzhiyun  *
251*4882a593Smuzhiyun  * Return:
252*4882a593Smuzhiyun  * 0 on success
253*4882a593Smuzhiyun  * -EINVAL if an error occurs
254*4882a593Smuzhiyun  * -EPROBE_DEFER if the IRQ lookup/conversion failed
255*4882a593Smuzhiyun  */
acpi_irq_get(acpi_handle handle,unsigned int index,struct resource * res)256*4882a593Smuzhiyun int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun 	struct irq_fwspec fwspec;
259*4882a593Smuzhiyun 	struct irq_domain *domain;
260*4882a593Smuzhiyun 	unsigned long flags;
261*4882a593Smuzhiyun 	int rc;
262*4882a593Smuzhiyun 
263*4882a593Smuzhiyun 	rc = acpi_irq_parse_one(handle, index, &fwspec, &flags);
264*4882a593Smuzhiyun 	if (rc)
265*4882a593Smuzhiyun 		return rc;
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	domain = irq_find_matching_fwnode(fwspec.fwnode, DOMAIN_BUS_ANY);
268*4882a593Smuzhiyun 	if (!domain)
269*4882a593Smuzhiyun 		return -EPROBE_DEFER;
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	rc = irq_create_fwspec_mapping(&fwspec);
272*4882a593Smuzhiyun 	if (rc <= 0)
273*4882a593Smuzhiyun 		return -EINVAL;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	res->start = rc;
276*4882a593Smuzhiyun 	res->end = rc;
277*4882a593Smuzhiyun 	res->flags = flags;
278*4882a593Smuzhiyun 
279*4882a593Smuzhiyun 	return 0;
280*4882a593Smuzhiyun }
281*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(acpi_irq_get);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun /**
284*4882a593Smuzhiyun  * acpi_set_irq_model - Setup the GSI irqdomain information
285*4882a593Smuzhiyun  * @model: the value assigned to acpi_irq_model
286*4882a593Smuzhiyun  * @fwnode: the irq_domain identifier for mapping and looking up
287*4882a593Smuzhiyun  *          GSI interrupts
288*4882a593Smuzhiyun  */
acpi_set_irq_model(enum acpi_irq_model_id model,struct fwnode_handle * fwnode)289*4882a593Smuzhiyun void __init acpi_set_irq_model(enum acpi_irq_model_id model,
290*4882a593Smuzhiyun 			       struct fwnode_handle *fwnode)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun 	acpi_irq_model = model;
293*4882a593Smuzhiyun 	acpi_gsi_domain_id = fwnode;
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun 
296*4882a593Smuzhiyun /**
297*4882a593Smuzhiyun  * acpi_irq_create_hierarchy - Create a hierarchical IRQ domain with the default
298*4882a593Smuzhiyun  *                             GSI domain as its parent.
299*4882a593Smuzhiyun  * @flags:      Irq domain flags associated with the domain
300*4882a593Smuzhiyun  * @size:       Size of the domain.
301*4882a593Smuzhiyun  * @fwnode:     Optional fwnode of the interrupt controller
302*4882a593Smuzhiyun  * @ops:        Pointer to the interrupt domain callbacks
303*4882a593Smuzhiyun  * @host_data:  Controller private data pointer
304*4882a593Smuzhiyun  */
acpi_irq_create_hierarchy(unsigned int flags,unsigned int size,struct fwnode_handle * fwnode,const struct irq_domain_ops * ops,void * host_data)305*4882a593Smuzhiyun struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
306*4882a593Smuzhiyun 					     unsigned int size,
307*4882a593Smuzhiyun 					     struct fwnode_handle *fwnode,
308*4882a593Smuzhiyun 					     const struct irq_domain_ops *ops,
309*4882a593Smuzhiyun 					     void *host_data)
310*4882a593Smuzhiyun {
311*4882a593Smuzhiyun 	struct irq_domain *d = irq_find_matching_fwnode(acpi_gsi_domain_id,
312*4882a593Smuzhiyun 							DOMAIN_BUS_ANY);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	if (!d)
315*4882a593Smuzhiyun 		return NULL;
316*4882a593Smuzhiyun 
317*4882a593Smuzhiyun 	return irq_domain_create_hierarchy(d, flags, size, fwnode, ops,
318*4882a593Smuzhiyun 					   host_data);
319*4882a593Smuzhiyun }
320*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy);
321