brintos

brintos / linux-shallow public Read only

0
0
Text · 9.9 KiB · 1687483 Raw
357 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * ACPI GSI IRQ layer4 *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 13enum acpi_irq_model_id acpi_irq_model;14 15static struct fwnode_handle *(*acpi_get_gsi_domain_id)(u32 gsi);16static u32 (*acpi_gsi_to_irq_fallback)(u32 gsi);17 18/**19 * acpi_gsi_to_irq() - Retrieve the linux irq number for a given GSI20 * @gsi: GSI IRQ number to map21 * @irq: pointer where linux IRQ number is stored22 *23 * irq location updated with irq value [>0 on success, 0 on failure]24 *25 * Returns: 0 on success26 *          -EINVAL on failure27 */28int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)29{30	struct irq_domain *d;31 32	d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),33					DOMAIN_BUS_ANY);34	*irq = irq_find_mapping(d, gsi);35	/*36	 * *irq == 0 means no mapping, that should be reported as a37	 * failure, unless there is an arch-specific fallback handler.38	 */39	if (!*irq && acpi_gsi_to_irq_fallback)40		*irq = acpi_gsi_to_irq_fallback(gsi);41 42	return (*irq > 0) ? 0 : -EINVAL;43}44EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);45 46/**47 * acpi_register_gsi() - Map a GSI to a linux IRQ number48 * @dev: device for which IRQ has to be mapped49 * @gsi: GSI IRQ number50 * @trigger: trigger type of the GSI number to be mapped51 * @polarity: polarity of the GSI to be mapped52 *53 * Returns: a valid linux IRQ number on success54 *          -EINVAL on failure55 */56int acpi_register_gsi(struct device *dev, u32 gsi, int trigger,57		      int polarity)58{59	struct irq_fwspec fwspec;60	unsigned int irq;61 62	fwspec.fwnode = acpi_get_gsi_domain_id(gsi);63	if (WARN_ON(!fwspec.fwnode)) {64		pr_warn("GSI: No registered irqchip, giving up\n");65		return -EINVAL;66	}67 68	fwspec.param[0] = gsi;69	fwspec.param[1] = acpi_dev_get_irq_type(trigger, polarity);70	fwspec.param_count = 2;71 72	irq = irq_create_fwspec_mapping(&fwspec);73	if (!irq)74		return -EINVAL;75 76	return irq;77}78EXPORT_SYMBOL_GPL(acpi_register_gsi);79 80/**81 * acpi_unregister_gsi() - Free a GSI<->linux IRQ number mapping82 * @gsi: GSI IRQ number83 */84void acpi_unregister_gsi(u32 gsi)85{86	struct irq_domain *d;87	int irq;88 89	if (WARN_ON(acpi_irq_model == ACPI_IRQ_MODEL_GIC && gsi < 16))90		return;91 92	d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(gsi),93				     DOMAIN_BUS_ANY);94	irq = irq_find_mapping(d, gsi);95	irq_dispose_mapping(irq);96}97EXPORT_SYMBOL_GPL(acpi_unregister_gsi);98 99/**100 * acpi_get_irq_source_fwhandle() - Retrieve fwhandle from IRQ resource source.101 * @source: acpi_resource_source to use for the lookup.102 * @gsi: GSI IRQ number103 *104 * Description:105 * Retrieve the fwhandle of the device referenced by the given IRQ resource106 * source.107 *108 * Return:109 * The referenced device fwhandle or NULL on failure110 */111static struct fwnode_handle *112acpi_get_irq_source_fwhandle(const struct acpi_resource_source *source,113			     u32 gsi)114{115	struct fwnode_handle *result;116	struct acpi_device *device;117	acpi_handle handle;118	acpi_status status;119 120	if (!source->string_length)121		return acpi_get_gsi_domain_id(gsi);122 123	status = acpi_get_handle(NULL, source->string_ptr, &handle);124	if (WARN_ON(ACPI_FAILURE(status)))125		return NULL;126 127	device = acpi_get_acpi_dev(handle);128	if (WARN_ON(!device))129		return NULL;130 131	result = &device->fwnode;132	acpi_put_acpi_dev(device);133	return result;134}135 136/*137 * Context for the resource walk used to lookup IRQ resources.138 * Contains a return code, the lookup index, and references to the flags139 * and fwspec where the result is returned.140 */141struct acpi_irq_parse_one_ctx {142	int rc;143	unsigned int index;144	unsigned long *res_flags;145	struct irq_fwspec *fwspec;146};147 148/**149 * acpi_irq_parse_one_match - Handle a matching IRQ resource.150 * @fwnode: matching fwnode151 * @hwirq: hardware IRQ number152 * @triggering: triggering attributes of hwirq153 * @polarity: polarity attributes of hwirq154 * @polarity: polarity attributes of hwirq155 * @shareable: shareable attributes of hwirq156 * @wake_capable: wake capable attribute of hwirq157 * @ctx: acpi_irq_parse_one_ctx updated by this function158 *159 * Description:160 * Handle a matching IRQ resource by populating the given ctx with161 * the information passed.162 */163static inline void acpi_irq_parse_one_match(struct fwnode_handle *fwnode,164					    u32 hwirq, u8 triggering,165					    u8 polarity, u8 shareable,166					    u8 wake_capable,167					    struct acpi_irq_parse_one_ctx *ctx)168{169	if (!fwnode)170		return;171	ctx->rc = 0;172	*ctx->res_flags = acpi_dev_irq_flags(triggering, polarity, shareable, wake_capable);173	ctx->fwspec->fwnode = fwnode;174	ctx->fwspec->param[0] = hwirq;175	ctx->fwspec->param[1] = acpi_dev_get_irq_type(triggering, polarity);176	ctx->fwspec->param_count = 2;177}178 179/**180 * acpi_irq_parse_one_cb - Handle the given resource.181 * @ares: resource to handle182 * @context: context for the walk183 *184 * Description:185 * This is called by acpi_walk_resources passing each resource returned by186 * the _CRS method. We only inspect IRQ resources. Since IRQ resources187 * might contain multiple interrupts we check if the index is within this188 * one's interrupt array, otherwise we subtract the current resource IRQ189 * count from the lookup index to prepare for the next resource.190 * Once a match is found we call acpi_irq_parse_one_match to populate191 * the result and end the walk by returning AE_CTRL_TERMINATE.192 *193 * Return:194 * AE_OK if the walk should continue, AE_CTRL_TERMINATE if a matching195 * IRQ resource was found.196 */197static acpi_status acpi_irq_parse_one_cb(struct acpi_resource *ares,198					 void *context)199{200	struct acpi_irq_parse_one_ctx *ctx = context;201	struct acpi_resource_irq *irq;202	struct acpi_resource_extended_irq *eirq;203	struct fwnode_handle *fwnode;204 205	switch (ares->type) {206	case ACPI_RESOURCE_TYPE_IRQ:207		irq = &ares->data.irq;208		if (ctx->index >= irq->interrupt_count) {209			ctx->index -= irq->interrupt_count;210			return AE_OK;211		}212		fwnode = acpi_get_gsi_domain_id(irq->interrupts[ctx->index]);213		acpi_irq_parse_one_match(fwnode, irq->interrupts[ctx->index],214					 irq->triggering, irq->polarity,215					 irq->shareable, irq->wake_capable, ctx);216		return AE_CTRL_TERMINATE;217	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:218		eirq = &ares->data.extended_irq;219		if (eirq->producer_consumer == ACPI_PRODUCER)220			return AE_OK;221		if (ctx->index >= eirq->interrupt_count) {222			ctx->index -= eirq->interrupt_count;223			return AE_OK;224		}225		fwnode = acpi_get_irq_source_fwhandle(&eirq->resource_source,226						      eirq->interrupts[ctx->index]);227		acpi_irq_parse_one_match(fwnode, eirq->interrupts[ctx->index],228					 eirq->triggering, eirq->polarity,229					 eirq->shareable, eirq->wake_capable, ctx);230		return AE_CTRL_TERMINATE;231	}232 233	return AE_OK;234}235 236/**237 * acpi_irq_parse_one - Resolve an interrupt for a device238 * @handle: the device whose interrupt is to be resolved239 * @index: index of the interrupt to resolve240 * @fwspec: structure irq_fwspec filled by this function241 * @flags: resource flags filled by this function242 *243 * Description:244 * Resolves an interrupt for a device by walking its CRS resources to find245 * the appropriate ACPI IRQ resource and populating the given struct irq_fwspec246 * and flags.247 *248 * Return:249 * The result stored in ctx.rc by the callback, or the default -EINVAL value250 * if an error occurs.251 */252static int acpi_irq_parse_one(acpi_handle handle, unsigned int index,253			      struct irq_fwspec *fwspec, unsigned long *flags)254{255	struct acpi_irq_parse_one_ctx ctx = { -EINVAL, index, flags, fwspec };256 257	acpi_walk_resources(handle, METHOD_NAME__CRS, acpi_irq_parse_one_cb, &ctx);258	return ctx.rc;259}260 261/**262 * acpi_irq_get - Lookup an ACPI IRQ resource and use it to initialize resource.263 * @handle: ACPI device handle264 * @index:  ACPI IRQ resource index to lookup265 * @res:    Linux IRQ resource to initialize266 *267 * Description:268 * Look for the ACPI IRQ resource with the given index and use it to initialize269 * the given Linux IRQ resource.270 *271 * Return:272 * 0 on success273 * -EINVAL if an error occurs274 * -EPROBE_DEFER if the IRQ lookup/conversion failed275 */276int acpi_irq_get(acpi_handle handle, unsigned int index, struct resource *res)277{278	struct irq_fwspec fwspec;279	struct irq_domain *domain;280	unsigned long flags;281	int rc;282 283	rc = acpi_irq_parse_one(handle, index, &fwspec, &flags);284	if (rc)285		return rc;286 287	domain = irq_find_matching_fwnode(fwspec.fwnode, DOMAIN_BUS_ANY);288	if (!domain)289		return -EPROBE_DEFER;290 291	rc = irq_create_fwspec_mapping(&fwspec);292	if (rc <= 0)293		return -EINVAL;294 295	res->start = rc;296	res->end = rc;297	res->flags = flags;298 299	return 0;300}301EXPORT_SYMBOL_GPL(acpi_irq_get);302 303/**304 * acpi_set_irq_model - Setup the GSI irqdomain information305 * @model: the value assigned to acpi_irq_model306 * @fn: a dispatcher function that will return the domain fwnode307 *	for a given GSI308 */309void __init acpi_set_irq_model(enum acpi_irq_model_id model,310			       struct fwnode_handle *(*fn)(u32))311{312	acpi_irq_model = model;313	acpi_get_gsi_domain_id = fn;314}315 316/**317 * acpi_set_gsi_to_irq_fallback - Register a GSI transfer318 * callback to fallback to arch specified implementation.319 * @fn: arch-specific fallback handler320 */321void __init acpi_set_gsi_to_irq_fallback(u32 (*fn)(u32))322{323	acpi_gsi_to_irq_fallback = fn;324}325 326/**327 * acpi_irq_create_hierarchy - Create a hierarchical IRQ domain with the default328 *                             GSI domain as its parent.329 * @flags:      Irq domain flags associated with the domain330 * @size:       Size of the domain.331 * @fwnode:     Optional fwnode of the interrupt controller332 * @ops:        Pointer to the interrupt domain callbacks333 * @host_data:  Controller private data pointer334 */335struct irq_domain *acpi_irq_create_hierarchy(unsigned int flags,336					     unsigned int size,337					     struct fwnode_handle *fwnode,338					     const struct irq_domain_ops *ops,339					     void *host_data)340{341	struct irq_domain *d;342 343	/* This only works for the GIC model... */344	if (acpi_irq_model != ACPI_IRQ_MODEL_GIC)345		return NULL;346 347	d = irq_find_matching_fwnode(acpi_get_gsi_domain_id(0),348				     DOMAIN_BUS_ANY);349 350	if (!d)351		return NULL;352 353	return irq_domain_create_hierarchy(d, flags, size, fwnode, ops,354					   host_data);355}356EXPORT_SYMBOL_GPL(acpi_irq_create_hierarchy);357