1583 lines · 41.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 1992, 1998-2006 Linus Torvalds, Ingo Molnar * Copyright (C) 2005-2006, Thomas Gleixner, Russell King * * This file contains the core interrupt handling code, for irq-chip based * architectures. Detailed information is available in * Documentation/core-api/genericirq.rst */ #include <linux/irq.h>#include <linux/msi.h>#include <linux/module.h>#include <linux/interrupt.h>#include <linux/kernel_stat.h>#include <linux/irqdomain.h>#include <linux/preempt.h>#include <linux/random.h> #include <trace/events/irq.h> #include "internals.h" static irqreturn_t bad_chained_irq(int irq, void *dev_id){	WARN_ONCE(1, "Chained irq %d should not call an action\n", irq);	return IRQ_NONE;} /* * Chained handlers should never call action on their IRQ. This default * action will emit warning if such thing happens. */struct irqaction chained_action = {	.handler = bad_chained_irq,}; /** * irq_set_chip - set the irq chip for an irq * @irq:	irq number * @chip:	pointer to irq chip description structure */int irq_set_chip(unsigned int irq, const struct irq_chip *chip){	int ret = -EINVAL; 	scoped_irqdesc_get_and_lock(irq, 0) {		scoped_irqdesc->irq_data.chip = (struct irq_chip *)(chip ?: &no_irq_chip);		ret = 0;	}	if (!ret) {		/* For !CONFIG_SPARSE_IRQ make the irq show up in allocated_irqs. */		irq_mark_irq(irq);		irq_proc_update_chip(chip);	}	return ret;}EXPORT_SYMBOL(irq_set_chip); /** * irq_set_irq_type - set the irq trigger type for an irq * @irq:	irq number * @type:	IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h */int irq_set_irq_type(unsigned int irq, unsigned int type){	scoped_irqdesc_get_and_buslock(irq, IRQ_GET_DESC_CHECK_GLOBAL)		return __irq_set_trigger(scoped_irqdesc, type);	return -EINVAL;}EXPORT_SYMBOL(irq_set_irq_type); /** * irq_set_handler_data - set irq handler data for an irq * @irq:	Interrupt number * @data:	Pointer to interrupt specific data * * Set the hardware irq controller data for an irq */int irq_set_handler_data(unsigned int irq, void *data){	scoped_irqdesc_get_and_lock(irq, 0) {		scoped_irqdesc->irq_common_data.handler_data = data;		return 0;	}	return -EINVAL;}EXPORT_SYMBOL(irq_set_handler_data); /** * irq_set_msi_desc_off - set MSI descriptor data for an irq at offset * @irq_base:	Interrupt number base * @irq_offset:	Interrupt number offset * @entry:		Pointer to MSI descriptor data * * Set the MSI descriptor entry for an irq at offset */int irq_set_msi_desc_off(unsigned int irq_base, unsigned int irq_offset, struct msi_desc *entry){	scoped_irqdesc_get_and_lock(irq_base + irq_offset, IRQ_GET_DESC_CHECK_GLOBAL) {		scoped_irqdesc->irq_common_data.msi_desc = entry;		if (entry && !irq_offset)			entry->irq = irq_base;		return 0;	}	return -EINVAL;} /** * irq_set_msi_desc - set MSI descriptor data for an irq * @irq:	Interrupt number * @entry:	Pointer to MSI descriptor data * * Set the MSI descriptor entry for an irq */int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry){	return irq_set_msi_desc_off(irq, 0, entry);} /** * irq_set_chip_data - set irq chip data for an irq * @irq:	Interrupt number * @data:	Pointer to chip specific data * * Set the hardware irq chip data for an irq */int irq_set_chip_data(unsigned int irq, void *data){	scoped_irqdesc_get_and_lock(irq, 0) {		scoped_irqdesc->irq_data.chip_data = data;		return 0;	}	return -EINVAL;}EXPORT_SYMBOL(irq_set_chip_data); struct irq_data *irq_get_irq_data(unsigned int irq){	struct irq_desc *desc = irq_to_desc(irq); 	return desc ? &desc->irq_data : NULL;}EXPORT_SYMBOL_GPL(irq_get_irq_data); static void irq_state_clr_disabled(struct irq_desc *desc){	irqd_clear(&desc->irq_data, IRQD_IRQ_DISABLED);} static void irq_state_clr_masked(struct irq_desc *desc){	irqd_clear(&desc->irq_data, IRQD_IRQ_MASKED);} static void irq_state_clr_started(struct irq_desc *desc){	irqd_clear(&desc->irq_data, IRQD_IRQ_STARTED);} static void irq_state_set_started(struct irq_desc *desc){	irqd_set(&desc->irq_data, IRQD_IRQ_STARTED);} enum {	IRQ_STARTUP_NORMAL,	IRQ_STARTUP_MANAGED,	IRQ_STARTUP_ABORT,}; #ifdef CONFIG_SMPstatic int__irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,		      bool force){	struct irq_data *d = irq_desc_get_irq_data(desc); 	if (!irqd_affinity_is_managed(d))		return IRQ_STARTUP_NORMAL; 	irqd_clr_managed_shutdown(d); 	if (!cpumask_intersects(aff, cpu_online_mask)) {		/*		 * Catch code which fiddles with enable_irq() on a managed		 * and potentially shutdown IRQ. Chained interrupt		 * installment or irq auto probing should not happen on		 * managed irqs either.		 */		if (WARN_ON_ONCE(force))			return IRQ_STARTUP_ABORT;		/*		 * The interrupt was requested, but there is no online CPU		 * in it's affinity mask. Put it into managed shutdown		 * state and let the cpu hotplug mechanism start it up once		 * a CPU in the mask becomes available.		 */		return IRQ_STARTUP_ABORT;	}	/*	 * Managed interrupts have reserved resources, so this should not	 * happen.	 */	if (WARN_ON(irq_domain_activate_irq(d, false)))		return IRQ_STARTUP_ABORT;	return IRQ_STARTUP_MANAGED;} void irq_startup_managed(struct irq_desc *desc){	struct irq_data *d = irq_desc_get_irq_data(desc); 	/*	 * Clear managed-shutdown flag, so we don't repeat managed-startup for	 * multiple hotplugs, and cause imbalanced disable depth.	 */	irqd_clr_managed_shutdown(d); 	/*	 * Only start it up when the disable depth is 1, so that a disable,	 * hotunplug, hotplug sequence does not end up enabling it during	 * hotplug unconditionally.	 */	desc->depth--;	if (!desc->depth)		irq_startup(desc, IRQ_RESEND, IRQ_START_COND);} #elsestatic __always_inline int__irq_startup_managed(struct irq_desc *desc, const struct cpumask *aff,		      bool force){	return IRQ_STARTUP_NORMAL;}#endif static void irq_enable(struct irq_desc *desc){	if (!irqd_irq_disabled(&desc->irq_data)) {		unmask_irq(desc);	} else {		irq_state_clr_disabled(desc);		if (desc->irq_data.chip->irq_enable) {			desc->irq_data.chip->irq_enable(&desc->irq_data);			irq_state_clr_masked(desc);		} else {			unmask_irq(desc);		}	}} static int __irq_startup(struct irq_desc *desc){	struct irq_data *d = irq_desc_get_irq_data(desc);	int ret = 0; 	/* Warn if this interrupt is not activated but try nevertheless */	WARN_ON_ONCE(!irqd_is_activated(d)); 	if (d->chip->irq_startup) {		ret = d->chip->irq_startup(d);		irq_state_clr_disabled(desc);		irq_state_clr_masked(desc);	} else {		irq_enable(desc);	}	irq_state_set_started(desc);	return ret;} int irq_startup(struct irq_desc *desc, bool resend, bool force){	struct irq_data *d = irq_desc_get_irq_data(desc);	const struct cpumask *aff = irq_data_get_affinity_mask(d);	int ret = 0; 	desc->depth = 0; 	if (irqd_is_started(d)) {		irq_enable(desc);	} else {		switch (__irq_startup_managed(desc, aff, force)) {		case IRQ_STARTUP_NORMAL:			if (d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP)				irq_setup_affinity(desc);			ret = __irq_startup(desc);			if (!(d->chip->flags & IRQCHIP_AFFINITY_PRE_STARTUP))				irq_setup_affinity(desc);			break;		case IRQ_STARTUP_MANAGED:			irq_do_set_affinity(d, aff, false);			ret = __irq_startup(desc);			break;		case IRQ_STARTUP_ABORT:			desc->depth = 1;			irqd_set_managed_shutdown(d);			return 0;		}	}	if (resend)		check_irq_resend(desc, false); 	return ret;} int irq_activate(struct irq_desc *desc){	struct irq_data *d = irq_desc_get_irq_data(desc); 	if (!irqd_affinity_is_managed(d))		return irq_domain_activate_irq(d, false);	return 0;} int irq_activate_and_startup(struct irq_desc *desc, bool resend){	if (WARN_ON(irq_activate(desc)))		return 0;	return irq_startup(desc, resend, IRQ_START_FORCE);} static void __irq_disable(struct irq_desc *desc, bool mask); void irq_shutdown(struct irq_desc *desc){	if (irqd_is_started(&desc->irq_data)) {		clear_irq_resend(desc);		/*		 * Increment disable depth, so that a managed shutdown on		 * CPU hotunplug preserves the actual disabled state when the		 * CPU comes back online. See irq_startup_managed().		 */		desc->depth++; 		if (desc->irq_data.chip->irq_shutdown) {			desc->irq_data.chip->irq_shutdown(&desc->irq_data);			irq_state_set_disabled(desc);			irq_state_set_masked(desc);		} else {			__irq_disable(desc, true);		}		irq_state_clr_started(desc);	}}  void irq_shutdown_and_deactivate(struct irq_desc *desc){	irq_shutdown(desc);	/*	 * This must be called even if the interrupt was never started up,	 * because the activation can happen before the interrupt is	 * available for request/startup. It has it's own state tracking so	 * it's safe to call it unconditionally.	 */	irq_domain_deactivate_irq(&desc->irq_data);} static void __irq_disable(struct irq_desc *desc, bool mask){	if (irqd_irq_disabled(&desc->irq_data)) {		if (mask)			mask_irq(desc);	} else {		irq_state_set_disabled(desc);		if (desc->irq_data.chip->irq_disable) {			desc->irq_data.chip->irq_disable(&desc->irq_data);			irq_state_set_masked(desc);		} else if (mask) {			mask_irq(desc);		}	}} /** * irq_disable - Mark interrupt disabled * @desc:	irq descriptor which should be disabled * * If the chip does not implement the irq_disable callback, we * use a lazy disable approach. That means we mark the interrupt * disabled, but leave the hardware unmasked. That's an * optimization because we avoid the hardware access for the * common case where no interrupt happens after we marked it * disabled. If an interrupt happens, then the interrupt flow * handler masks the line at the hardware level and marks it * pending. * * If the interrupt chip does not implement the irq_disable callback, * a driver can disable the lazy approach for a particular irq line by * calling 'irq_set_status_flags(irq, IRQ_DISABLE_UNLAZY)'. This can * be used for devices which cannot disable the interrupt at the * device level under certain circumstances and have to use * disable_irq[_nosync] instead. */void irq_disable(struct irq_desc *desc){	__irq_disable(desc, irq_settings_disable_unlazy(desc));} void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu){	if (desc->irq_data.chip->irq_enable)		desc->irq_data.chip->irq_enable(&desc->irq_data);	else		desc->irq_data.chip->irq_unmask(&desc->irq_data);	cpumask_set_cpu(cpu, desc->percpu_enabled);} void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu){	if (desc->irq_data.chip->irq_disable)		desc->irq_data.chip->irq_disable(&desc->irq_data);	else		desc->irq_data.chip->irq_mask(&desc->irq_data);	cpumask_clear_cpu(cpu, desc->percpu_enabled);} static inline void mask_ack_irq(struct irq_desc *desc){	if (desc->irq_data.chip->irq_mask_ack) {		desc->irq_data.chip->irq_mask_ack(&desc->irq_data);		irq_state_set_masked(desc);	} else {		mask_irq(desc);		if (desc->irq_data.chip->irq_ack)			desc->irq_data.chip->irq_ack(&desc->irq_data);	}} void mask_irq(struct irq_desc *desc){	if (irqd_irq_masked(&desc->irq_data))		return; 	if (desc->irq_data.chip->irq_mask) {		desc->irq_data.chip->irq_mask(&desc->irq_data);		irq_state_set_masked(desc);	}} void unmask_irq(struct irq_desc *desc){	if (!irqd_irq_masked(&desc->irq_data))		return; 	if (desc->irq_data.chip->irq_unmask) {		desc->irq_data.chip->irq_unmask(&desc->irq_data);		irq_state_clr_masked(desc);	}} void unmask_threaded_irq(struct irq_desc *desc){	struct irq_chip *chip = desc->irq_data.chip; 	if (chip->flags & IRQCHIP_EOI_THREADED)		chip->irq_eoi(&desc->irq_data); 	unmask_irq(desc);} /* Busy wait until INPROGRESS is cleared */static bool irq_wait_on_inprogress(struct irq_desc *desc){	if (IS_ENABLED(CONFIG_SMP)) {		do {			raw_spin_unlock(&desc->lock);			while (irqd_irq_inprogress(&desc->irq_data))				cpu_relax();			raw_spin_lock(&desc->lock);		} while (irqd_irq_inprogress(&desc->irq_data)); 		/* Might have been disabled in meantime */		return !irqd_irq_disabled(&desc->irq_data) && desc->action;	}	return false;} static bool irq_can_handle_pm(struct irq_desc *desc){	struct irq_data *irqd = &desc->irq_data;	const struct cpumask *aff; 	/*	 * If the interrupt is not in progress and is not an armed	 * wakeup interrupt, proceed.	 */	if (!irqd_has_set(irqd, IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED))		return true; 	/*	 * If the interrupt is an armed wakeup source, mark it pending	 * and suspended, disable it and notify the pm core about the	 * event.	 */	if (unlikely(irqd_has_set(irqd, IRQD_WAKEUP_ARMED))) {		irq_pm_handle_wakeup(desc);		return false;

Lines 1–500 of 1583.