Commit | Line | Data |
---|---|---|
b0d5217c KP |
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); |