| /* |
| * Copyright 2002 Momentum Computer |
| * Author: mdharm@momenco.com |
| * Copyright (C) 2004 Ralf Baechle <ralf@linux-mips.org> |
| * |
| * This program is free software; you can redistribute it and/or modify it |
| * under the terms of the GNU General Public License as published by the |
| * Free Software Foundation; either version 2 of the License, or (at your |
| * option) any later version. |
| */ |
| #include <linux/module.h> |
| #include <linux/interrupt.h> |
| #include <linux/kernel.h> |
| #include <linux/kernel_stat.h> |
| #include <linux/mv643xx.h> |
| #include <linux/sched.h> |
| |
| #include <asm/ptrace.h> |
| #include <asm/io.h> |
| #include <asm/irq.h> |
| #include <asm/marvell.h> |
| |
| static unsigned int irq_base; |
| |
| static inline int ls1bit32(unsigned int x) |
| { |
| int b = 31, s; |
| |
| s = 16; if (x << 16 == 0) s = 0; b -= s; x <<= s; |
| s = 8; if (x << 8 == 0) s = 0; b -= s; x <<= s; |
| s = 4; if (x << 4 == 0) s = 0; b -= s; x <<= s; |
| s = 2; if (x << 2 == 0) s = 0; b -= s; x <<= s; |
| s = 1; if (x << 1 == 0) s = 0; b -= s; |
| |
| return b; |
| } |
| |
| /* mask off an interrupt -- 1 is enable, 0 is disable */ |
| static inline void mask_mv64340_irq(unsigned int irq) |
| { |
| uint32_t value; |
| |
| if (irq < (irq_base + 32)) { |
| value = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); |
| value &= ~(1 << (irq - irq_base)); |
| MV_WRITE(MV64340_INTERRUPT0_MASK_0_LOW, value); |
| } else { |
| value = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); |
| value &= ~(1 << (irq - irq_base - 32)); |
| MV_WRITE(MV64340_INTERRUPT0_MASK_0_HIGH, value); |
| } |
| } |
| |
| /* unmask an interrupt -- 1 is enable, 0 is disable */ |
| static inline void unmask_mv64340_irq(unsigned int irq) |
| { |
| uint32_t value; |
| |
| if (irq < (irq_base + 32)) { |
| value = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); |
| value |= 1 << (irq - irq_base); |
| MV_WRITE(MV64340_INTERRUPT0_MASK_0_LOW, value); |
| } else { |
| value = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); |
| value |= 1 << (irq - irq_base - 32); |
| MV_WRITE(MV64340_INTERRUPT0_MASK_0_HIGH, value); |
| } |
| } |
| |
| /* |
| * Enables the IRQ on Marvell Chip |
| */ |
| static void enable_mv64340_irq(unsigned int irq) |
| { |
| unmask_mv64340_irq(irq); |
| } |
| |
| /* |
| * Initialize the IRQ on Marvell Chip |
| */ |
| static unsigned int startup_mv64340_irq(unsigned int irq) |
| { |
| unmask_mv64340_irq(irq); |
| return 0; |
| } |
| |
| /* |
| * Disables the IRQ on Marvell Chip |
| */ |
| static void disable_mv64340_irq(unsigned int irq) |
| { |
| mask_mv64340_irq(irq); |
| } |
| |
| /* |
| * Masks and ACKs an IRQ |
| */ |
| static void mask_and_ack_mv64340_irq(unsigned int irq) |
| { |
| mask_mv64340_irq(irq); |
| } |
| |
| /* |
| * End IRQ processing |
| */ |
| static void end_mv64340_irq(unsigned int irq) |
| { |
| if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) |
| unmask_mv64340_irq(irq); |
| } |
| |
| /* |
| * Interrupt handler for interrupts coming from the Marvell chip. |
| * It could be built in ethernet ports etc... |
| */ |
| void ll_mv64340_irq(struct pt_regs *regs) |
| { |
| unsigned int irq_src_low, irq_src_high; |
| unsigned int irq_mask_low, irq_mask_high; |
| |
| /* read the interrupt status registers */ |
| irq_mask_low = MV_READ(MV64340_INTERRUPT0_MASK_0_LOW); |
| irq_mask_high = MV_READ(MV64340_INTERRUPT0_MASK_0_HIGH); |
| irq_src_low = MV_READ(MV64340_MAIN_INTERRUPT_CAUSE_LOW); |
| irq_src_high = MV_READ(MV64340_MAIN_INTERRUPT_CAUSE_HIGH); |
| |
| /* mask for just the interrupts we want */ |
| irq_src_low &= irq_mask_low; |
| irq_src_high &= irq_mask_high; |
| |
| if (irq_src_low) |
| do_IRQ(ls1bit32(irq_src_low) + irq_base, regs); |
| else |
| do_IRQ(ls1bit32(irq_src_high) + irq_base + 32, regs); |
| } |
| |
| #define shutdown_mv64340_irq disable_mv64340_irq |
| |
| struct irq_chip mv64340_irq_type = { |
| .typename = "MV-64340", |
| .startup = startup_mv64340_irq, |
| .shutdown = shutdown_mv64340_irq, |
| .enable = enable_mv64340_irq, |
| .disable = disable_mv64340_irq, |
| .ack = mask_and_ack_mv64340_irq, |
| .end = end_mv64340_irq, |
| }; |
| |
| void __init mv64340_irq_init(unsigned int base) |
| { |
| int i; |
| |
| /* Reset irq handlers pointers to NULL */ |
| for (i = base; i < base + 64; i++) { |
| irq_desc[i].status = IRQ_DISABLED; |
| irq_desc[i].action = 0; |
| irq_desc[i].depth = 2; |
| irq_desc[i].chip = &mv64340_irq_type; |
| } |
| |
| irq_base = base; |
| } |