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/sysdev.h> 16 17#include <asm/io.h> 18 19#include "intc.h" 20 21struct intc { 22 void __iomem *regs; 23 struct irq_chip chip; 24 struct sys_device sysdev; 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(unsigned int irq) 38{ 39 40} 41 42static void intc_unmask_irq(unsigned int irq) 43{ 44 45} 46 47static struct intc intc0 = { 48 .chip = { 49 .name = "intc", 50 .mask = intc_mask_irq, 51 .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 irq_desc *desc; 61 struct pt_regs *old_regs; 62 unsigned int irq; 63 unsigned long status_reg; 64 65 local_irq_disable(); 66 67 old_regs = set_irq_regs(regs); 68 69 irq_enter(); 70 71 irq = intc_readl(&intc0, INTCAUSE0 - 4 * level); 72 desc = irq_desc + irq; 73 desc->handle_irq(irq, desc); 74 75 /* 76 * Clear all interrupt level masks so that we may handle 77 * interrupts during softirq processing. If this is a nested 78 * interrupt, interrupts must stay globally disabled until we 79 * return. 80 */ 81 status_reg = sysreg_read(SR); 82 status_reg &= ~(SYSREG_BIT(I0M) | SYSREG_BIT(I1M) 83 | SYSREG_BIT(I2M) | SYSREG_BIT(I3M)); 84 sysreg_write(SR, status_reg); 85 86 irq_exit(); 87 88 set_irq_regs(old_regs); 89} 90 91void __init init_IRQ(void) 92{ 93 extern void _evba(void); 94 extern void irq_level0(void); 95 struct resource *regs; 96 struct clk *pclk; 97 unsigned int i; 98 u32 offset, readback; 99 100 regs = platform_get_resource(&at32_intc0_device, IORESOURCE_MEM, 0); 101 if (!regs) { 102 printk(KERN_EMERG "intc: no mmio resource defined\n"); 103 goto fail; 104 } 105 pclk = clk_get(&at32_intc0_device.dev, "pclk"); 106 if (IS_ERR(pclk)) { 107 printk(KERN_EMERG "intc: no clock defined\n"); 108 goto fail; 109 } 110 111 clk_enable(pclk); 112 113 intc0.regs = ioremap(regs->start, regs->end - regs->start + 1); 114 if (!intc0.regs) { 115 printk(KERN_EMERG "intc: failed to map registers (0x%08lx)\n", 116 (unsigned long)regs->start); 117 goto fail; 118 } 119 120 /* 121 * Initialize all interrupts to level 0 (lowest priority). The 122 * priority level may be changed by calling 123 * irq_set_priority(). 124 * 125 */ 126 offset = (unsigned long)&irq_level0 - (unsigned long)&_evba; 127 for (i = 0; i < NR_INTERNAL_IRQS; i++) { 128 intc_writel(&intc0, INTPR0 + 4 * i, offset); 129 readback = intc_readl(&intc0, INTPR0 + 4 * i); 130 if (readback == offset) 131 set_irq_chip_and_handler(i, &intc0.chip, 132 handle_simple_irq); 133 } 134 135 /* Unmask all interrupt levels */ 136 sysreg_write(SR, (sysreg_read(SR) 137 & ~(SR_I3M | SR_I2M | SR_I1M | SR_I0M))); 138 139 return; 140 141fail: 142 panic("Interrupt controller initialization failed!\n"); 143} 144 145#ifdef CONFIG_PM 146void intc_set_suspend_handler(unsigned long offset) 147{ 148 intc0.suspend_ipr = offset; 149} 150 151static int intc_suspend(struct sys_device *sdev, pm_message_t state) 152{ 153 struct intc *intc = container_of(sdev, struct intc, sysdev); 154 int i; 155 156 if (unlikely(!irqs_disabled())) { 157 pr_err("intc_suspend: called with interrupts enabled\n"); 158 return -EINVAL; 159 } 160 161 if (unlikely(!intc->suspend_ipr)) { 162 pr_err("intc_suspend: suspend_ipr not initialized\n"); 163 return -EINVAL; 164 } 165 166 for (i = 0; i < 64; i++) { 167 intc->saved_ipr[i] = intc_readl(intc, INTPR0 + 4 * i); 168 intc_writel(intc, INTPR0 + 4 * i, intc->suspend_ipr); 169 } 170 171 return 0; 172} 173 174static int intc_resume(struct sys_device *sdev) 175{ 176 struct intc *intc = container_of(sdev, struct intc, sysdev); 177 int i; 178 179 WARN_ON(!irqs_disabled()); 180 181 for (i = 0; i < 64; i++) 182 intc_writel(intc, INTPR0 + 4 * i, intc->saved_ipr[i]); 183 184 return 0; 185} 186#else 187#define intc_suspend NULL 188#define intc_resume NULL 189#endif 190 191static struct sysdev_class intc_class = { 192 .name = "intc", 193 .suspend = intc_suspend, 194 .resume = intc_resume, 195}; 196 197static int __init intc_init_sysdev(void) 198{ 199 int ret; 200 201 ret = sysdev_class_register(&intc_class); 202 if (ret) 203 return ret; 204 205 intc0.sysdev.id = 0; 206 intc0.sysdev.cls = &intc_class; 207 ret = sysdev_register(&intc0.sysdev); 208 209 return ret; 210} 211device_initcall(intc_init_sysdev); 212 213unsigned long intc_get_pending(unsigned int group) 214{ 215 return intc_readl(&intc0, INTREQ0 + 4 * group); 216} 217EXPORT_SYMBOL_GPL(intc_get_pending); 218