/* * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. * * arch/sh64/kernel/irq_cayman.c * * SH-5 Cayman Interrupt Support * * This file handles the board specific parts of the Cayman interrupt system * * Copyright (C) 2002 Stuart Menefy */ #include #include #include #include #include #include #include #include unsigned long epld_virt; #define EPLD_BASE 0x04002000 #define EPLD_STATUS_BASE (epld_virt + 0x10) #define EPLD_MASK_BASE (epld_virt + 0x20) /* Note the SMSC SuperIO chip and SMSC LAN chip interrupts are all muxed onto the same SH-5 interrupt */ static void cayman_interrupt_smsc(int irq, void *dev_id, struct pt_regs *regs) { printk(KERN_INFO "CAYMAN: spurious SMSC interrupt\n"); } static void cayman_interrupt_pci2(int irq, void *dev_id, struct pt_regs *regs) { printk(KERN_INFO "CAYMAN: spurious PCI interrupt, IRQ %d\n", irq); } static struct irqaction cayman_action_smsc = { .name = "Cayman SMSC Mux", .handler = cayman_interrupt_smsc, .flags = SA_INTERRUPT, }; static struct irqaction cayman_action_pci2 = { .name = "Cayman PCI2 Mux", .handler = cayman_interrupt_pci2, .flags = SA_INTERRUPT, }; static void enable_cayman_irq(unsigned int irq) { unsigned long flags; unsigned long mask; unsigned int reg; unsigned char bit; irq -= START_EXT_IRQS; reg = EPLD_MASK_BASE + ((irq / 8) << 2); bit = 1<<(irq % 8); save_and_cli(flags); mask = ctrl_inl(reg); mask |= bit; ctrl_outl(mask, reg); restore_flags(flags); } void disable_cayman_irq(unsigned int irq) { unsigned long flags; unsigned long mask; unsigned int reg; unsigned char bit; irq -= START_EXT_IRQS; reg = EPLD_MASK_BASE + ((irq / 8) << 2); bit = 1<<(irq % 8); save_and_cli(flags); mask = ctrl_inl(reg); mask &= ~bit; ctrl_outl(mask, reg); restore_flags(flags); } static void ack_cayman_irq(unsigned int irq) { disable_cayman_irq(irq); } static void end_cayman_irq(unsigned int irq) { if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) enable_cayman_irq(irq); } static unsigned int startup_cayman_irq(unsigned int irq) { enable_cayman_irq(irq); return 0; /* never anything pending */ } static void shutdown_cayman_irq(unsigned int irq) { disable_cayman_irq(irq); } struct hw_interrupt_type cayman_irq_type = { .typename = "Cayman-IRQ", .startup = startup_cayman_irq, .shutdown = shutdown_cayman_irq, .enable = enable_cayman_irq, .disable = disable_cayman_irq, .ack = ack_cayman_irq, .end = end_cayman_irq, }; int cayman_irq_demux(int evt) { int irq = intc_evt_to_irq[evt]; if (irq == SMSC_IRQ) { unsigned long status; int i; status = ctrl_inl(EPLD_STATUS_BASE) & ctrl_inl(EPLD_MASK_BASE) & 0xff; if (status == 0) { irq = -1; } else { for (i=0; i<8; i++) { if (status & (1<= NR_INTC_IRQS + 24) && (irq < NR_INTC_IRQS + 32)) { return sprintf(p, "(PCI2 %d)", irq - (NR_INTC_IRQS + 24)); } return 0; } #endif void init_cayman_irq(void) { int i; epld_virt = onchip_remap(EPLD_BASE, 1024, "EPLD"); if (!epld_virt) { printk(KERN_ERR "Cayman IRQ: Unable to remap EPLD\n"); return; } for (i=0; i