mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1596 lines
40 KiB
1596 lines
40 KiB
// 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 <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_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) |
|
{ |
|
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_SMP |
|
static 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. |
|
*/ |
|
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; |
|
} |
|
#else |
|
static __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; |
|
|
|
/* 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); |
|
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: |
|
irq_do_set_affinity(d, aff, false); |
|
ret = __irq_startup(desc); |
|
break; |
|
case IRQ_STARTUP_ABORT: |
|
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)) { |
|
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); |
|
} |
|
} |
|
|
|
|
|
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); |
|
} |
|
|
|
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 (!irq_settings_no_debug(desc)) |
|
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); |
|
} |
|
|
|
/** |
|
* handle_simple_irq - Simple and software-decoded IRQs. |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Simple interrupts are either sent from a demultiplexing interrupt |
|
* handler or come from hardware, where no interrupt hardware control |
|
* is necessary. |
|
* |
|
* Note: The caller is expected to handle the ack, clear, mask and |
|
* unmask issues if necessary. |
|
*/ |
|
void handle_simple_irq(struct irq_desc *desc) |
|
{ |
|
raw_spin_lock(&desc->lock); |
|
|
|
if (!irq_may_run(desc)) |
|
goto out_unlock; |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
|
desc->istate |= IRQS_PENDING; |
|
goto out_unlock; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
handle_irq_event(desc); |
|
|
|
out_unlock: |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_simple_irq); |
|
|
|
/** |
|
* handle_untracked_irq - Simple and software-decoded IRQs. |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Untracked interrupts are sent from a demultiplexing interrupt |
|
* handler when the demultiplexer does not know which device it its |
|
* multiplexed irq domain generated the interrupt. IRQ's handled |
|
* through here are not subjected to stats tracking, randomness, or |
|
* spurious interrupt detection. |
|
* |
|
* Note: Like handle_simple_irq, the caller is expected to handle |
|
* the ack, clear, mask and unmask issues if necessary. |
|
*/ |
|
void handle_untracked_irq(struct irq_desc *desc) |
|
{ |
|
unsigned int flags = 0; |
|
|
|
raw_spin_lock(&desc->lock); |
|
|
|
if (!irq_may_run(desc)) |
|
goto out_unlock; |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
|
desc->istate |= IRQS_PENDING; |
|
goto out_unlock; |
|
} |
|
|
|
desc->istate &= ~IRQS_PENDING; |
|
irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS); |
|
raw_spin_unlock(&desc->lock); |
|
|
|
__handle_irq_event_percpu(desc, &flags); |
|
|
|
raw_spin_lock(&desc->lock); |
|
irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS); |
|
|
|
out_unlock: |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_untracked_irq); |
|
|
|
/* |
|
* Called unconditionally from handle_level_irq() and only for oneshot |
|
* interrupts from handle_fasteoi_irq() |
|
*/ |
|
static void cond_unmask_irq(struct irq_desc *desc) |
|
{ |
|
/* |
|
* We need to unmask in the following cases: |
|
* - Standard level irq (IRQF_ONESHOT is not set) |
|
* - Oneshot irq which did not wake the thread (caused by a |
|
* spurious interrupt or a primary handler handling it |
|
* completely). |
|
*/ |
|
if (!irqd_irq_disabled(&desc->irq_data) && |
|
irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) |
|
unmask_irq(desc); |
|
} |
|
|
|
/** |
|
* handle_level_irq - Level type irq handler |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Level type interrupts are active as long as the hardware line has |
|
* the active level. This may require to mask the interrupt and unmask |
|
* it after the associated handler has acknowledged the device, so the |
|
* interrupt line is back to inactive. |
|
*/ |
|
void handle_level_irq(struct irq_desc *desc) |
|
{ |
|
raw_spin_lock(&desc->lock); |
|
mask_ack_irq(desc); |
|
|
|
if (!irq_may_run(desc)) |
|
goto out_unlock; |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
/* |
|
* If its disabled or no action available |
|
* keep it masked and get out of here |
|
*/ |
|
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
|
desc->istate |= IRQS_PENDING; |
|
goto out_unlock; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
handle_irq_event(desc); |
|
|
|
cond_unmask_irq(desc); |
|
|
|
out_unlock: |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_level_irq); |
|
|
|
static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip) |
|
{ |
|
if (!(desc->istate & IRQS_ONESHOT)) { |
|
chip->irq_eoi(&desc->irq_data); |
|
return; |
|
} |
|
/* |
|
* We need to unmask in the following cases: |
|
* - Oneshot irq which did not wake the thread (caused by a |
|
* spurious interrupt or a primary handler handling it |
|
* completely). |
|
*/ |
|
if (!irqd_irq_disabled(&desc->irq_data) && |
|
irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) { |
|
chip->irq_eoi(&desc->irq_data); |
|
unmask_irq(desc); |
|
} else if (!(chip->flags & IRQCHIP_EOI_THREADED)) { |
|
chip->irq_eoi(&desc->irq_data); |
|
} |
|
} |
|
|
|
/** |
|
* handle_fasteoi_irq - irq handler for transparent controllers |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Only a single callback will be issued to the chip: an ->eoi() |
|
* call when the interrupt has been serviced. This enables support |
|
* for modern forms of interrupt handlers, which handle the flow |
|
* details in hardware, transparently. |
|
*/ |
|
void handle_fasteoi_irq(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = desc->irq_data.chip; |
|
|
|
raw_spin_lock(&desc->lock); |
|
|
|
if (!irq_may_run(desc)) |
|
goto out; |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
/* |
|
* If its disabled or no action available |
|
* then mask it and get out of here: |
|
*/ |
|
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
|
desc->istate |= IRQS_PENDING; |
|
mask_irq(desc); |
|
goto out; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
if (desc->istate & IRQS_ONESHOT) |
|
mask_irq(desc); |
|
|
|
handle_irq_event(desc); |
|
|
|
cond_unmask_eoi_irq(desc, chip); |
|
|
|
raw_spin_unlock(&desc->lock); |
|
return; |
|
out: |
|
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED)) |
|
chip->irq_eoi(&desc->irq_data); |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_fasteoi_irq); |
|
|
|
/** |
|
* handle_fasteoi_nmi - irq handler for NMI interrupt lines |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* A simple NMI-safe handler, considering the restrictions |
|
* from request_nmi. |
|
* |
|
* Only a single callback will be issued to the chip: an ->eoi() |
|
* call when the interrupt has been serviced. This enables support |
|
* for modern forms of interrupt handlers, which handle the flow |
|
* details in hardware, transparently. |
|
*/ |
|
void handle_fasteoi_nmi(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = irq_desc_get_chip(desc); |
|
struct irqaction *action = desc->action; |
|
unsigned int irq = irq_desc_get_irq(desc); |
|
irqreturn_t res; |
|
|
|
__kstat_incr_irqs_this_cpu(desc); |
|
|
|
trace_irq_handler_entry(irq, action); |
|
/* |
|
* NMIs cannot be shared, there is only one action. |
|
*/ |
|
res = action->handler(irq, action->dev_id); |
|
trace_irq_handler_exit(irq, action, res); |
|
|
|
if (chip->irq_eoi) |
|
chip->irq_eoi(&desc->irq_data); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_fasteoi_nmi); |
|
|
|
/** |
|
* handle_edge_irq - edge type IRQ handler |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Interrupt occurs on the falling and/or rising edge of a hardware |
|
* signal. The occurrence is latched into the irq controller hardware |
|
* and must be acked in order to be reenabled. After the ack another |
|
* interrupt can happen on the same source even before the first one |
|
* is handled by the associated event handler. If this happens it |
|
* might be necessary to disable (mask) the interrupt depending on the |
|
* controller hardware. This requires to reenable the interrupt inside |
|
* of the loop which handles the interrupts which have arrived while |
|
* the handler was running. If all pending interrupts are handled, the |
|
* loop is left. |
|
*/ |
|
void handle_edge_irq(struct irq_desc *desc) |
|
{ |
|
raw_spin_lock(&desc->lock); |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
if (!irq_may_run(desc)) { |
|
desc->istate |= IRQS_PENDING; |
|
mask_ack_irq(desc); |
|
goto out_unlock; |
|
} |
|
|
|
/* |
|
* If its disabled or no action available then mask it and get |
|
* out of here. |
|
*/ |
|
if (irqd_irq_disabled(&desc->irq_data) || !desc->action) { |
|
desc->istate |= IRQS_PENDING; |
|
mask_ack_irq(desc); |
|
goto out_unlock; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
|
|
/* Start handling the irq */ |
|
desc->irq_data.chip->irq_ack(&desc->irq_data); |
|
|
|
do { |
|
if (unlikely(!desc->action)) { |
|
mask_irq(desc); |
|
goto out_unlock; |
|
} |
|
|
|
/* |
|
* When another irq arrived while we were handling |
|
* one, we could have masked the irq. |
|
* Reenable it, if it was not disabled in meantime. |
|
*/ |
|
if (unlikely(desc->istate & IRQS_PENDING)) { |
|
if (!irqd_irq_disabled(&desc->irq_data) && |
|
irqd_irq_masked(&desc->irq_data)) |
|
unmask_irq(desc); |
|
} |
|
|
|
handle_irq_event(desc); |
|
|
|
} while ((desc->istate & IRQS_PENDING) && |
|
!irqd_irq_disabled(&desc->irq_data)); |
|
|
|
out_unlock: |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL(handle_edge_irq); |
|
|
|
#ifdef CONFIG_IRQ_EDGE_EOI_HANDLER |
|
/** |
|
* handle_edge_eoi_irq - edge eoi type IRQ handler |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Similar as the above handle_edge_irq, but using eoi and w/o the |
|
* mask/unmask logic. |
|
*/ |
|
void handle_edge_eoi_irq(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = irq_desc_get_chip(desc); |
|
|
|
raw_spin_lock(&desc->lock); |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
if (!irq_may_run(desc)) { |
|
desc->istate |= IRQS_PENDING; |
|
goto out_eoi; |
|
} |
|
|
|
/* |
|
* If its disabled or no action available then mask it and get |
|
* out of here. |
|
*/ |
|
if (irqd_irq_disabled(&desc->irq_data) || !desc->action) { |
|
desc->istate |= IRQS_PENDING; |
|
goto out_eoi; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
|
|
do { |
|
if (unlikely(!desc->action)) |
|
goto out_eoi; |
|
|
|
handle_irq_event(desc); |
|
|
|
} while ((desc->istate & IRQS_PENDING) && |
|
!irqd_irq_disabled(&desc->irq_data)); |
|
|
|
out_eoi: |
|
chip->irq_eoi(&desc->irq_data); |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
#endif |
|
|
|
/** |
|
* handle_percpu_irq - Per CPU local irq handler |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Per CPU interrupts on SMP machines without locking requirements |
|
*/ |
|
void handle_percpu_irq(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = irq_desc_get_chip(desc); |
|
|
|
/* |
|
* PER CPU interrupts are not serialized. Do not touch |
|
* desc->tot_count. |
|
*/ |
|
__kstat_incr_irqs_this_cpu(desc); |
|
|
|
if (chip->irq_ack) |
|
chip->irq_ack(&desc->irq_data); |
|
|
|
handle_irq_event_percpu(desc); |
|
|
|
if (chip->irq_eoi) |
|
chip->irq_eoi(&desc->irq_data); |
|
} |
|
|
|
/** |
|
* handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Per CPU interrupts on SMP machines without locking requirements. Same as |
|
* handle_percpu_irq() above but with the following extras: |
|
* |
|
* action->percpu_dev_id is a pointer to percpu variables which |
|
* contain the real device id for the cpu on which this handler is |
|
* called |
|
*/ |
|
void handle_percpu_devid_irq(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = irq_desc_get_chip(desc); |
|
struct irqaction *action = desc->action; |
|
unsigned int irq = irq_desc_get_irq(desc); |
|
irqreturn_t res; |
|
|
|
/* |
|
* PER CPU interrupts are not serialized. Do not touch |
|
* desc->tot_count. |
|
*/ |
|
__kstat_incr_irqs_this_cpu(desc); |
|
|
|
if (chip->irq_ack) |
|
chip->irq_ack(&desc->irq_data); |
|
|
|
if (likely(action)) { |
|
trace_irq_handler_entry(irq, action); |
|
res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id)); |
|
trace_irq_handler_exit(irq, action, res); |
|
} else { |
|
unsigned int cpu = smp_processor_id(); |
|
bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled); |
|
|
|
if (enabled) |
|
irq_percpu_disable(desc, cpu); |
|
|
|
pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n", |
|
enabled ? " and unmasked" : "", irq, cpu); |
|
} |
|
|
|
if (chip->irq_eoi) |
|
chip->irq_eoi(&desc->irq_data); |
|
} |
|
|
|
/** |
|
* handle_percpu_devid_fasteoi_nmi - Per CPU local NMI handler with per cpu |
|
* dev ids |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Similar to handle_fasteoi_nmi, but handling the dev_id cookie |
|
* as a percpu pointer. |
|
*/ |
|
void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = irq_desc_get_chip(desc); |
|
struct irqaction *action = desc->action; |
|
unsigned int irq = irq_desc_get_irq(desc); |
|
irqreturn_t res; |
|
|
|
__kstat_incr_irqs_this_cpu(desc); |
|
|
|
trace_irq_handler_entry(irq, action); |
|
res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id)); |
|
trace_irq_handler_exit(irq, action, res); |
|
|
|
if (chip->irq_eoi) |
|
chip->irq_eoi(&desc->irq_data); |
|
} |
|
|
|
static void |
|
__irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle, |
|
int is_chained, const char *name) |
|
{ |
|
if (!handle) { |
|
handle = handle_bad_irq; |
|
} else { |
|
struct irq_data *irq_data = &desc->irq_data; |
|
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY |
|
/* |
|
* With hierarchical domains we might run into a |
|
* situation where the outermost chip is not yet set |
|
* up, but the inner chips are there. Instead of |
|
* bailing we install the handler, but obviously we |
|
* cannot enable/startup the interrupt at this point. |
|
*/ |
|
while (irq_data) { |
|
if (irq_data->chip != &no_irq_chip) |
|
break; |
|
/* |
|
* Bail out if the outer chip is not set up |
|
* and the interrupt supposed to be started |
|
* right away. |
|
*/ |
|
if (WARN_ON(is_chained)) |
|
return; |
|
/* Try the parent */ |
|
irq_data = irq_data->parent_data; |
|
} |
|
#endif |
|
if (WARN_ON(!irq_data || irq_data->chip == &no_irq_chip)) |
|
return; |
|
} |
|
|
|
/* Uninstall? */ |
|
if (handle == handle_bad_irq) { |
|
if (desc->irq_data.chip != &no_irq_chip) |
|
mask_ack_irq(desc); |
|
irq_state_set_disabled(desc); |
|
if (is_chained) |
|
desc->action = NULL; |
|
desc->depth = 1; |
|
} |
|
desc->handle_irq = handle; |
|
desc->name = name; |
|
|
|
if (handle != handle_bad_irq && is_chained) { |
|
unsigned int type = irqd_get_trigger_type(&desc->irq_data); |
|
|
|
/* |
|
* We're about to start this interrupt immediately, |
|
* hence the need to set the trigger configuration. |
|
* But the .set_type callback may have overridden the |
|
* flow handler, ignoring that we're dealing with a |
|
* chained interrupt. Reset it immediately because we |
|
* do know better. |
|
*/ |
|
if (type != IRQ_TYPE_NONE) { |
|
__irq_set_trigger(desc, type); |
|
desc->handle_irq = handle; |
|
} |
|
|
|
irq_settings_set_noprobe(desc); |
|
irq_settings_set_norequest(desc); |
|
irq_settings_set_nothread(desc); |
|
desc->action = &chained_action; |
|
irq_activate_and_startup(desc, IRQ_RESEND); |
|
} |
|
} |
|
|
|
void |
|
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, |
|
const char *name) |
|
{ |
|
unsigned long flags; |
|
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0); |
|
|
|
if (!desc) |
|
return; |
|
|
|
__irq_do_set_handler(desc, handle, is_chained, name); |
|
irq_put_desc_busunlock(desc, flags); |
|
} |
|
EXPORT_SYMBOL_GPL(__irq_set_handler); |
|
|
|
void |
|
irq_set_chained_handler_and_data(unsigned int irq, irq_flow_handler_t handle, |
|
void *data) |
|
{ |
|
unsigned long flags; |
|
struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0); |
|
|
|
if (!desc) |
|
return; |
|
|
|
desc->irq_common_data.handler_data = data; |
|
__irq_do_set_handler(desc, handle, 1, NULL); |
|
|
|
irq_put_desc_busunlock(desc, flags); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_set_chained_handler_and_data); |
|
|
|
void |
|
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, |
|
irq_flow_handler_t handle, const char *name) |
|
{ |
|
irq_set_chip(irq, chip); |
|
__irq_set_handler(irq, handle, 0, name); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_set_chip_and_handler_name); |
|
|
|
void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) |
|
{ |
|
unsigned long flags, trigger, tmp; |
|
struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); |
|
|
|
if (!desc) |
|
return; |
|
|
|
/* |
|
* Warn when a driver sets the no autoenable flag on an already |
|
* active interrupt. |
|
*/ |
|
WARN_ON_ONCE(!desc->depth && (set & _IRQ_NOAUTOEN)); |
|
|
|
irq_settings_clr_and_set(desc, clr, set); |
|
|
|
trigger = irqd_get_trigger_type(&desc->irq_data); |
|
|
|
irqd_clear(&desc->irq_data, IRQD_NO_BALANCING | IRQD_PER_CPU | |
|
IRQD_TRIGGER_MASK | IRQD_LEVEL | IRQD_MOVE_PCNTXT); |
|
if (irq_settings_has_no_balance_set(desc)) |
|
irqd_set(&desc->irq_data, IRQD_NO_BALANCING); |
|
if (irq_settings_is_per_cpu(desc)) |
|
irqd_set(&desc->irq_data, IRQD_PER_CPU); |
|
if (irq_settings_can_move_pcntxt(desc)) |
|
irqd_set(&desc->irq_data, IRQD_MOVE_PCNTXT); |
|
if (irq_settings_is_level(desc)) |
|
irqd_set(&desc->irq_data, IRQD_LEVEL); |
|
|
|
tmp = irq_settings_get_trigger_mask(desc); |
|
if (tmp != IRQ_TYPE_NONE) |
|
trigger = tmp; |
|
|
|
irqd_set(&desc->irq_data, trigger); |
|
|
|
irq_put_desc_unlock(desc, flags); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_modify_status); |
|
|
|
/** |
|
* irq_cpu_online - Invoke all irq_cpu_online functions. |
|
* |
|
* Iterate through all irqs and invoke the chip.irq_cpu_online() |
|
* for each. |
|
*/ |
|
void irq_cpu_online(void) |
|
{ |
|
struct irq_desc *desc; |
|
struct irq_chip *chip; |
|
unsigned long flags; |
|
unsigned int irq; |
|
|
|
for_each_active_irq(irq) { |
|
desc = irq_to_desc(irq); |
|
if (!desc) |
|
continue; |
|
|
|
raw_spin_lock_irqsave(&desc->lock, flags); |
|
|
|
chip = irq_data_get_irq_chip(&desc->irq_data); |
|
if (chip && chip->irq_cpu_online && |
|
(!(chip->flags & IRQCHIP_ONOFFLINE_ENABLED) || |
|
!irqd_irq_disabled(&desc->irq_data))) |
|
chip->irq_cpu_online(&desc->irq_data); |
|
|
|
raw_spin_unlock_irqrestore(&desc->lock, flags); |
|
} |
|
} |
|
|
|
/** |
|
* irq_cpu_offline - Invoke all irq_cpu_offline functions. |
|
* |
|
* Iterate through all irqs and invoke the chip.irq_cpu_offline() |
|
* for each. |
|
*/ |
|
void irq_cpu_offline(void) |
|
{ |
|
struct irq_desc *desc; |
|
struct irq_chip *chip; |
|
unsigned long flags; |
|
unsigned int irq; |
|
|
|
for_each_active_irq(irq) { |
|
desc = irq_to_desc(irq); |
|
if (!desc) |
|
continue; |
|
|
|
raw_spin_lock_irqsave(&desc->lock, flags); |
|
|
|
chip = irq_data_get_irq_chip(&desc->irq_data); |
|
if (chip && chip->irq_cpu_offline && |
|
(!(chip->flags & IRQCHIP_ONOFFLINE_ENABLED) || |
|
!irqd_irq_disabled(&desc->irq_data))) |
|
chip->irq_cpu_offline(&desc->irq_data); |
|
|
|
raw_spin_unlock_irqrestore(&desc->lock, flags); |
|
} |
|
} |
|
|
|
#ifdef CONFIG_IRQ_DOMAIN_HIERARCHY |
|
|
|
#ifdef CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS |
|
/** |
|
* handle_fasteoi_ack_irq - irq handler for edge hierarchy |
|
* stacked on transparent controllers |
|
* |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Like handle_fasteoi_irq(), but for use with hierarchy where |
|
* the irq_chip also needs to have its ->irq_ack() function |
|
* called. |
|
*/ |
|
void handle_fasteoi_ack_irq(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = desc->irq_data.chip; |
|
|
|
raw_spin_lock(&desc->lock); |
|
|
|
if (!irq_may_run(desc)) |
|
goto out; |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
/* |
|
* If its disabled or no action available |
|
* then mask it and get out of here: |
|
*/ |
|
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
|
desc->istate |= IRQS_PENDING; |
|
mask_irq(desc); |
|
goto out; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
if (desc->istate & IRQS_ONESHOT) |
|
mask_irq(desc); |
|
|
|
/* Start handling the irq */ |
|
desc->irq_data.chip->irq_ack(&desc->irq_data); |
|
|
|
handle_irq_event(desc); |
|
|
|
cond_unmask_eoi_irq(desc, chip); |
|
|
|
raw_spin_unlock(&desc->lock); |
|
return; |
|
out: |
|
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED)) |
|
chip->irq_eoi(&desc->irq_data); |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_fasteoi_ack_irq); |
|
|
|
/** |
|
* handle_fasteoi_mask_irq - irq handler for level hierarchy |
|
* stacked on transparent controllers |
|
* |
|
* @desc: the interrupt description structure for this irq |
|
* |
|
* Like handle_fasteoi_irq(), but for use with hierarchy where |
|
* the irq_chip also needs to have its ->irq_mask_ack() function |
|
* called. |
|
*/ |
|
void handle_fasteoi_mask_irq(struct irq_desc *desc) |
|
{ |
|
struct irq_chip *chip = desc->irq_data.chip; |
|
|
|
raw_spin_lock(&desc->lock); |
|
mask_ack_irq(desc); |
|
|
|
if (!irq_may_run(desc)) |
|
goto out; |
|
|
|
desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); |
|
|
|
/* |
|
* If its disabled or no action available |
|
* then mask it and get out of here: |
|
*/ |
|
if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { |
|
desc->istate |= IRQS_PENDING; |
|
mask_irq(desc); |
|
goto out; |
|
} |
|
|
|
kstat_incr_irqs_this_cpu(desc); |
|
if (desc->istate & IRQS_ONESHOT) |
|
mask_irq(desc); |
|
|
|
handle_irq_event(desc); |
|
|
|
cond_unmask_eoi_irq(desc, chip); |
|
|
|
raw_spin_unlock(&desc->lock); |
|
return; |
|
out: |
|
if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED)) |
|
chip->irq_eoi(&desc->irq_data); |
|
raw_spin_unlock(&desc->lock); |
|
} |
|
EXPORT_SYMBOL_GPL(handle_fasteoi_mask_irq); |
|
|
|
#endif /* CONFIG_IRQ_FASTEOI_HIERARCHY_HANDLERS */ |
|
|
|
/** |
|
* irq_chip_set_parent_state - set the state of a parent interrupt. |
|
* |
|
* @data: Pointer to interrupt specific data |
|
* @which: State to be restored (one of IRQCHIP_STATE_*) |
|
* @val: Value corresponding to @which |
|
* |
|
* Conditional success, if the underlying irqchip does not implement it. |
|
*/ |
|
int irq_chip_set_parent_state(struct irq_data *data, |
|
enum irqchip_irq_state which, |
|
bool val) |
|
{ |
|
data = data->parent_data; |
|
|
|
if (!data || !data->chip->irq_set_irqchip_state) |
|
return 0; |
|
|
|
return data->chip->irq_set_irqchip_state(data, which, val); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_set_parent_state); |
|
|
|
/** |
|
* irq_chip_get_parent_state - get the state of a parent interrupt. |
|
* |
|
* @data: Pointer to interrupt specific data |
|
* @which: one of IRQCHIP_STATE_* the caller wants to know |
|
* @state: a pointer to a boolean where the state is to be stored |
|
* |
|
* Conditional success, if the underlying irqchip does not implement it. |
|
*/ |
|
int irq_chip_get_parent_state(struct irq_data *data, |
|
enum irqchip_irq_state which, |
|
bool *state) |
|
{ |
|
data = data->parent_data; |
|
|
|
if (!data || !data->chip->irq_get_irqchip_state) |
|
return 0; |
|
|
|
return data->chip->irq_get_irqchip_state(data, which, state); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_get_parent_state); |
|
|
|
/** |
|
* irq_chip_enable_parent - Enable the parent interrupt (defaults to unmask if |
|
* NULL) |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_enable_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
if (data->chip->irq_enable) |
|
data->chip->irq_enable(data); |
|
else |
|
data->chip->irq_unmask(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_enable_parent); |
|
|
|
/** |
|
* irq_chip_disable_parent - Disable the parent interrupt (defaults to mask if |
|
* NULL) |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_disable_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
if (data->chip->irq_disable) |
|
data->chip->irq_disable(data); |
|
else |
|
data->chip->irq_mask(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_disable_parent); |
|
|
|
/** |
|
* irq_chip_ack_parent - Acknowledge the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_ack_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
data->chip->irq_ack(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_ack_parent); |
|
|
|
/** |
|
* irq_chip_mask_parent - Mask the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_mask_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
data->chip->irq_mask(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_mask_parent); |
|
|
|
/** |
|
* irq_chip_mask_ack_parent - Mask and acknowledge the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_mask_ack_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
data->chip->irq_mask_ack(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_mask_ack_parent); |
|
|
|
/** |
|
* irq_chip_unmask_parent - Unmask the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_unmask_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
data->chip->irq_unmask(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_unmask_parent); |
|
|
|
/** |
|
* irq_chip_eoi_parent - Invoke EOI on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_eoi_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
data->chip->irq_eoi(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_eoi_parent); |
|
|
|
/** |
|
* irq_chip_set_affinity_parent - Set affinity on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
* @dest: The affinity mask to set |
|
* @force: Flag to enforce setting (disable online checks) |
|
* |
|
* Conditional, as the underlying parent chip might not implement it. |
|
*/ |
|
int irq_chip_set_affinity_parent(struct irq_data *data, |
|
const struct cpumask *dest, bool force) |
|
{ |
|
data = data->parent_data; |
|
if (data->chip->irq_set_affinity) |
|
return data->chip->irq_set_affinity(data, dest, force); |
|
|
|
return -ENOSYS; |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_set_affinity_parent); |
|
|
|
/** |
|
* irq_chip_set_type_parent - Set IRQ type on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
* @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h |
|
* |
|
* Conditional, as the underlying parent chip might not implement it. |
|
*/ |
|
int irq_chip_set_type_parent(struct irq_data *data, unsigned int type) |
|
{ |
|
data = data->parent_data; |
|
|
|
if (data->chip->irq_set_type) |
|
return data->chip->irq_set_type(data, type); |
|
|
|
return -ENOSYS; |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_set_type_parent); |
|
|
|
/** |
|
* irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware |
|
* @data: Pointer to interrupt specific data |
|
* |
|
* Iterate through the domain hierarchy of the interrupt and check |
|
* whether a hw retrigger function exists. If yes, invoke it. |
|
*/ |
|
int irq_chip_retrigger_hierarchy(struct irq_data *data) |
|
{ |
|
for (data = data->parent_data; data; data = data->parent_data) |
|
if (data->chip && data->chip->irq_retrigger) |
|
return data->chip->irq_retrigger(data); |
|
|
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_retrigger_hierarchy); |
|
|
|
/** |
|
* irq_chip_set_vcpu_affinity_parent - Set vcpu affinity on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
* @vcpu_info: The vcpu affinity information |
|
*/ |
|
int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info) |
|
{ |
|
data = data->parent_data; |
|
if (data->chip->irq_set_vcpu_affinity) |
|
return data->chip->irq_set_vcpu_affinity(data, vcpu_info); |
|
|
|
return -ENOSYS; |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_set_vcpu_affinity_parent); |
|
/** |
|
* irq_chip_set_wake_parent - Set/reset wake-up on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
* @on: Whether to set or reset the wake-up capability of this irq |
|
* |
|
* Conditional, as the underlying parent chip might not implement it. |
|
*/ |
|
int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on) |
|
{ |
|
data = data->parent_data; |
|
|
|
if (data->chip->flags & IRQCHIP_SKIP_SET_WAKE) |
|
return 0; |
|
|
|
if (data->chip->irq_set_wake) |
|
return data->chip->irq_set_wake(data, on); |
|
|
|
return -ENOSYS; |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_set_wake_parent); |
|
|
|
/** |
|
* irq_chip_request_resources_parent - Request resources on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
int irq_chip_request_resources_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
|
|
if (data->chip->irq_request_resources) |
|
return data->chip->irq_request_resources(data); |
|
|
|
return -ENOSYS; |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_request_resources_parent); |
|
|
|
/** |
|
* irq_chip_release_resources_parent - Release resources on the parent interrupt |
|
* @data: Pointer to interrupt specific data |
|
*/ |
|
void irq_chip_release_resources_parent(struct irq_data *data) |
|
{ |
|
data = data->parent_data; |
|
if (data->chip->irq_release_resources) |
|
data->chip->irq_release_resources(data); |
|
} |
|
EXPORT_SYMBOL_GPL(irq_chip_release_resources_parent); |
|
#endif |
|
|
|
/** |
|
* irq_chip_compose_msi_msg - Compose msi message for a irq chip |
|
* @data: Pointer to interrupt specific data |
|
* @msg: Pointer to the MSI message |
|
* |
|
* For hierarchical domains we find the first chip in the hierarchy |
|
* which implements the irq_compose_msi_msg callback. For non |
|
* hierarchical we use the top level chip. |
|
*/ |
|
int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) |
|
{ |
|
struct irq_data *pos; |
|
|
|
for (pos = NULL; !pos && data; data = irqd_get_parent_data(data)) { |
|
if (data->chip && data->chip->irq_compose_msi_msg) |
|
pos = data; |
|
} |
|
|
|
if (!pos) |
|
return -ENOSYS; |
|
|
|
pos->chip->irq_compose_msi_msg(pos, msg); |
|
return 0; |
|
} |
|
|
|
/** |
|
* irq_chip_pm_get - Enable power for an IRQ chip |
|
* @data: Pointer to interrupt specific data |
|
* |
|
* Enable the power to the IRQ chip referenced by the interrupt data |
|
* structure. |
|
*/ |
|
int irq_chip_pm_get(struct irq_data *data) |
|
{ |
|
int retval; |
|
|
|
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device) { |
|
retval = pm_runtime_get_sync(data->chip->parent_device); |
|
if (retval < 0) { |
|
pm_runtime_put_noidle(data->chip->parent_device); |
|
return retval; |
|
} |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* irq_chip_pm_put - Disable power for an IRQ chip |
|
* @data: Pointer to interrupt specific data |
|
* |
|
* Disable the power to the IRQ chip referenced by the interrupt data |
|
* structure, belongs. Note that power will only be disabled, once this |
|
* function has been called for all IRQs that have called irq_chip_pm_get(). |
|
*/ |
|
int irq_chip_pm_put(struct irq_data *data) |
|
{ |
|
int retval = 0; |
|
|
|
if (IS_ENABLED(CONFIG_PM) && data->chip->parent_device) |
|
retval = pm_runtime_put(data->chip->parent_device); |
|
|
|
return (retval < 0) ? retval : 0; |
|
}
|
|
|