summaryrefslogtreecommitdiff
path: root/drivers/power/reset/gemini-poweroff.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/reset/gemini-poweroff.c')
-rw-r--r--drivers/power/reset/gemini-poweroff.c65
1 files changed, 40 insertions, 25 deletions
diff --git a/drivers/power/reset/gemini-poweroff.c b/drivers/power/reset/gemini-poweroff.c
index de878fd26f27..06d6992dec89 100644
--- a/drivers/power/reset/gemini-poweroff.c
+++ b/drivers/power/reset/gemini-poweroff.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Gemini power management controller
* Copyright (C) 2017 Linus Walleij <linus.walleij@linaro.org>
@@ -46,8 +47,12 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
val &= 0x70U;
switch (val) {
case GEMINI_STAT_CIR:
- dev_info(gpw->dev, "infrared poweroff\n");
- orderly_poweroff(true);
+ /*
+ * We do not yet have a driver for the infrared
+ * controller so it can cause spurious poweroff
+ * events. Ignore those for now.
+ */
+ dev_info(gpw->dev, "infrared poweroff - ignored\n");
break;
case GEMINI_STAT_RTC:
dev_info(gpw->dev, "RTC poweroff\n");
@@ -65,12 +70,9 @@ static irqreturn_t gemini_powerbutton_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
-/* This callback needs this static local as it has void as argument */
-static struct gemini_powercon *gpw_poweroff;
-
-static void gemini_poweroff(void)
+static int gemini_poweroff(struct sys_off_data *data)
{
- struct gemini_powercon *gpw = gpw_poweroff;
+ struct gemini_powercon *gpw = data->cb_data;
u32 val;
dev_crit(gpw->dev, "Gemini power off\n");
@@ -81,12 +83,13 @@ static void gemini_poweroff(void)
val &= ~GEMINI_CTRL_ENABLE;
val |= GEMINI_CTRL_SHUTDOWN;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+
+ return NOTIFY_DONE;
}
static int gemini_poweroff_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct resource *res;
struct gemini_powercon *gpw;
u32 val;
int irq;
@@ -96,14 +99,13 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
if (!gpw)
return -ENOMEM;
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- gpw->base = devm_ioremap_resource(dev, res);
+ gpw->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(gpw->base))
return PTR_ERR(gpw->base);
irq = platform_get_irq(pdev, 0);
- if (!irq)
- return -EINVAL;
+ if (irq < 0)
+ return irq;
gpw->dev = dev;
@@ -115,19 +117,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
return -ENODEV;
}
- /* Clear the power management IRQ */
- val = readl(gpw->base + GEMINI_PWC_CTRLREG);
- val |= GEMINI_CTRL_IRQ_CLR;
- writel(val, gpw->base + GEMINI_PWC_CTRLREG);
-
- ret = devm_request_irq(dev, irq, gemini_powerbutton_interrupt, 0,
- "poweroff", gpw);
- if (ret)
- return ret;
-
- pm_power_off = gemini_poweroff;
- gpw_poweroff = gpw;
-
/*
* Enable the power controller. This is crucial on Gemini
* systems: if this is not done, pressing the power button
@@ -138,6 +127,32 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
val |= GEMINI_CTRL_ENABLE;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+ /* Clear the IRQ */
+ val = readl(gpw->base + GEMINI_PWC_CTRLREG);
+ val |= GEMINI_CTRL_IRQ_CLR;
+ writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+
+ /* Wait for this to clear */
+ val = readl(gpw->base + GEMINI_PWC_STATREG);
+ while (val & 0x70U)
+ val = readl(gpw->base + GEMINI_PWC_STATREG);
+
+ /* Clear the IRQ again */
+ val = readl(gpw->base + GEMINI_PWC_CTRLREG);
+ val |= GEMINI_CTRL_IRQ_CLR;
+ writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+
+ ret = devm_request_irq(dev, irq, gemini_powerbutton_interrupt, 0,
+ "poweroff", gpw);
+ if (ret)
+ return ret;
+
+ ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF,
+ SYS_OFF_PRIO_DEFAULT,
+ gemini_poweroff, gpw);
+ if (ret)
+ return ret;
+
dev_info(dev, "Gemini poweroff driver registered\n");
return 0;