diff options
Diffstat (limited to 'drivers/power/reset')
-rw-r--r-- | drivers/power/reset/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/reset/Makefile | 1 | ||||
-rw-r--r-- | drivers/power/reset/ocelot-reset.c | 30 | ||||
-rw-r--r-- | drivers/power/reset/qnap-poweroff.c | 8 | ||||
-rw-r--r-- | drivers/power/reset/regulator-poweroff.c | 82 | ||||
-rw-r--r-- | drivers/power/reset/syscon-poweroff.c | 8 |
6 files changed, 121 insertions, 15 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index d55b3727e00e..b22c4fdb2561 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -177,6 +177,13 @@ config POWER_RESET_QNAP Say Y if you have a QNAP NAS. +config POWER_RESET_REGULATOR + bool "Regulator subsystem power-off driver" + depends on OF && REGULATOR + help + This driver supports turning off your board by disabling a + power regulator defined in the devicetree. + config POWER_RESET_RESTART bool "Restart power-off driver" help diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index c51eceba9ea3..9dc49d3a57ff 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -19,6 +19,7 @@ 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 +obj-$(CONFIG_POWER_RESET_REGULATOR) += regulator-poweroff.o obj-$(CONFIG_POWER_RESET_RESTART) += restart-poweroff.o obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c index f74e1dbb4ba3..8caa90cb58fc 100644 --- a/drivers/power/reset/ocelot-reset.c +++ b/drivers/power/reset/ocelot-reset.c @@ -29,6 +29,8 @@ struct ocelot_reset_context { struct notifier_block restart_handler; }; +#define BIT_OFF_INVALID 32 + #define SOFT_CHIP_RST BIT(0) #define ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL 0x24 @@ -50,9 +52,11 @@ static int ocelot_restart_handle(struct notifier_block *this, ctx->props->vcore_protect, 0); /* Make the SI back to boot mode */ - regmap_update_bits(ctx->cpu_ctrl, ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL, - IF_SI_OWNER_MASK << if_si_owner_bit, - IF_SI_OWNER_SIBM << if_si_owner_bit); + if (if_si_owner_bit != BIT_OFF_INVALID) + regmap_update_bits(ctx->cpu_ctrl, + ICPU_CFG_CPU_SYSTEM_CTRL_GENERAL_CTRL, + IF_SI_OWNER_MASK << if_si_owner_bit, + IF_SI_OWNER_SIBM << if_si_owner_bit); pr_emerg("Resetting SoC\n"); @@ -96,6 +100,20 @@ static int ocelot_reset_probe(struct platform_device *pdev) return err; } +static const struct reset_props reset_props_jaguar2 = { + .syscon = "mscc,ocelot-cpu-syscon", + .protect_reg = 0x20, + .vcore_protect = BIT(2), + .if_si_owner_bit = 6, +}; + +static const struct reset_props reset_props_luton = { + .syscon = "mscc,ocelot-cpu-syscon", + .protect_reg = 0x20, + .vcore_protect = BIT(2), + .if_si_owner_bit = BIT_OFF_INVALID, /* n/a */ +}; + static const struct reset_props reset_props_ocelot = { .syscon = "mscc,ocelot-cpu-syscon", .protect_reg = 0x20, @@ -112,6 +130,12 @@ static const struct reset_props reset_props_sparx5 = { static const struct of_device_id ocelot_reset_of_match[] = { { + .compatible = "mscc,jaguar2-chip-reset", + .data = &reset_props_jaguar2 + }, { + .compatible = "mscc,luton-chip-reset", + .data = &reset_props_luton + }, { .compatible = "mscc,ocelot-chip-reset", .data = &reset_props_ocelot }, { diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c index 52b7dc61d870..0ddf7f25f7b8 100644 --- a/drivers/power/reset/qnap-poweroff.c +++ b/drivers/power/reset/qnap-poweroff.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/serial_reg.h> -#include <linux/kallsyms.h> #include <linux/of.h> #include <linux/io.h> #include <linux/clk.h> @@ -75,7 +74,6 @@ static int qnap_power_off_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct resource *res; struct clk *clk; - char symname[KSYM_NAME_LEN]; const struct of_device_id *match = of_match_node(qnap_power_off_of_match_table, np); @@ -104,10 +102,8 @@ static int qnap_power_off_probe(struct platform_device *pdev) /* Check that nothing else has already setup a handler */ if (pm_power_off) { - lookup_symbol_name((ulong)pm_power_off, symname); - dev_err(&pdev->dev, - "pm_power_off already claimed %p %s", - pm_power_off, symname); + dev_err(&pdev->dev, "pm_power_off already claimed for %ps", + pm_power_off); return -EBUSY; } pm_power_off = qnap_power_off; diff --git a/drivers/power/reset/regulator-poweroff.c b/drivers/power/reset/regulator-poweroff.c new file mode 100644 index 000000000000..f697088e0ad1 --- /dev/null +++ b/drivers/power/reset/regulator-poweroff.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Force-disables a regulator to power down a device + * + * Michael Klein <michael@fossekall.de> + * + * Copyright (C) 2020 Michael Klein + * + * Based on the gpio-poweroff driver. + */ +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> + +#define TIMEOUT_MS 3000 + +/* + * Hold configuration here, cannot be more than one instance of the driver + * since pm_power_off itself is global. + */ +static struct regulator *cpu_regulator; + +static void regulator_poweroff_do_poweroff(void) +{ + if (cpu_regulator && regulator_is_enabled(cpu_regulator)) + regulator_force_disable(cpu_regulator); + + /* give it some time */ + mdelay(TIMEOUT_MS); + + WARN_ON(1); +} + +static int regulator_poweroff_probe(struct platform_device *pdev) +{ + /* If a pm_power_off function has already been added, leave it alone */ + if (pm_power_off != NULL) { + dev_err(&pdev->dev, + "%s: pm_power_off function already registered\n", + __func__); + return -EBUSY; + } + + cpu_regulator = devm_regulator_get(&pdev->dev, "cpu"); + if (IS_ERR(cpu_regulator)) + return PTR_ERR(cpu_regulator); + + pm_power_off = ®ulator_poweroff_do_poweroff; + return 0; +} + +static int regulator_poweroff_remove(__maybe_unused struct platform_device *pdev) +{ + if (pm_power_off == ®ulator_poweroff_do_poweroff) + pm_power_off = NULL; + + return 0; +} + +static const struct of_device_id of_regulator_poweroff_match[] = { + { .compatible = "regulator-poweroff", }, + {}, +}; + +static struct platform_driver regulator_poweroff_driver = { + .probe = regulator_poweroff_probe, + .remove = regulator_poweroff_remove, + .driver = { + .name = "poweroff-regulator", + .of_match_table = of_regulator_poweroff_match, + }, +}; + +module_platform_driver(regulator_poweroff_driver); + +MODULE_AUTHOR("Michael Klein <michael@fossekall.de>"); +MODULE_DESCRIPTION("Regulator poweroff driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:poweroff-regulator"); diff --git a/drivers/power/reset/syscon-poweroff.c b/drivers/power/reset/syscon-poweroff.c index 4d6923b102b6..ed58bdf41e27 100644 --- a/drivers/power/reset/syscon-poweroff.c +++ b/drivers/power/reset/syscon-poweroff.c @@ -6,7 +6,6 @@ * Author: Moritz Fischer <moritz.fischer@ettus.com> */ -#include <linux/kallsyms.h> #include <linux/delay.h> #include <linux/io.h> #include <linux/notifier.h> @@ -34,7 +33,6 @@ static void syscon_poweroff(void) static int syscon_poweroff_probe(struct platform_device *pdev) { - char symname[KSYM_NAME_LEN]; int mask_err, value_err; map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); @@ -65,10 +63,8 @@ static int syscon_poweroff_probe(struct platform_device *pdev) } if (pm_power_off) { - lookup_symbol_name((ulong)pm_power_off, symname); - dev_err(&pdev->dev, - "pm_power_off already claimed %p %s", - pm_power_off, symname); + dev_err(&pdev->dev, "pm_power_off already claimed for %ps", + pm_power_off); return -EBUSY; } |