* Symbols in this file are called "nmk_gpio" for "nomadik gpio"
*/
-static const u32 backup_regs[] = {
- 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,
-};
-
#define NMK_GPIO_PER_CHIP 32
struct nmk_gpio_chip {
/* Keep track of configured edges */
u32 edge_rising;
u32 edge_falling;
- u32 backup[ARRAY_SIZE(backup_regs)];
- /* Bitmap, 1 = pull up, 0 = pull down */
- u32 pull;
+ u32 real_wake;
+ u32 rwimsc;
+ u32 fwimsc;
+ u32 slpm;
};
static struct nmk_gpio_chip *
pdis &= ~bit;
writel(pdis, nmk_chip->addr + NMK_GPIO_PDIS);
- if (pull == NMK_GPIO_PULL_UP) {
- nmk_chip->pull |= bit;
+ if (pull == NMK_GPIO_PULL_UP)
writel(bit, nmk_chip->addr + NMK_GPIO_DATS);
- } else if (pull == NMK_GPIO_PULL_DOWN) {
- nmk_chip->pull &= ~bit;
+ else if (pull == NMK_GPIO_PULL_DOWN)
writel(bit, nmk_chip->addr + NMK_GPIO_DATC);
- }
}
static void __nmk_gpio_make_input(struct nmk_gpio_chip *nmk_chip,
unsigned offset, int gpio_mode,
bool glitch)
{
- u32 rwimsc;
- u32 fwimsc;
+ u32 rwimsc = readl(nmk_chip->addr + NMK_GPIO_RWIMSC);
+ u32 fwimsc = readl(nmk_chip->addr + NMK_GPIO_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);
}
}
-static int nmk_gpio_irq_modify(struct irq_data *d, enum nmk_gpio_irq_type which,
- bool enable)
+static void __nmk_gpio_set_wake(struct nmk_gpio_chip *nmk_chip,
+ int gpio, bool on)
+{
+#ifdef CONFIG_ARCH_U8500
+ if (cpu_is_u8500v2()) {
+ __nmk_gpio_set_slpm(nmk_chip, gpio - nmk_chip->chip.base,
+ on ? NMK_GPIO_SLPM_WAKEUP_ENABLE
+ : NMK_GPIO_SLPM_WAKEUP_DISABLE);
+ }
+#endif
+ __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on);
+}
+
+static int nmk_gpio_irq_maskunmask(struct irq_data *d, bool enable)
{
int gpio;
struct nmk_gpio_chip *nmk_chip;
if (!nmk_chip)
return -EINVAL;
- spin_lock_irqsave(&nmk_chip->lock, flags);
- __nmk_gpio_irq_modify(nmk_chip, gpio, which, enable);
- spin_unlock_irqrestore(&nmk_chip->lock, flags);
+ spin_lock_irqsave(&nmk_gpio_slpm_lock, flags);
+ spin_lock(&nmk_chip->lock);
+
+ __nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, enable);
+
+ if (!(nmk_chip->real_wake & bitmask))
+ __nmk_gpio_set_wake(nmk_chip, gpio, enable);
+
+ spin_unlock(&nmk_chip->lock);
+ spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
return 0;
}
static void nmk_gpio_irq_mask(struct irq_data *d)
{
- nmk_gpio_irq_modify(d, NORMAL, false);
+ nmk_gpio_irq_maskunmask(d, false);
}
static void nmk_gpio_irq_unmask(struct irq_data *d)
{
- nmk_gpio_irq_modify(d, NORMAL, true);
+ nmk_gpio_irq_maskunmask(d, true);
}
static int nmk_gpio_irq_set_wake(struct irq_data *d, unsigned int on)
{
+ struct irq_desc *desc = irq_to_desc(d->irq);
+ bool enabled = !(desc->status & IRQ_DISABLED);
struct nmk_gpio_chip *nmk_chip;
unsigned long flags;
+ u32 bitmask;
int gpio;
gpio = NOMADIK_IRQ_TO_GPIO(d->irq);
nmk_chip = irq_data_get_irq_chip_data(d);
if (!nmk_chip)
return -EINVAL;
+ bitmask = nmk_gpio_get_bitmask(gpio);
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,
- on ? NMK_GPIO_SLPM_WAKEUP_ENABLE
- : NMK_GPIO_SLPM_WAKEUP_DISABLE);
- }
-#endif
- __nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, on);
+ if (!enabled)
+ __nmk_gpio_set_wake(nmk_chip, gpio, on);
+
+ if (on)
+ nmk_chip->real_wake |= bitmask;
+ else
+ nmk_chip->real_wake &= ~bitmask;
spin_unlock(&nmk_chip->lock);
spin_unlock_irqrestore(&nmk_gpio_slpm_lock, flags);
if (enabled)
__nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, false);
- if (wake)
+ if (enabled || wake)
__nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, false);
nmk_chip->edge_rising &= ~bitmask;
if (enabled)
__nmk_gpio_irq_modify(nmk_chip, gpio, NORMAL, true);
- if (wake)
+ if (enabled || wake)
__nmk_gpio_irq_modify(nmk_chip, gpio, WAKE, true);
spin_unlock_irqrestore(&nmk_chip->lock, flags);
: "? ",
(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");
}
}
.can_sleep = 0,
};
+/*
+ * Called from the suspend/resume path to only keep the real wakeup interrupts
+ * (those that have had set_irq_wake() called on them) as wakeup interrupts,
+ * and not the rest of the interrupts which we needed to have as wakeups for
+ * cpuidle.
+ *
+ * PM ops are not used since this needs to be done at the end, after all the
+ * other drivers are done with their suspend callbacks.
+ */
+void nmk_gpio_wakeups_suspend(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ break;
+
+ chip->rwimsc = readl(chip->addr + NMK_GPIO_RWIMSC);
+ chip->fwimsc = readl(chip->addr + NMK_GPIO_FWIMSC);
+
+ writel(chip->rwimsc & chip->real_wake,
+ chip->addr + NMK_GPIO_RWIMSC);
+ writel(chip->fwimsc & chip->real_wake,
+ chip->addr + NMK_GPIO_FWIMSC);
+
+ if (cpu_is_u8500v2()) {
+ chip->slpm = readl(chip->addr + NMK_GPIO_SLPC);
+
+ /* 0 -> wakeup enable */
+ writel(~chip->real_wake, chip->addr + NMK_GPIO_SLPC);
+ }
+ }
+}
+
+void nmk_gpio_wakeups_resume(void)
+{
+ int i;
+
+ for (i = 0; i < NUM_BANKS; i++) {
+ struct nmk_gpio_chip *chip = nmk_gpio_chips[i];
+
+ if (!chip)
+ break;
+
+ writel(chip->rwimsc, chip->addr + NMK_GPIO_RWIMSC);
+ writel(chip->fwimsc, chip->addr + NMK_GPIO_FWIMSC);
+
+ if (cpu_is_u8500v2())
+ writel(chip->slpm, chip->addr + NMK_GPIO_SLPC);
+ }
+}
+
static int __devinit nmk_gpio_probe(struct platform_device *dev)
{
struct nmk_gpio_platform_data *pdata = dev->dev.platform_data;
return ret;
}
-#ifdef CONFIG_NOMADIK_GPIO_PM
-static int nmk_gpio_pm(struct platform_device *dev, bool suspend)
-{
- struct nmk_gpio_chip *nmk_chip = platform_get_drvdata(dev);
- int i;
- u32 dir;
- u32 dat;
-
- for (i = 0; i < ARRAY_SIZE(backup_regs); i++) {
- if (suspend)
- nmk_chip->backup[i] = readl(nmk_chip->addr +
- backup_regs[i]);
- else
- writel(nmk_chip->backup[i],
- nmk_chip->addr + backup_regs[i]);
- }
-
- if (!suspend) {
- /*
- * Restore pull-up and pull-down on inputs and
- * outputs.
- */
- dir = readl(nmk_chip->addr + NMK_GPIO_DIR);
- dat = readl(nmk_chip->addr + NMK_GPIO_DAT);
-
- writel((nmk_chip->pull & ~dir) |
- (dat & dir),
- nmk_chip->addr + NMK_GPIO_DATS);
-
- writel((~nmk_chip->pull & ~dir) |
- (~dat & dir),
- nmk_chip->addr + NMK_GPIO_DATC);
- }
- 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 = nmk_gpio_suspend,
- .resume = nmk_gpio_resume,
};
static int __init nmk_gpio_init(void)