linux/arch/powerpc/sysdev/xics/icp-native.c
<<
>>
Prefs
   1/*
   2 * Copyright 2011 IBM Corporation.
   3 *
   4 *  This program is free software; you can redistribute it and/or
   5 *  modify it under the terms of the GNU General Public License
   6 *  as published by the Free Software Foundation; either version
   7 *  2 of the License, or (at your option) any later version.
   8 *
   9 */
  10
  11#include <linux/types.h>
  12#include <linux/kernel.h>
  13#include <linux/irq.h>
  14#include <linux/smp.h>
  15#include <linux/interrupt.h>
  16#include <linux/init.h>
  17#include <linux/cpu.h>
  18#include <linux/of.h>
  19#include <linux/spinlock.h>
  20#include <linux/module.h>
  21
  22#include <asm/prom.h>
  23#include <asm/io.h>
  24#include <asm/smp.h>
  25#include <asm/irq.h>
  26#include <asm/errno.h>
  27#include <asm/xics.h>
  28#include <asm/kvm_ppc.h>
  29
  30struct icp_ipl {
  31        union {
  32                u32 word;
  33                u8 bytes[4];
  34        } xirr_poll;
  35        union {
  36                u32 word;
  37                u8 bytes[4];
  38        } xirr;
  39        u32 dummy;
  40        union {
  41                u32 word;
  42                u8 bytes[4];
  43        } qirr;
  44        u32 link_a;
  45        u32 link_b;
  46        u32 link_c;
  47};
  48
  49static struct icp_ipl __iomem *icp_native_regs[NR_CPUS];
  50
  51static inline unsigned int icp_native_get_xirr(void)
  52{
  53        int cpu = smp_processor_id();
  54        unsigned int xirr;
  55
  56        /* Handled an interrupt latched by KVM */
  57        xirr = kvmppc_get_xics_latch();
  58        if (xirr)
  59                return xirr;
  60
  61        return in_be32(&icp_native_regs[cpu]->xirr.word);
  62}
  63
  64static inline void icp_native_set_xirr(unsigned int value)
  65{
  66        int cpu = smp_processor_id();
  67
  68        out_be32(&icp_native_regs[cpu]->xirr.word, value);
  69}
  70
  71static inline void icp_native_set_cppr(u8 value)
  72{
  73        int cpu = smp_processor_id();
  74
  75        out_8(&icp_native_regs[cpu]->xirr.bytes[0], value);
  76}
  77
  78static inline void icp_native_set_qirr(int n_cpu, u8 value)
  79{
  80        out_8(&icp_native_regs[n_cpu]->qirr.bytes[0], value);
  81}
  82
  83static void icp_native_set_cpu_priority(unsigned char cppr)
  84{
  85        xics_set_base_cppr(cppr);
  86        icp_native_set_cppr(cppr);
  87        iosync();
  88}
  89
  90void icp_native_eoi(struct irq_data *d)
  91{
  92        unsigned int hw_irq = (unsigned int)irqd_to_hwirq(d);
  93
  94        iosync();
  95        icp_native_set_xirr((xics_pop_cppr() << 24) | hw_irq);
  96}
  97
  98static void icp_native_teardown_cpu(void)
  99{
 100        int cpu = smp_processor_id();
 101
 102        /* Clear any pending IPI */
 103        icp_native_set_qirr(cpu, 0xff);
 104}
 105
 106static void icp_native_flush_ipi(void)
 107{
 108        /* We take the ipi irq but and never return so we
 109         * need to EOI the IPI, but want to leave our priority 0
 110         *
 111         * should we check all the other interrupts too?
 112         * should we be flagging idle loop instead?
 113         * or creating some task to be scheduled?
 114         */
 115
 116        icp_native_set_xirr((0x00 << 24) | XICS_IPI);
 117}
 118
 119static unsigned int icp_native_get_irq(void)
 120{
 121        unsigned int xirr = icp_native_get_xirr();
 122        unsigned int vec = xirr & 0x00ffffff;
 123        unsigned int irq;
 124
 125        if (vec == XICS_IRQ_SPURIOUS)
 126                return NO_IRQ;
 127
 128        irq = irq_find_mapping(xics_host, vec);
 129        if (likely(irq != NO_IRQ)) {
 130                xics_push_cppr(vec);
 131                return irq;
 132        }
 133
 134        /* We don't have a linux mapping, so have rtas mask it. */
 135        xics_mask_unknown_vec(vec);
 136
 137        /* We might learn about it later, so EOI it */
 138        icp_native_set_xirr(xirr);
 139
 140        return NO_IRQ;
 141}
 142
 143#ifdef CONFIG_SMP
 144
 145static void icp_native_cause_ipi(int cpu, unsigned long data)
 146{
 147        kvmppc_set_host_ipi(cpu, 1);
 148        icp_native_set_qirr(cpu, IPI_PRIORITY);
 149}
 150
 151void xics_wake_cpu(int cpu)
 152{
 153        icp_native_set_qirr(cpu, IPI_PRIORITY);
 154}
 155EXPORT_SYMBOL_GPL(xics_wake_cpu);
 156
 157static irqreturn_t icp_native_ipi_action(int irq, void *dev_id)
 158{
 159        int cpu = smp_processor_id();
 160
 161        kvmppc_set_host_ipi(cpu, 0);
 162        icp_native_set_qirr(cpu, 0xff);
 163
 164        return smp_ipi_demux();
 165}
 166
 167#endif /* CONFIG_SMP */
 168
 169static int __init icp_native_map_one_cpu(int hw_id, unsigned long addr,
 170                                         unsigned long size)
 171{
 172        char *rname;
 173        int i, cpu = -1;
 174
 175        /* This may look gross but it's good enough for now, we don't quite
 176         * have a hard -> linux processor id matching.
 177         */
 178        for_each_possible_cpu(i) {
 179                if (!cpu_present(i))
 180                        continue;
 181                if (hw_id == get_hard_smp_processor_id(i)) {
 182                        cpu = i;
 183                        break;
 184                }
 185        }
 186
 187        /* Fail, skip that CPU. Don't print, it's normal, some XICS come up
 188         * with way more entries in there than you have CPUs
 189         */
 190        if (cpu == -1)
 191                return 0;
 192
 193        rname = kasprintf(GFP_KERNEL, "CPU %d [0x%x] Interrupt Presentation",
 194                          cpu, hw_id);
 195
 196        if (!request_mem_region(addr, size, rname)) {
 197                pr_warning("icp_native: Could not reserve ICP MMIO"
 198                           " for CPU %d, interrupt server #0x%x\n",
 199                           cpu, hw_id);
 200                return -EBUSY;
 201        }
 202
 203        icp_native_regs[cpu] = ioremap(addr, size);
 204        kvmppc_set_xics_phys(cpu, addr);
 205        if (!icp_native_regs[cpu]) {
 206                pr_warning("icp_native: Failed ioremap for CPU %d, "
 207                           "interrupt server #0x%x, addr %#lx\n",
 208                           cpu, hw_id, addr);
 209                release_mem_region(addr, size);
 210                return -ENOMEM;
 211        }
 212        return 0;
 213}
 214
 215static int __init icp_native_init_one_node(struct device_node *np,
 216                                           unsigned int *indx)
 217{
 218        unsigned int ilen;
 219        const u32 *ireg;
 220        int i;
 221        int reg_tuple_size;
 222        int num_servers = 0;
 223
 224        /* This code does the theorically broken assumption that the interrupt
 225         * server numbers are the same as the hard CPU numbers.
 226         * This happens to be the case so far but we are playing with fire...
 227         * should be fixed one of these days. -BenH.
 228         */
 229        ireg = of_get_property(np, "ibm,interrupt-server-ranges", &ilen);
 230
 231        /* Do that ever happen ? we'll know soon enough... but even good'old
 232         * f80 does have that property ..
 233         */
 234        WARN_ON((ireg == NULL) || (ilen != 2*sizeof(u32)));
 235
 236        if (ireg) {
 237                *indx = of_read_number(ireg, 1);
 238                if (ilen >= 2*sizeof(u32))
 239                        num_servers = of_read_number(ireg + 1, 1);
 240        }
 241
 242        ireg = of_get_property(np, "reg", &ilen);
 243        if (!ireg) {
 244                pr_err("icp_native: Can't find interrupt reg property");
 245                return -1;
 246        }
 247
 248        reg_tuple_size = (of_n_addr_cells(np) + of_n_size_cells(np)) * 4;
 249        if (((ilen % reg_tuple_size) != 0)
 250            || (num_servers && (num_servers != (ilen / reg_tuple_size)))) {
 251                pr_err("icp_native: ICP reg len (%d) != num servers (%d)",
 252                       ilen / reg_tuple_size, num_servers);
 253                return -1;
 254        }
 255
 256        for (i = 0; i < (ilen / reg_tuple_size); i++) {
 257                struct resource r;
 258                int err;
 259
 260                err = of_address_to_resource(np, i, &r);
 261                if (err) {
 262                        pr_err("icp_native: Could not translate ICP MMIO"
 263                               " for interrupt server 0x%x (%d)\n", *indx, err);
 264                        return -1;
 265                }
 266
 267                if (icp_native_map_one_cpu(*indx, r.start, resource_size(&r)))
 268                        return -1;
 269
 270                (*indx)++;
 271        }
 272        return 0;
 273}
 274
 275static const struct icp_ops icp_native_ops = {
 276        .get_irq        = icp_native_get_irq,
 277        .eoi            = icp_native_eoi,
 278        .set_priority   = icp_native_set_cpu_priority,
 279        .teardown_cpu   = icp_native_teardown_cpu,
 280        .flush_ipi      = icp_native_flush_ipi,
 281#ifdef CONFIG_SMP
 282        .ipi_action     = icp_native_ipi_action,
 283        .cause_ipi      = icp_native_cause_ipi,
 284#endif
 285};
 286
 287int __init icp_native_init(void)
 288{
 289        struct device_node *np;
 290        u32 indx = 0;
 291        int found = 0;
 292
 293        for_each_compatible_node(np, NULL, "ibm,ppc-xicp")
 294                if (icp_native_init_one_node(np, &indx) == 0)
 295                        found = 1;
 296        if (!found) {
 297                for_each_node_by_type(np,
 298                        "PowerPC-External-Interrupt-Presentation") {
 299                                if (icp_native_init_one_node(np, &indx) == 0)
 300                                        found = 1;
 301                }
 302        }
 303
 304        if (found == 0)
 305                return -ENODEV;
 306
 307        icp_ops = &icp_native_ops;
 308
 309        return 0;
 310}
 311