upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / irq-eint.c
1 /* linux/arch/arm/mach-s5pv310/irq-eint.c
2  *
3  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com
5  *
6  * S5PV310 - IRQ EINT support
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 version 2 as
10  * published by the Free Software Foundation.
11 */
12
13 #include <linux/kernel.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/io.h>
17 #include <linux/sysdev.h>
18 #include <linux/gpio.h>
19
20 #include <plat/regs-irqtype.h>
21 #include <plat/pm.h>
22 #include <plat/cpu.h>
23 #include <plat/gpio-cfg.h>
24
25 #include <mach/map.h>
26 #include <mach/regs-gpio.h>
27
28 #include <asm/mach/irq.h>
29
30 static DEFINE_SPINLOCK(eint_lock);
31
32 static unsigned int s5pv310_get_irq_nr(unsigned int number)
33 {
34         u32 ret = 0;
35
36         switch (number) {
37         case 0 ... 3:
38                 ret = (number + IRQ_EINT0);
39                 break;
40         case 4 ... 7:
41                 ret = (number + (IRQ_EINT4 - 4));
42                 break;
43         case 8 ... 15:
44                 ret = (number + (IRQ_EINT8 - 8));
45                 break;
46         default:
47                 printk(KERN_ERR "[%s] input number is not available : %d\n",__func__,number);
48         }
49
50         return ret;
51 }
52
53 static unsigned int s5pv310_irq_split(unsigned int number)
54 {
55         int ret;
56         int test = number;
57
58         ret = do_div(test, IRQ_EINT_BASE);
59
60         do_div(ret, 8);
61
62         return ret;
63 }
64
65 static unsigned int s5pv310_irq_to_bit(unsigned int irq)
66 {
67         u32 ret;
68         u32 tmp;
69
70         tmp = do_div(irq, IRQ_EINT_BASE);
71
72         ret = do_div(tmp, 8);
73
74         return (1 << (ret));
75 }
76
77 static inline void s5pv310_irq_eint_mask(unsigned int irq)
78 {
79         u32 mask;
80
81         spin_lock(&eint_lock);
82         mask = __raw_readl(S5P_EINT_MASK(s5pv310_irq_split(irq)));
83         mask |= s5pv310_irq_to_bit(irq);
84         __raw_writel(mask, S5P_EINT_MASK(s5pv310_irq_split(irq)));
85         spin_unlock(&eint_lock);
86 }
87
88 static void s5pv310_irq_eint_unmask(unsigned int irq)
89 {
90         u32 mask;
91
92         spin_lock(&eint_lock);
93         mask = __raw_readl(S5P_EINT_MASK(s5pv310_irq_split(irq)));
94         mask &= ~(s5pv310_irq_to_bit(irq));
95         __raw_writel(mask, S5P_EINT_MASK(s5pv310_irq_split(irq)));
96         spin_unlock(&eint_lock);
97 }
98
99 static inline void s5pv310_irq_eint_ack(unsigned int irq)
100 {
101         spin_lock(&eint_lock);
102         __raw_writel(s5pv310_irq_to_bit(irq), S5P_EINT_PEND(s5pv310_irq_split(irq)));
103         spin_unlock(&eint_lock);
104 }
105
106 static void s5pv310_irq_eint_maskack(unsigned int irq)
107 {
108         s5pv310_irq_eint_mask(irq);
109         s5pv310_irq_eint_ack(irq);
110 }
111
112 static int s5pv310_irq_eint_set_type(unsigned int irq, unsigned int type)
113 {
114         int offs = EINT_OFFSET(irq);
115         int shift;
116         u32 ctrl, mask;
117         u32 newvalue = 0;
118         struct irq_desc *desc = irq_to_desc(irq);
119
120         switch (type) {
121         case IRQ_TYPE_EDGE_RISING:
122                 newvalue = S5P_EXTINT_RISEEDGE;
123                 break;
124
125         case IRQ_TYPE_EDGE_FALLING:
126                 newvalue = S5P_EXTINT_FALLEDGE;
127                 break;
128
129         case IRQ_TYPE_EDGE_BOTH:
130                 newvalue = S5P_EXTINT_BOTHEDGE;
131                 break;
132
133         case IRQ_TYPE_LEVEL_LOW:
134                 newvalue = S5P_EXTINT_LOWLEV;
135                 break;
136
137         case IRQ_TYPE_LEVEL_HIGH:
138                 newvalue = S5P_EXTINT_HILEV;
139                 break;
140
141         default:
142                 printk(KERN_ERR "No such irq type %d", type);
143                 return -EINVAL;
144         }
145
146         shift = (offs & 0x7) * 4;
147         mask = 0x7 << shift;
148
149         spin_lock(&eint_lock);
150         ctrl = __raw_readl(S5P_EINT_CON(s5pv310_irq_split(irq)));
151         ctrl &= ~mask;
152         ctrl |= newvalue << shift;
153         __raw_writel(ctrl, S5P_EINT_CON(s5pv310_irq_split(irq)));
154         spin_unlock(&eint_lock);
155
156         if ((0 <= offs) && (offs < 8))
157                 s3c_gpio_cfgpin(EINT_GPIO_0(offs & 0x7), EINT_MODE);
158
159         else if ((8 <= offs) && (offs < 16))
160                 s3c_gpio_cfgpin(EINT_GPIO_1(offs & 0x7), EINT_MODE);
161
162         else if ((16 <= offs) && (offs < 24))
163                 s3c_gpio_cfgpin(EINT_GPIO_2(offs & 0x7), EINT_MODE);
164
165         else if ((24 <= offs) && (offs < 32))
166                 s3c_gpio_cfgpin(EINT_GPIO_3(offs & 0x7), EINT_MODE);
167
168         else
169                 printk(KERN_ERR "No such irq number %d", offs);
170
171         if (type & IRQ_TYPE_EDGE_BOTH)
172                 desc->handle_irq = handle_edge_irq;
173         else
174                 desc->handle_irq = handle_level_irq;
175
176         return 0;
177 }
178
179 static struct irq_chip s5pv310_irq_eint = {
180         .name           = "s5pv310-eint",
181         .mask           = s5pv310_irq_eint_mask,
182         .unmask         = s5pv310_irq_eint_unmask,
183         .mask_ack       = s5pv310_irq_eint_maskack,
184         .ack            = s5pv310_irq_eint_ack,
185         .set_type       = s5pv310_irq_eint_set_type,
186 #ifdef CONFIG_PM
187         .set_wake       = s3c_irqext_wake,
188 #endif
189 };
190
191 /* s5pv310_irq_demux_eint
192  *
193  * This function demuxes the IRQ from the group0 external interrupts,
194  * from EINTs 16 to 31. It is designed to be inlined into the specific
195  * handler s5p_irq_demux_eintX_Y.
196  *
197  * Each EINT pend/mask registers handle eight of them.
198  */
199 static inline u32 s5pv310_irq_demux_eint(unsigned int irq, unsigned int start)
200 {
201         unsigned int cascade_irq;
202
203         u32 status = __raw_readl(S5P_EINT_PEND(s5pv310_irq_split(start)));
204         u32 mask = __raw_readl(S5P_EINT_MASK(s5pv310_irq_split(start)));
205         u32 action = 0;
206
207         status &= ~mask;
208         status &= 0xff;
209
210         while (status) {
211                 cascade_irq = fls(status) - 1;
212                 generic_handle_irq(cascade_irq + start);
213                 status &= ~(1 << cascade_irq);
214                 ++action;
215         }
216
217         return action;
218 }
219
220 static void s5pv310_irq_demux_eint16_31(unsigned int irq, struct irq_desc *desc)
221 {
222         struct irq_chip *chip = get_irq_chip(irq);
223         u32 a16_23, a24_31;
224
225         if (chip->ack)
226                 chip->ack(irq);
227
228         a16_23 = s5pv310_irq_demux_eint(irq, IRQ_EINT(16));
229         a24_31 = s5pv310_irq_demux_eint(irq, IRQ_EINT(24));
230
231         if (!a16_23 && !a24_31)
232                 do_bad_IRQ(irq, desc);
233
234         chip->unmask(irq);
235 }
236
237 static void s5pv310_irq_eint0_15(unsigned int irq, struct irq_desc *desc)
238 {
239         u32 i;
240         struct irq_chip *chip = get_irq_chip(irq);
241
242         if (chip->ack)
243                 chip->ack(irq);
244
245         for (i = 0 ; i <= 15 ; i++) {
246                 if (irq == s5pv310_get_irq_nr(i)) {
247                         generic_handle_irq(IRQ_EINT(i));
248                         goto out;
249                 }
250         }
251
252         do_bad_IRQ(irq, desc);
253 out:
254         chip->unmask(irq);
255 }
256
257 int __init s5pv310_init_irq_eint(void)
258 {
259         int irq;
260
261         for (irq = 0 ; irq <= 31 ; irq++) {
262                 set_irq_chip(IRQ_EINT(irq), &s5pv310_irq_eint);
263                 set_irq_handler(IRQ_EINT(irq), handle_level_irq);
264                 set_irq_flags(IRQ_EINT(irq), IRQF_VALID);
265         }
266
267         set_irq_chained_handler(IRQ_EINT16_31, s5pv310_irq_demux_eint16_31);
268
269         for (irq = 0 ; irq <= 15 ; irq++)
270                 set_irq_chained_handler(s5pv310_get_irq_nr(irq), s5pv310_irq_eint0_15);
271
272         return 0;
273 }
274
275 arch_initcall(s5pv310_init_irq_eint);