|
@@ -20,6 +20,7 @@
|
|
|
#include <linux/irqdomain.h>
|
|
|
#include <linux/irqchip/chained_irq.h>
|
|
|
#include <linux/interrupt.h>
|
|
|
+#include <linux/reboot.h>
|
|
|
|
|
|
#define GIO_BANK_SIZE 0x20
|
|
|
#define GIO_ODEN(bank) (((bank) * GIO_BANK_SIZE) + 0x00)
|
|
@@ -48,6 +49,7 @@ struct brcmstb_gpio_priv {
|
|
|
int gpio_base;
|
|
|
bool can_wake;
|
|
|
int parent_wake_irq;
|
|
|
+ struct notifier_block reboot_notifier;
|
|
|
};
|
|
|
|
|
|
#define MAX_GPIO_PER_BANK 32
|
|
@@ -167,10 +169,9 @@ static int brcmstb_gpio_irq_set_type(struct irq_data *d, unsigned int type)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
|
|
|
+static int brcmstb_gpio_priv_set_wake(struct brcmstb_gpio_priv *priv,
|
|
|
+ unsigned int enable)
|
|
|
{
|
|
|
- struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
|
- struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
|
|
|
int ret = 0;
|
|
|
|
|
|
/*
|
|
@@ -188,6 +189,14 @@ static int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
+static int brcmstb_gpio_irq_set_wake(struct irq_data *d, unsigned int enable)
|
|
|
+{
|
|
|
+ struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
|
+ struct brcmstb_gpio_priv *priv = brcmstb_gpio_gc_to_priv(gc);
|
|
|
+
|
|
|
+ return brcmstb_gpio_priv_set_wake(priv, enable);
|
|
|
+}
|
|
|
+
|
|
|
static irqreturn_t brcmstb_gpio_wake_irq_handler(int irq, void *data)
|
|
|
{
|
|
|
struct brcmstb_gpio_priv *priv = data;
|
|
@@ -246,6 +255,19 @@ static void brcmstb_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
|
|
|
chained_irq_exit(chip, desc);
|
|
|
}
|
|
|
|
|
|
+static int brcmstb_gpio_reboot(struct notifier_block *nb,
|
|
|
+ unsigned long action, void *data)
|
|
|
+{
|
|
|
+ struct brcmstb_gpio_priv *priv =
|
|
|
+ container_of(nb, struct brcmstb_gpio_priv, reboot_notifier);
|
|
|
+
|
|
|
+ /* Enable GPIO for S5 cold boot */
|
|
|
+ if (action == SYS_POWER_OFF)
|
|
|
+ brcmstb_gpio_priv_set_wake(priv, 1);
|
|
|
+
|
|
|
+ return NOTIFY_DONE;
|
|
|
+}
|
|
|
+
|
|
|
/* Make sure that the number of banks matches up between properties */
|
|
|
static int brcmstb_gpio_sanity_check_banks(struct device *dev,
|
|
|
struct device_node *np, struct resource *res)
|
|
@@ -276,6 +298,12 @@ static int brcmstb_gpio_remove(struct platform_device *pdev)
|
|
|
if (ret)
|
|
|
dev_err(&pdev->dev, "gpiochip_remove fail in cleanup\n");
|
|
|
}
|
|
|
+ if (priv->reboot_notifier.notifier_call) {
|
|
|
+ ret = unregister_reboot_notifier(&priv->reboot_notifier);
|
|
|
+ if (ret)
|
|
|
+ dev_err(&pdev->dev,
|
|
|
+ "failed to unregister reboot notifier\n");
|
|
|
+ }
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
@@ -333,7 +361,16 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,
|
|
|
dev_warn(dev,
|
|
|
"Couldn't get wake IRQ - GPIOs will not be able to wake from sleep");
|
|
|
} else {
|
|
|
- int err = devm_request_irq(dev, priv->parent_wake_irq,
|
|
|
+ int err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Set wakeup capability before requesting wakeup
|
|
|
+ * interrupt, 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, 0,
|
|
|
"brcmstb-gpio-wake", priv);
|
|
|
|
|
@@ -342,8 +379,9 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev,
|
|
|
return err;
|
|
|
}
|
|
|
|
|
|
- device_set_wakeup_capable(dev, true);
|
|
|
- device_wakeup_enable(dev);
|
|
|
+ priv->reboot_notifier.notifier_call =
|
|
|
+ brcmstb_gpio_reboot;
|
|
|
+ register_reboot_notifier(&priv->reboot_notifier);
|
|
|
priv->can_wake = true;
|
|
|
}
|
|
|
}
|
|
@@ -445,6 +483,12 @@ static int brcmstb_gpio_probe(struct platform_device *pdev)
|
|
|
/* not all ngpio lines are valid, will use bank width later */
|
|
|
gc->ngpio = MAX_GPIO_PER_BANK;
|
|
|
|
|
|
+ /*
|
|
|
+ * Mask all interrupts by default, since wakeup interrupts may
|
|
|
+ * be retained from S5 cold boot
|
|
|
+ */
|
|
|
+ bank->bgc.write_reg(reg_base + GIO_MASK(bank->id), 0);
|
|
|
+
|
|
|
err = gpiochip_add(gc);
|
|
|
if (err) {
|
|
|
dev_err(dev, "Could not add gpiochip for bank %d\n",
|