diff options
Diffstat (limited to 'drivers/power/reset')
41 files changed, 1426 insertions, 841 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a8c46ba5878f..f6c1bcbb57de 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -26,7 +26,7 @@ config POWER_RESET_AT91_POWEROFF config POWER_RESET_AT91_RESET tristate "Atmel AT91 reset driver" depends on ARCH_AT91 - default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAMA5 + default SOC_AT91SAM9 || SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 help This driver supports restart for Atmel AT91SAM9 and SAMA5 SoCs @@ -34,7 +34,7 @@ config POWER_RESET_AT91_RESET config POWER_RESET_AT91_SAMA5D2_SHDWC tristate "Atmel AT91 SAMA5D2-Compatible shutdown controller driver" depends on ARCH_AT91 - default SOC_SAM9X60 || SOC_SAMA5 + default SOC_SAM9X60 || SOC_SAM9X7 || SOC_SAMA5 help This driver supports the alternate shutdown controller for some Atmel SAMA5 SoCs. It is present for example on SAMA5D2 SoC. @@ -66,7 +66,7 @@ config POWER_RESET_BRCMKONA config POWER_RESET_BRCMSTB bool "Broadcom STB reset driver" - depends on ARM || ARM64 || MIPS || COMPILE_TEST + depends on ARCH_BRCMSTB || BMIPS_GENERIC || COMPILE_TEST depends on MFD_SYSCON default ARCH_BRCMSTB || BMIPS_GENERIC help @@ -75,6 +75,17 @@ config POWER_RESET_BRCMSTB Say Y here if you have a Broadcom STB board and you wish to have restart support. +config POWER_RESET_EP93XX + bool "Cirrus EP93XX reset driver" if COMPILE_TEST + depends on MFD_SYSCON + default ARCH_EP93XX + select AUXILIARY_BUS + help + This driver provides restart support for Cirrus EP93XX SoC. + + Say Y here if you have a Cirrus EP93XX SoC and you wish + to have restart support. + config POWER_RESET_GEMINI_POWEROFF bool "Cortina Gemini power-off driver" depends on ARCH_GEMINI || COMPILE_TEST @@ -117,6 +128,15 @@ config POWER_RESET_LINKSTATION Say Y here if you have a Buffalo LinkStation LS421D/E. +config POWER_RESET_MACSMC + tristate "Apple SMC reset/power-off driver" + depends on MFD_MACSMC + help + This driver supports reset and power-off on Apple Mac machines + that implement this functionality via the SMC. + + Say Y here if you have an Apple Silicon Mac. + config POWER_RESET_MSM bool "Qualcomm MSM power-off driver" depends on ARCH_QCOM @@ -141,16 +161,17 @@ config POWER_RESET_OCELOT_RESET help This driver supports restart for Microsemi Ocelot SoC and similar. -config POWER_RESET_OXNAS - bool "OXNAS SoC restart driver" - depends on ARCH_OXNAS - default MACH_OX820 +config POWER_RESET_ODROID_GO_ULTRA_POWEROFF + bool "Odroid Go Ultra power-off driver" + depends on ARCH_MESON || COMPILE_TEST + depends on I2C=y && OF help - Restart support for OXNAS/PLXTECH OX820 SoC. + This driver supports Power off for Odroid Go Ultra device. config POWER_RESET_PIIX4_POWEROFF tristate "Intel PIIX4 power-off driver" depends on PCI + depends on HAS_IOPORT depends on MIPS || COMPILE_TEST help This driver supports powering off a system using the Intel PIIX4 @@ -204,6 +225,27 @@ config POWER_RESET_ST help Reset support for STMicroelectronics boards. +config POWER_RESET_TH1520_AON + tristate "T-Head TH1520 AON firmware poweroff and reset driver" + depends on TH1520_PM_DOMAINS + help + This driver supports power-off and reset operations for T-Head + TH1520 SoCs running the AON firmware. + +config POWER_RESET_TORADEX_EC + tristate "Toradex Embedded Controller power-off and reset driver" + depends on ARCH_MXC || COMPILE_TEST + depends on I2C + select REGMAP_I2C + help + This driver supports power-off and reset for SMARC Toradex SoMs, + for example the SMARC iMX8MP and SMARC iMX95, using Toradex + Embedded Controller (EC). + + Say Y here if you have a Toradex SMARC SoM. + + If unsure, say N. + config POWER_RESET_TPS65086 bool "TPS65086 restart driver" depends on MFD_TPS65086 @@ -241,6 +283,15 @@ config POWER_RESET_KEYSTONE help Reboot support for the KEYSTONE SoCs. +config POWER_RESET_SPACEMIT_P1 + tristate "SpacemiT P1 poweroff and reset driver" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on MFD_SPACEMIT_P1 + default MFD_SPACEMIT_P1 + help + This driver supports power-off and reset operations for the SpacemiT + P1 PMIC. + config POWER_RESET_SYSCON bool "Generic SYSCON regmap reset driver" depends on OF @@ -299,7 +350,7 @@ config NVMEM_REBOOT_MODE config POWER_MLXBF tristate "Mellanox BlueField power handling driver" - depends on (GPIO_MLXBF2 && ACPI) + depends on (GPIO_MLXBF2 || GPIO_MLXBF3) && ACPI help This driver supports reset or low power mode handling for Mellanox BlueField. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 0a39424fc558..0e4ae6f6b5c5 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -7,22 +7,27 @@ obj-$(CONFIG_POWER_RESET_ATC260X) += atc260x-poweroff.o obj-$(CONFIG_POWER_RESET_AXXIA) += axxia-reset.o obj-$(CONFIG_POWER_RESET_BRCMKONA) += brcm-kona-reset.o obj-$(CONFIG_POWER_RESET_BRCMSTB) += brcmstb-reboot.o +obj-$(CONFIG_POWER_RESET_EP93XX) += ep93xx-restart.o obj-$(CONFIG_POWER_RESET_GEMINI_POWEROFF) += gemini-poweroff.o 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_LINKSTATION) += linkstation-poweroff.o +obj-$(CONFIG_POWER_RESET_MACSMC) += macsmc-reboot.o obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o -obj-$(CONFIG_POWER_RESET_OXNAS) += oxnas-restart.o obj-$(CONFIG_POWER_RESET_QCOM_PON) += qcom-pon.o obj-$(CONFIG_POWER_RESET_OCELOT_RESET) += ocelot-reset.o +obj-$(CONFIG_POWER_RESET_ODROID_GO_ULTRA_POWEROFF) += odroid-go-ultra-poweroff.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_SPACEMIT_P1) += spacemit-p1-reboot.o obj-$(CONFIG_POWER_RESET_ST) += st-poweroff.o +obj-$(CONFIG_POWER_RESET_TH1520_AON) += th1520-aon-reboot.o +obj-$(CONFIG_POWER_RESET_TORADEX_EC) += tdx-ec-poweroff.o obj-$(CONFIG_POWER_RESET_TPS65086) += tps65086-restart.o obj-$(CONFIG_POWER_RESET_VERSATILE) += arm-versatile-reboot.o obj-$(CONFIG_POWER_RESET_VEXPRESS) += vexpress-poweroff.o diff --git a/drivers/power/reset/as3722-poweroff.c b/drivers/power/reset/as3722-poweroff.c index 661e1c67f82e..8075382cbc36 100644 --- a/drivers/power/reset/as3722-poweroff.c +++ b/drivers/power/reset/as3722-poweroff.c @@ -10,8 +10,8 @@ #include <linux/mfd/as3722.h> #include <linux/module.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/reboot.h> #include <linux/slab.h> struct as3722_poweroff { @@ -19,22 +19,18 @@ struct as3722_poweroff { struct as3722 *as3722; }; -static struct as3722_poweroff *as3722_pm_poweroff; - -static void as3722_pm_power_off(void) +static int as3722_pm_power_off(struct sys_off_data *data) { + struct as3722_poweroff *as3722_pm_poweroff = data->cb_data; int ret; - if (!as3722_pm_poweroff) { - pr_err("AS3722 poweroff is not initialised\n"); - return; - } - ret = as3722_update_bits(as3722_pm_poweroff->as3722, AS3722_RESET_CONTROL_REG, AS3722_POWER_OFF, AS3722_POWER_OFF); if (ret < 0) dev_err(as3722_pm_poweroff->dev, "RESET_CONTROL_REG update failed, %d\n", ret); + + return NOTIFY_DONE; } static int as3722_poweroff_probe(struct platform_device *pdev) @@ -55,20 +51,12 @@ static int as3722_poweroff_probe(struct platform_device *pdev) as3722_poweroff->as3722 = dev_get_drvdata(pdev->dev.parent); as3722_poweroff->dev = &pdev->dev; - as3722_pm_poweroff = as3722_poweroff; - if (!pm_power_off) - pm_power_off = as3722_pm_power_off; - - return 0; -} - -static int as3722_poweroff_remove(struct platform_device *pdev) -{ - if (pm_power_off == as3722_pm_power_off) - pm_power_off = NULL; - as3722_pm_poweroff = NULL; - return 0; + return devm_register_sys_off_handler(as3722_poweroff->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + as3722_pm_power_off, + as3722_poweroff); } static struct platform_driver as3722_poweroff_driver = { @@ -76,7 +64,6 @@ static struct platform_driver as3722_poweroff_driver = { .name = "as3722-power-off", }, .probe = as3722_poweroff_probe, - .remove = as3722_poweroff_remove, }; module_platform_driver(as3722_poweroff_driver); @@ -84,4 +71,3 @@ module_platform_driver(as3722_poweroff_driver); MODULE_DESCRIPTION("Power off driver for ams AS3722 PMIC Device"); MODULE_ALIAS("platform:as3722-power-off"); MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/at91-poweroff.c b/drivers/power/reset/at91-poweroff.c index 9e74e131c675..7bc779055cf3 100644 --- a/drivers/power/reset/at91-poweroff.c +++ b/drivers/power/reset/at91-poweroff.c @@ -57,7 +57,7 @@ static struct shdwc { void __iomem *mpddrc_base; } at91_shdwc; -static void __init at91_wakeup_status(struct platform_device *pdev) +static void at91_wakeup_status(struct platform_device *pdev) { const char *reason; u32 reg = readl(at91_shdwc.shdwc_base + AT91_SHDW_SR); @@ -149,15 +149,13 @@ static void at91_poweroff_dt_set_wakeup_mode(struct platform_device *pdev) writel(wakeup_mode | mode, at91_shdwc.shdwc_base + AT91_SHDW_MR); } -static int __init at91_poweroff_probe(struct platform_device *pdev) +static int at91_poweroff_probe(struct platform_device *pdev) { - struct resource *res; struct device_node *np; u32 ddr_type; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - at91_shdwc.shdwc_base = devm_ioremap_resource(&pdev->dev, res); + at91_shdwc.shdwc_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(at91_shdwc.shdwc_base)) return PTR_ERR(at91_shdwc.shdwc_base); @@ -204,7 +202,7 @@ clk_disable: return ret; } -static int __exit at91_poweroff_remove(struct platform_device *pdev) +static void at91_poweroff_remove(struct platform_device *pdev) { if (pm_power_off == at91_poweroff) pm_power_off = NULL; @@ -213,8 +211,6 @@ static int __exit at91_poweroff_remove(struct platform_device *pdev) iounmap(at91_shdwc.mpddrc_base); clk_disable_unprepare(at91_shdwc.sclk); - - return 0; } static const struct of_device_id at91_poweroff_of_match[] = { @@ -226,13 +222,14 @@ static const struct of_device_id at91_poweroff_of_match[] = { MODULE_DEVICE_TABLE(of, at91_poweroff_of_match); static struct platform_driver at91_poweroff_driver = { - .remove = __exit_p(at91_poweroff_remove), + .probe = at91_poweroff_probe, + .remove = at91_poweroff_remove, .driver = { .name = "at91-poweroff", .of_match_table = at91_poweroff_of_match, }, }; -module_platform_driver_probe(at91_poweroff_driver, at91_poweroff_probe); +module_platform_driver(at91_poweroff_driver); MODULE_AUTHOR("Atmel Corporation"); MODULE_DESCRIPTION("Shutdown driver for Atmel SoCs"); diff --git a/drivers/power/reset/at91-reset.c b/drivers/power/reset/at91-reset.c index 741e44a017c3..511f5a8f8961 100644 --- a/drivers/power/reset/at91-reset.c +++ b/drivers/power/reset/at91-reset.c @@ -18,6 +18,7 @@ #include <linux/platform_device.h> #include <linux/reboot.h> #include <linux/reset-controller.h> +#include <linux/power/power_on_reason.h> #include <soc/at91/at91sam9_ddrsdr.h> #include <soc/at91/at91sam9_sdramc.h> @@ -128,12 +129,11 @@ static int at91_reset(struct notifier_block *this, unsigned long mode, " str %4, [%0, %6]\n\t" /* Disable SDRAM1 accesses */ "1: tst %1, #0\n\t" - " beq 2f\n\t" " strne %3, [%1, #" __stringify(AT91_DDRSDRC_RTR) "]\n\t" /* Power down SDRAM1 */ " strne %4, [%1, %6]\n\t" /* Reset CPU */ - "2: str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" + " str %5, [%2, #" __stringify(AT91_RSTC_CR) "]\n\t" " b .\n\t" : @@ -144,50 +144,59 @@ static int at91_reset(struct notifier_block *this, unsigned long mode, "r" cpu_to_le32(AT91_DDRSDRC_LPCB_POWER_DOWN), "r" (reset->data->reset_args), "r" (reset->ramc_lpr) - : "r4"); + ); return NOTIFY_DONE; } -static void __init at91_reset_status(struct platform_device *pdev, - void __iomem *base) +static const char *at91_reset_reason(struct at91_reset *reset) { + u32 reg = readl(reset->rstc_base + AT91_RSTC_SR); const char *reason; - u32 reg = readl(base + AT91_RSTC_SR); switch ((reg & AT91_RSTC_RSTTYP) >> 8) { case RESET_TYPE_GENERAL: - reason = "general reset"; + reason = POWER_ON_REASON_REGULAR; break; case RESET_TYPE_WAKEUP: - reason = "wakeup"; + reason = POWER_ON_REASON_RTC; break; case RESET_TYPE_WATCHDOG: - reason = "watchdog reset"; + reason = POWER_ON_REASON_WATCHDOG; break; case RESET_TYPE_SOFTWARE: - reason = "software reset"; + reason = POWER_ON_REASON_SOFTWARE; break; case RESET_TYPE_USER: - reason = "user reset"; + reason = POWER_ON_REASON_RST_BTN; break; case RESET_TYPE_CPU_FAIL: - reason = "CPU clock failure detection"; + reason = POWER_ON_REASON_CPU_CLK_FAIL; break; case RESET_TYPE_XTAL_FAIL: - reason = "32.768 kHz crystal failure detection"; + reason = POWER_ON_REASON_XTAL_FAIL; break; case RESET_TYPE_ULP2: - reason = "ULP2 reset"; + reason = POWER_ON_REASON_BROWN_OUT; break; default: - reason = "unknown reset"; + reason = POWER_ON_REASON_UNKNOWN; break; } - dev_info(&pdev->dev, "Starting after %s\n", reason); + return reason; } +static ssize_t power_on_reason_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct platform_device *pdev = to_platform_device(dev); + struct at91_reset *reset = platform_get_drvdata(pdev); + + return sprintf(buf, "%s\n", at91_reset_reason(reset)); +} +static DEVICE_ATTR_RO(power_on_reason); + static const struct of_device_id at91_ramc_of_match[] = { { .compatible = "atmel,at91sam9260-sdramc", @@ -327,7 +336,7 @@ static int at91_rcdev_init(struct at91_reset *reset, return devm_reset_controller_register(&pdev->dev, &reset->rcdev); } -static int __init at91_reset_probe(struct platform_device *pdev) +static int at91_reset_probe(struct platform_device *pdev) { const struct of_device_id *match; struct at91_reset *reset; @@ -392,7 +401,13 @@ static int __init at91_reset_probe(struct platform_device *pdev) if (ret) goto disable_clk; - at91_reset_status(pdev, reset->rstc_base); + ret = device_create_file(&pdev->dev, &dev_attr_power_on_reason); + if (ret) { + dev_err(&pdev->dev, "Could not create sysfs entry\n"); + return ret; + } + + dev_info(&pdev->dev, "Starting after %s\n", at91_reset_reason(reset)); return 0; @@ -401,24 +416,23 @@ disable_clk: return ret; } -static int __exit at91_reset_remove(struct platform_device *pdev) +static void at91_reset_remove(struct platform_device *pdev) { struct at91_reset *reset = platform_get_drvdata(pdev); unregister_restart_handler(&reset->nb); clk_disable_unprepare(reset->sclk); - - return 0; } static struct platform_driver at91_reset_driver = { - .remove = __exit_p(at91_reset_remove), + .probe = at91_reset_probe, + .remove = at91_reset_remove, .driver = { .name = "at91-reset", .of_match_table = at91_reset_of_match, }, }; -module_platform_driver_probe(at91_reset_driver, at91_reset_probe); +module_platform_driver(at91_reset_driver); MODULE_AUTHOR("Atmel Corporation"); MODULE_DESCRIPTION("Reset driver for Atmel SoCs"); diff --git a/drivers/power/reset/at91-sama5d2_shdwc.c b/drivers/power/reset/at91-sama5d2_shdwc.c index d8ecffe72f16..ecf15694f925 100644 --- a/drivers/power/reset/at91-sama5d2_shdwc.c +++ b/drivers/power/reset/at91-sama5d2_shdwc.c @@ -107,7 +107,7 @@ static const unsigned long long sdwc_dbc_period[] = { 0, 3, 32, 512, 4096, 32768, }; -static void __init at91_wakeup_status(struct platform_device *pdev) +static void at91_wakeup_status(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); const struct reg_config *rcfg = shdw->rcfg; @@ -129,7 +129,7 @@ static void __init at91_wakeup_status(struct platform_device *pdev) else if (SHDW_RTTWK(reg, &rcfg->shdwc)) reason = "RTT"; - pr_info("AT91: Wake-Up source: %s\n", reason); + dev_info(&pdev->dev, "Wake-Up source: %s\n", reason); } static void at91_poweroff(void) @@ -326,12 +326,13 @@ static const struct of_device_id at91_pmc_ids[] = { { .compatible = "atmel,sama5d2-pmc" }, { .compatible = "microchip,sam9x60-pmc" }, { .compatible = "microchip,sama7g5-pmc" }, + { .compatible = "microchip,sam9x7-pmc" }, + { .compatible = "microchip,sama7d65-pmc" }, { /* Sentinel. */ } }; -static int __init at91_shdwc_probe(struct platform_device *pdev) +static int at91_shdwc_probe(struct platform_device *pdev) { - struct resource *res; const struct of_device_id *match; struct device_node *np; u32 ddr_type; @@ -349,8 +350,7 @@ static int __init at91_shdwc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, at91_shdwc); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - at91_shdwc->shdwc_base = devm_ioremap_resource(&pdev->dev, res); + at91_shdwc->shdwc_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(at91_shdwc->shdwc_base)) return PTR_ERR(at91_shdwc->shdwc_base); @@ -423,7 +423,7 @@ clk_disable: return ret; } -static int __exit at91_shdwc_remove(struct platform_device *pdev) +static void at91_shdwc_remove(struct platform_device *pdev) { struct shdwc *shdw = platform_get_drvdata(pdev); @@ -439,18 +439,17 @@ static int __exit at91_shdwc_remove(struct platform_device *pdev) iounmap(shdw->pmc_base); clk_disable_unprepare(shdw->sclk); - - return 0; } static struct platform_driver at91_shdwc_driver = { - .remove = __exit_p(at91_shdwc_remove), + .probe = at91_shdwc_probe, + .remove = at91_shdwc_remove, .driver = { .name = "at91-shdwc", .of_match_table = at91_shdwc_of_match, }, }; -module_platform_driver_probe(at91_shdwc_driver, at91_shdwc_probe); +module_platform_driver(at91_shdwc_driver); MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>"); MODULE_DESCRIPTION("Atmel shutdown controller driver"); diff --git a/drivers/power/reset/atc260x-poweroff.c b/drivers/power/reset/atc260x-poweroff.c index 98f20251a6d1..e3e4621ccb1d 100644 --- a/drivers/power/reset/atc260x-poweroff.c +++ b/drivers/power/reset/atc260x-poweroff.c @@ -16,13 +16,9 @@ struct atc260x_pwrc { struct device *dev; struct regmap *regmap; - struct notifier_block restart_nb; int (*do_poweroff)(const struct atc260x_pwrc *pwrc, bool restart); }; -/* Global variable needed only for pm_power_off */ -static struct atc260x_pwrc *atc260x_pwrc_data; - static int atc2603c_do_poweroff(const struct atc260x_pwrc *pwrc, bool restart) { int ret, deep_sleep = 0; @@ -165,18 +161,20 @@ static int atc2609a_init(const struct atc260x_pwrc *pwrc) return ret; } -static void atc260x_pwrc_pm_handler(void) +static int atc260x_pwrc_pm_handler(struct sys_off_data *data) { - atc260x_pwrc_data->do_poweroff(atc260x_pwrc_data, false); + struct atc260x_pwrc *pwrc = data->cb_data; + + pwrc->do_poweroff(pwrc, false); WARN_ONCE(1, "Unable to power off system\n"); + + return NOTIFY_DONE; } -static int atc260x_pwrc_restart_handler(struct notifier_block *nb, - unsigned long mode, void *cmd) +static int atc260x_pwrc_restart_handler(struct sys_off_data *data) { - struct atc260x_pwrc *pwrc = container_of(nb, struct atc260x_pwrc, - restart_nb); + struct atc260x_pwrc *pwrc = data->cb_data; pwrc->do_poweroff(pwrc, true); return NOTIFY_DONE; @@ -194,8 +192,6 @@ static int atc260x_pwrc_probe(struct platform_device *pdev) priv->dev = &pdev->dev; priv->regmap = atc260x->regmap; - priv->restart_nb.notifier_call = atc260x_pwrc_restart_handler; - priv->restart_nb.priority = 192; switch (atc260x->ic_type) { case ATC2603C: @@ -216,16 +212,20 @@ static int atc260x_pwrc_probe(struct platform_device *pdev) if (ret) return ret; - platform_set_drvdata(pdev, priv); - - if (!pm_power_off) { - atc260x_pwrc_data = priv; - pm_power_off = atc260x_pwrc_pm_handler; - } else { - dev_warn(priv->dev, "Poweroff callback already assigned\n"); - } + ret = devm_register_sys_off_handler(priv->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + atc260x_pwrc_pm_handler, + priv); + if (ret) + dev_err(priv->dev, "failed to register power-off handler: %d\n", + ret); - ret = register_restart_handler(&priv->restart_nb); + ret = devm_register_sys_off_handler(priv->dev, + SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_HIGH, + atc260x_pwrc_restart_handler, + priv); if (ret) dev_err(priv->dev, "failed to register restart handler: %d\n", ret); @@ -233,23 +233,8 @@ static int atc260x_pwrc_probe(struct platform_device *pdev) return ret; } -static int atc260x_pwrc_remove(struct platform_device *pdev) -{ - struct atc260x_pwrc *priv = platform_get_drvdata(pdev); - - if (atc260x_pwrc_data == priv) { - pm_power_off = NULL; - atc260x_pwrc_data = NULL; - } - - unregister_restart_handler(&priv->restart_nb); - - return 0; -} - static struct platform_driver atc260x_pwrc_driver = { .probe = atc260x_pwrc_probe, - .remove = atc260x_pwrc_remove, .driver = { .name = "atc260x-pwrc", }, diff --git a/drivers/power/reset/axxia-reset.c b/drivers/power/reset/axxia-reset.c index f7b40be5d6b6..797bf6773860 100644 --- a/drivers/power/reset/axxia-reset.c +++ b/drivers/power/reset/axxia-reset.c @@ -26,11 +26,10 @@ #define SC_EFUSE_INT_STATUS 0x180c #define EFUSE_READ_DONE (1<<31) -static struct regmap *syscon; - -static int axxia_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int axxia_restart_handler(struct sys_off_data *data) { + struct regmap *syscon = data->cb_data; + /* Access Key (0xab) */ regmap_write(syscon, SC_CRIT_WRITE_KEY, 0xab); /* Select internal boot from 0xffff0000 */ @@ -44,14 +43,10 @@ static int axxia_restart_handler(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block axxia_restart_nb = { - .notifier_call = axxia_restart_handler, - .priority = 128, -}; - static int axxia_reset_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct regmap *syscon; int err; syscon = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon"); @@ -60,7 +55,8 @@ static int axxia_reset_probe(struct platform_device *pdev) return PTR_ERR(syscon); } - err = register_restart_handler(&axxia_restart_nb); + err = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, + 128, axxia_restart_handler, syscon); if (err) dev_err(dev, "cannot register restart handler (err=%d)\n", err); @@ -80,9 +76,4 @@ static struct platform_driver axxia_reset_driver = { .of_match_table = of_match_ptr(of_axxia_reset_match), }, }; - -static int __init axxia_reset_init(void) -{ - return platform_driver_register(&axxia_reset_driver); -} -device_initcall(axxia_reset_init); +builtin_platform_driver(axxia_reset_driver); diff --git a/drivers/power/reset/brcm-kona-reset.c b/drivers/power/reset/brcm-kona-reset.c index 3de024e3ceb7..ee3f1bb97653 100644 --- a/drivers/power/reset/brcm-kona-reset.c +++ b/drivers/power/reset/brcm-kona-reset.c @@ -2,8 +2,8 @@ // Copyright (C) 2016 Broadcom #include <linux/io.h> -#include <linux/of_address.h> -#include <linux/of_platform.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> #include <linux/reboot.h> #define RSTMGR_REG_WR_ACCESS_OFFSET 0 @@ -15,8 +15,7 @@ static void __iomem *kona_reset_base; -static int kona_reset_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int kona_reset_handler(struct sys_off_data *data) { /* * A soft reset is triggered by writing a 0 to bit 0 of the soft reset @@ -31,20 +30,14 @@ static int kona_reset_handler(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block kona_reset_nb = { - .notifier_call = kona_reset_handler, - .priority = 128, -}; - static int kona_reset_probe(struct platform_device *pdev) { - struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - kona_reset_base = devm_ioremap_resource(&pdev->dev, res); + kona_reset_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(kona_reset_base)) return PTR_ERR(kona_reset_base); - return register_restart_handler(&kona_reset_nb); + return devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, + 128, kona_reset_handler, NULL); } static const struct of_device_id of_match[] = { diff --git a/drivers/power/reset/brcmstb-reboot.c b/drivers/power/reset/brcmstb-reboot.c index 0f2944dc9355..b9c093f6064c 100644 --- a/drivers/power/reset/brcmstb-reboot.c +++ b/drivers/power/reset/brcmstb-reboot.c @@ -18,9 +18,6 @@ #include <linux/smp.h> #include <linux/mfd/syscon.h> -#define RESET_SOURCE_ENABLE_REG 1 -#define SW_MASTER_RESET_REG 2 - static struct regmap *regmap; static u32 rst_src_en; static u32 sw_mstr_rst; @@ -32,8 +29,7 @@ struct reset_reg_mask { static const struct reset_reg_mask *reset_masks; -static int brcmstb_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int brcmstb_restart_handler(struct sys_off_data *data) { int rc; u32 tmp; @@ -62,17 +58,9 @@ static int brcmstb_restart_handler(struct notifier_block *this, return NOTIFY_DONE; } - while (1) - ; - return NOTIFY_DONE; } -static struct notifier_block brcmstb_restart_nb = { - .notifier_call = brcmstb_restart_handler, - .priority = 128, -}; - static const struct reset_reg_mask reset_bits_40nm = { .rst_src_en_mask = BIT(0), .sw_mstr_rst_mask = BIT(0), @@ -83,46 +71,28 @@ static const struct reset_reg_mask reset_bits_65nm = { .sw_mstr_rst_mask = BIT(31), }; -static const struct of_device_id of_match[] = { - { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm }, - { .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm }, - {}, -}; - static int brcmstb_reboot_probe(struct platform_device *pdev) { int rc; struct device_node *np = pdev->dev.of_node; - const struct of_device_id *of_id; + unsigned int args[2]; - of_id = of_match_node(of_match, np); - if (!of_id) { - pr_err("failed to look up compatible string\n"); + reset_masks = device_get_match_data(&pdev->dev); + if (!reset_masks) { + pr_err("failed to get match data\n"); return -EINVAL; } - reset_masks = of_id->data; - regmap = syscon_regmap_lookup_by_phandle(np, "syscon"); + regmap = syscon_regmap_lookup_by_phandle_args(np, "syscon", ARRAY_SIZE(args), args); if (IS_ERR(regmap)) { pr_err("failed to get syscon phandle\n"); return -EINVAL; } + rst_src_en = args[0]; + sw_mstr_rst = args[1]; - rc = of_property_read_u32_index(np, "syscon", RESET_SOURCE_ENABLE_REG, - &rst_src_en); - if (rc) { - pr_err("can't get rst_src_en offset (%d)\n", rc); - return -EINVAL; - } - - rc = of_property_read_u32_index(np, "syscon", SW_MASTER_RESET_REG, - &sw_mstr_rst); - if (rc) { - pr_err("can't get sw_mstr_rst offset (%d)\n", rc); - return -EINVAL; - } - - rc = register_restart_handler(&brcmstb_restart_nb); + rc = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, + 128, brcmstb_restart_handler, NULL); if (rc) dev_err(&pdev->dev, "cannot register restart handler (err=%d)\n", rc); @@ -130,6 +100,12 @@ static int brcmstb_reboot_probe(struct platform_device *pdev) return rc; } +static const struct of_device_id of_match[] = { + { .compatible = "brcm,brcmstb-reboot", .data = &reset_bits_40nm }, + { .compatible = "brcm,bcm7038-reboot", .data = &reset_bits_65nm }, + {}, +}; + static struct platform_driver brcmstb_reboot_driver = { .probe = brcmstb_reboot_probe, .driver = { @@ -140,7 +116,6 @@ static struct platform_driver brcmstb_reboot_driver = { static int __init brcmstb_reboot_init(void) { - return platform_driver_probe(&brcmstb_reboot_driver, - brcmstb_reboot_probe); + return platform_driver_register(&brcmstb_reboot_driver); } subsys_initcall(brcmstb_reboot_init); diff --git a/drivers/power/reset/ep93xx-restart.c b/drivers/power/reset/ep93xx-restart.c new file mode 100644 index 000000000000..57cfb8620faf --- /dev/null +++ b/drivers/power/reset/ep93xx-restart.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Cirrus EP93xx SoC reset driver + * + * Copyright (C) 2021 Nikita Shubin <nikita.shubin@maquefel.me> + */ + +#include <linux/bits.h> +#include <linux/container_of.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/slab.h> + +#include <linux/soc/cirrus/ep93xx.h> + +#define EP93XX_SYSCON_DEVCFG 0x80 +#define EP93XX_SYSCON_DEVCFG_SWRST BIT(31) + +struct ep93xx_restart { + struct ep93xx_regmap_adev *aux_dev; + struct notifier_block restart_handler; +}; + +static int ep93xx_restart_handle(struct notifier_block *this, + unsigned long mode, void *cmd) +{ + struct ep93xx_restart *priv = + container_of(this, struct ep93xx_restart, restart_handler); + struct ep93xx_regmap_adev *aux = priv->aux_dev; + + /* Issue the reboot */ + aux->update_bits(aux->map, aux->lock, EP93XX_SYSCON_DEVCFG, + EP93XX_SYSCON_DEVCFG_SWRST, EP93XX_SYSCON_DEVCFG_SWRST); + aux->update_bits(aux->map, aux->lock, EP93XX_SYSCON_DEVCFG, + EP93XX_SYSCON_DEVCFG_SWRST, 0); + + return NOTIFY_DONE; +} + +static int ep93xx_reboot_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct ep93xx_regmap_adev *rdev = to_ep93xx_regmap_adev(adev); + struct device *dev = &adev->dev; + struct ep93xx_restart *priv; + int err; + + if (!rdev->update_bits) + return -ENODEV; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->aux_dev = rdev; + + priv->restart_handler.notifier_call = ep93xx_restart_handle; + priv->restart_handler.priority = 128; + + err = register_restart_handler(&priv->restart_handler); + if (err) + return dev_err_probe(dev, err, "can't register restart notifier\n"); + + return 0; +} + +static const struct auxiliary_device_id ep93xx_reboot_ids[] = { + { + .name = "soc_ep93xx.reset-ep93xx", + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(auxiliary, ep93xx_reboot_ids); + +static struct auxiliary_driver ep93xx_reboot_driver = { + .probe = ep93xx_reboot_probe, + .id_table = ep93xx_reboot_ids, +}; +module_auxiliary_driver(ep93xx_reboot_driver); diff --git a/drivers/power/reset/gemini-poweroff.c b/drivers/power/reset/gemini-poweroff.c index b7f7a8225f22..06d6992dec89 100644 --- a/drivers/power/reset/gemini-poweroff.c +++ b/drivers/power/reset/gemini-poweroff.c @@ -70,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"); @@ -86,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; @@ -101,8 +99,7 @@ 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); @@ -150,8 +147,11 @@ static int gemini_poweroff_probe(struct platform_device *pdev) if (ret) return ret; - pm_power_off = gemini_poweroff; - gpw_poweroff = gpw; + 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"); diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c index 1c5af2fef142..3eaae352ffb9 100644 --- a/drivers/power/reset/gpio-poweroff.c +++ b/drivers/power/reset/gpio-poweroff.c @@ -11,53 +11,61 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/gpio/consumer.h> -#include <linux/of_platform.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> +#include <linux/reboot.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 u32 active_delay = 100; -static u32 inactive_delay = 100; -static void gpio_poweroff_do_poweroff(void) +struct gpio_poweroff { + struct gpio_desc *reset_gpio; + u32 timeout_ms; + u32 active_delay_ms; + u32 inactive_delay_ms; +}; + +static int gpio_poweroff_do_poweroff(struct sys_off_data *data) { - BUG_ON(!reset_gpio); + struct gpio_poweroff *gpio_poweroff = data->cb_data; /* drive it active, also inactive->active edge */ - gpiod_direction_output(reset_gpio, 1); - mdelay(active_delay); + gpiod_direction_output(gpio_poweroff->reset_gpio, 1); + mdelay(gpio_poweroff->active_delay_ms); /* drive inactive, also active->inactive edge */ - gpiod_set_value_cansleep(reset_gpio, 0); - mdelay(inactive_delay); + gpiod_set_value_cansleep(gpio_poweroff->reset_gpio, 0); + mdelay(gpio_poweroff->inactive_delay_ms); /* drive it active, also inactive->active edge */ - gpiod_set_value_cansleep(reset_gpio, 1); + gpiod_set_value_cansleep(gpio_poweroff->reset_gpio, 1); /* give it some time */ - mdelay(timeout); + mdelay(gpio_poweroff->timeout_ms); - WARN_ON(1); + /* + * If code reaches this point, it means that gpio-poweroff has failed + * to actually power off the system. + * Warn the user that the attempt to poweroff via gpio-poweroff + * has gone wrong. + */ + WARN(1, "Failed to poweroff via gpio-poweroff mechanism\n"); + + return NOTIFY_DONE; } static int gpio_poweroff_probe(struct platform_device *pdev) { + struct gpio_poweroff *gpio_poweroff; bool input = false; enum gpiod_flags flags; + int priority = SYS_OFF_PRIO_DEFAULT; + int ret; - /* 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; - } + gpio_poweroff = devm_kzalloc(&pdev->dev, sizeof(*gpio_poweroff), GFP_KERNEL); + if (!gpio_poweroff) + return -ENOMEM; input = device_property_read_bool(&pdev->dev, "input"); if (input) @@ -65,23 +73,29 @@ static int gpio_poweroff_probe(struct platform_device *pdev) else flags = GPIOD_OUT_LOW; - device_property_read_u32(&pdev->dev, "active-delay-ms", &active_delay); - device_property_read_u32(&pdev->dev, "inactive-delay-ms", - &inactive_delay); - 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); + gpio_poweroff->active_delay_ms = 100; + gpio_poweroff->inactive_delay_ms = 100; + gpio_poweroff->timeout_ms = DEFAULT_TIMEOUT_MS; - pm_power_off = &gpio_poweroff_do_poweroff; - return 0; -} + device_property_read_u32(&pdev->dev, "active-delay-ms", &gpio_poweroff->active_delay_ms); + device_property_read_u32(&pdev->dev, "inactive-delay-ms", + &gpio_poweroff->inactive_delay_ms); + device_property_read_u32(&pdev->dev, "timeout-ms", &gpio_poweroff->timeout_ms); + device_property_read_u32(&pdev->dev, "priority", &priority); + if (priority > 255) { + dev_err(&pdev->dev, "Invalid priority property: %u\n", priority); + return -EINVAL; + } -static int gpio_poweroff_remove(struct platform_device *pdev) -{ - if (pm_power_off == &gpio_poweroff_do_poweroff) - pm_power_off = NULL; + gpio_poweroff->reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags); + if (IS_ERR(gpio_poweroff->reset_gpio)) + return PTR_ERR(gpio_poweroff->reset_gpio); + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, + priority, gpio_poweroff_do_poweroff, gpio_poweroff); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Cannot register poweroff handler\n"); return 0; } @@ -94,7 +108,6 @@ MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match); static struct platform_driver gpio_poweroff_driver = { .probe = gpio_poweroff_probe, - .remove = gpio_poweroff_remove, .driver = { .name = "poweroff-gpio", .of_match_table = of_gpio_poweroff_match, @@ -105,5 +118,4 @@ module_platform_driver(gpio_poweroff_driver); MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>"); MODULE_DESCRIPTION("GPIO poweroff driver"); -MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:poweroff-gpio"); diff --git a/drivers/power/reset/gpio-restart.c b/drivers/power/reset/gpio-restart.c index 5466eeea261c..d1e177176fa1 100644 --- a/drivers/power/reset/gpio-restart.c +++ b/drivers/power/reset/gpio-restart.c @@ -12,22 +12,19 @@ #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/gpio/consumer.h> -#include <linux/of_platform.h> #include <linux/module.h> +#include <linux/of.h> struct gpio_restart { struct gpio_desc *reset_gpio; - struct notifier_block restart_handler; u32 active_delay_ms; u32 inactive_delay_ms; u32 wait_delay_ms; }; -static int gpio_restart_notify(struct notifier_block *this, - unsigned long mode, void *cmd) +static int gpio_restart_notify(struct sys_off_data *data) { - struct gpio_restart *gpio_restart = - container_of(this, struct gpio_restart, restart_handler); + struct gpio_restart *gpio_restart = data->cb_data; /* drive it active, also inactive->active edge */ gpiod_direction_output(gpio_restart->reset_gpio, 1); @@ -52,6 +49,7 @@ static int gpio_restart_probe(struct platform_device *pdev) { struct gpio_restart *gpio_restart; bool open_source = false; + int priority = 129; u32 property; int ret; @@ -71,8 +69,6 @@ static int gpio_restart_probe(struct platform_device *pdev) return ret; } - gpio_restart->restart_handler.notifier_call = gpio_restart_notify; - gpio_restart->restart_handler.priority = 129; gpio_restart->active_delay_ms = 100; gpio_restart->inactive_delay_ms = 100; gpio_restart->wait_delay_ms = 3000; @@ -83,7 +79,7 @@ static int gpio_restart_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid priority property: %u\n", property); else - gpio_restart->restart_handler.priority = property; + priority = property; } of_property_read_u32(pdev->dev.of_node, "active-delay", @@ -93,9 +89,11 @@ static int gpio_restart_probe(struct platform_device *pdev) of_property_read_u32(pdev->dev.of_node, "wait-delay", &gpio_restart->wait_delay_ms); - platform_set_drvdata(pdev, gpio_restart); - - ret = register_restart_handler(&gpio_restart->restart_handler); + ret = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_RESTART, + priority, + gpio_restart_notify, + gpio_restart); if (ret) { dev_err(&pdev->dev, "%s: cannot register restart handler, %d\n", __func__, ret); @@ -105,22 +103,6 @@ static int gpio_restart_probe(struct platform_device *pdev) return 0; } -static int gpio_restart_remove(struct platform_device *pdev) -{ - struct gpio_restart *gpio_restart = platform_get_drvdata(pdev); - int ret; - - ret = unregister_restart_handler(&gpio_restart->restart_handler); - if (ret) { - dev_err(&pdev->dev, - "%s: cannot unregister restart handler, %d\n", - __func__, ret); - return -ENODEV; - } - - return 0; -} - static const struct of_device_id of_gpio_restart_match[] = { { .compatible = "gpio-restart", }, {}, @@ -128,7 +110,6 @@ static const struct of_device_id of_gpio_restart_match[] = { static struct platform_driver gpio_restart_driver = { .probe = gpio_restart_probe, - .remove = gpio_restart_remove, .driver = { .name = "restart-gpio", .of_match_table = of_gpio_restart_match, @@ -139,4 +120,3 @@ module_platform_driver(gpio_restart_driver); MODULE_AUTHOR("David Riley <davidriley@chromium.org>"); MODULE_DESCRIPTION("GPIO restart driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/keystone-reset.c b/drivers/power/reset/keystone-reset.c index c720112db704..d9268d150e1f 100644 --- a/drivers/power/reset/keystone-reset.c +++ b/drivers/power/reset/keystone-reset.c @@ -10,12 +10,12 @@ #include <linux/io.h> #include <linux/module.h> #include <linux/notifier.h> +#include <linux/platform_device.h> #include <linux/reboot.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> -#include <linux/of_platform.h> +#include <linux/of.h> -#define RSTYPE_RG 0x0 #define RSCTRL_RG 0x4 #define RSCFG_RG 0x8 #define RSISO_RG 0xc @@ -27,7 +27,6 @@ #define RSMUX_OMODE_MASK 0xe #define RSMUX_OMODE_RESET_ON 0xa #define RSMUX_OMODE_RESET_OFF 0x0 -#define RSMUX_LOCK_MASK 0x1 #define RSMUX_LOCK_SET 0x1 #define RSCFG_RSTYPE_SOFT 0x300f @@ -88,26 +87,16 @@ static int rsctrl_probe(struct platform_device *pdev) return -ENODEV; /* get regmaps */ - pllctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-pll"); + pllctrl_regs = syscon_regmap_lookup_by_phandle_args(np, "ti,syscon-pll", + 1, &rspll_offset); if (IS_ERR(pllctrl_regs)) return PTR_ERR(pllctrl_regs); - devctrl_regs = syscon_regmap_lookup_by_phandle(np, "ti,syscon-dev"); + devctrl_regs = syscon_regmap_lookup_by_phandle_args(np, "ti,syscon-dev", + 1, &rsmux_offset); if (IS_ERR(devctrl_regs)) return PTR_ERR(devctrl_regs); - ret = of_property_read_u32_index(np, "ti,syscon-pll", 1, &rspll_offset); - if (ret) { - dev_err(dev, "couldn't read the reset pll offset!\n"); - return -EINVAL; - } - - ret = of_property_read_u32_index(np, "ti,syscon-dev", 1, &rsmux_offset); - if (ret) { - dev_err(dev, "couldn't read the rsmux offset!\n"); - return -EINVAL; - } - /* set soft/hard reset */ val = of_property_read_bool(np, "ti,soft-reset"); val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD; @@ -169,5 +158,4 @@ module_platform_driver(rsctrl_driver); MODULE_AUTHOR("Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>"); MODULE_DESCRIPTION("Texas Instruments keystone reset driver"); -MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:" KBUILD_MODNAME); diff --git a/drivers/power/reset/ltc2952-poweroff.c b/drivers/power/reset/ltc2952-poweroff.c index 65d9528cc989..90c664d344d0 100644 --- a/drivers/power/reset/ltc2952-poweroff.c +++ b/drivers/power/reset/ltc2952-poweroff.c @@ -162,11 +162,11 @@ static void ltc2952_poweroff_default(struct ltc2952_poweroff *data) data->wde_interval = 300L * NSEC_PER_MSEC; data->trigger_delay = ktime_set(2, 500L * NSEC_PER_MSEC); - hrtimer_init(&data->timer_trigger, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_trigger.function = ltc2952_poweroff_timer_trigger; + hrtimer_setup(&data->timer_trigger, ltc2952_poweroff_timer_trigger, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); - hrtimer_init(&data->timer_wde, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - data->timer_wde.function = ltc2952_poweroff_timer_wde; + hrtimer_setup(&data->timer_wde, ltc2952_poweroff_timer_wde, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); } static int ltc2952_poweroff_init(struct platform_device *pdev) @@ -286,7 +286,7 @@ static int ltc2952_poweroff_probe(struct platform_device *pdev) return 0; } -static int ltc2952_poweroff_remove(struct platform_device *pdev) +static void ltc2952_poweroff_remove(struct platform_device *pdev) { struct ltc2952_poweroff *data = platform_get_drvdata(pdev); @@ -295,7 +295,6 @@ static int ltc2952_poweroff_remove(struct platform_device *pdev) hrtimer_cancel(&data->timer_wde); atomic_notifier_chain_unregister(&panic_notifier_list, &data->panic_notifier); - return 0; } static const struct of_device_id of_ltc2952_poweroff_match[] = { @@ -317,4 +316,3 @@ module_platform_driver(ltc2952_poweroff_driver); MODULE_AUTHOR("René Moll <rene.moll@xsens.com>"); MODULE_DESCRIPTION("LTC PowerPath power-off driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/macsmc-reboot.c b/drivers/power/reset/macsmc-reboot.c new file mode 100644 index 000000000000..e9702acdd366 --- /dev/null +++ b/drivers/power/reset/macsmc-reboot.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SMC Reboot/Poweroff Handler + * Copyright The Asahi Linux Contributors + */ + +#include <linux/delay.h> +#include <linux/mfd/core.h> +#include <linux/mfd/macsmc.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/slab.h> + +struct macsmc_reboot_nvmem { + struct nvmem_cell *shutdown_flag; + struct nvmem_cell *boot_stage; + struct nvmem_cell *boot_error_count; + struct nvmem_cell *panic_count; +}; + +static const char * const nvmem_names[] = { + "shutdown_flag", + "boot_stage", + "boot_error_count", + "panic_count", +}; + +enum boot_stage { + BOOT_STAGE_SHUTDOWN = 0x00, /* Clean shutdown */ + BOOT_STAGE_IBOOT_DONE = 0x2f, /* Last stage of bootloader */ + BOOT_STAGE_KERNEL_STARTED = 0x30, /* Normal OS booting */ +}; + +struct macsmc_reboot { + struct device *dev; + struct apple_smc *smc; + struct notifier_block reboot_notify; + + union { + struct macsmc_reboot_nvmem nvm; + struct nvmem_cell *nvm_cells[ARRAY_SIZE(nvmem_names)]; + }; +}; + +/* Helpers to read/write a u8 given a struct nvmem_cell */ +static int nvmem_cell_get_u8(struct nvmem_cell *cell) +{ + size_t len; + void *bfr; + u8 val; + + bfr = nvmem_cell_read(cell, &len); + if (IS_ERR(bfr)) + return PTR_ERR(bfr); + + if (len < 1) { + kfree(bfr); + return -EINVAL; + } + + val = *(u8 *)bfr; + kfree(bfr); + return val; +} + +static int nvmem_cell_set_u8(struct nvmem_cell *cell, u8 val) +{ + return nvmem_cell_write(cell, &val, sizeof(val)); +} + +/* + * SMC 'MBSE' key actions: + * + * 'offw' - shutdown warning + * 'slpw' - sleep warning + * 'rest' - restart warning + * 'off1' - shutdown (needs PMU bit set to stay on) + * 'susp' - suspend + * 'phra' - restart ("PE Halt Restart Action"?) + * 'panb' - panic beginning + * 'pane' - panic end + */ + +static int macsmc_prepare_atomic(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Preparing SMC for atomic mode\n"); + + apple_smc_enter_atomic(reboot->smc); + return NOTIFY_OK; +} + +static int macsmc_power_off(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Issuing power off (off1)\n"); + + if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(off1)) < 0) { + dev_err(reboot->dev, "Failed to issue MBSE = off1 (power_off)\n"); + } else { + mdelay(100); + WARN_ONCE(1, "Unable to power off system\n"); + } + + return NOTIFY_OK; +} + +static int macsmc_restart(struct sys_off_data *data) +{ + struct macsmc_reboot *reboot = data->cb_data; + + dev_info(reboot->dev, "Issuing restart (phra)\n"); + + if (apple_smc_write_u32_atomic(reboot->smc, SMC_KEY(MBSE), SMC_KEY(phra)) < 0) { + dev_err(reboot->dev, "Failed to issue MBSE = phra (restart)\n"); + } else { + mdelay(100); + WARN_ONCE(1, "Unable to restart system\n"); + } + + return NOTIFY_OK; +} + +static int macsmc_reboot_notify(struct notifier_block *this, unsigned long action, void *data) +{ + struct macsmc_reboot *reboot = container_of(this, struct macsmc_reboot, reboot_notify); + u8 shutdown_flag; + u32 val; + + switch (action) { + case SYS_RESTART: + val = SMC_KEY(rest); + shutdown_flag = 0; + break; + case SYS_POWER_OFF: + val = SMC_KEY(offw); + shutdown_flag = 1; + break; + default: + return NOTIFY_DONE; + } + + dev_info(reboot->dev, "Preparing for reboot (%p4ch)\n", &val); + + /* On the Mac Mini, this will turn off the LED for power off */ + if (apple_smc_write_u32(reboot->smc, SMC_KEY(MBSE), val) < 0) + dev_err(reboot->dev, "Failed to issue MBSE = %p4ch (reboot_prepare)\n", &val); + + /* Set the boot_stage to 0, which means we're doing a clean shutdown/reboot. */ + if (reboot->nvm.boot_stage && + nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_SHUTDOWN) < 0) + dev_err(reboot->dev, "Failed to write boot_stage\n"); + + /* + * Set the PMU flag to actually reboot into the off state. + * Without this, the device will just reboot. We make it optional in case it is no longer + * necessary on newer hardware. + */ + if (reboot->nvm.shutdown_flag && + nvmem_cell_set_u8(reboot->nvm.shutdown_flag, shutdown_flag) < 0) + dev_err(reboot->dev, "Failed to write shutdown_flag\n"); + + return NOTIFY_OK; +} + +static void macsmc_power_init_error_counts(struct macsmc_reboot *reboot) +{ + int boot_error_count, panic_count; + + if (!reboot->nvm.boot_error_count || !reboot->nvm.panic_count) + return; + + boot_error_count = nvmem_cell_get_u8(reboot->nvm.boot_error_count); + if (boot_error_count < 0) { + dev_err(reboot->dev, "Failed to read boot_error_count (%d)\n", boot_error_count); + return; + } + + panic_count = nvmem_cell_get_u8(reboot->nvm.panic_count); + if (panic_count < 0) { + dev_err(reboot->dev, "Failed to read panic_count (%d)\n", panic_count); + return; + } + + if (!boot_error_count && !panic_count) + return; + + dev_warn(reboot->dev, "PMU logged %d boot error(s) and %d panic(s)\n", + boot_error_count, panic_count); + + if (nvmem_cell_set_u8(reboot->nvm.panic_count, 0) < 0) + dev_err(reboot->dev, "Failed to reset panic_count\n"); + if (nvmem_cell_set_u8(reboot->nvm.boot_error_count, 0) < 0) + dev_err(reboot->dev, "Failed to reset boot_error_count\n"); +} + +static int macsmc_reboot_probe(struct platform_device *pdev) +{ + struct apple_smc *smc = dev_get_drvdata(pdev->dev.parent); + struct macsmc_reboot *reboot; + int ret, i; + + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + reboot->dev = &pdev->dev; + reboot->smc = smc; + + platform_set_drvdata(pdev, reboot); + + for (i = 0; i < ARRAY_SIZE(nvmem_names); i++) { + struct nvmem_cell *cell; + + cell = devm_nvmem_cell_get(&pdev->dev, + nvmem_names[i]); + if (IS_ERR(cell)) { + if (PTR_ERR(cell) == -EPROBE_DEFER) + return -EPROBE_DEFER; + dev_warn(&pdev->dev, "Missing NVMEM cell %s (%ld)\n", + nvmem_names[i], PTR_ERR(cell)); + /* Non fatal, we'll deal with it */ + cell = NULL; + } + reboot->nvm_cells[i] = cell; + } + + /* Set the boot_stage to indicate we're running the OS kernel */ + if (reboot->nvm.boot_stage && + nvmem_cell_set_u8(reboot->nvm.boot_stage, BOOT_STAGE_KERNEL_STARTED) < 0) + dev_err(reboot->dev, "Failed to write boot_stage\n"); + + /* Display and clear the error counts */ + macsmc_power_init_error_counts(reboot); + + reboot->reboot_notify.notifier_call = macsmc_reboot_notify; + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF_PREPARE, + SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register power-off prepare handler\n"); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, SYS_OFF_PRIO_HIGH, + macsmc_power_off, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register power-off handler\n"); + + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART_PREPARE, + SYS_OFF_PRIO_HIGH, macsmc_prepare_atomic, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, + "Failed to register restart prepare handler\n"); + ret = devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, SYS_OFF_PRIO_HIGH, + macsmc_restart, reboot); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register restart handler\n"); + + ret = devm_register_reboot_notifier(&pdev->dev, &reboot->reboot_notify); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register reboot notifier\n"); + + dev_info(&pdev->dev, "Handling reboot and poweroff requests via SMC\n"); + + return 0; +} + +static const struct of_device_id macsmc_reboot_of_table[] = { + { .compatible = "apple,smc-reboot", }, + {} +}; +MODULE_DEVICE_TABLE(of, macsmc_reboot_of_table); + +static struct platform_driver macsmc_reboot_driver = { + .driver = { + .name = "macsmc-reboot", + .of_match_table = macsmc_reboot_of_table, + }, + .probe = macsmc_reboot_probe, +}; +module_platform_driver(macsmc_reboot_driver); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_DESCRIPTION("Apple SMC reboot/poweroff driver"); +MODULE_AUTHOR("Hector Martin <marcan@marcan.st>"); diff --git a/drivers/power/reset/msm-poweroff.c b/drivers/power/reset/msm-poweroff.c index 0c439f83bf65..c7eb6dc8e90a 100644 --- a/drivers/power/reset/msm-poweroff.c +++ b/drivers/power/reset/msm-poweroff.c @@ -14,8 +14,8 @@ #include <linux/pm.h> static void __iomem *msm_ps_hold; -static int deassert_pshold(struct notifier_block *nb, unsigned long action, - void *data) + +static int do_msm_poweroff(struct sys_off_data *data) { writel(0, msm_ps_hold); mdelay(10000); @@ -23,29 +23,18 @@ static int deassert_pshold(struct notifier_block *nb, unsigned long action, return NOTIFY_DONE; } -static struct notifier_block restart_nb = { - .notifier_call = deassert_pshold, - .priority = 128, -}; - -static void do_msm_poweroff(void) -{ - deassert_pshold(&restart_nb, 0, NULL); -} - static int msm_restart_probe(struct platform_device *pdev) { - struct device *dev = &pdev->dev; - struct resource *mem; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - msm_ps_hold = devm_ioremap_resource(dev, mem); + msm_ps_hold = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(msm_ps_hold)) return PTR_ERR(msm_ps_hold); - register_restart_handler(&restart_nb); + devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_RESTART, + 128, do_msm_poweroff, NULL); - pm_power_off = do_msm_poweroff; + devm_register_sys_off_handler(&pdev->dev, SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, do_msm_poweroff, + NULL); return 0; } @@ -63,9 +52,4 @@ static struct platform_driver msm_restart_driver = { .of_match_table = of_match_ptr(of_msm_restart_match), }, }; - -static int __init msm_restart_init(void) -{ - return platform_driver_register(&msm_restart_driver); -} -device_initcall(msm_restart_init); +builtin_platform_driver(msm_restart_driver); diff --git a/drivers/power/reset/mt6323-poweroff.c b/drivers/power/reset/mt6323-poweroff.c index d90e76fcb938..c663347547f9 100644 --- a/drivers/power/reset/mt6323-poweroff.c +++ b/drivers/power/reset/mt6323-poweroff.c @@ -14,6 +14,7 @@ #include <linux/platform_device.h> #include <linux/mfd/mt6397/core.h> #include <linux/mfd/mt6397/rtc.h> +#include <linux/reboot.h> struct mt6323_pwrc { struct device *dev; @@ -21,11 +22,9 @@ struct mt6323_pwrc { u32 base; }; -static struct mt6323_pwrc *mt_pwrc; - -static void mt6323_do_pwroff(void) +static int mt6323_do_pwroff(struct sys_off_data *data) { - struct mt6323_pwrc *pwrc = mt_pwrc; + struct mt6323_pwrc *pwrc = data->cb_data; unsigned int val; int ret; @@ -44,6 +43,8 @@ static void mt6323_do_pwroff(void) mdelay(1000); WARN_ONCE(1, "Unable to power off system\n"); + + return NOTIFY_DONE; } static int mt6323_pwrc_probe(struct platform_device *pdev) @@ -51,6 +52,7 @@ static int mt6323_pwrc_probe(struct platform_device *pdev) struct mt6397_chip *mt6397_chip = dev_get_drvdata(pdev->dev.parent); struct mt6323_pwrc *pwrc; struct resource *res; + int ret; pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); if (!pwrc) @@ -63,17 +65,14 @@ static int mt6323_pwrc_probe(struct platform_device *pdev) pwrc->base = res->start; pwrc->regmap = mt6397_chip->regmap; pwrc->dev = &pdev->dev; - mt_pwrc = pwrc; - pm_power_off = &mt6323_do_pwroff; - - return 0; -} - -static int mt6323_pwrc_remove(struct platform_device *pdev) -{ - if (pm_power_off == &mt6323_do_pwroff) - pm_power_off = NULL; + ret = devm_register_sys_off_handler(pwrc->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + mt6323_do_pwroff, + pwrc); + if (ret) + return dev_err_probe(pwrc->dev, ret, "failed to register power-off handler\n"); return 0; } @@ -86,7 +85,6 @@ MODULE_DEVICE_TABLE(of, mt6323_pwrc_dt_match); static struct platform_driver mt6323_pwrc_driver = { .probe = mt6323_pwrc_probe, - .remove = mt6323_pwrc_remove, .driver = { .name = "mt6323-pwrc", .of_match_table = mt6323_pwrc_dt_match, @@ -97,4 +95,3 @@ module_platform_driver(mt6323_pwrc_driver); MODULE_DESCRIPTION("Poweroff driver for MT6323 PMIC"); MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/nvmem-reboot-mode.c b/drivers/power/reset/nvmem-reboot-mode.c index e229308d43e2..41530b70cfc4 100644 --- a/drivers/power/reset/nvmem-reboot-mode.c +++ b/drivers/power/reset/nvmem-reboot-mode.c @@ -45,8 +45,8 @@ static int nvmem_reboot_mode_probe(struct platform_device *pdev) nvmem_rbm->cell = devm_nvmem_cell_get(&pdev->dev, "reboot-mode"); if (IS_ERR(nvmem_rbm->cell)) { - dev_err(&pdev->dev, "failed to get the nvmem cell reboot-mode\n"); - return PTR_ERR(nvmem_rbm->cell); + return dev_err_probe(&pdev->dev, PTR_ERR(nvmem_rbm->cell), + "failed to get the nvmem cell reboot-mode\n"); } ret = devm_reboot_mode_register(&pdev->dev, &nvmem_rbm->reboot); diff --git a/drivers/power/reset/ocelot-reset.c b/drivers/power/reset/ocelot-reset.c index 8caa90cb58fc..56be64decf54 100644 --- a/drivers/power/reset/ocelot-reset.c +++ b/drivers/power/reset/ocelot-reset.c @@ -8,10 +8,10 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/notifier.h> +#include <linux/mod_devicetable.h> #include <linux/mfd/syscon.h> -#include <linux/of_address.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/reboot.h> #include <linux/regmap.h> @@ -69,8 +69,6 @@ static int ocelot_restart_handle(struct notifier_block *this, static int ocelot_reset_probe(struct platform_device *pdev) { struct ocelot_reset_context *ctx; - struct resource *res; - struct device *dev = &pdev->dev; int err; @@ -78,8 +76,7 @@ static int ocelot_reset_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - ctx->base = devm_ioremap_resource(dev, res); + ctx->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(ctx->base)) return PTR_ERR(ctx->base); diff --git a/drivers/power/reset/odroid-go-ultra-poweroff.c b/drivers/power/reset/odroid-go-ultra-poweroff.c new file mode 100644 index 000000000000..9cac7aef77f0 --- /dev/null +++ b/drivers/power/reset/odroid-go-ultra-poweroff.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2023 Neil Armstrong <neil.armstrong@linaro.org> + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/mfd/rk808.h> +#include <linux/regmap.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/i2c.h> + +/* + * The Odroid Go Ultra has 2 PMICs: + * - RK818 (manages the battery and USB-C power supply) + * - RK817 + * Both PMICs feeds power to the S922X SoC, so they must be powered-off in sequence. + * Vendor does power-off the RK817 first, then the RK818 so here we follow this sequence. + */ + +struct odroid_go_ultra_poweroff_data { + struct device *dev; + struct device *rk817; + struct device *rk818; +}; + +static int odroid_go_ultra_poweroff_prepare(struct sys_off_data *data) +{ + struct odroid_go_ultra_poweroff_data *poweroff_data = data->cb_data; + struct regmap *rk817, *rk818; + int ret; + + /* RK817 Regmap */ + rk817 = dev_get_regmap(poweroff_data->rk817, NULL); + if (!rk817) { + dev_err(poweroff_data->dev, "failed to get rk817 regmap\n"); + return notifier_from_errno(-EINVAL); + } + + /* RK818 Regmap */ + rk818 = dev_get_regmap(poweroff_data->rk818, NULL); + if (!rk818) { + dev_err(poweroff_data->dev, "failed to get rk818 regmap\n"); + return notifier_from_errno(-EINVAL); + } + + dev_info(poweroff_data->dev, "Setting PMICs for power off"); + + /* RK817 */ + ret = regmap_update_bits(rk817, RK817_SYS_CFG(3), DEV_OFF, DEV_OFF); + if (ret) { + dev_err(poweroff_data->dev, "failed to poweroff rk817\n"); + return notifier_from_errno(ret); + } + + /* RK818 */ + ret = regmap_update_bits(rk818, RK818_DEVCTRL_REG, DEV_OFF, DEV_OFF); + if (ret) { + dev_err(poweroff_data->dev, "failed to poweroff rk818\n"); + return notifier_from_errno(ret); + } + + return NOTIFY_OK; +} + +static void odroid_go_ultra_poweroff_put_pmic_device(void *data) +{ + struct device *dev = data; + + put_device(dev); +} + +static int odroid_go_ultra_poweroff_get_pmic_device(struct device *dev, const char *compatible, + struct device **pmic) +{ + struct device_node *pmic_node; + struct i2c_client *pmic_client; + + pmic_node = of_find_compatible_node(NULL, NULL, compatible); + if (!pmic_node) + return -ENODEV; + + pmic_client = of_find_i2c_device_by_node(pmic_node); + of_node_put(pmic_node); + if (!pmic_client) + return -EPROBE_DEFER; + + *pmic = &pmic_client->dev; + + return devm_add_action_or_reset(dev, odroid_go_ultra_poweroff_put_pmic_device, *pmic); +} + +static int odroid_go_ultra_poweroff_probe(struct platform_device *pdev) +{ + struct odroid_go_ultra_poweroff_data *poweroff_data; + int ret; + + poweroff_data = devm_kzalloc(&pdev->dev, sizeof(*poweroff_data), GFP_KERNEL); + if (!poweroff_data) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, poweroff_data); + + /* RK818 PMIC Device */ + ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk818", + &poweroff_data->rk818); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to get rk818 mfd data\n"); + + /* RK817 PMIC Device */ + ret = odroid_go_ultra_poweroff_get_pmic_device(&pdev->dev, "rockchip,rk817", + &poweroff_data->rk817); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to get rk817 mfd data\n"); + + /* Register as SYS_OFF_MODE_POWER_OFF_PREPARE because regmap_update_bits may sleep */ + ret = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_POWER_OFF_PREPARE, + SYS_OFF_PRIO_DEFAULT, + odroid_go_ultra_poweroff_prepare, + poweroff_data); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to register sys-off handler\n"); + + dev_info(&pdev->dev, "Registered Power-Off handler\n"); + + return 0; +} +static struct platform_device *pdev; + +static struct platform_driver odroid_go_ultra_poweroff_driver = { + .driver = { + .name = "odroid-go-ultra-poweroff", + }, + .probe = odroid_go_ultra_poweroff_probe, +}; + +static int __init odroid_go_ultra_poweroff_init(void) +{ + int ret; + + /* Only create when running on the Odroid Go Ultra device */ + if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra")) + return -ENODEV; + + ret = platform_driver_register(&odroid_go_ultra_poweroff_driver); + if (ret) + return ret; + + pdev = platform_device_register_resndata(NULL, "odroid-go-ultra-poweroff", -1, + NULL, 0, NULL, 0); + + if (IS_ERR(pdev)) { + platform_driver_unregister(&odroid_go_ultra_poweroff_driver); + return PTR_ERR(pdev); + } + + return 0; +} + +static void __exit odroid_go_ultra_poweroff_exit(void) +{ + /* Only delete when running on the Odroid Go Ultra device */ + if (!of_device_is_compatible(of_root, "hardkernel,odroid-go-ultra")) + return; + + platform_device_unregister(pdev); + platform_driver_unregister(&odroid_go_ultra_poweroff_driver); +} + +module_init(odroid_go_ultra_poweroff_init); +module_exit(odroid_go_ultra_poweroff_exit); + +MODULE_AUTHOR("Neil Armstrong <neil.armstrong@linaro.org>"); +MODULE_DESCRIPTION("Odroid Go Ultra poweroff driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/oxnas-restart.c b/drivers/power/reset/oxnas-restart.c deleted file mode 100644 index 13090bec058a..000000000000 --- a/drivers/power/reset/oxnas-restart.c +++ /dev/null @@ -1,233 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0) -/* - * oxnas SoC reset driver - * based on: - * Microsemi MIPS SoC reset driver - * and ox820_assert_system_reset() written by Ma Hajun <mahaijuns@gmail.com> - * - * Copyright (c) 2013 Ma Hajun <mahaijuns@gmail.com> - * Copyright (c) 2017 Microsemi Corporation - * Copyright (c) 2020 Daniel Golle <daniel@makrotopia.org> - */ -#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> - -/* bit numbers of reset control register */ -#define OX820_SYS_CTRL_RST_SCU 0 -#define OX820_SYS_CTRL_RST_COPRO 1 -#define OX820_SYS_CTRL_RST_ARM0 2 -#define OX820_SYS_CTRL_RST_ARM1 3 -#define OX820_SYS_CTRL_RST_USBHS 4 -#define OX820_SYS_CTRL_RST_USBHSPHYA 5 -#define OX820_SYS_CTRL_RST_MACA 6 -#define OX820_SYS_CTRL_RST_MAC OX820_SYS_CTRL_RST_MACA -#define OX820_SYS_CTRL_RST_PCIEA 7 -#define OX820_SYS_CTRL_RST_SGDMA 8 -#define OX820_SYS_CTRL_RST_CIPHER 9 -#define OX820_SYS_CTRL_RST_DDR 10 -#define OX820_SYS_CTRL_RST_SATA 11 -#define OX820_SYS_CTRL_RST_SATA_LINK 12 -#define OX820_SYS_CTRL_RST_SATA_PHY 13 -#define OX820_SYS_CTRL_RST_PCIEPHY 14 -#define OX820_SYS_CTRL_RST_STATIC 15 -#define OX820_SYS_CTRL_RST_GPIO 16 -#define OX820_SYS_CTRL_RST_UART1 17 -#define OX820_SYS_CTRL_RST_UART2 18 -#define OX820_SYS_CTRL_RST_MISC 19 -#define OX820_SYS_CTRL_RST_I2S 20 -#define OX820_SYS_CTRL_RST_SD 21 -#define OX820_SYS_CTRL_RST_MACB 22 -#define OX820_SYS_CTRL_RST_PCIEB 23 -#define OX820_SYS_CTRL_RST_VIDEO 24 -#define OX820_SYS_CTRL_RST_DDR_PHY 25 -#define OX820_SYS_CTRL_RST_USBHSPHYB 26 -#define OX820_SYS_CTRL_RST_USBDEV 27 -#define OX820_SYS_CTRL_RST_ARMDBG 29 -#define OX820_SYS_CTRL_RST_PLLA 30 -#define OX820_SYS_CTRL_RST_PLLB 31 - -/* bit numbers of clock control register */ -#define OX820_SYS_CTRL_CLK_COPRO 0 -#define OX820_SYS_CTRL_CLK_DMA 1 -#define OX820_SYS_CTRL_CLK_CIPHER 2 -#define OX820_SYS_CTRL_CLK_SD 3 -#define OX820_SYS_CTRL_CLK_SATA 4 -#define OX820_SYS_CTRL_CLK_I2S 5 -#define OX820_SYS_CTRL_CLK_USBHS 6 -#define OX820_SYS_CTRL_CLK_MACA 7 -#define OX820_SYS_CTRL_CLK_MAC OX820_SYS_CTRL_CLK_MACA -#define OX820_SYS_CTRL_CLK_PCIEA 8 -#define OX820_SYS_CTRL_CLK_STATIC 9 -#define OX820_SYS_CTRL_CLK_MACB 10 -#define OX820_SYS_CTRL_CLK_PCIEB 11 -#define OX820_SYS_CTRL_CLK_REF600 12 -#define OX820_SYS_CTRL_CLK_USBDEV 13 -#define OX820_SYS_CTRL_CLK_DDR 14 -#define OX820_SYS_CTRL_CLK_DDRPHY 15 -#define OX820_SYS_CTRL_CLK_DDRCK 16 - -/* Regmap offsets */ -#define OX820_CLK_SET_REGOFFSET 0x2c -#define OX820_CLK_CLR_REGOFFSET 0x30 -#define OX820_RST_SET_REGOFFSET 0x34 -#define OX820_RST_CLR_REGOFFSET 0x38 -#define OX820_SECONDARY_SEL_REGOFFSET 0x14 -#define OX820_TERTIARY_SEL_REGOFFSET 0x8c -#define OX820_QUATERNARY_SEL_REGOFFSET 0x94 -#define OX820_DEBUG_SEL_REGOFFSET 0x9c -#define OX820_ALTERNATIVE_SEL_REGOFFSET 0xa4 -#define OX820_PULLUP_SEL_REGOFFSET 0xac -#define OX820_SEC_SECONDARY_SEL_REGOFFSET 0x100014 -#define OX820_SEC_TERTIARY_SEL_REGOFFSET 0x10008c -#define OX820_SEC_QUATERNARY_SEL_REGOFFSET 0x100094 -#define OX820_SEC_DEBUG_SEL_REGOFFSET 0x10009c -#define OX820_SEC_ALTERNATIVE_SEL_REGOFFSET 0x1000a4 -#define OX820_SEC_PULLUP_SEL_REGOFFSET 0x1000ac - -struct oxnas_restart_context { - struct regmap *sys_ctrl; - struct notifier_block restart_handler; -}; - -static int ox820_restart_handle(struct notifier_block *this, - unsigned long mode, void *cmd) -{ - struct oxnas_restart_context *ctx = container_of(this, struct - oxnas_restart_context, - restart_handler); - u32 value; - - /* - * Assert reset to cores as per power on defaults - * Don't touch the DDR interface as things will come to an impromptu - * stop NB Possibly should be asserting reset for PLLB, but there are - * timing concerns here according to the docs - */ - value = BIT(OX820_SYS_CTRL_RST_COPRO) | - BIT(OX820_SYS_CTRL_RST_USBHS) | - BIT(OX820_SYS_CTRL_RST_USBHSPHYA) | - BIT(OX820_SYS_CTRL_RST_MACA) | - BIT(OX820_SYS_CTRL_RST_PCIEA) | - BIT(OX820_SYS_CTRL_RST_SGDMA) | - BIT(OX820_SYS_CTRL_RST_CIPHER) | - BIT(OX820_SYS_CTRL_RST_SATA) | - BIT(OX820_SYS_CTRL_RST_SATA_LINK) | - BIT(OX820_SYS_CTRL_RST_SATA_PHY) | - BIT(OX820_SYS_CTRL_RST_PCIEPHY) | - BIT(OX820_SYS_CTRL_RST_STATIC) | - BIT(OX820_SYS_CTRL_RST_UART1) | - BIT(OX820_SYS_CTRL_RST_UART2) | - BIT(OX820_SYS_CTRL_RST_MISC) | - BIT(OX820_SYS_CTRL_RST_I2S) | - BIT(OX820_SYS_CTRL_RST_SD) | - BIT(OX820_SYS_CTRL_RST_MACB) | - BIT(OX820_SYS_CTRL_RST_PCIEB) | - BIT(OX820_SYS_CTRL_RST_VIDEO) | - BIT(OX820_SYS_CTRL_RST_USBHSPHYB) | - BIT(OX820_SYS_CTRL_RST_USBDEV); - - regmap_write(ctx->sys_ctrl, OX820_RST_SET_REGOFFSET, value); - - /* Release reset to cores as per power on defaults */ - regmap_write(ctx->sys_ctrl, OX820_RST_CLR_REGOFFSET, - BIT(OX820_SYS_CTRL_RST_GPIO)); - - /* - * Disable clocks to cores as per power-on defaults - must leave DDR - * related clocks enabled otherwise we'll stop rather abruptly. - */ - value = BIT(OX820_SYS_CTRL_CLK_COPRO) | - BIT(OX820_SYS_CTRL_CLK_DMA) | - BIT(OX820_SYS_CTRL_CLK_CIPHER) | - BIT(OX820_SYS_CTRL_CLK_SD) | - BIT(OX820_SYS_CTRL_CLK_SATA) | - BIT(OX820_SYS_CTRL_CLK_I2S) | - BIT(OX820_SYS_CTRL_CLK_USBHS) | - BIT(OX820_SYS_CTRL_CLK_MAC) | - BIT(OX820_SYS_CTRL_CLK_PCIEA) | - BIT(OX820_SYS_CTRL_CLK_STATIC) | - BIT(OX820_SYS_CTRL_CLK_MACB) | - BIT(OX820_SYS_CTRL_CLK_PCIEB) | - BIT(OX820_SYS_CTRL_CLK_REF600) | - BIT(OX820_SYS_CTRL_CLK_USBDEV); - - regmap_write(ctx->sys_ctrl, OX820_CLK_CLR_REGOFFSET, value); - - /* Enable clocks to cores as per power-on defaults */ - - /* Set sys-control pin mux'ing as per power-on defaults */ - regmap_write(ctx->sys_ctrl, OX820_SECONDARY_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_TERTIARY_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_QUATERNARY_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_DEBUG_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_ALTERNATIVE_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_PULLUP_SEL_REGOFFSET, 0); - - regmap_write(ctx->sys_ctrl, OX820_SEC_SECONDARY_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_SEC_TERTIARY_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_SEC_QUATERNARY_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_SEC_DEBUG_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_SEC_ALTERNATIVE_SEL_REGOFFSET, 0); - regmap_write(ctx->sys_ctrl, OX820_SEC_PULLUP_SEL_REGOFFSET, 0); - - /* - * No need to save any state, as the ROM loader can determine whether - * reset is due to power cycling or programatic action, just hit the - * (self-clearing) CPU reset bit of the block reset register - */ - value = - BIT(OX820_SYS_CTRL_RST_SCU) | - BIT(OX820_SYS_CTRL_RST_ARM0) | - BIT(OX820_SYS_CTRL_RST_ARM1); - - regmap_write(ctx->sys_ctrl, OX820_RST_SET_REGOFFSET, value); - - pr_emerg("Unable to restart system\n"); - return NOTIFY_DONE; -} - -static int ox820_restart_probe(struct platform_device *pdev) -{ - struct oxnas_restart_context *ctx; - struct regmap *sys_ctrl; - struct device *dev = &pdev->dev; - int err = 0; - - sys_ctrl = syscon_node_to_regmap(pdev->dev.of_node); - if (IS_ERR(sys_ctrl)) - return PTR_ERR(sys_ctrl); - - ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); - if (!ctx) - return -ENOMEM; - - ctx->sys_ctrl = sys_ctrl; - ctx->restart_handler.notifier_call = ox820_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 ox820_restart_of_match[] = { - { .compatible = "oxsemi,ox820-sys-ctrl" }, - {} -}; - -static struct platform_driver ox820_restart_driver = { - .probe = ox820_restart_probe, - .driver = { - .name = "ox820-chip-reset", - .of_match_table = ox820_restart_of_match, - }, -}; -builtin_platform_driver(ox820_restart_driver); diff --git a/drivers/power/reset/piix4-poweroff.c b/drivers/power/reset/piix4-poweroff.c index 7f308292d7e3..e6822c021000 100644 --- a/drivers/power/reset/piix4-poweroff.c +++ b/drivers/power/reset/piix4-poweroff.c @@ -106,4 +106,5 @@ static struct pci_driver piix4_poweroff_driver = { module_pci_driver(piix4_poweroff_driver); MODULE_AUTHOR("Paul Burton <paul.burton@mips.com>"); +MODULE_DESCRIPTION("Intel PIIX4 power-off driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/pwr-mlxbf.c b/drivers/power/reset/pwr-mlxbf.c index 12dedf841a44..4f1cd1c0018c 100644 --- a/drivers/power/reset/pwr-mlxbf.c +++ b/drivers/power/reset/pwr-mlxbf.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0-only or BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0-only OR BSD-3-Clause /* * Copyright (c) 2022 NVIDIA CORPORATION & AFFILIATES. @@ -17,26 +17,26 @@ #include <linux/types.h> struct pwr_mlxbf { - struct work_struct send_work; + struct work_struct reboot_work; const char *hid; }; -static void pwr_mlxbf_send_work(struct work_struct *work) +static void pwr_mlxbf_reboot_work(struct work_struct *work) { - acpi_bus_generate_netlink_event("button/power.*", "Power Button", 0x80, 1); + acpi_bus_generate_netlink_event("button/reboot.*", "Reboot Button", 0x80, 1); } static irqreturn_t pwr_mlxbf_irq(int irq, void *ptr) { const char *rst_pwr_hid = "MLNXBF24"; - const char *low_pwr_hid = "MLNXBF29"; + const char *shutdown_hid = "MLNXBF29"; struct pwr_mlxbf *priv = ptr; if (!strncmp(priv->hid, rst_pwr_hid, 8)) - emergency_restart(); + schedule_work(&priv->reboot_work); - if (!strncmp(priv->hid, low_pwr_hid, 8)) - schedule_work(&priv->send_work); + if (!strncmp(priv->hid, shutdown_hid, 8)) + orderly_poweroff(true); return IRQ_HANDLED; } @@ -64,7 +64,7 @@ static int pwr_mlxbf_probe(struct platform_device *pdev) if (irq < 0) return dev_err_probe(dev, irq, "Error getting %s irq.\n", priv->hid); - err = devm_work_autocancel(dev, &priv->send_work, pwr_mlxbf_send_work); + err = devm_work_autocancel(dev, &priv->reboot_work, pwr_mlxbf_reboot_work); if (err) return err; diff --git a/drivers/power/reset/qcom-pon.c b/drivers/power/reset/qcom-pon.c index 16bc01738be9..7e108982a582 100644 --- a/drivers/power/reset/qcom-pon.c +++ b/drivers/power/reset/qcom-pon.c @@ -17,7 +17,9 @@ #define GEN1_REASON_SHIFT 2 #define GEN2_REASON_SHIFT 1 -struct pm8916_pon { +#define NO_REASON_SHIFT 0 + +struct qcom_pon { struct device *dev; struct regmap *regmap; u32 baseaddr; @@ -25,11 +27,11 @@ struct pm8916_pon { long reason_shift; }; -static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot, +static int qcom_pon_reboot_mode_write(struct reboot_mode_driver *reboot, unsigned int magic) { - struct pm8916_pon *pon = container_of - (reboot, struct pm8916_pon, reboot_mode); + struct qcom_pon *pon = container_of + (reboot, struct qcom_pon, reboot_mode); int ret; ret = regmap_update_bits(pon->regmap, @@ -42,9 +44,10 @@ static int pm8916_reboot_mode_write(struct reboot_mode_driver *reboot, return ret; } -static int pm8916_pon_probe(struct platform_device *pdev) +static int qcom_pon_probe(struct platform_device *pdev) { - struct pm8916_pon *pon; + struct qcom_pon *pon; + long reason_shift; int error; pon = devm_kzalloc(&pdev->dev, sizeof(*pon), GFP_KERNEL); @@ -64,13 +67,17 @@ static int pm8916_pon_probe(struct platform_device *pdev) if (error) return error; - pon->reboot_mode.dev = &pdev->dev; - pon->reason_shift = (long)of_device_get_match_data(&pdev->dev); - pon->reboot_mode.write = pm8916_reboot_mode_write; - error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode); - if (error) { - dev_err(&pdev->dev, "can't register reboot mode\n"); - return error; + reason_shift = (long)of_device_get_match_data(&pdev->dev); + + if (reason_shift != NO_REASON_SHIFT) { + pon->reboot_mode.dev = &pdev->dev; + pon->reason_shift = reason_shift; + pon->reboot_mode.write = qcom_pon_reboot_mode_write; + error = devm_reboot_mode_register(&pdev->dev, &pon->reboot_mode); + if (error) { + dev_err(&pdev->dev, "can't register reboot mode\n"); + return error; + } } platform_set_drvdata(pdev, pon); @@ -78,23 +85,24 @@ static int pm8916_pon_probe(struct platform_device *pdev) return devm_of_platform_populate(&pdev->dev); } -static const struct of_device_id pm8916_pon_id_table[] = { +static const struct of_device_id qcom_pon_id_table[] = { { .compatible = "qcom,pm8916-pon", .data = (void *)GEN1_REASON_SHIFT }, + { .compatible = "qcom,pm8941-pon", .data = (void *)NO_REASON_SHIFT }, { .compatible = "qcom,pms405-pon", .data = (void *)GEN1_REASON_SHIFT }, { .compatible = "qcom,pm8998-pon", .data = (void *)GEN2_REASON_SHIFT }, { .compatible = "qcom,pmk8350-pon", .data = (void *)GEN2_REASON_SHIFT }, { } }; -MODULE_DEVICE_TABLE(of, pm8916_pon_id_table); +MODULE_DEVICE_TABLE(of, qcom_pon_id_table); -static struct platform_driver pm8916_pon_driver = { - .probe = pm8916_pon_probe, +static struct platform_driver qcom_pon_driver = { + .probe = qcom_pon_probe, .driver = { - .name = "pm8916-pon", - .of_match_table = of_match_ptr(pm8916_pon_id_table), + .name = "qcom-pon", + .of_match_table = qcom_pon_id_table, }, }; -module_platform_driver(pm8916_pon_driver); +module_platform_driver(qcom_pon_driver); -MODULE_DESCRIPTION("pm8916 Power On driver"); +MODULE_DESCRIPTION("Qualcomm Power On driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/qnap-poweroff.c b/drivers/power/reset/qnap-poweroff.c index 0ddf7f25f7b8..f7d8fc935d51 100644 --- a/drivers/power/reset/qnap-poweroff.c +++ b/drivers/power/reset/qnap-poweroff.c @@ -111,10 +111,9 @@ static int qnap_power_off_probe(struct platform_device *pdev) return 0; } -static int qnap_power_off_remove(struct platform_device *pdev) +static void qnap_power_off_remove(struct platform_device *pdev) { pm_power_off = NULL; - return 0; } static struct platform_driver qnap_power_off_driver = { diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c index b4076b10b893..fba53f638da0 100644 --- a/drivers/power/reset/reboot-mode.c +++ b/drivers/power/reset/reboot-mode.c @@ -23,20 +23,29 @@ static unsigned int get_reboot_mode_magic(struct reboot_mode_driver *reboot, const char *cmd) { const char *normal = "normal"; - int magic = 0; struct mode_info *info; + char cmd_[110]; if (!cmd) cmd = normal; - list_for_each_entry(info, &reboot->head, list) { - if (!strcmp(info->mode, cmd)) { - magic = info->magic; - break; - } - } + list_for_each_entry(info, &reboot->head, list) + if (!strcmp(info->mode, cmd)) + return info->magic; + + /* try to match again, replacing characters impossible in DT */ + if (strscpy(cmd_, cmd, sizeof(cmd_)) == -E2BIG) + return 0; - return magic; + strreplace(cmd_, ' ', '-'); + strreplace(cmd_, ',', '-'); + strreplace(cmd_, '/', '-'); + + list_for_each_entry(info, &reboot->head, list) + if (!strcmp(info->mode, cmd_)) + return info->magic; + + return 0; } static int reboot_mode_notify(struct notifier_block *this, diff --git a/drivers/power/reset/regulator-poweroff.c b/drivers/power/reset/regulator-poweroff.c index 20701203935f..fed4978e3858 100644 --- a/drivers/power/reset/regulator-poweroff.c +++ b/drivers/power/reset/regulator-poweroff.c @@ -13,18 +13,15 @@ #include <linux/of.h> #include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/reboot.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) +static int regulator_poweroff_do_poweroff(struct sys_off_data *data) { + struct regulator *cpu_regulator = data->cb_data; + if (cpu_regulator && regulator_is_enabled(cpu_regulator)) regulator_force_disable(cpu_regulator); @@ -32,32 +29,24 @@ static void regulator_poweroff_do_poweroff(void) mdelay(TIMEOUT_MS); WARN_ON(1); + + return NOTIFY_DONE; } 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; - } + struct regulator *cpu_regulator; 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; + /* Set this handler to low priority to not override an existing handler */ + return devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_LOW, + regulator_poweroff_do_poweroff, + cpu_regulator); } static const struct of_device_id of_regulator_poweroff_match[] = { @@ -68,7 +57,6 @@ MODULE_DEVICE_TABLE(of, of_regulator_poweroff_match); 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, @@ -79,5 +67,4 @@ 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/restart-poweroff.c b/drivers/power/reset/restart-poweroff.c index 04d4228119b2..fcd588f9ae9d 100644 --- a/drivers/power/reset/restart-poweroff.c +++ b/drivers/power/reset/restart-poweroff.c @@ -14,31 +14,21 @@ #include <linux/module.h> #include <linux/reboot.h> -static void restart_poweroff_do_poweroff(void) +static int restart_poweroff_do_poweroff(struct sys_off_data *data) { reboot_mode = REBOOT_HARD; machine_restart(NULL); + return NOTIFY_DONE; } static int restart_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, - "pm_power_off function already registered"); - return -EBUSY; - } - - pm_power_off = &restart_poweroff_do_poweroff; - return 0; -} - -static int restart_poweroff_remove(struct platform_device *pdev) -{ - if (pm_power_off == &restart_poweroff_do_poweroff) - pm_power_off = NULL; - - return 0; + /* Set this handler to low priority to not override an existing handler */ + return devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_LOW, + restart_poweroff_do_poweroff, + NULL); } static const struct of_device_id of_restart_poweroff_match[] = { @@ -49,7 +39,6 @@ MODULE_DEVICE_TABLE(of, of_restart_poweroff_match); static struct platform_driver restart_poweroff_driver = { .probe = restart_poweroff_probe, - .remove = restart_poweroff_remove, .driver = { .name = "poweroff-restart", .of_match_table = of_restart_poweroff_match, @@ -59,5 +48,4 @@ module_platform_driver(restart_poweroff_driver); MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch"); MODULE_DESCRIPTION("restart poweroff driver"); -MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:poweroff-restart"); diff --git a/drivers/power/reset/rmobile-reset.c b/drivers/power/reset/rmobile-reset.c index bd3b396558e0..7dbc51c32b0e 100644 --- a/drivers/power/reset/rmobile-reset.c +++ b/drivers/power/reset/rmobile-reset.c @@ -19,12 +19,9 @@ /* Reset Control Register 2 */ #define RESCNT2_PRES 0x80000000 /* Soft power-on reset */ -static void __iomem *sysc_base2; - -static int rmobile_reset_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int rmobile_reset_handler(struct sys_off_data *data) { - pr_debug("%s %lu\n", __func__, mode); + void __iomem *sysc_base2 = (void __iomem *)data->cb_data; /* Let's assume we have acquired the HPB semaphore */ writel(RESCNT2_PRES, sysc_base2 + RESCNT2); @@ -32,38 +29,27 @@ static int rmobile_reset_handler(struct notifier_block *this, return NOTIFY_DONE; } -static struct notifier_block rmobile_reset_nb = { - .notifier_call = rmobile_reset_handler, - .priority = 192, -}; - static int rmobile_reset_probe(struct platform_device *pdev) { + void __iomem *sysc_base2; int error; - sysc_base2 = of_iomap(pdev->dev.of_node, 1); - if (!sysc_base2) - return -ENODEV; + sysc_base2 = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(sysc_base2)) + return PTR_ERR(sysc_base2); - error = register_restart_handler(&rmobile_reset_nb); + error = devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_HIGH, + rmobile_reset_handler, + (__force void *)sysc_base2); if (error) { dev_err(&pdev->dev, "cannot register restart handler (err=%d)\n", error); - goto fail_unmap; + return error; } return 0; - -fail_unmap: - iounmap(sysc_base2); - return error; -} - -static int rmobile_reset_remove(struct platform_device *pdev) -{ - unregister_restart_handler(&rmobile_reset_nb); - iounmap(sysc_base2); - return 0; } static const struct of_device_id rmobile_reset_of_match[] = { @@ -74,7 +60,6 @@ MODULE_DEVICE_TABLE(of, rmobile_reset_of_match); static struct platform_driver rmobile_reset_driver = { .probe = rmobile_reset_probe, - .remove = rmobile_reset_remove, .driver = { .name = "rmobile_reset", .of_match_table = rmobile_reset_of_match, diff --git a/drivers/power/reset/sc27xx-poweroff.c b/drivers/power/reset/sc27xx-poweroff.c index 90287c31992c..393bd1c33b73 100644 --- a/drivers/power/reset/sc27xx-poweroff.c +++ b/drivers/power/reset/sc27xx-poweroff.c @@ -28,7 +28,7 @@ static struct regmap *regmap; * taking cpus down to avoid racing regmap or spi mutex lock when poweroff * system through PMIC. */ -static void sc27xx_poweroff_shutdown(void) +static void sc27xx_poweroff_shutdown(void *data) { #ifdef CONFIG_HOTPLUG_CPU int cpu; @@ -40,10 +40,14 @@ static void sc27xx_poweroff_shutdown(void) #endif } -static struct syscore_ops poweroff_syscore_ops = { +static const struct syscore_ops poweroff_syscore_ops = { .shutdown = sc27xx_poweroff_shutdown, }; +static struct syscore poweroff_syscore = { + .ops = &poweroff_syscore_ops, +}; + static void sc27xx_poweroff_do_poweroff(void) { /* Disable the external subsys connection's power firstly */ @@ -62,7 +66,7 @@ static int sc27xx_poweroff_probe(struct platform_device *pdev) return -ENODEV; pm_power_off = sc27xx_poweroff_do_poweroff; - register_syscore_ops(&poweroff_syscore_ops); + register_syscore(&poweroff_syscore); return 0; } diff --git a/drivers/power/reset/spacemit-p1-reboot.c b/drivers/power/reset/spacemit-p1-reboot.c new file mode 100644 index 000000000000..9ec3d1fff8f3 --- /dev/null +++ b/drivers/power/reset/spacemit-p1-reboot.c @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 by Aurelien Jarno + */ + +#include <linux/bits.h> +#include <linux/mod_devicetable.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/reboot.h> + +/* Power Control Register 2 */ +#define PWR_CTRL2 0x7e +#define PWR_CTRL2_SHUTDOWN BIT(2) /* Shutdown request */ +#define PWR_CTRL2_RST BIT(1) /* Reset request */ + +static int spacemit_p1_pwroff_handler(struct sys_off_data *data) +{ + struct regmap *regmap = data->cb_data; + int ret; + + /* Put the PMIC into shutdown state */ + ret = regmap_set_bits(regmap, PWR_CTRL2, PWR_CTRL2_SHUTDOWN); + if (ret) { + dev_err(data->dev, "shutdown failed: %d\n", ret); + return notifier_from_errno(ret); + } + + return NOTIFY_DONE; +} + +static int spacemit_p1_restart_handler(struct sys_off_data *data) +{ + struct regmap *regmap = data->cb_data; + int ret; + + /* Put the PMIC into reset state */ + ret = regmap_set_bits(regmap, PWR_CTRL2, PWR_CTRL2_RST); + if (ret) { + dev_err(data->dev, "restart failed: %d\n", ret); + return notifier_from_errno(ret); + } + + return NOTIFY_DONE; +} + +static int spacemit_p1_reboot_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct regmap *regmap; + int ret; + + regmap = dev_get_regmap(dev->parent, NULL); + if (!regmap) + return -ENODEV; + + ret = devm_register_power_off_handler(dev, &spacemit_p1_pwroff_handler, + regmap); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register power off handler\n"); + + ret = devm_register_restart_handler(dev, spacemit_p1_restart_handler, + regmap); + if (ret) + return dev_err_probe(dev, ret, + "Failed to register restart handler\n"); + + return 0; +} + +static const struct platform_device_id spacemit_p1_reboot_id_table[] = { + { "spacemit-p1-reboot", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, spacemit_p1_reboot_id_table); + +static struct platform_driver spacemit_p1_reboot_driver = { + .driver = { + .name = "spacemit-p1-reboot", + }, + .probe = spacemit_p1_reboot_probe, + .id_table = spacemit_p1_reboot_id_table, +}; +module_platform_driver(spacemit_p1_reboot_driver); + +MODULE_DESCRIPTION("SpacemiT P1 reboot/poweroff driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/st-poweroff.c b/drivers/power/reset/st-poweroff.c index 5ccaacffab54..85175066beea 100644 --- a/drivers/power/reset/st-poweroff.c +++ b/drivers/power/reset/st-poweroff.c @@ -9,7 +9,7 @@ #include <linux/module.h> #include <linux/of.h> -#include <linux/of_platform.h> +#include <linux/of_device.h> #include <linux/platform_device.h> #include <linux/mfd/syscon.h> #include <linux/reboot.h> @@ -100,12 +100,7 @@ static struct platform_driver st_reset_driver = { }, }; -static int __init st_reset_init(void) -{ - return platform_driver_register(&st_reset_driver); -} - -device_initcall(st_reset_init); +builtin_platform_driver(st_reset_driver); MODULE_AUTHOR("Christophe Kerello <christophe.kerello@st.com>"); MODULE_DESCRIPTION("STMicroelectronics Power off Restart driver"); diff --git a/drivers/power/reset/syscon-poweroff.c b/drivers/power/reset/syscon-poweroff.c index ed58bdf41e27..203936f4c544 100644 --- a/drivers/power/reset/syscon-poweroff.c +++ b/drivers/power/reset/syscon-poweroff.c @@ -10,75 +10,77 @@ #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/of.h> #include <linux/platform_device.h> #include <linux/pm.h> +#include <linux/reboot.h> #include <linux/regmap.h> -static struct regmap *map; -static u32 offset; -static u32 value; -static u32 mask; +struct syscon_poweroff_data { + struct regmap *map; + u32 offset; + u32 value; + u32 mask; +}; -static void syscon_poweroff(void) +static int syscon_poweroff(struct sys_off_data *off_data) { + struct syscon_poweroff_data *data = off_data->cb_data; + /* Issue the poweroff */ - regmap_update_bits(map, offset, mask, value); + regmap_update_bits(data->map, data->offset, data->mask, data->value); mdelay(1000); pr_emerg("Unable to poweroff system\n"); + + return NOTIFY_DONE; } static int syscon_poweroff_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct syscon_poweroff_data *data; int mask_err, value_err; - map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "regmap"); - if (IS_ERR(map)) { - dev_err(&pdev->dev, "unable to get syscon"); - return PTR_ERR(map); + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->map = syscon_regmap_lookup_by_phandle(dev->of_node, "regmap"); + if (IS_ERR(data->map)) { + data->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(data->map)) { + dev_err(dev, "unable to get syscon"); + return PTR_ERR(data->map); + } } - if (of_property_read_u32(pdev->dev.of_node, "offset", &offset)) { - dev_err(&pdev->dev, "unable to read 'offset'"); + if (of_property_read_u32(dev->of_node, "offset", &data->offset)) { + dev_err(dev, "unable to read 'offset'"); return -EINVAL; } - value_err = of_property_read_u32(pdev->dev.of_node, "value", &value); - mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &mask); + value_err = of_property_read_u32(dev->of_node, "value", &data->value); + mask_err = of_property_read_u32(dev->of_node, "mask", &data->mask); if (value_err && mask_err) { - dev_err(&pdev->dev, "unable to read 'value' and 'mask'"); + dev_err(dev, "unable to read 'value' and 'mask'"); return -EINVAL; } if (value_err) { /* support old binding */ - value = mask; - mask = 0xFFFFFFFF; + data->value = data->mask; + data->mask = 0xFFFFFFFF; } else if (mask_err) { /* support value without mask*/ - mask = 0xFFFFFFFF; + data->mask = 0xFFFFFFFF; } - if (pm_power_off) { - dev_err(&pdev->dev, "pm_power_off already claimed for %ps", - pm_power_off); - return -EBUSY; - } - - pm_power_off = syscon_poweroff; - - return 0; -} - -static int syscon_poweroff_remove(struct platform_device *pdev) -{ - if (pm_power_off == syscon_poweroff) - pm_power_off = NULL; - - return 0; + return devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_DEFAULT, + syscon_poweroff, data); } static const struct of_device_id syscon_poweroff_of_match[] = { @@ -88,15 +90,9 @@ static const struct of_device_id syscon_poweroff_of_match[] = { static struct platform_driver syscon_poweroff_driver = { .probe = syscon_poweroff_probe, - .remove = syscon_poweroff_remove, .driver = { .name = "syscon-poweroff", .of_match_table = syscon_poweroff_of_match, }, }; - -static int __init syscon_poweroff_register(void) -{ - return platform_driver_register(&syscon_poweroff_driver); -} -device_initcall(syscon_poweroff_register); +builtin_platform_driver(syscon_poweroff_driver); diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c index 510e363381ca..2e2cf5f62d73 100644 --- a/drivers/power/reset/syscon-reboot.c +++ b/drivers/power/reset/syscon-reboot.c @@ -9,17 +9,29 @@ #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/of.h> #include <linux/platform_device.h> #include <linux/reboot.h> #include <linux/regmap.h> -struct syscon_reboot_context { - struct regmap *map; +struct reboot_mode_bits { u32 offset; - u32 value; u32 mask; + u32 value; + bool valid; +}; + +struct reboot_data { + struct reboot_mode_bits mode_bits[REBOOT_SOFT + 1]; + struct reboot_mode_bits catchall; +}; + +struct syscon_reboot_context { + struct regmap *map; + + const struct reboot_data *rd; /* from of match data, if any */ + struct reboot_mode_bits catchall; /* from DT */ + struct notifier_block restart_handler; }; @@ -29,9 +41,21 @@ static int syscon_restart_handle(struct notifier_block *this, struct syscon_reboot_context *ctx = container_of(this, struct syscon_reboot_context, restart_handler); + const struct reboot_mode_bits *mode_bits; + + if (ctx->rd) { + if (mode < ARRAY_SIZE(ctx->rd->mode_bits) && + ctx->rd->mode_bits[mode].valid) + mode_bits = &ctx->rd->mode_bits[mode]; + else + mode_bits = &ctx->rd->catchall; + } else { + mode_bits = &ctx->catchall; + } /* Issue the reboot */ - regmap_update_bits(ctx->map, ctx->offset, ctx->mask, ctx->value); + regmap_update_bits(ctx->map, mode_bits->offset, mode_bits->mask, + mode_bits->value); mdelay(1000); @@ -43,7 +67,7 @@ static int syscon_reboot_probe(struct platform_device *pdev) { struct syscon_reboot_context *ctx; struct device *dev = &pdev->dev; - int mask_err, value_err; + int priority; int err; ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL); @@ -57,27 +81,40 @@ static int syscon_reboot_probe(struct platform_device *pdev) return PTR_ERR(ctx->map); } - if (of_property_read_u32(pdev->dev.of_node, "offset", &ctx->offset)) - return -EINVAL; + if (of_property_read_s32(pdev->dev.of_node, "priority", &priority)) + priority = 192; - value_err = of_property_read_u32(pdev->dev.of_node, "value", &ctx->value); - mask_err = of_property_read_u32(pdev->dev.of_node, "mask", &ctx->mask); - if (value_err && mask_err) { - dev_err(dev, "unable to read 'value' and 'mask'"); - return -EINVAL; - } + ctx->rd = of_device_get_match_data(dev); + if (!ctx->rd) { + int mask_err, value_err; + + if (of_property_read_u32(pdev->dev.of_node, "offset", + &ctx->catchall.offset) && + of_property_read_u32(pdev->dev.of_node, "reg", + &ctx->catchall.offset)) + return -EINVAL; - if (value_err) { - /* support old binding */ - ctx->value = ctx->mask; - ctx->mask = 0xFFFFFFFF; - } else if (mask_err) { - /* support value without mask*/ - ctx->mask = 0xFFFFFFFF; + value_err = of_property_read_u32(pdev->dev.of_node, "value", + &ctx->catchall.value); + mask_err = of_property_read_u32(pdev->dev.of_node, "mask", + &ctx->catchall.mask); + if (value_err && mask_err) { + dev_err(dev, "unable to read 'value' and 'mask'"); + return -EINVAL; + } + + if (value_err) { + /* support old binding */ + ctx->catchall.value = ctx->catchall.mask; + ctx->catchall.mask = 0xFFFFFFFF; + } else if (mask_err) { + /* support value without mask */ + ctx->catchall.mask = 0xFFFFFFFF; + } } ctx->restart_handler.notifier_call = syscon_restart_handle; - ctx->restart_handler.priority = 192; + ctx->restart_handler.priority = priority; err = register_restart_handler(&ctx->restart_handler); if (err) dev_err(dev, "can't register restart notifier (err=%d)\n", err); @@ -85,7 +122,30 @@ static int syscon_reboot_probe(struct platform_device *pdev) return err; } +static const struct reboot_data gs101_reboot_data = { + .mode_bits = { + [REBOOT_WARM] = { + .offset = 0x3a00, /* SYSTEM_CONFIGURATION */ + .mask = 0x00000002, /* SWRESET_SYSTEM */ + .value = 0x00000002, + .valid = true, + }, + [REBOOT_SOFT] = { + .offset = 0x3a00, /* SYSTEM_CONFIGURATION */ + .mask = 0x00000002, /* SWRESET_SYSTEM */ + .value = 0x00000002, + .valid = true, + }, + }, + .catchall = { + .offset = 0x3e9c, /* PAD_CTRL_PWR_HOLD */ + .mask = 0x00000100, + .value = 0x00000000, + }, +}; + static const struct of_device_id syscon_reboot_of_match[] = { + { .compatible = "google,gs101-reboot", .data = &gs101_reboot_data }, { .compatible = "syscon-reboot" }, {} }; diff --git a/drivers/power/reset/tdx-ec-poweroff.c b/drivers/power/reset/tdx-ec-poweroff.c new file mode 100644 index 000000000000..3302a127fce5 --- /dev/null +++ b/drivers/power/reset/tdx-ec-poweroff.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Toradex Embedded Controller driver + * + * Copyright (C) 2025 Toradex + * + * Author: Emanuele Ghidoli <emanuele.ghidoli@toradex.com> + */ + +#include <linux/array_size.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/regmap.h> +#include <linux/types.h> + +#define EC_CHIP_ID_REG 0x00 +#define EC_CHIP_ID_SMARC_IMX95 0x11 +#define EC_CHIP_ID_SMARC_IMX8MP 0x12 + +#define EC_VERSION_REG_MAJOR 0x01 +#define EC_VERSION_REG_MINOR 0x02 +#define EC_ID_VERSION_LEN 3 + +#define EC_CMD_REG 0xD0 +#define EC_CMD_POWEROFF 0x01 +#define EC_CMD_RESET 0x02 + +#define EC_REG_MAX 0xD0 + +static const struct regmap_range volatile_ranges[] = { + regmap_reg_range(EC_CMD_REG, EC_CMD_REG), +}; + +static const struct regmap_access_table volatile_table = { + .yes_ranges = volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(volatile_ranges), +}; + +static const struct regmap_range read_ranges[] = { + regmap_reg_range(EC_CHIP_ID_REG, EC_VERSION_REG_MINOR), +}; + +static const struct regmap_access_table read_table = { + .yes_ranges = read_ranges, + .n_yes_ranges = ARRAY_SIZE(read_ranges), +}; + +static const struct regmap_config regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = EC_REG_MAX, + .cache_type = REGCACHE_RBTREE, + .rd_table = &read_table, + .volatile_table = &volatile_table, +}; + +static int tdx_ec_cmd(struct regmap *regmap, u8 cmd) +{ + int err = regmap_write(regmap, EC_CMD_REG, cmd); + + if (err) + dev_err(regmap_get_device(regmap), "Failed to send command 0x%02X: %d\n", cmd, err); + + return err; +} + +static int tdx_ec_power_off(struct sys_off_data *data) +{ + struct regmap *regmap = data->cb_data; + int err; + + err = tdx_ec_cmd(regmap, EC_CMD_POWEROFF); + + return err ? NOTIFY_BAD : NOTIFY_DONE; +} + +static int tdx_ec_restart(struct sys_off_data *data) +{ + struct regmap *regmap = data->cb_data; + int err; + + err = tdx_ec_cmd(regmap, EC_CMD_RESET); + + return err ? NOTIFY_BAD : NOTIFY_DONE; +} + +static int tdx_ec_register_power_off_restart(struct device *dev, struct regmap *regmap) +{ + int err; + + err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_FIRMWARE, + tdx_ec_restart, regmap); + if (err) + return err; + + return devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, + SYS_OFF_PRIO_FIRMWARE, + tdx_ec_power_off, regmap); +} + +static int tdx_ec_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + u8 reg_val[EC_ID_VERSION_LEN]; + struct regmap *regmap; + int err; + + regmap = devm_regmap_init_i2c(client, ®map_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + err = regmap_bulk_read(regmap, EC_CHIP_ID_REG, ®_val, EC_ID_VERSION_LEN); + if (err) + return dev_err_probe(dev, err, + "Cannot read id and version registers\n"); + + dev_info(dev, "Toradex Embedded Controller id %x - Firmware %u.%u\n", + reg_val[0], reg_val[1], reg_val[2]); + + err = tdx_ec_register_power_off_restart(dev, regmap); + if (err) + return dev_err_probe(dev, err, + "Cannot register system restart handler\n"); + + return 0; +} + +static const struct of_device_id __maybe_unused of_tdx_ec_match[] = { + { .compatible = "toradex,smarc-ec" }, + {} +}; +MODULE_DEVICE_TABLE(of, of_tdx_ec_match); + +static struct i2c_driver tdx_ec_driver = { + .probe = tdx_ec_probe, + .driver = { + .name = "toradex-smarc-ec", + .of_match_table = of_tdx_ec_match, + }, +}; +module_i2c_driver(tdx_ec_driver); + +MODULE_AUTHOR("Emanuele Ghidoli <emanuele.ghidoli@toradex.com>"); +MODULE_DESCRIPTION("Toradex SMARC Embedded Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/th1520-aon-reboot.c b/drivers/power/reset/th1520-aon-reboot.c new file mode 100644 index 000000000000..ec249667a0ff --- /dev/null +++ b/drivers/power/reset/th1520-aon-reboot.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * T-HEAD TH1520 AON Firmware Reboot Driver + * + * Copyright (c) 2025 Icenowy Zheng <uwu@icenowy.me> + */ + +#include <linux/auxiliary_bus.h> +#include <linux/firmware/thead/thead,th1520-aon.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/reboot.h> +#include <linux/slab.h> + +#define TH1520_AON_REBOOT_PRIORITY 200 + +struct th1520_aon_msg_empty_body { + struct th1520_aon_rpc_msg_hdr hdr; + u16 reserved[12]; +} __packed __aligned(1); + +static int th1520_aon_pwroff_handler(struct sys_off_data *data) +{ + struct th1520_aon_chan *aon_chan = data->cb_data; + struct th1520_aon_msg_empty_body msg = {}; + + msg.hdr.svc = TH1520_AON_RPC_SVC_WDG; + msg.hdr.func = TH1520_AON_WDG_FUNC_POWER_OFF; + msg.hdr.size = TH1520_AON_RPC_MSG_NUM; + + th1520_aon_call_rpc(aon_chan, &msg); + + return NOTIFY_DONE; +} + +static int th1520_aon_restart_handler(struct sys_off_data *data) +{ + struct th1520_aon_chan *aon_chan = data->cb_data; + struct th1520_aon_msg_empty_body msg = {}; + + msg.hdr.svc = TH1520_AON_RPC_SVC_WDG; + msg.hdr.func = TH1520_AON_WDG_FUNC_RESTART; + msg.hdr.size = TH1520_AON_RPC_MSG_NUM; + + th1520_aon_call_rpc(aon_chan, &msg); + + return NOTIFY_DONE; +} + +static int th1520_aon_reboot_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + int ret; + + /* Expect struct th1520_aon_chan to be passed via platform_data */ + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_POWER_OFF, + TH1520_AON_REBOOT_PRIORITY, + th1520_aon_pwroff_handler, + adev->dev.platform_data); + + if (ret) { + dev_err(dev, "Failed to register power off handler\n"); + return ret; + } + + ret = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, + TH1520_AON_REBOOT_PRIORITY, + th1520_aon_restart_handler, + adev->dev.platform_data); + + if (ret) { + dev_err(dev, "Failed to register restart handler\n"); + return ret; + } + + return 0; +} + +static const struct auxiliary_device_id th1520_aon_reboot_id_table[] = { + { .name = "th1520_pm_domains.reboot" }, + {}, +}; +MODULE_DEVICE_TABLE(auxiliary, th1520_aon_reboot_id_table); + +static struct auxiliary_driver th1520_aon_reboot_driver = { + .driver = { + .name = "th1520-aon-reboot", + }, + .probe = th1520_aon_reboot_probe, + .id_table = th1520_aon_reboot_id_table, +}; +module_auxiliary_driver(th1520_aon_reboot_driver); + +MODULE_AUTHOR("Icenowy Zheng <uwu@icenowy.me>"); +MODULE_DESCRIPTION("T-HEAD TH1520 AON-firmware-based reboot driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/power/reset/tps65086-restart.c b/drivers/power/reset/tps65086-restart.c index 78b89f745a3d..6976dbcac74f 100644 --- a/drivers/power/reset/tps65086-restart.c +++ b/drivers/power/reset/tps65086-restart.c @@ -9,22 +9,14 @@ #include <linux/platform_device.h> #include <linux/reboot.h> -struct tps65086_restart { - struct notifier_block handler; - struct device *dev; -}; - -static int tps65086_restart_notify(struct notifier_block *this, - unsigned long mode, void *cmd) +static int tps65086_restart_notify(struct sys_off_data *data) { - struct tps65086_restart *tps65086_restart = - container_of(this, struct tps65086_restart, handler); - struct tps65086 *tps65086 = dev_get_drvdata(tps65086_restart->dev->parent); + struct tps65086 *tps65086 = data->cb_data; int ret; ret = regmap_write(tps65086->regmap, TPS65086_FORCESHUTDN, 1); if (ret) { - dev_err(tps65086_restart->dev, "%s: error writing to tps65086 pmic: %d\n", + dev_err(tps65086->dev, "%s: error writing to tps65086 pmic: %d\n", __func__, ret); return NOTIFY_DONE; } @@ -39,42 +31,13 @@ static int tps65086_restart_notify(struct notifier_block *this, static int tps65086_restart_probe(struct platform_device *pdev) { - struct tps65086_restart *tps65086_restart; - int ret; - - tps65086_restart = devm_kzalloc(&pdev->dev, sizeof(*tps65086_restart), GFP_KERNEL); - if (!tps65086_restart) - return -ENOMEM; - - platform_set_drvdata(pdev, tps65086_restart); - - tps65086_restart->handler.notifier_call = tps65086_restart_notify; - tps65086_restart->handler.priority = 192; - tps65086_restart->dev = &pdev->dev; - - ret = register_restart_handler(&tps65086_restart->handler); - if (ret) { - dev_err(&pdev->dev, "%s: cannot register restart handler: %d\n", - __func__, ret); - return -ENODEV; - } - - return 0; -} - -static int tps65086_restart_remove(struct platform_device *pdev) -{ - struct tps65086_restart *tps65086_restart = platform_get_drvdata(pdev); - int ret; - - ret = unregister_restart_handler(&tps65086_restart->handler); - if (ret) { - dev_err(&pdev->dev, "%s: cannot unregister restart handler: %d\n", - __func__, ret); - return -ENODEV; - } + struct tps65086 *tps65086 = dev_get_drvdata(pdev->dev.parent); - return 0; + return devm_register_sys_off_handler(&pdev->dev, + SYS_OFF_MODE_RESTART, + SYS_OFF_PRIO_HIGH, + tps65086_restart_notify, + tps65086); } static const struct platform_device_id tps65086_restart_id_table[] = { @@ -88,11 +51,9 @@ static struct platform_driver tps65086_restart_driver = { .name = "tps65086-restart", }, .probe = tps65086_restart_probe, - .remove = tps65086_restart_remove, .id_table = tps65086_restart_id_table, }; module_platform_driver(tps65086_restart_driver); MODULE_AUTHOR("Emil Renner Berthing <kernel@esmil.dk>"); MODULE_DESCRIPTION("TPS65086 restart driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c index 447ffdacddf9..bb22b2db5907 100644 --- a/drivers/power/reset/vexpress-poweroff.c +++ b/drivers/power/reset/vexpress-poweroff.c @@ -7,8 +7,8 @@ #include <linux/delay.h> #include <linux/notifier.h> #include <linux/of.h> -#include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/reboot.h> #include <linux/stat.h> #include <linux/vexpress.h> @@ -108,20 +108,17 @@ static int _vexpress_register_restart_handler(struct device *dev) static int vexpress_reset_probe(struct platform_device *pdev) { - const struct of_device_id *match = - of_match_device(vexpress_reset_of_match, &pdev->dev); + enum vexpress_reset_func func; struct regmap *regmap; int ret = 0; - if (!match) - return -EINVAL; - regmap = devm_regmap_init_vexpress_config(&pdev->dev); if (IS_ERR(regmap)) return PTR_ERR(regmap); dev_set_drvdata(&pdev->dev, regmap); - switch ((enum vexpress_reset_func)match->data) { + func = (uintptr_t)device_get_match_data(&pdev->dev); + switch (func) { case FUNC_SHUTDOWN: vexpress_power_off_device = &pdev->dev; pm_power_off = vexpress_power_off; diff --git a/drivers/power/reset/xgene-reboot.c b/drivers/power/reset/xgene-reboot.c index 0b0d2fd2bd0c..b5eee19bac42 100644 --- a/drivers/power/reset/xgene-reboot.c +++ b/drivers/power/reset/xgene-reboot.c @@ -13,7 +13,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/notifier.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/of_address.h> #include <linux/platform_device.h> #include <linux/reboot.h> @@ -22,17 +22,13 @@ struct xgene_reboot_context { struct device *dev; - void *csr; + void __iomem *csr; u32 mask; - struct notifier_block restart_handler; }; -static int xgene_restart_handler(struct notifier_block *this, - unsigned long mode, void *cmd) +static int xgene_restart_handler(struct sys_off_data *data) { - struct xgene_reboot_context *ctx = - container_of(this, struct xgene_reboot_context, - restart_handler); + struct xgene_reboot_context *ctx = data->cb_data; /* Issue the reboot */ writel(ctx->mask, ctx->csr); @@ -54,23 +50,20 @@ static int xgene_reboot_probe(struct platform_device *pdev) if (!ctx) return -ENOMEM; - ctx->csr = of_iomap(dev->of_node, 0); - if (!ctx->csr) { + ctx->csr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ctx->csr)) { dev_err(dev, "can not map resource\n"); - return -ENODEV; + return PTR_ERR(ctx->csr); } if (of_property_read_u32(dev->of_node, "mask", &ctx->mask)) ctx->mask = 0xFFFFFFFF; ctx->dev = dev; - ctx->restart_handler.notifier_call = xgene_restart_handler; - ctx->restart_handler.priority = 128; - err = register_restart_handler(&ctx->restart_handler); - if (err) { - iounmap(ctx->csr); + err = devm_register_sys_off_handler(dev, SYS_OFF_MODE_RESTART, 128, + xgene_restart_handler, ctx); + if (err) dev_err(dev, "cannot register restart handler (err=%d)\n", err); - } return err; } @@ -87,9 +80,4 @@ static struct platform_driver xgene_reboot_driver = { .of_match_table = xgene_reboot_of_match, }, }; - -static int __init xgene_reboot_init(void) -{ - return platform_driver_register(&xgene_reboot_driver); -} -device_initcall(xgene_reboot_init); +builtin_platform_driver(xgene_reboot_driver); |
