aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s5p/irq-gpioint.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-s5p/irq-gpioint.c')
-rw-r--r--arch/arm/plat-s5p/irq-gpioint.c170
1 files changed, 102 insertions, 68 deletions
diff --git a/arch/arm/plat-s5p/irq-gpioint.c b/arch/arm/plat-s5p/irq-gpioint.c
index 3b6bf89d1739..cd87d3256e03 100644
--- a/arch/arm/plat-s5p/irq-gpioint.c
+++ b/arch/arm/plat-s5p/irq-gpioint.c
@@ -17,82 +17,79 @@
17#include <linux/irq.h> 17#include <linux/irq.h>
18#include <linux/io.h> 18#include <linux/io.h>
19#include <linux/gpio.h> 19#include <linux/gpio.h>
20#include <linux/slab.h>
20 21
21#include <mach/map.h> 22#include <mach/map.h>
22#include <plat/gpio-core.h> 23#include <plat/gpio-core.h>
23#include <plat/gpio-cfg.h> 24#include <plat/gpio-cfg.h>
24 25
25#define S5P_GPIOREG(x) (S5P_VA_GPIO + (x)) 26#define GPIO_BASE(chip) (((unsigned long)(chip)->base) & 0xFFFFF000u)
26 27
27#define GPIOINT_CON_OFFSET 0x700 28#define CON_OFFSET 0x700
28#define GPIOINT_MASK_OFFSET 0x900 29#define MASK_OFFSET 0x900
29#define GPIOINT_PEND_OFFSET 0xA00 30#define PEND_OFFSET 0xA00
31#define REG_OFFSET(x) ((x) << 2)
30 32
31static struct s3c_gpio_chip *irq_chips[S5P_GPIOINT_GROUP_MAXNR]; 33struct s5p_gpioint_bank {
32 34 struct list_head list;
33static int s5p_gpioint_get_group(struct irq_data *data) 35 int start;
34{ 36 int nr_groups;
35 struct gpio_chip *chip = irq_data_get_irq_data(data); 37 int irq;
36 struct s3c_gpio_chip *s3c_chip = container_of(chip, 38 struct s3c_gpio_chip **chips;
37 struct s3c_gpio_chip, chip); 39 void (*handler)(unsigned int, struct irq_desc *);
38 int group; 40};
39
40 for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++)
41 if (s3c_chip == irq_chips[group])
42 break;
43 41
44 return group; 42LIST_HEAD(banks);
45}
46 43
47static int s5p_gpioint_get_offset(struct irq_data *data) 44static int s5p_gpioint_get_offset(struct irq_data *data)
48{ 45{
49 struct gpio_chip *chip = irq_data_get_irq_data(data); 46 struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
50 struct s3c_gpio_chip *s3c_chip = container_of(chip, 47 return data->irq - chip->irq_base;
51 struct s3c_gpio_chip, chip);
52
53 return data->irq - s3c_chip->irq_base;
54} 48}
55 49
56static void s5p_gpioint_ack(struct irq_data *data) 50static void s5p_gpioint_ack(struct irq_data *data)
57{ 51{
52 struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
58 int group, offset, pend_offset; 53 int group, offset, pend_offset;
59 unsigned int value; 54 unsigned int value;
60 55
61 group = s5p_gpioint_get_group(data); 56 group = chip->group;
62 offset = s5p_gpioint_get_offset(data); 57 offset = s5p_gpioint_get_offset(data);
63 pend_offset = group << 2; 58 pend_offset = REG_OFFSET(group);
64 59
65 value = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset); 60 value = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
66 value |= 1 << offset; 61 value |= BIT(offset);
67 __raw_writel(value, S5P_GPIOREG(GPIOINT_PEND_OFFSET) + pend_offset); 62 __raw_writel(value, GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
68} 63}
69 64
70static void s5p_gpioint_mask(struct irq_data *data) 65static void s5p_gpioint_mask(struct irq_data *data)
71{ 66{
67 struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
72 int group, offset, mask_offset; 68 int group, offset, mask_offset;
73 unsigned int value; 69 unsigned int value;
74 70
75 group = s5p_gpioint_get_group(data); 71 group = chip->group;
76 offset = s5p_gpioint_get_offset(data); 72 offset = s5p_gpioint_get_offset(data);
77 mask_offset = group << 2; 73 mask_offset = REG_OFFSET(group);
78 74
79 value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); 75 value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
80 value |= 1 << offset; 76 value |= BIT(offset);
81 __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); 77 __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
82} 78}
83 79
84static void s5p_gpioint_unmask(struct irq_data *data) 80static void s5p_gpioint_unmask(struct irq_data *data)
85{ 81{
82 struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
86 int group, offset, mask_offset; 83 int group, offset, mask_offset;
87 unsigned int value; 84 unsigned int value;
88 85
89 group = s5p_gpioint_get_group(data); 86 group = chip->group;
90 offset = s5p_gpioint_get_offset(data); 87 offset = s5p_gpioint_get_offset(data);
91 mask_offset = group << 2; 88 mask_offset = REG_OFFSET(group);
92 89
93 value = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); 90 value = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
94 value &= ~(1 << offset); 91 value &= ~BIT(offset);
95 __raw_writel(value, S5P_GPIOREG(GPIOINT_MASK_OFFSET) + mask_offset); 92 __raw_writel(value, GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
96} 93}
97 94
98static void s5p_gpioint_mask_ack(struct irq_data *data) 95static void s5p_gpioint_mask_ack(struct irq_data *data)
@@ -103,12 +100,13 @@ static void s5p_gpioint_mask_ack(struct irq_data *data)
103 100
104static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type) 101static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type)
105{ 102{
103 struct s3c_gpio_chip *chip = irq_data_get_irq_data(data);
106 int group, offset, con_offset; 104 int group, offset, con_offset;
107 unsigned int value; 105 unsigned int value;
108 106
109 group = s5p_gpioint_get_group(data); 107 group = chip->group;
110 offset = s5p_gpioint_get_offset(data); 108 offset = s5p_gpioint_get_offset(data);
111 con_offset = group << 2; 109 con_offset = REG_OFFSET(group);
112 110
113 switch (type) { 111 switch (type) {
114 case IRQ_TYPE_EDGE_RISING: 112 case IRQ_TYPE_EDGE_RISING:
@@ -132,15 +130,15 @@ static int s5p_gpioint_set_type(struct irq_data *data, unsigned int type)
132 return -EINVAL; 130 return -EINVAL;
133 } 131 }
134 132
135 value = __raw_readl(S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset); 133 value = __raw_readl(GPIO_BASE(chip) + CON_OFFSET + con_offset);
136 value &= ~(0x7 << (offset * 0x4)); 134 value &= ~(0x7 << (offset * 0x4));
137 value |= (type << (offset * 0x4)); 135 value |= (type << (offset * 0x4));
138 __raw_writel(value, S5P_GPIOREG(GPIOINT_CON_OFFSET) + con_offset); 136 __raw_writel(value, GPIO_BASE(chip) + CON_OFFSET + con_offset);
139 137
140 return 0; 138 return 0;
141} 139}
142 140
143struct irq_chip s5p_gpioint = { 141static struct irq_chip s5p_gpioint = {
144 .name = "s5p_gpioint", 142 .name = "s5p_gpioint",
145 .irq_ack = s5p_gpioint_ack, 143 .irq_ack = s5p_gpioint_ack,
146 .irq_mask = s5p_gpioint_mask, 144 .irq_mask = s5p_gpioint_mask,
@@ -151,30 +149,29 @@ struct irq_chip s5p_gpioint = {
151 149
152static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc) 150static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc)
153{ 151{
154 int group, offset, pend_offset, mask_offset; 152 struct s5p_gpioint_bank *bank = get_irq_data(irq);
155 int real_irq; 153 int group, pend_offset, mask_offset;
156 unsigned int pend, mask; 154 unsigned int pend, mask;
157 155
158 for (group = 0; group < S5P_GPIOINT_GROUP_MAXNR; group++) { 156 for (group = 0; group < bank->nr_groups; group++) {
159 pend_offset = group << 2; 157 struct s3c_gpio_chip *chip = bank->chips[group];
160 pend = __raw_readl(S5P_GPIOREG(GPIOINT_PEND_OFFSET) + 158 if (!chip)
161 pend_offset); 159 continue;
160
161 pend_offset = REG_OFFSET(group);
162 pend = __raw_readl(GPIO_BASE(chip) + PEND_OFFSET + pend_offset);
162 if (!pend) 163 if (!pend)
163 continue; 164 continue;
164 165
165 mask_offset = group << 2; 166 mask_offset = REG_OFFSET(group);
166 mask = __raw_readl(S5P_GPIOREG(GPIOINT_MASK_OFFSET) + 167 mask = __raw_readl(GPIO_BASE(chip) + MASK_OFFSET + mask_offset);
167 mask_offset);
168 pend &= ~mask; 168 pend &= ~mask;
169 169
170 for (offset = 0; offset < 8; offset++) { 170 while (pend) {
171 if (pend & (1 << offset)) { 171 int offset = fls(pend) - 1;
172 struct s3c_gpio_chip *chip = irq_chips[group]; 172 int real_irq = chip->irq_base + offset;
173 if (chip) { 173 generic_handle_irq(real_irq);
174 real_irq = chip->irq_base + offset; 174 pend &= ~BIT(offset);
175 generic_handle_irq(real_irq);
176 }
177 }
178 } 175 }
179 } 176 }
180} 177}
@@ -182,27 +179,48 @@ static void s5p_gpioint_handler(unsigned int irq, struct irq_desc *desc)
182static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip) 179static __init int s5p_gpioint_add(struct s3c_gpio_chip *chip)
183{ 180{
184 static int used_gpioint_groups = 0; 181 static int used_gpioint_groups = 0;
185 static bool handler_registered = 0;
186 int irq, group = chip->group; 182 int irq, group = chip->group;
187 int i; 183 int i;
184 struct s5p_gpioint_bank *bank = NULL;
188 185
189 if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT) 186 if (used_gpioint_groups >= S5P_GPIOINT_GROUP_COUNT)
190 return -ENOMEM; 187 return -ENOMEM;
191 188
189 list_for_each_entry(bank, &banks, list) {
190 if (group >= bank->start &&
191 group < bank->start + bank->nr_groups)
192 break;
193 }
194 if (!bank)
195 return -EINVAL;
196
197 if (!bank->handler) {
198 bank->chips = kzalloc(sizeof(struct s3c_gpio_chip *) *
199 bank->nr_groups, GFP_KERNEL);
200 if (!bank->chips)
201 return -ENOMEM;
202
203 set_irq_chained_handler(bank->irq, s5p_gpioint_handler);
204 set_irq_data(bank->irq, bank);
205 bank->handler = s5p_gpioint_handler;
206 printk(KERN_INFO "Registered chained gpio int handler for interrupt %d.\n",
207 bank->irq);
208 }
209
210 /*
211 * chained GPIO irq has been sucessfully registered, allocate new gpio
212 * int group and assign irq nubmers
213 */
214
192 chip->irq_base = S5P_GPIOINT_BASE + 215 chip->irq_base = S5P_GPIOINT_BASE +
193 used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE; 216 used_gpioint_groups * S5P_GPIOINT_GROUP_SIZE;
194 used_gpioint_groups++; 217 used_gpioint_groups++;
195 218
196 if (!handler_registered) { 219 bank->chips[group - bank->start] = chip;
197 set_irq_chained_handler(IRQ_GPIOINT, s5p_gpioint_handler);
198 handler_registered = 1;
199 }
200
201 irq_chips[group] = chip;
202 for (i = 0; i < chip->chip.ngpio; i++) { 220 for (i = 0; i < chip->chip.ngpio; i++) {
203 irq = chip->irq_base + i; 221 irq = chip->irq_base + i;
204 set_irq_chip(irq, &s5p_gpioint); 222 set_irq_chip(irq, &s5p_gpioint);
205 set_irq_data(irq, &chip->chip); 223 set_irq_data(irq, chip);
206 set_irq_handler(irq, handle_level_irq); 224 set_irq_handler(irq, handle_level_irq);
207 set_irq_flags(irq, IRQF_VALID); 225 set_irq_flags(irq, IRQF_VALID);
208 } 226 }
@@ -235,3 +253,19 @@ int __init s5p_register_gpio_interrupt(int pin)
235 } 253 }
236 return ret; 254 return ret;
237} 255}
256
257int __init s5p_register_gpioint_bank(int chain_irq, int start, int nr_groups)
258{
259 struct s5p_gpioint_bank *bank;
260
261 bank = kzalloc(sizeof(*bank), GFP_KERNEL);
262 if (!bank)
263 return -ENOMEM;
264
265 bank->start = start;
266 bank->nr_groups = nr_groups;
267 bank->irq = chain_irq;
268
269 list_add_tail(&bank->list, &banks);
270 return 0;
271}