diff options
-rw-r--r-- | arch/arm/plat-nomadik/gpio.c | 211 | ||||
-rw-r--r-- | arch/arm/plat-nomadik/include/plat/gpio.h | 1 |
2 files changed, 172 insertions, 40 deletions
diff --git a/arch/arm/plat-nomadik/gpio.c b/arch/arm/plat-nomadik/gpio.c index bf299cf34594..9f1b72056270 100644 --- a/arch/arm/plat-nomadik/gpio.c +++ b/arch/arm/plat-nomadik/gpio.c | |||
@@ -47,6 +47,8 @@ static const u32 backup_regs[] = { | |||
47 | NMK_GPIO_FWIMSC, | 47 | NMK_GPIO_FWIMSC, |
48 | }; | 48 | }; |
49 | 49 | ||
50 | #define NMK_GPIO_PER_CHIP 32 | ||
51 | |||
50 | struct nmk_gpio_chip { | 52 | struct nmk_gpio_chip { |
51 | struct gpio_chip chip; | 53 | struct gpio_chip chip; |
52 | void __iomem *addr; | 54 | void __iomem *addr; |
@@ -55,6 +57,7 @@ struct nmk_gpio_chip { | |||
55 | unsigned int parent_irq; | 57 | unsigned int parent_irq; |
56 | int secondary_parent_irq; | 58 | int secondary_parent_irq; |
57 | u32 (*get_secondary_status)(unsigned int bank); | 59 | u32 (*get_secondary_status)(unsigned int bank); |
60 | void (*set_ioforce)(bool enable); | ||
58 | spinlock_t lock; | 61 | spinlock_t lock; |
59 | /* Keep track of configured edges */ | 62 | /* Keep track of configured edges */ |
60 | u32 edge_rising; | 63 | u32 edge_rising; |
@@ -64,6 +67,13 @@ struct nmk_gpio_chip { | |||
64 | u32 pull; | 67 | u32 pull; |
65 | }; | 68 | }; |
66 | 69 | ||
70 | static struct nmk_gpio_chip * | ||
71 | nmk_gpio_chips[DIV_ROUND_UP(ARCH_NR_GPIOS, NMK_GPIO_PER_CHIP)]; | ||
72 | |||
73 | static DEFINE_SPINLOCK(nmk_gpio_slpm_lock); | ||
74 | |||
75 | #define NUM_BANKS ARRAY_SIZE(nmk_gpio_chips) | ||
76 | |||
67 | static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip, | 77 | static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip, |
68 | unsigned offset, int gpio_mode) | 78 | unsigned offset, int gpio_mode) |
69 | { | 79 | { |
@@ -138,8 +148,38 @@ static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip, | |||
138 | __nmk_gpio_set_output(nmk_chip, offset, val); | 148 | __nmk_gpio_set_output(nmk_chip, offset, val); |
139 | } | 149 | } |
140 | 150 | ||
151 | static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip, | ||
152 | unsigned offset, int gpio_mode, | ||
153 | bool glitch) | ||
154 | { | ||
155 | u32 rwimsc; | ||
156 | u32 fwimsc; | ||
157 | |||
158 | if (glitch && nmk_chip->set_ioforce) { | ||
159 | u32 bit = BIT(offset); | ||
160 | |||
161 | rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC); | ||
162 | fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC); | ||
163 | |||
164 | /* Prevent spurious wakeups */ | ||
165 | writel(rwimsc & ~bit, nmk_chip->addr + NMK_GPIO_RWIMSC); | ||
166 | writel(fwimsc & ~bit, nmk_chip->addr + NMK_GPIO_FWIMSC); | ||
167 | |||
168 | nmk_chip->set_ioforce(true); | ||
169 | } | ||
170 | |||
171 | __nmk_gpio_set_mode(nmk_chip, offset, gpio_mode); | ||
172 | |||
173 | if (glitch && nmk_chip->set_ioforce) { | ||
174 | nmk_chip->set_ioforce(false); | ||
175 | |||
176 | writel(rwimsc, nmk_chip->addr + NMK_GPIO_RWIMSC); | ||
177 | writel(fwimsc, nmk_chip->addr + NMK_GPIO_FWIMSC); | ||
178 | } | ||
179 | } | ||
180 | |||
141 | static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, | 181 | static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, |
142 | pin_cfg_t cfg, bool sleep) | 182 | pin_cfg_t cfg, bool sleep, unsigned int *slpmregs) |
143 | { | 183 | { |
144 | static const char *afnames[] = { | 184 | static const char *afnames[] = { |
145 | [NMK_GPIO_ALT_GPIO] = "GPIO", | 185 | [NMK_GPIO_ALT_GPIO] = "GPIO", |
@@ -164,6 +204,7 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, | |||
164 | int slpm = PIN_SLPM(cfg); | 204 | int slpm = PIN_SLPM(cfg); |
165 | int output = PIN_DIR(cfg); | 205 | int output = PIN_DIR(cfg); |
166 | int val = PIN_VAL(cfg); | 206 | int val = PIN_VAL(cfg); |
207 | bool glitch = af == NMK_GPIO_ALT_C; | ||
167 | 208 | ||
168 | dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", | 209 | dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n", |
169 | pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], | 210 | pin, cfg, afnames[af], pullnames[pull], slpmnames[slpm], |
@@ -202,8 +243,116 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, | |||
202 | __nmk_gpio_set_pull(nmk_chip, offset, pull); | 243 | __nmk_gpio_set_pull(nmk_chip, offset, pull); |
203 | } | 244 | } |
204 | 245 | ||
205 | __nmk_gpio_set_slpm(nmk_chip, offset, slpm); | 246 | /* |
206 | __nmk_gpio_set_mode(nmk_chip, offset, af); | 247 | * If we've backed up the SLPM registers (glitch workaround), modify |
248 | * the backups since they will be restored. | ||
249 | */ | ||
250 | if (slpmregs) { | ||
251 | if (slpm == NMK_GPIO_SLPM_NOCHANGE) | ||
252 | slpmregs[nmk_chip->bank] |= BIT(offset); | ||
253 | else | ||
254 | slpmregs[nmk_chip->bank] &= ~BIT(offset); | ||
255 | } else | ||
256 | __nmk_gpio_set_slpm(nmk_chip, offset, slpm); | ||
257 | |||
258 | __nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch); | ||
259 | } | ||
260 | |||
261 | /* | ||
262 | * Safe sequence used to switch IOs between GPIO and Alternate-C mode: | ||
263 | * - Save SLPM registers | ||
264 | * - Set SLPM=0 for the IOs you want to switch and others to 1 | ||
265 | * - Configure the GPIO registers for the IOs that are being switched | ||
266 | * - Set IOFORCE=1 | ||
267 | * - Modify the AFLSA/B registers for the IOs that are being switched | ||
268 | * - Set IOFORCE=0 | ||
269 | * - Restore SLPM registers | ||
270 | * - Any spurious wake up event during switch sequence to be ignored and | ||
271 | * cleared | ||
272 | */ | ||
273 | static void nmk_gpio_glitch_slpm_init(unsigned int *slpm) | ||
274 | { | ||
275 | int i; | ||
276 | |||
277 | for (i = 0; i < NUM_BANKS; i++) { | ||
278 | struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; | ||
279 | unsigned int temp = slpm[i]; | ||
280 | |||
281 | if (!chip) | ||
282 | break; | ||
283 | |||
284 | slpm[i] = readl(chip->addr + NMK_GPIO_SLPC); | ||
285 | writel(temp, chip->addr + NMK_GPIO_SLPC); | ||
286 | } | ||
287 | } | ||
288 | |||
289 | static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm) | ||
290 | { | ||
291 | int i; | ||
292 | |||
293 | for (i = 0; i < NUM_BANKS; i++) { | ||
294 | struct nmk_gpio_chip *chip = nmk_gpio_chips[i]; | ||
295 | |||
296 | if (!chip) | ||
297 | break; | ||
298 | |||
299 | writel(slpm[i], chip->addr + NMK_GPIO_SLPC); | ||
300 | } | ||
301 | } | ||
302 | |||
303 | static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep) | ||
304 | { | ||
305 | static unsigned int slpm[NUM_BANKS]; | ||
306 | unsigned long flags; | ||
307 | bool glitch = false; | ||
308 | int ret = 0; | ||
309 | int i; | ||
310 | |||
311 | for (i = 0; i < num; i++) { | ||
312 | if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) { | ||
313 | glitch = true; | ||
314 | break; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); | ||
319 | |||
320 | if (glitch) { | ||
321 | memset(slpm, 0xff, sizeof(slpm)); | ||
322 | |||
323 | for (i = 0; i < num; i++) { | ||
324 | int pin = PIN_NUM(cfgs[i]); | ||
325 | int offset = pin % NMK_GPIO_PER_CHIP; | ||
326 | |||
327 | if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) | ||
328 | slpm[pin / NMK_GPIO_PER_CHIP] &= ~BIT(offset); | ||
329 | } | ||
330 | |||
331 | nmk_gpio_glitch_slpm_init(slpm); | ||
332 | } | ||
333 | |||
334 | for (i = 0; i < num; i++) { | ||
335 | struct nmk_gpio_chip *nmk_chip; | ||
336 | int pin = PIN_NUM(cfgs[i]); | ||
337 | |||
338 | nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(pin)); | ||
339 | if (!nmk_chip) { | ||
340 | ret = -EINVAL; | ||
341 | break; | ||
342 | } | ||
343 | |||
344 | spin_lock(&nmk_chip->lock); | ||
345 | __nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base, | ||
346 | cfgs[i], sleep, glitch ? slpm : NULL); | ||
347 | spin_unlock(&nmk_chip->lock); | ||
348 | } | ||
349 | |||
350 | if (glitch) | ||
351 | nmk_gpio_glitch_slpm_restore(slpm); | ||
352 | |||
353 | spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); | ||
354 | |||
355 | return ret; | ||
207 | } | 356 | } |
208 | 357 | ||
209 | /** | 358 | /** |
@@ -222,19 +371,7 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, | |||
222 | */ | 371 | */ |
223 | int nmk_config_pin(pin_cfg_t cfg, bool sleep) | 372 | int nmk_config_pin(pin_cfg_t cfg, bool sleep) |
224 | { | 373 | { |
225 | struct nmk_gpio_chip *nmk_chip; | 374 | return __nmk_config_pins(&cfg, 1, sleep); |
226 | int gpio = PIN_NUM(cfg); | ||
227 | unsigned long flags; | ||
228 | |||
229 | nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio)); | ||
230 | if (!nmk_chip) | ||
231 | return -EINVAL; | ||
232 | |||
233 | spin_lock_irqsave(&nmk_chip->lock, flags); | ||
234 | __nmk_config_pin(nmk_chip, gpio - nmk_chip->chip.base, cfg, sleep); | ||
235 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | ||
236 | |||
237 | return 0; | ||
238 | } | 375 | } |
239 | EXPORT_SYMBOL(nmk_config_pin); | 376 | EXPORT_SYMBOL(nmk_config_pin); |
240 | 377 | ||
@@ -248,31 +385,13 @@ EXPORT_SYMBOL(nmk_config_pin); | |||
248 | */ | 385 | */ |
249 | int nmk_config_pins(pin_cfg_t *cfgs, int num) | 386 | int nmk_config_pins(pin_cfg_t *cfgs, int num) |
250 | { | 387 | { |
251 | int ret = 0; | 388 | return __nmk_config_pins(cfgs, num, false); |
252 | int i; | ||
253 | |||
254 | for (i = 0; i < num; i++) { | ||
255 | ret = nmk_config_pin(cfgs[i], false); | ||
256 | if (ret) | ||
257 | break; | ||
258 | } | ||
259 | |||
260 | return ret; | ||
261 | } | 389 | } |
262 | EXPORT_SYMBOL(nmk_config_pins); | 390 | EXPORT_SYMBOL(nmk_config_pins); |
263 | 391 | ||
264 | int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) | 392 | int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) |
265 | { | 393 | { |
266 | int ret = 0; | 394 | return __nmk_config_pins(cfgs, num, true); |
267 | int i; | ||
268 | |||
269 | for (i = 0; i < num; i++) { | ||
270 | ret = nmk_config_pin(cfgs[i], true); | ||
271 | if (ret) | ||
272 | break; | ||
273 | } | ||
274 | |||
275 | return ret; | ||
276 | } | 395 | } |
277 | EXPORT_SYMBOL(nmk_config_pins_sleep); | 396 | EXPORT_SYMBOL(nmk_config_pins_sleep); |
278 | 397 | ||
@@ -299,9 +418,13 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode) | |||
299 | if (!nmk_chip) | 418 | if (!nmk_chip) |
300 | return -EINVAL; | 419 | return -EINVAL; |
301 | 420 | ||
302 | spin_lock_irqsave(&nmk_chip->lock, flags); | 421 | spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); |
422 | spin_lock(&nmk_chip->lock); | ||
423 | |||
303 | __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, mode); | 424 | __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, mode); |
304 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | 425 | |
426 | spin_unlock(&nmk_chip->lock); | ||
427 | spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); | ||
305 | 428 | ||
306 | return 0; | 429 | return 0; |
307 | } | 430 | } |
@@ -474,7 +597,9 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) | |||
474 | if (!nmk_chip) | 597 | if (!nmk_chip) |
475 | return -EINVAL; | 598 | return -EINVAL; |
476 | 599 | ||
477 | spin_lock_irqsave(&nmk_chip->lock, flags); | 600 | spin_lock_irqsave(&nmk_gpio_slpm_lock, flags); |
601 | spin_lock(&nmk_chip->lock); | ||
602 | |||
478 | #ifdef CONFIG_ARCH_U8500 | 603 | #ifdef CONFIG_ARCH_U8500 |
479 | if (cpu_is_u8500v2()) { | 604 | if (cpu_is_u8500v2()) { |
480 | __nmk_gpio_set_slpm(nmk_chip, gpio, | 605 | __nmk_gpio_set_slpm(nmk_chip, gpio, |
@@ -483,7 +608,9 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on) | |||
483 | } | 608 | } |
484 | #endif | 609 | #endif |
485 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); | 610 | __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on); |
486 | spin_unlock_irqrestore(&nmk_chip->lock, flags); | 611 | |
612 | spin_unlock(&nmk_chip->lock); | ||
613 | spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags); | ||
487 | 614 | ||
488 | return 0; | 615 | return 0; |
489 | } | 616 | } |
@@ -826,6 +953,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) | |||
826 | nmk_chip->parent_irq = irq; | 953 | nmk_chip->parent_irq = irq; |
827 | nmk_chip->secondary_parent_irq = secondary_irq; | 954 | nmk_chip->secondary_parent_irq = secondary_irq; |
828 | nmk_chip->get_secondary_status = pdata->get_secondary_status; | 955 | nmk_chip->get_secondary_status = pdata->get_secondary_status; |
956 | nmk_chip->set_ioforce = pdata->set_ioforce; | ||
829 | spin_lock_init(&nmk_chip->lock); | 957 | spin_lock_init(&nmk_chip->lock); |
830 | 958 | ||
831 | chip = &nmk_chip->chip; | 959 | chip = &nmk_chip->chip; |
@@ -839,6 +967,9 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev) | |||
839 | if (ret) | 967 | if (ret) |
840 | goto out_free; | 968 | goto out_free; |
841 | 969 | ||
970 | BUG_ON(nmk_chip->bank >= ARRAY_SIZE(nmk_gpio_chips)); | ||
971 | |||
972 | nmk_gpio_chips[nmk_chip->bank] = nmk_chip; | ||
842 | platform_set_drvdata(dev, nmk_chip); | 973 | platform_set_drvdata(dev, nmk_chip); |
843 | 974 | ||
844 | nmk_gpio_init_irq(nmk_chip); | 975 | nmk_gpio_init_irq(nmk_chip); |
diff --git a/arch/arm/plat-nomadik/include/plat/gpio.h b/arch/arm/plat-nomadik/include/plat/gpio.h index d108a326a0ab..e3a4837e86f4 100644 --- a/arch/arm/plat-nomadik/include/plat/gpio.h +++ b/arch/arm/plat-nomadik/include/plat/gpio.h | |||
@@ -84,6 +84,7 @@ struct nmk_gpio_platform_data { | |||
84 | int first_irq; | 84 | int first_irq; |
85 | int num_gpio; | 85 | int num_gpio; |
86 | u32 (*get_secondary_status)(unsigned int bank); | 86 | u32 (*get_secondary_status)(unsigned int bank); |
87 | void (*set_ioforce)(bool enable); | ||
87 | }; | 88 | }; |
88 | 89 | ||
89 | #endif /* __ASM_PLAT_GPIO_H */ | 90 | #endif /* __ASM_PLAT_GPIO_H */ |