genirq: handling GIC per-cpu interrupts
Subsystems (6)
v3 cover genirq: handling GIC per-cpu interrupts
The current GIC per-cpu interrupts (aka PPIs) suffer from a number of problems:
- They use a completely separate scheme to handle the interrupts, mostly because the PPI concept doesn't really match the kernel view of an interrupt.
- PPIs can only be used by the timer code, unless we add more low-level assembly code.
- The local timer code can only be used by devices generating PPIs, and not SPIs.
- At least one platform (msm) has started implementing its own alternative scheme.
- Some low-level code gets duplicated, as usual...
The proposed solution is to handle the PPIs using the same path as SPIs. A new core API is added to deal with per-cpu interrupts in a less awkward way. The local timer code is updated to reflect these changes.
The core API changes are based on an initial idea by Thomas Gleixner.
Tested on ARM Versatile Express (Cortex A15), ARM RealView PB11MP, OMAP4 (Panda) and Tegra (Harmony). Patch series against next-20110923.
Quoted 1 line
From v2:
- Fixed !GENERIC_HARDIRQS build
- Fixed request_percpu_irq documentation
Quoted 1 line
From v1:
- General tidy-up after Thomas' review. I've kept the config option for the time being until we can sort out the anonymous union problem.
Marc Zyngier (3):
genirq: add support for per-cpu dev_id interrupts
ARM: gic: consolidate PPI handling
ARM: gic, local timers: use the request_percpu_irq() interface
arch/arm/common/Kconfig | 1 + arch/arm/common/gic.c | 38 +++- arch/arm/include/asm/entry-macro-multi.S | 7 - arch/arm/include/asm/hardirq.h | 3 - arch/arm/include/asm/hardware/entry-macro-gic.S | 19 +-- arch/arm/include/asm/hardware/gic.h | 1 - arch/arm/include/asm/localtimer.h | 19 +- arch/arm/include/asm/smp.h | 5 - arch/arm/include/asm/smp_twd.h | 2 +- arch/arm/kernel/irq.c | 3 - arch/arm/kernel/smp.c | 33 +---- arch/arm/kernel/smp_twd.c | 47 +++++- arch/arm/mach-exynos4/include/mach/entry-macro.S | 6 +- arch/arm/mach-exynos4/mct.c | 5 - arch/arm/mach-msm/board-msm8x60.c | 11 - arch/arm/mach-msm/include/mach/entry-macro-qgic.S | 73 +------- arch/arm/mach-msm/timer.c | 69 ++++--- arch/arm/mach-omap2/include/mach/entry-macro.S | 14 +-- arch/arm/mach-shmobile/entry-intc.S | 3 - arch/arm/mach-shmobile/include/mach/entry-macro.S | 3 - include/linux/interrupt.h | 40 +++- include/linux/irq.h | 16 ++- include/linux/irqdesc.h | 1 + kernel/irq/Kconfig | 4 + kernel/irq/chip.c | 54 ++++++ kernel/irq/internals.h | 2 + kernel/irq/irqdesc.c | 25 +++ kernel/irq/manage.c | 206 ++++++++++++++++++++- kernel/irq/settings.h | 7 + 29 files changed, 468 insertions(+), 249 deletions(-)
PATCH v3 1/3 genirq: add support for per-cpu dev_id interrupts
The ARM GIC interrupt controller offers per CPU interrupts (PPIs), which are usually used to connect local timers to each core. Each CPU has its own private interface to the GIC,
and only sees the PPIs that are directly connect to it.
While these timers are separate devices and have a separate interrupt line to a core, they all use the same IRQ number.
For these devices, request_irq() is not the right API as it assumes that an IRQ number is visible by a number of CPUs (through the affinity setting), but makes it very awkward to express that an IRQ number can be handled by all CPUs, and yet be a different interrupt line on each CPU, requiring a different dev_id cookie to be passed back to the handler.
The *_percpu_irq() functions is designed to overcome these limitations, by providing a per-cpu dev_id vector:
int request_percpu_irq(unsigned int irq, irq_handler_t handler,
const char *devname, void __percpu *percpu_dev_id);
void free_percpu_irq(unsigned int, void __percpu *); int setup_percpu_irq(unsigned int irq, struct irqaction *new); void remove_percpu_irq(unsigned int irq, struct irqaction *act); void enable_percpu_irq(unsigned int irq);
void disable_percpu_irq(unsigned int irq);
The API has a number of limitations:
- no interrupt sharing
- no threading
- common handler across all the CPUs
Once the interrupt is requested using setup_percpu_irq() or request_percpu_irq(), it must be enabled by each core that wishes its local interrupt to be delivered.
Based on an initial patch by Thomas Gleixner.
--- include/linux/interrupt.h | 40 +++++++--- include/linux/irq.h | 16 ++++- include/linux/irqdesc.h | 1 + kernel/irq/Kconfig | 4 + kernel/irq/chip.c | 54 ++++++++++++ kernel/irq/internals.h | 2 + kernel/irq/irqdesc.c | 25 ++++++ kernel/irq/manage.c | 206 ++++++++++++++++++++++++++++++++++++++++++++- kernel/irq/settings.h | 7 ++ 9 files changed, 339 insertions(+), 16 deletions(-)
include/linux/interrupt.h +29 -11
@@ -95,6 +95,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);* @flags: flags (see IRQF_* above) * @name: name of the device * @dev_id: cookie to identify the device +* @percpu_dev_id: cookie to identify the device * @next: pointer to the next irqaction for shared interrupts * @irq: interrupt number * @dir: pointer to the proc/irq/NN/name entry
@@ -104,17 +105,20 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);* @thread_mask: bitmask for keeping track of @thread activity */ struct irqaction { -irq_handler_t handler; -unsigned long flags; -void *dev_id; -struct irqaction *next; -int irq; -irq_handler_t thread_fn; -struct task_struct *thread; -unsigned long thread_flags; -unsigned long thread_mask; -const char *name; -struct proc_dir_entry *dir; +irq_handler_t handler; +unsigned long flags; +void *dev_id; +#ifdef CONFIG_IRQ_PERCPU_DEVID +void __percpu *percpu_dev_id; +#endif +struct irqaction *next; +int irq; +irq_handler_t thread_fn; +struct task_struct *thread; +unsigned long thread_flags; +unsigned long thread_mask; +const char *name; +struct proc_dir_entry *dir; }____cacheline_internodealigned_in_smp ;extern irqreturn_t no_action (int cpl,void *dev_id);
@@ -136,6 +140,10 @@ extern int __must_check request_any_context_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev_id); +extern int __must_check +request_percpu_irq (unsigned int irq,irq_handler_t handler, +const char *devname,void __percpu *percpu_dev_id); +extern void exit_irq_thread (void );#else
@@ -164,10 +172,18 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler, return request_irq(irq, handler, flags, name, dev_id); } +static inline int __must_check +request_percpu_irq (unsigned int irq,irq_handler_t handler, +const char *devname,void __percpu *percpu_dev_id) +{ +return request_irq (irq, handler,0 , devname, percpu_dev_id); +} +static inline void exit_irq_thread (void ) { }#endif extern void free_irq (unsigned int ,void *); +extern void free_percpu_irq (unsigned int ,void __percpu *);struct device ;
@@ -207,7 +223,9 @@ extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);extern void disable_irq_nosync (unsigned int irq);extern void disable_irq (unsigned int irq); +extern void disable_percpu_irq (unsigned int irq);extern void enable_irq (unsigned int irq); +extern void enable_percpu_irq (unsigned int irq);/* The following three functions are for the core kernel use only. */ #ifdef CONFIG_GENERIC_HARDIRQS
include/linux/irq.h +15 -1
@@ -66,6 +66,7 @@ typedef void (*irq_preflow_handler_t)(struct irq_data *data);* IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set) * IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context * IRQ_NESTED_TRHEAD - Interrupt nests into another thread +* IRQ_PER_CPU_DEVID - Dev_id is a per-cpu variable */ enum {IRQ_TYPE_NONE =0x00000000 ,
@@ -88,12 +89,13 @@ enum {IRQ_MOVE_PCNTXT = (1 <<14 ),IRQ_NESTED_THREAD = (1 <<15 ),IRQ_NOTHREAD = (1 <<16 ), + IRQ_PER_CPU_DEVID = (1 << 17), };#define IRQF_MODIFY_MASK \ (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ - IRQ_PER_CPU | IRQ_NESTED_THREAD) + IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
@@ -367,6 +369,8 @@ enum {struct irqaction ;extern int setup_irq (unsigned int irq,struct irqaction *new);extern void remove_irq (unsigned int irq,struct irqaction *act); +extern int setup_percpu_irq (unsigned int irq,struct irqaction *new); +extern void remove_percpu_irq (unsigned int irq,struct irqaction *act);extern void irq_cpu_online (void );extern void irq_cpu_offline (void );
@@ -394,6 +398,7 @@ extern void handle_edge_irq(unsigned int irq, struct irq_desc *desc);extern void handle_edge_eoi_irq (unsigned int irq,struct irq_desc *desc);extern void handle_simple_irq (unsigned int irq,struct irq_desc *desc);extern void handle_percpu_irq (unsigned int irq,struct irq_desc *desc); +extern void handle_percpu_devid_irq (unsigned int irq,struct irq_desc *desc);extern void handle_bad_irq (unsigned int irq,struct irq_desc *desc);extern void handle_nested_irq (unsigned int irq);
@@ -422,6 +427,8 @@ static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *cirq_set_chip_and_handler_name (irq, chip, handle,NULL ); } +extern int irq_set_percpu_devid (unsigned int irq); +extern void __irq_set_handler (unsigned int irq,irq_flow_handler_t handle,int is_chained,const char *name);
@@ -483,6 +490,13 @@ static inline void irq_set_nested_thread(unsigned int irq, bool nest)irq_clear_status_flags (irq,IRQ_NESTED_THREAD ); } +static inline void irq_set_percpu_devid_flags (unsigned int irq) +{ +irq_set_status_flags (irq, +IRQ_NOAUTOEN |IRQ_PER_CPU |IRQ_NOTHREAD | +IRQ_NOPROBE |IRQ_PER_CPU_DEVID ); +} +/* Handle dynamic irq creation and destruction */ extern unsigned int create_irq_nr (unsigned int irq_want,int node);extern int create_irq (void );
include/linux/irqdesc.h +1 -0
@@ -53,6 +53,7 @@ struct irq_desc {unsigned long last_unhandled;/* Aging timer for unhandled count */ unsigned int irqs_unhandled;raw_spinlock_t lock; +cpumask_var_t *percpu_enabled;#ifdef CONFIG_SMP const struct cpumask *affinity_hint;struct irq_affinity_notify *affinity_notify;
kernel/irq/Kconfig +4 -0
@@ -60,6 +60,10 @@ config IRQ_DOMAIN config IRQ_FORCED_THREADING bool +# Support per CPU dev id +config IRQ_PERCPU_DEVID + bool + config SPARSE_IRQ bool "Support sparse irq numbering" depends on HAVE_SPARSE_IRQ
kernel/irq/chip.c +54 -0
66 lines not on this page · raw message
kernel/irq/internals.h +2 -0
@@ -71,6 +71,8 @@ extern int irq_startup(struct irq_desc *desc);extern void irq_shutdown (struct irq_desc *desc);extern void irq_enable (struct irq_desc *desc);extern void irq_disable (struct irq_desc *desc); +extern void irq_percpu_enable (struct irq_desc *desc,unsigned int cpu); +extern void irq_percpu_disable (struct irq_desc *desc,unsigned int cpu);extern void mask_irq (struct irq_desc *desc);extern void unmask_irq (struct irq_desc *desc);
kernel/irq/irqdesc.c +25 -0
@@ -443,6 +443,31 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus)chip_bus_sync_unlock (desc); } +#ifdef CONFIG_IRQ_PERCPU_DEVID +int irq_set_percpu_devid (unsigned int irq) +{ +struct irq_desc *desc =irq_to_desc (irq); + +if (!desc) +return -EINVAL ; + +if (desc->percpu_enabled) +return -EINVAL ; + + desc->percpu_enabled =kzalloc (sizeof (*desc->percpu_enabled),GFP_KERNEL ); + +if (!desc->percpu_enabled || + !zalloc_cpumask_var (desc->percpu_enabled,GFP_KERNEL )) { +kfree (desc->percpu_enabled); + desc->percpu_enabled =NULL ; +return -ENOMEM ; + } + +irq_set_percpu_devid_flags (irq); +return 0 ; +} +#endif +/** * dynamic_irq_cleanup - cleanup a dynamically allocated irq * @irq: irq number to initialize
kernel/irq/manage.c +202 -4
239 lines not on this page · raw message
kernel/irq/settings.h +7 -0
@@ -13,6 +13,7 @@ enum { _IRQ_MOVE_PCNTXT =IRQ_MOVE_PCNTXT , _IRQ_NO_BALANCING =IRQ_NO_BALANCING , _IRQ_NESTED_THREAD =IRQ_NESTED_THREAD , + _IRQ_PER_CPU_DEVID =IRQ_PER_CPU_DEVID , _IRQF_MODIFY_MASK = IRQF_MODIFY_MASK, };
@@ -24,6 +25,7 @@ enum {#define IRQ_NOTHREAD GOT_YOU_MORON#define IRQ_NOAUTOEN GOT_YOU_MORON#define IRQ_NESTED_THREAD GOT_YOU_MORON +#define IRQ_PER_CPU_DEVID GOT_YOU_MORON#undef IRQF_MODIFY_MASK#define IRQF_MODIFY_MASK GOT_YOU_MORON
@@ -39,6 +41,11 @@ static inline bool irq_settings_is_per_cpu(struct irq_desc *desc)return desc->status_use_accessors & _IRQ_PER_CPU; } +static inline bool irq_settings_is_per_cpu_devid (struct irq_desc *desc) +{ +return desc->status_use_accessors & _IRQ_PER_CPU_DEVID; +} +static inline void irq_settings_set_per_cpu (struct irq_desc *desc) { desc->status_use_accessors |= _IRQ_PER_CPU;
Signature · 1 line
1.7.0.4
Re: [PATCH v3 1/3] genirq: add support for per-cpu dev_id interrupts
Quoted 18 lines · Marc Zyngier
The ARM GIC interrupt controller offers per CPU interrupts (PPIs), which are usually used to connect local timers to each core. Each CPU has its own private interface to the GIC,
and only sees the PPIs that are directly connect to it.While these timers are separate devices and have a separate interrupt line to a core, they all use the same IRQ number.
For these devices,
request_irq()is not the right API as it assumes that an IRQ number is visible by a number of CPUs (through the affinity setting), but makes it very awkward to express that an IRQ number can be handled by all CPUs, and yet be a different interrupt line on each CPU, requiring a differentdev_idcookie to be passed back to the handler.The
*_percpu_irq()functions is designed to overcome these limitations, by providing a per-cpudev_idvector:
Quoted 6 lines
int request_percpu_irq(unsigned int irq,
irq_handler_thandler,const char *devname, void __percpu *percpu_dev_id);void
free_percpu_irq(unsigned int, void __percpu *); intsetup_percpu_irq(unsigned int irq, struct irqaction *new); voidremove_percpu_irq(unsigned int irq, struct irqaction *act); voidenable_percpu_irq(unsigned int irq);
As mentioned here
https://lkml.org/lkml/2011/9/25/121
can we add irqflags to enable_percpu_irq? This will make msm's usage cleaner and it wont have to rely on accessing the gic registers outside the driver.
---
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Re: [PATCH v3 1/3] genirq: add support for per-cpu dev_id interrupts
Quoted 26 lines · Abhijeet Dharmapurikar
Quoted 18 lines · Marc Zyngier
The ARM GIC interrupt controller offers per CPU interrupts (PPIs), which are usually used to connect local timers to each core. Each CPU has its own private interface to the GIC,
and only sees the PPIs that are directly connect to it.While these timers are separate devices and have a separate interrupt line to a core, they all use the same IRQ number.
For these devices,
request_irq()is not the right API as it assumes that an IRQ number is visible by a number of CPUs (through the affinity setting), but makes it very awkward to express that an IRQ number can be handled by all CPUs, and yet be a different interrupt line on each CPU, requiring a differentdev_idcookie to be passed back to the handler.The
*_percpu_irq()functions is designed to overcome these limitations, by providing a per-cpudev_idvector:Quoted 6 lines
int request_percpu_irq(unsigned int irq,
irq_handler_thandler,const char *devname, void __percpu *percpu_dev_id);void
free_percpu_irq(unsigned int, void __percpu *); intsetup_percpu_irq(unsigned int irq, struct irqaction *new); voidremove_percpu_irq(unsigned int irq, struct irqaction *act); voidenable_percpu_irq(unsigned int irq);
Quoted 6 lines
As mentioned here
https://lkml.org/lkml/2011/9/25/121can we add irqflags to
enable_percpu_irq? This will make msm's usage cleaner and it wont have to rely on accessing the gic registers outside the driver.
I have the attached patch in my tree (and obviously hacked the corresponding timer code).
Can you give it a whirl by removing your private GIC configuration and passing the type to enable_percpu_irq()?
Thanks,
M.
Signature · 1 line
Jazz is not dead. It just smells funny...
Quoted 1 line
From
9b1df95320aea19dce7232e8b7d66104c380aed2Mon Sep 17 00:00:00 2001
From: Marc Zyngier <[email protected]>
Date: Fri, 30 Sep 2011 10:48:47 +0100
Subject: [PATCH] genirq: percpu: allow interrupt type to be set at enable time
As request_percpu_irq() doesn't allow for a percpu interrupt to have its type configured (it is generally impossible to configure it on all CPUs at once), add a 'type' argument to enable_percpu_irq().
This allows some low-level, board specific init code to be switched to a generic API.
--- include/linux/interrupt.h | 2 +- kernel/irq/manage.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-)
include/linux/interrupt.h +1 -1
@@ -223,7 +223,7 @@ extern void disable_irq_nosync(unsigned int irq);extern void disable_irq (unsigned int irq);extern void disable_percpu_irq (unsigned int irq);extern void enable_irq (unsigned int irq); -extern void enable_percpu_irq (unsigned int irq); +extern void enable_percpu_irq (unsigned int irq,unsigned int type);/* The following three functions are for the core kernel use only. */ #ifdef CONFIG_GENERIC_HARDIRQS
kernel/irq/manage.c +14 -1
@@ -1419,7 +1419,7 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler, }EXPORT_SYMBOL_GPL (request_any_context_irq); -void enable_percpu_irq (unsigned int irq) +void enable_percpu_irq (unsigned int irq,unsigned int type) {unsigned int cpu =smp_processor_id ();unsigned long flags;
@@ -1428,7 +1428,20 @@ void enable_percpu_irq(unsigned int irq)if (!desc)return ; + type &=IRQ_TYPE_SENSE_MASK ; +if (type !=IRQ_TYPE_NONE ) { +int ret; + + ret =__irq_set_trigger (desc, irq, type); + +if (ret) { +WARN (1 ,"failed to set type for IRQ%d\n" ); + goto out; + } + } +irq_percpu_enable (desc, cpu); +out:irq_put_desc_unlock (desc, flags); }
Signature · 1 line
1.7.0.4
PATCH v3 2/3 ARM: gic: consolidate PPI handling
In reply to Marc ZyngierPPI handling is a bit of an odd beast. It uses its own low level handling code and is hardwired to the local timers (hence lacking a registration interface).
Instead, switch the low handling to the normal SPI handling code. PPIs are handled by the handle_percpu_devid_irq flow.
This also allows the removal of some duplicated code.
--- arch/arm/common/Kconfig | 1 + arch/arm/common/gic.c | 70 +++++++++++++++++++- arch/arm/include/asm/entry-macro-multi.S | 7 -- arch/arm/include/asm/hardirq.h | 3 - arch/arm/include/asm/hardware/entry-macro-gic.S | 19 +----- arch/arm/include/asm/localtimer.h | 6 +- arch/arm/include/asm/smp.h | 5 -- arch/arm/kernel/irq.c | 3 - arch/arm/kernel/smp.c | 27 ++------ arch/arm/mach-exynos4/include/mach/entry-macro.S | 6 +-- arch/arm/mach-msm/board-msm8x60.c | 11 --- arch/arm/mach-msm/include/mach/entry-macro-qgic.S | 73 +-------------------- arch/arm/mach-omap2/include/mach/entry-macro.S | 14 +---- arch/arm/mach-shmobile/entry-intc.S | 3 - arch/arm/mach-shmobile/include/mach/entry-macro.S | 3 - 15 files changed, 84 insertions(+), 167 deletions(-)
arch/arm/common/Kconfig +1 -0
@@ -1,4 +1,5 @@ config ARM_GIC + select IRQ_PERCPU_DEVID bool config ARM_VIC
arch/arm/common/gic.c +69 -1
104 lines not on this page · raw message
arch/arm/include/asm/entry-macro-multi.S +0 -7
@@ -25,13 +25,6 @@ movne r1, sp adrne lr, BSYM(1b) bne do_IPI - -#ifdef CONFIG_LOCAL_TIMERS - test_for_ltirq r0, r2, r6, lr - movne r0, sp - adrne lr, BSYM(1b) - bne do_local_timer -#endif #endif 9997: .endm
arch/arm/include/asm/hardirq.h +0 -3
@@ -9,9 +9,6 @@ typedef struct { unsigned int __softirq_pending; -#ifdef CONFIG_LOCAL_TIMERS - unsigned int local_timer_irqs; -#endif #ifdef CONFIG_SMP unsigned int ipi_irqs[NR_IPI]; #endif
arch/arm/include/asm/hardware/entry-macro-gic.S +2 -17
@@ -22,15 +22,11 @@ * interrupt controller spec. To wit: * * Interrupts 0-15 are IPI - * 16-28 are reserved - * 29-31 are local. We allow 30 to be used for the watchdog. + * 16-31 are local. We allow 30 to be used for the watchdog. * 32-1020 are global * 1021-1022 are reserved * 1023 is "spurious" (no interrupt) * - * For now, we ignore all local interrupts so only return an interrupt if it's - * between 30 and 1020. The test_for_ipi routine below will pick up on IPIs. - * * A simple read from the controller will tell us the number of the highest * priority enabled interrupt. We then just need to check whether it is in the * valid range for an IRQ (30-1020 inclusive).
@@ -43,7 +39,7 @@ ldr \tmp, =1021 bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr
@@ -62,14 +58,3 @@ strcc \irqstat, [\base, #GIC_CPU_EOI] cmpcs \irqnr, \irqnr .endm - -/* As above, this assumes that irqstat and base are preserved.. */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - mov \tmp, #0 - cmp \irqnr, #29 - moveq \tmp, #1 - streq \irqstat, [\base, #GIC_CPU_EOI] - cmp \tmp, #0 - .endm
arch/arm/include/asm/localtimer.h +4 -2
@@ -10,6 +10,8 @@#ifndef __ASM_ARM_LOCALTIMER_H#define __ASM_ARM_LOCALTIMER_H +#include <linux/interrupt.h> +struct clock_event_device ;/*
@@ -18,9 +20,9 @@ struct clock_event_device;void percpu_timer_setup (void );/* -* Called from assembly, this is the local timer IRQ handler +* Per-cpu timer IRQ handler */ -asmlinkage void do_local_timer(struct pt_regs *); +irqreturn_t percpu_timer_handler (int irq,void *dev_id);#ifdef CONFIG_LOCAL_TIMERS
arch/arm/include/asm/smp.h +0 -5
@@ -94,9 +94,4 @@ extern void platform_cpu_enable(unsigned int cpu);extern void arch_send_call_function_single_ipi (int cpu);extern void arch_send_call_function_ipi_mask (const struct cpumask *mask); -/* -* show local interrupt info -*/ -extern void show_local_irqs (struct seq_file *,int ); -#endif /* ifndef __ASM_ARM_SMP_H */
arch/arm/kernel/irq.c +0 -3
@@ -58,9 +58,6 @@ int arch_show_interrupts(struct seq_file *p, int prec)#ifdef CONFIG_SMP show_ipi_list (p, prec);#endif -#ifdef CONFIG_LOCAL_TIMERS -show_local_irqs (p, prec); -#endif seq_printf (p,"%*s: %10lu\n" , prec,"Err" , irq_err_count);return 0 ; }
arch/arm/kernel/smp.c +5 -22
@@ -504,10 +504,6 @@ u64 smp_irq_stat_cpu(unsigned int cpu)for (i =0 ; i <NR_IPI ; i++) sum +=__get_irq_stat (cpu, ipi_irqs[i]); -#ifdef CONFIG_LOCAL_TIMERS - sum +=__get_irq_stat (cpu, local_timer_irqs); -#endif -return sum; }
@@ -525,29 +521,16 @@ static void ipi_timer(void) }#ifdef CONFIG_LOCAL_TIMERS -asmlinkage void __exception_irq_entry do_local_timer(struct pt_regs *regs) +irqreturn_t percpu_timer_handler (int irq,void *dev_id) { -struct pt_regs *old_regs =set_irq_regs (regs); -int cpu =smp_processor_id (); +struct clock_event_device *evt = &__get_cpu_var (percpu_clockevent);if (local_timer_ack ()) { -__inc_irq_stat (cpu, local_timer_irqs); -ipi_timer (); + evt->event_handler (evt); +return IRQ_HANDLED ; } -set_irq_regs (old_regs); -} - -void show_local_irqs (struct seq_file *p,int prec) -{ -unsigned int cpu; - -seq_printf (p,"%*s: " , prec,"LOC" ); - -for_each_present_cpu (cpu) -seq_printf (p,"%10u " ,__get_irq_stat (cpu, local_timer_irqs)); - -seq_printf (p," Local timer interrupts\n" ); +return IRQ_NONE ; }#endif
arch/arm/mach-exynos4/include/mach/entry-macro.S +1 -5
@@ -55,7 +55,7 @@ bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr
@@ -77,7 +77,3 @@ cmpcs \irqnr, \irqnr .endm - /* As above, this assumes that irqstat and base are preserved.. */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm
arch/arm/mach-msm/board-msm8x60.c +0 -11
@@ -53,8 +53,6 @@ static void __init msm8x60_map_io(void)static void __init msm8x60_init_irq (void ) { -unsigned int i; -gic_init (0 ,GIC_PPI_START ,MSM_QGIC_DIST_BASE , (void *)MSM_QGIC_CPU_BASE );
@@ -66,15 +64,6 @@ static void __init msm8x60_init_irq(void)*/ if (!machine_is_msm8x60_sim ())writel (0x0000FFFF ,MSM_QGIC_DIST_BASE +GIC_DIST_ENABLE_SET ); - -/* FIXME: Not installing AVS_SVICINT and AVS_SVICINTSWDONE yet -* as they are configured as level, which does not play nice with -* handle_percpu_irq. -*/ -for (i =GIC_PPI_START ; i <GIC_SPI_START ; i++) { -if (i !=AVS_SVICINT && i !=AVS_SVICINTSWDONE ) -irq_set_handler (i, handle_percpu_irq); - } }static void __init msm8x60_init (void )
arch/arm/mach-msm/include/mach/entry-macro-qgic.S +1 -72
82 lines not on this page · raw message
arch/arm/mach-omap2/include/mach/entry-macro.S +1 -13
@@ -78,7 +78,7 @@ 4401: ldr \irqstat, [\base, #GIC_CPU_INTACK] ldr \tmp, =1021 bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr
@@ -101,18 +101,6 @@ it cs cmpcs \irqnr, \irqnr .endm - - /* As above, this assumes that irqstat and base are preserved */ - - .macro test_for_ltirq, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - mov \tmp, #0 - cmp \irqnr, #29 - itt eq - moveq \tmp, #1 - streq \irqstat, [\base, #GIC_CPU_EOI] - cmp \tmp, #0 - .endm #endif /* CONFIG_SMP */ #else /* MULTI_OMAP2 */
arch/arm/mach-shmobile/entry-intc.S +0 -3
@@ -51,7 +51,4 @@ .macro test_for_ipi, irqnr, irqstat, base, tmp .endm - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm - arch_irq_handler shmobile_handle_irq_intc
arch/arm/mach-shmobile/include/mach/entry-macro.S +0 -3
@@ -27,8 +27,5 @@ .macro test_for_ipi, irqnr, irqstat, base, tmp .endm - .macro test_for_ltirq, irqnr, irqstat, base, tmp - .endm - .macro arch_ret_to_user, tmp1, tmp2 .endm
Signature · 1 line
1.7.0.4
Tony Lindgren
maintainerPATCH v3 2/3 ARM: gic: consolidate PPI handling
Quoted 9 lines · Marc Zyngier
PPI handling is a bit of an odd beast. It uses its own low level handling code and is hardwired to the local timers (hence lacking a registration interface).
Instead, switch the low handling to the normal SPI handling code. PPIs are handled by the
handle_percpu_devid_irqflow.This also allows the removal of some duplicated code.
Quoted 8 lines
Cc Kukjin Kim <[email protected]>Cc David Brown <[email protected]>Cc Bryan Huntsman <[email protected]>Cc Tony Lindgren <[email protected]>Cc Paul Mundt <[email protected]>Cc Magnus Damm <[email protected]>Cc Thomas Gleixner <[email protected]>Signed-off-by Marc Zyngier <[email protected]>
For the omap changes:
(Sorry had to dig this one from the archive, looks like the Cc addresses might be left out)
PATCH v3 3/3 ARM: gic, local timers: use the request_percpu_irq() interface
In reply to Marc ZyngierThis patch remove the hardcoded link between local timers and PPIs, and convert the PPI users (TWD and MSM timers) to the new *_percpu_irq interface. Also some collateral cleanup (local_timer_ack() is gone, and the interrupt handler is strictly private to each driver).
PPIs are now useable for more than just the local timers.
--- arch/arm/common/gic.c | 52 -------------------------- arch/arm/include/asm/hardware/gic.h | 1 - arch/arm/include/asm/localtimer.h | 17 ++++----- arch/arm/include/asm/smp_twd.h | 2 +- arch/arm/kernel/smp.c | 16 +-------- arch/arm/kernel/smp_twd.c | 47 +++++++++++++++++++++++- arch/arm/mach-exynos4/mct.c | 5 --- arch/arm/mach-msm/timer.c | 69 ++++++++++++++++++++--------------- 8 files changed, 94 insertions(+), 115 deletions(-)
arch/arm/common/gic.c +0 -52
@@ -35,7 +35,6 @@#include <asm/irq.h> #include <asm/mach/irq.h> #include <asm/hardware/gic.h> -#include <asm/localtimer.h> static DEFINE_RAW_SPINLOCK (irq_controller_lock);
@@ -259,32 +258,6 @@ void __init gic_cascade_irq(unsigned int gic_nr, unsigned int irq)irq_set_chained_handler (irq, gic_handle_cascade_irq); } -#ifdef CONFIG_LOCAL_TIMERS -#define gic_ppi_handler percpu_timer_handler -#else -static irqreturn_t gic_ppi_handler (int irq,void *dev_id) -{ -return IRQ_NONE ; -} -#endif - -#define PPI_IRQACT (nr) \ - { \ - .handler = gic_ppi_handler, \ - .flags = IRQF_PERCPU | IRQF_TIMER, \ - .irq = nr, \ - .name = "PPI-" # nr, \ - } - -static struct irqaction ppi_irqaction_template[16 ]__initdata = { -PPI_IRQACT (0 ),PPI_IRQACT (1 ),PPI_IRQACT (2 ),PPI_IRQACT (3 ), -PPI_IRQACT (4 ),PPI_IRQACT (5 ),PPI_IRQACT (6 ),PPI_IRQACT (7 ), -PPI_IRQACT (8 ),PPI_IRQACT (9 ),PPI_IRQACT (10 ),PPI_IRQACT (11 ), -PPI_IRQACT (12 ),PPI_IRQACT (13 ),PPI_IRQACT (14 ),PPI_IRQACT (15 ), -}; - -static struct irqaction *ppi_irqaction; -static void __init gic_dist_init (struct gic_chip_data *gic,unsigned int irq_start) {
@@ -320,16 +293,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic,if (gic == &gic_data[0 ]) { nrppis =16 - (irq_start &15 ); ppi_base = gic->irq_offset +32 - nrppis; - - ppi_irqaction =kmemdup (&ppi_irqaction_template[16 - nrppis], -sizeof (*ppi_irqaction) * nrppis, -GFP_KERNEL ); - -if (nrppis && !ppi_irqaction) { -pr_err ("GIC: Can't allocate PPI memory" ); - nrppis =0 ; - ppi_base =0 ; - } } pr_info("Configuring GIC with %d sources (%d PPIs)\n",
@@ -372,17 +335,12 @@ static void __init gic_dist_init(struct gic_chip_data *gic,*/ for (i =0 ; i < nrppis; i++) {int ppi = i + ppi_base; -int err;irq_set_percpu_devid (ppi);irq_set_chip_and_handler (ppi, &gic_chip, handle_percpu_devid_irq);irq_set_chip_data (ppi, gic);set_irq_flags (ppi,IRQF_VALID |IRQF_NOAUTOEN ); - - err =setup_percpu_irq (ppi, &ppi_irqaction[i]); -if (err) -pr_err ("GIC: can't setup PPI%d (%d)\n" , ppi, err); }for (i = irq_start + nrppis; i < irq_limit; i++) {
@@ -443,16 +401,6 @@ void __cpuinit gic_secondary_init(unsigned int gic_nr)gic_cpu_init (&gic_data[gic_nr]); } -void __cpuinit gic_enable_ppi (unsigned int irq) -{ -unsigned long flags; - -local_irq_save (flags); -irq_set_status_flags (irq,IRQ_NOPROBE ); -gic_unmask_irq (irq_get_irq_data (irq)); -local_irq_restore (flags); -} -#ifdef CONFIG_SMP void gic_raise_softirq (const struct cpumask *mask,unsigned int irq) {
arch/arm/include/asm/hardware/gic.h +0 -1
@@ -40,7 +40,6 @@ void gic_init(unsigned int, unsigned int, void __iomem *, void __iomem *);void gic_secondary_init (unsigned int );void gic_cascade_irq (unsigned int gic_nr,unsigned int irq);void gic_raise_softirq (const struct cpumask *mask,unsigned int irq); -void gic_enable_ppi (unsigned int );struct gic_chip_data {unsigned int irq_offset;
arch/arm/include/asm/localtimer.h +7 -10
@@ -19,27 +19,20 @@ struct clock_event_device;*/ void percpu_timer_setup (void ); -/* -* Per-cpu timer IRQ handler -*/ -irqreturn_t percpu_timer_handler (int irq,void *dev_id); - -#ifdef CONFIG_LOCAL_TIMERS #ifdef CONFIG_HAVE_ARM_TWD #include "smp_twd.h" -#define local_timer_ack () twd_timer_ack() +#define local_timer_stop (c) twd_timer_stop((c))#else /* -* Platform provides this to acknowledge a local timer IRQ. -* Returns true if the local timer IRQ is to be processed. +* Stop the local timer */ -int local_timer_ack (void ); +void local_timer_stop (struct clock_event_device *);#endif
@@ -54,6 +47,10 @@ static inline int local_timer_setup(struct clock_event_device *evt) {return -ENXIO ; } + +static inline void local_timer_stop (struct clock_event_device *evt) +{ +}#endif #endif
arch/arm/include/asm/smp_twd.h +1 -1
@@ -22,7 +22,7 @@ struct clock_event_device;extern void __iomem *twd_base; -int twd_timer_ack (void );void twd_timer_setup (struct clock_event_device *); +void twd_timer_stop (struct clock_event_device *);#endif
arch/arm/kernel/smp.c +1 -15
@@ -520,20 +520,6 @@ static void ipi_timer(void)irq_exit (); } -#ifdef CONFIG_LOCAL_TIMERS -irqreturn_t percpu_timer_handler (int irq,void *dev_id) -{ -struct clock_event_device *evt = &__get_cpu_var (percpu_clockevent); - -if (local_timer_ack ()) { - evt->event_handler (evt); -return IRQ_HANDLED ; - } - -return IRQ_NONE ; -} -#endif -#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST static void smp_timer_broadcast (const struct cpumask *mask) {
@@ -584,7 +570,7 @@ static void percpu_timer_stop(void)unsigned int cpu =smp_processor_id ();struct clock_event_device *evt = &per_cpu (percpu_clockevent, cpu); - evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); + local_timer_stop(evt); }#endif
arch/arm/kernel/smp_twd.c +45 -2
82 lines not on this page · raw message
arch/arm/mach-exynos4/mct.c +0 -5
@@ -402,11 +402,6 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt) return 0; } -int local_timer_ack (void ) -{ -return 0 ; -} -#endif /* CONFIG_LOCAL_TIMERS */ static void __init exynos4_timer_resources (void )
arch/arm/mach-msm/timer.c +40 -29
136 lines not on this page · raw message
Signature · 1 line
1.7.0.4
Quoted 12 lines · Marc Zyngier
Tested on ARM Versatile Express (Cortex A15), ARM RealView PB11MP, OMAP4 (Panda) and Tegra (Harmony). Patch series against next-20110923.
From v2:
- Fixed !GENERIC_HARDIRQS build
- Fixedrequest_percpu_irqdocumentationFrom v1:
- General tidy-up after Thomas' review. I've kept the config option for the time being until we can sort out the anonymous union problem.
Quoted 4 lines
Marc Zyngier (3):
genirq: add support for per-cpudev_idinterrupts
ARM: gic: consolidate PPI handling
ARM: gic, local timers: use therequest_percpu_irq()interface
I've tested this on an MSM8660 based off of next-20110926.
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Quoted 19 lines · David Brown
Quoted 12 lines · Marc Zyngier
Tested on ARM Versatile Express (Cortex A15), ARM RealView PB11MP, OMAP4 (Panda) and Tegra (Harmony). Patch series against next-20110923.
From v2:
- Fixed !GENERIC_HARDIRQS build
- Fixedrequest_percpu_irqdocumentationFrom v1:
- General tidy-up after Thomas' review. I've kept the config option for the time being until we can sort out the anonymous union problem.Quoted 4 lines
Marc Zyngier (3):
genirq: add support for per-cpudev_idinterrupts
ARM: gic: consolidate PPI handling
ARM: gic, local timers: use therequest_percpu_irq()interface
Quoted 4 lines
I've tested this on an MSM8660 based off of next-20110926.
Tested-by David Brown <[email protected]>Acked-by David Brown <[email protected]>
Also tested on MSM8250.
David
Signature · 2 lines
Sent by an employee of the Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Quoted 33 lines · Marc Zyngier
The current GIC per-cpu interrupts (aka PPIs) suffer from a number of problems:
- They use a completely separate scheme to handle the interrupts, mostly because the PPI concept doesn't really match the kernel view of an interrupt.
- PPIs can only be used by the timer code, unless we add more low-level assembly code.
- The local timer code can only be used by devices generating PPIs, and not SPIs.
- At least one platform (msm) has started implementing its own alternative scheme.
- Some low-level code gets duplicated, as usual...The proposed solution is to handle the PPIs using the same path as SPIs. A new core API is added to deal with per-cpu interrupts in a less awkward way. The local timer code is updated to reflect these changes.
The core API changes are based on an initial idea by Thomas Gleixner.
Tested on ARM Versatile Express (Cortex A15), ARM RealView PB11MP, OMAP4 (Panda) and Tegra (Harmony). Patch series against next-20110923.
From v2:
- Fixed !GENERIC_HARDIRQS build
- Fixedrequest_percpu_irqdocumentationFrom v1:
- General tidy-up after Thomas' review. I've kept the config option for the time being until we can sort out the anonymous union problem.
Quoted 5 lines
Marc Zyngier (3):
genirq: add support for per-cpudev_idinterrupts
ARM: gic: consolidate PPI handling
ARM: gic, local timers: use therequest_percpu_irq()interface
On imx6q:
Signature · 2 lines
Regards, Shawn