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_domain *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_domain *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 const struct irq_domain_ops opb_host_ops = {
 200        .map = opb_host_map,
 201        .xlate = irq_domain_xlate_twocell,
 202};
 203
 204irqreturn_t opb_irq_handler(int irq, void *private)
 205{
 206        struct opb_pic *opb;
 207        u32 ir, src, subvirq;
 208
 209        opb = (struct opb_pic *) private;
 210
 211        /* Read the OPB MLS Interrupt Register for
 212         * asserted interrupts */
 213        ir = opb_in(opb, OPB_MLSIR);
 214        if (!ir)
 215                return IRQ_NONE;
 216
 217        do {
 218                /* Get 1 - 32 source, *NOT* bit */
 219                src = 32 - ffs(ir);
 220
 221                /* Translate from the OPB's conception of interrupt number to
 222                 * Linux's virtual IRQ */
 223
 224                subvirq = irq_linear_revmap(opb->host, src);
 225
 226                generic_handle_irq(subvirq);
 227        } while ((ir = opb_in(opb, OPB_MLSIR)));
 228
 229        return IRQ_HANDLED;
 230}
 231
 232struct opb_pic *opb_pic_init_one(struct device_node *dn)
 233{
 234        struct opb_pic *opb;
 235        struct resource res;
 236
 237        if (of_address_to_resource(dn, 0, &res)) {
 238                printk(KERN_ERR "opb: Couldn't translate resource\n");
 239                return  NULL;
 240        }
 241
 242        opb = kzalloc(sizeof(struct opb_pic), GFP_KERNEL);
 243        if (!opb) {
 244                printk(KERN_ERR "opb: Failed to allocate opb struct!\n");
 245                return NULL;
 246        }
 247
 248        /* Get access to the OPB MMIO registers */
 249        opb->regs = ioremap(res.start + 0x10000, 0x1000);
 250        if (!opb->regs) {
 251                printk(KERN_ERR "opb: Failed to allocate register space!\n");
 252                goto free_opb;
 253        }
 254
 255        /* Allocate an irq domain so that Linux knows that despite only
 256         * having one interrupt to issue, we're the controller for multiple
 257         * hardware IRQs, so later we can lookup their virtual IRQs. */
 258
 259        opb->host = irq_domain_add_linear(dn, OPB_NR_IRQS, &opb_host_ops, opb);
 260        if (!opb->host) {
 261                printk(KERN_ERR "opb: Failed to allocate IRQ host!\n");
 262                goto free_regs;
 263        }
 264
 265        opb->index = opb_index++;
 266        spin_lock_init(&opb->lock);
 267
 268        /* Disable all interrupts by default */
 269        opb_out(opb, OPB_MLSASIER, 0);
 270        opb_out(opb, OPB_MLSIER, 0);
 271
 272        /* ACK any interrupts left by FW */
 273        opb_out(opb, OPB_MLSIR, 0xFFFFFFFF);
 274
 275        return opb;
 276
 277free_regs:
 278        iounmap(opb->regs);
 279free_opb:
 280        kfree(opb);
 281        return NULL;
 282}
 283
 284void __init opb_pic_init(void)
 285{
 286        struct device_node *dn;
 287        struct opb_pic *opb;
 288        int virq;
 289        int rc;
 290
 291        /* Call init_one for each OPB device */
 292        for_each_compatible_node(dn, NULL, "ibm,opb") {
 293
 294                /* Fill in an OPB struct */
 295                opb = opb_pic_init_one(dn);
 296                if (!opb) {
 297                        printk(KERN_WARNING "opb: Failed to init node, skipped!\n");
 298                        continue;
 299                }
 300
 301                /* Map / get opb's hardware virtual irq */
 302                virq = irq_of_parse_and_map(dn, 0);
 303                if (virq <= 0) {
 304                        printk("opb: irq_op_parse_and_map failed!\n");
 305                        continue;
 306                }
 307
 308                /* Attach opb interrupt handler to new virtual IRQ */
 309                rc = request_irq(virq, opb_irq_handler, IRQF_NO_THREAD,
 310                                 "OPB LS Cascade", opb);
 311                if (rc) {
 312                        printk("opb: request_irq failed: %d\n", rc);
 313                        continue;
 314                }
 315
 316                printk("OPB%d init with %d IRQs at %p\n", opb->index,
 317                                OPB_NR_IRQS, opb->regs);
 318        }
 319}
 320