diff options
Diffstat (limited to 'drivers/power/reset/gemini-poweroff.c')
| -rw-r--r-- | drivers/power/reset/gemini-poweroff.c | 65 |
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; |
