linux/arch/sh/boards/mach-cayman/irq.c
<<
>>
Prefs
   1/*
   2 * arch/sh/mach-cayman/irq.c - SH-5 Cayman Interrupt Support
   3 *
   4 * This file handles the board specific parts of the Cayman interrupt system
   5 *
   6 * Copyright (C) 2002 Stuart Menefy
   7 *
   8 * This file is subject to the terms and conditions of the GNU General Public
   9 * License.  See the file "COPYING" in the main directory of this archive
  10 * for more details.
  11 */
  12#include <linux/io.h>
  13#include <linux/irq.h>
  14#include <linux/interrupt.h>
  15#include <linux/signal.h>
  16#include <cpu/irq.h>
  17#include <asm/page.h>
  18
  19/* Setup for the SMSC FDC37C935 / LAN91C100FD */
  20#define SMSC_IRQ         IRQ_IRL1
  21
  22/* Setup for PCI Bus 2, which transmits interrupts via the EPLD */
  23#define PCI2_IRQ         IRQ_IRL3
  24
  25unsigned long epld_virt;
  26
  27#define EPLD_BASE        0x04002000
  28#define EPLD_STATUS_BASE (epld_virt + 0x10)
  29#define EPLD_MASK_BASE   (epld_virt + 0x20)
  30
  31/* Note the SMSC SuperIO chip and SMSC LAN chip interrupts are all muxed onto
  32   the same SH-5 interrupt */
  33
  34static irqreturn_t cayman_interrupt_smsc(int irq, void *dev_id)
  35{
  36        printk(KERN_INFO "CAYMAN: spurious SMSC interrupt\n");
  37        return IRQ_NONE;
  38}
  39
  40static irqreturn_t cayman_interrupt_pci2(int irq, void *dev_id)
  41{
  42        printk(KERN_INFO "CAYMAN: spurious PCI interrupt, IRQ %d\n", irq);
  43        return IRQ_NONE;
  44}
  45
  46static struct irqaction cayman_action_smsc = {
  47        .name           = "Cayman SMSC Mux",
  48        .handler        = cayman_interrupt_smsc,
  49};
  50
  51static struct irqaction cayman_action_pci2 = {
  52        .name           = "Cayman PCI2 Mux",
  53        .handler        = cayman_interrupt_pci2,
  54};
  55
  56static void enable_cayman_irq(struct irq_data *data)
  57{
  58        unsigned int irq = data->irq;
  59        unsigned long flags;
  60        unsigned long mask;
  61        unsigned int reg;
  62        unsigned char bit;
  63
  64        irq -= START_EXT_IRQS;
  65        reg = EPLD_MASK_BASE + ((irq / 8) << 2);
  66        bit = 1<<(irq % 8);
  67        local_irq_save(flags);
  68        mask = __raw_readl(reg);
  69        mask |= bit;
  70        __raw_writel(mask, reg);
  71        local_irq_restore(flags);
  72}
  73
  74static void disable_cayman_irq(struct irq_data *data)
  75{
  76        unsigned int irq = data->irq;
  77        unsigned long flags;
  78        unsigned long mask;
  79        unsigned int reg;
  80        unsigned char bit;
  81
  82        irq -= START_EXT_IRQS;
  83        reg = EPLD_MASK_BASE + ((irq / 8) << 2);
  84        bit = 1<<(irq % 8);
  85        local_irq_save(flags);
  86        mask = __raw_readl(reg);
  87        mask &= ~bit;
  88        __raw_writel(mask, reg);
  89        local_irq_restore(flags);
  90}
  91
  92struct irq_chip cayman_irq_type = {
  93        .name           = "Cayman-IRQ",
  94        .irq_unmask     = enable_cayman_irq,
  95        .irq_mask       = disable_cayman_irq,
  96};
  97
  98int cayman_irq_demux(int evt)
  99{
 100        int irq = intc_evt_to_irq[evt];
 101
 102        if (irq == SMSC_IRQ) {
 103                unsigned long status;
 104                int i;
 105
 106                status = __raw_readl(EPLD_STATUS_BASE) &
 107                         __raw_readl(EPLD_MASK_BASE) & 0xff;
 108                if (status == 0) {
 109                        irq = -1;
 110                } else {
 111                        for (i=0; i<8; i++) {
 112                                if (status & (1<<i))
 113                                        break;
 114                        }
 115                        irq = START_EXT_IRQS + i;
 116                }
 117        }
 118
 119        if (irq == PCI2_IRQ) {
 120                unsigned long status;
 121                int i;
 122
 123                status = __raw_readl(EPLD_STATUS_BASE + 3 * sizeof(u32)) &
 124                         __raw_readl(EPLD_MASK_BASE + 3 * sizeof(u32)) & 0xff;
 125                if (status == 0) {
 126                        irq = -1;
 127                } else {
 128                        for (i=0; i<8; i++) {
 129                                if (status & (1<<i))
 130                                        break;
 131                        }
 132                        irq = START_EXT_IRQS + (3 * 8) + i;
 133                }
 134        }
 135
 136        return irq;
 137}
 138
 139void init_cayman_irq(void)
 140{
 141        int i;
 142
 143        epld_virt = (unsigned long)ioremap_nocache(EPLD_BASE, 1024);
 144        if (!epld_virt) {
 145                printk(KERN_ERR "Cayman IRQ: Unable to remap EPLD\n");
 146                return;
 147        }
 148
 149        for (i = 0; i < NR_EXT_IRQS; i++) {
 150                irq_set_chip_and_handler(START_EXT_IRQS + i,
 151                                         &cayman_irq_type, handle_level_irq);
 152        }
 153
 154        /* Setup the SMSC interrupt */
 155        setup_irq(SMSC_IRQ, &cayman_action_smsc);
 156        setup_irq(PCI2_IRQ, &cayman_action_pci2);
 157}
 158