diff options
| author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 13:45:02 -0400 |
|---|---|---|
| committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-20 02:09:59 -0400 |
| commit | 5c7c1e9444d8bfb721a27a35bba3eeb5236c75d8 (patch) | |
| tree | b63c895e73bfb9b921a20acde8254c10656e99b4 /arch/powerpc/sysdev/xics | |
| parent | 628daa8d5abfd904a7329a660c5c374212230123 (diff) | |
powerpc/powernv: Add OPAL ICS backend
OPAL handles HW access to the various ICS or equivalent chips
for us (with the exception of p5ioc2 based HEA which uses a
different backend) similarily to what RTAS does on pSeries.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch/powerpc/sysdev/xics')
| -rw-r--r-- | arch/powerpc/sysdev/xics/Makefile | 1 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/xics/ics-opal.c | 244 | ||||
| -rw-r--r-- | arch/powerpc/sysdev/xics/xics-common.c | 8 |
3 files changed, 247 insertions, 6 deletions
diff --git a/arch/powerpc/sysdev/xics/Makefile b/arch/powerpc/sysdev/xics/Makefile index b75a6059337..c606aa8ba60 100644 --- a/arch/powerpc/sysdev/xics/Makefile +++ b/arch/powerpc/sysdev/xics/Makefile | |||
| @@ -4,3 +4,4 @@ obj-y += xics-common.o | |||
| 4 | obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o | 4 | obj-$(CONFIG_PPC_ICP_NATIVE) += icp-native.o |
| 5 | obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o | 5 | obj-$(CONFIG_PPC_ICP_HV) += icp-hv.o |
| 6 | obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o | 6 | obj-$(CONFIG_PPC_ICS_RTAS) += ics-rtas.o |
| 7 | obj-$(CONFIG_PPC_POWERNV) += ics-opal.o | ||
diff --git a/arch/powerpc/sysdev/xics/ics-opal.c b/arch/powerpc/sysdev/xics/ics-opal.c new file mode 100644 index 00000000000..f7e8609df0d --- /dev/null +++ b/arch/powerpc/sysdev/xics/ics-opal.c | |||
| @@ -0,0 +1,244 @@ | |||
| 1 | /* | ||
| 2 | * ICS backend for OPAL managed interrupts. | ||
| 3 | * | ||
| 4 | * Copyright 2011 IBM Corp. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License | ||
| 8 | * as published by the Free Software Foundation; either version | ||
| 9 | * 2 of the License, or (at your option) any later version. | ||
| 10 | */ | ||
| 11 | |||
| 12 | #undef DEBUG | ||
| 13 | |||
| 14 | #include <linux/types.h> | ||
| 15 | #include <linux/kernel.h> | ||
| 16 | #include <linux/irq.h> | ||
| 17 | #include <linux/smp.h> | ||
| 18 | #include <linux/interrupt.h> | ||
| 19 | #include <linux/init.h> | ||
| 20 | #include <linux/cpu.h> | ||
| 21 | #include <linux/of.h> | ||
| 22 | #include <linux/spinlock.h> | ||
| 23 | #include <linux/msi.h> | ||
| 24 | |||
| 25 | #include <asm/prom.h> | ||
| 26 | #include <asm/smp.h> | ||
| 27 | #include <asm/machdep.h> | ||
| 28 | #include <asm/irq.h> | ||
| 29 | #include <asm/errno.h> | ||
| 30 | #include <asm/xics.h> | ||
| 31 | #include <asm/opal.h> | ||
| 32 | #include <asm/firmware.h> | ||
| 33 | |||
| 34 | static int ics_opal_mangle_server(int server) | ||
| 35 | { | ||
| 36 | /* No link for now */ | ||
| 37 | return server << 2; | ||
| 38 | } | ||
| 39 | |||
| 40 | static int ics_opal_unmangle_server(int server) | ||
| 41 | { | ||
| 42 | /* No link for now */ | ||
| 43 | return server >> 2; | ||
| 44 | } | ||
| 45 | |||
| 46 | static void ics_opal_unmask_irq(struct irq_data *d) | ||
| 47 | { | ||
| 48 | unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); | ||
| 49 | int64_t rc; | ||
| 50 | int server; | ||
| 51 | |||
| 52 | pr_devel("ics-hal: unmask virq %d [hw 0x%x]\n", d->irq, hw_irq); | ||
| 53 | |||
| 54 | if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) | ||
| 55 | return; | ||
| 56 | |||
| 57 | server = xics_get_irq_server(d->irq, d->affinity, 0); | ||
| 58 | server = ics_opal_mangle_server(server); | ||
| 59 | |||
| 60 | rc = opal_set_xive(hw_irq, server, DEFAULT_PRIORITY); | ||
| 61 | if (rc != OPAL_SUCCESS) | ||
| 62 | pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" | ||
| 63 | " error %lld\n", | ||
| 64 | __func__, d->irq, hw_irq, server, rc); | ||
| 65 | } | ||
| 66 | |||
| 67 | static unsigned int ics_opal_startup(struct irq_data *d) | ||
| 68 | { | ||
| 69 | #ifdef CONFIG_PCI_MSI | ||
| 70 | /* | ||
| 71 | * The generic MSI code returns with the interrupt disabled on the | ||
| 72 | * card, using the MSI mask bits. Firmware doesn't appear to unmask | ||
| 73 | * at that level, so we do it here by hand. | ||
| 74 | */ | ||
| 75 | if (d->msi_desc) | ||
| 76 | unmask_msi_irq(d); | ||
| 77 | #endif | ||
| 78 | |||
| 79 | /* unmask it */ | ||
| 80 | ics_opal_unmask_irq(d); | ||
| 81 | return 0; | ||
| 82 | } | ||
| 83 | |||
| 84 | static void ics_opal_mask_real_irq(unsigned int hw_irq) | ||
| 85 | { | ||
| 86 | int server = ics_opal_mangle_server(xics_default_server); | ||
| 87 | int64_t rc; | ||
| 88 | |||
| 89 | if (hw_irq == XICS_IPI) | ||
| 90 | return; | ||
| 91 | |||
| 92 | /* Have to set XIVE to 0xff to be able to remove a slot */ | ||
| 93 | rc = opal_set_xive(hw_irq, server, 0xff); | ||
| 94 | if (rc != OPAL_SUCCESS) | ||
| 95 | pr_err("%s: opal_set_xive(0xff) irq=%u returned %lld\n", | ||
| 96 | __func__, hw_irq, rc); | ||
| 97 | } | ||
| 98 | |||
| 99 | static void ics_opal_mask_irq(struct irq_data *d) | ||
| 100 | { | ||
| 101 | unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); | ||
| 102 | |||
| 103 | pr_devel("ics-hal: mask virq %d [hw 0x%x]\n", d->irq, hw_irq); | ||
| 104 | |||
| 105 | if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) | ||
| 106 | return; | ||
| 107 | ics_opal_mask_real_irq(hw_irq); | ||
| 108 | } | ||
| 109 | |||
| 110 | static int ics_opal_set_affinity(struct irq_data *d, | ||
| 111 | const struct cpumask *cpumask, | ||
| 112 | bool force) | ||
| 113 | { | ||
| 114 | unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d); | ||
| 115 | int16_t server; | ||
| 116 | int8_t priority; | ||
| 117 | int64_t rc; | ||
| 118 | int wanted_server; | ||
| 119 | |||
| 120 | if (hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS) | ||
| 121 | return -1; | ||
| 122 | |||
| 123 | rc = opal_get_xive(hw_irq, &server, &priority); | ||
| 124 | if (rc != OPAL_SUCCESS) { | ||
| 125 | pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" | ||
| 126 | " error %lld\n", | ||
| 127 | __func__, d->irq, hw_irq, server, rc); | ||
| 128 | return -1; | ||
| 129 | } | ||
| 130 | |||
| 131 | wanted_server = xics_get_irq_server(d->irq, cpumask, 1); | ||
| 132 | if (wanted_server < 0) { | ||
| 133 | char cpulist[128]; | ||
| 134 | cpumask_scnprintf(cpulist, sizeof(cpulist), cpumask); | ||
| 135 | pr_warning("%s: No online cpus in the mask %s for irq %d\n", | ||
| 136 | __func__, cpulist, d->irq); | ||
| 137 | return -1; | ||
| 138 | } | ||
| 139 | server = ics_opal_mangle_server(wanted_server); | ||
| 140 | |||
| 141 | pr_devel("ics-hal: set-affinity irq %d [hw 0x%x] server: 0x%x/0x%x\n", | ||
| 142 | d->irq, hw_irq, wanted_server, server); | ||
| 143 | |||
| 144 | rc = opal_set_xive(hw_irq, server, priority); | ||
| 145 | if (rc != OPAL_SUCCESS) { | ||
| 146 | pr_err("%s: opal_set_xive(irq=%d [hw 0x%x] server=%x)" | ||
| 147 | " error %lld\n", | ||
| 148 | __func__, d->irq, hw_irq, server, rc); | ||
| 149 | return -1; | ||
| 150 | } | ||
| 151 | return 0; | ||
| 152 | } | ||
| 153 | |||
| 154 | static struct irq_chip ics_opal_irq_chip = { | ||
| 155 | .name = "OPAL ICS", | ||
| 156 | .irq_startup = ics_opal_startup, | ||
| 157 | .irq_mask = ics_opal_mask_irq, | ||
| 158 | .irq_unmask = ics_opal_unmask_irq, | ||
| 159 | .irq_eoi = NULL, /* Patched at init time */ | ||
| 160 | .irq_set_affinity = ics_opal_set_affinity | ||
| 161 | }; | ||
| 162 | |||
| 163 | static int ics_opal_map(struct ics *ics, unsigned int virq); | ||
| 164 | static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec); | ||
| 165 | static long ics_opal_get_server(struct ics *ics, unsigned long vec); | ||
| 166 | |||
| 167 | static int ics_opal_host_match(struct ics *ics, struct device_node *node) | ||
| 168 | { | ||
| 169 | return 1; | ||
| 170 | } | ||
| 171 | |||
| 172 | /* Only one global & state struct ics */ | ||
| 173 | static struct ics ics_hal = { | ||
| 174 | .map = ics_opal_map, | ||
| 175 | .mask_unknown = ics_opal_mask_unknown, | ||
| 176 | .get_server = ics_opal_get_server, | ||
| 177 | .host_match = ics_opal_host_match, | ||
| 178 | }; | ||
| 179 | |||
| 180 | static int ics_opal_map(struct ics *ics, unsigned int virq) | ||
| 181 | { | ||
| 182 | unsigned int hw_irq = (unsigned int)virq_to_hw(virq); | ||
| 183 | int64_t rc; | ||
| 184 | int16_t server; | ||
| 185 | int8_t priority; | ||
| 186 | |||
| 187 | if (WARN_ON(hw_irq == XICS_IPI || hw_irq == XICS_IRQ_SPURIOUS)) | ||
| 188 | return -EINVAL; | ||
| 189 | |||
| 190 | /* Check if HAL knows about this interrupt */ | ||
| 191 | rc = opal_get_xive(hw_irq, &server, &priority); | ||
| 192 | if (rc != OPAL_SUCCESS) | ||
| 193 | return -ENXIO; | ||
| 194 | |||
| 195 | irq_set_chip_and_handler(virq, &ics_opal_irq_chip, handle_fasteoi_irq); | ||
| 196 | irq_set_chip_data(virq, &ics_hal); | ||
| 197 | |||
| 198 | return 0; | ||
| 199 | } | ||
| 200 | |||
| 201 | static void ics_opal_mask_unknown(struct ics *ics, unsigned long vec) | ||
| 202 | { | ||
| 203 | int64_t rc; | ||
| 204 | int16_t server; | ||
| 205 | int8_t priority; | ||
| 206 | |||
| 207 | /* Check if HAL knows about this interrupt */ | ||
| 208 | rc = opal_get_xive(vec, &server, &priority); | ||
| 209 | if (rc != OPAL_SUCCESS) | ||
| 210 | return; | ||
| 211 | |||
| 212 | ics_opal_mask_real_irq(vec); | ||
| 213 | } | ||
| 214 | |||
| 215 | static long ics_opal_get_server(struct ics *ics, unsigned long vec) | ||
| 216 | { | ||
| 217 | int64_t rc; | ||
| 218 | int16_t server; | ||
| 219 | int8_t priority; | ||
| 220 | |||
| 221 | /* Check if HAL knows about this interrupt */ | ||
| 222 | rc = opal_get_xive(vec, &server, &priority); | ||
| 223 | if (rc != OPAL_SUCCESS) | ||
| 224 | return -1; | ||
| 225 | return ics_opal_unmangle_server(server); | ||
| 226 | } | ||
| 227 | |||
| 228 | int __init ics_opal_init(void) | ||
| 229 | { | ||
| 230 | if (!firmware_has_feature(FW_FEATURE_OPAL)) | ||
| 231 | return -ENODEV; | ||
| 232 | |||
| 233 | /* We need to patch our irq chip's EOI to point to the | ||
| 234 | * right ICP | ||
| 235 | */ | ||
| 236 | ics_opal_irq_chip.irq_eoi = icp_ops->eoi; | ||
| 237 | |||
| 238 | /* Register ourselves */ | ||
| 239 | xics_register_ics(&ics_hal); | ||
| 240 | |||
| 241 | pr_info("ICS OPAL backend registered\n"); | ||
| 242 | |||
| 243 | return 0; | ||
| 244 | } | ||
diff --git a/arch/powerpc/sysdev/xics/xics-common.c b/arch/powerpc/sysdev/xics/xics-common.c index 445c5a01b76..3d93a8ded0f 100644 --- a/arch/powerpc/sysdev/xics/xics-common.c +++ b/arch/powerpc/sysdev/xics/xics-common.c | |||
| @@ -409,14 +409,10 @@ void __init xics_init(void) | |||
| 409 | int rc = -1; | 409 | int rc = -1; |
| 410 | 410 | ||
| 411 | /* Fist locate ICP */ | 411 | /* Fist locate ICP */ |
| 412 | #ifdef CONFIG_PPC_ICP_HV | ||
| 413 | if (firmware_has_feature(FW_FEATURE_LPAR)) | 412 | if (firmware_has_feature(FW_FEATURE_LPAR)) |
| 414 | rc = icp_hv_init(); | 413 | rc = icp_hv_init(); |
| 415 | #endif | ||
| 416 | #ifdef CONFIG_PPC_ICP_NATIVE | ||
| 417 | if (rc < 0) | 414 | if (rc < 0) |
| 418 | rc = icp_native_init(); | 415 | rc = icp_native_init(); |
| 419 | #endif | ||
| 420 | if (rc < 0) { | 416 | if (rc < 0) { |
| 421 | pr_warning("XICS: Cannot find a Presentation Controller !\n"); | 417 | pr_warning("XICS: Cannot find a Presentation Controller !\n"); |
| 422 | return; | 418 | return; |
| @@ -429,9 +425,9 @@ void __init xics_init(void) | |||
| 429 | xics_ipi_chip.irq_eoi = icp_ops->eoi; | 425 | xics_ipi_chip.irq_eoi = icp_ops->eoi; |
| 430 | 426 | ||
| 431 | /* Now locate ICS */ | 427 | /* Now locate ICS */ |
| 432 | #ifdef CONFIG_PPC_ICS_RTAS | ||
| 433 | rc = ics_rtas_init(); | 428 | rc = ics_rtas_init(); |
| 434 | #endif | 429 | if (rc < 0) |
| 430 | rc = ics_opal_init(); | ||
| 435 | if (rc < 0) | 431 | if (rc < 0) |
| 436 | pr_warning("XICS: Cannot find a Source Controller !\n"); | 432 | pr_warning("XICS: Cannot find a Source Controller !\n"); |
| 437 | 433 | ||
