Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * ACPI GSI IRQ layer
0004  *
0005  * Copyright (C) 2015 ARM Ltd.
0006  * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
0007  */
0008 #include <linux/acpi.h>
0009 #include <linux/irq.h>
0010 #include <linux/irqdomain.h>
0011 #include <linux/of.h>
0012 
0013 enum acpi_irq_model_id acpi_irq_model;
0014 
0015 static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);
0016 static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);
0017 
0018 /**
0019  * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI
0020  * @gsi: GSI IRQ number to map
0021  * @irq: pointer where linux IRQ number is stored
0022  *
0023  * irq location updated with irq value [>0 on success, 0 on failure]
0024  *
0025  * Returns: 0 on success
0026  *          -EINVAL on failure
0027  */
0028 int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
0029 {
0030     struct irq_domain *d;
0031 
0032     d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
0033                     DOMAIN_BUS_ANY);
0034     *irq = irq_find_mapping(d, gsi);
0035     /*
0036      * *irq == 0 means no mapping, that should be reported as a
0037      * failure, unless there is an arch-specific fallback handler.
0038      */
0039     if (!*irq && acpi_gsi_to_irq_fallback)
0040         *irq = acpi_gsi_to_irq_fallback(gsi);
0041 
0042     return (*irq > 0) ? 0 : -EINVAL;
0043 }
0044 EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
0045 
0046 /**
0047  * acpi_register_gsi() - Map a GSI to a linux IRQ number
0048  * @dev: device for which IRQ has to be mapped
0049  * @gsi: GSI IRQ number
0050  * @trigger: trigger type of the GSI number to be mapped
0051  * @polarity: polarity of the GSI to be mapped
0052  *
0053  * Returns: a valid linux IRQ number on success
0054  *          -EINVAL on failure
0055  */
0056 int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,
0057               int polarity)
0058 {
0059     struct irq_fwspec fwspec;
0060 
0061     fwspec.fwnode = acpi_get_gsi_domain_id(gsi);
0062     if (WARN_ON(!fwspec.fwnode)) {
0063         pr_warn("GSI: No registered irqchip, giving up\n");
0064         return -EINVAL;
0065     }
0066 
0067     fwspec.param[0] = gsi;
0068     fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);
0069     fwspec.param_count = 2;
0070 
0071     return irq_create_fwspec_mapping(&fwspec);
0072 }
0073 EXPORT_SYMBOL_GPL(acpi_register_gsi);
0074 
0075 /**
0076  * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping
0077  * @gsi: GSI IRQ number
0078  */
0079 void acpi_unregister_gsi(u32 gsi)
0080 {
0081     struct irq_domain *d;
0082     int irq;
0083 
0084     if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))
0085         return;
0086 
0087     d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),
0088                      DOMAIN_BUS_ANY);
0089     irq = irq_find_mapping(d, gsi);
0090     irq_dispose_mapping(irq);
0091 }
0092 EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
0093 
0094 /**
0095  * acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source.
0096  * @source: acpi_resource_source to use for the lookup.
0097  *
0098  * Description:
0099  * Retrieve the fwhandle of the device referenced by the given IRQ resource
0100  * source.
0101  *
0102  * Return:
0103  * The referenced device fwhandle or NULL on failure
0104  */
0105 static struct fwnode_handle *
0106 acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,
0107                  u32 gsi)
0108 {
0109     struct fwnode_handle *result;
0110     struct acpi_device *device;
0111     acpi_handle handle;
0112     acpi_status status;
0113 
0114     if (!source->string_length)
0115         return acpi_get_gsi_domain_id(gsi);
0116 
0117     status = acpi_get_handle(NULL, source->string_ptr, &handle);
0118     if (WARN_ON(ACPI_FAILURE(status)))
0119         return NULL;
0120 
0121     device = acpi_bus_get_acpi_device(handle);
0122     if (WARN_ON(!device))
0123         return NULL;
0124 
0125     result = &device->fwnode;
0126     acpi_bus_put_acpi_device(device);
0127     return result;
0128 }
0129 
0130 /*
0131  * Context for the resource walk used to lookup IRQ resources.
0132  * Contains a return code, the lookup index, and references to the flags
0133  * and fwspec where the result is returned.
0134  */
0135 struct acpi_irq_parse_one_ctx {
0136     int rc;
0137     unsigned int index;
0138     unsigned long *res_flags;
0139     struct irq_fwspec *fwspec;
0140 };
0141 
0142 /**
0143  * acpi_irq_parse_one_match - Handle a matching IRQ resource.
0144  * @fwnode: matching fwnode
0145  * @hwirq: hardware IRQ number
0146  * @triggering: triggering attributes of hwirq
0147  * @polarity: polarity attributes of hwirq
0148  * @polarity: polarity attributes of hwirq
0149  * @shareable: shareable attributes of hwirq
0150  * @ctx: acpi_irq_parse_one_ctx updated by this function
0151  *
0152  * Description:
0153  * Handle a matching IRQ resource by populating the given ctx with
0154  * the information passed.
0155  */
0156 static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode,
0157                         u32 hwirq, u8 triggering,
0158                         u8 polarity, u8 shareable,
0159                         struct acpi_irq_parse_one_ctx *ctx)
0160 {
0161     if (!fwnode)
0162         return;
0163     ctx->rc = 0;
0164     *ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable);
0165     ctx->fwspec->fwnode = fwnode;
0166     ctx->fwspec->param[0] = hwirq;
0167     ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);
0168     ctx->fwspec->param_count = 2;
0169 }
0170 
0171 /**
0172  * acpi_irq_parse_one_cb - Handle the given resource.
0173  * @ares: resource to handle
0174  * @context: context for the walk
0175  *
0176  * Description:
0177  * This is called by acpi_walk_resources passing each resource returned by
0178  * the _CRS method. We only inspect IRQ resources. Since IRQ resources
0179  * might contain multiple interrupts we check if the index is within this
0180  * one's interrupt array, otherwise we subtract the current resource IRQ
0181  * count from the lookup index to prepare for the next resource.
0182  * Once a match is found we call acpi_irq_parse_one_match to populate
0183  * the result and end the walk by returning AE_CTRL_TERMINATE.
0184  *
0185  * Return:
0186  * AE_OK if the walk should continue, AE_CTRL_TERMINATE if a matching
0187  * IRQ resource was found.
0188  */
0189 static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,
0190                      void *context)
0191 {
0192     struct acpi_irq_parse_one_ctx *ctx = context;
0193     struct acpi_resource_irq *irq;
0194     struct acpi_resource_extended_irq *eirq;
0195     struct fwnode_handle *fwnode;
0196 
0197     switch (ares->type) {
0198     case ACPI_RESOURCE_TYPE_IRQ:
0199         irq = &ares->data.irq;
0200         if (ctx->index >= irq->interrupt_count) {
0201             ctx->index -= irq->interrupt_count;
0202             return AE_OK;
0203         }
0204         fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);
0205         acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],
0206                      irq->triggering, irq->polarity,
0207                      irq->shareable, ctx);
0208         return AE_CTRL_TERMINATE;
0209     case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
0210         eirq = &ares->data.extended_irq;
0211         if (eirq->producer_consumer == ACPI_PRODUCER)
0212             return AE_OK;
0213         if (ctx->index >= eirq->interrupt_count) {
0214             ctx->index -= eirq->interrupt_count;
0215             return AE_OK;
0216         }
0217         fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source,
0218                               eirq->interrupts[ctx->index]);
0219         acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],
0220                      eirq->triggering, eirq->polarity,
0221                      eirq->shareable, ctx);
0222         return AE_CTRL_TERMINATE;
0223     }
0224 
0225     return AE_OK;
0226 }
0227 
0228 /**
0229  * acpi_irq_parse_one - Resolve an interrupt for a device
0230  * @handle: the device whose interrupt is to be resolved
0231  * @index: index of the interrupt to resolve
0232  * @fwspec: structure irq_fwspec filled by this function
0233  * @flags: resource flags filled by this function
0234  *
0235  * Description:
0236  * Resolves an interrupt for a device by walking its CRS resources to find
0237  * the appropriate ACPI IRQ resource and populating the given struct irq_fwspec
0238  * and flags.
0239  *
0240  * Return:
0241  * The result stored in ctx.rc by the callback, or the default -EINVAL value
0242  * if an error occurs.
0243  */
0244 static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,
0245                   struct irq_fwspec *fwspec, unsigned long *flags)
0246 {
0247     struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };
0248 
0249     acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);
0250     return ctx.rc;
0251 }
0252 
0253 /**
0254  * acpi_irq_get - Lookup an ACPI IRQ resource and use it to initialize resource.
0255  * @handle: ACPI device handle
0256  * @index:  ACPI IRQ resource index to lookup
0257  * @res:    Linux IRQ resource to initialize
0258  *
0259  * Description:
0260  * Look for the ACPI IRQ resource with the given index and use it to initialize
0261  * the given Linux IRQ resource.
0262  *
0263  * Return:
0264  * 0 on success
0265  * -EINVAL if an error occurs
0266  * -EPROBE_DEFER if the IRQ lookup/conversion failed
0267  */
0268 int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)
0269 {
0270     struct irq_fwspec fwspec;
0271     struct irq_domain *domain;
0272     unsigned long flags;
0273     int rc;
0274 
0275     rc = acpi_irq_parse_one(handle, index, &fwspec, &flags);
0276     if (rc)
0277         return rc;
0278 
0279     domain = irq_find_matching_fwnode(fwspec.fwnode, DOMAIN_BUS_ANY);
0280     if (!domain)
0281         return -EPROBE_DEFER;
0282 
0283     rc = irq_create_fwspec_mapping(&fwspec);
0284     if (rc <= 0)
0285         return -EINVAL;
0286 
0287     res->start = rc;
0288     res->end = rc;
0289     res->flags = flags;
0290 
0291     return 0;
0292 }
0293 EXPORT_SYMBOL_GPL(acpi_irq_get);
0294 
0295 /**
0296  * acpi_set_irq_model - Setup the GSI irqdomain information
0297  * @model: the value assigned to acpi_irq_model
0298  * @fwnode: the irq_domain identifier for mapping and looking up
0299  *          GSI interrupts
0300  */
0301 void __init acpi_set_irq_model(enum acpi_irq_model_id model,
0302                    struct fwnode_handle *(*fn)(u32))
0303 {
0304     acpi_irq_model = model;
0305     acpi_get_gsi_domain_id = fn;
0306 }
0307 
0308 /**
0309  * acpi_set_gsi_to_irq_fallback - Register a GSI transfer
0310  * callback to fallback to arch specified implementation.
0311  * @fn: arch-specific fallback handler
0312  */
0313 void __init acpi_set_gsi_to_irq_fallback(u32 (*fn)(u32))
0314 {
0315     acpi_gsi_to_irq_fallback = fn;
0316 }
0317 
0318 /**
0319  * acpi_irq_create_hierarchy - Create a hierarchical IRQ domain with the default
0320  *                             GSI domain as its parent.
0321  * @flags:      Irq domain flags associated with the domain
0322  * @size:       Size of the domain.
0323  * @fwnode:     Optional fwnode of the interrupt controller
0324  * @ops:        Pointer to the interrupt domain callbacks
0325  * @host_data:  Controller private data pointer
0326  */
0327 struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,
0328                          unsigned int size,
0329                          struct fwnode_handle *fwnode,
0330                          const struct irq_domain_ops *ops,
0331                          void *host_data)
0332 {
0333     struct irq_domain *d;
0334 
0335     /* This only works for the GIC model... */
0336     if (acpi_irq_model != ACPI_IRQ_MODEL_GIC)
0337         return NULL;
0338 
0339     d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(0),
0340                      DOMAIN_BUS_ANY);
0341 
0342     if (!d)
0343         return NULL;
0344 
0345     return irq_domain_create_hierarchy(d, flags, size, fwnode, ops,
0346                        host_data);
0347 }
0348 EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy);