diff options
Diffstat (limited to 'arch/arm/mach-msm/gpio.c')
-rw-r--r-- | arch/arm/mach-msm/gpio.c | 409 |
1 files changed, 350 insertions, 59 deletions
diff --git a/arch/arm/mach-msm/gpio.c b/arch/arm/mach-msm/gpio.c index bc32c845c7b0..33051b509e88 100644 --- a/arch/arm/mach-msm/gpio.c +++ b/arch/arm/mach-msm/gpio.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* linux/arch/arm/mach-msm/gpio.c | 1 | /* linux/arch/arm/mach-msm/gpio.c |
2 | * | 2 | * |
3 | * Copyright (C) 2007 Google, Inc. | 3 | * Copyright (C) 2007 Google, Inc. |
4 | * Copyright (c) 2009, Code Aurora Forum. All rights reserved. | 4 | * Copyright (c) 2009-2010, Code Aurora Forum. All rights reserved. |
5 | * | 5 | * |
6 | * This software is licensed under the terms of the GNU General Public | 6 | * This software is licensed under the terms of the GNU General Public |
7 | * License version 2, as published by the Free Software Foundation, and | 7 | * License version 2, as published by the Free Software Foundation, and |
@@ -14,72 +14,363 @@ | |||
14 | * | 14 | * |
15 | */ | 15 | */ |
16 | 16 | ||
17 | #include <linux/bitops.h> | ||
18 | #include <linux/gpio.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/io.h> | ||
21 | #include <linux/irq.h> | ||
17 | #include <linux/module.h> | 22 | #include <linux/module.h> |
18 | #include <mach/gpio.h> | 23 | #include "gpio_hw.h" |
19 | #include "proc_comm.h" | 24 | #include "gpiomux.h" |
20 | 25 | ||
21 | int gpio_tlmm_config(unsigned config, unsigned disable) | 26 | #define FIRST_GPIO_IRQ MSM_GPIO_TO_INT(0) |
22 | { | 27 | |
23 | return msm_proc_comm(PCOM_RPC_GPIO_TLMM_CONFIG_EX, &config, &disable); | 28 | #define MSM_GPIO_BANK(bank, first, last) \ |
24 | } | 29 | { \ |
25 | EXPORT_SYMBOL(gpio_tlmm_config); | 30 | .regs = { \ |
26 | 31 | .out = MSM_GPIO_OUT_##bank, \ | |
27 | int msm_gpios_enable(const struct msm_gpio *table, int size) | 32 | .in = MSM_GPIO_IN_##bank, \ |
28 | { | 33 | .int_status = MSM_GPIO_INT_STATUS_##bank, \ |
29 | int rc; | 34 | .int_clear = MSM_GPIO_INT_CLEAR_##bank, \ |
30 | int i; | 35 | .int_en = MSM_GPIO_INT_EN_##bank, \ |
31 | const struct msm_gpio *g; | 36 | .int_edge = MSM_GPIO_INT_EDGE_##bank, \ |
32 | for (i = 0; i < size; i++) { | 37 | .int_pos = MSM_GPIO_INT_POS_##bank, \ |
33 | g = table + i; | 38 | .oe = MSM_GPIO_OE_##bank, \ |
34 | rc = gpio_tlmm_config(g->gpio_cfg, GPIO_ENABLE); | 39 | }, \ |
35 | if (rc) { | 40 | .chip = { \ |
36 | pr_err("gpio_tlmm_config(0x%08x, GPIO_ENABLE)" | 41 | .base = (first), \ |
37 | " <%s> failed: %d\n", | 42 | .ngpio = (last) - (first) + 1, \ |
38 | g->gpio_cfg, g->label ?: "?", rc); | 43 | .get = msm_gpio_get, \ |
39 | pr_err("pin %d func %d dir %d pull %d drvstr %d\n", | 44 | .set = msm_gpio_set, \ |
40 | GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg), | 45 | .direction_input = msm_gpio_direction_input, \ |
41 | GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg), | 46 | .direction_output = msm_gpio_direction_output, \ |
42 | GPIO_DRVSTR(g->gpio_cfg)); | 47 | .to_irq = msm_gpio_to_irq, \ |
43 | goto err; | 48 | .request = msm_gpio_request, \ |
44 | } | 49 | .free = msm_gpio_free, \ |
50 | } \ | ||
45 | } | 51 | } |
52 | |||
53 | #define MSM_GPIO_BROKEN_INT_CLEAR 1 | ||
54 | |||
55 | struct msm_gpio_regs { | ||
56 | void __iomem *out; | ||
57 | void __iomem *in; | ||
58 | void __iomem *int_status; | ||
59 | void __iomem *int_clear; | ||
60 | void __iomem *int_en; | ||
61 | void __iomem *int_edge; | ||
62 | void __iomem *int_pos; | ||
63 | void __iomem *oe; | ||
64 | }; | ||
65 | |||
66 | struct msm_gpio_chip { | ||
67 | spinlock_t lock; | ||
68 | struct gpio_chip chip; | ||
69 | struct msm_gpio_regs regs; | ||
70 | #if MSM_GPIO_BROKEN_INT_CLEAR | ||
71 | unsigned int_status_copy; | ||
72 | #endif | ||
73 | unsigned int both_edge_detect; | ||
74 | unsigned int int_enable[2]; /* 0: awake, 1: sleep */ | ||
75 | }; | ||
76 | |||
77 | static int msm_gpio_write(struct msm_gpio_chip *msm_chip, | ||
78 | unsigned offset, unsigned on) | ||
79 | { | ||
80 | unsigned mask = BIT(offset); | ||
81 | unsigned val; | ||
82 | |||
83 | val = readl(msm_chip->regs.out); | ||
84 | if (on) | ||
85 | writel(val | mask, msm_chip->regs.out); | ||
86 | else | ||
87 | writel(val & ~mask, msm_chip->regs.out); | ||
46 | return 0; | 88 | return 0; |
47 | err: | 89 | } |
48 | msm_gpios_disable(table, i); | 90 | |
49 | return rc; | 91 | static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip) |
50 | } | 92 | { |
51 | EXPORT_SYMBOL(msm_gpios_enable); | 93 | int loop_limit = 100; |
52 | 94 | unsigned pol, val, val2, intstat; | |
53 | void msm_gpios_disable(const struct msm_gpio *table, int size) | 95 | do { |
54 | { | 96 | val = readl(msm_chip->regs.in); |
55 | int rc; | 97 | pol = readl(msm_chip->regs.int_pos); |
56 | int i; | 98 | pol = (pol & ~msm_chip->both_edge_detect) | |
57 | const struct msm_gpio *g; | 99 | (~val & msm_chip->both_edge_detect); |
58 | for (i = size-1; i >= 0; i--) { | 100 | writel(pol, msm_chip->regs.int_pos); |
59 | g = table + i; | 101 | intstat = readl(msm_chip->regs.int_status); |
60 | rc = gpio_tlmm_config(g->gpio_cfg, GPIO_DISABLE); | 102 | val2 = readl(msm_chip->regs.in); |
61 | if (rc) { | 103 | if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0) |
62 | pr_err("gpio_tlmm_config(0x%08x, GPIO_DISABLE)" | 104 | return; |
63 | " <%s> failed: %d\n", | 105 | } while (loop_limit-- > 0); |
64 | g->gpio_cfg, g->label ?: "?", rc); | 106 | printk(KERN_ERR "msm_gpio_update_both_edge_detect, " |
65 | pr_err("pin %d func %d dir %d pull %d drvstr %d\n", | 107 | "failed to reach stable state %x != %x\n", val, val2); |
66 | GPIO_PIN(g->gpio_cfg), GPIO_FUNC(g->gpio_cfg), | 108 | } |
67 | GPIO_DIR(g->gpio_cfg), GPIO_PULL(g->gpio_cfg), | 109 | |
68 | GPIO_DRVSTR(g->gpio_cfg)); | 110 | static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip, |
69 | } | 111 | unsigned offset) |
112 | { | ||
113 | unsigned bit = BIT(offset); | ||
114 | |||
115 | #if MSM_GPIO_BROKEN_INT_CLEAR | ||
116 | /* Save interrupts that already triggered before we loose them. */ | ||
117 | /* Any interrupt that triggers between the read of int_status */ | ||
118 | /* and the write to int_clear will still be lost though. */ | ||
119 | msm_chip->int_status_copy |= readl(msm_chip->regs.int_status); | ||
120 | msm_chip->int_status_copy &= ~bit; | ||
121 | #endif | ||
122 | writel(bit, msm_chip->regs.int_clear); | ||
123 | msm_gpio_update_both_edge_detect(msm_chip); | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) | ||
128 | { | ||
129 | struct msm_gpio_chip *msm_chip; | ||
130 | unsigned long irq_flags; | ||
131 | |||
132 | msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
133 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
134 | writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe); | ||
135 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | static int | ||
140 | msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) | ||
141 | { | ||
142 | struct msm_gpio_chip *msm_chip; | ||
143 | unsigned long irq_flags; | ||
144 | |||
145 | msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
146 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
147 | msm_gpio_write(msm_chip, offset, value); | ||
148 | writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe); | ||
149 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
150 | return 0; | ||
151 | } | ||
152 | |||
153 | static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) | ||
154 | { | ||
155 | struct msm_gpio_chip *msm_chip; | ||
156 | |||
157 | msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
158 | return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0; | ||
159 | } | ||
160 | |||
161 | static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) | ||
162 | { | ||
163 | struct msm_gpio_chip *msm_chip; | ||
164 | unsigned long irq_flags; | ||
165 | |||
166 | msm_chip = container_of(chip, struct msm_gpio_chip, chip); | ||
167 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
168 | msm_gpio_write(msm_chip, offset, value); | ||
169 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
170 | } | ||
171 | |||
172 | static int msm_gpio_to_irq(struct gpio_chip *chip, unsigned offset) | ||
173 | { | ||
174 | return MSM_GPIO_TO_INT(chip->base + offset); | ||
175 | } | ||
176 | |||
177 | #ifdef CONFIG_MSM_GPIOMUX | ||
178 | static int msm_gpio_request(struct gpio_chip *chip, unsigned offset) | ||
179 | { | ||
180 | return msm_gpiomux_get(chip->base + offset); | ||
181 | } | ||
182 | |||
183 | static void msm_gpio_free(struct gpio_chip *chip, unsigned offset) | ||
184 | { | ||
185 | msm_gpiomux_put(chip->base + offset); | ||
186 | } | ||
187 | #else | ||
188 | #define msm_gpio_request NULL | ||
189 | #define msm_gpio_free NULL | ||
190 | #endif | ||
191 | |||
192 | struct msm_gpio_chip msm_gpio_chips[] = { | ||
193 | #if defined(CONFIG_ARCH_MSM7X00A) | ||
194 | MSM_GPIO_BANK(0, 0, 15), | ||
195 | MSM_GPIO_BANK(1, 16, 42), | ||
196 | MSM_GPIO_BANK(2, 43, 67), | ||
197 | MSM_GPIO_BANK(3, 68, 94), | ||
198 | MSM_GPIO_BANK(4, 95, 106), | ||
199 | MSM_GPIO_BANK(5, 107, 121), | ||
200 | #elif defined(CONFIG_ARCH_MSM7X25) || defined(CONFIG_ARCH_MSM7X27) | ||
201 | MSM_GPIO_BANK(0, 0, 15), | ||
202 | MSM_GPIO_BANK(1, 16, 42), | ||
203 | MSM_GPIO_BANK(2, 43, 67), | ||
204 | MSM_GPIO_BANK(3, 68, 94), | ||
205 | MSM_GPIO_BANK(4, 95, 106), | ||
206 | MSM_GPIO_BANK(5, 107, 132), | ||
207 | #elif defined(CONFIG_ARCH_MSM7X30) | ||
208 | MSM_GPIO_BANK(0, 0, 15), | ||
209 | MSM_GPIO_BANK(1, 16, 43), | ||
210 | MSM_GPIO_BANK(2, 44, 67), | ||
211 | MSM_GPIO_BANK(3, 68, 94), | ||
212 | MSM_GPIO_BANK(4, 95, 106), | ||
213 | MSM_GPIO_BANK(5, 107, 133), | ||
214 | MSM_GPIO_BANK(6, 134, 150), | ||
215 | MSM_GPIO_BANK(7, 151, 181), | ||
216 | #elif defined(CONFIG_ARCH_QSD8X50) | ||
217 | MSM_GPIO_BANK(0, 0, 15), | ||
218 | MSM_GPIO_BANK(1, 16, 42), | ||
219 | MSM_GPIO_BANK(2, 43, 67), | ||
220 | MSM_GPIO_BANK(3, 68, 94), | ||
221 | MSM_GPIO_BANK(4, 95, 103), | ||
222 | MSM_GPIO_BANK(5, 104, 121), | ||
223 | MSM_GPIO_BANK(6, 122, 152), | ||
224 | MSM_GPIO_BANK(7, 153, 164), | ||
225 | #endif | ||
226 | }; | ||
227 | |||
228 | static void msm_gpio_irq_ack(unsigned int irq) | ||
229 | { | ||
230 | unsigned long irq_flags; | ||
231 | struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
232 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
233 | msm_gpio_clear_detect_status(msm_chip, | ||
234 | irq - gpio_to_irq(msm_chip->chip.base)); | ||
235 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
236 | } | ||
237 | |||
238 | static void msm_gpio_irq_mask(unsigned int irq) | ||
239 | { | ||
240 | unsigned long irq_flags; | ||
241 | struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
242 | unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
243 | |||
244 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
245 | /* level triggered interrupts are also latched */ | ||
246 | if (!(readl(msm_chip->regs.int_edge) & BIT(offset))) | ||
247 | msm_gpio_clear_detect_status(msm_chip, offset); | ||
248 | msm_chip->int_enable[0] &= ~BIT(offset); | ||
249 | writel(msm_chip->int_enable[0], msm_chip->regs.int_en); | ||
250 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
251 | } | ||
252 | |||
253 | static void msm_gpio_irq_unmask(unsigned int irq) | ||
254 | { | ||
255 | unsigned long irq_flags; | ||
256 | struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
257 | unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
258 | |||
259 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
260 | /* level triggered interrupts are also latched */ | ||
261 | if (!(readl(msm_chip->regs.int_edge) & BIT(offset))) | ||
262 | msm_gpio_clear_detect_status(msm_chip, offset); | ||
263 | msm_chip->int_enable[0] |= BIT(offset); | ||
264 | writel(msm_chip->int_enable[0], msm_chip->regs.int_en); | ||
265 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
266 | } | ||
267 | |||
268 | static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on) | ||
269 | { | ||
270 | unsigned long irq_flags; | ||
271 | struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
272 | unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
273 | |||
274 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
275 | |||
276 | if (on) | ||
277 | msm_chip->int_enable[1] |= BIT(offset); | ||
278 | else | ||
279 | msm_chip->int_enable[1] &= ~BIT(offset); | ||
280 | |||
281 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type) | ||
286 | { | ||
287 | unsigned long irq_flags; | ||
288 | struct msm_gpio_chip *msm_chip = get_irq_chip_data(irq); | ||
289 | unsigned offset = irq - gpio_to_irq(msm_chip->chip.base); | ||
290 | unsigned val, mask = BIT(offset); | ||
291 | |||
292 | spin_lock_irqsave(&msm_chip->lock, irq_flags); | ||
293 | val = readl(msm_chip->regs.int_edge); | ||
294 | if (flow_type & IRQ_TYPE_EDGE_BOTH) { | ||
295 | writel(val | mask, msm_chip->regs.int_edge); | ||
296 | irq_desc[irq].handle_irq = handle_edge_irq; | ||
297 | } else { | ||
298 | writel(val & ~mask, msm_chip->regs.int_edge); | ||
299 | irq_desc[irq].handle_irq = handle_level_irq; | ||
300 | } | ||
301 | if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { | ||
302 | msm_chip->both_edge_detect |= mask; | ||
303 | msm_gpio_update_both_edge_detect(msm_chip); | ||
304 | } else { | ||
305 | msm_chip->both_edge_detect &= ~mask; | ||
306 | val = readl(msm_chip->regs.int_pos); | ||
307 | if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) | ||
308 | writel(val | mask, msm_chip->regs.int_pos); | ||
309 | else | ||
310 | writel(val & ~mask, msm_chip->regs.int_pos); | ||
70 | } | 311 | } |
312 | spin_unlock_irqrestore(&msm_chip->lock, irq_flags); | ||
313 | return 0; | ||
71 | } | 314 | } |
72 | EXPORT_SYMBOL(msm_gpios_disable); | ||
73 | 315 | ||
74 | int msm_gpios_request_enable(const struct msm_gpio *table, int size) | 316 | static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) |
75 | { | 317 | { |
76 | int rc = msm_gpios_enable(table, size); | 318 | int i, j, mask; |
77 | return rc; | 319 | unsigned val; |
320 | |||
321 | for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { | ||
322 | struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i]; | ||
323 | val = readl(msm_chip->regs.int_status); | ||
324 | val &= msm_chip->int_enable[0]; | ||
325 | while (val) { | ||
326 | mask = val & -val; | ||
327 | j = fls(mask) - 1; | ||
328 | /* printk("%s %08x %08x bit %d gpio %d irq %d\n", | ||
329 | __func__, v, m, j, msm_chip->chip.start + j, | ||
330 | FIRST_GPIO_IRQ + msm_chip->chip.start + j); */ | ||
331 | val &= ~mask; | ||
332 | generic_handle_irq(FIRST_GPIO_IRQ + | ||
333 | msm_chip->chip.base + j); | ||
334 | } | ||
335 | } | ||
336 | desc->chip->ack(irq); | ||
78 | } | 337 | } |
79 | EXPORT_SYMBOL(msm_gpios_request_enable); | ||
80 | 338 | ||
81 | void msm_gpios_disable_free(const struct msm_gpio *table, int size) | 339 | static struct irq_chip msm_gpio_irq_chip = { |
340 | .name = "msmgpio", | ||
341 | .ack = msm_gpio_irq_ack, | ||
342 | .mask = msm_gpio_irq_mask, | ||
343 | .unmask = msm_gpio_irq_unmask, | ||
344 | .set_wake = msm_gpio_irq_set_wake, | ||
345 | .set_type = msm_gpio_irq_set_type, | ||
346 | }; | ||
347 | |||
348 | static int __init msm_init_gpio(void) | ||
82 | { | 349 | { |
83 | msm_gpios_disable(table, size); | 350 | int i, j = 0; |
351 | |||
352 | for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) { | ||
353 | if (i - FIRST_GPIO_IRQ >= | ||
354 | msm_gpio_chips[j].chip.base + | ||
355 | msm_gpio_chips[j].chip.ngpio) | ||
356 | j++; | ||
357 | set_irq_chip_data(i, &msm_gpio_chips[j]); | ||
358 | set_irq_chip(i, &msm_gpio_irq_chip); | ||
359 | set_irq_handler(i, handle_edge_irq); | ||
360 | set_irq_flags(i, IRQF_VALID); | ||
361 | } | ||
362 | |||
363 | for (i = 0; i < ARRAY_SIZE(msm_gpio_chips); i++) { | ||
364 | spin_lock_init(&msm_gpio_chips[i].lock); | ||
365 | writel(0, msm_gpio_chips[i].regs.int_en); | ||
366 | gpiochip_add(&msm_gpio_chips[i].chip); | ||
367 | } | ||
368 | |||
369 | set_irq_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler); | ||
370 | set_irq_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler); | ||
371 | set_irq_wake(INT_GPIO_GROUP1, 1); | ||
372 | set_irq_wake(INT_GPIO_GROUP2, 2); | ||
373 | return 0; | ||
84 | } | 374 | } |
85 | EXPORT_SYMBOL(msm_gpios_disable_free); | 375 | |
376 | postcore_initcall(msm_init_gpio); | ||