diff options
Diffstat (limited to 'arch/powerpc/sysdev/xics')
-rw-r--r-- | arch/powerpc/sysdev/xics/Kconfig | 1 | ||||
-rw-r--r-- | arch/powerpc/sysdev/xics/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/xics/icp-opal.c | 146 | ||||
-rw-r--r-- | arch/powerpc/sysdev/xics/ics-opal.c | 4 | ||||
-rw-r--r-- | arch/powerpc/sysdev/xics/ics-rtas.c | 4 | ||||
-rw-r--r-- | arch/powerpc/sysdev/xics/xics-common.c | 64 |
6 files changed, 210 insertions, 11 deletions
diff --git a/arch/powerpc/sysdev/xics/Kconfig b/arch/powerpc/sysdev/xics/Kconfig index 0031eda32..385e7aa9e 100644 --- a/arch/powerpc/sysdev/xics/Kconfig +++ b/arch/powerpc/sysdev/xics/Kconfig @@ -1,6 +1,7 @@ config PPC_XICS def_bool n select PPC_SMP_MUXED_IPI + select HARDIRQS_SW_RESEND config PPC_ICP_NATIVE def_bool n diff --git a/arch/powerpc/sysdev/xics/Makefile b/arch/powerpc/sysdev/xics/Makefile index c606aa8ba..5d7f5a656 100644 --- a/arch/powerpc/sysdev/xics/Makefile +++ b/arch/powerpc/sysdev/xics/Makefile @@ -4,4 +4,4 @@ obj-y += xics-common.o obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o -obj-$(CONFIG_PPC_POWERNV) += ics-opal.o +obj-$(CONFIG_PPC_POWERNV) += ics-opal.o icp-opal.o diff --git a/arch/powerpc/sysdev/xics/icp-opal.c b/arch/powerpc/sysdev/xics/icp-opal.c new file mode 100644 index 000000000..9114243fa --- /dev/null +++ b/arch/powerpc/sysdev/xics/icp-opal.c @@ -0,0 +1,146 @@ +/* + * Copyright 2016 IBM Corporation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/irq.h> +#include <linux/smp.h> +#include <linux/interrupt.h> +#include <linux/cpu.h> +#include <linux/of.h> + +#include <asm/smp.h> +#include <asm/irq.h> +#include <asm/errno.h> +#include <asm/xics.h> +#include <asm/io.h> +#include <asm/opal.h> + +static void icp_opal_teardown_cpu(void) +{ + int hw_cpu = hard_smp_processor_id(); + + /* Clear any pending IPI */ + opal_int_set_mfrr(hw_cpu, 0xff); +} + +static void icp_opal_flush_ipi(void) +{ + /* + * We take the ipi irq but and never return so we need to EOI the IPI, + * but want to leave our priority 0. + * + * Should we check all the other interrupts too? + * Should we be flagging idle loop instead? + * Or creating some task to be scheduled? + */ + opal_int_eoi((0x00 << 24) | XICS_IPI); +} + +static unsigned int icp_opal_get_irq(void) +{ + unsigned int xirr; + unsigned int vec; + unsigned int irq; + int64_t rc; + + rc = opal_int_get_xirr(&xirr, false); + if (rc < 0) + return NO_IRQ; + xirr = be32_to_cpu(xirr); + vec = xirr & 0x00ffffff; + if (vec == XICS_IRQ_SPURIOUS) + return NO_IRQ; + + irq = irq_find_mapping(xics_host, vec); + if (likely(irq != NO_IRQ)) { + xics_push_cppr(vec); + return irq; + } + + /* We don't have a linux mapping, so have rtas mask it. */ + xics_mask_unknown_vec(vec); + + /* We might learn about it later, so EOI it */ + opal_int_eoi(xirr); + + return NO_IRQ; +} + +static void icp_opal_set_cpu_priority(unsigned char cppr) +{ + xics_set_base_cppr(cppr); + opal_int_set_cppr(cppr); + iosync(); +} + +static void icp_opal_eoi(struct irq_data *d) +{ + unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); + int64_t rc; + + iosync(); + rc = opal_int_eoi((xics_pop_cppr() << 24) | hw_irq); + + /* + * EOI tells us whether there are more interrupts to fetch. + * + * Some HW implementations might not be able to send us another + * external interrupt in that case, so we force a replay. + */ + if (rc > 0) + force_external_irq_replay(); +} + +#ifdef CONFIG_SMP + +static void icp_opal_cause_ipi(int cpu, unsigned long data) +{ + int hw_cpu = get_hard_smp_processor_id(cpu); + + opal_int_set_mfrr(hw_cpu, IPI_PRIORITY); +} + +static irqreturn_t icp_opal_ipi_action(int irq, void *dev_id) +{ + int hw_cpu = hard_smp_processor_id(); + + opal_int_set_mfrr(hw_cpu, 0xff); + + return smp_ipi_demux(); +} + +#endif /* CONFIG_SMP */ + +static const struct icp_ops icp_opal_ops = { + .get_irq = icp_opal_get_irq, + .eoi = icp_opal_eoi, + .set_priority = icp_opal_set_cpu_priority, + .teardown_cpu = icp_opal_teardown_cpu, + .flush_ipi = icp_opal_flush_ipi, +#ifdef CONFIG_SMP + .ipi_action = icp_opal_ipi_action, + .cause_ipi = icp_opal_cause_ipi, +#endif +}; + +int icp_opal_init(void) +{ + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "ibm,opal-intc"); + if (!np) + return -ENODEV; + + icp_ops = &icp_opal_ops; + + printk("XICS: Using OPAL ICP fallbacks\n"); + + return 0; +} + diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c index 27c936c08..1c6bf4b66 100644 --- a/arch/powerpc/sysdev/xics/ics-opal.c +++ b/arch/powerpc/sysdev/xics/ics-opal.c @@ -156,7 +156,9 @@ static struct irq_chip ics_opal_irq_chip = { .irq_mask = ics_opal_mask_irq, .irq_unmask = ics_opal_unmask_irq, .irq_eoi = NULL, /* Patched at init time */ - .irq_set_affinity = ics_opal_set_affinity + .irq_set_affinity = ics_opal_set_affinity, + .irq_set_type = xics_set_irq_type, + .irq_retrigger = xics_retrigger, }; static int ics_opal_map(struct ics *ics, unsigned int virq); diff --git a/arch/powerpc/sysdev/xics/ics-rtas.c b/arch/powerpc/sysdev/xics/ics-rtas.c index 3854dd415..78ee5c778 100644 --- a/arch/powerpc/sysdev/xics/ics-rtas.c +++ b/arch/powerpc/sysdev/xics/ics-rtas.c @@ -163,7 +163,9 @@ static struct irq_chip ics_rtas_irq_chip = { .irq_mask = ics_rtas_mask_irq, .irq_unmask = ics_rtas_unmask_irq, .irq_eoi = NULL, /* Patched at init time */ - .irq_set_affinity = ics_rtas_set_affinity + .irq_set_affinity = ics_rtas_set_affinity, + .irq_set_type = xics_set_irq_type, + .irq_retrigger = xics_retrigger, }; static int ics_rtas_map(struct ics *ics, unsigned int virq) diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c index 47e43b7b0..9d530f479 100644 --- a/arch/powerpc/sysdev/xics/xics-common.c +++ b/arch/powerpc/sysdev/xics/xics-common.c @@ -328,8 +328,12 @@ static int xics_host_map(struct irq_domain *h, unsigned int virq, pr_devel("xics: map virq %d, hwirq 0x%lx\n", virq, hw); - /* They aren't all level sensitive but we just don't really know */ - irq_set_status_flags(virq, IRQ_LEVEL); + /* + * Mark interrupts as edge sensitive by default so that resend + * actually works. The device-tree parsing will turn the LSIs + * back to level. + */ + irq_clear_status_flags(virq, IRQ_LEVEL); /* Don't call into ICS for IPIs */ if (hw == XICS_IPI) { @@ -351,13 +355,54 @@ static int xics_host_xlate(struct irq_domain *h, struct device_node *ct, irq_hw_number_t *out_hwirq, unsigned int *out_flags) { - /* Current xics implementation translates everything - * to level. It is not technically right for MSIs but this - * is irrelevant at this point. We might get smarter in the future - */ *out_hwirq = intspec[0]; - *out_flags = IRQ_TYPE_LEVEL_LOW; + /* + * If intsize is at least 2, we look for the type in the second cell, + * we assume the LSB indicates a level interrupt. + */ + if (intsize > 1) { + if (intspec[1] & 1) + *out_flags = IRQ_TYPE_LEVEL_LOW; + else + *out_flags = IRQ_TYPE_EDGE_RISING; + } else + *out_flags = IRQ_TYPE_LEVEL_LOW; + + return 0; +} + +int xics_set_irq_type(struct irq_data *d, unsigned int flow_type) +{ + /* + * We only support these. This has really no effect other than setting + * the corresponding descriptor bits mind you but those will in turn + * affect the resend function when re-enabling an edge interrupt. + * + * Set set the default to edge as explained in map(). + */ + if (flow_type == IRQ_TYPE_DEFAULT || flow_type == IRQ_TYPE_NONE) + flow_type = IRQ_TYPE_EDGE_RISING; + + if (flow_type != IRQ_TYPE_EDGE_RISING && + flow_type != IRQ_TYPE_LEVEL_LOW) + return -EINVAL; + + irqd_set_trigger_type(d, flow_type); + + return IRQ_SET_MASK_OK_NOCOPY; +} + +int xics_retrigger(struct irq_data *data) +{ + /* + * We need to push a dummy CPPR when retriggering, since the subsequent + * EOI will try to pop it. Passing 0 works, as the function hard codes + * the priority value anyway. + */ + xics_push_cppr(0); + + /* Tell the core to do a soft retrigger */ return 0; } @@ -404,8 +449,11 @@ void __init xics_init(void) /* Fist locate ICP */ if (firmware_has_feature(FW_FEATURE_LPAR)) rc = icp_hv_init(); - if (rc < 0) + if (rc < 0) { rc = icp_native_init(); + if (rc == -ENODEV) + rc = icp_opal_init(); + } if (rc < 0) { pr_warning("XICS: Cannot find a Presentation Controller !\n"); return; |