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