1/* linux/arch/arm/mach-s3c2416/irq.c 2 * 3 * Copyright (c) 2009 Yauhen Kharuzhy <jekhor@gmail.com>, 4 * as part of OpenInkpot project 5 * Copyright (c) 2009 Promwad Innovation Company 6 * Yauhen Kharuzhy <yauhen.kharuzhy@promwad.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software 20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 * 22*/ 23 24#include <linux/init.h> 25#include <linux/module.h> 26#include <linux/interrupt.h> 27#include <linux/ioport.h> 28#include <linux/device.h> 29#include <linux/io.h> 30 31#include <mach/hardware.h> 32#include <asm/irq.h> 33 34#include <asm/mach/irq.h> 35 36#include <mach/regs-irq.h> 37#include <mach/regs-gpio.h> 38 39#include <plat/cpu.h> 40#include <plat/pm.h> 41#include <plat/irq.h> 42 43#define INTMSK(start, end) ((1 << ((end) + 1 - (start))) - 1) 44 45static inline void s3c2416_irq_demux(unsigned int irq, unsigned int len) 46{ 47 unsigned int subsrc, submsk; 48 unsigned int end; 49 50 /* read the current pending interrupts, and the mask 51 * for what it is available */ 52 53 subsrc = __raw_readl(S3C2410_SUBSRCPND); 54 submsk = __raw_readl(S3C2410_INTSUBMSK); 55 56 subsrc &= ~submsk; 57 subsrc >>= (irq - S3C2410_IRQSUB(0)); 58 subsrc &= (1 << len)-1; 59 60 end = len + irq; 61 62 for (; irq < end && subsrc; irq++) { 63 if (subsrc & 1) 64 generic_handle_irq(irq); 65 66 subsrc >>= 1; 67 } 68} 69 70/* WDT/AC97 sub interrupts */ 71 72static void s3c2416_irq_demux_wdtac97(unsigned int irq, struct irq_desc *desc) 73{ 74 s3c2416_irq_demux(IRQ_S3C2443_WDT, 4); 75} 76 77#define INTMSK_WDTAC97 (1UL << (IRQ_WDT - IRQ_EINT0)) 78#define SUBMSK_WDTAC97 INTMSK(IRQ_S3C2443_WDT, IRQ_S3C2443_AC97) 79 80static void s3c2416_irq_wdtac97_mask(struct irq_data *data) 81{ 82 s3c_irqsub_mask(data->irq, INTMSK_WDTAC97, SUBMSK_WDTAC97); 83} 84 85static void s3c2416_irq_wdtac97_unmask(struct irq_data *data) 86{ 87 s3c_irqsub_unmask(data->irq, INTMSK_WDTAC97); 88} 89 90static void s3c2416_irq_wdtac97_ack(struct irq_data *data) 91{ 92 s3c_irqsub_maskack(data->irq, INTMSK_WDTAC97, SUBMSK_WDTAC97); 93} 94 95static struct irq_chip s3c2416_irq_wdtac97 = { 96 .irq_mask = s3c2416_irq_wdtac97_mask, 97 .irq_unmask = s3c2416_irq_wdtac97_unmask, 98 .irq_ack = s3c2416_irq_wdtac97_ack, 99}; 100 101/* LCD sub interrupts */ 102 103static void s3c2416_irq_demux_lcd(unsigned int irq, struct irq_desc *desc) 104{ 105 s3c2416_irq_demux(IRQ_S3C2443_LCD1, 4); 106} 107 108#define INTMSK_LCD (1UL << (IRQ_LCD - IRQ_EINT0)) 109#define SUBMSK_LCD INTMSK(IRQ_S3C2443_LCD1, IRQ_S3C2443_LCD4) 110 111static void s3c2416_irq_lcd_mask(struct irq_data *data) 112{ 113 s3c_irqsub_mask(data->irq, INTMSK_LCD, SUBMSK_LCD); 114} 115 116static void s3c2416_irq_lcd_unmask(struct irq_data *data) 117{ 118 s3c_irqsub_unmask(data->irq, INTMSK_LCD); 119} 120 121static void s3c2416_irq_lcd_ack(struct irq_data *data) 122{ 123 s3c_irqsub_maskack(data->irq, INTMSK_LCD, SUBMSK_LCD); 124} 125 126static struct irq_chip s3c2416_irq_lcd = { 127 .irq_mask = s3c2416_irq_lcd_mask, 128 .irq_unmask = s3c2416_irq_lcd_unmask, 129 .irq_ack = s3c2416_irq_lcd_ack, 130}; 131 132/* DMA sub interrupts */ 133 134static void s3c2416_irq_demux_dma(unsigned int irq, struct irq_desc *desc) 135{ 136 s3c2416_irq_demux(IRQ_S3C2443_DMA0, 6); 137} 138 139#define INTMSK_DMA (1UL << (IRQ_S3C2443_DMA - IRQ_EINT0)) 140#define SUBMSK_DMA INTMSK(IRQ_S3C2443_DMA0, IRQ_S3C2443_DMA5) 141 142 143static void s3c2416_irq_dma_mask(struct irq_data *data) 144{ 145 s3c_irqsub_mask(data->irq, INTMSK_DMA, SUBMSK_DMA); 146} 147 148static void s3c2416_irq_dma_unmask(struct irq_data *data) 149{ 150 s3c_irqsub_unmask(data->irq, INTMSK_DMA); 151} 152 153static void s3c2416_irq_dma_ack(struct irq_data *data) 154{ 155 s3c_irqsub_maskack(data->irq, INTMSK_DMA, SUBMSK_DMA); 156} 157 158static struct irq_chip s3c2416_irq_dma = { 159 .irq_mask = s3c2416_irq_dma_mask, 160 .irq_unmask = s3c2416_irq_dma_unmask, 161 .irq_ack = s3c2416_irq_dma_ack, 162}; 163 164/* UART3 sub interrupts */ 165 166static void s3c2416_irq_demux_uart3(unsigned int irq, struct irq_desc *desc) 167{ 168 s3c2416_irq_demux(IRQ_S3C2443_RX3, 3); 169} 170 171#define INTMSK_UART3 (1UL << (IRQ_S3C2443_UART3 - IRQ_EINT0)) 172#define SUBMSK_UART3 (0x7 << (IRQ_S3C2443_RX3 - S3C2410_IRQSUB(0))) 173 174static void s3c2416_irq_uart3_mask(struct irq_data *data) 175{ 176 s3c_irqsub_mask(data->irq, INTMSK_UART3, SUBMSK_UART3); 177} 178 179static void s3c2416_irq_uart3_unmask(struct irq_data *data) 180{ 181 s3c_irqsub_unmask(data->irq, INTMSK_UART3); 182} 183 184static void s3c2416_irq_uart3_ack(struct irq_data *data) 185{ 186 s3c_irqsub_maskack(data->irq, INTMSK_UART3, SUBMSK_UART3); 187} 188 189static struct irq_chip s3c2416_irq_uart3 = { 190 .irq_mask = s3c2416_irq_uart3_mask, 191 .irq_unmask = s3c2416_irq_uart3_unmask, 192 .irq_ack = s3c2416_irq_uart3_ack, 193}; 194 195/* IRQ initialisation code */ 196 197static int __init s3c2416_add_sub(unsigned int base, 198 void (*demux)(unsigned int, 199 struct irq_desc *), 200 struct irq_chip *chip, 201 unsigned int start, unsigned int end) 202{ 203 unsigned int irqno; 204 205 irq_set_chip_and_handler(base, &s3c_irq_level_chip, handle_level_irq); 206 irq_set_chained_handler(base, demux); 207 208 for (irqno = start; irqno <= end; irqno++) { 209 irq_set_chip_and_handler(irqno, chip, handle_level_irq); 210 set_irq_flags(irqno, IRQF_VALID); 211 } 212 213 return 0; 214} 215 216static int __init s3c2416_irq_add(struct device *dev, 217 struct subsys_interface *sif) 218{ 219 printk(KERN_INFO "S3C2416: IRQ Support\n"); 220 221 s3c2416_add_sub(IRQ_LCD, s3c2416_irq_demux_lcd, &s3c2416_irq_lcd, 222 IRQ_S3C2443_LCD2, IRQ_S3C2443_LCD4); 223 224 s3c2416_add_sub(IRQ_S3C2443_DMA, s3c2416_irq_demux_dma, 225 &s3c2416_irq_dma, IRQ_S3C2443_DMA0, IRQ_S3C2443_DMA5); 226 227 s3c2416_add_sub(IRQ_S3C2443_UART3, s3c2416_irq_demux_uart3, 228 &s3c2416_irq_uart3, 229 IRQ_S3C2443_RX3, IRQ_S3C2443_ERR3); 230 231 s3c2416_add_sub(IRQ_WDT, s3c2416_irq_demux_wdtac97, 232 &s3c2416_irq_wdtac97, 233 IRQ_S3C2443_WDT, IRQ_S3C2443_AC97); 234 235 return 0; 236} 237 238static struct subsys_interface s3c2416_irq_interface = { 239 .name = "s3c2416_irq", 240 .subsys = &s3c2416_subsys, 241 .add_dev = s3c2416_irq_add, 242}; 243 244static int __init s3c2416_irq_init(void) 245{ 246 return subsys_interface_register(&s3c2416_irq_interface); 247} 248 249arch_initcall(s3c2416_irq_init); 250 251