summaryrefslogtreecommitdiffstats
path: root/drivers/power/reset
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/reset')
-rw-r--r--drivers/power/reset/Kconfig16
-rw-r--r--drivers/power/reset/Makefile2
-rw-r--r--drivers/power/reset/at91-poweroff.c14
-rw-r--r--drivers/power/reset/at91-reset.c4
-rw-r--r--drivers/power/reset/gemini-poweroff.c30
-rw-r--r--drivers/power/reset/gpio-poweroff.c8
-rw-r--r--drivers/power/reset/ocelot-reset.c88
-rw-r--r--drivers/power/reset/sc27xx-poweroff.c66
8 files changed, 204 insertions, 24 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index a102e74ab24e..df58fc878b3e 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -104,6 +104,13 @@ config POWER_RESET_MSM
help
Power off and restart support for Qualcomm boards.
+config POWER_RESET_OCELOT_RESET
+ bool "Microsemi Ocelot reset driver"
+ depends on MSCC_OCELOT || COMPILE_TEST
+ select MFD_SYSCON
+ help
+ This driver supports restart for Microsemi Ocelot SoC.
+
config POWER_RESET_PIIX4_POWEROFF
tristate "Intel PIIX4 power-off driver"
depends on PCI
@@ -218,5 +225,14 @@ config SYSCON_REBOOT_MODE
register, then the bootloader can read it to take different
action according to the mode.
+config POWER_RESET_SC27XX
+ bool "Spreadtrum SC27xx PMIC power-off driver"
+ depends on MFD_SC27XX_PMIC || COMPILE_TEST
+ help
+ This driver supports powering off a system through
+ Spreadtrum SC27xx series PMICs. The SC27xx series
+ PMICs includes the SC2720, SC2721, SC2723, SC2730
+ and SC2731 chips.
+
endif
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index dcc92f5f7a37..7778c7485cf1 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
+obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o
obj-$(CONFIG_POWER_RESET_PIIX4_POWEROFF) += piix4-poweroff.o
obj-$(CONFIG_POWER_RESET_LTC2952) += ltc2952-poweroff.o
obj-$(CONFIG_POWER_RESET_QNAP) += qnap-poweroff.o
@@ -26,3 +27,4 @@ obj-$(CONFIG_POWER_RESET_RMOBILE) += rmobile-reset.o
obj-$(CONFIG_POWER_RESET_ZX) += zx-reboot.o
obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o
obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o
+obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o
diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c
index c30c40193aaa..fb2fc8f741a1 100644
--- a/drivers/power/reset/at91-poweroff.c
+++ b/drivers/power/reset/at91-poweroff.c
@@ -55,10 +55,10 @@ static void __iomem *at91_shdwc_base;
static struct clk *sclk;
static void __iomem *mpddrc_base;
-static void __init at91_wakeup_status(void)
+static void __init at91_wakeup_status(struct platform_device *pdev)
{
+ const char *reason;
u32 reg = readl(at91_shdwc_base + AT91_SHDW_SR);
- char *reason = "unknown";
/* Simple power-on, just bail out */
if (!reg)
@@ -68,8 +68,10 @@ static void __init at91_wakeup_status(void)
reason = "RTT";
else if (reg & AT91_SHDW_RTCWK)
reason = "RTC";
+ else
+ reason = "unknown";
- pr_info("AT91: Wake-Up source: %s\n", reason);
+ dev_info(&pdev->dev, "Wake-Up source: %s\n", reason);
}
static void at91_poweroff(void)
@@ -157,10 +159,8 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
at91_shdwc_base = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(at91_shdwc_base)) {
- dev_err(&pdev->dev, "Could not map reset controller address\n");
+ if (IS_ERR(at91_shdwc_base))
return PTR_ERR(at91_shdwc_base);
- }
sclk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(sclk))
@@ -172,7 +172,7 @@ static int __init at91_poweroff_probe(struct platform_device *pdev)
return ret;
}
- at91_wakeup_status();
+ at91_wakeup_status(pdev);
if (pdev->dev.of_node)
at91_poweroff_dt_set_wakeup_mode(pdev);
diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c
index b99769f8ab15..f44a9ffcc2ab 100644
--- a/drivers/power/reset/at91-reset.c
+++ b/drivers/power/reset/at91-reset.c
@@ -145,8 +145,8 @@ static int samx7_restart(struct notifier_block *this, unsigned long mode,
static void __init at91_reset_status(struct platform_device *pdev)
{
+ const char *reason;
u32 reg = readl(at91_rstc_base + AT91_RSTC_SR);
- char *reason;
switch ((reg & AT91_RSTC_RSTTYP) >> 8) {
case RESET_TYPE_GENERAL:
@@ -169,7 +169,7 @@ static void __init at91_reset_status(struct platform_device *pdev)
break;
}
- pr_info("AT91: Starting after %s\n", reason);
+ dev_info(&pdev->dev, "Starting after %s\n", reason);
}
static const struct of_device_id at91_ramc_of_match[] = {
diff --git a/drivers/power/reset/gemini-poweroff.c b/drivers/power/reset/gemini-poweroff.c
index ff75af5abbc5..2ac291af1265 100644
--- a/drivers/power/reset/gemini-poweroff.c
+++ b/drivers/power/reset/gemini-poweroff.c
@@ -47,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");
@@ -116,7 +120,17 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
return -ENODEV;
}
- /* Clear the power management IRQ */
+ /*
+ * Enable the power controller. This is crucial on Gemini
+ * systems: if this is not done, pressing the power button
+ * will result in unconditional poweroff without any warning.
+ * This makes the kernel handle the poweroff.
+ */
+ val = readl(gpw->base + GEMINI_PWC_CTRLREG);
+ val |= GEMINI_CTRL_ENABLE;
+ writel(val, gpw->base + GEMINI_PWC_CTRLREG);
+
+ /* Now that the state machine is active, clear the IRQ */
val = readl(gpw->base + GEMINI_PWC_CTRLREG);
val |= GEMINI_CTRL_IRQ_CLR;
writel(val, gpw->base + GEMINI_PWC_CTRLREG);
@@ -129,16 +143,6 @@ static int gemini_poweroff_probe(struct platform_device *pdev)
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
- * will result in unconditional poweroff without any warning.
- * This makes the kernel handle the poweroff.
- */
- val = readl(gpw->base + GEMINI_PWC_CTRLREG);
- val |= GEMINI_CTRL_ENABLE;
- writel(val, gpw->base + GEMINI_PWC_CTRLREG);
-
dev_info(dev, "Gemini poweroff driver registered\n");
return 0;
diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c
index be3d81ff51cc..6273ad3b411d 100644
--- a/drivers/power/reset/gpio-poweroff.c
+++ b/drivers/power/reset/gpio-poweroff.c
@@ -19,11 +19,13 @@
#include <linux/of_platform.h>
#include <linux/module.h>
+#define DEFAULT_TIMEOUT_MS 3000
/*
* Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global.
*/
static struct gpio_desc *reset_gpio;
+static u32 timeout = DEFAULT_TIMEOUT_MS;
static void gpio_poweroff_do_poweroff(void)
{
@@ -40,7 +42,7 @@ static void gpio_poweroff_do_poweroff(void)
gpiod_set_value(reset_gpio, 1);
/* give it some time */
- mdelay(3000);
+ mdelay(timeout);
WARN_ON(1);
}
@@ -58,12 +60,14 @@ static int gpio_poweroff_probe(struct platform_device *pdev)
return -EBUSY;
}
- input = of_property_read_bool(pdev->dev.of_node, "input");
+ input = device_property_read_bool(&pdev->dev, "input");
if (input)
flags = GPIOD_IN;
else
flags = GPIOD_OUT_LOW;
+ device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
+
reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
if (IS_ERR(reset_gpio))
return PTR_ERR(reset_gpio);
diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c
new file mode 100644
index 000000000000..5a13a5cc8188
--- /dev/null
+++ b/drivers/power/reset/ocelot-reset.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+/*
+ * Microsemi MIPS SoC reset driver
+ *
+ * License: Dual MIT/GPL
+ * Copyright (c) 2017 Microsemi Corporation
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/notifier.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/reboot.h>
+#include <linux/regmap.h>
+
+struct ocelot_reset_context {
+ void __iomem *base;
+ struct regmap *cpu_ctrl;
+ struct notifier_block restart_handler;
+};
+
+#define ICPU_CFG_CPU_SYSTEM_CTRL_RESET 0x20
+#define CORE_RST_PROTECT BIT(2)
+
+#define SOFT_CHIP_RST BIT(0)
+
+static int ocelot_restart_handle(struct notifier_block *this,
+ unsigned long mode, void *cmd)
+{
+ struct ocelot_reset_context *ctx = container_of(this, struct
+ ocelot_reset_context,
+ restart_handler);
+
+ /* Make sure the core is not protected from reset */
+ regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_RESET,
+ CORE_RST_PROTECT, 0);
+
+ writel(SOFT_CHIP_RST, ctx->base);
+
+ pr_emerg("Unable to restart system\n");
+ return NOTIFY_DONE;
+}
+
+static int ocelot_reset_probe(struct platform_device *pdev)
+{
+ struct ocelot_reset_context *ctx;
+ struct resource *res;
+
+ struct device *dev = &pdev->dev;
+ int err;
+
+ ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ ctx->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(ctx->base))
+ return PTR_ERR(ctx->base);
+
+ ctx->cpu_ctrl = syscon_regmap_lookup_by_compatible("mscc,ocelot-cpu-syscon");
+ if (IS_ERR(ctx->cpu_ctrl))
+ return PTR_ERR(ctx->cpu_ctrl);
+
+ ctx->restart_handler.notifier_call = ocelot_restart_handle;
+ ctx->restart_handler.priority = 192;
+ err = register_restart_handler(&ctx->restart_handler);
+ if (err)
+ dev_err(dev, "can't register restart notifier (err=%d)\n", err);
+
+ return err;
+}
+
+static const struct of_device_id ocelot_reset_of_match[] = {
+ { .compatible = "mscc,ocelot-chip-reset" },
+ {}
+};
+
+static struct platform_driver ocelot_reset_driver = {
+ .probe = ocelot_reset_probe,
+ .driver = {
+ .name = "ocelot-chip-reset",
+ .of_match_table = ocelot_reset_of_match,
+ },
+};
+builtin_platform_driver(ocelot_reset_driver);
diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c
new file mode 100644
index 000000000000..29fb08b8faa0
--- /dev/null
+++ b/drivers/power/reset/sc27xx-poweroff.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Spreadtrum Communications Inc.
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/cpu.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/syscore_ops.h>
+
+#define SC27XX_PWR_PD_HW 0xc2c
+#define SC27XX_PWR_OFF_EN BIT(0)
+
+static struct regmap *regmap;
+
+/*
+ * On Spreadtrum platform, we need power off system through external SC27xx
+ * series PMICs, and it is one similar SPI bus mapped by regmap to access PMIC,
+ * which is not fast io access.
+ *
+ * So before stopping other cores, we need release other cores' resource by
+ * taking cpus down to avoid racing regmap or spi mutex lock when poweroff
+ * system through PMIC.
+ */
+static void sc27xx_poweroff_shutdown(void)
+{
+#ifdef CONFIG_PM_SLEEP_SMP
+ int cpu = smp_processor_id();
+
+ freeze_secondary_cpus(cpu);
+#endif
+}
+
+static struct syscore_ops poweroff_syscore_ops = {
+ .shutdown = sc27xx_poweroff_shutdown,
+};
+
+static void sc27xx_poweroff_do_poweroff(void)
+{
+ regmap_write(regmap, SC27XX_PWR_PD_HW, SC27XX_PWR_OFF_EN);
+}
+
+static int sc27xx_poweroff_probe(struct platform_device *pdev)
+{
+ if (regmap)
+ return -EINVAL;
+
+ regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ pm_power_off = sc27xx_poweroff_do_poweroff;
+ register_syscore_ops(&poweroff_syscore_ops);
+ return 0;
+}
+
+static struct platform_driver sc27xx_poweroff_driver = {
+ .probe = sc27xx_poweroff_probe,
+ .driver = {
+ .name = "sc27xx-poweroff",
+ },
+};
+builtin_platform_driver(sc27xx_poweroff_driver);
OpenPOWER on IntegriCloud