aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-s5pc100
diff options
context:
space:
mode:
authorMarek Szyprowski <m.szyprowski@samsung.com>2010-05-20 01:51:09 -0400
committerBen Dooks <ben-linux@fluff.org>2010-05-20 02:28:43 -0400
commit23686a07b6222d5c40ea403705325e49d360603e (patch)
tree0b2ecdde9c30bbcbe43fe40e4a1249de7315775a /arch/arm/mach-s5pc100
parentacc84707d3487735fc666fdeab76185d086428c0 (diff)
ARM: S5PC100: Add support for gpio interrupt
This patch moves support for gpio interrupts from plat-s5pc1xx to mach-s5pc100 directory. Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com> Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Diffstat (limited to 'arch/arm/mach-s5pc100')
-rw-r--r--arch/arm/mach-s5pc100/Makefile2
-rw-r--r--arch/arm/mach-s5pc100/gpiolib.c9
-rw-r--r--arch/arm/mach-s5pc100/irq-gpio.c266
3 files changed, 271 insertions, 6 deletions
diff --git a/arch/arm/mach-s5pc100/Makefile b/arch/arm/mach-s5pc100/Makefile
index 9b52d2af013f..6e3f57c39c97 100644
--- a/arch/arm/mach-s5pc100/Makefile
+++ b/arch/arm/mach-s5pc100/Makefile
@@ -11,7 +11,7 @@ obj- :=
11 11
12# Core support for S5PC100 system 12# Core support for S5PC100 system
13 13
14obj-$(CONFIG_CPU_S5PC100) += cpu.o init.o clock.o gpiolib.o 14obj-$(CONFIG_CPU_S5PC100) += cpu.o init.o clock.o gpiolib.o irq-gpio.o
15obj-$(CONFIG_CPU_S5PC100) += setup-i2c0.o 15obj-$(CONFIG_CPU_S5PC100) += setup-i2c0.o
16 16
17# Helper and device support 17# Helper and device support
diff --git a/arch/arm/mach-s5pc100/gpiolib.c b/arch/arm/mach-s5pc100/gpiolib.c
index 494a53b10479..88dd913c86d4 100644
--- a/arch/arm/mach-s5pc100/gpiolib.c
+++ b/arch/arm/mach-s5pc100/gpiolib.c
@@ -387,7 +387,6 @@ extern void s5pc100_irq_gpioint_handler(unsigned int irq, struct irq_desc *desc)
387 387
388static __init void s5pc100_gpiolib_link(struct s3c_gpio_chip *chip) 388static __init void s5pc100_gpiolib_link(struct s3c_gpio_chip *chip)
389{ 389{
390#if 0
391 /* Interrupt */ 390 /* Interrupt */
392 if (chip->config == &gpio_cfg) { 391 if (chip->config == &gpio_cfg) {
393 int i, irq; 392 int i, irq;
@@ -401,9 +400,9 @@ static __init void s5pc100_gpiolib_link(struct s3c_gpio_chip *chip)
401 set_irq_handler(irq, handle_level_irq); 400 set_irq_handler(irq, handle_level_irq);
402 set_irq_flags(irq, IRQF_VALID); 401 set_irq_flags(irq, IRQF_VALID);
403 } 402 }
404 } else if (chip->config == &gpio_cfg_eint) 403 } else if (chip->config == &gpio_cfg_eint) {
405 chip->chip.to_irq = s5pc100_gpiolib_to_eint; 404 chip->chip.to_irq = s5pc100_gpiolib_to_eint;
406#endif 405 }
407} 406}
408 407
409static __init int s5pc100_gpiolib_init(void) 408static __init int s5pc100_gpiolib_init(void)
@@ -419,10 +418,10 @@ static __init int s5pc100_gpiolib_init(void)
419 418
420 samsung_gpiolib_add_4bit_chips(s5pc100_gpio_chips, 419 samsung_gpiolib_add_4bit_chips(s5pc100_gpio_chips,
421 ARRAY_SIZE(s5pc100_gpio_chips)); 420 ARRAY_SIZE(s5pc100_gpio_chips));
422#if 0 421
423 /* Interrupt */ 422 /* Interrupt */
424 set_irq_chained_handler(IRQ_GPIOINT, s5pc100_irq_gpioint_handler); 423 set_irq_chained_handler(IRQ_GPIOINT, s5pc100_irq_gpioint_handler);
425#endif 424
426 return 0; 425 return 0;
427} 426}
428core_initcall(s5pc100_gpiolib_init); 427core_initcall(s5pc100_gpiolib_init);
diff --git a/arch/arm/mach-s5pc100/irq-gpio.c b/arch/arm/mach-s5pc100/irq-gpio.c
new file mode 100644
index 000000000000..2bf86c18bc73
--- /dev/null
+++ b/arch/arm/mach-s5pc100/irq-gpio.c
@@ -0,0 +1,266 @@
1/*
2 * arch/arm/mach-s5pc100/irq-gpio.c
3 *
4 * Copyright (C) 2009 Samsung Electronics
5 *
6 * S5PC100 - 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 S5P_GPIOREG(x) (S5P_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 s5pc100_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 s5pc100_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 s5pc100_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 s5pc100_gpioint_ack(unsigned int irq)
144{
145 int group, offset, pend_offset;
146 unsigned int value;
147
148 group = s5pc100_get_group(irq);
149 offset = s5pc100_get_offset(irq);
150 pend_offset = group_to_pend_offset(group);
151
152 value = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
153 value |= 1 << offset;
154 __raw_writel(value, S5P_GPIOREG(PEND_OFFSET) + pend_offset);
155}
156
157static void s5pc100_gpioint_mask(unsigned int irq)
158{
159 int group, offset, mask_offset;
160 unsigned int value;
161
162 group = s5pc100_get_group(irq);
163 offset = s5pc100_get_offset(irq);
164 mask_offset = group_to_mask_offset(group);
165
166 value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
167 value |= 1 << offset;
168 __raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
169}
170
171static void s5pc100_gpioint_unmask(unsigned int irq)
172{
173 int group, offset, mask_offset;
174 unsigned int value;
175
176 group = s5pc100_get_group(irq);
177 offset = s5pc100_get_offset(irq);
178 mask_offset = group_to_mask_offset(group);
179
180 value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
181 value &= ~(1 << offset);
182 __raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
183}
184
185static void s5pc100_gpioint_mask_ack(unsigned int irq)
186{
187 s5pc100_gpioint_mask(irq);
188 s5pc100_gpioint_ack(irq);
189}
190
191static int s5pc100_gpioint_set_type(unsigned int irq, unsigned int type)
192{
193 int group, offset, con_offset;
194 unsigned int value;
195
196 group = s5pc100_get_group(irq);
197 offset = s5pc100_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(S5P_GPIOREG(CON_OFFSET) + con_offset);
225 value &= ~(0xf << (offset * 0x4));
226 value |= (type << (offset * 0x4));
227 __raw_writel(value, S5P_GPIOREG(CON_OFFSET) + con_offset);
228
229 return 0;
230}
231
232struct irq_chip s5pc100_gpioint = {
233 .name = "GPIO",
234 .ack = s5pc100_gpioint_ack,
235 .mask = s5pc100_gpioint_mask,
236 .mask_ack = s5pc100_gpioint_mask_ack,
237 .unmask = s5pc100_gpioint_unmask,
238 .set_type = s5pc100_gpioint_set_type,
239};
240
241void s5pc100_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(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
252 if (!pend)
253 continue;
254
255 mask_offset = group_to_mask_offset(group);
256 mask = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
257 pend &= ~mask;
258
259 for (offset = 0; offset < 8; offset++) {
260 if (pend & (1 << offset)) {
261 real_irq = s5pc100_get_start(group) + offset;
262 generic_handle_irq(S3C_IRQ_GPIO(real_irq));
263 }
264 }
265 }
266}