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#include <linux/slab.h>
  21
  22#include <asm/io.h>
  23#include <asm/prom.h>
  24#include <asm/cpm2.h>
  25
  26#include "pq2.h"
  27
  28static DEFINE_RAW_SPINLOCK(pci_pic_lock);
  29
  30struct pq2ads_pci_pic {
  31        struct device_node *node;
  32        struct irq_host *host;
  33
  34        struct {
  35                u32 stat;
  36                u32 mask;
  37        } __iomem *regs;
  38};
  39
  40#define NUM_IRQS 32
  41
  42static void pq2ads_pci_mask_irq(struct irq_data *d)
  43{
  44        struct pq2ads_pci_pic *priv = irq_data_get_irq_chip_data(d);
  45        int irq = NUM_IRQS - irqd_to_hwirq(d) - 1;
  46
  47        if (irq != -1) {
  48                unsigned long flags;
  49                raw_spin_lock_irqsave(&pci_pic_lock, flags);
  50
  51                setbits32(&priv->regs->mask, 1 << irq);
  52                mb();
  53
  54                raw_spin_unlock_irqrestore(&pci_pic_lock, flags);
  55        }
  56}
  57
  58static void pq2ads_pci_unmask_irq(struct irq_data *d)
  59{
  60        struct pq2ads_pci_pic *priv = irq_data_get_irq_chip_data(d);
  61        int irq = NUM_IRQS - irqd_to_hwirq(d) - 1;
  62
  63        if (irq != -1) {
  64                unsigned long flags;
  65
  66                raw_spin_lock_irqsave(&pci_pic_lock, flags);
  67                clrbits32(&priv->regs->mask, 1 << irq);
  68                raw_spin_unlock_irqrestore(&pci_pic_lock, flags);
  69        }
  70}
  71
  72static struct irq_chip pq2ads_pci_ic = {
  73        .name = "PQ2 ADS PCI",
  74        .irq_mask = pq2ads_pci_mask_irq,
  75        .irq_mask_ack = pq2ads_pci_mask_irq,
  76        .irq_ack = pq2ads_pci_mask_irq,
  77        .irq_unmask = pq2ads_pci_unmask_irq,
  78        .irq_enable = pq2ads_pci_unmask_irq,
  79        .irq_disable = pq2ads_pci_mask_irq
  80};
  81
  82static void pq2ads_pci_irq_demux(unsigned int irq, struct irq_desc *desc)
  83{
  84        struct pq2ads_pci_pic *priv = irq_desc_get_handler_data(desc);
  85        u32 stat, mask, pend;
  86        int bit;
  87
  88        for (;;) {
  89                stat = in_be32(&priv->regs->stat);
  90                mask = in_be32(&priv->regs->mask);
  91
  92                pend = stat & ~mask;
  93
  94                if (!pend)
  95                        break;
  96
  97                for (bit = 0; pend != 0; ++bit, pend <<= 1) {
  98                        if (pend & 0x80000000) {
  99                                int virq = irq_linear_revmap(priv->host, bit);
 100                                generic_handle_irq(virq);
 101                        }
 102                }
 103        }
 104}
 105
 106static int pci_pic_host_map(struct irq_host *h, unsigned int virq,
 107                            irq_hw_number_t hw)
 108{
 109        irq_set_status_flags(virq, IRQ_LEVEL);
 110        irq_set_chip_data(virq, h->host_data);
 111        irq_set_chip_and_handler(virq, &pq2ads_pci_ic, handle_level_irq);
 112        return 0;
 113}
 114
 115static struct irq_host_ops pci_pic_host_ops = {
 116        .map = pci_pic_host_map,
 117};
 118
 119int __init pq2ads_pci_init_irq(void)
 120{
 121        struct pq2ads_pci_pic *priv;
 122        struct irq_host *host;
 123        struct device_node *np;
 124        int ret = -ENODEV;
 125        int irq;
 126
 127        np = of_find_compatible_node(NULL, NULL, "fsl,pq2ads-pci-pic");
 128        if (!np) {
 129                printk(KERN_ERR "No pci pic node in device tree.\n");
 130                of_node_put(np);
 131                goto out;
 132        }
 133
 134        irq = irq_of_parse_and_map(np, 0);
 135        if (irq == NO_IRQ) {
 136                printk(KERN_ERR "No interrupt in pci pic node.\n");
 137                of_node_put(np);
 138                goto out;
 139        }
 140
 141        priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 142        if (!priv) {
 143                of_node_put(np);
 144                ret = -ENOMEM;
 145                goto out_unmap_irq;
 146        }
 147
 148        /* PCI interrupt controller registers: status and mask */
 149        priv->regs = of_iomap(np, 0);
 150        if (!priv->regs) {
 151                printk(KERN_ERR "Cannot map PCI PIC registers.\n");
 152                goto out_free_bootmem;
 153        }
 154
 155        /* mask all PCI interrupts */
 156        out_be32(&priv->regs->mask, ~0);
 157        mb();
 158
 159        host = irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, NUM_IRQS,
 160                              &pci_pic_host_ops, NUM_IRQS);
 161        if (!host) {
 162                ret = -ENOMEM;
 163                goto out_unmap_regs;
 164        }
 165
 166        host->host_data = priv;
 167
 168        priv->host = host;
 169        host->host_data = priv;
 170        irq_set_handler_data(irq, priv);
 171        irq_set_chained_handler(irq, pq2ads_pci_irq_demux);
 172
 173        of_node_put(np);
 174        return 0;
 175
 176out_unmap_regs:
 177        iounmap(priv->regs);
 178out_free_bootmem:
 179        free_bootmem((unsigned long)priv,
 180                     sizeof(struct pq2ads_pci_pic));
 181        of_node_put(np);
 182out_unmap_irq:
 183        irq_dispose_mapping(irq);
 184out:
 185        return ret;
 186}
 187