]> git.karo-electronics.de Git - mv-sheeva.git/blobdiff - arch/mips/ath79/irq.c
Merge tag 'v2.6.38' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
[mv-sheeva.git] / arch / mips / ath79 / irq.c
diff --git a/arch/mips/ath79/irq.c b/arch/mips/ath79/irq.c
new file mode 100644 (file)
index 0000000..1bf7f71
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ *  Atheros AR71xx/AR724x/AR913x specific interrupt handling
+ *
+ *  Copyright (C) 2008-2010 Gabor Juhos <juhosg@openwrt.org>
+ *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
+ *
+ *  Parts of this file are based on Atheros' 2.6.15 BSP
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License version 2 as published
+ *  by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+
+#include <asm/mach-ath79/ath79.h>
+#include <asm/mach-ath79/ar71xx_regs.h>
+#include "common.h"
+
+static unsigned int ath79_ip2_flush_reg;
+static unsigned int ath79_ip3_flush_reg;
+
+static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+       void __iomem *base = ath79_reset_base;
+       u32 pending;
+
+       pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
+                 __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+
+       if (pending & MISC_INT_UART)
+               generic_handle_irq(ATH79_MISC_IRQ_UART);
+
+       else if (pending & MISC_INT_DMA)
+               generic_handle_irq(ATH79_MISC_IRQ_DMA);
+
+       else if (pending & MISC_INT_PERFC)
+               generic_handle_irq(ATH79_MISC_IRQ_PERFC);
+
+       else if (pending & MISC_INT_TIMER)
+               generic_handle_irq(ATH79_MISC_IRQ_TIMER);
+
+       else if (pending & MISC_INT_OHCI)
+               generic_handle_irq(ATH79_MISC_IRQ_OHCI);
+
+       else if (pending & MISC_INT_ERROR)
+               generic_handle_irq(ATH79_MISC_IRQ_ERROR);
+
+       else if (pending & MISC_INT_GPIO)
+               generic_handle_irq(ATH79_MISC_IRQ_GPIO);
+
+       else if (pending & MISC_INT_WDOG)
+               generic_handle_irq(ATH79_MISC_IRQ_WDOG);
+
+       else
+               spurious_interrupt();
+}
+
+static void ar71xx_misc_irq_unmask(unsigned int irq)
+{
+       void __iomem *base = ath79_reset_base;
+       u32 t;
+
+       irq -= ATH79_MISC_IRQ_BASE;
+
+       t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+       __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+
+       /* flush write */
+       __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+}
+
+static void ar71xx_misc_irq_mask(unsigned int irq)
+{
+       void __iomem *base = ath79_reset_base;
+       u32 t;
+
+       irq -= ATH79_MISC_IRQ_BASE;
+
+       t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+       __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+
+       /* flush write */
+       __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+}
+
+static void ar724x_misc_irq_ack(unsigned int irq)
+{
+       void __iomem *base = ath79_reset_base;
+       u32 t;
+
+       irq -= ATH79_MISC_IRQ_BASE;
+
+       t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
+       __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
+
+       /* flush write */
+       __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
+}
+
+static struct irq_chip ath79_misc_irq_chip = {
+       .name           = "MISC",
+       .unmask         = ar71xx_misc_irq_unmask,
+       .mask           = ar71xx_misc_irq_mask,
+};
+
+static void __init ath79_misc_irq_init(void)
+{
+       void __iomem *base = ath79_reset_base;
+       int i;
+
+       __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
+       __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
+
+       if (soc_is_ar71xx() || soc_is_ar913x())
+               ath79_misc_irq_chip.mask_ack = ar71xx_misc_irq_mask;
+       else if (soc_is_ar724x())
+               ath79_misc_irq_chip.ack = ar724x_misc_irq_ack;
+       else
+               BUG();
+
+       for (i = ATH79_MISC_IRQ_BASE;
+            i < ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT; i++) {
+               irq_desc[i].status = IRQ_DISABLED;
+               set_irq_chip_and_handler(i, &ath79_misc_irq_chip,
+                                        handle_level_irq);
+       }
+
+       set_irq_chained_handler(ATH79_CPU_IRQ_MISC, ath79_misc_irq_handler);
+}
+
+asmlinkage void plat_irq_dispatch(void)
+{
+       unsigned long pending;
+
+       pending = read_c0_status() & read_c0_cause() & ST0_IM;
+
+       if (pending & STATUSF_IP7)
+               do_IRQ(ATH79_CPU_IRQ_TIMER);
+
+       else if (pending & STATUSF_IP2) {
+               ath79_ddr_wb_flush(ath79_ip2_flush_reg);
+               do_IRQ(ATH79_CPU_IRQ_IP2);
+       }
+
+       else if (pending & STATUSF_IP4)
+               do_IRQ(ATH79_CPU_IRQ_GE0);
+
+       else if (pending & STATUSF_IP5)
+               do_IRQ(ATH79_CPU_IRQ_GE1);
+
+       else if (pending & STATUSF_IP3) {
+               ath79_ddr_wb_flush(ath79_ip3_flush_reg);
+               do_IRQ(ATH79_CPU_IRQ_USB);
+       }
+
+       else if (pending & STATUSF_IP6)
+               do_IRQ(ATH79_CPU_IRQ_MISC);
+
+       else
+               spurious_interrupt();
+}
+
+void __init arch_init_irq(void)
+{
+       if (soc_is_ar71xx()) {
+               ath79_ip2_flush_reg = AR71XX_DDR_REG_FLUSH_PCI;
+               ath79_ip3_flush_reg = AR71XX_DDR_REG_FLUSH_USB;
+       } else if (soc_is_ar724x()) {
+               ath79_ip2_flush_reg = AR724X_DDR_REG_FLUSH_PCIE;
+               ath79_ip3_flush_reg = AR724X_DDR_REG_FLUSH_USB;
+       } else if (soc_is_ar913x()) {
+               ath79_ip2_flush_reg = AR913X_DDR_REG_FLUSH_WMAC;
+               ath79_ip3_flush_reg = AR913X_DDR_REG_FLUSH_USB;
+       } else
+               BUG();
+
+       cp0_perfcount_irq = ATH79_MISC_IRQ_PERFC;
+       mips_cpu_irq_init();
+       ath79_misc_irq_init();
+}