From ed585a651681e822089087b426e6ebfb6d3d9873 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 11 Sep 2011 13:59:27 +0200 Subject: [PATCH 01/15] genirq: Make irq_shutdown() symmetric vs. irq_startup again If an irq_chip provides .irq_shutdown(), but neither of .irq_disable() or .irq_mask(), free_irq() crashes when jumping to NULL. Fix this by only trying .irq_disable() and .irq_mask() if there's no .irq_shutdown() provided. This revives the symmetry with irq_startup(), which tries .irq_startup(), .irq_enable(), and irq_unmask(), and makes it consistent with the comment for irq_chip.irq_shutdown() in , which says: * @irq_shutdown: shut down the interrupt (defaults to ->disable if NULL) This is also how __free_irq() behaved before the big overhaul, cfr. e.g. 3b56f0585fd4c02d047dc406668cb40159b2d340 ("genirq: Remove bogus conditional"), where the core interrupt code always overrode .irq_shutdown() to .irq_disable() if .irq_shutdown() was NULL. Signed-off-by: Geert Uytterhoeven Cc: linux-m68k@lists.linux-m68k.org Link: http://lkml.kernel.org/r/1315742394-16036-2-git-send-email-geert@linux-m68k.org Cc: stable@kernel.org Signed-off-by: Thomas Gleixner --- kernel/irq/chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index d5a3009da71a..dc5114b4c16c 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -178,7 +178,7 @@ void irq_shutdown(struct irq_desc *desc) desc->depth = 1; if (desc->irq_data.chip->irq_shutdown) desc->irq_data.chip->irq_shutdown(&desc->irq_data); - if (desc->irq_data.chip->irq_disable) + else 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); From 60f96b41f71d2a13d1c0a457b8b77958f77142d1 Mon Sep 17 00:00:00 2001 From: Santosh Shilimkar Date: Fri, 9 Sep 2011 13:59:35 +0530 Subject: [PATCH 02/15] genirq: Add IRQCHIP_SKIP_SET_WAKE flag Some irq chips need the irq_set_wake() functionality, but do not require a irq_set_wake() callback. Instead of forcing an empty callback to be implemented add a flag which notes this fact. Check for the flag in set_irq_wake_real() and return success when set. Signed-off-by: Santosh Shilimkar Cc: Thomas Gleixner --- include/linux/irq.h | 2 ++ kernel/irq/manage.c | 3 +++ 2 files changed, 5 insertions(+) diff --git a/include/linux/irq.h b/include/linux/irq.h index 59517300a315..73e31abeba1c 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -336,12 +336,14 @@ struct irq_chip { * IRQCHIP_MASK_ON_SUSPEND: Mask non wake irqs in the suspend path * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks * when irq enabled + * IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip */ enum { IRQCHIP_SET_TYPE_MASKED = (1 << 0), IRQCHIP_EOI_IF_HANDLED = (1 << 1), IRQCHIP_MASK_ON_SUSPEND = (1 << 2), IRQCHIP_ONOFFLINE_ENABLED = (1 << 3), + IRQCHIP_SKIP_SET_WAKE = (1 << 4), }; /* This include will go away once we isolated irq_desc usage to core code */ diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 9b956fa20308..7e1a3ed1e61a 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -467,6 +467,9 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on) struct irq_desc *desc = irq_to_desc(irq); int ret = -ENXIO; + if (irq_desc_get_chip(desc)->flags & IRQCHIP_SKIP_SET_WAKE) + return 0; + if (desc->irq_data.chip->irq_set_wake) ret = desc->irq_data.chip->irq_set_wake(&desc->irq_data, on); From 31d9d9b6d83030f748d013e61502fa5477e2ac0e Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 23 Sep 2011 17:03:06 +0100 Subject: [PATCH 03/15] 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 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 --- include/linux/interrupt.h | 38 ++++--- include/linux/irq.h | 16 ++- include/linux/irqdesc.h | 1 + kernel/irq/chip.c | 64 ++++++++++-- kernel/irq/internals.h | 19 +++- kernel/irq/irqdesc.c | 32 +++++- kernel/irq/manage.c | 202 ++++++++++++++++++++++++++++++++++++-- kernel/irq/settings.h | 7 ++ 8 files changed, 345 insertions(+), 34 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 *); * @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,18 @@ 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; + void __percpu *percpu_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; } ____cacheline_internodealigned_in_smp; extern irqreturn_t no_action(int cpl, void *dev_id); @@ -136,6 +138,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 +170,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 +221,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 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); * 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 *c irq_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); 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 { unsigned long last_unhandled; /* Aging timer for unhandled count */ unsigned int irqs_unhandled; raw_spinlock_t lock; + struct cpumask *percpu_enabled; #ifdef CONFIG_SMP const struct cpumask *affinity_hint; struct irq_affinity_notify *affinity_notify; diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index dc5114b4c16c..f7c543a801d9 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -26,7 +26,7 @@ int irq_set_chip(unsigned int irq, struct irq_chip *chip) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); if (!desc) return -EINVAL; @@ -54,7 +54,7 @@ EXPORT_SYMBOL(irq_set_chip); int irq_set_irq_type(unsigned int irq, unsigned int type) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); + struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); int ret = 0; if (!desc) @@ -78,7 +78,7 @@ EXPORT_SYMBOL(irq_set_irq_type); int irq_set_handler_data(unsigned int irq, void *data) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); if (!desc) return -EINVAL; @@ -98,7 +98,7 @@ EXPORT_SYMBOL(irq_set_handler_data); int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); if (!desc) return -EINVAL; @@ -119,7 +119,7 @@ int irq_set_msi_desc(unsigned int irq, struct msi_desc *entry) int irq_set_chip_data(unsigned int irq, void *data) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); if (!desc) return -EINVAL; @@ -204,6 +204,24 @@ void irq_disable(struct irq_desc *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) @@ -544,12 +562,44 @@ handle_percpu_irq(unsigned int irq, struct irq_desc *desc) chip->irq_eoi(&desc->irq_data); } +/** + * handle_percpu_devid_irq - Per CPU local irq handler with per cpu dev ids + * @irq: the interrupt number + * @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(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_desc_get_chip(desc); + struct irqaction *action = desc->action; + void *dev_id = __this_cpu_ptr(action->percpu_dev_id); + irqreturn_t res; + + kstat_incr_irqs_this_cpu(irq, desc); + + if (chip->irq_ack) + chip->irq_ack(&desc->irq_data); + + trace_irq_handler_entry(irq, action); + res = action->handler(irq, dev_id); + trace_irq_handler_exit(irq, action, res); + + if (chip->irq_eoi) + chip->irq_eoi(&desc->irq_data); +} + 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); + struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0); if (!desc) return; @@ -593,7 +643,7 @@ irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip, void irq_modify_status(unsigned int irq, unsigned long clr, unsigned long set) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); if (!desc) return; diff --git a/kernel/irq/internals.h b/kernel/irq/internals.h index 6546431447d7..a73dd6c7372d 100644 --- a/kernel/irq/internals.h +++ b/kernel/irq/internals.h @@ -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); @@ -114,14 +116,21 @@ static inline void chip_bus_sync_unlock(struct irq_desc *desc) desc->irq_data.chip->irq_bus_sync_unlock(&desc->irq_data); } +#define _IRQ_DESC_CHECK (1 << 0) +#define _IRQ_DESC_PERCPU (1 << 1) + +#define IRQ_GET_DESC_CHECK_GLOBAL (_IRQ_DESC_CHECK) +#define IRQ_GET_DESC_CHECK_PERCPU (_IRQ_DESC_CHECK | _IRQ_DESC_PERCPU) + struct irq_desc * -__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus); +__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus, + unsigned int check); void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus); static inline struct irq_desc * -irq_get_desc_buslock(unsigned int irq, unsigned long *flags) +irq_get_desc_buslock(unsigned int irq, unsigned long *flags, unsigned int check) { - return __irq_get_desc_lock(irq, flags, true); + return __irq_get_desc_lock(irq, flags, true, check); } static inline void @@ -131,9 +140,9 @@ irq_put_desc_busunlock(struct irq_desc *desc, unsigned long flags) } static inline struct irq_desc * -irq_get_desc_lock(unsigned int irq, unsigned long *flags) +irq_get_desc_lock(unsigned int irq, unsigned long *flags, unsigned int check) { - return __irq_get_desc_lock(irq, flags, false); + return __irq_get_desc_lock(irq, flags, false, check); } static inline void diff --git a/kernel/irq/irqdesc.c b/kernel/irq/irqdesc.c index 039b889ea053..1550e8447a16 100644 --- a/kernel/irq/irqdesc.c +++ b/kernel/irq/irqdesc.c @@ -424,11 +424,22 @@ unsigned int irq_get_next_irq(unsigned int offset) } struct irq_desc * -__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus) +__irq_get_desc_lock(unsigned int irq, unsigned long *flags, bool bus, + unsigned int check) { struct irq_desc *desc = irq_to_desc(irq); if (desc) { + if (check & _IRQ_DESC_CHECK) { + if ((check & _IRQ_DESC_PERCPU) && + !irq_settings_is_per_cpu_devid(desc)) + return NULL; + + if (!(check & _IRQ_DESC_PERCPU) && + irq_settings_is_per_cpu_devid(desc)) + return NULL; + } + if (bus) chip_bus_lock(desc); raw_spin_lock_irqsave(&desc->lock, *flags); @@ -443,6 +454,25 @@ void __irq_put_desc_unlock(struct irq_desc *desc, unsigned long flags, bool bus) chip_bus_sync_unlock(desc); } +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) + return -ENOMEM; + + irq_set_percpu_devid_flags(irq); + return 0; +} + /** * dynamic_irq_cleanup - cleanup a dynamically allocated irq * @irq: irq number to initialize diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7e1a3ed1e61a..7b4b156d065c 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -195,7 +195,7 @@ int irq_set_affinity(unsigned int irq, const struct cpumask *mask) int irq_set_affinity_hint(unsigned int irq, const struct cpumask *m) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); if (!desc) return -EINVAL; @@ -356,7 +356,7 @@ void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend) static int __disable_irq_nosync(unsigned int irq) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); + struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); if (!desc) return -EINVAL; @@ -448,7 +448,7 @@ void __enable_irq(struct irq_desc *desc, unsigned int irq, bool resume) void enable_irq(unsigned int irq) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); + struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); if (!desc) return; @@ -491,7 +491,7 @@ static int set_irq_wake_real(unsigned int irq, unsigned int on) int irq_set_irq_wake(unsigned int irq, unsigned int on) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_buslock(irq, &flags); + struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL); int ret = 0; if (!desc) @@ -532,7 +532,7 @@ EXPORT_SYMBOL(irq_set_irq_wake); int can_request_irq(unsigned int irq, unsigned long irqflags) { unsigned long flags; - struct irq_desc *desc = irq_get_desc_lock(irq, &flags); + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0); int canrequest = 0; if (!desc) @@ -1121,6 +1121,8 @@ int setup_irq(unsigned int irq, struct irqaction *act) int retval; struct irq_desc *desc = irq_to_desc(irq); + if (WARN_ON(irq_settings_is_per_cpu_devid(desc))) + return -EINVAL; chip_bus_lock(desc); retval = __setup_irq(irq, desc, act); chip_bus_sync_unlock(desc); @@ -1129,7 +1131,7 @@ int setup_irq(unsigned int irq, struct irqaction *act) } EXPORT_SYMBOL_GPL(setup_irq); - /* +/* * Internal function to unregister an irqaction - used to free * regular and special interrupts that are part of the architecture. */ @@ -1227,7 +1229,10 @@ static struct irqaction *__free_irq(unsigned int irq, void *dev_id) */ void remove_irq(unsigned int irq, struct irqaction *act) { - __free_irq(irq, act->dev_id); + struct irq_desc *desc = irq_to_desc(irq); + + if (desc && !WARN_ON(irq_settings_is_per_cpu_devid(desc))) + __free_irq(irq, act->dev_id); } EXPORT_SYMBOL_GPL(remove_irq); @@ -1249,7 +1254,7 @@ void free_irq(unsigned int irq, void *dev_id) { struct irq_desc *desc = irq_to_desc(irq); - if (!desc) + if (!desc || WARN_ON(irq_settings_is_per_cpu_devid(desc))) return; #ifdef CONFIG_SMP @@ -1327,7 +1332,8 @@ int request_threaded_irq(unsigned int irq, irq_handler_t handler, if (!desc) return -EINVAL; - if (!irq_settings_can_request(desc)) + if (!irq_settings_can_request(desc) || + WARN_ON(irq_settings_is_per_cpu_devid(desc))) return -EINVAL; if (!handler) { @@ -1412,3 +1418,181 @@ int request_any_context_irq(unsigned int irq, irq_handler_t handler, return !ret ? IRQC_IS_HARDIRQ : ret; } EXPORT_SYMBOL_GPL(request_any_context_irq); + +void enable_percpu_irq(unsigned int irq) +{ + unsigned int cpu = smp_processor_id(); + unsigned long flags; + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU); + + if (!desc) + return; + + irq_percpu_enable(desc, cpu); + irq_put_desc_unlock(desc, flags); +} + +void disable_percpu_irq(unsigned int irq) +{ + unsigned int cpu = smp_processor_id(); + unsigned long flags; + struct irq_desc *desc = irq_get_desc_lock(irq, &flags, IRQ_GET_DESC_CHECK_PERCPU); + + if (!desc) + return; + + irq_percpu_disable(desc, cpu); + irq_put_desc_unlock(desc, flags); +} + +/* + * Internal function to unregister a percpu irqaction. + */ +static struct irqaction *__free_percpu_irq(unsigned int irq, void __percpu *dev_id) +{ + struct irq_desc *desc = irq_to_desc(irq); + struct irqaction *action; + unsigned long flags; + + WARN(in_interrupt(), "Trying to free IRQ %d from IRQ context!\n", irq); + + if (!desc) + return NULL; + + raw_spin_lock_irqsave(&desc->lock, flags); + + action = desc->action; + if (!action || action->percpu_dev_id != dev_id) { + WARN(1, "Trying to free already-free IRQ %d\n", irq); + goto bad; + } + + if (!cpumask_empty(desc->percpu_enabled)) { + WARN(1, "percpu IRQ %d still enabled on CPU%d!\n", + irq, cpumask_first(desc->percpu_enabled)); + goto bad; + } + + /* Found it - now remove it from the list of entries: */ + desc->action = NULL; + + raw_spin_unlock_irqrestore(&desc->lock, flags); + + unregister_handler_proc(irq, action); + + module_put(desc->owner); + return action; + +bad: + raw_spin_unlock_irqrestore(&desc->lock, flags); + return NULL; +} + +/** + * remove_percpu_irq - free a per-cpu interrupt + * @irq: Interrupt line to free + * @act: irqaction for the interrupt + * + * Used to remove interrupts statically setup by the early boot process. + */ +void remove_percpu_irq(unsigned int irq, struct irqaction *act) +{ + struct irq_desc *desc = irq_to_desc(irq); + + if (desc && irq_settings_is_per_cpu_devid(desc)) + __free_percpu_irq(irq, act->percpu_dev_id); +} + +/** + * free_percpu_irq - free an interrupt allocated with request_percpu_irq + * @irq: Interrupt line to free + * @dev_id: Device identity to free + * + * Remove a percpu interrupt handler. The handler is removed, but + * the interrupt line is not disabled. This must be done on each + * CPU before calling this function. The function does not return + * until any executing interrupts for this IRQ have completed. + * + * This function must not be called from interrupt context. + */ +void free_percpu_irq(unsigned int irq, void __percpu *dev_id) +{ + struct irq_desc *desc = irq_to_desc(irq); + + if (!desc || !irq_settings_is_per_cpu_devid(desc)) + return; + + chip_bus_lock(desc); + kfree(__free_percpu_irq(irq, dev_id)); + chip_bus_sync_unlock(desc); +} + +/** + * setup_percpu_irq - setup a per-cpu interrupt + * @irq: Interrupt line to setup + * @act: irqaction for the interrupt + * + * Used to statically setup per-cpu interrupts in the early boot process. + */ +int setup_percpu_irq(unsigned int irq, struct irqaction *act) +{ + struct irq_desc *desc = irq_to_desc(irq); + int retval; + + if (!desc || !irq_settings_is_per_cpu_devid(desc)) + return -EINVAL; + chip_bus_lock(desc); + retval = __setup_irq(irq, desc, act); + chip_bus_sync_unlock(desc); + + return retval; +} + +/** + * request_percpu_irq - allocate a percpu interrupt line + * @irq: Interrupt line to allocate + * @handler: Function to be called when the IRQ occurs. + * @devname: An ascii name for the claiming device + * @dev_id: A percpu cookie passed back to the handler function + * + * This call allocates interrupt resources, but doesn't + * automatically enable the interrupt. It has to be done on each + * CPU using enable_percpu_irq(). + * + * Dev_id must be globally unique. It is a per-cpu variable, and + * the handler gets called with the interrupted CPU's instance of + * that variable. + */ +int request_percpu_irq(unsigned int irq, irq_handler_t handler, + const char *devname, void __percpu *dev_id) +{ + struct irqaction *action; + struct irq_desc *desc; + int retval; + + if (!dev_id) + return -EINVAL; + + desc = irq_to_desc(irq); + if (!desc || !irq_settings_can_request(desc) || + !irq_settings_is_per_cpu_devid(desc)) + return -EINVAL; + + action = kzalloc(sizeof(struct irqaction), GFP_KERNEL); + if (!action) + return -ENOMEM; + + action->handler = handler; + action->flags = IRQF_PERCPU; + action->name = devname; + action->percpu_dev_id = dev_id; + + chip_bus_lock(desc); + retval = __setup_irq(irq, desc, action); + chip_bus_sync_unlock(desc); + + if (retval) + kfree(action); + + return retval; +} diff --git a/kernel/irq/settings.h b/kernel/irq/settings.h index f1667833d444..1162f1030f18 100644 --- a/kernel/irq/settings.h +++ b/kernel/irq/settings.h @@ -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; From 1e7c5fd29487ee88cb3abac945bafa60ae026146 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 30 Sep 2011 10:48:47 +0100 Subject: [PATCH 04/15] 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. [ tglx: Added WARN_ON argument ] Signed-off-by: Marc Zyngier Cc: Abhijeet Dharmapurikar Signed-off-by: Thomas Gleixner --- include/linux/interrupt.h | 2 +- kernel/irq/manage.c | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/include/linux/interrupt.h b/include/linux/interrupt.h index 1cdfd09c8abc..664544ff77d5 100644 --- a/include/linux/interrupt.h +++ b/include/linux/interrupt.h @@ -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 diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 7b4b156d065c..2bc86869859e 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -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, irq"); + goto out; + } + } + irq_percpu_enable(desc, cpu); +out: irq_put_desc_unlock(desc, flags); } From 32cffdde4a3ee6c2d9e0f0a94edecf1a9ce7586b Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 4 Oct 2011 18:43:57 +0200 Subject: [PATCH 05/15] genirq: Fix fatfinered fixup really Putting the argument inside the quote does not really help. Signed-off-by: Thomas Gleixner --- kernel/irq/manage.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/irq/manage.c b/kernel/irq/manage.c index 2bc86869859e..67ce837ae52c 100644 --- a/kernel/irq/manage.c +++ b/kernel/irq/manage.c @@ -1435,7 +1435,7 @@ void enable_percpu_irq(unsigned int irq, unsigned int type) ret = __irq_set_trigger(desc, irq, type); if (ret) { - WARN(1, "failed to set type for IRQ%d\n, irq"); + WARN(1, "failed to set type for IRQ%d\n", irq); goto out; } } From c9018aab8eee24b993c12c7aff7fc99d3d73f298 Mon Sep 17 00:00:00 2001 From: Vincent Guittot Date: Mon, 8 Aug 2011 13:21:59 +0100 Subject: [PATCH 06/15] ARM: 7011/1: Add ARM cpu topology definition The affinity between ARM processors is defined in the MPIDR register. We can identify which processors are in the same cluster, and which ones have performance interdependency. We can define the cpu topology of ARM platform, that is then used by sched_mc and sched_smt. The default state of sched_mc and sched_smt config is disable. When enabled, the behavior of the scheduler can be modified with sched_mc_power_savings and sched_smt_power_savings sysfs interfaces. Changes since v4 : * Remove unnecessary parentheses and blank lines Changes since v3 : * Update the format of printk message * Remove blank line Changes since v2 : * Update the commit message and some comments Changes since v1 : * Update the commit message * Add read_cpuid_mpidr in arch/arm/include/asm/cputype.h * Modify header of arch/arm/kernel/topology.c * Modify tests and manipulation of MPIDR's bitfields * Modify the place and dependancy of the config * Modify Noop functions Signed-off-by: Vincent Guittot Reviewed-by: Amit Kucheria Signed-off-by: Russell King --- arch/arm/Kconfig | 25 ++++++ arch/arm/include/asm/cputype.h | 6 ++ arch/arm/include/asm/topology.h | 33 +++++++ arch/arm/kernel/Makefile | 1 + arch/arm/kernel/smp.c | 5 ++ arch/arm/kernel/topology.c | 148 ++++++++++++++++++++++++++++++++ 6 files changed, 218 insertions(+) create mode 100644 arch/arm/kernel/topology.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3269576dbfa8..c208fd97c180 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1393,6 +1393,31 @@ config SMP_ON_UP If you don't know what to do here, say Y. +config ARM_CPU_TOPOLOGY + bool "Support cpu topology definition" + depends on SMP && CPU_V7 + default y + help + Support ARM cpu topology definition. The MPIDR register defines + affinity between processors which is then used to describe the cpu + topology of an ARM System. + +config SCHED_MC + bool "Multi-core scheduler support" + depends on ARM_CPU_TOPOLOGY + help + Multi-core scheduler support improves the CPU scheduler's decision + making when dealing with multi-core CPU chips at a cost of slightly + increased overhead in some places. If unsure say N here. + +config SCHED_SMT + bool "SMT scheduler support" + depends on ARM_CPU_TOPOLOGY + help + Improves the CPU scheduler's decision making when dealing with + MultiThreading at a cost of slightly increased overhead in some + places. If unsure say N here. + config HAVE_ARM_SCU bool help diff --git a/arch/arm/include/asm/cputype.h b/arch/arm/include/asm/cputype.h index cd4458f64171..cb47d28cbe1f 100644 --- a/arch/arm/include/asm/cputype.h +++ b/arch/arm/include/asm/cputype.h @@ -8,6 +8,7 @@ #define CPUID_CACHETYPE 1 #define CPUID_TCM 2 #define CPUID_TLBTYPE 3 +#define CPUID_MPIDR 5 #define CPUID_EXT_PFR0 "c1, 0" #define CPUID_EXT_PFR1 "c1, 1" @@ -70,6 +71,11 @@ static inline unsigned int __attribute_const__ read_cpuid_tcmstatus(void) return read_cpuid(CPUID_TCM); } +static inline unsigned int __attribute_const__ read_cpuid_mpidr(void) +{ + return read_cpuid(CPUID_MPIDR); +} + /* * Intel's XScale3 core supports some v6 features (supersections, L2) * but advertises itself as v5 as it does not support the v6 ISA. For diff --git a/arch/arm/include/asm/topology.h b/arch/arm/include/asm/topology.h index accbd7cad9b5..a7e457ed27c3 100644 --- a/arch/arm/include/asm/topology.h +++ b/arch/arm/include/asm/topology.h @@ -1,6 +1,39 @@ #ifndef _ASM_ARM_TOPOLOGY_H #define _ASM_ARM_TOPOLOGY_H +#ifdef CONFIG_ARM_CPU_TOPOLOGY + +#include + +struct cputopo_arm { + int thread_id; + int core_id; + int socket_id; + cpumask_t thread_sibling; + cpumask_t core_sibling; +}; + +extern struct cputopo_arm cpu_topology[NR_CPUS]; + +#define topology_physical_package_id(cpu) (cpu_topology[cpu].socket_id) +#define topology_core_id(cpu) (cpu_topology[cpu].core_id) +#define topology_core_cpumask(cpu) (&cpu_topology[cpu].core_sibling) +#define topology_thread_cpumask(cpu) (&cpu_topology[cpu].thread_sibling) + +#define mc_capable() (cpu_topology[0].socket_id != -1) +#define smt_capable() (cpu_topology[0].thread_id != -1) + +void init_cpu_topology(void); +void store_cpu_topology(unsigned int cpuid); +const struct cpumask *cpu_coregroup_mask(unsigned int cpu); + +#else + +static inline void init_cpu_topology(void) { } +static inline void store_cpu_topology(unsigned int cpuid) { } + +#endif + #include #endif /* _ASM_ARM_TOPOLOGY_H */ diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile index f7887dc53c1f..c687bceba7da 100644 --- a/arch/arm/kernel/Makefile +++ b/arch/arm/kernel/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_IWMMXT) += iwmmxt.o obj-$(CONFIG_CPU_HAS_PMU) += pmu.o obj-$(CONFIG_HW_PERF_EVENTS) += perf_event.o AFLAGS_iwmmxt.o := -Wa,-mcpu=iwmmxt +obj-$(CONFIG_ARM_CPU_TOPOLOGY) += topology.o ifneq ($(CONFIG_ARCH_EBSA110),y) obj-y += io.o diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index d88ff0230e82..62775c5c5ba0 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -268,6 +269,8 @@ static void __cpuinit smp_store_cpu_info(unsigned int cpuid) struct cpuinfo_arm *cpu_info = &per_cpu(cpu_data, cpuid); cpu_info->loops_per_jiffy = loops_per_jiffy; + + store_cpu_topology(cpuid); } /* @@ -358,6 +361,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus) { unsigned int ncores = num_possible_cpus(); + init_cpu_topology(); + smp_store_cpu_info(smp_processor_id()); /* diff --git a/arch/arm/kernel/topology.c b/arch/arm/kernel/topology.c new file mode 100644 index 000000000000..1040c00405d0 --- /dev/null +++ b/arch/arm/kernel/topology.c @@ -0,0 +1,148 @@ +/* + * arch/arm/kernel/topology.c + * + * Copyright (C) 2011 Linaro Limited. + * Written by: Vincent Guittot + * + * based on arch/sh/kernel/topology.c + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MPIDR_SMP_BITMASK (0x3 << 30) +#define MPIDR_SMP_VALUE (0x2 << 30) + +#define MPIDR_MT_BITMASK (0x1 << 24) + +/* + * These masks reflect the current use of the affinity levels. + * The affinity level can be up to 16 bits according to ARM ARM + */ + +#define MPIDR_LEVEL0_MASK 0x3 +#define MPIDR_LEVEL0_SHIFT 0 + +#define MPIDR_LEVEL1_MASK 0xF +#define MPIDR_LEVEL1_SHIFT 8 + +#define MPIDR_LEVEL2_MASK 0xFF +#define MPIDR_LEVEL2_SHIFT 16 + +struct cputopo_arm cpu_topology[NR_CPUS]; + +const struct cpumask *cpu_coregroup_mask(unsigned int cpu) +{ + return &cpu_topology[cpu].core_sibling; +} + +/* + * store_cpu_topology is called at boot when only one cpu is running + * and with the mutex cpu_hotplug.lock locked, when several cpus have booted, + * which prevents simultaneous write access to cpu_topology array + */ +void store_cpu_topology(unsigned int cpuid) +{ + struct cputopo_arm *cpuid_topo = &cpu_topology[cpuid]; + unsigned int mpidr; + unsigned int cpu; + + /* If the cpu topology has been already set, just return */ + if (cpuid_topo->core_id != -1) + return; + + mpidr = read_cpuid_mpidr(); + + /* create cpu topology mapping */ + if ((mpidr & MPIDR_SMP_BITMASK) == MPIDR_SMP_VALUE) { + /* + * This is a multiprocessor system + * multiprocessor format & multiprocessor mode field are set + */ + + if (mpidr & MPIDR_MT_BITMASK) { + /* core performance interdependency */ + cpuid_topo->thread_id = (mpidr >> MPIDR_LEVEL0_SHIFT) + & MPIDR_LEVEL0_MASK; + cpuid_topo->core_id = (mpidr >> MPIDR_LEVEL1_SHIFT) + & MPIDR_LEVEL1_MASK; + cpuid_topo->socket_id = (mpidr >> MPIDR_LEVEL2_SHIFT) + & MPIDR_LEVEL2_MASK; + } else { + /* largely independent cores */ + cpuid_topo->thread_id = -1; + cpuid_topo->core_id = (mpidr >> MPIDR_LEVEL0_SHIFT) + & MPIDR_LEVEL0_MASK; + cpuid_topo->socket_id = (mpidr >> MPIDR_LEVEL1_SHIFT) + & MPIDR_LEVEL1_MASK; + } + } else { + /* + * This is an uniprocessor system + * we are in multiprocessor format but uniprocessor system + * or in the old uniprocessor format + */ + cpuid_topo->thread_id = -1; + cpuid_topo->core_id = 0; + cpuid_topo->socket_id = -1; + } + + /* update core and thread sibling masks */ + for_each_possible_cpu(cpu) { + struct cputopo_arm *cpu_topo = &cpu_topology[cpu]; + + if (cpuid_topo->socket_id == cpu_topo->socket_id) { + cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); + if (cpu != cpuid) + cpumask_set_cpu(cpu, + &cpuid_topo->core_sibling); + + if (cpuid_topo->core_id == cpu_topo->core_id) { + cpumask_set_cpu(cpuid, + &cpu_topo->thread_sibling); + if (cpu != cpuid) + cpumask_set_cpu(cpu, + &cpuid_topo->thread_sibling); + } + } + } + smp_wmb(); + + printk(KERN_INFO "CPU%u: thread %d, cpu %d, socket %d, mpidr %x\n", + cpuid, cpu_topology[cpuid].thread_id, + cpu_topology[cpuid].core_id, + cpu_topology[cpuid].socket_id, mpidr); +} + +/* + * init_cpu_topology is called at boot when only one cpu is running + * which prevent simultaneous write access to cpu_topology array + */ +void init_cpu_topology(void) +{ + unsigned int cpu; + + /* init core mask */ + for_each_possible_cpu(cpu) { + struct cputopo_arm *cpu_topo = &(cpu_topology[cpu]); + + cpu_topo->thread_id = -1; + cpu_topo->core_id = -1; + cpu_topo->socket_id = -1; + cpumask_clear(&cpu_topo->core_sibling); + cpumask_clear(&cpu_topo->thread_sibling); + } + smp_wmb(); +} From d6257288c4052465feeff7e283e35ec0ed06ca03 Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 23 Aug 2011 22:19:29 +0100 Subject: [PATCH 07/15] ARM: 7060/1: smp: populate logical CPU mapping during boot To allow booting Linux on a CPU with physical ID != 0, we need to provide a mapping from the logical CPU number to the physical CPU number. This patch adds such a mapping and populates it during boot. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/include/asm/smp.h | 6 ++++++ arch/arm/kernel/smp.c | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index e42d96a45d3e..674ebcd337fe 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -65,6 +65,12 @@ extern void platform_secondary_init(unsigned int cpu); */ extern void platform_smp_prepare_cpus(unsigned int); +/* + * Logical CPU mapping. + */ +extern int __cpu_logical_map[NR_CPUS]; +#define cpu_logical_map(cpu) __cpu_logical_map[cpu] + /* * Initial data for bringing up a secondary CPU. */ diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 62775c5c5ba0..3f12ce9b0796 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -40,6 +40,7 @@ #include #include #include +#include /* * as from 2.5, kernels no longer have an init_tasks structure @@ -260,6 +261,20 @@ void __ref cpu_die(void) } #endif /* CONFIG_HOTPLUG_CPU */ +int __cpu_logical_map[NR_CPUS]; + +void __init smp_setup_processor_id(void) +{ + int i; + u32 cpu = is_smp() ? read_cpuid_mpidr() & 0xff : 0; + + cpu_logical_map(0) = cpu; + for (i = 1; i < NR_CPUS; ++i) + cpu_logical_map(i) = i == cpu ? 0 : i; + + printk(KERN_INFO "Booting Linux on physical CPU %d\n", cpu); +} + /* * Called by both boot and secondaries to move global data into * per-processor storage. From 267840f3397fd9f6a2bdde14de38b9d29d525d7b Mon Sep 17 00:00:00 2001 From: Will Deacon Date: Tue, 23 Aug 2011 22:20:03 +0100 Subject: [PATCH 08/15] ARM: 7061/1: gic: convert logical CPU numbers into physical numbers The GIC driver must convert logical CPU numbers passed in from Linux into physical CPU numbers that are understood by the hardware. This patch uses the new cpu_logical_map macro for performing the conversion inside the GIC driver. Signed-off-by: Will Deacon Signed-off-by: Russell King --- arch/arm/common/gic.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 3227ca952a12..666b278e56d7 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -180,7 +180,7 @@ static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val, return -EINVAL; mask = 0xff << shift; - bit = 1 << (cpu + shift); + bit = 1 << (cpu_logical_map(cpu) + shift); spin_lock(&irq_controller_lock); val = readl_relaxed(reg) & ~mask; @@ -259,9 +259,15 @@ static void __init gic_dist_init(struct gic_chip_data *gic, unsigned int irq_start) { unsigned int gic_irqs, irq_limit, i; + u32 cpumask; void __iomem *base = gic->dist_base; - u32 cpumask = 1 << smp_processor_id(); + u32 cpu = 0; +#ifdef CONFIG_SMP + cpu = cpu_logical_map(smp_processor_id()); +#endif + + cpumask = 1 << cpu; cpumask |= cpumask << 8; cpumask |= cpumask << 16; @@ -382,7 +388,12 @@ void __cpuinit gic_enable_ppi(unsigned int irq) #ifdef CONFIG_SMP void gic_raise_softirq(const struct cpumask *mask, unsigned int irq) { - unsigned long map = *cpus_addr(*mask); + int cpu; + unsigned long map = 0; + + /* Convert our logical CPU mask into a physical one. */ + for_each_cpu(cpu, mask) + map |= 1 << cpu_logical_map(cpu); /* * Ensure that stores to Normal memory are visible to the From 26a527e69d6e6077bff9e2cddcb08337ac33a52d Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Sun, 25 Sep 2011 08:25:43 +0100 Subject: [PATCH 09/15] ARM: 7100/1: smp_scu: remove __init annotation from scu_enable() When Cortex-A9 MPCore resumes from Dormant or Shutdown modes, SCU needs to be re-enabled. This patch removes __init annotation from function scu_enable(), so that platform resume procedure can call it to re-enable SCU. Signed-off-by: Shawn Guo Signed-off-by: Russell King --- arch/arm/kernel/smp_scu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/kernel/smp_scu.c b/arch/arm/kernel/smp_scu.c index 79ed5e7f204a..5b6d536cbfe3 100644 --- a/arch/arm/kernel/smp_scu.c +++ b/arch/arm/kernel/smp_scu.c @@ -33,7 +33,7 @@ unsigned int __init scu_get_core_count(void __iomem *scu_base) /* * Enable the SCU */ -void __init scu_enable(void __iomem *scu_base) +void scu_enable(void __iomem *scu_base) { u32 scu_ctrl; From 0b5a1b95dcdfa451125132d5ce3f79a27ffb0950 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 6 Oct 2011 15:18:14 +0100 Subject: [PATCH 10/15] ARM: 7123/1: smp: Add an IPI handler callable from C code In order to be able to handle IPI directly from C code instead of assembly code, introduce handle_IPI(), which is modeled after handle_IRQ(). Signed-off-by: Marc Zyngier Signed-off-by: Russell King --- arch/arm/include/asm/smp.h | 5 +++++ arch/arm/kernel/smp.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 674ebcd337fe..0a17b62538c2 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -32,6 +32,11 @@ extern void show_ipi_list(struct seq_file *, int); */ asmlinkage void do_IPI(int ipinr, struct pt_regs *regs); +/* + * Called from C code, this handles an IPI. + */ +void handle_IPI(int ipinr, struct pt_regs *regs); + /* * Setup the set of possible CPUs (via set_cpu_possible) */ diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 3f12ce9b0796..2e49f1883fe9 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -586,6 +586,11 @@ static void ipi_cpu_stop(unsigned int cpu) * Main handler for inter-processor interrupts */ asmlinkage void __exception_irq_entry do_IPI(int ipinr, struct pt_regs *regs) +{ + handle_IPI(ipinr, regs); +} + +void handle_IPI(int ipinr, struct pt_regs *regs) { unsigned int cpu = smp_processor_id(); struct pt_regs *old_regs = set_irq_regs(regs); From 0af8aa0069e43f90d59666510342c05e97d8c4b8 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 6 Oct 2011 15:19:14 +0100 Subject: [PATCH 11/15] ARM: 7124/1: smp: Add a localtimer handler callable from C code In order to be able to handle localtimer directly from C code instead of assembly code, introduce handle_local_timer(), which is modeled after handle_IRQ(). Signed-off-by: Shawn Guo Signed-off-by: Russell King --- arch/arm/include/asm/localtimer.h | 4 ++++ arch/arm/kernel/smp.c | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h index 080d74f8128d..3306f281333c 100644 --- a/arch/arm/include/asm/localtimer.h +++ b/arch/arm/include/asm/localtimer.h @@ -22,6 +22,10 @@ void percpu_timer_setup(void); */ asmlinkage void do_local_timer(struct pt_regs *); +/* + * Called from C code + */ +void handle_local_timer(struct pt_regs *); #ifdef CONFIG_LOCAL_TIMERS diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 2e49f1883fe9..0949007d09a8 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -479,6 +479,11 @@ static void ipi_timer(void) #ifdef CONFIG_LOCAL_TIMERS asmlinkage void __exception_irq_entry do_local_timer(struct pt_regs *regs) +{ + handle_local_timer(regs); +} + +void handle_local_timer(struct pt_regs *regs) { struct pt_regs *old_regs = set_irq_regs(regs); int cpu = smp_processor_id(); From 5a567d78c437e3be1c512734cdfe64b4ae6b82d7 Mon Sep 17 00:00:00 2001 From: Jamie Iles Date: Sat, 8 Oct 2011 11:20:42 +0100 Subject: [PATCH 12/15] ARM: 7115/4: move __exception and friends to asm/exception.h The definition of __exception_irq_entry for CONFIG_FUNCTION_GRAPH_TRACER=y needs linux/ftrace.h, but this creates a circular dependency with it's current home in asm/system.h. Create asm/exception.h and update all current users. v4: - rebase to rmk/for-next v3: - remove redundant includes of linux/ftrace.h v2: - document the usage restricitions of __exception* Cc: Zoltan Devai Signed-off-by: Jamie Iles Signed-off-by: Russell King --- arch/arm/include/asm/exception.h | 19 +++++++++++++++++++ arch/arm/include/asm/system.h | 7 ------- arch/arm/kernel/irq.c | 2 +- arch/arm/kernel/smp.c | 2 +- arch/arm/kernel/traps.c | 1 + arch/arm/mach-pxa/irq.c | 2 ++ arch/arm/mm/fault.c | 1 + 7 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 arch/arm/include/asm/exception.h diff --git a/arch/arm/include/asm/exception.h b/arch/arm/include/asm/exception.h new file mode 100644 index 000000000000..5abaf5bbd985 --- /dev/null +++ b/arch/arm/include/asm/exception.h @@ -0,0 +1,19 @@ +/* + * Annotations for marking C functions as exception handlers. + * + * These should only be used for C functions that are called from the low + * level exception entry code and not any intervening C code. + */ +#ifndef __ASM_ARM_EXCEPTION_H +#define __ASM_ARM_EXCEPTION_H + +#include + +#define __exception __attribute__((section(".exception.text"))) +#ifdef CONFIG_FUNCTION_GRAPH_TRACER +#define __exception_irq_entry __irq_entry +#else +#define __exception_irq_entry __exception +#endif + +#endif /* __ASM_ARM_EXCEPTION_H */ diff --git a/arch/arm/include/asm/system.h b/arch/arm/include/asm/system.h index 832888d0c20c..ed6b0499a106 100644 --- a/arch/arm/include/asm/system.h +++ b/arch/arm/include/asm/system.h @@ -62,13 +62,6 @@ #include -#define __exception __attribute__((section(".exception.text"))) -#ifdef CONFIG_FUNCTION_GRAPH_TRACER -#define __exception_irq_entry __irq_entry -#else -#define __exception_irq_entry __exception -#endif - struct thread_info; struct task_struct; diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index de3dcab8610b..53919b230e8b 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -35,8 +35,8 @@ #include #include #include -#include +#include #include #include #include diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 0949007d09a8..35417d0fb8ab 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -31,6 +30,7 @@ #include #include #include +#include #include #include #include diff --git a/arch/arm/kernel/traps.c b/arch/arm/kernel/traps.c index bc9f9da782cb..210382555af1 100644 --- a/arch/arm/kernel/traps.c +++ b/arch/arm/kernel/traps.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include diff --git a/arch/arm/mach-pxa/irq.c b/arch/arm/mach-pxa/irq.c index b09e848eb6c6..ca6075717824 100644 --- a/arch/arm/mach-pxa/irq.c +++ b/arch/arm/mach-pxa/irq.c @@ -19,6 +19,8 @@ #include #include +#include + #include #include #include diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c index 3b5ea68acbb8..aa33949fef60 100644 --- a/arch/arm/mm/fault.c +++ b/arch/arm/mm/fault.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include From a06f916b7a9b57447ceb875eb0a89f1a66b31bca Mon Sep 17 00:00:00 2001 From: Russell King Date: Thu, 20 Oct 2011 22:04:18 +0100 Subject: [PATCH 13/15] ARM: smp: fix clipping of number of CPUs Rather than clipping the number of CPUs using the compile-time NR_CPUS constant, use the runtime nr_cpu_ids value instead. This allows the nr_cpus command line option to work as expected. Cc: Reported-by: Mark Salter Signed-off-by: Russell King --- arch/arm/mach-exynos4/platsmp.c | 10 ++++------ arch/arm/mach-msm/platsmp.c | 6 ++++++ arch/arm/mach-omap2/omap-smp.c | 10 ++++------ arch/arm/mach-realview/platsmp.c | 10 ++++------ arch/arm/mach-shmobile/platsmp.c | 6 ++++++ arch/arm/mach-tegra/platsmp.c | 8 ++++---- arch/arm/mach-ux500/platsmp.c | 10 ++++------ arch/arm/mach-vexpress/ct-ca9x4.c | 6 ++++++ 8 files changed, 38 insertions(+), 28 deletions(-) diff --git a/arch/arm/mach-exynos4/platsmp.c b/arch/arm/mach-exynos4/platsmp.c index 7c2282c6ba81..a08c536923f9 100644 --- a/arch/arm/mach-exynos4/platsmp.c +++ b/arch/arm/mach-exynos4/platsmp.c @@ -191,12 +191,10 @@ void __init smp_init_cpus(void) ncores = scu_base ? scu_get_core_count(scu_base) : 1; /* sanity check */ - if (ncores > NR_CPUS) { - printk(KERN_WARNING - "EXYNOS4: no. of cores (%d) greater than configured " - "maximum of %d - clipping\n", - ncores, NR_CPUS); - ncores = NR_CPUS; + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; } for (i = 0; i < ncores; i++) diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c index 1a1af9e56250..727659520912 100644 --- a/arch/arm/mach-msm/platsmp.c +++ b/arch/arm/mach-msm/platsmp.c @@ -156,6 +156,12 @@ void __init smp_init_cpus(void) { unsigned int i, ncores = get_core_count(); + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + for (i = 0; i < ncores; i++) set_cpu_possible(i, true); diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c index ce65e9329c7b..889464dc7b2d 100644 --- a/arch/arm/mach-omap2/omap-smp.c +++ b/arch/arm/mach-omap2/omap-smp.c @@ -109,12 +109,10 @@ void __init smp_init_cpus(void) ncores = scu_get_core_count(scu_base); /* sanity check */ - if (ncores > NR_CPUS) { - printk(KERN_WARNING - "OMAP4: no. of cores (%d) greater than configured " - "maximum of %d - clipping\n", - ncores, NR_CPUS); - ncores = NR_CPUS; + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; } for (i = 0; i < ncores; i++) diff --git a/arch/arm/mach-realview/platsmp.c b/arch/arm/mach-realview/platsmp.c index 4ae943bafa92..e83c654a58d0 100644 --- a/arch/arm/mach-realview/platsmp.c +++ b/arch/arm/mach-realview/platsmp.c @@ -52,12 +52,10 @@ void __init smp_init_cpus(void) ncores = scu_base ? scu_get_core_count(scu_base) : 1; /* sanity check */ - if (ncores > NR_CPUS) { - printk(KERN_WARNING - "Realview: no. of cores (%d) greater than configured " - "maximum of %d - clipping\n", - ncores, NR_CPUS); - ncores = NR_CPUS; + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; } for (i = 0; i < ncores; i++) diff --git a/arch/arm/mach-shmobile/platsmp.c b/arch/arm/mach-shmobile/platsmp.c index 66f980625a33..e4e485fa2532 100644 --- a/arch/arm/mach-shmobile/platsmp.c +++ b/arch/arm/mach-shmobile/platsmp.c @@ -56,6 +56,12 @@ void __init smp_init_cpus(void) unsigned int ncores = shmobile_smp_get_core_count(); unsigned int i; + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + for (i = 0; i < ncores; i++) set_cpu_possible(i, true); diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 0886cbccddee..7d2b5d03c1df 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -114,10 +114,10 @@ void __init smp_init_cpus(void) { unsigned int i, ncores = scu_get_core_count(scu_base); - if (ncores > NR_CPUS) { - printk(KERN_ERR "Tegra: no. of cores (%u) greater than configured (%u), clipping\n", - ncores, NR_CPUS); - ncores = NR_CPUS; + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; } for (i = 0; i < ncores; i++) diff --git a/arch/arm/mach-ux500/platsmp.c b/arch/arm/mach-ux500/platsmp.c index a33df5f4c27a..eb5199102cfa 100644 --- a/arch/arm/mach-ux500/platsmp.c +++ b/arch/arm/mach-ux500/platsmp.c @@ -156,12 +156,10 @@ void __init smp_init_cpus(void) ncores = scu_base ? scu_get_core_count(scu_base) : 1; /* sanity check */ - if (ncores > NR_CPUS) { - printk(KERN_WARNING - "U8500: no. of cores (%d) greater than configured " - "maximum of %d - clipping\n", - ncores, NR_CPUS); - ncores = NR_CPUS; + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; } for (i = 0; i < ncores; i++) diff --git a/arch/arm/mach-vexpress/ct-ca9x4.c b/arch/arm/mach-vexpress/ct-ca9x4.c index bfd32f52c2db..2b1e836a76ed 100644 --- a/arch/arm/mach-vexpress/ct-ca9x4.c +++ b/arch/arm/mach-vexpress/ct-ca9x4.c @@ -221,6 +221,12 @@ static void ct_ca9x4_init_cpu_map(void) { int i, ncores = scu_get_core_count(MMIO_P2V(A9_MPCORE_SCU)); + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + for (i = 0; i < ncores; ++i) set_cpu_possible(i, true); From 292b293ceef2eda1f96f0c90b96e954d7bdabd1c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Wed, 20 Jul 2011 16:24:14 +0100 Subject: [PATCH 14/15] ARM: gic: consolidate PPI handling 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_irq flow. This also allows the removal of some duplicated code. Cc: Kukjin Kim Cc: David Brown Cc: Bryan Huntsman Cc: Tony Lindgren Cc: Paul Mundt Cc: Magnus Damm Cc: Thomas Gleixner Acked-by: David Brown Tested-by: David Brown Tested-by: Shawn Guo Signed-off-by: Marc Zyngier --- arch/arm/common/gic.c | 75 ++++++++++++++++++- arch/arm/include/asm/entry-macro-multi.S | 7 -- arch/arm/include/asm/hardirq.h | 3 - .../include/asm/hardware/entry-macro-gic.S | 19 +---- arch/arm/include/asm/localtimer.h | 11 +-- arch/arm/include/asm/smp.h | 5 -- arch/arm/kernel/irq.c | 3 - arch/arm/kernel/smp.c | 32 ++------ .../mach-exynos4/include/mach/entry-macro.S | 7 +- arch/arm/mach-msm/board-msm8x60.c | 11 --- .../mach-msm/include/mach/entry-macro-qgic.S | 73 +----------------- .../arm/mach-omap2/include/mach/entry-macro.S | 14 +--- arch/arm/mach-shmobile/entry-intc.S | 3 - .../mach-shmobile/include/mach/entry-macro.S | 3 - 14 files changed, 88 insertions(+), 178 deletions(-) diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index 666b278e56d7..bbea0168779b 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -28,10 +28,14 @@ #include #include #include +#include +#include +#include #include #include #include +#include static DEFINE_SPINLOCK(irq_controller_lock); @@ -255,6 +259,32 @@ 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) { @@ -262,6 +292,7 @@ static void __init gic_dist_init(struct gic_chip_data *gic, u32 cpumask; void __iomem *base = gic->dist_base; u32 cpu = 0; + u32 nrppis = 0, ppi_base = 0; #ifdef CONFIG_SMP cpu = cpu_logical_map(smp_processor_id()); @@ -282,6 +313,33 @@ static void __init gic_dist_init(struct gic_chip_data *gic, if (gic_irqs > 1020) gic_irqs = 1020; + /* + * Nobody would be insane enough to use PPIs on a secondary + * GIC, right? + */ + if (gic == &gic_data[0]) { + nrppis = (32 - irq_start) & 31; + + /* The GIC only supports up to 16 PPIs. */ + if (nrppis > 16) + BUG(); + + 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", + gic_irqs, (gic == &gic_data[0]) ? nrppis : 0); + /* * Set all global interrupts to be level triggered, active low. */ @@ -317,7 +375,22 @@ static void __init gic_dist_init(struct gic_chip_data *gic, /* * Setup the Linux IRQ subsystem. */ - for (i = irq_start; i < irq_limit; i++) { + 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++) { irq_set_chip_and_handler(i, &gic_chip, handle_fasteoi_irq); irq_set_chip_data(i, gic); set_irq_flags(i, IRQF_VALID | IRQF_PROBE); diff --git a/arch/arm/include/asm/entry-macro-multi.S b/arch/arm/include/asm/entry-macro-multi.S index 2f1e2098dfe7..88d61815f0c0 100644 --- a/arch/arm/include/asm/entry-macro-multi.S +++ b/arch/arm/include/asm/entry-macro-multi.S @@ -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 diff --git a/arch/arm/include/asm/hardirq.h b/arch/arm/include/asm/hardirq.h index 89ad1805e579..ddf07a92a6c8 100644 --- a/arch/arm/include/asm/hardirq.h +++ b/arch/arm/include/asm/hardirq.h @@ -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 diff --git a/arch/arm/include/asm/hardware/entry-macro-gic.S b/arch/arm/include/asm/hardware/entry-macro-gic.S index c115b82fe80a..74ebc803904d 100644 --- a/arch/arm/include/asm/hardware/entry-macro-gic.S +++ b/arch/arm/include/asm/hardware/entry-macro-gic.S @@ -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 diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h index 3306f281333c..5c8acb4c4040 100644 --- a/arch/arm/include/asm/localtimer.h +++ b/arch/arm/include/asm/localtimer.h @@ -10,6 +10,8 @@ #ifndef __ASM_ARM_LOCALTIMER_H #define __ASM_ARM_LOCALTIMER_H +#include + struct clock_event_device; /* @@ -18,14 +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 *); - -/* - * Called from C code - */ -void handle_local_timer(struct pt_regs *); +irqreturn_t percpu_timer_handler(int irq, void *dev_id); #ifdef CONFIG_LOCAL_TIMERS diff --git a/arch/arm/include/asm/smp.h b/arch/arm/include/asm/smp.h index 0a17b62538c2..1e5717afc4ac 100644 --- a/arch/arm/include/asm/smp.h +++ b/arch/arm/include/asm/smp.h @@ -99,9 +99,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 */ diff --git a/arch/arm/kernel/irq.c b/arch/arm/kernel/irq.c index 53919b230e8b..7cb29261249a 100644 --- a/arch/arm/kernel/irq.c +++ b/arch/arm/kernel/irq.c @@ -58,9 +58,6 @@ int arch_show_interrupts(struct seq_file *p, int prec) #endif #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; diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 35417d0fb8ab..917ed2fa4e4c 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -457,10 +457,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; } @@ -478,34 +474,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) { - handle_local_timer(regs); -} - -void handle_local_timer(struct pt_regs *regs) -{ - 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 diff --git a/arch/arm/mach-exynos4/include/mach/entry-macro.S b/arch/arm/mach-exynos4/include/mach/entry-macro.S index d7a1e281ce7a..006a4f4c65c6 100644 --- a/arch/arm/mach-exynos4/include/mach/entry-macro.S +++ b/arch/arm/mach-exynos4/include/mach/entry-macro.S @@ -55,7 +55,7 @@ bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #29 + cmp \irqnr, #15 cmpcc \irqnr, \irqnr cmpne \irqnr, \tmp cmpcs \irqnr, \irqnr @@ -76,8 +76,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 - .endm diff --git a/arch/arm/mach-msm/board-msm8x60.c b/arch/arm/mach-msm/board-msm8x60.c index 1163b6fd05d2..d70a2f643613 100644 --- a/arch/arm/mach-msm/board-msm8x60.c +++ b/arch/arm/mach-msm/board-msm8x60.c @@ -36,8 +36,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); @@ -49,15 +47,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) diff --git a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S index 12467157afb9..717076f3ca73 100644 --- a/arch/arm/mach-msm/include/mach/entry-macro-qgic.S +++ b/arch/arm/mach-msm/include/mach/entry-macro-qgic.S @@ -8,81 +8,10 @@ * warranty of any kind, whether express or implied. */ -#include -#include +#include .macro disable_fiq .endm - .macro get_irqnr_preamble, base, tmp - ldr \base, =gic_cpu_base_addr - ldr \base, [\base] - .endm - .macro arch_ret_to_user, tmp1, tmp2 .endm - - /* - * The interrupt numbering scheme is defined in the - * interrupt controller spec. To wit: - * - * Migrated the code from ARM MP port to be more consistent - * with interrupt processing , the following still holds true - * however, all interrupts are treated the same regardless of - * if they are local IPI or PPI - * - * Interrupts 0-15 are IPI - * 16-31 are PPI - * (16-18 are the timers) - * 32-1020 are global - * 1021-1022 are reserved - * 1023 is "spurious" (no interrupt) - * - * 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 (0-1020 inclusive). - * - * Base ARM code assumes that the local (private) peripheral interrupts - * are not valid, we treat them differently, in that the privates are - * handled like normal shared interrupts with the exception that only - * one processor can register the interrupt and the handler must be - * the same for all processors. - */ - - .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - - ldr \irqstat, [\base, #GIC_CPU_INTACK] /* bits 12-10 =srcCPU, - 9-0 =int # */ - - bic \irqnr, \irqstat, #0x1c00 @mask src - cmp \irqnr, #15 - ldr \tmp, =1021 - cmpcc \irqnr, \irqnr - cmpne \irqnr, \tmp - cmpcs \irqnr, \irqnr - - .endm - - /* We assume that irqstat (the raw value of the IRQ acknowledge - * register) is preserved from the macro above. - * If there is an IPI, we immediately signal end of interrupt on the - * controller, since this requires the original irqstat value which - * we won't easily be able to recreate later. - */ - .macro test_for_ipi, irqnr, irqstat, base, tmp - bic \irqnr, \irqstat, #0x1c00 - cmp \irqnr, #16 - 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, #16 - moveq \tmp, #1 - streq \irqstat, [\base, #GIC_CPU_EOI] - cmp \tmp, #0 - .endm diff --git a/arch/arm/mach-omap2/include/mach/entry-macro.S b/arch/arm/mach-omap2/include/mach/entry-macro.S index ceb8b7e593d7..feb90a10945a 100644 --- a/arch/arm/mach-omap2/include/mach/entry-macro.S +++ b/arch/arm/mach-omap2/include/mach/entry-macro.S @@ -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 */ diff --git a/arch/arm/mach-shmobile/entry-intc.S b/arch/arm/mach-shmobile/entry-intc.S index cac0a7ae2084..1a1c00ca39a2 100644 --- a/arch/arm/mach-shmobile/entry-intc.S +++ b/arch/arm/mach-shmobile/entry-intc.S @@ -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 diff --git a/arch/arm/mach-shmobile/include/mach/entry-macro.S b/arch/arm/mach-shmobile/include/mach/entry-macro.S index d791f10eeac7..8d4a416d4285 100644 --- a/arch/arm/mach-shmobile/include/mach/entry-macro.S +++ b/arch/arm/mach-shmobile/include/mach/entry-macro.S @@ -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 From 28af690a284dfcb627bd69d0963db1c0f412cb8c Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 22 Jul 2011 12:52:37 +0100 Subject: [PATCH 15/15] ARM: gic, local timers: use the request_percpu_irq() interface This patch remove the hardcoded link between local timers and PPIs, and convert the PPI users (TWD, MCT 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. Additional testing by David Brown (msm8250 and msm8660) and Shawn Guo (imx6q). Cc: David Brown Cc: Thomas Gleixner Acked-by: David Brown Tested-by: David Brown Tested-by: Shawn Guo Signed-off-by: Marc Zyngier --- arch/arm/common/gic.c | 52 ---------------------- arch/arm/include/asm/hardware/gic.h | 1 - arch/arm/include/asm/localtimer.h | 16 +++---- 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 | 7 ++- arch/arm/mach-msm/timer.c | 69 +++++++++++++++++------------ 8 files changed, 99 insertions(+), 111 deletions(-) diff --git a/arch/arm/common/gic.c b/arch/arm/common/gic.c index bbea0168779b..a2b320503931 100644 --- a/arch/arm/common/gic.c +++ b/arch/arm/common/gic.c @@ -35,7 +35,6 @@ #include #include #include -#include static DEFINE_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) { @@ -325,16 +298,6 @@ static void __init gic_dist_init(struct gic_chip_data *gic, BUG(); 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", @@ -377,17 +340,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++) { @@ -448,16 +406,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) { diff --git a/arch/arm/include/asm/hardware/gic.h b/arch/arm/include/asm/hardware/gic.h index 435d3f86c708..2dadd50a77d2 100644 --- a/arch/arm/include/asm/hardware/gic.h +++ b/arch/arm/include/asm/hardware/gic.h @@ -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; diff --git a/arch/arm/include/asm/localtimer.h b/arch/arm/include/asm/localtimer.h index 5c8acb4c4040..f5e1cec7e35c 100644 --- a/arch/arm/include/asm/localtimer.h +++ b/arch/arm/include/asm/localtimer.h @@ -19,26 +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 @@ -53,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 diff --git a/arch/arm/include/asm/smp_twd.h b/arch/arm/include/asm/smp_twd.h index fed9981fba08..ef9ffba97ad8 100644 --- a/arch/arm/include/asm/smp_twd.h +++ b/arch/arm/include/asm/smp_twd.h @@ -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 diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c index 917ed2fa4e4c..a96c08cd6125 100644 --- a/arch/arm/kernel/smp.c +++ b/arch/arm/kernel/smp.c @@ -473,20 +473,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) { @@ -537,7 +523,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 diff --git a/arch/arm/kernel/smp_twd.c b/arch/arm/kernel/smp_twd.c index 01c186222f3b..a8a6682d6b52 100644 --- a/arch/arm/kernel/smp_twd.c +++ b/arch/arm/kernel/smp_twd.c @@ -19,6 +19,7 @@ #include #include +#include #include /* set up by the platform code */ @@ -26,6 +27,8 @@ void __iomem *twd_base; static unsigned long twd_timer_rate; +static struct clock_event_device __percpu **twd_evt; + static void twd_set_mode(enum clock_event_mode mode, struct clock_event_device *clk) { @@ -80,6 +83,12 @@ int twd_timer_ack(void) return 0; } +void twd_timer_stop(struct clock_event_device *clk) +{ + twd_set_mode(CLOCK_EVT_MODE_UNUSED, clk); + disable_percpu_irq(clk->irq); +} + static void __cpuinit twd_calibrate_rate(void) { unsigned long count; @@ -119,11 +128,43 @@ static void __cpuinit twd_calibrate_rate(void) } } +static irqreturn_t twd_handler(int irq, void *dev_id) +{ + struct clock_event_device *evt = *(struct clock_event_device **)dev_id; + + if (twd_timer_ack()) { + evt->event_handler(evt); + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + /* * Setup the local clock events for a CPU. */ void __cpuinit twd_timer_setup(struct clock_event_device *clk) { + struct clock_event_device **this_cpu_clk; + + if (!twd_evt) { + int err; + + twd_evt = alloc_percpu(struct clock_event_device *); + if (!twd_evt) { + pr_err("twd: can't allocate memory\n"); + return; + } + + err = request_percpu_irq(clk->irq, twd_handler, + "twd", twd_evt); + if (err) { + pr_err("twd: can't register interrupt %d (%d)\n", + clk->irq, err); + return; + } + } + twd_calibrate_rate(); clk->name = "local_timer"; @@ -137,8 +178,10 @@ void __cpuinit twd_timer_setup(struct clock_event_device *clk) clk->max_delta_ns = clockevent_delta2ns(0xffffffff, clk); clk->min_delta_ns = clockevent_delta2ns(0xf, clk); + this_cpu_clk = __this_cpu_ptr(twd_evt); + *this_cpu_clk = clk; + clockevents_register_device(clk); - /* Make sure our local interrupt controller has this enabled */ - gic_enable_ppi(clk->irq); + enable_percpu_irq(clk->irq, 0); } diff --git a/arch/arm/mach-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c index 1ae059b7ad7b..85a1bb79f11c 100644 --- a/arch/arm/mach-exynos4/mct.c +++ b/arch/arm/mach-exynos4/mct.c @@ -380,9 +380,11 @@ static void exynos4_mct_tick_init(struct clock_event_device *evt) if (cpu == 0) { mct_tick0_event_irq.dev_id = &mct_tick[cpu]; + evt->irq = IRQ_MCT_L0; setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq); } else { mct_tick1_event_irq.dev_id = &mct_tick[cpu]; + evt->irq = IRQ_MCT_L1; setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq); irq_set_affinity(IRQ_MCT_L1, cpumask_of(1)); } @@ -394,9 +396,10 @@ void __cpuinit local_timer_setup(struct clock_event_device *evt) exynos4_mct_tick_init(evt); } -int local_timer_ack(void) +void local_timer_stop(struct clock_event_device *evt) { - return 0; + evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); + disable_irq(evt->irq); } #endif /* CONFIG_LOCAL_TIMERS */ diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c index 63621f152c98..afeeca52fc66 100644 --- a/arch/arm/mach-msm/timer.c +++ b/arch/arm/mach-msm/timer.c @@ -71,12 +71,16 @@ enum timer_location { struct msm_clock { struct clock_event_device clockevent; struct clocksource clocksource; - struct irqaction irq; + unsigned int irq; void __iomem *regbase; uint32_t freq; uint32_t shift; void __iomem *global_counter; void __iomem *local_counter; + union { + struct clock_event_device *evt; + struct clock_event_device __percpu **percpu_evt; + }; }; enum { @@ -87,13 +91,10 @@ enum { static struct msm_clock msm_clocks[]; -static struct clock_event_device *local_clock_event; static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) { - struct clock_event_device *evt = dev_id; - if (smp_processor_id() != 0) - evt = local_clock_event; + struct clock_event_device *evt = *(struct clock_event_device **)dev_id; if (evt->event_handler == NULL) return IRQ_HANDLED; evt->event_handler(evt); @@ -171,13 +172,7 @@ static struct msm_clock msm_clocks[] = { .mask = CLOCKSOURCE_MASK(32), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }, - .irq = { - .name = "gp_timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, - .handler = msm_timer_interrupt, - .dev_id = &msm_clocks[0].clockevent, - .irq = INT_GP_TIMER_EXP - }, + .irq = INT_GP_TIMER_EXP, .freq = GPT_HZ, }, [MSM_CLOCK_DGT] = { @@ -196,13 +191,7 @@ static struct msm_clock msm_clocks[] = { .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }, - .irq = { - .name = "dg_timer", - .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_TRIGGER_RISING, - .handler = msm_timer_interrupt, - .dev_id = &msm_clocks[1].clockevent, - .irq = INT_DEBUG_TIMER_EXP - }, + .irq = INT_DEBUG_TIMER_EXP, .freq = DGT_HZ >> MSM_DGT_SHIFT, .shift = MSM_DGT_SHIFT, } @@ -261,10 +250,30 @@ static void __init msm_timer_init(void) printk(KERN_ERR "msm_timer_init: clocksource_register " "failed for %s\n", cs->name); - res = setup_irq(clock->irq.irq, &clock->irq); + ce->irq = clock->irq; + if (cpu_is_msm8x60() || cpu_is_msm8960()) { + clock->percpu_evt = alloc_percpu(struct clock_event_device *); + if (!clock->percpu_evt) { + pr_err("msm_timer_init: memory allocation " + "failed for %s\n", ce->name); + continue; + } + + *__this_cpu_ptr(clock->percpu_evt) = ce; + res = request_percpu_irq(ce->irq, msm_timer_interrupt, + ce->name, clock->percpu_evt); + if (!res) + enable_percpu_irq(ce->irq, 0); + } else { + clock->evt = ce; + res = request_irq(ce->irq, msm_timer_interrupt, + IRQF_TIMER | IRQF_NOBALANCING | IRQF_TRIGGER_RISING, + ce->name, &clock->evt); + } + if (res) - printk(KERN_ERR "msm_timer_init: setup_irq " - "failed for %s\n", cs->name); + pr_err("msm_timer_init: request_irq failed for %s\n", + ce->name); clockevents_register_device(ce); } @@ -273,6 +282,7 @@ static void __init msm_timer_init(void) #ifdef CONFIG_SMP int __cpuinit local_timer_setup(struct clock_event_device *evt) { + static bool local_timer_inited; struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER]; /* Use existing clock_event for cpu 0 */ @@ -281,12 +291,13 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt) writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); - if (!local_clock_event) { + if (!local_timer_inited) { writel(0, clock->regbase + TIMER_ENABLE); writel(0, clock->regbase + TIMER_CLEAR); writel(~0, clock->regbase + TIMER_MATCH_VAL); + local_timer_inited = true; } - evt->irq = clock->irq.irq; + evt->irq = clock->irq; evt->name = "local_timer"; evt->features = CLOCK_EVT_FEAT_ONESHOT; evt->rating = clock->clockevent.rating; @@ -298,17 +309,17 @@ int __cpuinit local_timer_setup(struct clock_event_device *evt) clockevent_delta2ns(0xf0000000 >> clock->shift, evt); evt->min_delta_ns = clockevent_delta2ns(4, evt); - local_clock_event = evt; - - gic_enable_ppi(clock->irq.irq); + *__this_cpu_ptr(clock->percpu_evt) = evt; + enable_percpu_irq(evt->irq, 0); clockevents_register_device(evt); return 0; } -inline int local_timer_ack(void) +void local_timer_stop(struct clock_event_device *evt) { - return 1; + evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt); + disable_percpu_irq(evt->irq); } #endif