linux/arch/powerpc/platforms/wsp/opb_pic.c
<<
>>
Prefs
   1/*
   2 * IBM Onboard Peripheral Bus Interrupt Controller
   3 *
   4 * Copyright 2010 Jack Miller, IBM Corporation.
   5 *
   6 * This program is free software; you can redistribute  it and/or modify it
   7 * under  the terms of  the GNU General  Public License as published by the
   8 * Free Software Foundation;  either version 2 of the  License, or (at your
   9 * option) any later version.
  10 */
  11
  12#include <linux/interrupt.h>
  13#include <linux/io.h>
  14#include <linux/irq.h>
  15#include <linux/of.h>
  16#include <linux/slab.h>
  17#include <linux/time.h>
  18
  19#include <asm/reg_a2.h>
  20#include <asm/irq.h>
  21
  22#define OPB_NR_IRQS 32
  23
  24#define OPB_MLSASIER    0x04    /* MLS Accumulated Status IER */
  25#define OPB_MLSIR       0x50    /* MLS Interrupt Register */
  26#define OPB_MLSIER      0x54    /* MLS Interrupt Enable Register */
  27#define OPB_MLSIPR      0x58    /* MLS Interrupt Polarity Register */
  28#define OPB_MLSIIR      0x5c    /* MLS Interrupt Inputs Register */
  29
  30static int opb_index = 0;
  31
  32struct opb_pic {
  33        struct irq_host *host;
  34        void *regs;
  35        int index;
  36        spinlock_t lock;
  37};
  38
  39static u32 opb_in(struct opb_pic *opb, int offset)
  40{
  41        return in_be32(opb->regs + offset);
  42}
  43
  44static void opb_out(struct opb_pic *opb, int offset, u32 val)
  45{
  46        out_be32(opb->regs + offset, val);
  47}
  48
  49static void opb_unmask_irq(struct irq_data *d)
  50{
  51        struct opb_pic *opb;
  52        unsigned long flags;
  53        u32 ier, bitset;
  54
  55        opb = d->chip_data;
  56        bitset = (1 << (31 - irqd_to_hwirq(d)));
  57
  58        spin_lock_irqsave(&opb->lock, flags);
  59
  60        ier = opb_in(opb, OPB_MLSIER);
  61        opb_out(opb, OPB_MLSIER, ier | bitset);
  62        ier = opb_in(opb, OPB_MLSIER);
  63
  64        spin_unlock_irqrestore(&opb->lock, flags);
  65}
  66
  67static void opb_mask_irq(struct irq_data *d)
  68{
  69        struct opb_pic *opb;
  70        unsigned long flags;
  71        u32 ier, mask;
  72
  73        opb = d->chip_data;
  74        mask = ~(1 << (31 - irqd_to_hwirq(d)));
  75
  76        spin_lock_irqsave(&opb->lock, flags);
  77
  78        ier = opb_in(opb, OPB_MLSIER);
  79        opb_out(opb, OPB_MLSIER, ier & mask);
  80        ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
  81
  82        spin_unlock_irqrestore(&opb->lock, flags);
  83}
  84
  85static void opb_ack_irq(struct irq_data *d)
  86{
  87        struct opb_pic *opb;
  88        unsigned long flags;
  89        u32 bitset;
  90
  91        opb = d->chip_data;
  92        bitset = (1 << (31 - irqd_to_hwirq(d)));
  93
  94        spin_lock_irqsave(&opb->lock, flags);
  95
  96        opb_out(opb, OPB_MLSIR, bitset);
  97        opb_in(opb, OPB_MLSIR); // Flush posted writes
  98
  99        spin_unlock_irqrestore(&opb->lock, flags);
 100}
 101
 102static void opb_mask_ack_irq(struct irq_data *d)
 103{
 104        struct opb_pic *opb;
 105        unsigned long flags;
 106        u32 bitset;
 107        u32 ier, ir;
 108
 109        opb = d->chip_data;
 110        bitset = (1 << (31 - irqd_to_hwirq(d)));
 111
 112        spin_lock_irqsave(&opb->lock, flags);
 113
 114        ier = opb_in(opb, OPB_MLSIER);
 115        opb_out(opb, OPB_MLSIER, ier & ~bitset);
 116        ier = opb_in(opb, OPB_MLSIER); // Flush posted writes
 117
 118        opb_out(opb, OPB_MLSIR, bitset);
 119        ir = opb_in(opb, OPB_MLSIR); // Flush posted writes
 120
 121        spin_unlock_irqrestore(&opb->lock, flags);
 122}
 123
 124static int opb_set_irq_type(struct irq_data *d, unsigned int flow)
 125{
 126        struct opb_pic *opb;
 127        unsigned long flags;
 128        int invert, ipr, mask, bit;
 129
 130        opb = d->chip_data;
 131
 132        /* The only information we're interested in in the type is whether it's
 133         * a high or low trigger. For high triggered interrupts, the polarity
 134         * set for it in the MLS Interrupt Polarity Register is 0, for low
 135         * interrupts it's 1 so that the proper input in the MLS Interrupt Input
 136         * Register is interrupted as asserting the interrupt. */
 137
 138        switch (flow) {
 139                case IRQ_TYPE_NONE:
 140                        opb_mask_irq(d);
 141                        return 0;
 142
 143                case IRQ_TYPE_LEVEL_HIGH:
 144                        invert = 0;
 145                        break;
 146
 147                case IRQ_TYPE_LEVEL_LOW:
 148                        invert = 1;
 149                        break;
 150
 151                default:
 152                        return -EINVAL;
 153        }
 154
 155        bit = (1 << (31 - irqd_to_hwirq(d)));
 156        mask = ~bit;
 157
 158        spin_lock_irqsave(&opb->lock, flags);
 159
 160        ipr = opb_in(opb, OPB_MLSIPR);
 161        ipr = (ipr & mask) | (invert ? bit : 0);
 162        opb_out(opb, OPB_MLSIPR, ipr);
 163        ipr = opb_in(opb, OPB_MLSIPR);  // Flush posted writes
 164
 165        spin_unlock_irqrestore(&opb->lock, flags);
 166
 167        /* Record the type in the interrupt descriptor */
 168        irqd_set_trigger_type(d, flow);
 169
 170        return 0;
 171}
 172
 173static struct irq_chip opb_irq_chip = {
 174        .name           = "OPB",
 175        .irq_mask       = opb_mask_irq,
 176        .irq_unmask     = opb_unmask_irq,
 177        .irq_mask_ack   = opb_mask_ack_irq,
 178        .irq_ack        = opb_ack_irq,
 179        .irq_set_type   = opb_set_irq_type
 180};
 181
 182static int opb_host_map(struct irq_host *host, unsigned int virq,
 183                irq_hw_number_t hwirq)
 184{
 185        struct opb_pic *opb;
 186
 187        opb = host->host_data;
 188
 189        /* Most of the important stuff is handled by the generic host code, like
 190         * the lookup, so just attach some info to the virtual irq */
 191
 192        irq_set_chip_data(virq, opb);
 193        irq_set_chip_and_handler(virq, &opb_irq_chip, handle_level_irq);
 194        irq_set_irq_type(virq, IRQ_TYPE_NONE);
 195
 196        return 0;
 197}
 198
 199static int opb_host_xlate(struct irq_host *host, struct device_node *dn,
 200                const u32 *intspec, unsigned int intsize,
 201                irq_hw_number_t *out_hwirq, unsigned int *out_type)
 202{
 203        /* Interrupt size must == 2 */
 204        BUG_ON(intsize != 2);
 205        *out_hwirq = intspec[0];
 206        *out_type = intspec[1];
 207        return 0;
 208}
 209
 210static struct irq_host_ops opb_host_ops = {
 211        .map = opb_host_map,
 212        .xlate = opb_host_xlate,
 213};
 214
 215irqreturn_t opb_irq_handler(int irq, void *private)
 216{
 217        struct opb_pic *opb;
 218        u32 ir, src, subvirq;
 219
 220        opb = (struct opb_pic *) private;
 221
 222        /* Read the OPB MLS Interrupt Register for
 223         * asserted interrupts */
 224        ir = opb_in(opb, OPB_MLSIR);
 225        if (!ir)
 226                return IRQ_NONE;
 227
 228        do {
 229                /* Get 1 - 32 source, *NOT* bit */
 230                src = 32 - ffs(ir);
 231
 232                /* Translate from the OPB's conception of interrupt number to
 233                 * Linux's virtual IRQ */
 234
 235                subvirq = irq_linear_revmap(opb->host, src);
 236
 237                generic_handle_irq(subvirq);
 238        } while ((ir = opb_in(opb, OPB_MLSIR)));
 239
 240        return IRQ_HANDLED;
 241}
 242
 243struct opb_pic *opb_pic_init_one(struct device_node *dn)
 244{
 245        struct opb_pic *opb;
 246        struct resource res;
 247
 248        if (of_address_to_resource(dn, 0, &res)) {
 249                printk(KERN_ERR "opb: Couldn't translate resource\n");
 250                return  NULL;
 251        }
 252
 253        opb = kzalloc(sizeof(struct opb_pic), GFP_KERNEL);
 254        if (!opb) {
 255                printk(KERN_ERR "opb: Failed to allocate opb struct!\n");
 256                return NULL;
 257        }
 258
 259        /* Get access to the OPB MMIO registers */
 260        opb->regs = ioremap(res.start + 0x10000, 0x1000);
 261        if (!opb->regs) {
 262                printk(KERN_ERR "opb: Failed to allocate register space!\n");
 263                goto free_opb;
 264        }
 265
 266        /* Allocate an irq host so that Linux knows that despite only
 267         * having one interrupt to issue, we're the controller for multiple
 268         * hardware IRQs, so later we can lookup their virtual IRQs. */
 269
 270        opb->host = irq_alloc_host(dn, IRQ_HOST_MAP_LINEAR,
 271                        OPB_NR_IRQS, &opb_host_ops, -1);
 272
 273        if (!opb->host) {
 274                printk(KERN_ERR "opb: Failed to allocate IRQ host!\n");
 275                goto free_regs;
 276        }
 277
 278        opb->index = opb_index++;
 279        spin_lock_init(&opb->lock);
 280        opb->host->host_data = opb;
 281
 282        /* Disable all interrupts by default */
 283        opb_out(opb, OPB_MLSASIER, 0);
 284        opb_out(opb, OPB_MLSIER, 0);
 285
 286        /* ACK any interrupts left by FW */
 287        opb_out(opb, OPB_MLSIR, 0xFFFFFFFF);
 288
 289        return opb;
 290
 291free_regs:
 292        iounmap(opb->regs);
 293free_opb:
 294        kfree(opb);
 295        return NULL;
 296}
 297
 298void __init opb_pic_init(void)
 299{
 300        struct device_node *dn;
 301        struct opb_pic *opb;
 302        int virq;
 303        int rc;
 304
 305        /* Call init_one for each OPB device */
 306        for_each_compatible_node(dn, NULL, "ibm,opb") {
 307
 308                /* Fill in an OPB struct */
 309                opb = opb_pic_init_one(dn);
 310                if (!opb) {
 311                        printk(KERN_WARNING "opb: Failed to init node, skipped!\n");
 312                        continue;
 313                }
 314
 315                /* Map / get opb's hardware virtual irq */
 316                virq = irq_of_parse_and_map(dn, 0);
 317                if (virq <= 0) {
 318                        printk("opb: irq_op_parse_and_map failed!\n");
 319                        continue;
 320                }
 321
 322                /* Attach opb interrupt handler to new virtual IRQ */
 323                rc = request_irq(virq, opb_irq_handler, IRQF_NO_THREAD,
 324                                 "OPB LS Cascade", opb);
 325                if (rc) {
 326                        printk("opb: request_irq failed: %d\n", rc);
 327                        continue;
 328                }
 329
 330                printk("OPB%d init with %d IRQs at %p\n", opb->index,
 331                                OPB_NR_IRQS, opb->regs);
 332        }
 333}
 334