lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251007-sc27xx-mfd-poweroff-v1-1-89a2f919b731@abscue.de>
Date: Tue, 07 Oct 2025 20:14:19 +0200
From: Otto Pflüger <otto.pflueger@...cue.de>
To: Lee Jones <lee@...nel.org>, Orson Zhai <orsonzhai@...il.com>, 
 Baolin Wang <baolin.wang@...ux.alibaba.com>, 
 Chunyan Zhang <zhang.lyra@...il.com>, Mark Brown <broonie@...nel.org>, 
 Sebastian Reichel <sre@...nel.org>
Cc: Rob Herring <robh@...nel.org>, linux-kernel@...r.kernel.org, 
 linux-spi@...r.kernel.org, linux-pm@...r.kernel.org, 
 Otto Pflüger <otto.pflueger@...cue.de>
Subject: [PATCH 1/3] mfd: sprd-sc27xx: Integrate power off and reboot
 support

The SC27xx PMICs allow restarting and powering off the device. Since
this functionality is rather simple and not configurable in any way,
make it part of the main PMIC driver.

Signed-off-by: Otto Pflüger <otto.pflueger@...cue.de>
---
 drivers/mfd/sprd-sc27xx-spi.c | 152 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 152 insertions(+)

diff --git a/drivers/mfd/sprd-sc27xx-spi.c b/drivers/mfd/sprd-sc27xx-spi.c
index d6b4350779e6aecfa19d9fa21b9174447d589e33..28dc82002e3c8eb7748c45f9f1bdadea4feb8206 100644
--- a/drivers/mfd/sprd-sc27xx-spi.c
+++ b/drivers/mfd/sprd-sc27xx-spi.c
@@ -10,6 +10,7 @@
 #include <linux/mfd/sc27xx-pmic.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
+#include <linux/reboot.h>
 #include <linux/regmap.h>
 #include <linux/spi/spi.h>
 #include <uapi/linux/usb/charger.h>
@@ -21,10 +22,48 @@
 #define SPRD_SC2730_IRQ_BASE		0x80
 #define SPRD_SC2730_IRQ_NUMS		10
 #define SPRD_SC2730_CHG_DET		0x1b9c
+
+#define SPRD_SC2730_PWR_PD_HW		0x1820
+#define SPRD_SC2730_SOFT_RST_HW		0x1824
+#define SPRD_SC2730_SLP_CTRL		0x1a48
+#define SPRD_SC2730_RST_STATUS		0x1bac
+#define SPRD_SC2730_SWRST_CTRL0		0x1bf8
+
 #define SPRD_SC2731_IRQ_BASE		0x140
 #define SPRD_SC2731_IRQ_NUMS		16
 #define SPRD_SC2731_CHG_DET		0xedc
 
+#define SPRD_SC2731_PWR_PD_HW		0xc2c
+#define SPRD_SC2731_SLP_CTRL		0xdf0
+#define SPRD_SC2731_RST_STATUS		0xee8
+
+/* PMIC power off and reset definition */
+#define SPRD_SC2730_LDO_XTL_EN		BIT(2)
+#define SPRD_SC2730_SLP_LDO_PD_EN	BIT(0)
+
+#define SPRD_SC2731_LDO_XTL_EN		BIT(3)
+#define SPRD_SC2731_SLP_LDO_PD_EN	BIT(0)
+
+#define SPRD_PMIC_PWR_OFF		BIT(0)
+#define SPRD_PMIC_RESET			BIT(0)
+#define SPRD_PMIC_SOFT_RST_EN		BIT(4)
+
+#define HWRST_STATUS_SECURITY		0x02
+#define HWRST_STATUS_RECOVERY		0x20
+#define HWRST_STATUS_NORMAL		0x40
+#define HWRST_STATUS_ALARM		0x50
+#define HWRST_STATUS_SLEEP		0x60
+#define HWRST_STATUS_FASTBOOT		0x30
+#define HWRST_STATUS_SPECIAL		0x70
+#define HWRST_STATUS_PANIC		0x80
+#define HWRST_STATUS_CFTREBOOT		0x90
+#define HWRST_STATUS_AUTODLOADER	0xa0
+#define HWRST_STATUS_IQMODE		0xb0
+#define HWRST_STATUS_SPRDISK		0xc0
+#define HWRST_STATUS_FACTORYTEST	0xe0
+#define HWRST_STATUS_WATCHDOG		0xf0
+#define HWRST_STATUS_MASK		0xff
+
 /* PMIC charger detection definition */
 #define SPRD_PMIC_CHG_DET_DELAY_US	200000
 #define SPRD_PMIC_CHG_DET_TIMEOUT	2000000
@@ -48,6 +87,14 @@ struct sprd_pmic_data {
 	u32 irq_base;
 	u32 num_irqs;
 	u32 charger_det;
+
+	u32 poweroff_reg;
+	u32 slp_ctrl_reg;
+	u32 slp_ctrl_mask;
+
+	u32 reset_reg;
+	u32 rst_sts_reg;
+	u32 swrst_ctrl_reg;
 };
 
 /*
@@ -59,12 +106,26 @@ static const struct sprd_pmic_data sc2730_data = {
 	.irq_base = SPRD_SC2730_IRQ_BASE,
 	.num_irqs = SPRD_SC2730_IRQ_NUMS,
 	.charger_det = SPRD_SC2730_CHG_DET,
+
+	.poweroff_reg = SPRD_SC2730_PWR_PD_HW,
+	.slp_ctrl_reg = SPRD_SC2730_SLP_CTRL,
+	.slp_ctrl_mask = SPRD_SC2730_LDO_XTL_EN | SPRD_SC2730_SLP_LDO_PD_EN,
+
+	.reset_reg = SPRD_SC2730_SOFT_RST_HW,
+	.rst_sts_reg = SPRD_SC2730_RST_STATUS,
+	.swrst_ctrl_reg = SPRD_SC2730_SWRST_CTRL0,
 };
 
 static const struct sprd_pmic_data sc2731_data = {
 	.irq_base = SPRD_SC2731_IRQ_BASE,
 	.num_irqs = SPRD_SC2731_IRQ_NUMS,
 	.charger_det = SPRD_SC2731_CHG_DET,
+
+	.poweroff_reg = SPRD_SC2731_PWR_PD_HW,
+	.slp_ctrl_reg = SPRD_SC2731_SLP_CTRL,
+	.slp_ctrl_mask = SPRD_SC2731_LDO_XTL_EN | SPRD_SC2731_SLP_LDO_PD_EN,
+
+	.rst_sts_reg = SPRD_SC2731_RST_STATUS,
 };
 
 enum usb_charger_type sprd_pmic_detect_charger_type(struct device *dev)
@@ -149,6 +210,79 @@ static const struct regmap_config sprd_pmic_config = {
 	.max_register = 0xffff,
 };
 
+static int sprd_pmic_poweroff(struct sys_off_data *off_data)
+{
+	struct sprd_pmic *ddata = off_data->cb_data;
+	const struct sprd_pmic_data *pdata = ddata->pdata;
+
+	regmap_clear_bits(ddata->regmap, pdata->slp_ctrl_reg,
+			  pdata->slp_ctrl_mask);
+
+	regmap_write(ddata->regmap, pdata->poweroff_reg, SPRD_PMIC_PWR_OFF);
+
+	mdelay(1000);
+
+	pr_emerg("Unable to poweroff system\n");
+
+	return NOTIFY_DONE;
+}
+
+static int sprd_pmic_restart(struct sys_off_data *off_data)
+{
+	struct sprd_pmic *ddata = off_data->cb_data;
+	const struct sprd_pmic_data *pdata = ddata->pdata;
+	u32 reboot_mode;
+
+	if (!off_data->cmd)
+		reboot_mode = HWRST_STATUS_NORMAL;
+	else if (!strcmp(off_data->cmd, "recovery"))
+		reboot_mode = HWRST_STATUS_RECOVERY;
+	else if (!strcmp(off_data->cmd, "alarm"))
+		reboot_mode = HWRST_STATUS_ALARM;
+	else if (!strcmp(off_data->cmd, "fastsleep"))
+		reboot_mode = HWRST_STATUS_SLEEP;
+	else if (!strcmp(off_data->cmd, "bootloader"))
+		reboot_mode = HWRST_STATUS_FASTBOOT;
+	else if (!strcmp(off_data->cmd, "panic"))
+		reboot_mode = HWRST_STATUS_PANIC;
+	else if (!strcmp(off_data->cmd, "special"))
+		reboot_mode = HWRST_STATUS_SPECIAL;
+	else if (!strcmp(off_data->cmd, "cftreboot"))
+		reboot_mode = HWRST_STATUS_CFTREBOOT;
+	else if (!strcmp(off_data->cmd, "autodloader"))
+		reboot_mode = HWRST_STATUS_AUTODLOADER;
+	else if (!strcmp(off_data->cmd, "iqmode"))
+		reboot_mode = HWRST_STATUS_IQMODE;
+	else if (!strcmp(off_data->cmd, "sprdisk"))
+		reboot_mode = HWRST_STATUS_SPRDISK;
+	else if (!strcmp(off_data->cmd, "tospanic"))
+		reboot_mode = HWRST_STATUS_SECURITY;
+	else if (!strcmp(off_data->cmd, "factorytest"))
+		reboot_mode = HWRST_STATUS_FACTORYTEST;
+	else
+		reboot_mode = HWRST_STATUS_NORMAL;
+
+	regmap_update_bits(ddata->regmap, pdata->rst_sts_reg,
+			   HWRST_STATUS_MASK, reboot_mode);
+
+	/*
+	 * On SC2731, this part is skipped because there is no reset register
+	 * and the restart must be performed using the watchdog.
+	 */
+	if (pdata->reset_reg) {
+		regmap_set_bits(ddata->regmap, pdata->swrst_ctrl_reg,
+				SPRD_PMIC_SOFT_RST_EN);
+
+		regmap_write(ddata->regmap, pdata->reset_reg, SPRD_PMIC_RESET);
+
+		mdelay(1000);
+
+		pr_emerg("Unable to restart system\n");
+	}
+
+	return NOTIFY_DONE;
+}
+
 static int sprd_pmic_probe(struct spi_device *spi)
 {
 	struct sprd_pmic *ddata;
@@ -204,6 +338,24 @@ static int sprd_pmic_probe(struct spi_device *spi)
 		return ret;
 	}
 
+	ret = devm_register_sys_off_handler(&spi->dev, SYS_OFF_MODE_RESTART,
+					    192, sprd_pmic_restart, ddata);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to register restart handler: %d\n",
+			ret);
+		return ret;
+	}
+
+	ret = devm_register_sys_off_handler(&spi->dev,
+					    SYS_OFF_MODE_POWER_OFF,
+					    SYS_OFF_PRIO_DEFAULT,
+					    sprd_pmic_poweroff, ddata);
+	if (ret) {
+		dev_err(&spi->dev, "Failed to register poweroff handler: %d\n",
+			ret);
+		return ret;
+	}
+
 	ret = devm_of_platform_populate(&spi->dev);
 	if (ret) {
 		dev_err(&spi->dev, "Failed to populate sub-devices %d\n", ret);

-- 
2.50.0


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ