int parent_irq;
int num_gpios;
int parent_wake_irq;
+ bool suspended;
};
#define MAX_GPIO_PER_BANK 32
{
int ret = 0;
+ if (priv->parent_wake_irq == priv->parent_irq)
+ return ret;
+
if (enable)
ret = enable_irq_wake(priv->parent_wake_irq);
else
while ((status = brcmstb_gpio_get_active_irqs(bank))) {
unsigned int offset;
+ if (priv->suspended && bank->wake_active & status) {
+ priv->suspended = false;
+ pm_wakeup_event(&priv->pdev->dev, 0);
+ }
+
for_each_set_bit(offset, &status, 32) {
if (offset >= bank->width)
dev_warn(&priv->pdev->dev,
}
if (of_property_read_bool(np, "wakeup-source")) {
+ /*
+ * Set wakeup capability so we can process boot-time
+ * "wakeups" (e.g., from S5 cold boot).
+ */
+ device_set_wakeup_capable(dev, true);
+ device_wakeup_enable(dev);
priv->parent_wake_irq = platform_get_irq(pdev, 1);
if (priv->parent_wake_irq < 0) {
- priv->parent_wake_irq = 0;
+ priv->parent_wake_irq = priv->parent_irq;
dev_warn(dev,
"Couldn't get wake IRQ - GPIOs will not be able to wake from sleep");
} else {
- /*
- * Set wakeup capability so we can process boot-time
- * "wakeups" (e.g., from S5 cold boot)
- */
- device_set_wakeup_capable(dev, true);
- device_wakeup_enable(dev);
err = devm_request_irq(dev, priv->parent_wake_irq,
brcmstb_gpio_wake_irq_handler,
IRQF_SHARED,
goto out_free_domain;
}
}
+ priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake;
}
priv->irq_chip.name = dev_name(dev);
priv->irq_chip.irq_ack = brcmstb_gpio_irq_ack;
priv->irq_chip.irq_set_type = brcmstb_gpio_irq_set_type;
- if (priv->parent_wake_irq)
- priv->irq_chip.irq_set_wake = brcmstb_gpio_irq_set_wake;
-
irq_set_chained_handler_and_data(priv->parent_irq,
brcmstb_gpio_irq_handler, priv);
irq_set_status_flags(priv->parent_irq, IRQ_DISABLE_UNLAZY);
priv->reg_base + GIO_BANK_OFF(bank->id, i));
}
-static void brcmstb_gpio_quiesce(struct device *dev, bool save)
+static void brcmstb_gpio_quiesce(struct brcmstb_gpio_priv *priv, bool save)
{
- struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
struct brcmstb_gpio_bank *bank;
u32 imask;
- /* disable non-wake interrupt */
- if (priv->parent_irq >= 0)
- disable_irq(priv->parent_irq);
-
list_for_each_entry(bank, &priv->bank_list, node) {
if (save)
brcmstb_gpio_bank_save(priv, bank);
static void brcmstb_gpio_shutdown(struct platform_device *pdev)
{
+ struct brcmstb_gpio_priv *priv = dev_get_drvdata(&pdev->dev);
+
+ if (priv->parent_irq > 0)
+ disable_irq(priv->parent_irq);
+
/* Enable GPIO for S5 cold boot */
- brcmstb_gpio_quiesce(&pdev->dev, false);
+ brcmstb_gpio_quiesce(priv, false);
}
static void brcmstb_gpio_bank_restore(struct brcmstb_gpio_priv *priv,
static int brcmstb_gpio_suspend(struct device *dev)
{
- brcmstb_gpio_quiesce(dev, true);
+ struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
+
+ if (priv->parent_irq > 0)
+ priv->suspended = true;
+
+ return 0;
+}
+
+static int brcmstb_gpio_suspend_noirq(struct device *dev)
+{
+ struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
+
+ /* Catch any wakeup sources occurring between suspend and noirq */
+ if (!priv->suspended)
+ return -EBUSY;
+
+ if (priv->parent_irq > 0)
+ disable_irq(priv->parent_irq);
+
+ brcmstb_gpio_quiesce(priv, true);
+
+ if (priv->parent_wake_irq)
+ enable_irq(priv->parent_irq);
+
return 0;
}
{
struct brcmstb_gpio_priv *priv = dev_get_drvdata(dev);
struct brcmstb_gpio_bank *bank;
- bool need_wakeup_event = false;
- list_for_each_entry(bank, &priv->bank_list, node) {
- need_wakeup_event |= !!__brcmstb_gpio_get_active_irqs(bank);
- brcmstb_gpio_bank_restore(priv, bank);
- }
+ if (priv->parent_wake_irq)
+ disable_irq(priv->parent_irq);
- if (priv->parent_wake_irq && need_wakeup_event)
- pm_wakeup_event(dev, 0);
+ priv->suspended = false;
+
+ list_for_each_entry(bank, &priv->bank_list, node)
+ brcmstb_gpio_bank_restore(priv, bank);
- /* enable non-wake interrupt */
- if (priv->parent_irq >= 0)
+ if (priv->parent_irq > 0)
enable_irq(priv->parent_irq);
return 0;
}
static const struct dev_pm_ops brcmstb_gpio_pm_ops = {
- .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend),
+ .suspend = pm_sleep_ptr(brcmstb_gpio_suspend),
+ .suspend_noirq = pm_sleep_ptr(brcmstb_gpio_suspend_noirq),
.resume_noirq = pm_sleep_ptr(brcmstb_gpio_resume),
};