Merge branch 'fix/hda' into for-linus
[pandora-kernel.git] / arch / arm / plat-s5pc1xx / irq-eint.c
1 /*
2  * linux/arch/arm/plat-s5pc1xx/irq-eint.c
3  *
4  *  Copyright 2009 Samsung Electronics Co.
5  *  Byungho Min <bhmin@samsung.com>
6  *  Kyungin Park <kyungmin.park@samsung.com>
7  *
8  * Based on plat-s3c64xx/irq-eint.c
9  *
10  * S5PC1XX - Interrupt handling for IRQ_EINT(x)
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License version 2 as
14  * published by the Free Software Foundation.
15  */
16
17 #include <linux/kernel.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/io.h>
21 #include <linux/sysdev.h>
22 #include <linux/pm.h>
23 #include <linux/gpio.h>
24
25 #include <asm/hardware/vic.h>
26
27 #include <mach/map.h>
28
29 #include <plat/gpio-cfg.h>
30 #include <plat/gpio-ext.h>
31 #include <plat/pm.h>
32 #include <plat/regs-gpio.h>
33 #include <plat/regs-irqtype.h>
34
35 /*
36  * bank is a group of external interrupt
37  * bank0 means EINT0 ... EINT7
38  * bank1 means EINT8 ... EINT15
39  * bank2 means EINT16 ... EINT23
40  * bank3 means EINT24 ... EINT31
41  */
42
43 static inline int s3c_get_eint(unsigned int irq)
44 {
45         int real;
46
47         if (irq < IRQ_EINT16_31)
48                 real = (irq - IRQ_EINT0);
49         else
50                 real = (irq - S3C_IRQ_EINT_BASE) + IRQ_EINT16_31 - IRQ_EINT0;
51
52         return real;
53 }
54
55 static inline int s3c_get_bank(unsigned int irq)
56 {
57         return s3c_get_eint(irq) >> 3;
58 }
59
60 static inline int s3c_eint_to_bit(unsigned int irq)
61 {
62         int real, bit;
63
64         real = s3c_get_eint(irq);
65         bit = 1 << (real & (8 - 1));
66
67         return bit;
68 }
69
70 static inline void s3c_irq_eint_mask(unsigned int irq)
71 {
72         u32 mask;
73         u32 bank = s3c_get_bank(irq);
74
75         mask = __raw_readl(S5PC1XX_WKUP_INT_MASK(bank));
76         mask |= s3c_eint_to_bit(irq);
77         __raw_writel(mask, S5PC1XX_WKUP_INT_MASK(bank));
78 }
79
80 static void s3c_irq_eint_unmask(unsigned int irq)
81 {
82         u32 mask;
83         u32 bank = s3c_get_bank(irq);
84
85         mask = __raw_readl(S5PC1XX_WKUP_INT_MASK(bank));
86         mask &= ~(s3c_eint_to_bit(irq));
87         __raw_writel(mask, S5PC1XX_WKUP_INT_MASK(bank));
88 }
89
90 static inline void s3c_irq_eint_ack(unsigned int irq)
91 {
92         u32 bank = s3c_get_bank(irq);
93
94         __raw_writel(s3c_eint_to_bit(irq), S5PC1XX_WKUP_INT_PEND(bank));
95 }
96
97 static void s3c_irq_eint_maskack(unsigned int irq)
98 {
99         /* compiler should in-line these */
100         s3c_irq_eint_mask(irq);
101         s3c_irq_eint_ack(irq);
102 }
103
104 static int s3c_irq_eint_set_type(unsigned int irq, unsigned int type)
105 {
106         u32 bank = s3c_get_bank(irq);
107         int real = s3c_get_eint(irq);
108         int gpio, shift, sfn;
109         u32 ctrl, con = 0;
110
111         switch (type) {
112         case IRQ_TYPE_NONE:
113                 printk(KERN_WARNING "No edge setting!\n");
114                 break;
115
116         case IRQ_TYPE_EDGE_RISING:
117                 con = S5PC1XX_WKUP_INT_RISEEDGE;
118                 break;
119
120         case IRQ_TYPE_EDGE_FALLING:
121                 con = S5PC1XX_WKUP_INT_FALLEDGE;
122                 break;
123
124         case IRQ_TYPE_EDGE_BOTH:
125                 con = S5PC1XX_WKUP_INT_BOTHEDGE;
126                 break;
127
128         case IRQ_TYPE_LEVEL_LOW:
129                 con = S5PC1XX_WKUP_INT_LOWLEV;
130                 break;
131
132         case IRQ_TYPE_LEVEL_HIGH:
133                 con = S5PC1XX_WKUP_INT_HILEV;
134                 break;
135
136         default:
137                 printk(KERN_ERR "No such irq type %d", type);
138                 return -EINVAL;
139         }
140
141         gpio = real & (8 - 1);
142         shift = gpio << 2;
143
144         ctrl = __raw_readl(S5PC1XX_WKUP_INT_CON(bank));
145         ctrl &= ~(0x7 << shift);
146         ctrl |= con << shift;
147         __raw_writel(ctrl, S5PC1XX_WKUP_INT_CON(bank));
148
149         switch (real) {
150         case 0 ... 7:
151                         gpio = S5PC100_GPH0(gpio);
152                 break;
153         case 8 ... 15:
154                         gpio = S5PC100_GPH1(gpio);
155                 break;
156         case 16 ... 23:
157                         gpio = S5PC100_GPH2(gpio);
158                 break;
159         case 24 ... 31:
160                         gpio = S5PC100_GPH3(gpio);
161                 break;
162         default:
163                 return -EINVAL;
164         }
165
166         sfn = S3C_GPIO_SFN(0x2);
167         s3c_gpio_cfgpin(gpio, sfn);
168
169         return 0;
170 }
171
172 static struct irq_chip s3c_irq_eint = {
173         .name           = "EINT",
174         .mask           = s3c_irq_eint_mask,
175         .unmask         = s3c_irq_eint_unmask,
176         .mask_ack       = s3c_irq_eint_maskack,
177         .ack            = s3c_irq_eint_ack,
178         .set_type       = s3c_irq_eint_set_type,
179         .set_wake       = s3c_irqext_wake,
180 };
181
182 /* s3c_irq_demux_eint
183  *
184  * This function demuxes the IRQ from external interrupts,
185  * from IRQ_EINT(16) to IRQ_EINT(31). It is designed to be inlined into
186  * the specific handlers s3c_irq_demux_eintX_Y.
187  */
188 static inline void s3c_irq_demux_eint(unsigned int start, unsigned int end)
189 {
190         u32 status = __raw_readl(S5PC1XX_WKUP_INT_PEND((start >> 3)));
191         u32 mask = __raw_readl(S5PC1XX_WKUP_INT_MASK((start >> 3)));
192         unsigned int irq;
193
194         status &= ~mask;
195         status &= (1 << (end - start + 1)) - 1;
196
197         for (irq = IRQ_EINT(start); irq <= IRQ_EINT(end); irq++) {
198                 if (status & 1)
199                         generic_handle_irq(irq);
200
201                 status >>= 1;
202         }
203 }
204
205 static void s3c_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
206 {
207         s3c_irq_demux_eint(16, 23);
208         s3c_irq_demux_eint(24, 31);
209 }
210
211 /*
212  * Handle EINT0 ... EINT15 at VIC directly
213  */
214 static void s3c_irq_vic_eint_mask(unsigned int irq)
215 {
216         void __iomem *base = get_irq_chip_data(irq);
217         unsigned int real;
218
219         s3c_irq_eint_mask(irq);
220         real = s3c_get_eint(irq);
221         writel(1 << real, base + VIC_INT_ENABLE_CLEAR);
222 }
223
224 static void s3c_irq_vic_eint_unmask(unsigned int irq)
225 {
226         void __iomem *base = get_irq_chip_data(irq);
227         unsigned int real;
228
229         s3c_irq_eint_unmask(irq);
230         real = s3c_get_eint(irq);
231         writel(1 << real, base + VIC_INT_ENABLE);
232 }
233
234 static inline void s3c_irq_vic_eint_ack(unsigned int irq)
235 {
236         u32 bit;
237         u32 bank = s3c_get_bank(irq);
238
239         bit = s3c_eint_to_bit(irq);
240         __raw_writel(bit, S5PC1XX_WKUP_INT_PEND(bank));
241 }
242
243 static void s3c_irq_vic_eint_maskack(unsigned int irq)
244 {
245         /* compiler should in-line these */
246         s3c_irq_vic_eint_mask(irq);
247         s3c_irq_vic_eint_ack(irq);
248 }
249
250 static struct irq_chip s3c_irq_vic_eint = {
251         .name           = "EINT",
252         .mask           = s3c_irq_vic_eint_mask,
253         .unmask         = s3c_irq_vic_eint_unmask,
254         .mask_ack       = s3c_irq_vic_eint_maskack,
255         .ack            = s3c_irq_vic_eint_ack,
256         .set_type       = s3c_irq_eint_set_type,
257         .set_wake       = s3c_irqext_wake,
258 };
259
260 static int __init s5pc1xx_init_irq_eint(void)
261 {
262         int irq;
263
264         for (irq = IRQ_EINT0; irq <= IRQ_EINT15; irq++) {
265                 set_irq_chip(irq, &s3c_irq_vic_eint);
266                 set_irq_handler(irq, handle_level_irq);
267                 set_irq_flags(irq, IRQF_VALID);
268         }
269
270         for (irq = IRQ_EINT(16); irq <= IRQ_EINT(31); irq++) {
271                 set_irq_chip(irq, &s3c_irq_eint);
272                 set_irq_handler(irq, handle_level_irq);
273                 set_irq_flags(irq, IRQF_VALID);
274         }
275
276         set_irq_chained_handler(IRQ_EINT16_31, s3c_irq_demux_eint16_31);
277
278         return 0;
279 }
280
281 arch_initcall(s5pc1xx_init_irq_eint);