1/* 2 * Copyright (C) 2006, 2008 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#include <linux/syscore_ops.h> 16#include <linux/export.h> 17 18#include <asm/io.h> 19 20#include "intc.h" 21 22struct intc { 23 void __iomem *regs; 24 struct irq_chip chip; 25#ifdef CONFIG_PM 26 unsigned long suspend_ipr; 27 unsigned long saved_ipr[64]; 28#endif 29}; 30 31extern struct platform_device at32_intc0_device; 32 33/* 34 * TODO: We may be able to implement mask/unmask by setting IxM flags 35 * in the status register. 36 */ 37static void intc_mask_irq(struct irq_data *d) 38{ 39 40} 41 42static void intc_unmask_irq(struct irq_data *d) 43{ 44 45} 46 47static struct intc intc0 = { 48 .chip = { 49 .name = "intc", 50 .irq_mask = intc_mask_irq, 51 .irq_unmask = intc_unmask_irq, 52 }, 53}; 54 55/* 56 * All interrupts go via intc at some point. 57 */ 58asmlinkage void do_IRQ(int level, struct pt_regs *regs) 59{ 60 struct pt_regs *old_regs; 61 unsigned int irq; 62 unsigned long status_reg; 63 64 local_irq_disable(); 65 66 old_regs = set_irq_regs(regs); 67 68 irq_enter(); 69 70 irq = intc_readl(&intc0, INTCAUSE0 - 4 * level); 71 generic_handle_irq(irq); 72 73 /* 74 * Clear all interrupt level masks so that we may handle 75 * interrupts during softirq processing. If this is a nested 76 * interrupt, interrupts must stay globally disabled until we 77 * return. 78 */ 79 status_reg = sysreg_read(SR); 80 status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M) 81 | SYSREG_BIT(I2M) | SYSREG_BIT(I3M)); 82 sysreg_write(SR, status_reg); 83 84 irq_exit(); 85 86 set_irq_regs(old_regs); 87} 88 89void __init init_IRQ(void) 90{ 91 extern void _evba(void); 92 extern void irq_level0(void); 93 struct resource *regs; 94 struct clk *pclk; 95 unsigned int i; 96 u32 offset, readback; 97 98 regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0); 99 if (!regs) { 100 printk(KERN_EMERG "intc: no mmio resource defined\n"); 101 goto fail; 102 } 103 pclk = clk_get(&at32_intc0_device.dev, "pclk"); 104 if (IS_ERR(pclk)) { 105 printk(KERN_EMERG "intc: no clock defined\n"); 106 goto fail; 107 } 108 109 clk_enable(pclk); 110 111 intc0.regs = ioremap(regs->start, resource_size(regs)); 112 if (!intc0.regs) { 113 printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n", 114 (unsigned long)regs->start); 115 goto fail; 116 } 117 118 /* 119 * Initialize all interrupts to level 0 (lowest priority). The 120 * priority level may be changed by calling 121 * irq_set_priority(). 122 * 123 */ 124 offset = (unsigned long)&irq_level0 - (unsigned long)&_evba; 125 for (i = 0; i < NR_INTERNAL_IRQS; i++) { 126 intc_writel(&intc0, INTPR0 + 4 * i, offset); 127 readback = intc_readl(&intc0, INTPR0 + 4 * i); 128 if (readback == offset) 129 irq_set_chip_and_handler(i, &intc0.chip, 130 handle_simple_irq); 131 } 132 133 /* Unmask all interrupt levels */ 134 sysreg_write(SR, (sysreg_read(SR) 135 & ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M))); 136 137 return; 138 139fail: 140 panic("Interrupt controller initialization failed!\n"); 141} 142 143#ifdef CONFIG_PM 144void intc_set_suspend_handler(unsigned long offset) 145{ 146 intc0.suspend_ipr = offset; 147} 148 149static int intc_suspend(void) 150{ 151 int i; 152 153 if (unlikely(!irqs_disabled())) { 154 pr_err("intc_suspend: called with interrupts enabled\n"); 155 return -EINVAL; 156 } 157 158 if (unlikely(!intc0.suspend_ipr)) { 159 pr_err("intc_suspend: suspend_ipr not initialized\n"); 160 return -EINVAL; 161 } 162 163 for (i = 0; i < 64; i++) { 164 intc0.saved_ipr[i] = intc_readl(&intc0, INTPR0 + 4 * i); 165 intc_writel(&intc0, INTPR0 + 4 * i, intc0.suspend_ipr); 166 } 167 168 return 0; 169} 170 171static void intc_resume(void) 172{ 173 int i; 174 175 for (i = 0; i < 64; i++) 176 intc_writel(&intc0, INTPR0 + 4 * i, intc0.saved_ipr[i]); 177} 178#else 179#define intc_suspend NULL 180#define intc_resume NULL 181#endif 182 183static struct syscore_ops intc_syscore_ops = { 184 .suspend = intc_suspend, 185 .resume = intc_resume, 186}; 187 188static int __init intc_init_syscore(void) 189{ 190 register_syscore_ops(&intc_syscore_ops); 191 192 return 0; 193} 194device_initcall(intc_init_syscore); 195 196unsigned long intc_get_pending(unsigned int group) 197{ 198 return intc_readl(&intc0, INTREQ0 + 4 * group); 199} 200EXPORT_SYMBOL_GPL(intc_get_pending); 201