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