2 * arch/arm/plat-s5pc1xx/irq-gpio.c
4 * Copyright (C) 2009 Samsung Electronics
6 * S5PC1XX - Interrupt handling for IRQ_GPIO${group}(x)
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.
13 #include <linux/kernel.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
17 #include <linux/gpio.h>
20 #include <plat/gpio-cfg.h>
22 #define S5PC1XX_GPIOREG(x) (S5PC1XX_VA_GPIO + (x))
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
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
37 static int group_to_con_offset(int group
)
42 static int group_to_mask_offset(int group
)
47 static int group_to_pend_offset(int group
)
52 static int s5pc1xx_get_start(unsigned int 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
;
83 static int s5pc1xx_get_group(unsigned int irq
)
85 irq
-= S3C_IRQ_GPIO(0);
88 case S5PC100_GPIO_A0_START
... S5PC100_GPIO_A1_START
- 1:
90 case S5PC100_GPIO_A1_START
... S5PC100_GPIO_B_START
- 1:
92 case S5PC100_GPIO_B_START
... S5PC100_GPIO_C_START
- 1:
94 case S5PC100_GPIO_C_START
... S5PC100_GPIO_D_START
- 1:
96 case S5PC100_GPIO_D_START
... S5PC100_GPIO_E0_START
- 1:
98 case S5PC100_GPIO_E0_START
... S5PC100_GPIO_E1_START
- 1:
100 case S5PC100_GPIO_E1_START
... S5PC100_GPIO_F0_START
- 1:
102 case S5PC100_GPIO_F0_START
... S5PC100_GPIO_F1_START
- 1:
104 case S5PC100_GPIO_F1_START
... S5PC100_GPIO_F2_START
- 1:
106 case S5PC100_GPIO_F2_START
... S5PC100_GPIO_F3_START
- 1:
108 case S5PC100_GPIO_F3_START
... S5PC100_GPIO_G0_START
- 1:
110 case S5PC100_GPIO_G0_START
... S5PC100_GPIO_G1_START
- 1:
112 case S5PC100_GPIO_G1_START
... S5PC100_GPIO_G2_START
- 1:
114 case S5PC100_GPIO_G2_START
... S5PC100_GPIO_G3_START
- 1:
116 case S5PC100_GPIO_G3_START
... S5PC100_GPIO_H0_START
- 1:
118 case S5PC100_GPIO_I_START
... S5PC100_GPIO_J0_START
- 1:
120 case S5PC100_GPIO_J0_START
... S5PC100_GPIO_J1_START
- 1:
122 case S5PC100_GPIO_J1_START
... S5PC100_GPIO_J2_START
- 1:
124 case S5PC100_GPIO_J2_START
... S5PC100_GPIO_J3_START
- 1:
126 case S5PC100_GPIO_J3_START
... S5PC100_GPIO_J4_START
- 1:
128 case S5PC100_GPIO_J4_START
... S5PC100_GPIO_K0_START
- 1:
137 static int s5pc1xx_get_offset(unsigned int irq
)
139 struct gpio_chip
*chip
= get_irq_data(irq
);
140 return irq
- S3C_IRQ_GPIO(chip
->base
);
143 static void s5pc1xx_gpioint_ack(unsigned int irq
)
145 int group
, offset
, pend_offset
;
148 group
= s5pc1xx_get_group(irq
);
149 offset
= s5pc1xx_get_offset(irq
);
150 pend_offset
= group_to_pend_offset(group
);
152 value
= __raw_readl(S5PC1XX_GPIOREG(PEND_OFFSET
) + pend_offset
);
153 value
|= 1 << offset
;
154 __raw_writel(value
, S5PC1XX_GPIOREG(PEND_OFFSET
) + pend_offset
);
157 static void s5pc1xx_gpioint_mask(unsigned int irq
)
159 int group
, offset
, mask_offset
;
162 group
= s5pc1xx_get_group(irq
);
163 offset
= s5pc1xx_get_offset(irq
);
164 mask_offset
= group_to_mask_offset(group
);
166 value
= __raw_readl(S5PC1XX_GPIOREG(MASK_OFFSET
) + mask_offset
);
167 value
|= 1 << offset
;
168 __raw_writel(value
, S5PC1XX_GPIOREG(MASK_OFFSET
) + mask_offset
);
171 static void s5pc1xx_gpioint_unmask(unsigned int irq
)
173 int group
, offset
, mask_offset
;
176 group
= s5pc1xx_get_group(irq
);
177 offset
= s5pc1xx_get_offset(irq
);
178 mask_offset
= group_to_mask_offset(group
);
180 value
= __raw_readl(S5PC1XX_GPIOREG(MASK_OFFSET
) + mask_offset
);
181 value
&= ~(1 << offset
);
182 __raw_writel(value
, S5PC1XX_GPIOREG(MASK_OFFSET
) + mask_offset
);
185 static void s5pc1xx_gpioint_mask_ack(unsigned int irq
)
187 s5pc1xx_gpioint_mask(irq
);
188 s5pc1xx_gpioint_ack(irq
);
191 static int s5pc1xx_gpioint_set_type(unsigned int irq
, unsigned int type
)
193 int group
, offset
, con_offset
;
196 group
= s5pc1xx_get_group(irq
);
197 offset
= s5pc1xx_get_offset(irq
);
198 con_offset
= group_to_con_offset(group
);
202 printk(KERN_WARNING
"No irq type\n");
204 case IRQ_TYPE_EDGE_RISING
:
205 type
= GPIOINT_EDGE_RISING
;
207 case IRQ_TYPE_EDGE_FALLING
:
208 type
= GPIOINT_EDGE_FALLING
;
210 case IRQ_TYPE_EDGE_BOTH
:
211 type
= GPIOINT_EDGE_BOTH
;
213 case IRQ_TYPE_LEVEL_HIGH
:
214 type
= GPIOINT_LEVEL_HIGH
;
216 case IRQ_TYPE_LEVEL_LOW
:
217 type
= GPIOINT_LEVEL_LOW
;
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
);
232 struct irq_chip s5pc1xx_gpioint
= {
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
,
241 void s5pc1xx_irq_gpioint_handler(unsigned int irq
, struct irq_desc
*desc
)
243 int group
, offset
, pend_offset
, mask_offset
;
244 int real_irq
, group_end
;
245 unsigned int pend
, mask
;
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
);
255 mask_offset
= group_to_mask_offset(group
);
256 mask
= __raw_readl(S5PC1XX_GPIOREG(MASK_OFFSET
) + mask_offset
);
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
));