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