aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRabin Vincent <rabin.vincent@stericsson.com>2010-12-13 01:32:40 -0500
committerLinus Walleij <linus.walleij@linaro.org>2011-03-14 09:05:15 -0400
commit01727e61f0967dd18fadd9dfcee5cf1246e6d8a6 (patch)
tree710acb7f8eacaa4d11dfbc8bf584226680ce6513
parent3546d15c5c3f923f7925f92bfe0f8e1cf8bccc1c (diff)
plat-nomadik: implement safe switch sequence for Alt-C
Setting pinmux alternative C for a GPIO pin is actually not so easy since it ivolves setting value "1" in two registers, and since the combined result will take effect for intermediate values (01 or 10) this will cause glitches while you wrote one register but have not yet written the other. This patch implements a series of kludges including an optional machine-specific callback to avoid glitches when changing pin mux mode to alternative C. Signed-off-by: Rabin Vincent <rabin.vincent@stericsson.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--arch/arm/plat-nomadik/gpio.c211
-rw-r--r--arch/arm/plat-nomadik/include/plat/gpio.h1
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
50struct nmk_gpio_chip { 52struct 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
70static struct nmk_gpio_chip *
71nmk_gpio_chips[DIV_ROUND_UP(ARCH_NR_GPIOS, NMK_GPIO_PER_CHIP)];
72
73static DEFINE_SPINLOCK(nmk_gpio_slpm_lock);
74
75#define NUM_BANKS ARRAY_SIZE(nmk_gpio_chips)
76
67static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip, 77static 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
151static 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
141static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset, 181static 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 */
273static 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
289static 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
303static 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 */
223int nmk_config_pin(pin_cfg_t cfg, bool sleep) 372int 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}
239EXPORT_SYMBOL(nmk_config_pin); 376EXPORT_SYMBOL(nmk_config_pin);
240 377
@@ -248,31 +385,13 @@ EXPORT_SYMBOL(nmk_config_pin);
248 */ 385 */
249int nmk_config_pins(pin_cfg_t *cfgs, int num) 386int 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}
262EXPORT_SYMBOL(nmk_config_pins); 390EXPORT_SYMBOL(nmk_config_pins);
263 391
264int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num) 392int 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}
277EXPORT_SYMBOL(nmk_config_pins_sleep); 396EXPORT_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 */