/* * Interrupt routines for Gemini * * Copyright (C) 2001-2006 Storlink, Corp. * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> * * 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/init.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/stddef.h> #include <linux/list.h> #include <linux/sched.h> #include <linux/cpu.h> #include <asm/irq.h> #include <asm/mach/irq.h> #include <asm/system_misc.h> #include <mach/hardware.h> #define IRQ_SOURCE(base_addr) (base_addr + 0x00) #define IRQ_MASK(base_addr) (base_addr + 0x04) #define IRQ_CLEAR(base_addr) (base_addr + 0x08) #define IRQ_TMODE(base_addr) (base_addr + 0x0C) #define IRQ_TLEVEL(base_addr) (base_addr + 0x10) #define IRQ_STATUS(base_addr) (base_addr + 0x14) #define FIQ_SOURCE(base_addr) (base_addr + 0x20) #define FIQ_MASK(base_addr) (base_addr + 0x24) #define FIQ_CLEAR(base_addr) (base_addr + 0x28) #define FIQ_TMODE(base_addr) (base_addr + 0x2C) #define FIQ_LEVEL(base_addr) (base_addr + 0x30) #define FIQ_STATUS(base_addr) (base_addr + 0x34) static void gemini_ack_irq(struct irq_data *d) { __raw_writel(1 << d->irq, IRQ_CLEAR(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); } static void gemini_mask_irq(struct irq_data *d) { unsigned int mask; mask = __raw_readl(IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); mask &= ~(1 << d->irq); __raw_writel(mask, IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); } static void gemini_unmask_irq(struct irq_data *d) { unsigned int mask; mask = __raw_readl(IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); mask |= (1 << d->irq); __raw_writel(mask, IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); } static struct irq_chip gemini_irq_chip = { .name = "INTC", .irq_ack = gemini_ack_irq, .irq_mask = gemini_mask_irq, .irq_unmask = gemini_unmask_irq, }; static struct resource irq_resource = { .name = "irq_handler", .start = GEMINI_INTERRUPT_BASE, .end = FIQ_STATUS(GEMINI_INTERRUPT_BASE) + 4, }; void __init gemini_init_irq(void) { unsigned int i, mode = 0, level = 0; /* * Disable the idle handler by default since it is buggy * For more info see arch/arm/mach-gemini/idle.c */ cpu_idle_poll_ctrl(true); request_resource(&iomem_resource, &irq_resource); for (i = 0; i < NR_IRQS; i++) { irq_set_chip(i, &gemini_irq_chip); if((i >= IRQ_TIMER1 && i <= IRQ_TIMER3) || (i >= IRQ_SERIRQ0 && i <= IRQ_SERIRQ1)) { irq_set_handler(i, handle_edge_irq); mode |= 1 << i; level |= 1 << i; } else { irq_set_handler(i, handle_level_irq); } set_irq_flags(i, IRQF_VALID | IRQF_PROBE); } /* Disable all interrupts */ __raw_writel(0, IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); __raw_writel(0, FIQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); /* Set interrupt mode */ __raw_writel(mode, IRQ_TMODE(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); __raw_writel(level, IRQ_TLEVEL(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); }