1320 lines · 32.8 KB · C
RawDownload
/* * linux/kernel/irq/chip.c * * 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 <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, struct irq_chip *chip){	unsigned long flags;	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); 	if (!desc)		return -EINVAL; 	if (!chip)		chip = &no_irq_chip; 	desc->irq_data.chip = chip;	irq_put_desc_unlock(desc, flags);	/*	 * For !CONFIG_SPARSE_IRQ make the irq show up in	 * allocated_irqs.	 */	irq_mark_irq(irq);	return 0;}EXPORT_SYMBOL(irq_set_chip); /** *	irq_set_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){	unsigned long flags;	struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);	int ret = 0; 	if (!desc)		return -EINVAL; 	ret = __irq_set_trigger(desc, type);	irq_put_desc_busunlock(desc, flags);	return ret;}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){	unsigned long flags;	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); 	if (!desc)		return -EINVAL;	desc->irq_common_data.handler_data = data;	irq_put_desc_unlock(desc, flags);	return 0;}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){	unsigned long flags;	struct irq_desc *desc = irq_get_desc_lock(irq_base + irq_offset, &flags, IRQ_GET_DESC_CHECK_GLOBAL); 	if (!desc)		return -EINVAL;	desc->irq_common_data.msi_desc = entry;	if (entry && !irq_offset)		entry->irq = irq_base;	irq_put_desc_unlock(desc, flags);	return 0;} /** *	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){	unsigned long flags;	struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); 	if (!desc)		return -EINVAL;	desc->irq_data.chip_data = data;	irq_put_desc_unlock(desc, flags);	return 0;}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, 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_any_and(aff, cpu_online_mask) > nr_cpu_ids) {		/*		 * 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. Emit a warning, break the affinity		 * and start it up as a normal interrupt.		 */		if (WARN_ON_ONCE(force))			return IRQ_STARTUP_NORMAL;		/*		 * 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.		 */		irqd_set_managed_shutdown(d);		return IRQ_STARTUP_ABORT;	}	return IRQ_STARTUP_MANAGED;}#elsestatic __always_inline int__irq_startup_managed(struct irq_desc *desc, struct cpumask *aff, bool force){	return IRQ_STARTUP_NORMAL;}#endif static int __irq_startup(struct irq_desc *desc){	struct irq_data *d = irq_desc_get_irq_data(desc);	int ret = 0; 	irq_domain_activate_irq(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);	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:			ret = __irq_startup(desc);			irq_setup_affinity(desc);			break;		case IRQ_STARTUP_MANAGED:			ret = __irq_startup(desc);			irq_set_affinity_locked(d, aff, false);			break;		case IRQ_STARTUP_ABORT:			return 0;		}	}	if (resend)		check_irq_resend(desc); 	return ret;} static void __irq_disable(struct irq_desc *desc, bool mask); void irq_shutdown(struct irq_desc *desc){	if (irqd_is_started(&desc->irq_data)) {		desc->depth = 1;		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);	}	/*	 * 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);} 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 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);} /* *	handle_nested_irq - Handle a nested irq from a irq thread *	@irq:	the interrupt number * *	Handle interrupts which are nested into a threaded interrupt *	handler. The handler function is called inside the calling *	threads context. */void handle_nested_irq(unsigned int irq){	struct irq_desc *desc = irq_to_desc(irq);	struct irqaction *action;	irqreturn_t action_ret; 	might_sleep(); 	raw_spin_lock_irq(&desc->lock); 	desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); 	action = desc->action;	if (unlikely(!action || irqd_irq_disabled(&desc->irq_data))) {		desc->istate |= IRQS_PENDING;		goto out_unlock;	} 	kstat_incr_irqs_this_cpu(desc);	irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);	raw_spin_unlock_irq(&desc->lock); 	action_ret = IRQ_NONE;	for_each_action_of_desc(desc, action)		action_ret |= action->thread_fn(action->irq, action->dev_id); 	if (!noirqdebug)		note_interrupt(desc, action_ret); 	raw_spin_lock_irq(&desc->lock);	irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); out_unlock:	raw_spin_unlock_irq(&desc->lock);}EXPORT_SYMBOL_GPL(handle_nested_irq); static bool irq_check_poll(struct irq_desc *desc){	if (!(desc->istate & IRQS_POLL_INPROGRESS))		return false;	return irq_wait_for_poll(desc);} static bool irq_may_run(struct irq_desc *desc){	unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED; 	/*	 * If the interrupt is not in progress and is not an armed	 * wakeup interrupt, proceed.	 */	if (!irqd_has_set(&desc->irq_data, mask))		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 (irq_pm_check_wakeup(desc))		return false; 	/*	 * Handle a potential concurrent poll on a different core.	 */	return irq_check_poll(desc);} /**

Lines 1–500 of 1320.