diff options
Diffstat (limited to 'arch/powerpc/platforms/pseries/xics.c')
-rw-r--r-- | arch/powerpc/platforms/pseries/xics.c | 84 |
1 files changed, 65 insertions, 19 deletions
diff --git a/arch/powerpc/platforms/pseries/xics.c b/arch/powerpc/platforms/pseries/xics.c index b9bf0eedccf..d80f193cd87 100644 --- a/arch/powerpc/platforms/pseries/xics.c +++ b/arch/powerpc/platforms/pseries/xics.c @@ -20,6 +20,7 @@ #include <linux/cpu.h> #include <linux/msi.h> #include <linux/of.h> +#include <linux/percpu.h> #include <asm/firmware.h> #include <asm/io.h> @@ -46,6 +47,12 @@ static struct irq_host *xics_host; */ #define IPI_PRIORITY 4 +/* The least favored priority */ +#define LOWEST_PRIORITY 0xFF + +/* The number of priorities defined above */ +#define MAX_NUM_PRIORITIES 3 + static unsigned int default_server = 0xFF; static unsigned int default_distrib_server = 0; static unsigned int interrupt_server_size = 8; @@ -56,6 +63,12 @@ static int ibm_set_xive; static int ibm_int_on; static int ibm_int_off; +struct xics_cppr { + unsigned char stack[MAX_NUM_PRIORITIES]; + int index; +}; + +static DEFINE_PER_CPU(struct xics_cppr, xics_cppr); /* Direct hardware low level accessors */ @@ -150,14 +163,13 @@ static inline void lpar_qirr_info(int n_cpu , u8 value) /* Interface to generic irq subsystem */ #ifdef CONFIG_SMP -static int get_irq_server(unsigned int virq, unsigned int strict_check) +static int get_irq_server(unsigned int virq, cpumask_t cpumask, + unsigned int strict_check) { int server; /* For the moment only implement delivery to all cpus or one cpu */ - cpumask_t cpumask; cpumask_t tmp = CPU_MASK_NONE; - cpumask_copy(&cpumask, irq_desc[virq].affinity); if (!distribute_irqs) return default_server; @@ -179,7 +191,8 @@ static int get_irq_server(unsigned int virq, unsigned int strict_check) return default_server; } #else -static int get_irq_server(unsigned int virq, unsigned int strict_check) +static int get_irq_server(unsigned int virq, cpumask_t cpumask, + unsigned int strict_check) { return default_server; } @@ -198,7 +211,7 @@ static void xics_unmask_irq(unsigned int virq) if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) return; - server = get_irq_server(virq, 0); + server = get_irq_server(virq, *(irq_to_desc(virq)->affinity), 0); call_status = rtas_call(ibm_set_xive, 3, 1, NULL, irq, server, DEFAULT_PRIORITY); @@ -284,6 +297,19 @@ static inline unsigned int xics_xirr_vector(unsigned int xirr) return xirr & 0x00ffffff; } +static void push_cppr(unsigned int vec) +{ + struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr); + + if (WARN_ON(os_cppr->index >= MAX_NUM_PRIORITIES - 1)) + return; + + if (vec == XICS_IPI) + os_cppr->stack[++os_cppr->index] = IPI_PRIORITY; + else + os_cppr->stack[++os_cppr->index] = DEFAULT_PRIORITY; +} + static unsigned int xics_get_irq_direct(void) { unsigned int xirr = direct_xirr_info_get(); @@ -294,8 +320,10 @@ static unsigned int xics_get_irq_direct(void) return NO_IRQ; irq = irq_radix_revmap_lookup(xics_host, vec); - if (likely(irq != NO_IRQ)) + if (likely(irq != NO_IRQ)) { + push_cppr(vec); return irq; + } /* We don't have a linux mapping, so have rtas mask it. */ xics_mask_unknown_vec(vec); @@ -315,8 +343,10 @@ static unsigned int xics_get_irq_lpar(void) return NO_IRQ; irq = irq_radix_revmap_lookup(xics_host, vec); - if (likely(irq != NO_IRQ)) + if (likely(irq != NO_IRQ)) { + push_cppr(vec); return irq; + } /* We don't have a linux mapping, so have RTAS mask it. */ xics_mask_unknown_vec(vec); @@ -326,12 +356,22 @@ static unsigned int xics_get_irq_lpar(void) return NO_IRQ; } +static unsigned char pop_cppr(void) +{ + struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr); + + if (WARN_ON(os_cppr->index < 1)) + return LOWEST_PRIORITY; + + return os_cppr->stack[--os_cppr->index]; +} + static void xics_eoi_direct(unsigned int virq) { unsigned int irq = (unsigned int)irq_map[virq].hwirq; iosync(); - direct_xirr_info_set((0xff << 24) | irq); + direct_xirr_info_set((pop_cppr() << 24) | irq); } static void xics_eoi_lpar(unsigned int virq) @@ -339,7 +379,7 @@ static void xics_eoi_lpar(unsigned int virq) unsigned int irq = (unsigned int)irq_map[virq].hwirq; iosync(); - lpar_xirr_info_set((0xff << 24) | irq); + lpar_xirr_info_set((pop_cppr() << 24) | irq); } static int xics_set_affinity(unsigned int virq, const struct cpumask *cpumask) @@ -365,7 +405,7 @@ static int xics_set_affinity(unsigned int virq, const struct cpumask *cpumask) * For the moment only implement delivery to all cpus or one cpu. * Get current irq_server for the given irq */ - irq_server = get_irq_server(virq, 1); + irq_server = get_irq_server(virq, *cpumask, 1); if (irq_server == -1) { char cpulist[128]; cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); @@ -388,7 +428,7 @@ static int xics_set_affinity(unsigned int virq, const struct cpumask *cpumask) } static struct irq_chip xics_pic_direct = { - .typename = " XICS ", + .name = " XICS ", .startup = xics_startup, .mask = xics_mask_irq, .unmask = xics_unmask_irq, @@ -397,7 +437,7 @@ static struct irq_chip xics_pic_direct = { }; static struct irq_chip xics_pic_lpar = { - .typename = " XICS ", + .name = " XICS ", .startup = xics_startup, .mask = xics_mask_irq, .unmask = xics_unmask_irq, @@ -428,13 +468,13 @@ static int xics_host_map(struct irq_host *h, unsigned int virq, /* Insert the interrupt mapping into the radix tree for fast lookup */ irq_radix_revmap_insert(xics_host, virq, hw); - get_irq_desc(virq)->status |= IRQ_LEVEL; + irq_to_desc(virq)->status |= IRQ_LEVEL; set_irq_chip_and_handler(virq, xics_irq_chip, handle_fasteoi_irq); return 0; } static int xics_host_xlate(struct irq_host *h, struct device_node *ct, - u32 *intspec, unsigned int intsize, + const u32 *intspec, unsigned int intsize, irq_hw_number_t *out_hwirq, unsigned int *out_flags) { @@ -746,6 +786,12 @@ void __init xics_init_IRQ(void) static void xics_set_cpu_priority(unsigned char cppr) { + struct xics_cppr *os_cppr = &__get_cpu_var(xics_cppr); + + BUG_ON(os_cppr->index != 0); + + os_cppr->stack[os_cppr->index] = cppr; + if (firmware_has_feature(FW_FEATURE_LPAR)) lpar_cppr_info(cppr); else @@ -772,7 +818,7 @@ static void xics_set_cpu_giq(unsigned int gserver, unsigned int join) void xics_setup_cpu(void) { - xics_set_cpu_priority(0xff); + xics_set_cpu_priority(LOWEST_PRIORITY); xics_set_cpu_giq(default_distrib_server, 1); } @@ -852,7 +898,7 @@ void xics_migrate_irqs_away(void) /* We need to get IPIs still. */ if (irq == XICS_IPI || irq == XICS_IRQ_SPURIOUS) continue; - desc = get_irq_desc(virq); + desc = irq_to_desc(virq); /* We only need to migrate enabled IRQS */ if (desc == NULL || desc->chip == NULL @@ -860,7 +906,7 @@ void xics_migrate_irqs_away(void) || desc->chip->set_affinity == NULL) continue; - spin_lock_irqsave(&desc->lock, flags); + raw_spin_lock_irqsave(&desc->lock, flags); status = rtas_call(ibm_get_xive, 1, 3, xics_status, irq); if (status) { @@ -881,10 +927,10 @@ void xics_migrate_irqs_away(void) virq, cpu); /* Reset affinity to all cpus */ - cpumask_setall(irq_desc[virq].affinity); + cpumask_setall(irq_to_desc(virq)->affinity); desc->chip->set_affinity(virq, cpu_all_mask); unlock: - spin_unlock_irqrestore(&desc->lock, flags); + raw_spin_unlock_irqrestore(&desc->lock, flags); } } #endif |