Commit e26a4331 authored by Catalin Marinas's avatar Catalin Marinas

Merge branch 'irq/generic-nmi' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms

* 'irq/generic-nmi' of git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms:
  irqdesc: Add domain handler for NMIs
  genirq: Provide NMI handlers
  genirq: Provide NMI management for percpu_devid interrupts
  genirq: Provide basic NMI management for interrupt lines
parents 8aa67d18 6e4933a0
...@@ -156,6 +156,10 @@ __request_percpu_irq(unsigned int irq, irq_handler_t handler, ...@@ -156,6 +156,10 @@ __request_percpu_irq(unsigned int irq, irq_handler_t handler,
unsigned long flags, const char *devname, unsigned long flags, const char *devname,
void __percpu *percpu_dev_id); void __percpu *percpu_dev_id);
extern int __must_check
request_nmi(unsigned int irq, irq_handler_t handler, unsigned long flags,
const char *name, void *dev);
static inline int __must_check static inline int __must_check
request_percpu_irq(unsigned int irq, irq_handler_t handler, request_percpu_irq(unsigned int irq, irq_handler_t handler,
const char *devname, void __percpu *percpu_dev_id) const char *devname, void __percpu *percpu_dev_id)
...@@ -164,9 +168,16 @@ request_percpu_irq(unsigned int irq, irq_handler_t handler, ...@@ -164,9 +168,16 @@ request_percpu_irq(unsigned int irq, irq_handler_t handler,
devname, percpu_dev_id); devname, percpu_dev_id);
} }
extern int __must_check
request_percpu_nmi(unsigned int irq, irq_handler_t handler,
const char *devname, void __percpu *dev);
extern const void *free_irq(unsigned int, void *); extern const void *free_irq(unsigned int, void *);
extern void free_percpu_irq(unsigned int, void __percpu *); extern void free_percpu_irq(unsigned int, void __percpu *);
extern const void *free_nmi(unsigned int irq, void *dev_id);
extern void free_percpu_nmi(unsigned int irq, void __percpu *percpu_dev_id);
struct device; struct device;
extern int __must_check extern int __must_check
...@@ -217,6 +228,13 @@ extern void enable_percpu_irq(unsigned int irq, unsigned int type); ...@@ -217,6 +228,13 @@ extern void enable_percpu_irq(unsigned int irq, unsigned int type);
extern bool irq_percpu_is_enabled(unsigned int irq); extern bool irq_percpu_is_enabled(unsigned int irq);
extern void irq_wake_thread(unsigned int irq, void *dev_id); extern void irq_wake_thread(unsigned int irq, void *dev_id);
extern void disable_nmi_nosync(unsigned int irq);
extern void disable_percpu_nmi(unsigned int irq);
extern void enable_nmi(unsigned int irq);
extern void enable_percpu_nmi(unsigned int irq, unsigned int type);
extern int prepare_percpu_nmi(unsigned int irq);
extern void teardown_percpu_nmi(unsigned int irq);
/* The following three functions are for the core kernel use only. */ /* The following three functions are for the core kernel use only. */
extern void suspend_device_irqs(void); extern void suspend_device_irqs(void);
extern void resume_device_irqs(void); extern void resume_device_irqs(void);
......
...@@ -442,6 +442,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d) ...@@ -442,6 +442,8 @@ static inline irq_hw_number_t irqd_to_hwirq(struct irq_data *d)
* @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine * @irq_set_vcpu_affinity: optional to target a vCPU in a virtual machine
* @ipi_send_single: send a single IPI to destination cpus * @ipi_send_single: send a single IPI to destination cpus
* @ipi_send_mask: send an IPI to destination cpus in cpumask * @ipi_send_mask: send an IPI to destination cpus in cpumask
* @irq_nmi_setup: function called from core code before enabling an NMI
* @irq_nmi_teardown: function called from core code after disabling an NMI
* @flags: chip specific flags * @flags: chip specific flags
*/ */
struct irq_chip { struct irq_chip {
...@@ -490,6 +492,9 @@ struct irq_chip { ...@@ -490,6 +492,9 @@ struct irq_chip {
void (*ipi_send_single)(struct irq_data *data, unsigned int cpu); void (*ipi_send_single)(struct irq_data *data, unsigned int cpu);
void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest); void (*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);
int (*irq_nmi_setup)(struct irq_data *data);
void (*irq_nmi_teardown)(struct irq_data *data);
unsigned long flags; unsigned long flags;
}; };
...@@ -505,6 +510,7 @@ struct irq_chip { ...@@ -505,6 +510,7 @@ struct irq_chip {
* IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask * IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask
* IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode * IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode
* IRQCHIP_SUPPORTS_LEVEL_MSI Chip can provide two doorbells for Level MSIs * IRQCHIP_SUPPORTS_LEVEL_MSI Chip can provide two doorbells for Level MSIs
* IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips
*/ */
enum { enum {
IRQCHIP_SET_TYPE_MASKED = (1 << 0), IRQCHIP_SET_TYPE_MASKED = (1 << 0),
...@@ -515,6 +521,7 @@ enum { ...@@ -515,6 +521,7 @@ enum {
IRQCHIP_ONESHOT_SAFE = (1 << 5), IRQCHIP_ONESHOT_SAFE = (1 << 5),
IRQCHIP_EOI_THREADED = (1 << 6), IRQCHIP_EOI_THREADED = (1 << 6),
IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7), IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7),
IRQCHIP_SUPPORTS_NMI = (1 << 8),
}; };
#include <linux/irqdesc.h> #include <linux/irqdesc.h>
...@@ -594,6 +601,9 @@ extern void handle_percpu_devid_irq(struct irq_desc *desc); ...@@ -594,6 +601,9 @@ extern void handle_percpu_devid_irq(struct irq_desc *desc);
extern void handle_bad_irq(struct irq_desc *desc); extern void handle_bad_irq(struct irq_desc *desc);
extern void handle_nested_irq(unsigned int irq); extern void handle_nested_irq(unsigned int irq);
extern void handle_fasteoi_nmi(struct irq_desc *desc);
extern void handle_percpu_devid_fasteoi_nmi(struct irq_desc *desc);
extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg); extern int irq_chip_compose_msi_msg(struct irq_data *data, struct msi_msg *msg);
extern int irq_chip_pm_get(struct irq_data *data); extern int irq_chip_pm_get(struct irq_data *data);
extern int irq_chip_pm_put(struct irq_data *data); extern int irq_chip_pm_put(struct irq_data *data);
......
...@@ -171,6 +171,11 @@ static inline int handle_domain_irq(struct irq_domain *domain, ...@@ -171,6 +171,11 @@ static inline int handle_domain_irq(struct irq_domain *domain,
{ {
return __handle_domain_irq(domain, hwirq, true, regs); return __handle_domain_irq(domain, hwirq, true, regs);
} }
#ifdef CONFIG_IRQ_DOMAIN
int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
struct pt_regs *regs);
#endif
#endif #endif
/* Test to see if a driver has successfully requested an irq */ /* Test to see if a driver has successfully requested an irq */
......
...@@ -729,6 +729,37 @@ void handle_fasteoi_irq(struct irq_desc *desc) ...@@ -729,6 +729,37 @@ void handle_fasteoi_irq(struct irq_desc *desc)
} }
EXPORT_SYMBOL_GPL(handle_fasteoi_irq); 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;
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 * handle_edge_irq - edge type IRQ handler
* @desc: the interrupt description structure for this irq * @desc: the interrupt description structure for this irq
...@@ -908,6 +939,29 @@ void handle_percpu_devid_irq(struct irq_desc *desc) ...@@ -908,6 +939,29 @@ void handle_percpu_devid_irq(struct irq_desc *desc)
chip->irq_eoi(&desc->irq_data); 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;
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 static void
__irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle, __irq_do_set_handler(struct irq_desc *desc, irq_flow_handler_t handle,
int is_chained, const char *name) int is_chained, const char *name)
......
...@@ -56,6 +56,7 @@ static const struct irq_bit_descr irqchip_flags[] = { ...@@ -56,6 +56,7 @@ static const struct irq_bit_descr irqchip_flags[] = {
BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE), BIT_MASK_DESCR(IRQCHIP_ONESHOT_SAFE),
BIT_MASK_DESCR(IRQCHIP_EOI_THREADED), BIT_MASK_DESCR(IRQCHIP_EOI_THREADED),
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI), BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI),
BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI),
}; };
static void static void
...@@ -140,6 +141,7 @@ static const struct irq_bit_descr irqdesc_istates[] = { ...@@ -140,6 +141,7 @@ static const struct irq_bit_descr irqdesc_istates[] = {
BIT_MASK_DESCR(IRQS_WAITING), BIT_MASK_DESCR(IRQS_WAITING),
BIT_MASK_DESCR(IRQS_PENDING), BIT_MASK_DESCR(IRQS_PENDING),
BIT_MASK_DESCR(IRQS_SUSPENDED), BIT_MASK_DESCR(IRQS_SUSPENDED),
BIT_MASK_DESCR(IRQS_NMI),
}; };
...@@ -203,8 +205,8 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf, ...@@ -203,8 +205,8 @@ static ssize_t irq_debug_write(struct file *file, const char __user *user_buf,
chip_bus_lock(desc); chip_bus_lock(desc);
raw_spin_lock_irqsave(&desc->lock, flags); raw_spin_lock_irqsave(&desc->lock, flags);
if (irq_settings_is_level(desc)) { if (irq_settings_is_level(desc) || desc->istate & IRQS_NMI) {
/* Can't do level, sorry */ /* Can't do level nor NMIs, sorry */
err = -EINVAL; err = -EINVAL;
} else { } else {
desc->istate |= IRQS_PENDING; desc->istate |= IRQS_PENDING;
......
...@@ -49,6 +49,7 @@ enum { ...@@ -49,6 +49,7 @@ enum {
* IRQS_WAITING - irq is waiting * IRQS_WAITING - irq is waiting
* IRQS_PENDING - irq is pending and replayed later * IRQS_PENDING - irq is pending and replayed later
* IRQS_SUSPENDED - irq is suspended * IRQS_SUSPENDED - irq is suspended
* IRQS_NMI - irq line is used to deliver NMIs
*/ */
enum { enum {
IRQS_AUTODETECT = 0x00000001, IRQS_AUTODETECT = 0x00000001,
...@@ -60,6 +61,7 @@ enum { ...@@ -60,6 +61,7 @@ enum {
IRQS_PENDING = 0x00000200, IRQS_PENDING = 0x00000200,
IRQS_SUSPENDED = 0x00000800, IRQS_SUSPENDED = 0x00000800,
IRQS_TIMINGS = 0x00001000, IRQS_TIMINGS = 0x00001000,
IRQS_NMI = 0x00002000,
}; };
#include "debug.h" #include "debug.h"
......
...@@ -669,6 +669,41 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, ...@@ -669,6 +669,41 @@ int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
set_irq_regs(old_regs); set_irq_regs(old_regs);
return ret; return ret;
} }
#ifdef CONFIG_IRQ_DOMAIN
/**
* handle_domain_nmi - Invoke the handler for a HW irq belonging to a domain
* @domain: The domain where to perform the lookup
* @hwirq: The HW irq number to convert to a logical one
* @regs: Register file coming from the low-level handling code
*
* Returns: 0 on success, or -EINVAL if conversion has failed
*/
int handle_domain_nmi(struct irq_domain *domain, unsigned int hwirq,
struct pt_regs *regs)
{
struct pt_regs *old_regs = set_irq_regs(regs);
unsigned int irq;
int ret = 0;
nmi_enter();
irq = irq_find_mapping(domain, hwirq);
/*
* ack_bad_irq is not NMI-safe, just report
* an invalid interrupt.
*/
if (likely(irq))
generic_handle_irq(irq);
else
ret = -EINVAL;
nmi_exit();
set_irq_regs(old_regs);
return ret;
}
#endif
#endif #endif
/* Dynamic interrupt handling */ /* Dynamic interrupt handling */
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment