aboutsummaryrefslogtreecommitdiffstats
path: root/include
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2011-09-23 12:03:06 -0400
committerThomas Gleixner <tglx@linutronix.de>2011-10-03 09:35:26 -0400
commit31d9d9b6d83030f748d013e61502fa5477e2ac0e (patch)
tree503670b94d594c09daa83c047b426e7b5328aa76 /include
parent60f96b41f71d2a13d1c0a457b8b77958f77142d1 (diff)
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. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Cc: linux-arm-kernel@lists.infradead.org Link: http://lkml.kernel.org/r/1316793788-14500-2-git-send-email-marc.zyngier@arm.com Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'include')
-rw-r--r--include/linux/interrupt.h38
-rw-r--r--include/linux/irq.h16
-rw-r--r--include/linux/irqdesc.h1
3 files changed, 43 insertions, 12 deletions
diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h
index a103732b7588..1cdfd09c8abc 100644
--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -95,6 +95,7 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
95 * @flags: flags (see IRQF_* above) 95 * @flags: flags (see IRQF_* above)
96 * @name: name of the device 96 * @name: name of the device
97 * @dev_id: cookie to identify the device 97 * @dev_id: cookie to identify the device
98 * @percpu_dev_id: cookie to identify the device
98 * @next: pointer to the next irqaction for shared interrupts 99 * @next: pointer to the next irqaction for shared interrupts
99 * @irq: interrupt number 100 * @irq: interrupt number
100 * @dir: pointer to the proc/irq/NN/name entry 101 * @dir: pointer to the proc/irq/NN/name entry
@@ -104,17 +105,18 @@ typedef irqreturn_t (*irq_handler_t)(int, void *);
104 * @thread_mask: bitmask for keeping track of @thread activity 105 * @thread_mask: bitmask for keeping track of @thread activity
105 */ 106 */
106struct irqaction { 107struct irqaction {
107 irq_handler_t handler; 108 irq_handler_t handler;
108 unsigned long flags; 109 unsigned long flags;
109 void *dev_id; 110 void *dev_id;
110 struct irqaction *next; 111 void __percpu *percpu_dev_id;
111 int irq; 112 struct irqaction *next;
112 irq_handler_t thread_fn; 113 int irq;
113 struct task_struct *thread; 114 irq_handler_t thread_fn;
114 unsigned long thread_flags; 115 struct task_struct *thread;
115 unsigned long thread_mask; 116 unsigned long thread_flags;
116 const char *name; 117 unsigned long thread_mask;
117 struct proc_dir_entry *dir; 118 const char *name;
119 struct proc_dir_entry *dir;
118} ____cacheline_internodealigned_in_smp; 120} ____cacheline_internodealigned_in_smp;
119 121
120extern irqreturn_t no_action(int cpl, void *dev_id); 122extern irqreturn_t no_action(int cpl, void *dev_id);
@@ -136,6 +138,10 @@ extern int __must_check
136request_any_context_irq(unsigned int irq, irq_handler_t handler, 138request_any_context_irq(unsigned int irq, irq_handler_t handler,
137 unsigned long flags, const char *name, void *dev_id); 139 unsigned long flags, const char *name, void *dev_id);
138 140
141extern int __must_check
142request_percpu_irq(unsigned int irq, irq_handler_t handler,
143 const char *devname, void __percpu *percpu_dev_id);
144
139extern void exit_irq_thread(void); 145extern void exit_irq_thread(void);
140#else 146#else
141 147
@@ -164,10 +170,18 @@ request_any_context_irq(unsigned int irq, irq_handler_t handler,
164 return request_irq(irq, handler, flags, name, dev_id); 170 return request_irq(irq, handler, flags, name, dev_id);
165} 171}
166 172
173static inline int __must_check
174request_percpu_irq(unsigned int irq, irq_handler_t handler,
175 const char *devname, void __percpu *percpu_dev_id)
176{
177 return request_irq(irq, handler, 0, devname, percpu_dev_id);
178}
179
167static inline void exit_irq_thread(void) { } 180static inline void exit_irq_thread(void) { }
168#endif 181#endif
169 182
170extern void free_irq(unsigned int, void *); 183extern void free_irq(unsigned int, void *);
184extern void free_percpu_irq(unsigned int, void __percpu *);
171 185
172struct device; 186struct device;
173 187
@@ -207,7 +221,9 @@ extern void devm_free_irq(struct device *dev, unsigned int irq, void *dev_id);
207 221
208extern void disable_irq_nosync(unsigned int irq); 222extern void disable_irq_nosync(unsigned int irq);
209extern void disable_irq(unsigned int irq); 223extern void disable_irq(unsigned int irq);
224extern void disable_percpu_irq(unsigned int irq);
210extern void enable_irq(unsigned int irq); 225extern void enable_irq(unsigned int irq);
226extern void enable_percpu_irq(unsigned int irq);
211 227
212/* The following three functions are for the core kernel use only. */ 228/* The following three functions are for the core kernel use only. */
213#ifdef CONFIG_GENERIC_HARDIRQS 229#ifdef CONFIG_GENERIC_HARDIRQS
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 73e31abeba1c..59e49c80cc2c 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -66,6 +66,7 @@ typedef void (*irq_preflow_handler_t)(struct irq_data *data);
66 * IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set) 66 * IRQ_NO_BALANCING - Interrupt cannot be balanced (affinity set)
67 * IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context 67 * IRQ_MOVE_PCNTXT - Interrupt can be migrated from process context
68 * IRQ_NESTED_TRHEAD - Interrupt nests into another thread 68 * IRQ_NESTED_TRHEAD - Interrupt nests into another thread
69 * IRQ_PER_CPU_DEVID - Dev_id is a per-cpu variable
69 */ 70 */
70enum { 71enum {
71 IRQ_TYPE_NONE = 0x00000000, 72 IRQ_TYPE_NONE = 0x00000000,
@@ -88,12 +89,13 @@ enum {
88 IRQ_MOVE_PCNTXT = (1 << 14), 89 IRQ_MOVE_PCNTXT = (1 << 14),
89 IRQ_NESTED_THREAD = (1 << 15), 90 IRQ_NESTED_THREAD = (1 << 15),
90 IRQ_NOTHREAD = (1 << 16), 91 IRQ_NOTHREAD = (1 << 16),
92 IRQ_PER_CPU_DEVID = (1 << 17),
91}; 93};
92 94
93#define IRQF_MODIFY_MASK \ 95#define IRQF_MODIFY_MASK \
94 (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \ 96 (IRQ_TYPE_SENSE_MASK | IRQ_NOPROBE | IRQ_NOREQUEST | \
95 IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \ 97 IRQ_NOAUTOEN | IRQ_MOVE_PCNTXT | IRQ_LEVEL | IRQ_NO_BALANCING | \
96 IRQ_PER_CPU | IRQ_NESTED_THREAD) 98 IRQ_PER_CPU | IRQ_NESTED_THREAD | IRQ_NOTHREAD | IRQ_PER_CPU_DEVID)
97 99
98#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING) 100#define IRQ_NO_BALANCING_MASK (IRQ_PER_CPU | IRQ_NO_BALANCING)
99 101
@@ -367,6 +369,8 @@ enum {
367struct irqaction; 369struct irqaction;
368extern int setup_irq(unsigned int irq, struct irqaction *new); 370extern int setup_irq(unsigned int irq, struct irqaction *new);
369extern void remove_irq(unsigned int irq, struct irqaction *act); 371extern void remove_irq(unsigned int irq, struct irqaction *act);
372extern int setup_percpu_irq(unsigned int irq, struct irqaction *new);
373extern void remove_percpu_irq(unsigned int irq, struct irqaction *act);
370 374
371extern void irq_cpu_online(void); 375extern void irq_cpu_online(void);
372extern void irq_cpu_offline(void); 376extern void irq_cpu_offline(void);
@@ -394,6 +398,7 @@ extern void handle_edge_irq(unsigned int irq, struct irq_desc *desc);
394extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc); 398extern void handle_edge_eoi_irq(unsigned int irq, struct irq_desc *desc);
395extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc); 399extern void handle_simple_irq(unsigned int irq, struct irq_desc *desc);
396extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc); 400extern void handle_percpu_irq(unsigned int irq, struct irq_desc *desc);
401extern void handle_percpu_devid_irq(unsigned int irq, struct irq_desc *desc);
397extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc); 402extern void handle_bad_irq(unsigned int irq, struct irq_desc *desc);
398extern void handle_nested_irq(unsigned int irq); 403extern void handle_nested_irq(unsigned int irq);
399 404
@@ -422,6 +427,8 @@ static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *c
422 irq_set_chip_and_handler_name(irq, chip, handle, NULL); 427 irq_set_chip_and_handler_name(irq, chip, handle, NULL);
423} 428}
424 429
430extern int irq_set_percpu_devid(unsigned int irq);
431
425extern void 432extern void
426__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained, 433__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
427 const char *name); 434 const char *name);
@@ -483,6 +490,13 @@ static inline void irq_set_nested_thread(unsigned int irq, bool nest)
483 irq_clear_status_flags(irq, IRQ_NESTED_THREAD); 490 irq_clear_status_flags(irq, IRQ_NESTED_THREAD);
484} 491}
485 492
493static inline void irq_set_percpu_devid_flags(unsigned int irq)
494{
495 irq_set_status_flags(irq,
496 IRQ_NOAUTOEN | IRQ_PER_CPU | IRQ_NOTHREAD |
497 IRQ_NOPROBE | IRQ_PER_CPU_DEVID);
498}
499
486/* Handle dynamic irq creation and destruction */ 500/* Handle dynamic irq creation and destruction */
487extern unsigned int create_irq_nr(unsigned int irq_want, int node); 501extern unsigned int create_irq_nr(unsigned int irq_want, int node);
488extern int create_irq(void); 502extern int create_irq(void);
diff --git a/include/linux/irqdesc.h b/include/linux/irqdesc.h
index 150134ac709a..6b69c2c9dff1 100644
--- a/include/linux/irqdesc.h
+++ b/include/linux/irqdesc.h
@@ -53,6 +53,7 @@ struct irq_desc {
53 unsigned long last_unhandled; /* Aging timer for unhandled count */ 53 unsigned long last_unhandled; /* Aging timer for unhandled count */
54 unsigned int irqs_unhandled; 54 unsigned int irqs_unhandled;
55 raw_spinlock_t lock; 55 raw_spinlock_t lock;
56 struct cpumask *percpu_enabled;
56#ifdef CONFIG_SMP 57#ifdef CONFIG_SMP
57 const struct cpumask *affinity_hint; 58 const struct cpumask *affinity_hint;
58 struct irq_affinity_notify *affinity_notify; 59 struct irq_affinity_notify *affinity_notify;