Skip to contentSkip to conversation

plat-nomadik: GPIO updates

PATCH New Linus Walleij 3 participants 12 messages Feb 8, 2011 – Feb 9, 2011

Subsystems (3)
lore
Share & tools
lore
#0 lore raw

This patch set consolidates a number of updates to the plat-nomadik GPIO code that we have stabilized inside ST-Ericsson for the past few months. It's time to get in sync.

Yours,
Linus Walleij

#1 lore raw

PATCH 1/9 plat-nomadik: support varying number of GPIOs per block

From: Rabin Vincent <[email protected]>

[Added constant 32-pin assignment in platform data]
---
 arch/arm/mach-ux500/devices-common.c      |    1 +
 arch/arm/plat-nomadik/gpio.c              |    5 ++---
 arch/arm/plat-nomadik/include/plat/gpio.h |    1 +
 3 files changed, 4 insertions(+), 3 deletions(-)
arch/arm/mach-ux500/devices-common.c +1 -0
index fe69f5f..13a4ce0 100644
@@ -139,6 +139,7 @@ void dbx500_add_gpios(resource_size_t *base, int num, int irq,
 	for (i = 0; i < num; i++, first += 32, irq++) {
 		pdata->first_gpio = first;
 		pdata->first_irq = NOMADIK_GPIO_TO_IRQ(first);
+		pdata->num_gpio = 32;
 
 		dbx500_add_gpio(i, base[i], irq, pdata);
 	}
arch/arm/plat-nomadik/gpio.c +2 -3
index 1e88ecb..30bb92a 100644
@@ -35,7 +35,6 @@
  * Symbols in this file are called "nmk_gpio" for "nomadik gpio"
  */
 
-#define NMK_GPIO_PER_CHIP 32
 struct nmk_gpio_chip {
 	struct gpio_chip chip;
 	void __iomem *addr;
@@ -546,7 +545,7 @@ static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
 	int i;
 
 	first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base);
-	for (i = first_irq; i < first_irq + NMK_GPIO_PER_CHIP; i++) {
+	for (i = first_irq; i < first_irq + nmk_chip->chip.ngpio; i++) {
 		set_irq_chip(i, &nmk_gpio_irq_chip);
 		set_irq_handler(i, handle_edge_irq);
 		set_irq_flags(i, IRQF_VALID);
@@ -612,7 +611,6 @@ static struct gpio_chip nmk_gpio_template = {
 	.direction_output	= nmk_gpio_make_output,
 	.set			= nmk_gpio_set_output,
 	.to_irq			= nmk_gpio_to_irq,
-	.ngpio			= NMK_GPIO_PER_CHIP,
 	.can_sleep		= 0,
 };
 
@@ -672,6 +670,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
 
 	chip = &nmk_chip->chip;
 	chip->base = pdata->first_gpio;
+	chip->ngpio = pdata->num_gpio;
 	chip->label = pdata->name ?: dev_name(&dev->dev);
 	chip->dev = &dev->dev;
 	chip->owner = THIS_MODULE;
arch/arm/plat-nomadik/include/plat/gpio.h +1 -0
index 67b113d..d745f3a 100644
@@ -82,6 +82,7 @@ struct nmk_gpio_platform_data {
 	char *name;
 	int first_gpio;
 	int first_irq;
+	int num_gpio;
 };
 
 #endif /* __ASM_PLAT_GPIO_H */
Signature · 1 line
1.7.3.2
#2 lore raw

PATCH 2/9 plat-nomadik: add custom dbg_show for GPIO

In reply to Linus Walleij

From: Rabin Vincent <[email protected]>

---
 arch/arm/plat-nomadik/gpio.c |   92 ++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 92 insertions(+), 0 deletions(-)
arch/arm/plat-nomadik/gpio.c +92 -0
index 30bb92a..5b5fd1e 100644
@@ -604,6 +604,97 @@ static int nmk_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
 	return NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base) + offset;
 }
 
+#ifdef CONFIG_DEBUG_FS
+
+#include <linux/seq_file.h>
+
+static void nmk_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
+{
+	int mode;
+	unsigned		i;
+	unsigned		gpio = chip->base;
+	int			is_out;
+	struct nmk_gpio_chip *nmk_chip =
+		container_of(chip, struct nmk_gpio_chip, chip);
+	const char *modes[] = {
+		[NMK_GPIO_ALT_GPIO]	= "gpio",
+		[NMK_GPIO_ALT_A]	= "altA",
+		[NMK_GPIO_ALT_B]	= "altB",
+		[NMK_GPIO_ALT_C]	= "altC",
+	};
+
+	for (i = 0; i < chip->ngpio; i++, gpio++) {
+		const char *label = gpiochip_is_requested(chip, i);
+		bool pull;
+		u32 bit = 1 << i;
+
+		if (!label)
+			continue;
+
+		is_out = readl(nmk_chip->addr + NMK_GPIO_DIR) & bit;
+		pull = !(readl(nmk_chip->addr + NMK_GPIO_PDIS) & bit);
+		mode = nmk_gpio_get_mode(gpio);
+		seq_printf(s, " gpio-%-3d (%-20.20s) %s %s %s %s",
+			gpio, label,
+			is_out ? "out" : "in ",
+			chip->get
+				? (chip->get(chip, i) ? "hi" : "lo")
+				: "?  ",
+			(mode < 0) ? "unknown" : modes[mode],
+			pull ? "pull" : "none");
+
+		if (!is_out) {
+			int		irq = gpio_to_irq(gpio);
+			struct irq_desc	*desc = irq_to_desc(irq);
+
+			/* This races with request_irq(), set_irq_type(),
+			 * and set_irq_wake() ... but those are "rare".
+			 *
+			 * More significantly, trigger type flags aren't
+			 * currently maintained by genirq.
+			 */
+			if (irq >= 0 && desc->action) {
+				char *trigger;
+
+				switch (desc->status & IRQ_TYPE_SENSE_MASK) {
+				case IRQ_TYPE_NONE:
+					trigger = "(default)";
+					break;
+				case IRQ_TYPE_EDGE_FALLING:
+					trigger = "edge-falling";
+					break;
+				case IRQ_TYPE_EDGE_RISING:
+					trigger = "edge-rising";
+					break;
+				case IRQ_TYPE_EDGE_BOTH:
+					trigger = "edge-both";
+					break;
+				case IRQ_TYPE_LEVEL_HIGH:
+					trigger = "level-high";
+					break;
+				case IRQ_TYPE_LEVEL_LOW:
+					trigger = "level-low";
+					break;
+				default:
+					trigger = "?trigger?";
+					break;
+				}
+
+				seq_printf(s, " irq-%d %s%s",
+					irq, trigger,
+					(desc->status & IRQ_WAKEUP)
+						? " wakeup" : "");
+			}
+		}
+
+		seq_printf(s, "\n");
+	}
+}
+
+#else
+#define nmk_gpio_dbg_show	NULL
+#endif
+
 /* This structure is replicated for each GPIO block allocated at probe time */
 static struct gpio_chip nmk_gpio_template = {
 	.direction_input	= nmk_gpio_make_input,
@@ -611,6 +702,7 @@ static struct gpio_chip nmk_gpio_template = {
 	.direction_output	= nmk_gpio_make_output,
 	.set			= nmk_gpio_set_output,
 	.to_irq			= nmk_gpio_to_irq,
+	.dbg_show		= nmk_gpio_dbg_show,
 	.can_sleep		= 0,
 };
 
Signature · 1 line
1.7.3.2
#3 lore raw

PATCH 3/9 plat-nomadik: implement suspend/resume for GPIO

In reply to Linus Walleij

From: Rabin Vincent <[email protected]>

---
 arch/arm/plat-nomadik/gpio.c |   51 ++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 49 insertions(+), 2 deletions(-)
arch/arm/plat-nomadik/gpio.c +49 -2
index 5b5fd1e..46f1af8 100644
@@ -44,6 +44,7 @@ struct nmk_gpio_chip {
 	/* Keep track of configured edges */
 	u32 edge_rising;
 	u32 edge_falling;
+	u32 backup[10];
 };
 
 static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip,
@@ -792,14 +793,60 @@ out:
 	return ret;
 }
 
+#ifdef CONFIG_PM
+static int nmk_gpio_pm(struct platform_device *dev, bool suspend)
+{
+	struct nmk_gpio_chip *nmk_chip = platform_get_drvdata(dev);
+	int i;
+	static const unsigned int regs[] = {
+		NMK_GPIO_DAT,
+		NMK_GPIO_PDIS,
+		NMK_GPIO_DIR,
+		NMK_GPIO_AFSLA,
+		NMK_GPIO_AFSLB,
+		NMK_GPIO_SLPC,
+		NMK_GPIO_RIMSC,
+		NMK_GPIO_FIMSC,
+		NMK_GPIO_RWIMSC,
+		NMK_GPIO_FWIMSC,
+	};
+
+	BUILD_BUG_ON(ARRAY_SIZE(nmk_chip->backup) != ARRAY_SIZE(regs));
+
+	/* XXX: is this sufficient? what about pull-up/down configuration? */
+
+	for (i = 0; i < ARRAY_SIZE(regs); i++) {
+		if (suspend)
+			nmk_chip->backup[i] = readl(nmk_chip->addr + regs[i]);
+		else
+			writel(nmk_chip->backup[i], nmk_chip->addr + regs[i]);
+	}
+
+	return 0;
+}
+
+static int nmk_gpio_suspend(struct platform_device *dev, pm_message_t state)
+{
+	return nmk_gpio_pm(dev, true);
+}
+
+static int nmk_gpio_resume(struct platform_device *dev)
+{
+	return nmk_gpio_pm(dev, false);
+}
+#else
+#define nmk_gpio_suspend	NULL
+#define nmk_gpio_resume		NULL
+#endif
+
 static struct platform_driver nmk_gpio_driver = {
 	.driver = {
 		.owner = THIS_MODULE,
 		.name = "gpio",
 		},
 	.probe = nmk_gpio_probe,
-	.suspend = NULL, /* to be done */
-	.resume = NULL,
+	.suspend = nmk_gpio_suspend,
+	.resume = nmk_gpio_resume,
 };
 
 static int __init nmk_gpio_init(void)
Signature · 1 line
1.7.3.2
#4 lore raw

PATCH 4/9 plat-nomadik: support secondary GPIO interrupts

In reply to Linus Walleij

From: Rabin Vincent <[email protected]>

When GPIOs wake up the system from sleep mode, the normal GPIO interrupt handler does not hit and the normal interrupt status register does not contain the status. Instead the secondary GPIO handler does, and the interrupt status needs to be retrieved from the wakeup status saved by the suspend/resume code.

---
 arch/arm/plat-nomadik/gpio.c              |   51 +++++++++++++++++++++++++---
 arch/arm/plat-nomadik/include/plat/gpio.h |    1 +
 2 files changed, 46 insertions(+), 6 deletions(-)
arch/arm/plat-nomadik/gpio.c +45 -6
index 46f1af8..971e5d3 100644
@@ -39,7 +39,10 @@ struct nmk_gpio_chip {
 	struct gpio_chip chip;
 	void __iomem *addr;
 	struct clk *clk;
+	unsigned int bank;
 	unsigned int parent_irq;
+	unsigned int secondary_parent_irq;
+	u32 (*get_secondary_status)(unsigned int bank);
 	spinlock_t lock;
 	/* Keep track of configured edges */
 	u32 edge_rising;
@@ -514,12 +517,11 @@ static struct irq_chip nmk_gpio_irq_chip = {
 	.irq_set_wake	= nmk_gpio_irq_set_wake,
 };
 
-static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+static void __nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc,
+				   u32 status)
 {
 	struct nmk_gpio_chip *nmk_chip;
 	struct irq_chip *host_chip = get_irq_chip(irq);
-	unsigned int gpio_irq;
-	u32 pending;
 	unsigned int first_irq;
 
 	if (host_chip->irq_mask_ack)
@@ -532,14 +534,33 @@ static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
 
 	nmk_chip = get_irq_data(irq);
 	first_irq = NOMADIK_GPIO_TO_IRQ(nmk_chip->chip.base);
-	while ( (pending = readl(nmk_chip->addr + NMK_GPIO_IS)) ) {
-		gpio_irq = first_irq + __ffs(pending);
-		generic_handle_irq(gpio_irq);
+	while (status) {
+		int bit = __ffs(status);
+
+		generic_handle_irq(first_irq + bit);
+		status &= ~BIT(bit);
 	}
 
 	host_chip->irq_unmask(&desc->irq_data);
 }
 
+static void nmk_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+	struct nmk_gpio_chip *nmk_chip = get_irq_data(irq);
+	u32 status = readl(nmk_chip->addr + NMK_GPIO_IS);
+
+	__nmk_gpio_irq_handler(irq, desc, status);
+}
+
+static void nmk_gpio_secondary_irq_handler(unsigned int irq,
+					   struct irq_desc *desc)
+{
+	struct nmk_gpio_chip *nmk_chip = get_irq_data(irq);
+	u32 status = nmk_chip->get_secondary_status(nmk_chip->bank);
+
+	__nmk_gpio_irq_handler(irq, desc, status);
+}
+
 static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
 {
 	unsigned int first_irq;
@@ -553,8 +574,16 @@ static int nmk_gpio_init_irq(struct nmk_gpio_chip *nmk_chip)
 		set_irq_chip_data(i, nmk_chip);
 		set_irq_type(i, IRQ_TYPE_EDGE_FALLING);
 	}
+
 	set_irq_chained_handler(nmk_chip->parent_irq, nmk_gpio_irq_handler);
 	set_irq_data(nmk_chip->parent_irq, nmk_chip);
+
+	if (nmk_chip->secondary_parent_irq >= 0) {
+		set_irq_chained_handler(nmk_chip->secondary_parent_irq,
+					nmk_gpio_secondary_irq_handler);
+		set_irq_data(nmk_chip->secondary_parent_irq, nmk_chip);
+	}
+
 	return 0;
 }
 
@@ -714,6 +743,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
 	struct gpio_chip *chip;
 	struct resource *res;
 	struct clk *clk;
+	int secondary_irq;
 	int irq;
 	int ret;
 
@@ -732,6 +762,12 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
 		goto out;
 	}
 
+	secondary_irq = platform_get_irq(dev, 1);
+	if (secondary_irq >= 0 && !pdata->get_secondary_status) {
+		ret = -EINVAL;
+		goto out;
+	}
+
 	if (request_mem_region(res->start, resource_size(res),
 			       dev_name(&dev->dev)) == NULL) {
 		ret = -EBUSY;
@@ -755,10 +791,13 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
 	 * The virt address in nmk_chip->addr is in the nomadik register space,
 	 * so we can simply convert the resource address, without remapping
 	 */
+	nmk_chip->bank = dev->id;
 	nmk_chip->clk = clk;
 	nmk_chip->addr = io_p2v(res->start);
 	nmk_chip->chip = nmk_gpio_template;
 	nmk_chip->parent_irq = irq;
+	nmk_chip->secondary_parent_irq = secondary_irq;
+	nmk_chip->get_secondary_status = pdata->get_secondary_status;
 	spin_lock_init(&nmk_chip->lock);
 
 	chip = &nmk_chip->chip;
arch/arm/plat-nomadik/include/plat/gpio.h +1 -0
index d745f3a..d108a32 100644
@@ -83,6 +83,7 @@ struct nmk_gpio_platform_data {
 	int first_gpio;
 	int first_irq;
 	int num_gpio;
+	u32 (*get_secondary_status)(unsigned int bank);
 };
 
 #endif /* __ASM_PLAT_GPIO_H */
Signature · 1 line
1.7.3.2
#5 lore raw

PATCH 5/9 plat-nomadik: pull-up/down settings for GPIO resume

In reply to Linus Walleij

From: Jonas Aaberg <[email protected]>

Suspend/resume didn't take care of pull-up and pull-down settings and writing back the DAT register at resume can change pull up/down settings, depending on pin input value. Output values are now also restored.

---
 arch/arm/plat-nomadik/Kconfig |    6 +++
 arch/arm/plat-nomadik/gpio.c  |   78 ++++++++++++++++++++++++++++------------
 2 files changed, 60 insertions(+), 24 deletions(-)
arch/arm/plat-nomadik/Kconfig +6 -0
index 187f4e8..ad25c96 100644
@@ -25,4 +25,10 @@ config NOMADIK_GPIO
 	help
 	  Support for the Nomadik GPIO controller.
 
+config NOMADIK_GPIO_PM
+	bool
+	depends on NOMADIK_GPIO && PM
+	help
+	  Support PM for the Nomadik GPIO controller.
+
 endif
arch/arm/plat-nomadik/gpio.c +54 -24

123 lines not on this page · raw message

Signature · 1 line
1.7.3.2
#6 lore raw

PATCH 6/9 plat-nomadik: type secondary IRQ correctly

In reply to Linus Walleij

From: Virupax Sadashivpetimath <[email protected]>

Coverity found that we were checking an unsigned variable for greater than zero. Type it correctly.

---
 arch/arm/plat-nomadik/gpio.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/arm/plat-nomadik/gpio.c +1 -1
index 71682a8..cc9de59 100644
@@ -53,7 +53,7 @@ struct nmk_gpio_chip {
 	struct clk *clk;
 	unsigned int bank;
 	unsigned int parent_irq;
-	unsigned int secondary_parent_irq;
+	int secondary_parent_irq;
 	u32 (*get_secondary_status)(unsigned int bank);
 	spinlock_t lock;
 	/* Keep track of configured edges */
Signature · 1 line
1.7.3.2
#7 lore raw

PATCH 6/9 plat-nomadik: type secondary IRQ correctly

Hello.

Quoted 4 lines · Linus Walleij

From: Virupax Sadashivpetimath <[email protected]>

Coverity found that we were checking an unsigned variable for greater than zero. Type it correctly.

There's nothing criminal in checking unsigned for > 0. Perhaps you meant >= 0?

Quoted 3 lines

Twice?

WBR, Sergei

#8 lore raw

PATCH 6/9 plat-nomadik: type secondary IRQ correctly

2011/2/8 Sergei Shtylyov <[email protected]>:

Quoted 11 lines

Hello.

Quoted 4 lines · Linus Walleij

From: Virupax Sadashivpetimath <[email protected]>

Coverity found that we were checking an unsigned variable for greater than zero. Type it correctly.

? There's nothing criminal in checking unsigned for > 0. Perhaps you meant

Quoted 1 line

= 0?

Yeah the if()-clause Coverity reacted to was this:
if (nmk_chip->secondary_parent_irq >= 0) {

I'll update the patch description.

Quoted 6 lines
Quoted 4 lines

Signed-off-by: Virupax Sadashivpetimath
<[email protected]>

? Twice?

Yes, for two different organizations. One pays my wages and the other one control my work.

Let's say that I think if Greg and the Linux Foundation wasn't doing these statistics over the organizations contributing to Linux every kernel release, it wouldn't be such a big deal. Now everybody wants to be in those stats. Lee from Linaro may want to expand on this...

Yours,
Linus Walleij

#9 lore raw

PATCH 7/9 plat-nomadik: set altfunc to GPIO when enabling the sleep config

In reply to Linus Walleij

From: Rabin Vincent <[email protected]>

---
 arch/arm/plat-nomadik/gpio.c |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
arch/arm/plat-nomadik/gpio.c +2 -0
index cc9de59..bf299cf 100644
@@ -175,6 +175,8 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
 		int slpm_output = PIN_SLPM_DIR(cfg);
 		int slpm_val = PIN_SLPM_VAL(cfg);
 
+		af = NMK_GPIO_ALT_GPIO;
+
 		/*
 		 * The SLPM_* values are normal values + 1 to allow zero to
 		 * mean "same as normal".
Signature · 1 line
1.7.3.2
#10 lore raw

PATCH 8/9 plat-nomadik: implement safe switch sequence for Alt-C

In reply to Linus Walleij

From: Rabin Vincent <[email protected]>

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.

---
 arch/arm/plat-nomadik/gpio.c              |  211 +++++++++++++++++++++++------
 arch/arm/plat-nomadik/include/plat/gpio.h |    1 +
 2 files changed, 172 insertions(+), 40 deletions(-)
arch/arm/plat-nomadik/gpio.c +171 -40
index bf299cf..9f1b720 100644
@@ -47,6 +47,8 @@ static const u32 backup_regs[] = {
 	NMK_GPIO_FWIMSC,
 };
 
+#define NMK_GPIO_PER_CHIP	32
+
 struct nmk_gpio_chip {
 	struct gpio_chip chip;
 	void __iomem *addr;
@@ -55,6 +57,7 @@ struct nmk_gpio_chip {
 	unsigned int parent_irq;
 	int secondary_parent_irq;
 	u32 (*get_secondary_status)(unsigned int bank);
+	void (*set_ioforce)(bool enable);
 	spinlock_t lock;
 	/* Keep track of configured edges */
 	u32 edge_rising;
@@ -64,6 +67,13 @@ struct nmk_gpio_chip {
 	u32 pull;
 };
 
+static struct nmk_gpio_chip *
+nmk_gpio_chips[DIV_ROUND_UP(ARCH_NR_GPIOS, NMK_GPIO_PER_CHIP)];
+
+static DEFINE_SPINLOCK(nmk_gpio_slpm_lock);
+
+#define NUM_BANKS ARRAY_SIZE(nmk_gpio_chips)
+
 static void __nmk_gpio_set_mode(struct nmk_gpio_chip *nmk_chip,
 				unsigned offset, int gpio_mode)
 {
@@ -138,8 +148,38 @@ static void __nmk_gpio_make_output(struct nmk_gpio_chip *nmk_chip,
 	__nmk_gpio_set_output(nmk_chip, offset, val);
 }
 
+static void __nmk_gpio_set_mode_safe(struct nmk_gpio_chip *nmk_chip,
+				     unsigned offset, int gpio_mode,
+				     bool glitch)
+{
+	u32 rwimsc;
+	u32 fwimsc;
+
+	if (glitch && nmk_chip->set_ioforce) {
+		u32 bit = BIT(offset);
+
+		rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
+		fwimsc = readl(nmk_chip->addr + NMK_GPIO_FWIMSC);
+
+		/* Prevent spurious wakeups */
+		writel(rwimsc & ~bit, nmk_chip->addr + NMK_GPIO_RWIMSC);
+		writel(fwimsc & ~bit, nmk_chip->addr + NMK_GPIO_FWIMSC);
+
+		nmk_chip->set_ioforce(true);
+	}
+
+	__nmk_gpio_set_mode(nmk_chip, offset, gpio_mode);
+
+	if (glitch && nmk_chip->set_ioforce) {
+		nmk_chip->set_ioforce(false);
+
+		writel(rwimsc, nmk_chip->addr + NMK_GPIO_RWIMSC);
+		writel(fwimsc, nmk_chip->addr + NMK_GPIO_FWIMSC);
+	}
+}
+
 static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
-			     pin_cfg_t cfg, bool sleep)
+			     pin_cfg_t cfg, bool sleep, unsigned int *slpmregs)
 {
 	static const char *afnames[] = {
 		[NMK_GPIO_ALT_GPIO]	= "GPIO",
@@ -164,6 +204,7 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
 	int slpm = PIN_SLPM(cfg);
 	int output = PIN_DIR(cfg);
 	int val = PIN_VAL(cfg);
+	bool glitch = af == NMK_GPIO_ALT_C;
 
 	dev_dbg(nmk_chip->chip.dev, "pin %d [%#lx]: af %s, pull %s, slpm %s (%s%s)\n",
 		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,
 		__nmk_gpio_set_pull(nmk_chip, offset, pull);
 	}
 
-	__nmk_gpio_set_slpm(nmk_chip, offset, slpm);
-	__nmk_gpio_set_mode(nmk_chip, offset, af);
+	/*
+	 * If we've backed up the SLPM registers (glitch workaround), modify
+	 * the backups since they will be restored.
+	 */
+	if (slpmregs) {
+		if (slpm == NMK_GPIO_SLPM_NOCHANGE)
+			slpmregs[nmk_chip->bank] |= BIT(offset);
+		else
+			slpmregs[nmk_chip->bank] &= ~BIT(offset);
+	} else
+		__nmk_gpio_set_slpm(nmk_chip, offset, slpm);
+
+	__nmk_gpio_set_mode_safe(nmk_chip, offset, af, glitch);
+}
+
+/*
+ * Safe sequence used to switch IOs between GPIO and Alternate-C mode:
+ *  - Save SLPM registers
+ *  - Set SLPM=0 for the IOs you want to switch and others to 1
+ *  - Configure the GPIO registers for the IOs that are being switched
+ *  - Set IOFORCE=1
+ *  - Modify the AFLSA/B registers for the IOs that are being switched
+ *  - Set IOFORCE=0
+ *  - Restore SLPM registers
+ *  - Any spurious wake up event during switch sequence to be ignored and
+ *    cleared
+ */
+static void nmk_gpio_glitch_slpm_init(unsigned int *slpm)
+{
+	int i;
+
+	for (i = 0; i < NUM_BANKS; i++) {
+		struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+		unsigned int temp = slpm[i];
+
+		if (!chip)
+			break;
+
+		slpm[i] = readl(chip->addr + NMK_GPIO_SLPC);
+		writel(temp, chip->addr + NMK_GPIO_SLPC);
+	}
+}
+
+static void nmk_gpio_glitch_slpm_restore(unsigned int *slpm)
+{
+	int i;
+
+	for (i = 0; i < NUM_BANKS; i++) {
+		struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+		if (!chip)
+			break;
+
+		writel(slpm[i], chip->addr + NMK_GPIO_SLPC);
+	}
+}
+
+static int __nmk_config_pins(pin_cfg_t *cfgs, int num, bool sleep)
+{
+	static unsigned int slpm[NUM_BANKS];
+	unsigned long flags;
+	bool glitch = false;
+	int ret = 0;
+	int i;
+
+	for (i = 0; i < num; i++) {
+		if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C) {
+			glitch = true;
+			break;
+		}
+	}
+
+	spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+
+	if (glitch) {
+		memset(slpm, 0xff, sizeof(slpm));
+
+		for (i = 0; i < num; i++) {
+			int pin = PIN_NUM(cfgs[i]);
+			int offset = pin % NMK_GPIO_PER_CHIP;
+
+			if (PIN_ALT(cfgs[i]) == NMK_GPIO_ALT_C)
+				slpm[pin / NMK_GPIO_PER_CHIP] &= ~BIT(offset);
+		}
+
+		nmk_gpio_glitch_slpm_init(slpm);
+	}
+
+	for (i = 0; i < num; i++) {
+		struct nmk_gpio_chip *nmk_chip;
+		int pin = PIN_NUM(cfgs[i]);
+
+		nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(pin));
+		if (!nmk_chip) {
+			ret = -EINVAL;
+			break;
+		}
+
+		spin_lock(&nmk_chip->lock);
+		__nmk_config_pin(nmk_chip, pin - nmk_chip->chip.base,
+				 cfgs[i], sleep, glitch ? slpm : NULL);
+		spin_unlock(&nmk_chip->lock);
+	}
+
+	if (glitch)
+		nmk_gpio_glitch_slpm_restore(slpm);
+
+	spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
+
+	return ret;
 }
 
 /**
@@ -222,19 +371,7 @@ static void __nmk_config_pin(struct nmk_gpio_chip *nmk_chip, unsigned offset,
  */
 int nmk_config_pin(pin_cfg_t cfg, bool sleep)
 {
-	struct nmk_gpio_chip *nmk_chip;
-	int gpio = PIN_NUM(cfg);
-	unsigned long flags;
-
-	nmk_chip = get_irq_chip_data(NOMADIK_GPIO_TO_IRQ(gpio));
-	if (!nmk_chip)
-		return -EINVAL;
-
-	spin_lock_irqsave(&nmk_chip->lock, flags);
-	__nmk_config_pin(nmk_chip, gpio - nmk_chip->chip.base, cfg, sleep);
-	spin_unlock_irqrestore(&nmk_chip->lock, flags);
-
-	return 0;
+	return __nmk_config_pins(&cfg, 1, sleep);
 }
 EXPORT_SYMBOL(nmk_config_pin);
 
@@ -248,31 +385,13 @@ EXPORT_SYMBOL(nmk_config_pin);
  */
 int nmk_config_pins(pin_cfg_t *cfgs, int num)
 {
-	int ret = 0;
-	int i;
-
-	for (i = 0; i < num; i++) {
-		ret = nmk_config_pin(cfgs[i], false);
-		if (ret)
-			break;
-	}
-
-	return ret;
+	return __nmk_config_pins(cfgs, num, false);
 }
 EXPORT_SYMBOL(nmk_config_pins);
 
 int nmk_config_pins_sleep(pin_cfg_t *cfgs, int num)
 {
-	int ret = 0;
-	int i;
-
-	for (i = 0; i < num; i++) {
-		ret = nmk_config_pin(cfgs[i], true);
-		if (ret)
-			break;
-	}
-
-	return ret;
+	return __nmk_config_pins(cfgs, num, true);
 }
 EXPORT_SYMBOL(nmk_config_pins_sleep);
 
@@ -299,9 +418,13 @@ int nmk_gpio_set_slpm(int gpio, enum nmk_gpio_slpm mode)
 	if (!nmk_chip)
 		return -EINVAL;
 
-	spin_lock_irqsave(&nmk_chip->lock, flags);
+	spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+	spin_lock(&nmk_chip->lock);
+
 	__nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base, mode);
-	spin_unlock_irqrestore(&nmk_chip->lock, flags);
+
+	spin_unlock(&nmk_chip->lock);
+	spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
 
 	return 0;
 }
@@ -474,7 +597,9 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
 	if (!nmk_chip)
 		return -EINVAL;
 
-	spin_lock_irqsave(&nmk_chip->lock, flags);
+	spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+	spin_lock(&nmk_chip->lock);
+
 #ifdef CONFIG_ARCH_U8500
 	if (cpu_is_u8500v2()) {
 		__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)
 	}
 #endif
 	__nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on);
-	spin_unlock_irqrestore(&nmk_chip->lock, flags);
+
+	spin_unlock(&nmk_chip->lock);
+	spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
 
 	return 0;
 }
@@ -826,6 +953,7 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
 	nmk_chip->parent_irq = irq;
 	nmk_chip->secondary_parent_irq = secondary_irq;
 	nmk_chip->get_secondary_status = pdata->get_secondary_status;
+	nmk_chip->set_ioforce = pdata->set_ioforce;
 	spin_lock_init(&nmk_chip->lock);
 
 	chip = &nmk_chip->chip;
@@ -839,6 +967,9 @@ static int __devinit nmk_gpio_probe(struct platform_device *dev)
 	if (ret)
 		goto out_free;
 
+	BUG_ON(nmk_chip->bank >= ARRAY_SIZE(nmk_gpio_chips));
+
+	nmk_gpio_chips[nmk_chip->bank] = nmk_chip;
 	platform_set_drvdata(dev, nmk_chip);
 
 	nmk_gpio_init_irq(nmk_chip);
arch/arm/plat-nomadik/include/plat/gpio.h +1 -0
index d108a32..e3a4837 100644
@@ -84,6 +84,7 @@ struct nmk_gpio_platform_data {
 	int first_irq;
 	int num_gpio;
 	u32 (*get_secondary_status)(unsigned int bank);
+	void (*set_ioforce)(bool enable);
 };
 
 #endif /* __ASM_PLAT_GPIO_H */
Signature · 1 line
1.7.3.2
#11 lore raw

PATCH 9/9 plat-nomadik: change sleep/wakeup setting in GPIO SLPM register

In reply to Linus Walleij

From: Rikard Olsson <[email protected]>

This patch fixes a bug when setting SLPM register for DB8500. When calling__nmk_gpio_set_slpm(...) offset to GPIO is now used instead of the GPIO number itself.

---
 arch/arm/plat-nomadik/gpio.c |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)
arch/arm/plat-nomadik/gpio.c +1 -1
index 9f1b720..acc9de2 100644
@@ -602,7 +602,7 @@ static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
 
 #ifdef CONFIG_ARCH_U8500
 	if (cpu_is_u8500v2()) {
-		__nmk_gpio_set_slpm(nmk_chip, gpio,
+		__nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base,
 				    on ? NMK_GPIO_SLPM_WAKEUP_ENABLE
 				       : NMK_GPIO_SLPM_WAKEUP_DISABLE);
 	}
Signature · 1 line
1.7.3.2