1/* 2 * Copyright (C) 2006 Atmel Corporation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License version 2 as 6 * published by the Free Software Foundation. 7 */ 8 9#include <linux/clk.h> 10#include <linux/err.h> 11#include <linux/init.h> 12#include <linux/interrupt.h> 13#include <linux/irq.h> 14#include <linux/platform_device.h> 15 16#include <asm/io.h> 17 18#include "intc.h" 19 20struct intc { 21 void __iomem *regs; 22 struct irq_chip chip; 23}; 24 25extern struct platform_device at32_intc0_device; 26 27/* 28 * TODO: We may be able to implement mask/unmask by setting IxM flags 29 * in the status register. 30 */ 31static void intc_mask_irq(unsigned int irq) 32{ 33 34} 35 36static void intc_unmask_irq(unsigned int irq) 37{ 38 39} 40 41static struct intc intc0 = { 42 .chip = { 43 .name = "intc", 44 .mask = intc_mask_irq, 45 .unmask = intc_unmask_irq, 46 }, 47}; 48 49/* 50 * All interrupts go via intc at some point. 51 */ 52asmlinkage void do_IRQ(int level, struct pt_regs *regs) 53{ 54 struct irq_desc *desc; 55 struct pt_regs *old_regs; 56 unsigned int irq; 57 unsigned long status_reg; 58 59 local_irq_disable(); 60 61 old_regs = set_irq_regs(regs); 62 63 irq_enter(); 64 65 irq = intc_readl(&intc0, INTCAUSE0 - 4 * level); 66 desc = irq_desc + irq; 67 desc->handle_irq(irq, desc); 68 69 /* 70 * Clear all interrupt level masks so that we may handle 71 * interrupts during softirq processing. If this is a nested 72 * interrupt, interrupts must stay globally disabled until we 73 * return. 74 */ 75 status_reg = sysreg_read(SR); 76 status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M) 77 | SYSREG_BIT(I2M) | SYSREG_BIT(I3M)); 78 sysreg_write(SR, status_reg); 79 80 irq_exit(); 81 82 set_irq_regs(old_regs); 83} 84 85void __init init_IRQ(void) 86{ 87 extern void _evba(void); 88 extern void irq_level0(void); 89 struct resource *regs; 90 struct clk *pclk; 91 unsigned int i; 92 u32 offset, readback; 93 94 regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0); 95 if (!regs) { 96 printk(KERN_EMERG "intc: no mmio resource defined\n"); 97 goto fail; 98 } 99 pclk = clk_get(&at32_intc0_device.dev, "pclk"); 100 if (IS_ERR(pclk)) { 101 printk(KERN_EMERG "intc: no clock defined\n"); 102 goto fail; 103 } 104 105 clk_enable(pclk); 106 107 intc0.regs = ioremap(regs->start, regs->end - regs->start + 1); 108 if (!intc0.regs) { 109 printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n", 110 (unsigned long)regs->start); 111 goto fail; 112 } 113 114 /* 115 * Initialize all interrupts to level 0 (lowest priority). The 116 * priority level may be changed by calling 117 * irq_set_priority(). 118 * 119 */ 120 offset = (unsigned long)&irq_level0 - (unsigned long)&_evba; 121 for (i = 0; i < NR_INTERNAL_IRQS; i++) { 122 intc_writel(&intc0, INTPR0 + 4 * i, offset); 123 readback = intc_readl(&intc0, INTPR0 + 4 * i); 124 if (readback == offset) 125 set_irq_chip_and_handler(i, &intc0.chip, 126 handle_simple_irq); 127 } 128 129 /* Unmask all interrupt levels */ 130 sysreg_write(SR, (sysreg_read(SR) 131 & ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M))); 132 133 return; 134 135fail: 136 panic("Interrupt controller initialization failed!\n"); 137} 138 139unsigned long intc_get_pending(int group) 140{ 141 return intc_readl(&intc0, INTREQ0 + 4 * group); 142} 143