blob: 2bf86c18bc73a3aeae8ead9341789e76f32ab4eb [file] [log] [blame]
Kyungmin Parkb0d52172009-11-17 08:41:16 +01001/*
Marek Szyprowski23686a02010-05-20 07:51:09 +02002 * arch/arm/mach-s5pc100/irq-gpio.c
Kyungmin Parkb0d52172009-11-17 08:41:16 +01003 *
4 * Copyright (C) 2009 Samsung Electronics
5 *
Marek Szyprowski23686a02010-05-20 07:51:09 +02006 * S5PC100 - Interrupt handling for IRQ_GPIO${group}(x)
Kyungmin Parkb0d52172009-11-17 08:41:16 +01007 *
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
Marek Szyprowski23686a02010-05-20 07:51:09 +020022#define S5P_GPIOREG(x) (S5P_VA_GPIO + (x))
Kyungmin Parkb0d52172009-11-17 08:41:16 +010023
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
Marek Szyprowski23686a02010-05-20 07:51:09 +020052static int s5pc100_get_start(unsigned int group)
Kyungmin Parkb0d52172009-11-17 08:41:16 +010053{
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
Marek Szyprowski23686a02010-05-20 07:51:09 +020083static int s5pc100_get_group(unsigned int irq)
Kyungmin Parkb0d52172009-11-17 08:41:16 +010084{
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
Marek Szyprowski23686a02010-05-20 07:51:09 +0200137static int s5pc100_get_offset(unsigned int irq)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100138{
139 struct gpio_chip *chip = get_irq_data(irq);
140 return irq - S3C_IRQ_GPIO(chip->base);
141}
142
Marek Szyprowski23686a02010-05-20 07:51:09 +0200143static void s5pc100_gpioint_ack(unsigned int irq)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100144{
145 int group, offset, pend_offset;
146 unsigned int value;
147
Marek Szyprowski23686a02010-05-20 07:51:09 +0200148 group = s5pc100_get_group(irq);
149 offset = s5pc100_get_offset(irq);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100150 pend_offset = group_to_pend_offset(group);
151
Marek Szyprowski23686a02010-05-20 07:51:09 +0200152 value = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100153 value |= 1 << offset;
Marek Szyprowski23686a02010-05-20 07:51:09 +0200154 __raw_writel(value, S5P_GPIOREG(PEND_OFFSET) + pend_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100155}
156
Marek Szyprowski23686a02010-05-20 07:51:09 +0200157static void s5pc100_gpioint_mask(unsigned int irq)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100158{
159 int group, offset, mask_offset;
160 unsigned int value;
161
Marek Szyprowski23686a02010-05-20 07:51:09 +0200162 group = s5pc100_get_group(irq);
163 offset = s5pc100_get_offset(irq);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100164 mask_offset = group_to_mask_offset(group);
165
Marek Szyprowski23686a02010-05-20 07:51:09 +0200166 value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100167 value |= 1 << offset;
Marek Szyprowski23686a02010-05-20 07:51:09 +0200168 __raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100169}
170
Marek Szyprowski23686a02010-05-20 07:51:09 +0200171static void s5pc100_gpioint_unmask(unsigned int irq)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100172{
173 int group, offset, mask_offset;
174 unsigned int value;
175
Marek Szyprowski23686a02010-05-20 07:51:09 +0200176 group = s5pc100_get_group(irq);
177 offset = s5pc100_get_offset(irq);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100178 mask_offset = group_to_mask_offset(group);
179
Marek Szyprowski23686a02010-05-20 07:51:09 +0200180 value = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100181 value &= ~(1 << offset);
Marek Szyprowski23686a02010-05-20 07:51:09 +0200182 __raw_writel(value, S5P_GPIOREG(MASK_OFFSET) + mask_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100183}
184
Marek Szyprowski23686a02010-05-20 07:51:09 +0200185static void s5pc100_gpioint_mask_ack(unsigned int irq)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100186{
Marek Szyprowski23686a02010-05-20 07:51:09 +0200187 s5pc100_gpioint_mask(irq);
188 s5pc100_gpioint_ack(irq);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100189}
190
Marek Szyprowski23686a02010-05-20 07:51:09 +0200191static int s5pc100_gpioint_set_type(unsigned int irq, unsigned int type)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100192{
193 int group, offset, con_offset;
194 unsigned int value;
195
Marek Szyprowski23686a02010-05-20 07:51:09 +0200196 group = s5pc100_get_group(irq);
197 offset = s5pc100_get_offset(irq);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100198 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
Marek Szyprowski23686a02010-05-20 07:51:09 +0200224 value = __raw_readl(S5P_GPIOREG(CON_OFFSET) + con_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100225 value &= ~(0xf << (offset * 0x4));
226 value |= (type << (offset * 0x4));
Marek Szyprowski23686a02010-05-20 07:51:09 +0200227 __raw_writel(value, S5P_GPIOREG(CON_OFFSET) + con_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100228
229 return 0;
230}
231
Marek Szyprowski23686a02010-05-20 07:51:09 +0200232struct irq_chip s5pc100_gpioint = {
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100233 .name = "GPIO",
Marek Szyprowski23686a02010-05-20 07:51:09 +0200234 .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,
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100239};
240
Marek Szyprowski23686a02010-05-20 07:51:09 +0200241void s5pc100_irq_gpioint_handler(unsigned int irq, struct irq_desc *desc)
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100242{
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);
Marek Szyprowski23686a02010-05-20 07:51:09 +0200251 pend = __raw_readl(S5P_GPIOREG(PEND_OFFSET) + pend_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100252 if (!pend)
253 continue;
254
255 mask_offset = group_to_mask_offset(group);
Marek Szyprowski23686a02010-05-20 07:51:09 +0200256 mask = __raw_readl(S5P_GPIOREG(MASK_OFFSET) + mask_offset);
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100257 pend &= ~mask;
258
259 for (offset = 0; offset < 8; offset++) {
260 if (pend & (1 << offset)) {
Marek Szyprowski23686a02010-05-20 07:51:09 +0200261 real_irq = s5pc100_get_start(group) + offset;
Kyungmin Parkb0d52172009-11-17 08:41:16 +0100262 generic_handle_irq(S3C_IRQ_GPIO(real_irq));
263 }
264 }
265 }
266}