aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorKyungmin Park <kyungmin.park@samsung.com>2009-11-17 02:41:16 -0500
committerBen Dooks <ben-linux@fluff.org>2009-11-30 20:33:14 -0500
commitb0d5217cfb0a2357ac076977400c648cccff6154 (patch)
tree79795a07f16b3f3962836206651911a869bbca71 /arch/arm
parentd7b9ace51d949e1bfec7f32d21d094cf2c683ca0 (diff)
ARM: S5PC1xx: add gpiolib and external/gpio interrupt support
Add support for gpiolib calls. This is based on the gpiolib implementation from plat-s3c64xx tree. Add support for external interrupts for GPIO H banks. Add support for GPIO interrupts for all banks. Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/plat-s3c/Kconfig6
-rw-r--r--arch/arm/plat-s5pc1xx/Kconfig3
-rw-r--r--arch/arm/plat-s5pc1xx/Makefile4
-rw-r--r--arch/arm/plat-s5pc1xx/cpu.c5
-rw-r--r--arch/arm/plat-s5pc1xx/gpio-config.c62
-rw-r--r--arch/arm/plat-s5pc1xx/gpiolib.c503
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/gpio-cfg-s5pc1xx.h32
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/gpio-ext.h44
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/irqs.h15
-rw-r--r--arch/arm/plat-s5pc1xx/include/plat/regs-gpio.h70
-rw-r--r--arch/arm/plat-s5pc1xx/irq-eint.c281
-rw-r--r--arch/arm/plat-s5pc1xx/irq-gpio.c266
-rw-r--r--arch/arm/plat-s5pc1xx/irq.c2
13 files changed, 1288 insertions, 5 deletions
diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig
index 8931c5f0e46b..f155a849e705 100644
--- a/arch/arm/plat-s3c/Kconfig
+++ b/arch/arm/plat-s3c/Kconfig
@@ -159,6 +159,12 @@ config S3C_GPIO_CFG_S3C64XX
159 Internal configuration to enable S3C64XX style GPIO configuration 159 Internal configuration to enable S3C64XX style GPIO configuration
160 functions. 160 functions.
161 161
162config S5P_GPIO_CFG_S5PC1XX
163 bool
164 help
165 Internal configuration to enable S5PC1XX style GPIO configuration
166 functions.
167
162# DMA 168# DMA
163 169
164config S3C_DMA 170config S3C_DMA
diff --git a/arch/arm/plat-s5pc1xx/Kconfig b/arch/arm/plat-s5pc1xx/Kconfig
index a8a711c3c064..86edd27c7f24 100644
--- a/arch/arm/plat-s5pc1xx/Kconfig
+++ b/arch/arm/plat-s5pc1xx/Kconfig
@@ -15,6 +15,9 @@ config PLAT_S5PC1XX
15 select ARCH_REQUIRE_GPIOLIB 15 select ARCH_REQUIRE_GPIOLIB
16 select S3C_GPIO_TRACK 16 select S3C_GPIO_TRACK
17 select S3C_GPIO_PULL_UPDOWN 17 select S3C_GPIO_PULL_UPDOWN
18 select S3C_GPIO_CFG_S3C24XX
19 select S3C_GPIO_CFG_S3C64XX
20 select S5P_GPIO_CFG_S5PC1XX
18 help 21 help
19 Base platform code for any Samsung S5PC1XX device 22 Base platform code for any Samsung S5PC1XX device
20 23
diff --git a/arch/arm/plat-s5pc1xx/Makefile b/arch/arm/plat-s5pc1xx/Makefile
index ebbf36490f4c..e860813cb8de 100644
--- a/arch/arm/plat-s5pc1xx/Makefile
+++ b/arch/arm/plat-s5pc1xx/Makefile
@@ -13,8 +13,9 @@ obj- :=
13 13
14obj-y += dev-uart.o 14obj-y += dev-uart.o
15obj-y += cpu.o 15obj-y += cpu.o
16obj-y += irq.o 16obj-y += irq.o irq-gpio.o irq-eint.o
17obj-y += clock.o 17obj-y += clock.o
18obj-y += gpiolib.o
18 19
19# CPU support 20# CPU support
20 21
@@ -23,5 +24,6 @@ obj-$(CONFIG_CPU_S5PC100_CLOCK) += s5pc100-clock.o
23 24
24# Device setup 25# Device setup
25 26
27obj-$(CONFIG_S5P_GPIO_CFG_S5PC1XX) += gpio-config.o
26obj-$(CONFIG_S5PC100_SETUP_I2C0) += setup-i2c0.o 28obj-$(CONFIG_S5PC100_SETUP_I2C0) += setup-i2c0.o
27obj-$(CONFIG_S5PC100_SETUP_I2C1) += setup-i2c1.o 29obj-$(CONFIG_S5PC100_SETUP_I2C1) += setup-i2c1.o
diff --git a/arch/arm/plat-s5pc1xx/cpu.c b/arch/arm/plat-s5pc1xx/cpu.c
index e8f347218ecb..02baeaa2a121 100644
--- a/arch/arm/plat-s5pc1xx/cpu.c
+++ b/arch/arm/plat-s5pc1xx/cpu.c
@@ -60,6 +60,11 @@ static struct map_desc s5pc1xx_iodesc[] __initdata = {
60 .length = SZ_4K, 60 .length = SZ_4K,
61 .type = MT_DEVICE, 61 .type = MT_DEVICE,
62 }, { 62 }, {
63 .virtual = (unsigned long)S5PC1XX_VA_GPIO,
64 .pfn = __phys_to_pfn(S5PC100_PA_GPIO),
65 .length = SZ_4K,
66 .type = MT_DEVICE,
67 }, {
63 .virtual = (unsigned long)S5PC1XX_VA_CHIPID, 68 .virtual = (unsigned long)S5PC1XX_VA_CHIPID,
64 .pfn = __phys_to_pfn(S5PC1XX_PA_CHIPID), 69 .pfn = __phys_to_pfn(S5PC1XX_PA_CHIPID),
65 .length = SZ_16, 70 .length = SZ_16,
diff --git a/arch/arm/plat-s5pc1xx/gpio-config.c b/arch/arm/plat-s5pc1xx/gpio-config.c
new file mode 100644
index 000000000000..bba675df9c75
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/gpio-config.c
@@ -0,0 +1,62 @@
1/* linux/arch/arm/plat-s5pc1xx/gpio-config.c
2 *
3 * Copyright 2009 Samsung Electronics
4 *
5 * S5PC1XX GPIO Configuration.
6 *
7 * Based on plat-s3c64xx/gpio-config.c
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12*/
13
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/gpio.h>
17#include <linux/io.h>
18
19#include <mach/gpio-core.h>
20#include <plat/gpio-cfg-s5pc1xx.h>
21
22s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin, unsigned int off)
23{
24 struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);
25 void __iomem *reg;
26 int shift = off * 2;
27 u32 drvstr;
28
29 if (!chip)
30 return -EINVAL;
31
32 reg = chip->base + 0x0C;
33
34 drvstr = __raw_readl(reg);
35 drvstr = 0xffff & (0x3 << shift);
36 drvstr = drvstr >> shift;
37
38 return (__force s5p_gpio_drvstr_t)drvstr;
39}
40EXPORT_SYMBOL(s5p_gpio_get_drvstr);
41
42int s5p_gpio_set_drvstr(unsigned int pin, unsigned int off,
43 s5p_gpio_drvstr_t drvstr)
44{
45 struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin);
46 void __iomem *reg;
47 int shift = off * 2;
48 u32 tmp;
49
50 if (!chip)
51 return -EINVAL;
52
53 reg = chip->base + 0x0C;
54
55 tmp = __raw_readl(reg);
56 tmp |= drvstr << shift;
57
58 __raw_writel(tmp, reg);
59
60 return 0;
61}
62EXPORT_SYMBOL(s5p_gpio_set_drvstr);
diff --git a/arch/arm/plat-s5pc1xx/gpiolib.c b/arch/arm/plat-s5pc1xx/gpiolib.c
new file mode 100644
index 000000000000..facb410e7a71
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/gpiolib.c
@@ -0,0 +1,503 @@
1/*
2 * arch/arm/plat-s5pc1xx/gpiolib.c
3 *
4 * Copyright 2009 Samsung Electronics Co
5 * Kyungmin Park <kyungmin.park@samsung.com>
6 *
7 * S5PC1XX - GPIOlib support
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
12 */
13
14#include <linux/kernel.h>
15#include <linux/irq.h>
16#include <linux/io.h>
17#include <linux/gpio.h>
18
19#include <mach/map.h>
20#include <mach/gpio-core.h>
21
22#include <plat/gpio-cfg.h>
23#include <plat/gpio-cfg-helpers.h>
24#include <plat/regs-gpio.h>
25
26/* S5PC100 GPIO bank summary:
27 *
28 * Bank GPIOs Style INT Type
29 * A0 8 4Bit GPIO_INT0
30 * A1 5 4Bit GPIO_INT1
31 * B 8 4Bit GPIO_INT2
32 * C 5 4Bit GPIO_INT3
33 * D 7 4Bit GPIO_INT4
34 * E0 8 4Bit GPIO_INT5
35 * E1 6 4Bit GPIO_INT6
36 * F0 8 4Bit GPIO_INT7
37 * F1 8 4Bit GPIO_INT8
38 * F2 8 4Bit GPIO_INT9
39 * F3 4 4Bit GPIO_INT10
40 * G0 8 4Bit GPIO_INT11
41 * G1 3 4Bit GPIO_INT12
42 * G2 7 4Bit GPIO_INT13
43 * G3 7 4Bit GPIO_INT14
44 * H0 8 4Bit WKUP_INT
45 * H1 8 4Bit WKUP_INT
46 * H2 8 4Bit WKUP_INT
47 * H3 8 4Bit WKUP_INT
48 * I 8 4Bit GPIO_INT15
49 * J0 8 4Bit GPIO_INT16
50 * J1 5 4Bit GPIO_INT17
51 * J2 8 4Bit GPIO_INT18
52 * J3 8 4Bit GPIO_INT19
53 * J4 4 4Bit GPIO_INT20
54 * K0 8 4Bit None
55 * K1 6 4Bit None
56 * K2 8 4Bit None
57 * K3 8 4Bit None
58 * L0 8 4Bit None
59 * L1 8 4Bit None
60 * L2 8 4Bit None
61 * L3 8 4Bit None
62 */
63
64#define OFF_GPCON (0x00)
65#define OFF_GPDAT (0x04)
66
67#define con_4bit_shift(__off) ((__off) * 4)
68
69#if 1
70#define gpio_dbg(x...) do { } while (0)
71#else
72#define gpio_dbg(x...) printk(KERN_DEBUG x)
73#endif
74
75/* The s5pc1xx_gpiolib routines are to control the gpio banks where
76 * the gpio configuration register (GPxCON) has 4 bits per GPIO, as the
77 * following example:
78 *
79 * base + 0x00: Control register, 4 bits per gpio
80 * gpio n: 4 bits starting at (4*n)
81 * 0000 = input, 0001 = output, others mean special-function
82 * base + 0x04: Data register, 1 bit per gpio
83 * bit n: data bit n
84 *
85 * Note, since the data register is one bit per gpio and is at base + 0x4
86 * we can use s3c_gpiolib_get and s3c_gpiolib_set to change the state of
87 * the output.
88 */
89
90static int s5pc1xx_gpiolib_input(struct gpio_chip *chip, unsigned offset)
91{
92 struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
93 void __iomem *base = ourchip->base;
94 unsigned long con;
95
96 con = __raw_readl(base + OFF_GPCON);
97 con &= ~(0xf << con_4bit_shift(offset));
98 __raw_writel(con, base + OFF_GPCON);
99
100 gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);
101
102 return 0;
103}
104
105static int s5pc1xx_gpiolib_output(struct gpio_chip *chip,
106 unsigned offset, int value)
107{
108 struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
109 void __iomem *base = ourchip->base;
110 unsigned long con;
111 unsigned long dat;
112
113 con = __raw_readl(base + OFF_GPCON);
114 con &= ~(0xf << con_4bit_shift(offset));
115 con |= 0x1 << con_4bit_shift(offset);
116
117 dat = __raw_readl(base + OFF_GPDAT);
118 if (value)
119 dat |= 1 << offset;
120 else
121 dat &= ~(1 << offset);
122
123 __raw_writel(dat, base + OFF_GPDAT);
124 __raw_writel(con, base + OFF_GPCON);
125 __raw_writel(dat, base + OFF_GPDAT);
126
127 gpio_dbg("%s: %p: CON %08lx, DAT %08lx\n", __func__, base, con, dat);
128
129 return 0;
130}
131
132static int s5pc1xx_gpiolib_to_irq(struct gpio_chip *chip, unsigned int offset)
133{
134 return S3C_IRQ_GPIO(chip->base + offset);
135}
136
137static int s5pc1xx_gpiolib_to_eint(struct gpio_chip *chip, unsigned int offset)
138{
139 int base;
140
141 base = chip->base - S5PC100_GPH0(0);
142 if (base == 0)
143 return IRQ_EINT(offset);
144 base = chip->base - S5PC100_GPH1(0);
145 if (base == 0)
146 return IRQ_EINT(8 + offset);
147 base = chip->base - S5PC100_GPH2(0);
148 if (base == 0)
149 return IRQ_EINT(16 + offset);
150 base = chip->base - S5PC100_GPH3(0);
151 if (base == 0)
152 return IRQ_EINT(24 + offset);
153 return -EINVAL;
154}
155
156static struct s3c_gpio_cfg gpio_cfg = {
157 .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
158 .set_pull = s3c_gpio_setpull_updown,
159 .get_pull = s3c_gpio_getpull_updown,
160};
161
162static struct s3c_gpio_cfg gpio_cfg_eint = {
163 .cfg_eint = 0xf,
164 .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
165 .set_pull = s3c_gpio_setpull_updown,
166 .get_pull = s3c_gpio_getpull_updown,
167};
168
169static struct s3c_gpio_cfg gpio_cfg_noint = {
170 .set_config = s3c_gpio_setcfg_s3c64xx_4bit,
171 .set_pull = s3c_gpio_setpull_updown,
172 .get_pull = s3c_gpio_getpull_updown,
173};
174
175static struct s3c_gpio_chip s5pc100_gpio_chips[] = {
176 {
177 .base = S5PC100_GPA0_BASE,
178 .config = &gpio_cfg,
179 .chip = {
180 .base = S5PC100_GPA0(0),
181 .ngpio = S5PC100_GPIO_A0_NR,
182 .label = "GPA0",
183 },
184 }, {
185 .base = S5PC100_GPA1_BASE,
186 .config = &gpio_cfg,
187 .chip = {
188 .base = S5PC100_GPA1(0),
189 .ngpio = S5PC100_GPIO_A1_NR,
190 .label = "GPA1",
191 },
192 }, {
193 .base = S5PC100_GPB_BASE,
194 .config = &gpio_cfg,
195 .chip = {
196 .base = S5PC100_GPB(0),
197 .ngpio = S5PC100_GPIO_B_NR,
198 .label = "GPB",
199 },
200 }, {
201 .base = S5PC100_GPC_BASE,
202 .config = &gpio_cfg,
203 .chip = {
204 .base = S5PC100_GPC(0),
205 .ngpio = S5PC100_GPIO_C_NR,
206 .label = "GPC",
207 },
208 }, {
209 .base = S5PC100_GPD_BASE,
210 .config = &gpio_cfg,
211 .chip = {
212 .base = S5PC100_GPD(0),
213 .ngpio = S5PC100_GPIO_D_NR,
214 .label = "GPD",
215 },
216 }, {
217 .base = S5PC100_GPE0_BASE,
218 .config = &gpio_cfg,
219 .chip = {
220 .base = S5PC100_GPE0(0),
221 .ngpio = S5PC100_GPIO_E0_NR,
222 .label = "GPE0",
223 },
224 }, {
225 .base = S5PC100_GPE1_BASE,
226 .config = &gpio_cfg,
227 .chip = {
228 .base = S5PC100_GPE1(0),
229 .ngpio = S5PC100_GPIO_E1_NR,
230 .label = "GPE1",
231 },
232 }, {
233 .base = S5PC100_GPF0_BASE,
234 .config = &gpio_cfg,
235 .chip = {
236 .base = S5PC100_GPF0(0),
237 .ngpio = S5PC100_GPIO_F0_NR,
238 .label = "GPF0",
239 },
240 }, {
241 .base = S5PC100_GPF1_BASE,
242 .config = &gpio_cfg,
243 .chip = {
244 .base = S5PC100_GPF1(0),
245 .ngpio = S5PC100_GPIO_F1_NR,
246 .label = "GPF1",
247 },
248 }, {
249 .base = S5PC100_GPF2_BASE,
250 .config = &gpio_cfg,
251 .chip = {
252 .base = S5PC100_GPF2(0),
253 .ngpio = S5PC100_GPIO_F2_NR,
254 .label = "GPF2",
255 },
256 }, {
257 .base = S5PC100_GPF3_BASE,
258 .config = &gpio_cfg,
259 .chip = {
260 .base = S5PC100_GPF3(0),
261 .ngpio = S5PC100_GPIO_F3_NR,
262 .label = "GPF3",
263 },
264 }, {
265 .base = S5PC100_GPG0_BASE,
266 .config = &gpio_cfg,
267 .chip = {
268 .base = S5PC100_GPG0(0),
269 .ngpio = S5PC100_GPIO_G0_NR,
270 .label = "GPG0",
271 },
272 }, {
273 .base = S5PC100_GPG1_BASE,
274 .config = &gpio_cfg,
275 .chip = {
276 .base = S5PC100_GPG1(0),
277 .ngpio = S5PC100_GPIO_G1_NR,
278 .label = "GPG1",
279 },
280 }, {
281 .base = S5PC100_GPG2_BASE,
282 .config = &gpio_cfg,
283 .chip = {
284 .base = S5PC100_GPG2(0),
285 .ngpio = S5PC100_GPIO_G2_NR,
286 .label = "GPG2",
287 },
288 }, {
289 .base = S5PC100_GPG3_BASE,
290 .config = &gpio_cfg,
291 .chip = {
292 .base = S5PC100_GPG3(0),
293 .ngpio = S5PC100_GPIO_G3_NR,
294 .label = "GPG3",
295 },
296 }, {
297 .base = S5PC100_GPH0_BASE,
298 .config = &gpio_cfg_eint,
299 .chip = {
300 .base = S5PC100_GPH0(0),
301 .ngpio = S5PC100_GPIO_H0_NR,
302 .label = "GPH0",
303 },
304 }, {
305 .base = S5PC100_GPH1_BASE,
306 .config = &gpio_cfg_eint,
307 .chip = {
308 .base = S5PC100_GPH1(0),
309 .ngpio = S5PC100_GPIO_H1_NR,
310 .label = "GPH1",
311 },
312 }, {
313 .base = S5PC100_GPH2_BASE,
314 .config = &gpio_cfg_eint,
315 .chip = {
316 .base = S5PC100_GPH2(0),
317 .ngpio = S5PC100_GPIO_H2_NR,
318 .label = "GPH2",
319 },
320 }, {
321 .base = S5PC100_GPH3_BASE,
322 .config = &gpio_cfg_eint,
323 .chip = {
324 .base = S5PC100_GPH3(0),
325 .ngpio = S5PC100_GPIO_H3_NR,
326 .label = "GPH3",
327 },
328 }, {
329 .base = S5PC100_GPI_BASE,
330 .config = &gpio_cfg,
331 .chip = {
332 .base = S5PC100_GPI(0),
333 .ngpio = S5PC100_GPIO_I_NR,
334 .label = "GPI",
335 },
336 }, {
337 .base = S5PC100_GPJ0_BASE,
338 .config = &gpio_cfg,
339 .chip = {
340 .base = S5PC100_GPJ0(0),
341 .ngpio = S5PC100_GPIO_J0_NR,
342 .label = "GPJ0",
343 },
344 }, {
345 .base = S5PC100_GPJ1_BASE,
346 .config = &gpio_cfg,
347 .chip = {
348 .base = S5PC100_GPJ1(0),
349 .ngpio = S5PC100_GPIO_J1_NR,
350 .label = "GPJ1",
351 },
352 }, {
353 .base = S5PC100_GPJ2_BASE,
354 .config = &gpio_cfg,
355 .chip = {
356 .base = S5PC100_GPJ2(0),
357 .ngpio = S5PC100_GPIO_J2_NR,
358 .label = "GPJ2",
359 },
360 }, {
361 .base = S5PC100_GPJ3_BASE,
362 .config = &gpio_cfg,
363 .chip = {
364 .base = S5PC100_GPJ3(0),
365 .ngpio = S5PC100_GPIO_J3_NR,
366 .label = "GPJ3",
367 },
368 }, {
369 .base = S5PC100_GPJ4_BASE,
370 .config = &gpio_cfg,
371 .chip = {
372 .base = S5PC100_GPJ4(0),
373 .ngpio = S5PC100_GPIO_J4_NR,
374 .label = "GPJ4",
375 },
376 }, {
377 .base = S5PC100_GPK0_BASE,
378 .config = &gpio_cfg_noint,
379 .chip = {
380 .base = S5PC100_GPK0(0),
381 .ngpio = S5PC100_GPIO_K0_NR,
382 .label = "GPK0",
383 },
384 }, {
385 .base = S5PC100_GPK1_BASE,
386 .config = &gpio_cfg_noint,
387 .chip = {
388 .base = S5PC100_GPK1(0),
389 .ngpio = S5PC100_GPIO_K1_NR,
390 .label = "GPK1",
391 },
392 }, {
393 .base = S5PC100_GPK2_BASE,
394 .config = &gpio_cfg_noint,
395 .chip = {
396 .base = S5PC100_GPK2(0),
397 .ngpio = S5PC100_GPIO_K2_NR,
398 .label = "GPK2",
399 },
400 }, {
401 .base = S5PC100_GPK3_BASE,
402 .config = &gpio_cfg_noint,
403 .chip = {
404 .base = S5PC100_GPK3(0),
405 .ngpio = S5PC100_GPIO_K3_NR,
406 .label = "GPK3",
407 },
408 }, {
409 .base = S5PC100_GPL0_BASE,
410 .config = &gpio_cfg_noint,
411 .chip = {
412 .base = S5PC100_GPL0(0),
413 .ngpio = S5PC100_GPIO_L0_NR,
414 .label = "GPL0",
415 },
416 }, {
417 .base = S5PC100_GPL1_BASE,
418 .config = &gpio_cfg_noint,
419 .chip = {
420 .base = S5PC100_GPL1(0),
421 .ngpio = S5PC100_GPIO_L1_NR,
422 .label = "GPL1",
423 },
424 }, {
425 .base = S5PC100_GPL2_BASE,
426 .config = &gpio_cfg_noint,
427 .chip = {
428 .base = S5PC100_GPL2(0),
429 .ngpio = S5PC100_GPIO_L2_NR,
430 .label = "GPL2",
431 },
432 }, {
433 .base = S5PC100_GPL3_BASE,
434 .config = &gpio_cfg_noint,
435 .chip = {
436 .base = S5PC100_GPL3(0),
437 .ngpio = S5PC100_GPIO_L3_NR,
438 .label = "GPL3",
439 },
440 }, {
441 .base = S5PC100_GPL4_BASE,
442 .config = &gpio_cfg_noint,
443 .chip = {
444 .base = S5PC100_GPL4(0),
445 .ngpio = S5PC100_GPIO_L4_NR,
446 .label = "GPL4",
447 },
448 },
449};
450
451/* FIXME move from irq-gpio.c */
452extern struct irq_chip s5pc1xx_gpioint;
453extern void s5pc1xx_irq_gpioint_handler(unsigned int irq, struct irq_desc *desc);
454
455static __init void s5pc1xx_gpiolib_link(struct s3c_gpio_chip *chip)
456{
457 chip->chip.direction_input = s5pc1xx_gpiolib_input;
458 chip->chip.direction_output = s5pc1xx_gpiolib_output;
459 chip->pm = __gpio_pm(&s3c_gpio_pm_4bit);
460
461 /* Interrupt */
462 if (chip->config == &gpio_cfg) {
463 int i, irq;
464
465 chip->chip.to_irq = s5pc1xx_gpiolib_to_irq;
466
467 for (i = 0; i < chip->chip.ngpio; i++) {
468 irq = S3C_IRQ_GPIO_BASE + chip->chip.base + i;
469 set_irq_chip(irq, &s5pc1xx_gpioint);
470 set_irq_data(irq, &chip->chip);
471 set_irq_handler(irq, handle_level_irq);
472 set_irq_flags(irq, IRQF_VALID);
473 }
474 } else if (chip->config == &gpio_cfg_eint)
475 chip->chip.to_irq = s5pc1xx_gpiolib_to_eint;
476}
477
478static __init void s5pc1xx_gpiolib_add(struct s3c_gpio_chip *chips,
479 int nr_chips,
480 void (*fn)(struct s3c_gpio_chip *))
481{
482 for (; nr_chips > 0; nr_chips--, chips++) {
483 if (fn)
484 (fn)(chips);
485 s3c_gpiolib_add(chips);
486 }
487}
488
489static __init int s5pc1xx_gpiolib_init(void)
490{
491 struct s3c_gpio_chip *chips;
492 int nr_chips;
493
494 chips = s5pc100_gpio_chips;
495 nr_chips = ARRAY_SIZE(s5pc100_gpio_chips);
496
497 s5pc1xx_gpiolib_add(chips, nr_chips, s5pc1xx_gpiolib_link);
498 /* Interrupt */
499 set_irq_chained_handler(IRQ_GPIOINT, s5pc1xx_irq_gpioint_handler);
500
501 return 0;
502}
503core_initcall(s5pc1xx_gpiolib_init);
diff --git a/arch/arm/plat-s5pc1xx/include/plat/gpio-cfg-s5pc1xx.h b/arch/arm/plat-s5pc1xx/include/plat/gpio-cfg-s5pc1xx.h
new file mode 100644
index 000000000000..72ad59f61efc
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/gpio-cfg-s5pc1xx.h
@@ -0,0 +1,32 @@
1/* linux/arch/arm/plat-s5pc1xx/include/plat/gpio-cfg.h
2 *
3 * Copyright 2009 Samsung Electronic
4 *
5 * S5PC1XX Platform - GPIO pin configuration
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12/* This file contains the necessary definitions to get the basic gpio
13 * pin configuration done such as setting a pin to input or output or
14 * changing the pull-{up,down} configurations.
15 */
16
17#ifndef __GPIO_CFG_S5PC1XX_H
18#define __GPIO_CFG_S5PC1XX_H __FILE__
19
20typedef unsigned int __bitwise__ s5p_gpio_drvstr_t;
21
22#define S5P_GPIO_DRVSTR_LV1 0x00
23#define S5P_GPIO_DRVSTR_LV2 0x01
24#define S5P_GPIO_DRVSTR_LV3 0x10
25#define S5P_GPIO_DRVSTR_LV4 0x11
26
27extern s5p_gpio_drvstr_t s5p_gpio_get_drvstr(unsigned int pin, unsigned int off);
28
29extern int s5p_gpio_set_drvstr(unsigned int pin, unsigned int off,
30 s5p_gpio_drvstr_t drvstr);
31
32#endif /* __GPIO_CFG_S5PC1XX_H */
diff --git a/arch/arm/plat-s5pc1xx/include/plat/gpio-ext.h b/arch/arm/plat-s5pc1xx/include/plat/gpio-ext.h
new file mode 100644
index 000000000000..33ad267e8477
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/gpio-ext.h
@@ -0,0 +1,44 @@
1/* linux/arch/arm/plat-s5pc1xx/include/plat/gpio-eint.h
2 *
3 * Copyright 2009 Samsung Electronics Co.
4 *
5 * External Interrupt (GPH0 ~ GPH3) control register definitions
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10*/
11
12#define S5PC1XX_WKUP_INT_CON0_7 (S5PC1XX_EINT_BASE + 0x0)
13#define S5PC1XX_WKUP_INT_CON8_15 (S5PC1XX_EINT_BASE + 0x4)
14#define S5PC1XX_WKUP_INT_CON16_23 (S5PC1XX_EINT_BASE + 0x8)
15#define S5PC1XX_WKUP_INT_CON24_31 (S5PC1XX_EINT_BASE + 0xC)
16#define S5PC1XX_WKUP_INT_CON(x) (S5PC1XX_WKUP_INT_CON0_7 + (x * 0x4))
17
18#define S5PC1XX_WKUP_INT_FLTCON0_3 (S5PC1XX_EINT_BASE + 0x80)
19#define S5PC1XX_WKUP_INT_FLTCON4_7 (S5PC1XX_EINT_BASE + 0x84)
20#define S5PC1XX_WKUP_INT_FLTCON8_11 (S5PC1XX_EINT_BASE + 0x88)
21#define S5PC1XX_WKUP_INT_FLTCON12_15 (S5PC1XX_EINT_BASE + 0x8C)
22#define S5PC1XX_WKUP_INT_FLTCON16_19 (S5PC1XX_EINT_BASE + 0x90)
23#define S5PC1XX_WKUP_INT_FLTCON20_23 (S5PC1XX_EINT_BASE + 0x94)
24#define S5PC1XX_WKUP_INT_FLTCON24_27 (S5PC1XX_EINT_BASE + 0x98)
25#define S5PC1XX_WKUP_INT_FLTCON28_31 (S5PC1XX_EINT_BASE + 0x9C)
26#define S5PC1XX_WKUP_INT_FLTCON(x) (S5PC1XX_WKUP_INT_FLTCON0_3 + (x * 0x4))
27
28#define S5PC1XX_WKUP_INT_MASK0_7 (S5PC1XX_EINT_BASE + 0x100)
29#define S5PC1XX_WKUP_INT_MASK8_15 (S5PC1XX_EINT_BASE + 0x104)
30#define S5PC1XX_WKUP_INT_MASK16_23 (S5PC1XX_EINT_BASE + 0x108)
31#define S5PC1XX_WKUP_INT_MASK24_31 (S5PC1XX_EINT_BASE + 0x10C)
32#define S5PC1XX_WKUP_INT_MASK(x) (S5PC1XX_WKUP_INT_MASK0_7 + (x * 0x4))
33
34#define S5PC1XX_WKUP_INT_PEND0_7 (S5PC1XX_EINT_BASE + 0x140)
35#define S5PC1XX_WKUP_INT_PEND8_15 (S5PC1XX_EINT_BASE + 0x144)
36#define S5PC1XX_WKUP_INT_PEND16_23 (S5PC1XX_EINT_BASE + 0x148)
37#define S5PC1XX_WKUP_INT_PEND24_31 (S5PC1XX_EINT_BASE + 0x14C)
38#define S5PC1XX_WKUP_INT_PEND(x) (S5PC1XX_WKUP_INT_PEND0_7 + (x * 0x4))
39
40#define S5PC1XX_WKUP_INT_LOWLEV (0x00)
41#define S5PC1XX_WKUP_INT_HILEV (0x01)
42#define S5PC1XX_WKUP_INT_FALLEDGE (0x02)
43#define S5PC1XX_WKUP_INT_RISEEDGE (0x03)
44#define S5PC1XX_WKUP_INT_BOTHEDGE (0x04)
diff --git a/arch/arm/plat-s5pc1xx/include/plat/irqs.h b/arch/arm/plat-s5pc1xx/include/plat/irqs.h
index f07d8c3b25d6..ef8736366f0d 100644
--- a/arch/arm/plat-s5pc1xx/include/plat/irqs.h
+++ b/arch/arm/plat-s5pc1xx/include/plat/irqs.h
@@ -171,12 +171,21 @@
171#define IRQ_SDMIRQ S5PC1XX_IRQ_VIC2(30) 171#define IRQ_SDMIRQ S5PC1XX_IRQ_VIC2(30)
172#define IRQ_SDMFIQ S5PC1XX_IRQ_VIC2(31) 172#define IRQ_SDMFIQ S5PC1XX_IRQ_VIC2(31)
173 173
174/* External interrupt */
174#define S3C_IRQ_EINT_BASE (IRQ_SDMFIQ + 1) 175#define S3C_IRQ_EINT_BASE (IRQ_SDMFIQ + 1)
175 176
176#define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE) 177#define S3C_EINT(x) (S3C_IRQ_EINT_BASE + (x - 16))
177#define IRQ_EINT(x) S3C_EINT(x) 178#define IRQ_EINT(x) (x < 16 ? IRQ_EINT0 + x : S3C_EINT(x))
179#define IRQ_EINT_BIT(x) (x < IRQ_EINT16_31 ? x - IRQ_EINT0 : x - S3C_EINT(0))
178 180
179#define NR_IRQS (IRQ_EINT(31)+1) 181/* GPIO interrupt */
182#define S3C_IRQ_GPIO_BASE (IRQ_EINT(31) + 1)
183#define S3C_IRQ_GPIO(x) (S3C_IRQ_GPIO_BASE + (x))
184
185/*
186 * Until MP04 Groups -> 40 (exactly 39) Groups * 8 ~= 320 GPIOs
187 */
188#define NR_IRQS (S3C_IRQ_GPIO(320) + 1)
180 189
181#endif /* __ASM_PLAT_S5PC1XX_IRQS_H */ 190#endif /* __ASM_PLAT_S5PC1XX_IRQS_H */
182 191
diff --git a/arch/arm/plat-s5pc1xx/include/plat/regs-gpio.h b/arch/arm/plat-s5pc1xx/include/plat/regs-gpio.h
new file mode 100644
index 000000000000..43c7bc8bf784
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/include/plat/regs-gpio.h
@@ -0,0 +1,70 @@
1/* linux/arch/arm/plat-s5pc1xx/include/plat/regs-gpio.h
2 *
3 * Copyright 2009 Samsung Electronics Co.
4 * Byungho Min <bhmin@samsung.com>
5 *
6 * S5PC1XX - GPIO register definitions
7 */
8
9#ifndef __ASM_PLAT_S5PC1XX_REGS_GPIO_H
10#define __ASM_PLAT_S5PC1XX_REGS_GPIO_H __FILE__
11
12#include <mach/map.h>
13
14/* S5PC100 */
15#define S5PC100_GPIO_BASE S5PC1XX_VA_GPIO
16#define S5PC100_GPA0_BASE (S5PC100_GPIO_BASE + 0x0000)
17#define S5PC100_GPA1_BASE (S5PC100_GPIO_BASE + 0x0020)
18#define S5PC100_GPB_BASE (S5PC100_GPIO_BASE + 0x0040)
19#define S5PC100_GPC_BASE (S5PC100_GPIO_BASE + 0x0060)
20#define S5PC100_GPD_BASE (S5PC100_GPIO_BASE + 0x0080)
21#define S5PC100_GPE0_BASE (S5PC100_GPIO_BASE + 0x00A0)
22#define S5PC100_GPE1_BASE (S5PC100_GPIO_BASE + 0x00C0)
23#define S5PC100_GPF0_BASE (S5PC100_GPIO_BASE + 0x00E0)
24#define S5PC100_GPF1_BASE (S5PC100_GPIO_BASE + 0x0100)
25#define S5PC100_GPF2_BASE (S5PC100_GPIO_BASE + 0x0120)
26#define S5PC100_GPF3_BASE (S5PC100_GPIO_BASE + 0x0140)
27#define S5PC100_GPG0_BASE (S5PC100_GPIO_BASE + 0x0160)
28#define S5PC100_GPG1_BASE (S5PC100_GPIO_BASE + 0x0180)
29#define S5PC100_GPG2_BASE (S5PC100_GPIO_BASE + 0x01A0)
30#define S5PC100_GPG3_BASE (S5PC100_GPIO_BASE + 0x01C0)
31#define S5PC100_GPH0_BASE (S5PC100_GPIO_BASE + 0x0C00)
32#define S5PC100_GPH1_BASE (S5PC100_GPIO_BASE + 0x0C20)
33#define S5PC100_GPH2_BASE (S5PC100_GPIO_BASE + 0x0C40)
34#define S5PC100_GPH3_BASE (S5PC100_GPIO_BASE + 0x0C60)
35#define S5PC100_GPI_BASE (S5PC100_GPIO_BASE + 0x01E0)
36#define S5PC100_GPJ0_BASE (S5PC100_GPIO_BASE + 0x0200)
37#define S5PC100_GPJ1_BASE (S5PC100_GPIO_BASE + 0x0220)
38#define S5PC100_GPJ2_BASE (S5PC100_GPIO_BASE + 0x0240)
39#define S5PC100_GPJ3_BASE (S5PC100_GPIO_BASE + 0x0260)
40#define S5PC100_GPJ4_BASE (S5PC100_GPIO_BASE + 0x0280)
41#define S5PC100_GPK0_BASE (S5PC100_GPIO_BASE + 0x02A0)
42#define S5PC100_GPK1_BASE (S5PC100_GPIO_BASE + 0x02C0)
43#define S5PC100_GPK2_BASE (S5PC100_GPIO_BASE + 0x02E0)
44#define S5PC100_GPK3_BASE (S5PC100_GPIO_BASE + 0x0300)
45#define S5PC100_GPL0_BASE (S5PC100_GPIO_BASE + 0x0320)
46#define S5PC100_GPL1_BASE (S5PC100_GPIO_BASE + 0x0340)
47#define S5PC100_GPL2_BASE (S5PC100_GPIO_BASE + 0x0360)
48#define S5PC100_GPL3_BASE (S5PC100_GPIO_BASE + 0x0380)
49#define S5PC100_GPL4_BASE (S5PC100_GPIO_BASE + 0x03A0)
50#define S5PC100_EINT_BASE (S5PC100_GPIO_BASE + 0x0E00)
51
52#define S5PC100_UHOST (S5PC100_GPIO_BASE + 0x0B68)
53#define S5PC100_PDNEN (S5PC100_GPIO_BASE + 0x0F80)
54
55/* PDNEN */
56#define S5PC100_PDNEN_CFG_PDNEN (1 << 1)
57#define S5PC100_PDNEN_CFG_AUTO (0 << 1)
58#define S5PC100_PDNEN_POWERDOWN (1 << 0)
59#define S5PC100_PDNEN_NORMAL (0 << 0)
60
61/* Common part */
62/* External interrupt base is same at both s5pc100 and s5pc110 */
63#define S5PC1XX_EINT_BASE (S5PC100_EINT_BASE)
64
65#define S5PC100_GPx_INPUT(__gpio) (0x0 << ((__gpio) * 4))
66#define S5PC100_GPx_OUTPUT(__gpio) (0x1 << ((__gpio) * 4))
67#define S5PC100_GPx_CONMASK(__gpio) (0xf << ((__gpio) * 4))
68
69#endif /* __ASM_PLAT_S5PC1XX_REGS_GPIO_H */
70
diff --git a/arch/arm/plat-s5pc1xx/irq-eint.c b/arch/arm/plat-s5pc1xx/irq-eint.c
new file mode 100644
index 000000000000..373122f57d56
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/irq-eint.c
@@ -0,0 +1,281 @@
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
43static 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
55static inline int s3c_get_bank(unsigned int irq)
56{
57 return s3c_get_eint(irq) >> 3;
58}
59
60static 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
70static 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
80static 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
90static 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
97static 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
104static 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
172static 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 */
188static 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
205static 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 */
214static 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
224static 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
234static 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
243static 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
250static 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
260static 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
281arch_initcall(s5pc1xx_init_irq_eint);
diff --git a/arch/arm/plat-s5pc1xx/irq-gpio.c b/arch/arm/plat-s5pc1xx/irq-gpio.c
new file mode 100644
index 000000000000..fecca7a679b0
--- /dev/null
+++ b/arch/arm/plat-s5pc1xx/irq-gpio.c
@@ -0,0 +1,266 @@
1/*
2 * arch/arm/plat-s5pc1xx/irq-gpio.c
3 *
4 * Copyright (C) 2009 Samsung Electronics
5 *
6 * S5PC1XX - Interrupt handling for IRQ_GPIO${group}(x)
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/gpio.h>
18
19#include <mach/map.h>
20#include <plat/gpio-cfg.h>
21
22#define S5PC1XX_GPIOREG(x) (S5PC1XX_VA_GPIO + (x))
23
24#define CON_OFFSET 0x700
25#define MASK_OFFSET 0x900
26#define PEND_OFFSET 0xA00
27#define CON_OFFSET_2 0xE00
28#define MASK_OFFSET_2 0xF00
29#define PEND_OFFSET_2 0xF40
30
31#define GPIOINT_LEVEL_LOW 0x0
32#define GPIOINT_LEVEL_HIGH 0x1
33#define GPIOINT_EDGE_FALLING 0x2
34#define GPIOINT_EDGE_RISING 0x3
35#define GPIOINT_EDGE_BOTH 0x4
36
37static int group_to_con_offset(int group)
38{
39 return group << 2;
40}
41
42static int group_to_mask_offset(int group)
43{
44 return group << 2;
45}
46
47static int group_to_pend_offset(int group)
48{
49 return group << 2;
50}
51
52static int s5pc1xx_get_start(unsigned int group)
53{
54 switch (group) {
55 case 0: return S5PC100_GPIO_A0_START;
56 case 1: return S5PC100_GPIO_A1_START;
57 case 2: return S5PC100_GPIO_B_START;
58 case 3: return S5PC100_GPIO_C_START;
59 case 4: return S5PC100_GPIO_D_START;
60 case 5: return S5PC100_GPIO_E0_START;
61 case 6: return S5PC100_GPIO_E1_START;
62 case 7: return S5PC100_GPIO_F0_START;
63 case 8: return S5PC100_GPIO_F1_START;
64 case 9: return S5PC100_GPIO_F2_START;
65 case 10: return S5PC100_GPIO_F3_START;
66 case 11: return S5PC100_GPIO_G0_START;
67 case 12: return S5PC100_GPIO_G1_START;
68 case 13: return S5PC100_GPIO_G2_START;
69 case 14: return S5PC100_GPIO_G3_START;
70 case 15: return S5PC100_GPIO_I_START;
71 case 16: return S5PC100_GPIO_J0_START;
72 case 17: return S5PC100_GPIO_J1_START;
73 case 18: return S5PC100_GPIO_J2_START;
74 case 19: return S5PC100_GPIO_J3_START;
75 case 20: return S5PC100_GPIO_J4_START;
76 default:
77 BUG();
78 }
79
80 return -EINVAL;
81}
82
83static int s5pc1xx_get_group(unsigned int irq)
84{
85 irq -= S3C_IRQ_GPIO(0);
86
87 switch (irq) {
88 case S5PC100_GPIO_A0_START ... S5PC100_GPIO_A1_START - 1:
89 return 0;
90 case S5PC100_GPIO_A1_START ... S5PC100_GPIO_B_START - 1:
91 return 1;
92 case S5PC100_GPIO_B_START ... S5PC100_GPIO_C_START - 1:
93 return 2;
94 case S5PC100_GPIO_C_START ... S5PC100_GPIO_D_START - 1:
95 return 3;
96 case S5PC100_GPIO_D_START ... S5PC100_GPIO_E0_START - 1:
97 return 4;
98 case S5PC100_GPIO_E0_START ... S5PC100_GPIO_E1_START - 1:
99 return 5;
100 case S5PC100_GPIO_E1_START ... S5PC100_GPIO_F0_START - 1:
101 return 6;
102 case S5PC100_GPIO_F0_START ... S5PC100_GPIO_F1_START - 1:
103 return 7;
104 case S5PC100_GPIO_F1_START ... S5PC100_GPIO_F2_START - 1:
105 return 8;
106 case S5PC100_GPIO_F2_START ... S5PC100_GPIO_F3_START - 1:
107 return 9;
108 case S5PC100_GPIO_F3_START ... S5PC100_GPIO_G0_START - 1:
109 return 10;
110 case S5PC100_GPIO_G0_START ... S5PC100_GPIO_G1_START - 1:
111 return 11;
112 case S5PC100_GPIO_G1_START ... S5PC100_GPIO_G2_START - 1:
113 return 12;
114 case S5PC100_GPIO_G2_START ... S5PC100_GPIO_G3_START - 1:
115 return 13;
116 case S5PC100_GPIO_G3_START ... S5PC100_GPIO_H0_START - 1:
117 return 14;
118 case S5PC100_GPIO_I_START ... S5PC100_GPIO_J0_START - 1:
119 return 15;
120 case S5PC100_GPIO_J0_START ... S5PC100_GPIO_J1_START - 1:
121 return 16;
122 case S5PC100_GPIO_J1_START ... S5PC100_GPIO_J2_START - 1:
123 return 17;
124 case S5PC100_GPIO_J2_START ... S5PC100_GPIO_J3_START - 1:
125 return 18;
126 case S5PC100_GPIO_J3_START ... S5PC100_GPIO_J4_START - 1:
127 return 19;
128 case S5PC100_GPIO_J4_START ... S5PC100_GPIO_K0_START - 1:
129 return 20;
130 default:
131 BUG();
132 }
133
134 return -EINVAL;
135}
136
137static int s5pc1xx_get_offset(unsigned int irq)
138{
139 struct gpio_chip *chip = get_irq_data(irq);
140 return irq - S3C_IRQ_GPIO(chip->base);
141}
142
143static void s5pc1xx_gpioint_ack(unsigned int irq)
144{
145 int group, offset, pend_offset;
146 unsigned int value;
147
148 group = s5pc1xx_get_group(irq);
149 offset = s5pc1xx_get_offset(irq);
150 pend_offset = group_to_pend_offset(group);
151
152 value = __raw_readl(S5PC1XX_GPIOREG(PEND_OFFSET) + pend_offset);
153 value |= 1 << offset;
154 __raw_writel(value, S5PC1XX_GPIOREG(PEND_OFFSET) + pend_offset);
155}
156
157static void s5pc1xx_gpioint_mask(unsigned int irq)
158{
159 int group, offset, mask_offset;
160 unsigned int value;
161
162 group = s5pc1xx_get_group(irq);
163 offset = s5pc1xx_get_offset(irq);
164 mask_offset = group_to_mask_offset(group);
165
166 value = __raw_readl(S5PC1XX_GPIOREG(MASK_OFFSET) + mask_offset);
167 value |= 1 << offset;
168 __raw_writel(value, S5PC1XX_GPIOREG(MASK_OFFSET) + mask_offset);
169}
170
171static void s5pc1xx_gpioint_unmask(unsigned int irq)
172{
173 int group, offset, mask_offset;
174 unsigned int value;
175
176 group = s5pc1xx_get_group(irq);
177 offset = s5pc1xx_get_offset(irq);
178 mask_offset = group_to_mask_offset(group);
179
180 value = __raw_readl(S5PC1XX_GPIOREG(MASK_OFFSET) + mask_offset);
181 value &= ~(1 << offset);
182 __raw_writel(value, S5PC1XX_GPIOREG(MASK_OFFSET) + mask_offset);
183}
184
185static void s5pc1xx_gpioint_mask_ack(unsigned int irq)
186{
187 s5pc1xx_gpioint_mask(irq);
188 s5pc1xx_gpioint_ack(irq);
189}
190
191static int s5pc1xx_gpioint_set_type(unsigned int irq, unsigned int type)
192{
193 int group, offset, con_offset;
194 unsigned int value;
195
196 group = s5pc1xx_get_group(irq);
197 offset = s5pc1xx_get_offset(irq);
198 con_offset = group_to_con_offset(group);
199
200 switch (type) {
201 case IRQ_TYPE_NONE:
202 printk(KERN_WARNING "No irq type\n");
203 return -EINVAL;
204 case IRQ_TYPE_EDGE_RISING:
205 type = GPIOINT_EDGE_RISING;
206 break;
207 case IRQ_TYPE_EDGE_FALLING:
208 type = GPIOINT_EDGE_FALLING;
209 break;
210 case IRQ_TYPE_EDGE_BOTH:
211 type = GPIOINT_EDGE_BOTH;
212 break;
213 case IRQ_TYPE_LEVEL_HIGH:
214 type = GPIOINT_LEVEL_HIGH;
215 break;
216 case IRQ_TYPE_LEVEL_LOW:
217 type = GPIOINT_LEVEL_LOW;
218 break;
219 default:
220 BUG();
221 }
222
223
224 value = __raw_readl(S5PC1XX_GPIOREG(CON_OFFSET) + con_offset);
225 value &= ~(0xf << (offset * 0x4));
226 value |= (type << (offset * 0x4));
227 __raw_writel(value, S5PC1XX_GPIOREG(CON_OFFSET) + con_offset);
228
229 return 0;
230}
231
232struct irq_chip s5pc1xx_gpioint = {
233 .name = "GPIO",
234 .ack = s5pc1xx_gpioint_ack,
235 .mask = s5pc1xx_gpioint_mask,
236 .mask_ack = s5pc1xx_gpioint_mask_ack,
237 .unmask = s5pc1xx_gpioint_unmask,
238 .set_type = s5pc1xx_gpioint_set_type,
239};
240
241void s5pc1xx_irq_gpioint_handler(unsigned int irq, struct irq_desc *desc)
242{
243 int group, offset, pend_offset, mask_offset;
244 int real_irq, group_end;
245 unsigned int pend, mask;
246
247 group_end = 21;
248
249 for (group = 0; group < group_end; group++) {
250 pend_offset = group_to_pend_offset(group);
251 pend = __raw_readl(S5PC1XX_GPIOREG(PEND_OFFSET) + pend_offset);
252 if (!pend)
253 continue;
254
255 mask_offset = group_to_mask_offset(group);
256 mask = __raw_readl(S5PC1XX_GPIOREG(MASK_OFFSET) + mask_offset);
257 pend &= ~mask;
258
259 for (offset = 0; offset < 8; offset++) {
260 if (pend & (1 << offset)) {
261 real_irq = s5pc1xx_get_start(group) + offset;
262 generic_handle_irq(S3C_IRQ_GPIO(real_irq));
263 }
264 }
265 }
266}
diff --git a/arch/arm/plat-s5pc1xx/irq.c b/arch/arm/plat-s5pc1xx/irq.c
index 80d6dd942cb8..e44fd04ef333 100644
--- a/arch/arm/plat-s5pc1xx/irq.c
+++ b/arch/arm/plat-s5pc1xx/irq.c
@@ -79,7 +79,7 @@ static void s3c_irq_timer_ack(unsigned int irq)
79{ 79{
80 u32 reg = __raw_readl(S3C64XX_TINT_CSTAT); 80 u32 reg = __raw_readl(S3C64XX_TINT_CSTAT);
81 81
82 reg &= 0x1f; 82 reg &= 0x1f; /* mask out pending interrupts */
83 reg |= (1 << 5) << (irq - IRQ_TIMER0); 83 reg |= (1 << 5) << (irq - IRQ_TIMER0);
84 __raw_writel(reg, S3C64XX_TINT_CSTAT); 84 __raw_writel(reg, S3C64XX_TINT_CSTAT);
85} 85}