1/* 2 * Interrupt routines for Gemini 3 * 4 * Copyright (C) 2001-2006 Storlink, Corp. 5 * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License as published by 9 * the Free Software Foundation; either version 2 of the License, or 10 * (at your option) any later version. 11 */ 12#include <linux/init.h> 13#include <linux/io.h> 14#include <linux/ioport.h> 15#include <linux/stddef.h> 16#include <linux/list.h> 17#include <linux/sched.h> 18#include <linux/cpu.h> 19 20#include <asm/irq.h> 21#include <asm/mach/irq.h> 22#include <asm/system_misc.h> 23#include <mach/hardware.h> 24 25#define IRQ_SOURCE(base_addr) (base_addr + 0x00) 26#define IRQ_MASK(base_addr) (base_addr + 0x04) 27#define IRQ_CLEAR(base_addr) (base_addr + 0x08) 28#define IRQ_TMODE(base_addr) (base_addr + 0x0C) 29#define IRQ_TLEVEL(base_addr) (base_addr + 0x10) 30#define IRQ_STATUS(base_addr) (base_addr + 0x14) 31#define FIQ_SOURCE(base_addr) (base_addr + 0x20) 32#define FIQ_MASK(base_addr) (base_addr + 0x24) 33#define FIQ_CLEAR(base_addr) (base_addr + 0x28) 34#define FIQ_TMODE(base_addr) (base_addr + 0x2C) 35#define FIQ_LEVEL(base_addr) (base_addr + 0x30) 36#define FIQ_STATUS(base_addr) (base_addr + 0x34) 37 38static void gemini_ack_irq(struct irq_data *d) 39{ 40 __raw_writel(1 << d->irq, IRQ_CLEAR(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 41} 42 43static void gemini_mask_irq(struct irq_data *d) 44{ 45 unsigned int mask; 46 47 mask = __raw_readl(IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 48 mask &= ~(1 << d->irq); 49 __raw_writel(mask, IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 50} 51 52static void gemini_unmask_irq(struct irq_data *d) 53{ 54 unsigned int mask; 55 56 mask = __raw_readl(IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 57 mask |= (1 << d->irq); 58 __raw_writel(mask, IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 59} 60 61static struct irq_chip gemini_irq_chip = { 62 .name = "INTC", 63 .irq_ack = gemini_ack_irq, 64 .irq_mask = gemini_mask_irq, 65 .irq_unmask = gemini_unmask_irq, 66}; 67 68static struct resource irq_resource = { 69 .name = "irq_handler", 70 .start = GEMINI_INTERRUPT_BASE, 71 .end = FIQ_STATUS(GEMINI_INTERRUPT_BASE) + 4, 72}; 73 74void __init gemini_init_irq(void) 75{ 76 unsigned int i, mode = 0, level = 0; 77 78 /* 79 * Disable the idle handler by default since it is buggy 80 * For more info see arch/arm/mach-gemini/idle.c 81 */ 82 cpu_idle_poll_ctrl(true); 83 84 request_resource(&iomem_resource, &irq_resource); 85 86 for (i = 0; i < NR_IRQS; i++) { 87 irq_set_chip(i, &gemini_irq_chip); 88 if((i >= IRQ_TIMER1 && i <= IRQ_TIMER3) || (i >= IRQ_SERIRQ0 && i <= IRQ_SERIRQ1)) { 89 irq_set_handler(i, handle_edge_irq); 90 mode |= 1 << i; 91 level |= 1 << i; 92 } else { 93 irq_set_handler(i, handle_level_irq); 94 } 95 irq_clear_status_flags(i, IRQ_NOREQUEST | IRQ_NOPROBE); 96 } 97 98 /* Disable all interrupts */ 99 __raw_writel(0, IRQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 100 __raw_writel(0, FIQ_MASK(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 101 102 /* Set interrupt mode */ 103 __raw_writel(mode, IRQ_TMODE(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 104 __raw_writel(level, IRQ_TLEVEL(IO_ADDRESS(GEMINI_INTERRUPT_BASE))); 105} 106