Devicetree
 help / color / mirror / Atom feed
* [PATCH v2 0/2] PM: spacemit: Add power domain support
@ 2026-10-08  3:44 Yixun Lan
  2026-10-08  3:44 ` [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs Yixun Lan
  2026-10-08  3:44 ` [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver Yixun Lan
  0 siblings, 2 replies; 8+ messages in thread
From: Yixun Lan @ 2026-10-08  3:44 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Icenowy Zheng, Iker Pedrosa, devicetree, linux-riscv, spacemit,
	linux-pm, linux-kernel, Yixun Lan

In this series, we try to add a Power Domain driver support for SpacemiT
K1/K3 SoCs, the driver implement the power-switch functionaly which support
power on/off, and it's based on Linux Generic PM Domain framework and
similiar to vendor K1's implementation, but unlike the K3's vendor
version which is more complicated.

  The DTS patch is obsolete, so drop it. For devices required PM property,
the DT part can be sent in separate patch.

  In order to test this series, a prerequisite CCU patches are needed,
which can be found here [1].

Link: https://lore.kernel.org/spacemit/20261008-04-k3-clk-pm-aux-v1-0-8372d7307694@kernel.org/ [1]
Signed-off-by: Yixun Lan <dlan@kernel.org>
---
Changes in v2:
- convert to auxilary driver
- no need dt-binding doc, drop it
- drop dts patch
- support to build as module
- set always on for K3 audio pm 
- no global variable for struct spacemit_pmu
- no force to do power-off in spacemit_pd_power_on()
- check return value of pm_genpd_init()
- adjust license header format
- Link to v1: https://patch.msgid.link/20260918-04-k3-pm-support-v1-0-0acd2b36b96f@kernel.org

---
Yixun Lan (2):
      dt-bindings: power: spacemit: Add power domain controller IDs
      pmdomain: spacemit: Add power domain driver

 drivers/pmdomain/Kconfig                        |   1 +
 drivers/pmdomain/Makefile                       |   1 +
 drivers/pmdomain/spacemit/Kconfig               |  21 ++
 drivers/pmdomain/spacemit/Makefile              |   1 +
 drivers/pmdomain/spacemit/spacemit-pm-domains.c | 413 ++++++++++++++++++++++++
 include/dt-bindings/power/spacemit,k1-power.h   |  14 +
 include/dt-bindings/power/spacemit,k3-power.h   |  12 +
 7 files changed, 463 insertions(+)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260909-04-k3-pm-support-0bef40e87fd7

Best regards,
--  
Yixun Lan <dlan@kernel.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs
  2026-10-08  3:44 [PATCH v2 0/2] PM: spacemit: Add power domain support Yixun Lan
@ 2026-10-08  3:44 ` Yixun Lan
  2026-10-08  7:27   ` Conor Dooley
  2026-10-08  3:44 ` [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver Yixun Lan
  1 sibling, 1 reply; 8+ messages in thread
From: Yixun Lan @ 2026-10-08  3:44 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Icenowy Zheng, Iker Pedrosa, devicetree, linux-riscv, spacemit,
	linux-pm, linux-kernel, Yixun Lan

Add initial power domain driver support for SpacemiT K1/K3 SoC, The Power
Domain Controller is SoC internal module which is part of APMU (Advanced
Power Management Unit), it is responsible for managing power across multi
power domains, which includes power on/off, isolation, power-on sequence.

Signed-off-by: Yixun Lan <dlan@kernel.org>
---
 include/dt-bindings/power/spacemit,k1-power.h | 14 ++++++++++++++
 include/dt-bindings/power/spacemit,k3-power.h | 12 ++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/include/dt-bindings/power/spacemit,k1-power.h b/include/dt-bindings/power/spacemit,k1-power.h
new file mode 100644
index 000000000000..41ef540b5c44
--- /dev/null
+++ b/include/dt-bindings/power/spacemit,k1-power.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_SPACEMIT_K1_POWER_H_
+#define _DT_BINDINGS_SPACEMIT_K1_POWER_H_
+
+#define K1_PMDOMAIN_VPU		0
+#define K1_PMDOMAIN_GPU		1
+#define K1_PMDOMAIN_LCD		2
+#define K1_PMDOMAIN_ISP		3
+#define K1_PMDOMAIN_AUDIO	4
+#define K1_PMDOMAIN_GNSS	5
+#define K1_PMDOMAIN_HDMI	6
+
+#endif
diff --git a/include/dt-bindings/power/spacemit,k3-power.h b/include/dt-bindings/power/spacemit,k3-power.h
new file mode 100644
index 000000000000..519fbf946312
--- /dev/null
+++ b/include/dt-bindings/power/spacemit,k3-power.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
+
+#ifndef _DT_BINDINGS_SPACEMIT_K3_POWER_H_
+#define _DT_BINDINGS_SPACEMIT_K3_POWER_H_
+
+#define K3_PMDOMAIN_VPU		0
+#define K3_PMDOMAIN_GPU		1
+#define K3_PMDOMAIN_AUDIO	2
+#define K3_PMDOMAIN_LCD0	3
+#define K3_PMDOMAIN_LCD1	4
+
+#endif

-- 
2.56.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver
  2026-10-08  3:44 [PATCH v2 0/2] PM: spacemit: Add power domain support Yixun Lan
  2026-10-08  3:44 ` [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs Yixun Lan
@ 2026-10-08  3:44 ` Yixun Lan
  2026-10-08  3:55   ` sashiko-bot
  1 sibling, 1 reply; 8+ messages in thread
From: Yixun Lan @ 2026-10-08  3:44 UTC (permalink / raw)
  To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti
  Cc: Icenowy Zheng, Iker Pedrosa, devicetree, linux-riscv, spacemit,
	linux-pm, linux-kernel, Yixun Lan

  SpacemiT's PMU (Power Management Unit) consist of several power domains
which can be managed independently, depending on different application
scenario, each domain can be powered on/off for saving power.

  The driver is implemented based on Linux Generic PM Domain framework.
From a hardware perspective, either of two distinct power-on sequences
are supported, in software mode (SW mode), the driver is responsible for
controlling the states of bits such as sleep1, sleep2, isolation and
pwr_state, while in hardware mode (HW mode), the PMU hardware will complete
the sequence automatically without requiring software intervention.

Signed-off-by: Yixun Lan <dlan@kernel.org>
---
 drivers/pmdomain/Kconfig                        |   1 +
 drivers/pmdomain/Makefile                       |   1 +
 drivers/pmdomain/spacemit/Kconfig               |  21 ++
 drivers/pmdomain/spacemit/Makefile              |   1 +
 drivers/pmdomain/spacemit/spacemit-pm-domains.c | 413 ++++++++++++++++++++++++
 5 files changed, 437 insertions(+)

diff --git a/drivers/pmdomain/Kconfig b/drivers/pmdomain/Kconfig
index 23076ae90e66..0610acd030f6 100644
--- a/drivers/pmdomain/Kconfig
+++ b/drivers/pmdomain/Kconfig
@@ -13,6 +13,7 @@ source "drivers/pmdomain/qcom/Kconfig"
 source "drivers/pmdomain/renesas/Kconfig"
 source "drivers/pmdomain/rockchip/Kconfig"
 source "drivers/pmdomain/samsung/Kconfig"
+source "drivers/pmdomain/spacemit/Kconfig"
 source "drivers/pmdomain/st/Kconfig"
 source "drivers/pmdomain/starfive/Kconfig"
 source "drivers/pmdomain/sunxi/Kconfig"
diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile
index ebc802f13eb9..ba25b444e263 100644
--- a/drivers/pmdomain/Makefile
+++ b/drivers/pmdomain/Makefile
@@ -11,6 +11,7 @@ obj-y					+= qcom/
 obj-y					+= renesas/
 obj-y					+= rockchip/
 obj-y					+= samsung/
+obj-y					+= spacemit/
 obj-y					+= st/
 obj-y					+= starfive/
 obj-y					+= sunxi/
diff --git a/drivers/pmdomain/spacemit/Kconfig b/drivers/pmdomain/spacemit/Kconfig
new file mode 100644
index 000000000000..0dce5eef8da8
--- /dev/null
+++ b/drivers/pmdomain/spacemit/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+menu "SpacemiT PM Domains"
+
+config SPACEMIT_PM_DOMAINS
+	tristate "PM Domain driver for SpacemiT SoC"
+	depends on ARCH_SPACEMIT || COMPILE_TEST
+	depends on PM && OF
+	select PM_GENERIC_DOMAINS
+	select PM_GENERIC_DOMAINS_OF
+	select AUXILIARY_BUS
+	help
+	  This driver enables power domain management for the SpacemiT
+	  SoC. On the SoC there are number of power domains available,
+	  which can be managed independently. For example VPU, GPU, LCD,
+	  and Audio reside in their own power domains which can be
+	  turned on/off.
+
+	  Say y to enable device power management through PM domains.
+
+endmenu
diff --git a/drivers/pmdomain/spacemit/Makefile b/drivers/pmdomain/spacemit/Makefile
new file mode 100644
index 000000000000..d0bd77a520c0
--- /dev/null
+++ b/drivers/pmdomain/spacemit/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SPACEMIT_PM_DOMAINS)	+= spacemit-pm-domains.o
diff --git a/drivers/pmdomain/spacemit/spacemit-pm-domains.c b/drivers/pmdomain/spacemit/spacemit-pm-domains.c
new file mode 100644
index 000000000000..8fbe3b953464
--- /dev/null
+++ b/drivers/pmdomain/spacemit/spacemit-pm-domains.c
@@ -0,0 +1,413 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Spacemit Generic power domain support.
+ *
+ * Copyright (c) 2026 SpacemiT Technology Co. Ltd
+ */
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/regmap.h>
+#include <linux/auxiliary_bus.h>
+#include <dt-bindings/power/spacemit,k1-power.h>
+#include <dt-bindings/power/spacemit,k3-power.h>
+#include <soc/spacemit/ccu.h>
+
+#define APMU_POWER_STATUS_REG	0xf0
+#define APMU_POWER_TIMEOUT_US	10000
+
+struct spacemit_pm_domain_param {
+	int reg_pwr_ctrl;
+	int bit_hw_mode;
+	int bit_sleep2;
+	int bit_sleep1;
+	int bit_isolation;
+	int bit_auto_pwr_on;
+	int bit_hw_pwr_stat;
+	int bit_pwr_stat;
+	int use_hw;
+	unsigned int flags;
+	const char *name;
+};
+
+struct spacemit_pm_domain {
+	struct generic_pm_domain genpd;
+	struct spacemit_pmu *gpmu;
+	int pm_index;
+	const struct spacemit_pm_domain_param *param;
+};
+
+struct spacemit_pmu {
+	struct device *dev;
+	struct genpd_onecell_data genpd_data;
+	struct regmap *regmap;
+	struct spacemit_pm_domain **domains;
+	int num_domains;
+};
+
+struct spacemit_pm_aux_data {
+	int num_domains;
+	const struct spacemit_pm_domain_param *param;
+};
+
+static int spacemit_pd_power_off(struct generic_pm_domain *domain)
+{
+	struct spacemit_pm_domain *spd = container_of(domain, struct spacemit_pm_domain, genpd);
+	struct spacemit_pmu *gpmu = spd->gpmu;
+	const struct spacemit_pm_domain_param *p = spd->param;
+	unsigned int val;
+	int ret;
+
+	if (!spd->param->use_hw) {
+		regmap_clear_bits(gpmu->regmap, p->reg_pwr_ctrl, BIT(p->bit_isolation));
+		fsleep(15);
+
+		val = BIT(p->bit_sleep1) | BIT(p->bit_sleep2);
+		regmap_clear_bits(gpmu->regmap, p->reg_pwr_ctrl, val);
+		fsleep(15);
+
+		ret = regmap_read_poll_timeout(gpmu->regmap,
+					       APMU_POWER_STATUS_REG,
+					       val,
+					       !(val & BIT(p->bit_pwr_stat)),
+					       5,
+					       APMU_POWER_TIMEOUT_US);
+	} else {
+		val = BIT(p->bit_auto_pwr_on) | BIT(p->bit_hw_mode);
+		regmap_clear_bits(gpmu->regmap, p->reg_pwr_ctrl, val);
+		fsleep(15);
+
+		ret = regmap_read_poll_timeout(gpmu->regmap,
+					       APMU_POWER_STATUS_REG,
+					       val,
+					       !(val & BIT(p->bit_hw_pwr_stat)),
+					       5,
+					       APMU_POWER_TIMEOUT_US);
+	}
+
+	if (ret) {
+		dev_err(&domain->dev, "Fail to power-off domain: %s (%d)\n",
+			p->name, spd->pm_index);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static int spacemit_pd_power_on(struct generic_pm_domain *domain)
+{
+	struct spacemit_pm_domain *spd = container_of(domain, struct spacemit_pm_domain, genpd);
+	struct spacemit_pmu *gpmu = spd->gpmu;
+	const struct spacemit_pm_domain_param *p = spd->param;
+	unsigned int val;
+	int ret = 0;
+
+	if (!p->use_hw) {
+		regmap_set_bits(gpmu->regmap, p->reg_pwr_ctrl, BIT(p->bit_sleep1));
+		fsleep(20);
+
+		val = BIT(p->bit_sleep2) | BIT(p->bit_sleep1);
+		regmap_set_bits(gpmu->regmap, p->reg_pwr_ctrl, val);
+		fsleep(20);
+
+		regmap_set_bits(gpmu->regmap, p->reg_pwr_ctrl, BIT(p->bit_isolation));
+		fsleep(15);
+
+		ret = regmap_read_poll_timeout(gpmu->regmap,
+					       APMU_POWER_STATUS_REG,
+					       val,
+					       (val & BIT(p->bit_pwr_stat)),
+					       5,
+					       APMU_POWER_TIMEOUT_US);
+	} else {
+		val = BIT(p->bit_auto_pwr_on) | BIT(p->bit_hw_mode);
+		regmap_set_bits(gpmu->regmap, p->reg_pwr_ctrl, val);
+		fsleep(300);
+
+		ret = regmap_read_poll_timeout(gpmu->regmap,
+					       APMU_POWER_STATUS_REG,
+					       val,
+					       (val & BIT(p->bit_hw_pwr_stat)),
+					       5,
+					       APMU_POWER_TIMEOUT_US);
+	}
+
+	if (ret < 0) {
+		dev_err(&domain->dev, "power-on domain: %d, error\n", spd->pm_index);
+		return -EBUSY;
+	}
+
+	return 0;
+}
+
+static bool spacemit_pm_get_state(struct spacemit_pmu *pmu,
+				  struct spacemit_pm_domain *pd)
+{
+	const struct spacemit_pm_domain_param *p = pd->param;
+	u32 reg = 0, bit;
+
+	regmap_read(pmu->regmap, APMU_POWER_STATUS_REG, &reg);
+
+	bit = p->use_hw ? BIT(p->bit_hw_pwr_stat) : BIT(p->bit_pwr_stat);
+
+	return !!(reg & bit);
+}
+
+static int spacemit_pm_add_one_domain(struct spacemit_pmu *pmu, int id,
+				      const struct spacemit_pm_domain_param *param)
+{
+	struct spacemit_pm_domain *pd;
+	int ret;
+
+	pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd)
+		return -ENOMEM;
+
+	pd->pm_index        = id;
+	pd->param           = param;
+	pd->gpmu            = pmu;
+	pd->genpd.name      = param->name;
+	pd->genpd.flags     = param->flags;
+	pd->genpd.power_off = spacemit_pd_power_off;
+	pd->genpd.power_on  = spacemit_pd_power_on;
+
+	ret = pm_genpd_init(&pd->genpd, NULL, !spacemit_pm_get_state(pmu, pd));
+	if (ret)
+		return ret;
+
+	pmu->domains[id] = pd;
+
+	return 0;
+}
+
+static void spacemit_pm_domain_cleanup(struct spacemit_pmu *pmu)
+{
+	int i;
+
+	for (i = 0; i < pmu->num_domains; i++) {
+		if (pmu->domains[i])
+			pm_genpd_remove(&pmu->domains[i]->genpd);
+	}
+}
+
+static int spacemit_pm_domain_probe(struct auxiliary_device *adev,
+				    const struct auxiliary_device_id *id)
+{
+	struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
+	const struct spacemit_pm_aux_data *data;
+	struct device *dev = &adev->dev;
+	struct spacemit_pmu *pmu;
+	int err, i;
+
+	data = (const struct spacemit_pm_aux_data *)id->driver_data;
+
+	pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL);
+	if (!pmu)
+		return -ENOMEM;
+
+	pmu->dev = dev;
+	pmu->regmap = rdev->regmap;
+	pmu->num_domains = data->num_domains;
+
+	pmu->domains = devm_kcalloc(dev, data->num_domains,
+				    sizeof(*pmu->domains), GFP_KERNEL);
+	if (!pmu->domains)
+		return -ENOMEM;
+
+	for (i = 0; i < data->num_domains; i++) {
+		err = spacemit_pm_add_one_domain(pmu, i, &data->param[i]);
+		if (err) {
+			dev_err(dev, "failed to add domain %d: %d\n", i, err);
+			goto err_out;
+		}
+	}
+
+	pmu->genpd_data.domains     = (struct generic_pm_domain **)pmu->domains;
+	pmu->genpd_data.num_domains = data->num_domains;
+
+	err = of_genpd_add_provider_onecell(dev->of_node, &pmu->genpd_data);
+	if (err) {
+		dev_err(dev, "failed to add provider: %d\n", err);
+		goto err_out;
+	}
+
+	return 0;
+
+err_out:
+	spacemit_pm_domain_cleanup(pmu);
+	return err;
+}
+
+static const struct spacemit_pm_domain_param k1_domain_params[] = {
+	[K1_PMDOMAIN_VPU] = {
+		.reg_pwr_ctrl		= 0xa8,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_pwr_stat		= 1,
+		.bit_hw_pwr_stat	= 9,
+		.name			= "vpu",
+	},
+	[K1_PMDOMAIN_GPU] = {
+		.reg_pwr_ctrl		= 0xd0,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_pwr_stat		= 0,
+		.name			= "gpu",
+	},
+	[K1_PMDOMAIN_LCD] = {
+		.reg_pwr_ctrl		= 0x380,
+		.bit_hw_mode		= 4,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_auto_pwr_on	= 0,
+		.bit_pwr_stat		= 4,
+		.bit_hw_pwr_stat	= 12,
+		.use_hw			= 1,
+		.name			= "lcd",
+	},
+	[K1_PMDOMAIN_ISP] = {
+		.reg_pwr_ctrl		= 0x37c,
+		.bit_hw_mode		= 4,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_auto_pwr_on	= 0,
+		.bit_pwr_stat		= 2,
+		.bit_hw_pwr_stat	= 10,
+		.name			= "isp",
+	},
+	[K1_PMDOMAIN_AUDIO] = {
+		.reg_pwr_ctrl		= 0x378,
+		.bit_hw_mode		= 4,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_auto_pwr_on	= 0,
+		.bit_pwr_stat		= 3,
+		.bit_hw_pwr_stat	= 11,
+		.use_hw			= 1,
+		.name			= "audio",
+	},
+	[K1_PMDOMAIN_GNSS] = {
+		.reg_pwr_ctrl		= 0x13c,
+		.bit_hw_mode		= 4,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_auto_pwr_on	= 0,
+		.bit_pwr_stat		= 6,
+		.bit_hw_pwr_stat	= 14,
+		.name			= "gnss",
+	},
+	[K1_PMDOMAIN_HDMI] = {
+		.reg_pwr_ctrl		= 0x3f4,
+		.bit_hw_mode		= 4,
+		.bit_sleep2		= 3,
+		.bit_sleep1		= 2,
+		.bit_isolation		= 1,
+		.bit_auto_pwr_on	= 0,
+		.bit_pwr_stat		= 7,
+		.bit_hw_pwr_stat	= 15,
+		.use_hw			= 1,
+		.name			= "hdmi",
+	},
+};
+
+static const struct spacemit_pm_domain_param k3_domain_params[] = {
+	[K3_PMDOMAIN_VPU] = {
+		.bit_auto_pwr_on	= 0,
+		.bit_isolation		= 1,
+		.bit_sleep1		= 2,
+		.bit_sleep2		= 3,
+		.bit_hw_mode		= 4,
+		.bit_pwr_stat		= 2,
+		.bit_hw_pwr_stat	= 9,
+		.use_hw			= 1,
+		.reg_pwr_ctrl		= 0xa8,
+		.name			= "vpu",
+	},
+	[K3_PMDOMAIN_GPU] = {
+		.bit_auto_pwr_on	= 0,
+		.bit_isolation		= 1,
+		.bit_sleep1		= 2,
+		.bit_sleep2		= 3,
+		.bit_hw_mode		= 4,
+		.bit_pwr_stat		= 0,
+		.bit_hw_pwr_stat	= 8,
+		.use_hw			= 1,
+		.reg_pwr_ctrl		= 0xd0,
+		.name			= "gpu",
+	},
+	[K3_PMDOMAIN_AUDIO] = {
+		.bit_auto_pwr_on	= 0,
+		.bit_isolation		= 1,
+		.bit_sleep1		= 2,
+		.bit_sleep2		= 3,
+		.bit_hw_mode		= 4,
+		.bit_pwr_stat		= 3,
+		.reg_pwr_ctrl		= 0x378,
+		.flags			= GENPD_FLAG_ALWAYS_ON,
+		.name			= "audio",
+	},
+	[K3_PMDOMAIN_LCD0] = {
+		.bit_auto_pwr_on	= 0,
+		.bit_isolation		= 1,
+		.bit_sleep1		= 2,
+		.bit_sleep2		= 3,
+		.bit_hw_mode		= 4,
+		.bit_pwr_stat		= 4,
+		.bit_hw_pwr_stat	= 12,
+		.use_hw			= 1,
+		.reg_pwr_ctrl		= 0x380,
+		.name			= "lcd0",
+	},
+	[K3_PMDOMAIN_LCD1] = {
+		.bit_auto_pwr_on	= 0,
+		.bit_isolation		= 1,
+		.bit_sleep1		= 2,
+		.bit_sleep2		= 3,
+		.bit_hw_mode		= 4,
+		.bit_pwr_stat		= 5,
+		.bit_hw_pwr_stat	= 15,
+		.use_hw			= 1,
+		.reg_pwr_ctrl		= 0x3f4,
+		.name			= "lcd1",
+	},
+};
+
+static const struct spacemit_pm_aux_data k1_of_data = {
+	.num_domains	= ARRAY_SIZE(k1_domain_params),
+	.param		= k1_domain_params,
+};
+
+static const struct spacemit_pm_aux_data k3_of_data = {
+	.num_domains	= ARRAY_SIZE(k3_domain_params),
+	.param		= k3_domain_params,
+};
+
+static const struct auxiliary_device_id spacemit_pm_domain_ids[] = {
+	{
+		.name = "spacemit_ccu.k1-apmu-pmdomain",
+		.driver_data = (kernel_ulong_t)&k1_of_data,
+	},
+	{
+		.name = "spacemit_ccu.k3-apmu-pmdomain",
+		.driver_data = (kernel_ulong_t)&k3_of_data,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(auxiliary, spacemit_pm_domain_ids);
+
+static struct auxiliary_driver spacemit_pm_domain_driver = {
+	.probe          = spacemit_pm_domain_probe,
+	.id_table       = spacemit_pm_domain_ids,
+};
+module_auxiliary_driver(spacemit_pm_domain_driver);
+
+MODULE_DESCRIPTION("SpacemiT Generic Power Domains driver");
+MODULE_LICENSE("GPL");

-- 
2.56.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver
  2026-10-08  3:44 ` [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver Yixun Lan
@ 2026-10-08  3:55   ` sashiko-bot
  2026-10-08  8:51     ` Yixun Lan
  0 siblings, 1 reply; 8+ messages in thread
From: sashiko-bot @ 2026-10-08  3:55 UTC (permalink / raw)
  To: Yixun Lan; +Cc: devicetree, robh, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] pmdomain: missing .remove callback causes UAF on driver unbind
- [High] pmdomain: probe fails unconditionally if ALWAYS_ON domain is powered off
--

commit ce2204cb1ba682f1391fc6691c412bb036ad43ca
Author: Yixun Lan <dlan@kernel.org>
Date:   Thu Oct 8 03:44:30 2026 +0000

pmdomain: spacemit: Add power domain driver

This commit introduces a Generic PM Domain driver for SpacemiT SoCs. It
provides support for managing power domains across different application
scenarios through software or hardware control modes.

> diff --git a/drivers/pmdomain/spacemit/spacemit-pm-domains.c b/drivers/pmdomain/spacemit/spacemit-pm-domains.c
> new file mode 100644
> --- /dev/null
> +++ b/drivers/pmdomain/spacemit/spacemit-pm-domains.c

[ ... ]

> +static int spacemit_pm_add_one_domain(struct spacemit_pmu *pmu, int id,
> +				      const struct spacemit_pm_domain_param *param)
> +{
> +	struct spacemit_pm_domain *pd;
> +	int ret;
> +
> +	pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL);
> +	if (!pd)
> +		return -ENOMEM;
> +
> +	pd->pm_index        = id;
> +	pd->param           = param;
> +	pd->gpmu            = pmu;
> +	pd->genpd.name      = param->name;
> +	pd->genpd.flags     = param->flags;
> +	pd->genpd.power_off = spacemit_pd_power_off;
> +	pd->genpd.power_on  = spacemit_pd_power_on;
> +
> +	ret = pm_genpd_init(&pd->genpd, NULL, !spacemit_pm_get_state(pmu, pd));

[Severity: High]
Will this unconditionally fail probe if the bootloader leaves the audio
domain powered off?

The k3_domain_params define the audio domain with GENPD_FLAG_ALWAYS_ON, but
here pm_genpd_init() is called with the inverse of the current hardware
state (is_off = !spacemit_pm_get_state()).

If the hardware happens to be off, is_off evaluates to true. However, the
genpd core explicitly rejects initializing always-on domains in the off
state:

drivers/pmdomain/core.c:pm_genpd_init() {
    ...
    /* Always-on domains must be powered on at initialization. */
    if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) &&
            !genpd_status_on(genpd)) {
        pr_err("always-on PM domain %s is not on\n", genpd->name);
        return -EINVAL;
    }
    ...
}

Since a failure here aborts spacemit_pm_domain_probe(), does the driver
need to explicitly power on always-on domains before passing them to the
genpd framework?

> +	if (ret)
> +		return ret;
> +
> +	pmu->domains[id] = pd;
> +
> +	return 0;
> +}

[ ... ]

> +static int spacemit_pm_domain_probe(struct auxiliary_device *adev,
> +				    const struct auxiliary_device_id *id)
> +{
> +	struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
> +	const struct spacemit_pm_aux_data *data;
> +	struct device *dev = &adev->dev;
> +	struct spacemit_pmu *pmu;
> +	int err, i;
> +
> +	data = (const struct spacemit_pm_aux_data *)id->driver_data;
> +
> +	pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL);

[Severity: High]
Could this cause a use-after-free on module unload or driver unbind?

The driver dynamically allocates the pmu structure and domain entries using
devres APIs like devm_kzalloc() and devm_kcalloc(). During probe, these
domains and the provider are registered with the genpd core using
pm_genpd_init() and of_genpd_add_provider_onecell().

However, the spacemit_pm_domain_driver below lacks a .remove callback. When
the driver is unbound, devres will automatically free the allocated pmu and
domain memory, but pm_genpd_remove() and of_genpd_del_provider() are never
called.

Does this leave dangling pointers in the core gpd_list and
of_genpd_providers lists that could panic the kernel when accessed?

> +	if (!pmu)
> +		return -ENOMEM;

[ ... ]

> +static struct auxiliary_driver spacemit_pm_domain_driver = {
> +	.probe          = spacemit_pm_domain_probe,
> +	.id_table       = spacemit_pm_domain_ids,
> +};
> +module_auxiliary_driver(spacemit_pm_domain_driver);

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261008-04-k3-pm-support-v2-0-6f778a53dd2d@kernel.org?part=2

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs
  2026-10-08  3:44 ` [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs Yixun Lan
@ 2026-10-08  7:27   ` Conor Dooley
  2026-10-08  9:15     ` Yixun Lan
  2026-10-08 10:30     ` Icenowy Zheng
  0 siblings, 2 replies; 8+ messages in thread
From: Conor Dooley @ 2026-10-08  7:27 UTC (permalink / raw)
  To: Yixun Lan
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Icenowy Zheng, Iker Pedrosa, devicetree, linux-riscv, spacemit,
	linux-pm, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 2068 bytes --]

On Thu, Oct 08, 2026 at 03:44:29AM +0000, Yixun Lan wrote:
> Add initial power domain driver support for SpacemiT K1/K3 SoC, The Power
> Domain Controller is SoC internal module which is part of APMU (Advanced
> Power Management Unit), it is responsible for managing power across multi
> power domains, which includes power on/off, isolation, power-on sequence.

You should prob mention that this APMU is already documented elsewhere.
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: not-applicable

Thanks,
Conor.

> 
> Signed-off-by: Yixun Lan <dlan@kernel.org>
> ---
>  include/dt-bindings/power/spacemit,k1-power.h | 14 ++++++++++++++
>  include/dt-bindings/power/spacemit,k3-power.h | 12 ++++++++++++
>  2 files changed, 26 insertions(+)
> 
> diff --git a/include/dt-bindings/power/spacemit,k1-power.h b/include/dt-bindings/power/spacemit,k1-power.h
> new file mode 100644
> index 000000000000..41ef540b5c44
> --- /dev/null
> +++ b/include/dt-bindings/power/spacemit,k1-power.h
> @@ -0,0 +1,14 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +
> +#ifndef _DT_BINDINGS_SPACEMIT_K1_POWER_H_
> +#define _DT_BINDINGS_SPACEMIT_K1_POWER_H_
> +
> +#define K1_PMDOMAIN_VPU		0
> +#define K1_PMDOMAIN_GPU		1
> +#define K1_PMDOMAIN_LCD		2
> +#define K1_PMDOMAIN_ISP		3
> +#define K1_PMDOMAIN_AUDIO	4
> +#define K1_PMDOMAIN_GNSS	5
> +#define K1_PMDOMAIN_HDMI	6
> +
> +#endif
> diff --git a/include/dt-bindings/power/spacemit,k3-power.h b/include/dt-bindings/power/spacemit,k3-power.h
> new file mode 100644
> index 000000000000..519fbf946312
> --- /dev/null
> +++ b/include/dt-bindings/power/spacemit,k3-power.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> +
> +#ifndef _DT_BINDINGS_SPACEMIT_K3_POWER_H_
> +#define _DT_BINDINGS_SPACEMIT_K3_POWER_H_
> +
> +#define K3_PMDOMAIN_VPU		0
> +#define K3_PMDOMAIN_GPU		1
> +#define K3_PMDOMAIN_AUDIO	2
> +#define K3_PMDOMAIN_LCD0	3
> +#define K3_PMDOMAIN_LCD1	4
> +
> +#endif
> 
> -- 
> 2.56.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver
  2026-10-08  3:55   ` sashiko-bot
@ 2026-10-08  8:51     ` Yixun Lan
  0 siblings, 0 replies; 8+ messages in thread
From: Yixun Lan @ 2026-10-08  8:51 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: devicetree, robh, conor+dt, spacemit

Hi ALL,


On 03:55 Thu 08 Oct     , sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [High] pmdomain: missing .remove callback causes UAF on driver unbind
> - [High] pmdomain: probe fails unconditionally if ALWAYS_ON domain is powered off
> --
> 
> commit ce2204cb1ba682f1391fc6691c412bb036ad43ca
> Author: Yixun Lan <dlan@kernel.org>
> Date:   Thu Oct 8 03:44:30 2026 +0000
> 
> pmdomain: spacemit: Add power domain driver
> 
> This commit introduces a Generic PM Domain driver for SpacemiT SoCs. It
> provides support for managing power domains across different application
> scenarios through software or hardware control modes.
> 
> > diff --git a/drivers/pmdomain/spacemit/spacemit-pm-domains.c b/drivers/pmdomain/spacemit/spacemit-pm-domains.c
> > new file mode 100644
> > --- /dev/null
> > +++ b/drivers/pmdomain/spacemit/spacemit-pm-domains.c
> 
> [ ... ]
> 
> > +static int spacemit_pm_add_one_domain(struct spacemit_pmu *pmu, int id,
> > +				      const struct spacemit_pm_domain_param *param)
> > +{
> > +	struct spacemit_pm_domain *pd;
> > +	int ret;
> > +
> > +	pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL);
> > +	if (!pd)
> > +		return -ENOMEM;
> > +
> > +	pd->pm_index        = id;
> > +	pd->param           = param;
> > +	pd->gpmu            = pmu;
> > +	pd->genpd.name      = param->name;
> > +	pd->genpd.flags     = param->flags;
> > +	pd->genpd.power_off = spacemit_pd_power_off;
> > +	pd->genpd.power_on  = spacemit_pd_power_on;
> > +
> > +	ret = pm_genpd_init(&pd->genpd, NULL, !spacemit_pm_get_state(pmu, pd));
> 
> [Severity: High]
> Will this unconditionally fail probe if the bootloader leaves the audio
> domain powered off?
> 
I've checked, the bootloader always set audio domain on before linux starts

> The k3_domain_params define the audio domain with GENPD_FLAG_ALWAYS_ON, but
> here pm_genpd_init() is called with the inverse of the current hardware
> state (is_off = !spacemit_pm_get_state()).
> 
this is still correct, but the name of function makes people a bit confused,
 I could change it to a more obvious one to match pm_genpd_init(.., is_off),
probably spacemit_pm_is_off_state()

> If the hardware happens to be off, is_off evaluates to true. However, the
> genpd core explicitly rejects initializing always-on domains in the off
> state:
> 
> drivers/pmdomain/core.c:pm_genpd_init() {
>     ...
>     /* Always-on domains must be powered on at initialization. */
>     if ((genpd_is_always_on(genpd) || genpd_is_rpm_always_on(genpd)) &&
>             !genpd_status_on(genpd)) {
>         pr_err("always-on PM domain %s is not on\n", genpd->name);
>         return -EINVAL;
>     }
>     ...
> }
> 
> Since a failure here aborts spacemit_pm_domain_probe(), does the driver
> need to explicitly power on always-on domains before passing them to the
> genpd framework?
> 
The case said here won't happen, and the system will be in unusable state
if audio domain in a 'off' state, so leave kernel yell and abort, that's
expected.

> > +	if (ret)
> > +		return ret;
> > +
> > +	pmu->domains[id] = pd;
> > +
> > +	return 0;
> > +}
> 
> [ ... ]
> 
> > +static int spacemit_pm_domain_probe(struct auxiliary_device *adev,
> > +				    const struct auxiliary_device_id *id)
> > +{
> > +	struct spacemit_ccu_adev *rdev = to_spacemit_ccu_adev(adev);
> > +	const struct spacemit_pm_aux_data *data;
> > +	struct device *dev = &adev->dev;
> > +	struct spacemit_pmu *pmu;
> > +	int err, i;
> > +
> > +	data = (const struct spacemit_pm_aux_data *)id->driver_data;
> > +
> > +	pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL);
> 
> [Severity: High]
> Could this cause a use-after-free on module unload or driver unbind?
> 
> The driver dynamically allocates the pmu structure and domain entries using
> devres APIs like devm_kzalloc() and devm_kcalloc(). During probe, these
> domains and the provider are registered with the genpd core using
> pm_genpd_init() and of_genpd_add_provider_onecell().
> 
> However, the spacemit_pm_domain_driver below lacks a .remove callback. When
> the driver is unbound, devres will automatically free the allocated pmu and
> domain memory, but pm_genpd_remove() and of_genpd_del_provider() are never
> called.
> 
> Does this leave dangling pointers in the core gpd_list and
> of_genpd_providers lists that could panic the kernel when accessed?
> 
That's something I will fix in next version, I've changed the driver to
support be built as a module.. while not implement a complete remove() function

> > +	if (!pmu)
> > +		return -ENOMEM;
> 
> [ ... ]
> 
> > +static struct auxiliary_driver spacemit_pm_domain_driver = {
> > +	.probe          = spacemit_pm_domain_probe,
> > +	.id_table       = spacemit_pm_domain_ids,
> > +};
> > +module_auxiliary_driver(spacemit_pm_domain_driver);
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/20261008-04-k3-pm-support-v2-0-6f778a53dd2d@kernel.org?part=2

-- 
Yixun Lan (dlan)

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs
  2026-10-08  7:27   ` Conor Dooley
@ 2026-10-08  9:15     ` Yixun Lan
  2026-10-08 10:30     ` Icenowy Zheng
  1 sibling, 0 replies; 8+ messages in thread
From: Yixun Lan @ 2026-10-08  9:15 UTC (permalink / raw)
  To: Conor Dooley
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Icenowy Zheng, Iker Pedrosa, devicetree, linux-riscv, spacemit,
	linux-pm, linux-kernel

Hi Conor,

On 08:27 Thu 08 Oct     , Conor Dooley wrote:
> On Thu, Oct 08, 2026 at 03:44:29AM +0000, Yixun Lan wrote:
> > Add initial power domain driver support for SpacemiT K1/K3 SoC, The Power
> > Domain Controller is SoC internal module which is part of APMU (Advanced
> > Power Management Unit), it is responsible for managing power across multi
> > power domains, which includes power on/off, isolation, power-on sequence.
> 
> You should prob mention that this APMU is already documented elsewhere.
Right, the documentation already exists in
   Documentation/devicetree/bindings/soc/spacemit/spacemit,k1-syscon.yaml

I can add a short note in next version

> Acked-by: Conor Dooley <conor.dooley@microchip.com>
Thanks

-- 
Yixun Lan (dlan)

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs
  2026-10-08  7:27   ` Conor Dooley
  2026-10-08  9:15     ` Yixun Lan
@ 2026-10-08 10:30     ` Icenowy Zheng
  1 sibling, 0 replies; 8+ messages in thread
From: Icenowy Zheng @ 2026-10-08 10:30 UTC (permalink / raw)
  To: Conor Dooley, Yixun Lan
  Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Ulf Hansson,
	Paul Walmsley, Palmer Dabbelt, Albert Ou, Alexandre Ghiti,
	Iker Pedrosa, devicetree, linux-riscv, spacemit, linux-pm,
	linux-kernel

在 2026-10-08四的 08:27 +0100,Conor Dooley写道:
> On Thu, Oct 08, 2026 at 03:44:29AM +0000, Yixun Lan wrote:
> > Add initial power domain driver support for SpacemiT K1/K3 SoC, The
> > Power
> > Domain Controller is SoC internal module which is part of APMU
> > (Advanced
> > Power Management Unit), it is responsible for managing power across
> > multi
> > power domains, which includes power on/off, isolation, power-on
> > sequence.
> 
> You should prob mention that this APMU is already documented
> elsewhere.

Maybe it's also meaningful to mention that APMU already has #power-
domain-cells property, because the first reaction of me is to add such
a property (and then found it already there).

Thanks,
Icenowy

> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> pw-bot: not-applicable
> 
> Thanks,
> Conor.
> 
> > 
> > Signed-off-by: Yixun Lan <dlan@kernel.org>
> > ---
> >  include/dt-bindings/power/spacemit,k1-power.h | 14 ++++++++++++++
> >  include/dt-bindings/power/spacemit,k3-power.h | 12 ++++++++++++
> >  2 files changed, 26 insertions(+)
> > 
> > diff --git a/include/dt-bindings/power/spacemit,k1-power.h
> > b/include/dt-bindings/power/spacemit,k1-power.h
> > new file mode 100644
> > index 000000000000..41ef540b5c44
> > --- /dev/null
> > +++ b/include/dt-bindings/power/spacemit,k1-power.h
> > @@ -0,0 +1,14 @@
> > +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> > +
> > +#ifndef _DT_BINDINGS_SPACEMIT_K1_POWER_H_
> > +#define _DT_BINDINGS_SPACEMIT_K1_POWER_H_
> > +
> > +#define K1_PMDOMAIN_VPU		0
> > +#define K1_PMDOMAIN_GPU		1
> > +#define K1_PMDOMAIN_LCD		2
> > +#define K1_PMDOMAIN_ISP		3
> > +#define K1_PMDOMAIN_AUDIO	4
> > +#define K1_PMDOMAIN_GNSS	5
> > +#define K1_PMDOMAIN_HDMI	6
> > +
> > +#endif
> > diff --git a/include/dt-bindings/power/spacemit,k3-power.h
> > b/include/dt-bindings/power/spacemit,k3-power.h
> > new file mode 100644
> > index 000000000000..519fbf946312
> > --- /dev/null
> > +++ b/include/dt-bindings/power/spacemit,k3-power.h
> > @@ -0,0 +1,12 @@
> > +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
> > +
> > +#ifndef _DT_BINDINGS_SPACEMIT_K3_POWER_H_
> > +#define _DT_BINDINGS_SPACEMIT_K3_POWER_H_
> > +
> > +#define K3_PMDOMAIN_VPU		0
> > +#define K3_PMDOMAIN_GPU		1
> > +#define K3_PMDOMAIN_AUDIO	2
> > +#define K3_PMDOMAIN_LCD0	3
> > +#define K3_PMDOMAIN_LCD1	4
> > +
> > +#endif
> > 
> > -- 
> > 2.56.0
> > 

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-10-08 10:31 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08  3:44 [PATCH v2 0/2] PM: spacemit: Add power domain support Yixun Lan
2026-10-08  3:44 ` [PATCH v2 1/2] dt-bindings: power: spacemit: Add power domain controller IDs Yixun Lan
2026-10-08  7:27   ` Conor Dooley
2026-10-08  9:15     ` Yixun Lan
2026-10-08 10:30     ` Icenowy Zheng
2026-10-08  3:44 ` [PATCH v2 2/2] pmdomain: spacemit: Add power domain driver Yixun Lan
2026-10-08  3:55   ` sashiko-bot
2026-10-08  8:51     ` Yixun Lan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox