linux/arch/powerpc/platforms/82xx/pq2ads-pci-pic.c
<<
>>
Prefs
   1/*
   2 * PQ2 ADS-style PCI interrupt controller
   3 *
   4 * Copyright 2007 Freescale Semiconductor, Inc.
   5 * Author: Scott Wood <scottwood@freescale.com>
   6 *
   7 * Loosely based on mpc82xx ADS support by Vitaly Bordug <vbordug@ru.mvista.com>
   8 * Copyright (c) 2006 MontaVista Software, Inc.
   9 *
  10 * This program is free software; you can redistribute it and/or modify it
  11 * under the terms of the GNU General Public License version 2 as published
  12 * by the Free Software Foundation.
  13 */
  14
  15#include <linux/init.h>
  16#include <linux/spinlock.h>
  17#include <linux/irq.h>
  18#include <linux/types.h>
  19#include <linux/bootmem.h>
  20
  21#include <asm/io.h>
  22#include <asm/prom.h>
  23#include <asm/cpm2.h>
  24
  25#include "pq2.h"
  26
  27static DEFINE_SPINLOCK(pci_pic_lock);
  28
  29struct pq2ads_pci_pic {
  30        struct device_node *node;
  31        struct irq_host *host;
  32
  33        struct {
  34                u32 stat;
  35                u32 mask;
  36        } __iomem *regs;
  37};
  38
  39#define NUM_IRQS 32
  40
  41static void pq2ads_pci_mask_irq(unsigned int virq)
  42{
  43        struct pq2ads_pci_pic *priv = get_irq_chip_data(virq);
  44        int irq = NUM_IRQS - virq_to_hw(virq) - 1;
  45
  46        if (irq != -1) {
  47                unsigned long flags;
  48                spin_lock_irqsave(&pci_pic_lock, flags);
  49
  50                setbits32(&priv->regs->mask, 1 << irq);
  51                mb();
  52
  53                spin_unlock_irqrestore(&pci_pic_lock, flags);
  54        }
  55}
  56
  57static void pq2ads_pci_unmask_irq(unsigned int virq)
  58{
  59        struct pq2ads_pci_pic *priv = get_irq_chip_data(virq);
  60        int irq = NUM_IRQS - virq_to_hw(virq) - 1;
  61
  62        if (irq != -1) {
  63                unsigned long flags;
  64
  65                spin_lock_irqsave(&pci_pic_lock, flags);
  66                clrbits32(&priv->regs->mask, 1 << irq);
  67                spin_unlock_irqrestore(&pci_pic_lock, flags);
  68        }
  69}
  70
  71static struct irq_chip pq2ads_pci_ic = {
  72        .typename = "PQ2 ADS PCI",
  73        .name = "PQ2 ADS PCI",
  74        .end = pq2ads_pci_unmask_irq,
  75        .mask = pq2ads_pci_mask_irq,
  76        .mask_ack = pq2ads_pci_mask_irq,
  77        .ack = pq2ads_pci_mask_irq,
  78        .unmask = pq2ads_pci_unmask_irq,
  79        .enable = pq2ads_pci_unmask_irq,
  80        .disable = pq2ads_pci_mask_irq
  81};
  82
  83static void pq2ads_pci_irq_demux(unsigned int irq, struct irq_desc *desc)
  84{
  85        struct pq2ads_pci_pic *priv = desc->handler_data;
  86        u32 stat, mask, pend;
  87        int bit;
  88
  89        for (;;) {
  90                stat = in_be32(&priv->regs->stat);
  91                mask = in_be32(&priv->regs->mask);
  92
  93                pend = stat & ~mask;
  94
  95                if (!pend)
  96                        break;
  97
  98                for (bit = 0; pend != 0; ++bit, pend <<= 1) {
  99                        if (pend & 0x80000000) {
 100                                int virq = irq_linear_revmap(priv->host, bit);
 101                                generic_handle_irq(virq);
 102                        }
 103                }
 104        }
 105}
 106
 107static int pci_pic_host_map(struct irq_host *h, unsigned int virq,
 108                            irq_hw_number_t hw)
 109{
 110        get_irq_desc(virq)->status |= IRQ_LEVEL;
 111        set_irq_chip_data(virq, h->host_data);
 112        set_irq_chip_and_handler(virq, &pq2ads_pci_ic, handle_level_irq);
 113        return 0;
 114}
 115
 116static void pci_host_unmap(struct irq_host *h, unsigned int virq)
 117{
 118        /* remove chip and handler */
 119        set_irq_chip_data(virq, NULL);
 120        set_irq_chip(virq, NULL);
 121}
 122
 123static struct irq_host_ops pci_pic_host_ops = {
 124        .map = pci_pic_host_map,
 125        .unmap = pci_host_unmap,
 126};
 127
 128int __init pq2ads_pci_init_irq(void)
 129{
 130        struct pq2ads_pci_pic *priv;
 131        struct irq_host *host;
 132        struct device_node *np;
 133        int ret = -ENODEV;
 134        int irq;
 135
 136        np = of_find_compatible_node(NULL, NULL, "fsl,pq2ads-pci-pic");
 137        if (!np) {
 138                printk(KERN_ERR "No pci pic node in device tree.\n");
 139                of_node_put(np);
 140                goto out;
 141        }
 142
 143        irq = irq_of_parse_and_map(np, 0);
 144        if (irq == NO_IRQ) {
 145                printk(KERN_ERR "No interrupt in pci pic node.\n");
 146                of_node_put(np);
 147                goto out;
 148        }
 149
 150        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 151        if (!priv) {
 152                of_node_put(np);
 153                ret = -ENOMEM;
 154                goto out_unmap_irq;
 155        }
 156
 157        /* PCI interrupt controller registers: status and mask */
 158        priv->regs = of_iomap(np, 0);
 159        if (!priv->regs) {
 160                printk(KERN_ERR "Cannot map PCI PIC registers.\n");
 161                goto out_free_bootmem;
 162        }
 163
 164        /* mask all PCI interrupts */
 165        out_be32(&priv->regs->mask, ~0);
 166        mb();
 167
 168        host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, NUM_IRQS,
 169                              &pci_pic_host_ops, NUM_IRQS);
 170        if (!host) {
 171                ret = -ENOMEM;
 172                goto out_unmap_regs;
 173        }
 174
 175        host->host_data = priv;
 176
 177        priv->host = host;
 178        host->host_data = priv;
 179        set_irq_data(irq, priv);
 180        set_irq_chained_handler(irq, pq2ads_pci_irq_demux);
 181
 182        of_node_put(np);
 183        return 0;
 184
 185out_unmap_regs:
 186        iounmap(priv->regs);
 187out_free_bootmem:
 188        free_bootmem((unsigned long)priv,
 189                     sizeof(struct pq2ads_pci_pic));
 190        of_node_put(np);
 191out_unmap_irq:
 192        irq_dispose_mapping(irq);
 193out:
 194        return ret;
 195}
 196