diff options
Diffstat (limited to 'arch/sparc')
-rw-r--r-- | arch/sparc/kernel/leon_kernel.c | 64 |
1 files changed, 54 insertions, 10 deletions
diff --git a/arch/sparc/kernel/leon_kernel.c b/arch/sparc/kernel/leon_kernel.c index d8fafeacb49..ab145845442 100644 --- a/arch/sparc/kernel/leon_kernel.c +++ b/arch/sparc/kernel/leon_kernel.c @@ -99,25 +99,68 @@ static inline unsigned long get_irqmask(unsigned int irq) return mask; } +#ifdef CONFIG_SMP +static int irq_choose_cpu(const struct cpumask *affinity) +{ + cpumask_t mask; + + cpus_and(mask, cpu_online_map, *affinity); + if (cpus_equal(mask, cpu_online_map) || cpus_empty(mask)) + return 0; + else + return first_cpu(mask); +} +#else +#define irq_choose_cpu(affinity) 0 +#endif + +static int leon_set_affinity(struct irq_data *data, const struct cpumask *dest, + bool force) +{ + unsigned long mask, oldmask, flags; + int oldcpu, newcpu; + + mask = (unsigned long)data->chip_data; + oldcpu = irq_choose_cpu(data->affinity); + newcpu = irq_choose_cpu(dest); + + if (oldcpu == newcpu) + goto out; + + /* unmask on old CPU first before enabling on the selected CPU */ + spin_lock_irqsave(&leon_irq_lock, flags); + oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(oldcpu)); + LEON3_BYPASS_STORE_PA(LEON_IMASK(oldcpu), (oldmask & ~mask)); + oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(newcpu)); + LEON3_BYPASS_STORE_PA(LEON_IMASK(newcpu), (oldmask | mask)); + spin_unlock_irqrestore(&leon_irq_lock, flags); +out: + return IRQ_SET_MASK_OK; +} + static void leon_unmask_irq(struct irq_data *data) { unsigned long mask, oldmask, flags; + int cpu; mask = (unsigned long)data->chip_data; + cpu = irq_choose_cpu(data->affinity); spin_lock_irqsave(&leon_irq_lock, flags); - oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(0)); - LEON3_BYPASS_STORE_PA(LEON_IMASK(0), (oldmask | mask)); + oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(cpu)); + LEON3_BYPASS_STORE_PA(LEON_IMASK(cpu), (oldmask | mask)); spin_unlock_irqrestore(&leon_irq_lock, flags); } static void leon_mask_irq(struct irq_data *data) { unsigned long mask, oldmask, flags; + int cpu; mask = (unsigned long)data->chip_data; + cpu = irq_choose_cpu(data->affinity); spin_lock_irqsave(&leon_irq_lock, flags); - oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(0)); - LEON3_BYPASS_STORE_PA(LEON_IMASK(0), (oldmask & ~mask)); + oldmask = LEON3_BYPASS_LOAD_PA(LEON_IMASK(cpu)); + LEON3_BYPASS_STORE_PA(LEON_IMASK(cpu), (oldmask & ~mask)); spin_unlock_irqrestore(&leon_irq_lock, flags); } @@ -144,12 +187,13 @@ static void leon_eoi_irq(struct irq_data *data) } static struct irq_chip leon_irq = { - .name = "leon", - .irq_startup = leon_startup_irq, - .irq_shutdown = leon_shutdown_irq, - .irq_mask = leon_mask_irq, - .irq_unmask = leon_unmask_irq, - .irq_eoi = leon_eoi_irq, + .name = "leon", + .irq_startup = leon_startup_irq, + .irq_shutdown = leon_shutdown_irq, + .irq_mask = leon_mask_irq, + .irq_unmask = leon_unmask_irq, + .irq_eoi = leon_eoi_irq, + .irq_set_affinity = leon_set_affinity, }; /* |