1/* 2 * This file is subject to the terms and conditions of the GNU General Public 3 * License. See the file "COPYING" in the main directory of this archive 4 * for more details. 5 * 6 * arch/sh64/kernel/irq_cayman.c 7 * 8 * SH-5 Cayman Interrupt Support 9 * 10 * This file handles the board specific parts of the Cayman interrupt system 11 * 12 * Copyright (C) 2002 Stuart Menefy 13 */ 14 15#include <asm/irq.h> 16#include <asm/page.h> 17#include <asm/io.h> 18#include <linux/irq.h> 19#include <linux/interrupt.h> 20#include <linux/signal.h> 21#include <asm/cayman.h> 22 23unsigned long epld_virt; 24 25#define EPLD_BASE 0x04002000 26#define EPLD_STATUS_BASE (epld_virt + 0x10) 27#define EPLD_MASK_BASE (epld_virt + 0x20) 28 29/* Note the SMSC SuperIO chip and SMSC LAN chip interrupts are all muxed onto 30 the same SH-5 interrupt */ 31 32static irqreturn_t cayman_interrupt_smsc(int irq, void *dev_id) 33{ 34 printk(KERN_INFO "CAYMAN: spurious SMSC interrupt\n"); 35 return IRQ_NONE; 36} 37 38static irqreturn_t cayman_interrupt_pci2(int irq, void *dev_id) 39{ 40 printk(KERN_INFO "CAYMAN: spurious PCI interrupt, IRQ %d\n", irq); 41 return IRQ_NONE; 42} 43 44static struct irqaction cayman_action_smsc = { 45 .name = "Cayman SMSC Mux", 46 .handler = cayman_interrupt_smsc, 47 .flags = IRQF_DISABLED, 48}; 49 50static struct irqaction cayman_action_pci2 = { 51 .name = "Cayman PCI2 Mux", 52 .handler = cayman_interrupt_pci2, 53 .flags = IRQF_DISABLED, 54}; 55 56static void enable_cayman_irq(unsigned int irq) 57{ 58 unsigned long flags; 59 unsigned long mask; 60 unsigned int reg; 61 unsigned char bit; 62 63 irq -= START_EXT_IRQS; 64 reg = EPLD_MASK_BASE + ((irq / 8) << 2); 65 bit = 1<<(irq % 8); 66 local_irq_save(flags); 67 mask = ctrl_inl(reg); 68 mask |= bit; 69 ctrl_outl(mask, reg); 70 local_irq_restore(flags); 71} 72 73void disable_cayman_irq(unsigned int irq) 74{ 75 unsigned long flags; 76 unsigned long mask; 77 unsigned int reg; 78 unsigned char bit; 79 80 irq -= START_EXT_IRQS; 81 reg = EPLD_MASK_BASE + ((irq / 8) << 2); 82 bit = 1<<(irq % 8); 83 local_irq_save(flags); 84 mask = ctrl_inl(reg); 85 mask &= ~bit; 86 ctrl_outl(mask, reg); 87 local_irq_restore(flags); 88} 89 90static void ack_cayman_irq(unsigned int irq) 91{ 92 disable_cayman_irq(irq); 93} 94 95static void end_cayman_irq(unsigned int irq) 96{ 97 if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) 98 enable_cayman_irq(irq); 99} 100 101static unsigned int startup_cayman_irq(unsigned int irq) 102{ 103 enable_cayman_irq(irq); 104 return 0; /* never anything pending */ 105} 106 107static void shutdown_cayman_irq(unsigned int irq) 108{ 109 disable_cayman_irq(irq); 110} 111 112struct hw_interrupt_type cayman_irq_type = { 113 .typename = "Cayman-IRQ", 114 .startup = startup_cayman_irq, 115 .shutdown = shutdown_cayman_irq, 116 .enable = enable_cayman_irq, 117 .disable = disable_cayman_irq, 118 .ack = ack_cayman_irq, 119 .end = end_cayman_irq, 120}; 121 122int cayman_irq_demux(int evt) 123{ 124 int irq = intc_evt_to_irq[evt]; 125 126 if (irq == SMSC_IRQ) { 127 unsigned long status; 128 int i; 129 130 status = ctrl_inl(EPLD_STATUS_BASE) & 131 ctrl_inl(EPLD_MASK_BASE) & 0xff; 132 if (status == 0) { 133 irq = -1; 134 } else { 135 for (i=0; i<8; i++) { 136 if (status & (1<<i)) 137 break; 138 } 139 irq = START_EXT_IRQS + i; 140 } 141 } 142 143 if (irq == PCI2_IRQ) { 144 unsigned long status; 145 int i; 146 147 status = ctrl_inl(EPLD_STATUS_BASE + 3 * sizeof(u32)) & 148 ctrl_inl(EPLD_MASK_BASE + 3 * sizeof(u32)) & 0xff; 149 if (status == 0) { 150 irq = -1; 151 } else { 152 for (i=0; i<8; i++) { 153 if (status & (1<<i)) 154 break; 155 } 156 irq = START_EXT_IRQS + (3 * 8) + i; 157 } 158 } 159 160 return irq; 161} 162 163#if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL) 164int cayman_irq_describe(char* p, int irq) 165{ 166 if (irq < NR_INTC_IRQS) { 167 return intc_irq_describe(p, irq); 168 } else if (irq < NR_INTC_IRQS + 8) { 169 return sprintf(p, "(SMSC %d)", irq - NR_INTC_IRQS); 170 } else if ((irq >= NR_INTC_IRQS + 24) && (irq < NR_INTC_IRQS + 32)) { 171 return sprintf(p, "(PCI2 %d)", irq - (NR_INTC_IRQS + 24)); 172 } 173 174 return 0; 175} 176#endif 177 178void init_cayman_irq(void) 179{ 180 int i; 181 182 epld_virt = onchip_remap(EPLD_BASE, 1024, "EPLD"); 183 if (!epld_virt) { 184 printk(KERN_ERR "Cayman IRQ: Unable to remap EPLD\n"); 185 return; 186 } 187 188 for (i=0; i<NR_EXT_IRQS; i++) { 189 irq_desc[START_EXT_IRQS + i].chip = &cayman_irq_type; 190 } 191 192 /* Setup the SMSC interrupt */ 193 setup_irq(SMSC_IRQ, &cayman_action_smsc); 194 setup_irq(PCI2_IRQ, &cayman_action_pci2); 195} 196