All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] arm: rockchip: rk3399: rock-pi-4: power domain driver to boot from MMCSD
@ 2022-06-02 11:02 Xavier Drudis Ferran
  2022-06-02 11:05 ` [PATCH 1/3] Reserve some RAM at 0 for bl31_0x00000000.bin for Radxa Rock Pi 4 Xavier Drudis Ferran
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Xavier Drudis Ferran @ 2022-06-02 11:02 UTC (permalink / raw)
  To: u-boot; +Cc: sjg, philipp.tomsich, kever.yang, lukma, seanga2

Hello. 

I've been trying to compile myself all the stack to boot a Radxa Rock
Pi 4B.

I've found and worked around a few issues and I'm starting to select,
clean up and send the patches I used.

Since I'm new to the project I may have overseen something obvious. If
so sorry. For the rest I hope something can be useful to others.

Likewise I've found a couple of suspicions lines in clk_rk3399.c, but
since I haven't found cases where they give problems, I should not
send changes, or should I?
 
This series is the minimum I needed to be able to boot linux-libre
from a SD card, which wasn't possible for me just from master.  It
still displays some errors, but I leave them for later because it
boots.

The series is basically about trying not to overlap ATF, enabling a
couple of clocks that MMCSD uses, and adding a power domain driver for
rockchip, since I guess the DTS must have been updated from linux and
the board may have worked before but the new DTS maybe broke boot from
SD.

I'm not sure why power domain drivers in U-Boot leave the clocks they
need enabled, but I tried to disable them once powered up and it
didn't boot.

If people wants to test with other rockchip SOCs that'd be welcome,
but I'm not sure whether they need the new driver. For now I only
configured it for Rock Pi 4.

Thanks beforehand for any feedback. Specially if I should run any tests.

-- 
Xavier Drudis Ferran 

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

* Re: [PATCH 1/3] Reserve some RAM at 0 for bl31_0x00000000.bin for Radxa Rock Pi 4.
  2022-06-02 11:02 [PATCH 0/3] arm: rockchip: rk3399: rock-pi-4: power domain driver to boot from MMCSD Xavier Drudis Ferran
@ 2022-06-02 11:05 ` Xavier Drudis Ferran
  2022-06-02 11:06 ` [PATCH 2/3] Add a couple of missing clocks for MMCSD Xavier Drudis Ferran
  2022-06-02 11:08 ` [PATCH 3/3] arm: rockchip: rk3399: Power domain driver for rockchip Xavier Drudis Ferran
  2 siblings, 0 replies; 4+ messages in thread
From: Xavier Drudis Ferran @ 2022-06-02 11:05 UTC (permalink / raw)
  To: u-boot; +Cc: sjg, philipp.tomsich, kever.yang, lukma, seanga2

When trying to boot from MMC in a Rock Pi 4 where I shorted
SPI clk to GND to have bootrom load tpl+spl from MMC, SPL
hanged after the message "Trying to boot from MMC1".

With this patch it continued booting, printed two notices
from ATF with BL31 version and date, and loaded U-Boot proper.

I'm not sure if there's something special with the ATF I built,
or what would be a reasonable standard for the size of BL31@0
but in my u-boot.its I find

		atf_1 {
			description = "ARM Trusted Firmware";
			data = /incbin/("bl31_0x00000000.bin");
			type = "firmware";
			arch = "arm64";
			os = "arm-trusted-firmware";
			compression = "none";
			load = <0x00000000>;
			entry = <0x00001000>;
		};

and bl31_0x00000000.bin size is 82008 bytes.

0x15000 is 86016 which gives some room for variation and
seems good aligned enough. I don't want to make u-boot too big.

Most other boards which define CONFIG_ROCKCHIP_SPL_RESERVE_IRAM
put it at 0x4000 but in my tests this wasn't enough.

Signed-off-by: Xavier Drudis Ferran <xdrudis@tinet.cat>

---
 configs/rock-pi-4-rk3399_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/configs/rock-pi-4-rk3399_defconfig b/configs/rock-pi-4-rk3399_defconfig
index 8861f13878..510028b095 100644
--- a/configs/rock-pi-4-rk3399_defconfig
+++ b/configs/rock-pi-4-rk3399_defconfig
@@ -7,6 +7,7 @@ CONFIG_NR_DRAM_BANKS=1
 CONFIG_ENV_OFFSET=0x3F8000
 CONFIG_DEFAULT_DEVICE_TREE="rk3399-rock-pi-4b"
 CONFIG_ROCKCHIP_RK3399=y
+CONFIG_ROCKCHIP_SPL_RESERVE_IRAM=0x15000
 CONFIG_TARGET_EVB_RK3399=y
 CONFIG_DEBUG_UART_BASE=0xFF1A0000
 CONFIG_DEBUG_UART_CLOCK=24000000
-- 
2.20.1


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

* Re: [PATCH 2/3] Add a couple of missing clocks for MMCSD.
  2022-06-02 11:02 [PATCH 0/3] arm: rockchip: rk3399: rock-pi-4: power domain driver to boot from MMCSD Xavier Drudis Ferran
  2022-06-02 11:05 ` [PATCH 1/3] Reserve some RAM at 0 for bl31_0x00000000.bin for Radxa Rock Pi 4 Xavier Drudis Ferran
@ 2022-06-02 11:06 ` Xavier Drudis Ferran
  2022-06-02 11:08 ` [PATCH 3/3] arm: rockchip: rk3399: Power domain driver for rockchip Xavier Drudis Ferran
  2 siblings, 0 replies; 4+ messages in thread
From: Xavier Drudis Ferran @ 2022-06-02 11:06 UTC (permalink / raw)
  To: u-boot; +Cc: sjg, philipp.tomsich, kever.yang, lukma, seanga2

This helped boot a Radxa Rock Pi 4B from SD card. With U-boot master
at commit 4fe629d2e8bb ("Merge
https://source.denx.de/u-boot/custodians/u-boot-riscv") from
2022-05-27T08:50:42-0400 and the SPI clock shorted to GND to make the
bootrom boot from SD card, SPL stopped at the "Trying to boot from
MMC1" message.

Signed-off-by: Xavier Drudis Ferran <xdrudis@tinet.cat>
---
 drivers/clk/rockchip/clk_rk3399.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index 7d31a9f22a..fc3a5d4e9b 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -1184,6 +1184,12 @@ static int rk3399_clk_enable(struct clk *clk)
 	case SCLK_PCIEPHY_REF:
 		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
 		break;
+	case SCLK_SDMMC:
+		rk_clrreg(&priv->cru->clksel_con[6], BIT(1));
+		break;
+	case HCLK_SDMMC:
+		rk_clrreg(&priv->cru->clksel_con[33], BIT(8));
+		break;
 	default:
 		debug("%s: unsupported clk %ld\n", __func__, clk->id);
 		return -ENOENT;
@@ -1278,6 +1284,12 @@ static int rk3399_clk_disable(struct clk *clk)
 	case SCLK_PCIEPHY_REF:
 		rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
 		break;
+	case SCLK_SDMMC:
+		rk_setreg(&priv->cru->clksel_con[6], BIT(1));
+		break;
+	case HCLK_SDMMC:
+		rk_setreg(&priv->cru->clksel_con[33], BIT(8));
+		break;
 	default:
 		debug("%s: unsupported clk %ld\n", __func__, clk->id);
 		return -ENOENT;
-- 
2.20.1


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

* Re: [PATCH 3/3] arm: rockchip: rk3399: Power domain driver for rockchip.
  2022-06-02 11:02 [PATCH 0/3] arm: rockchip: rk3399: rock-pi-4: power domain driver to boot from MMCSD Xavier Drudis Ferran
  2022-06-02 11:05 ` [PATCH 1/3] Reserve some RAM at 0 for bl31_0x00000000.bin for Radxa Rock Pi 4 Xavier Drudis Ferran
  2022-06-02 11:06 ` [PATCH 2/3] Add a couple of missing clocks for MMCSD Xavier Drudis Ferran
@ 2022-06-02 11:08 ` Xavier Drudis Ferran
  2 siblings, 0 replies; 4+ messages in thread
From: Xavier Drudis Ferran @ 2022-06-02 11:08 UTC (permalink / raw)
  To: u-boot; +Cc: sjg, philipp.tomsich, kever.yang, lukma, seanga2

Enable clocks and power up domains when a get on a domain is made.

This (together with initialising a couple of new clocks) helped boot a
Radxa Rock Pi 4B from SD card. It's adapted from Linux 5.15.25
and includes parameters for a dozen Rockchip SOCs, but it's only tested
on RK3399.

Some of the parameters are for SOCs not even in U-Boot yet. To save a
little space it avoids most data for SOCs not configured in the build.
It even mentions CONFIG_ROCKCHIP_RK3066 and CONFIG_ROCKCHIP_RK3366
despite those constants not being in Kconfig yet.

Signed-off-by: Xavier Drudis Ferran <xdrudis@tinet.cat>
---
 configs/rock-pi-4-rk3399_defconfig           |    2 +
 drivers/power/domain/Kconfig                 |   11 +
 drivers/power/domain/Makefile                |    1 +
 drivers/power/domain/power-domain-uclass.c   |    4 +-
 drivers/power/domain/rockchip-power-domain.c | 1006 ++++++++++++++++++
 include/dt-bindings/power/rk3036-power.h     |   13 +
 include/dt-bindings/power/rk3128-power.h     |   14 +
 include/dt-bindings/power/rk3366-power.h     |   24 +
 include/dt-bindings/power/rk3368-power.h     |   29 +
 include/dt-bindings/power/rk3568-power.h     |   32 +
 include/power-domain-uclass.h                |   32 +
 11 files changed, 1166 insertions(+), 2 deletions(-)
 create mode 100644 drivers/power/domain/rockchip-power-domain.c
 create mode 100644 include/dt-bindings/power/rk3036-power.h
 create mode 100644 include/dt-bindings/power/rk3128-power.h
 create mode 100644 include/dt-bindings/power/rk3366-power.h
 create mode 100644 include/dt-bindings/power/rk3368-power.h
 create mode 100644 include/dt-bindings/power/rk3568-power.h

diff --git a/configs/rock-pi-4-rk3399_defconfig b/configs/rock-pi-4-rk3399_defconfig
index 80d1e63b59..8861f13878 100644
--- a/configs/rock-pi-4-rk3399_defconfig
+++ b/configs/rock-pi-4-rk3399_defconfig
@@ -46,6 +46,8 @@ CONFIG_NVME_PCI=y
 CONFIG_PCI=y
 CONFIG_PHY_ROCKCHIP_INNO_USB2=y
 CONFIG_PHY_ROCKCHIP_TYPEC=y
+CONFIG_POWER_DOMAIN=y
+CONFIG_ROCKCHIP_POWER_DOMAIN=y
 CONFIG_PMIC_RK8XX=y
 CONFIG_REGULATOR_PWM=y
 CONFIG_REGULATOR_RK8XX=y
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 7e1b8c072f..8cc7094611 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -68,6 +68,17 @@ config MESON_EE_POWER_DOMAIN
 	  Enable support for manipulating Amlogic Meson Everything-Else power
 	  domains.
 
+config ROCKCHIP_POWER_DOMAIN
+	bool "Enable the Rockchip Power domain driver"
+	depends on POWER_DOMAIN && ARCH_ROCKCHIP
+	help
+	  Power domain driver for Rockchip SOCs. It's only tested on
+	  RK3399 but includes parameters copied from linux-5.15.25 for
+	  PX30, RK3036,  RK3128, RK3188, RK3228, RK3288, RK3328,
+	  RK3368 and RK3568. It also includes parameters for RK3066
+	  and RK3366 waiting for mainline U-Boot support.
+	  Try at your own risk.
+
 config SANDBOX_POWER_DOMAIN
 	bool "Enable the sandbox power domain test driver"
 	depends on POWER_DOMAIN && SANDBOX
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index e624477621..232a128372 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o
 obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o
 obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
 obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
+obj-$(CONFIG_ROCKCHIP_POWER_DOMAIN) += rockchip-power-domain.o
 obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
 obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
 obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index f6286c70c1..4bf8911fdd 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -18,8 +18,8 @@ static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
 	return (struct power_domain_ops *)dev->driver->ops;
 }
 
-static int power_domain_of_xlate_default(struct power_domain *power_domain,
-					 struct ofnode_phandle_args *args)
+int power_domain_of_xlate_default(struct power_domain *power_domain,
+				  struct ofnode_phandle_args *args)
 {
 	debug("%s(power_domain=%p)\n", __func__, power_domain);
 
diff --git a/drivers/power/domain/rockchip-power-domain.c b/drivers/power/domain/rockchip-power-domain.c
new file mode 100644
index 0000000000..9902cbd661
--- /dev/null
+++ b/drivers/power/domain/rockchip-power-domain.c
@@ -0,0 +1,1006 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip power domain driver
+ *
+ * Copyright (C) 2022 Xavier Drudis Ferran
+ *	    reusing parts of ti-powerdomain.c from
+ *	    other files in this directory
+ *	    and linux-5.15.25 drivers/soc/rockchip/pm_domains.c
+ *	    by ROCKCHIP, Co Ltd (but I removed mutexes,
+ *	    etc, assuming u-boot won't run in parallel).
+ */
+#define DEBUG
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <power-domain-uclass.h>
+#include <regmap.h>
+#include <clk.h>
+#include <dm/ofnode.h>
+#include <dm/device_compat.h>
+#include <dm/device.h>
+#include <dm/devres.h>
+#include <syscon.h>
+#include <linux/iopoll.h>
+#include <dt-bindings/power/px30-power.h>
+#include <dt-bindings/power/rk3036-power.h>
+#include <dt-bindings/power/rk3066-power.h>
+#include <dt-bindings/power/rk3128-power.h>
+#include <dt-bindings/power/rk3188-power.h>
+#include <dt-bindings/power/rk3228-power.h>
+#include <dt-bindings/power/rk3288-power.h>
+#include <dt-bindings/power/rk3328-power.h>
+#include <dt-bindings/power/rk3366-power.h>
+#include <dt-bindings/power/rk3368-power.h>
+#include <dt-bindings/power/rk3399-power.h>
+#include <dt-bindings/power/rk3568-power.h>
+
+struct rockchip_domain_info {
+	const char *name;
+	int pwr_mask;
+	int status_mask;
+	int req_mask;
+	int idle_mask;
+	int ack_mask;
+	bool active_wakeup;
+	int pwr_w_mask;
+	int req_w_mask;
+};
+
+struct rockchip_pmu_info {
+	u32 pwr_offset;
+	u32 status_offset;
+	u32 req_offset;
+	u32 idle_offset;
+	u32 ack_offset;
+
+	u32 core_pwrcnt_offset;
+	u32 gpu_pwrcnt_offset;
+
+	unsigned int core_power_transition_time;
+	unsigned int gpu_power_transition_time;
+
+	int num_domains;
+	const struct rockchip_domain_info *domain_info;
+};
+
+#define MAX_QOS_REGS_NUM	5
+#define QOS_PRIORITY		0x08
+#define QOS_MODE		0x0c
+#define QOS_BANDWIDTH		0x10
+#define QOS_SATURATION		0x14
+#define QOS_EXTCONTROL		0x18
+
+struct rockchip_pm_domain {
+	const struct rockchip_domain_info *info;
+	struct rockchip_pmu *pmu;
+	int num_qos;
+	struct regmap **qos_regmap;
+	u32 *qos_save_regs[MAX_QOS_REGS_NUM];
+	struct clk_bulk clks;
+};
+
+struct rockchip_pmu {
+	struct udevice *dev;
+	struct regmap *regmap;
+	const struct rockchip_pmu_info *info;
+};
+
+#define DOMAIN(_name, pwr, status, req, idle, ack, wakeup)	\
+{								\
+	.name = _name,						\
+	.pwr_mask = (pwr),					\
+	.status_mask = (status),				\
+	.req_mask = (req),					\
+	.idle_mask = (idle),					\
+	.ack_mask = (ack),					\
+	.active_wakeup = (wakeup),				\
+}
+
+#define DOMAIN_M(_name, pwr, status, req, idle, ack, wakeup)	\
+{								\
+	.name = _name,						\
+	.pwr_w_mask = (pwr) << 16,				\
+	.pwr_mask = (pwr),					\
+	.status_mask = (status),				\
+	.req_w_mask = (req) << 16,				\
+	.req_mask = (req),					\
+	.idle_mask = (idle),					\
+	.ack_mask = (ack),					\
+	.active_wakeup = wakeup,				\
+}
+
+#define DOMAIN_RK3036(_name, req, ack, idle, wakeup)		\
+{								\
+	.name = _name,						\
+	.req_mask = (req),					\
+	.req_w_mask = (req) << 16,				\
+	.ack_mask = (ack),					\
+	.idle_mask = (idle),					\
+	.active_wakeup = wakeup,				\
+}
+
+#define DOMAIN_PX30(name, pwr, status, req, wakeup)		\
+	DOMAIN_M(name, pwr, status, req, (req) << 16, req, wakeup)
+
+#define DOMAIN_RK3288(name, pwr, status, req, wakeup)		\
+	DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup)
+
+#define DOMAIN_RK3328(name, pwr, status, req, wakeup)		\
+	DOMAIN_M(name, pwr, pwr, req, (req) << 10, req, wakeup)
+
+#define DOMAIN_RK3368(name, pwr, status, req, wakeup)		\
+	DOMAIN(name, pwr, status, req, (req) << 16, req, wakeup)
+
+#define DOMAIN_RK3399(name, pwr, status, req, wakeup)		\
+	DOMAIN(name, pwr, status, req, req, req, wakeup)
+
+#define DOMAIN_RK3568(name, pwr, req, wakeup)		\
+	DOMAIN_M(name, pwr, pwr, req, req, req, wakeup)
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_PX30)
+static const struct rockchip_domain_info px30_pm_domains[] = {
+	[PX30_PD_USB]		= DOMAIN_PX30("usb",	  BIT(5),  BIT(5),  BIT(10), false),
+	[PX30_PD_SDCARD]	= DOMAIN_PX30("sdcard",	  BIT(8),  BIT(8),  BIT(9),  false),
+	[PX30_PD_GMAC]		= DOMAIN_PX30("gmac",	  BIT(10), BIT(10), BIT(6),  false),
+	[PX30_PD_MMC_NAND]	= DOMAIN_PX30("mmc_nand", BIT(11), BIT(11), BIT(5),  false),
+	[PX30_PD_VPU]		= DOMAIN_PX30("vpu",	  BIT(12), BIT(12), BIT(14), false),
+	[PX30_PD_VO]		= DOMAIN_PX30("vo",	  BIT(13), BIT(13), BIT(7),  false),
+	[PX30_PD_VI]		= DOMAIN_PX30("vi",	  BIT(14), BIT(14), BIT(8),  false),
+	[PX30_PD_GPU]		= DOMAIN_PX30("gpu",	  BIT(15), BIT(15), BIT(2),  false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3036)
+static const struct rockchip_domain_info rk3036_pm_domains[] = {
+	[RK3036_PD_MSCH]	= DOMAIN_RK3036("msch", BIT(14), BIT(23), BIT(30), true),
+	[RK3036_PD_CORE]	= DOMAIN_RK3036("core", BIT(13), BIT(17), BIT(24), false),
+	[RK3036_PD_PERI]	= DOMAIN_RK3036("peri", BIT(12), BIT(18), BIT(25), false),
+	[RK3036_PD_VIO]		= DOMAIN_RK3036("vio",	BIT(11), BIT(19), BIT(26), false),
+	[RK3036_PD_VPU]		= DOMAIN_RK3036("vpu",	BIT(10), BIT(20), BIT(27), false),
+	[RK3036_PD_GPU]		= DOMAIN_RK3036("gpu",	BIT(9),	 BIT(21), BIT(28), false),
+	[RK3036_PD_SYS]		= DOMAIN_RK3036("sys",	BIT(8),	 BIT(22), BIT(29), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3066)
+static const struct rockchip_domain_info rk3066_pm_domains[] = {
+	[RK3066_PD_GPU]		= DOMAIN("gpu",	  BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false),
+	[RK3066_PD_VIDEO]	= DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false),
+	[RK3066_PD_VIO]		= DOMAIN("vio",	  BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false),
+	[RK3066_PD_PERI]	= DOMAIN("peri",  BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false),
+	[RK3066_PD_CPU]		= DOMAIN("cpu",	  0,	  BIT(5), BIT(1), BIT(26), BIT(31), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3128)
+static const struct rockchip_domain_info rk3128_pm_domains[] = {
+	[RK3128_PD_CORE]	= DOMAIN_RK3288("core",	 BIT(0), BIT(0), BIT(4), false),
+	[RK3128_PD_MSCH]	= DOMAIN_RK3288("msch",	 0,	 0,	 BIT(6), true),
+	[RK3128_PD_VIO]		= DOMAIN_RK3288("vio",	 BIT(3), BIT(3), BIT(2), false),
+	[RK3128_PD_VIDEO]	= DOMAIN_RK3288("video", BIT(2), BIT(2), BIT(1), false),
+	[RK3128_PD_GPU]		= DOMAIN_RK3288("gpu",	 BIT(1), BIT(1), BIT(3), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3188)
+static const struct rockchip_domain_info rk3188_pm_domains[] = {
+	[RK3188_PD_GPU]		= DOMAIN("gpu",	  BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false),
+	[RK3188_PD_VIDEO]	= DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false),
+	[RK3188_PD_VIO]		= DOMAIN("vio",	  BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false),
+	[RK3188_PD_PERI]	= DOMAIN("peri",  BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false),
+	[RK3188_PD_CPU]		= DOMAIN("cpu",	  BIT(5), BIT(5), BIT(1), BIT(26), BIT(31), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK322X)
+static const struct rockchip_domain_info rk3228_pm_domains[] = {
+	[RK3228_PD_CORE]	= DOMAIN_RK3036("core", BIT(0),	 BIT(0),  BIT(16), true),
+	[RK3228_PD_MSCH]	= DOMAIN_RK3036("msch", BIT(1),	 BIT(1),  BIT(17), true),
+	[RK3228_PD_BUS]		= DOMAIN_RK3036("bus",	BIT(2),	 BIT(2),  BIT(18), true),
+	[RK3228_PD_SYS]		= DOMAIN_RK3036("sys",	BIT(3),	 BIT(3),  BIT(19), true),
+	[RK3228_PD_VIO]		= DOMAIN_RK3036("vio",	BIT(4),	 BIT(4),  BIT(20), false),
+	[RK3228_PD_VOP]		= DOMAIN_RK3036("vop",	BIT(5),	 BIT(5),  BIT(21), false),
+	[RK3228_PD_VPU]		= DOMAIN_RK3036("vpu",	BIT(6),	 BIT(6),  BIT(22), false),
+	[RK3228_PD_RKVDEC]	= DOMAIN_RK3036("vdec", BIT(7),	 BIT(7),  BIT(23), false),
+	[RK3228_PD_GPU]		= DOMAIN_RK3036("gpu",	BIT(8),	 BIT(8),  BIT(24), false),
+	[RK3228_PD_PERI]	= DOMAIN_RK3036("peri", BIT(9),	 BIT(9),  BIT(25), true),
+	[RK3228_PD_GMAC]	= DOMAIN_RK3036("gmac", BIT(10), BIT(10), BIT(26), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3288)
+static const struct rockchip_domain_info rk3288_pm_domains[] = {
+	[RK3288_PD_VIO]		= DOMAIN_RK3288("vio",	 BIT(7),  BIT(7),  BIT(4), false),
+	[RK3288_PD_HEVC]	= DOMAIN_RK3288("hevc",	 BIT(14), BIT(10), BIT(9), false),
+	[RK3288_PD_VIDEO]	= DOMAIN_RK3288("video", BIT(8),  BIT(8),  BIT(3), false),
+	[RK3288_PD_GPU]		= DOMAIN_RK3288("gpu",	 BIT(9),  BIT(9),  BIT(2), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3328)
+static const struct rockchip_domain_info rk3328_pm_domains[] = {
+	[RK3328_PD_CORE]	= DOMAIN_RK3328("core",	 0, BIT(0), BIT(0), false),
+	[RK3328_PD_GPU]		= DOMAIN_RK3328("gpu",	 0, BIT(1), BIT(1), false),
+	[RK3328_PD_BUS]		= DOMAIN_RK3328("bus",	 0, BIT(2), BIT(2), true),
+	[RK3328_PD_MSCH]	= DOMAIN_RK3328("msch",	 0, BIT(3), BIT(3), true),
+	[RK3328_PD_PERI]	= DOMAIN_RK3328("peri",	 0, BIT(4), BIT(4), true),
+	[RK3328_PD_VIDEO]	= DOMAIN_RK3328("video", 0, BIT(5), BIT(5), false),
+	[RK3328_PD_HEVC]	= DOMAIN_RK3328("hevc",	 0, BIT(6), BIT(6), false),
+	[RK3328_PD_VIO]		= DOMAIN_RK3328("vio",	 0, BIT(8), BIT(8), false),
+	[RK3328_PD_VPU]		= DOMAIN_RK3328("vpu",	 0, BIT(9), BIT(9), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3366)
+static const struct rockchip_domain_info rk3366_pm_domains[] = {
+	[RK3366_PD_PERI]	= DOMAIN_RK3368("peri",	  BIT(10), BIT(10), BIT(6), true),
+	[RK3366_PD_VIO]		= DOMAIN_RK3368("vio",	  BIT(14), BIT(14), BIT(8), false),
+	[RK3366_PD_VIDEO]	= DOMAIN_RK3368("video",  BIT(13), BIT(13), BIT(7), false),
+	[RK3366_PD_RKVDEC]	= DOMAIN_RK3368("vdec",	  BIT(11), BIT(11), BIT(7), false),
+	[RK3366_PD_WIFIBT]	= DOMAIN_RK3368("wifibt", BIT(8),  BIT(8),  BIT(9), false),
+	[RK3366_PD_VPU]		= DOMAIN_RK3368("vpu",	  BIT(12), BIT(12), BIT(7), false),
+	[RK3366_PD_GPU]		= DOMAIN_RK3368("gpu",	  BIT(15), BIT(15), BIT(2), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3368)
+static const struct rockchip_domain_info rk3368_pm_domains[] = {
+	[RK3368_PD_PERI]	= DOMAIN_RK3368("peri",	 BIT(13), BIT(12), BIT(6), true),
+	[RK3368_PD_VIO]		= DOMAIN_RK3368("vio",	 BIT(15), BIT(14), BIT(8), false),
+	[RK3368_PD_VIDEO]	= DOMAIN_RK3368("video", BIT(14), BIT(13), BIT(7), false),
+	[RK3368_PD_GPU_0]	= DOMAIN_RK3368("gpu_0", BIT(16), BIT(15), BIT(2), false),
+	[RK3368_PD_GPU_1]	= DOMAIN_RK3368("gpu_1", BIT(17), BIT(16), BIT(2), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3399)
+static const struct rockchip_domain_info rk3399_pm_domains[] = {
+	[RK3399_PD_TCPD0]	= DOMAIN_RK3399("tcpd0",     BIT(8),  BIT(8),  0,	false),
+	[RK3399_PD_TCPD1]	= DOMAIN_RK3399("tcpd1",     BIT(9),  BIT(9),  0,	false),
+	[RK3399_PD_CCI]		= DOMAIN_RK3399("cci",	     BIT(10), BIT(10), 0,	true),
+	[RK3399_PD_CCI0]	= DOMAIN_RK3399("cci0",	     0,	      0,       BIT(15), true),
+	[RK3399_PD_CCI1]	= DOMAIN_RK3399("cci1",	     0,	      0,       BIT(16), true),
+	[RK3399_PD_PERILP]	= DOMAIN_RK3399("perilp",    BIT(11), BIT(11), BIT(1),	true),
+	[RK3399_PD_PERIHP]	= DOMAIN_RK3399("perihp",    BIT(12), BIT(12), BIT(2),	true),
+	[RK3399_PD_CENTER]	= DOMAIN_RK3399("center",    BIT(13), BIT(13), BIT(14), true),
+	[RK3399_PD_VIO]		= DOMAIN_RK3399("vio",	     BIT(14), BIT(14), BIT(17), false),
+	[RK3399_PD_GPU]		= DOMAIN_RK3399("gpu",	     BIT(15), BIT(15), BIT(0),	false),
+	[RK3399_PD_VCODEC]	= DOMAIN_RK3399("vcodec",    BIT(16), BIT(16), BIT(3),	false),
+	[RK3399_PD_VDU]		= DOMAIN_RK3399("vdu",	     BIT(17), BIT(17), BIT(4),	false),
+	[RK3399_PD_RGA]		= DOMAIN_RK3399("rga",	     BIT(18), BIT(18), BIT(5),	false),
+	[RK3399_PD_IEP]		= DOMAIN_RK3399("iep",	     BIT(19), BIT(19), BIT(6),	false),
+	[RK3399_PD_VO]		= DOMAIN_RK3399("vo",	     BIT(20), BIT(20), 0,	false),
+	[RK3399_PD_VOPB]	= DOMAIN_RK3399("vopb",	     0,	      0,       BIT(7),	false),
+	[RK3399_PD_VOPL]	= DOMAIN_RK3399("vopl",	     0,	      0,       BIT(8),	false),
+	[RK3399_PD_ISP0]	= DOMAIN_RK3399("isp0",	     BIT(22), BIT(22), BIT(9),	false),
+	[RK3399_PD_ISP1]	= DOMAIN_RK3399("isp1",	     BIT(23), BIT(23), BIT(10), false),
+	[RK3399_PD_HDCP]	= DOMAIN_RK3399("hdcp",	     BIT(24), BIT(24), BIT(11), false),
+	[RK3399_PD_GMAC]	= DOMAIN_RK3399("gmac",	     BIT(25), BIT(25), BIT(23), true),
+	[RK3399_PD_EMMC]	= DOMAIN_RK3399("emmc",	     BIT(26), BIT(26), BIT(24), true),
+	[RK3399_PD_USB3]	= DOMAIN_RK3399("usb3",	     BIT(27), BIT(27), BIT(12), true),
+	[RK3399_PD_EDP]		= DOMAIN_RK3399("edp",	     BIT(28), BIT(28), BIT(22), false),
+	[RK3399_PD_GIC]		= DOMAIN_RK3399("gic",	     BIT(29), BIT(29), BIT(27), true),
+	[RK3399_PD_SD]		= DOMAIN_RK3399("sd",	     BIT(30), BIT(30), BIT(28), true),
+	[RK3399_PD_SDIOAUDIO]	= DOMAIN_RK3399("sdioaudio", BIT(31), BIT(31), BIT(29), true),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3568)
+static const struct rockchip_domain_info rk3568_pm_domains[] = {
+	[RK3568_PD_NPU]		= DOMAIN_RK3568("npu",	BIT(1), BIT(2),	 false),
+	[RK3568_PD_GPU]		= DOMAIN_RK3568("gpu",	BIT(0), BIT(1),	 false),
+	[RK3568_PD_VI]		= DOMAIN_RK3568("vi",	BIT(6), BIT(3),	 false),
+	[RK3568_PD_VO]		= DOMAIN_RK3568("vo",	BIT(7), BIT(4),	 false),
+	[RK3568_PD_RGA]		= DOMAIN_RK3568("rga",	BIT(5), BIT(5),	 false),
+	[RK3568_PD_VPU]		= DOMAIN_RK3568("vpu",	BIT(2), BIT(6),	 false),
+	[RK3568_PD_RKVDEC]	= DOMAIN_RK3568("vdec", BIT(4), BIT(8),	 false),
+	[RK3568_PD_RKVENC]	= DOMAIN_RK3568("venc", BIT(3), BIT(7),	 false),
+	[RK3568_PD_PIPE]	= DOMAIN_RK3568("pipe", BIT(8), BIT(11), false),
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_PX30)
+static const struct rockchip_pmu_info px30_pmu = {
+	.pwr_offset = 0x18,
+	.status_offset = 0x20,
+	.req_offset = 0x64,
+	.idle_offset = 0x6c,
+	.ack_offset = 0x6c,
+
+	.num_domains = ARRAY_SIZE(px30_pm_domains),
+	.domain_info = px30_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3036)
+static const struct rockchip_pmu_info rk3036_pmu = {
+	.req_offset = 0x148,
+	.idle_offset = 0x14c,
+	.ack_offset = 0x14c,
+
+	.num_domains = ARRAY_SIZE(rk3036_pm_domains),
+	.domain_info = rk3036_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3066)
+static const struct rockchip_pmu_info rk3066_pmu = {
+	.pwr_offset = 0x08,
+	.status_offset = 0x0c,
+	.req_offset = 0x38, /* PMU_MISC_CON1 */
+	.idle_offset = 0x0c,
+	.ack_offset = 0x0c,
+
+	.num_domains = ARRAY_SIZE(rk3066_pm_domains),
+	.domain_info = rk3066_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3128)
+static const struct rockchip_pmu_info rk3128_pmu = {
+	.pwr_offset = 0x04,
+	.status_offset = 0x08,
+	.req_offset = 0x0c,
+	.idle_offset = 0x10,
+	.ack_offset = 0x10,
+
+	.num_domains = ARRAY_SIZE(rk3128_pm_domains),
+	.domain_info = rk3128_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3188)
+static const struct rockchip_pmu_info rk3188_pmu = {
+	.pwr_offset = 0x08,
+	.status_offset = 0x0c,
+	.req_offset = 0x38, /* PMU_MISC_CON1 */
+	.idle_offset = 0x0c,
+	.ack_offset = 0x0c,
+
+	.num_domains = ARRAY_SIZE(rk3188_pm_domains),
+	.domain_info = rk3188_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK322X)
+static const struct rockchip_pmu_info rk3228_pmu = {
+	.req_offset = 0x40c,
+	.idle_offset = 0x488,
+	.ack_offset = 0x488,
+
+	.num_domains = ARRAY_SIZE(rk3228_pm_domains),
+	.domain_info = rk3228_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3288)
+static const struct rockchip_pmu_info rk3288_pmu = {
+	.pwr_offset = 0x08,
+	.status_offset = 0x0c,
+	.req_offset = 0x10,
+	.idle_offset = 0x14,
+	.ack_offset = 0x14,
+
+	.core_pwrcnt_offset = 0x34,
+	.gpu_pwrcnt_offset = 0x3c,
+
+	.core_power_transition_time = 24, /* 1us */
+	.gpu_power_transition_time = 24, /* 1us */
+
+	.num_domains = ARRAY_SIZE(rk3288_pm_domains),
+	.domain_info = rk3288_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3328)
+static const struct rockchip_pmu_info rk3328_pmu = {
+	.req_offset = 0x414,
+	.idle_offset = 0x484,
+	.ack_offset = 0x484,
+
+	.num_domains = ARRAY_SIZE(rk3328_pm_domains),
+	.domain_info = rk3328_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3366)
+static const struct rockchip_pmu_info rk3366_pmu = {
+	.pwr_offset = 0x0c,
+	.status_offset = 0x10,
+	.req_offset = 0x3c,
+	.idle_offset = 0x40,
+	.ack_offset = 0x40,
+
+	.core_pwrcnt_offset = 0x48,
+	.gpu_pwrcnt_offset = 0x50,
+
+	.core_power_transition_time = 24,
+	.gpu_power_transition_time = 24,
+
+	.num_domains = ARRAY_SIZE(rk3366_pm_domains),
+	.domain_info = rk3366_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3368)
+static const struct rockchip_pmu_info rk3368_pmu = {
+	.pwr_offset = 0x0c,
+	.status_offset = 0x10,
+	.req_offset = 0x3c,
+	.idle_offset = 0x40,
+	.ack_offset = 0x40,
+
+	.core_pwrcnt_offset = 0x48,
+	.gpu_pwrcnt_offset = 0x50,
+
+	.core_power_transition_time = 24,
+	.gpu_power_transition_time = 24,
+
+	.num_domains = ARRAY_SIZE(rk3368_pm_domains),
+	.domain_info = rk3368_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3399)
+static const struct rockchip_pmu_info rk3399_pmu = {
+	.pwr_offset = 0x14,
+	.status_offset = 0x18,
+	.req_offset = 0x60,
+	.idle_offset = 0x64,
+	.ack_offset = 0x68,
+
+	/* ARM Trusted Firmware manages power transition times */
+
+	.num_domains = ARRAY_SIZE(rk3399_pm_domains),
+	.domain_info = rk3399_pm_domains,
+};
+#endif
+
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3568)
+static const struct rockchip_pmu_info rk3568_pmu = {
+	.pwr_offset = 0xa0,
+	.status_offset = 0x98,
+	.req_offset = 0x50,
+	.idle_offset = 0x68,
+	.ack_offset = 0x60,
+
+	.num_domains = ARRAY_SIZE(rk3568_pm_domains),
+	.domain_info = rk3568_pm_domains,
+};
+#endif
+
+static const struct rockchip_pmu_info *rockchip_pmu_infos[] = {
+	CONFIG_IS_ENABLED(ROCKCHIP_PX30,    (&px30_pmu),   (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3036,  (&rk3036_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3066,  (&rk3066_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3128,  (&rk3128_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3188,  (&rk3188_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK322X,  (&rk3228_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3288,  (&rk3288_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3328,  (&rk3328_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3366,  (&rk3366_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3368,  (&rk3368_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3399,  (&rk3399_pmu), (NULL)),
+	CONFIG_IS_ENABLED(ROCKCHIP_RK3568,  (&rk3568_pmu), (NULL)),
+};
+
+static const struct udevice_id rockchip_pm_domain_of_match[] = {
+#if CONFIG_IS_ENABLED(ROCKCHIP_PX30)
+	{
+		.compatible = "rockchip,px30-power-controller",
+		.data = 0,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3036)
+	{
+		.compatible = "rockchip,rk3036-power-controller",
+		.data = 1,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3066)
+	{
+		.compatible = "rockchip,rk3066-power-controller",
+		.data = 2,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3128)
+	{
+		.compatible = "rockchip,rk3128-power-controller",
+		.data = 3,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3188)
+	{
+		.compatible = "rockchip,rk3188-power-controller",
+		.data = 4,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK322X)
+	{
+		.compatible = "rockchip,rk3228-power-controller",
+		.data = 5,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3288)
+	{
+		.compatible = "rockchip,rk3288-power-controller",
+		.data = 6,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3328)
+	{
+		.compatible = "rockchip,rk3328-power-controller",
+		.data = 7,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3366)
+	{
+		.compatible = "rockchip,rk3366-power-controller",
+		.data = 8,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3368)
+	{
+		.compatible = "rockchip,rk3368-power-controller",
+		.data = 9,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3399)
+	{
+		.compatible = "rockchip,rk3399-power-controller",
+		.data = 10,
+	},
+#endif
+#if CONFIG_IS_ENABLED(ROCKCHIP_RK3568)
+	{
+		.compatible = "rockchip,rk3568-power-controller",
+		.data = 11,
+	},
+#endif
+	  { /* sentinel */ },
+};
+
+static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd)
+{
+	int i;
+
+	for (i = 0; i < pd->num_qos; i++) {
+		regmap_read(pd->qos_regmap[i],
+			    QOS_PRIORITY,
+			    &pd->qos_save_regs[0][i]);
+		regmap_read(pd->qos_regmap[i],
+			    QOS_MODE,
+			    &pd->qos_save_regs[1][i]);
+		regmap_read(pd->qos_regmap[i],
+			    QOS_BANDWIDTH,
+			    &pd->qos_save_regs[2][i]);
+		regmap_read(pd->qos_regmap[i],
+			    QOS_SATURATION,
+			    &pd->qos_save_regs[3][i]);
+		regmap_read(pd->qos_regmap[i],
+			    QOS_EXTCONTROL,
+			    &pd->qos_save_regs[4][i]);
+	}
+	return 0;
+}
+
+static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd)
+{
+	int i;
+
+	for (i = 0; i < pd->num_qos; i++) {
+		regmap_write(pd->qos_regmap[i],
+			     QOS_PRIORITY,
+			     pd->qos_save_regs[0][i]);
+		regmap_write(pd->qos_regmap[i],
+			     QOS_MODE,
+			     pd->qos_save_regs[1][i]);
+		regmap_write(pd->qos_regmap[i],
+			     QOS_BANDWIDTH,
+			     pd->qos_save_regs[2][i]);
+		regmap_write(pd->qos_regmap[i],
+			     QOS_SATURATION,
+			     pd->qos_save_regs[3][i]);
+		regmap_write(pd->qos_regmap[i],
+			     QOS_EXTCONTROL,
+			     pd->qos_save_regs[4][i]);
+	}
+	return 0;
+}
+
+static unsigned int rockchip_pmu_read_ack(struct rockchip_pmu *pmu)
+{
+	unsigned int val;
+
+	regmap_read(pmu->regmap, pmu->info->ack_offset, &val);
+	return val;
+}
+
+static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+	const struct rockchip_domain_info *pd_info = pd->info;
+	unsigned int val;
+
+	regmap_read(pmu->regmap, pmu->info->idle_offset, &val);
+	return (val & pd_info->idle_mask) == pd_info->idle_mask;
+}
+
+static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd,
+					 bool idle)
+{
+	const struct rockchip_domain_info *pd_info = pd->info;
+	struct rockchip_pmu *pmu = pd->pmu;
+	unsigned int target_ack;
+	unsigned int val;
+	bool is_idle;
+	int ret;
+
+	if (pd_info->req_mask == 0)
+		return 0;
+	else if (pd_info->req_w_mask)
+		regmap_write(pmu->regmap, pmu->info->req_offset,
+			     idle ? (pd_info->req_mask | pd_info->req_w_mask) :
+			     pd_info->req_w_mask);
+	else
+		regmap_update_bits(pmu->regmap, pmu->info->req_offset,
+				   pd_info->req_mask, idle ? -1U : 0);
+
+	asm volatile ("dsb sy");
+
+	/* Wait util idle_ack = 1 */
+	target_ack = idle ? pd_info->ack_mask : 0;
+	ret = readx_poll_timeout(rockchip_pmu_read_ack, pmu, val,
+				 (val & pd_info->ack_mask) == target_ack,
+				 10000);
+	if (ret) {
+		dev_err(pmu->dev,
+			"failed to get ack on domain '%s' of '%s', val=0x%x\n",
+			pd->info->name, pmu->dev->name, val);
+		return ret;
+	}
+
+	ret = readx_poll_timeout(rockchip_pmu_domain_is_idle, pd,
+				 is_idle, is_idle == idle, 10000);
+	if (ret) {
+		dev_err(pmu->dev,
+			"failed to set idle on domain '%s' of '%s', val=%d\n",
+			pd->info->name, pmu->dev->name, is_idle);
+		return ret;
+	}
+
+	return 0;
+}
+
+static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+	unsigned int val;
+
+	/* check idle status for idle-only domains */
+	if (pd->info->status_mask == 0)
+		return !rockchip_pmu_domain_is_idle(pd);
+
+	regmap_read(pmu->regmap, pmu->info->status_offset, &val);
+
+	/* 1'b0: power on, 1'b1: power off */
+	return !(val & pd->info->status_mask);
+}
+
+static int rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, bool on)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+	bool is_on;
+
+	if (pd->info->pwr_mask == 0) {
+		return -ENOSYS;
+	} else if (pd->info->pwr_w_mask) {
+		regmap_write(pmu->regmap, pmu->info->pwr_offset,
+			     on ? pd->info->pwr_w_mask :
+			     (pd->info->pwr_mask | pd->info->pwr_w_mask));
+	} else {
+		regmap_update_bits(pmu->regmap, pmu->info->pwr_offset,
+				   pd->info->pwr_mask, on ? 0 : -1U);
+	}
+
+	asm volatile ("dsb sy");
+	if (readx_poll_timeout(rockchip_pmu_domain_is_on, pd, is_on,
+			       is_on == on, 10000)) {
+		dev_err(pmu->dev,
+			"failed to set domain '%s' of '%s', val=%d\n",
+			pd->info->name, pmu->dev->name, is_on);
+		return -ETIMEDOUT;
+	}
+	return 0;
+}
+
+static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on)
+{
+	struct rockchip_pmu *pmu = pd->pmu;
+	int ret;
+
+	ret = clk_enable_bulk(&pd->clks);
+	if (ret < 0) {
+		dev_err(pmu->dev, "failed to enable clocks ret=%d clks->count=%d\n",
+			ret, pd->clks.count);
+		for (int i = 0; i < pd->clks.count; i++) {
+			dev_err(pmu->dev,
+				"clks[%d].rate=%lld .flags=%x "
+				".enable_count=%d id=%ld "
+				".data=%ld\n",
+				i, pd->clks.clks[i].rate, pd->clks.clks[i].flags,
+				pd->clks.clks[i].enable_count, pd->clks.clks[i].id,
+				pd->clks.clks[i].data);
+		}
+		return ret;
+	}
+
+	if (!power_on) {
+		rockchip_pmu_save_qos(pd);
+		/* if powering down, idle request to NIU first */
+		rockchip_pmu_set_idle_request(pd, true);
+	}
+
+	rockchip_do_pmu_set_power_domain(pd, power_on);
+
+	if (power_on) {
+		/* if powering up, leave idle mode */
+		rockchip_pmu_set_idle_request(pd, false);
+		rockchip_pmu_restore_qos(pd);
+	}
+
+	if (!power_on) /**
+			*  why? the U-boot power domain drivers leave
+			* clocks enabled when on, but linux
+			* drivers/soc/rockchip/pm-domains.c doesn't ?
+			* But it boots from SDMCC with this "if" and
+			* it doesn't with unconditional disable.
+			* Tested without CLK_CCF. With CLK_CCF it
+			* might give problems with too many enables
+			* and to few disables ?
+			*/
+		clk_disable_bulk(&pd->clks);
+
+	return 0;
+}
+
+static int rockchip_pd_power_on(struct power_domain *domain)
+{
+	return rockchip_pd_power(domain->priv, true);
+}
+
+static int rockchip_pd_power_off(struct power_domain *domain)
+{
+	return rockchip_pd_power(domain->priv, false);
+}
+
+static int rockchip_power_domain_free(struct power_domain *domain)
+{
+	struct rockchip_pm_domain *pd = domain->priv;
+	int j;
+
+	if (pd) {
+		for (j = 0; j < MAX_QOS_REGS_NUM; j++) {
+			if (pd->qos_save_regs[j]) {
+				devm_kfree(domain->dev, pd->qos_save_regs[j]);
+				pd->qos_save_regs[j] = NULL;
+			}
+		}
+		devm_kfree(domain->dev, pd);
+		domain->priv = NULL;
+	}
+	return 0;
+}
+
+static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu,
+				      u32 domain_reg_offset,
+				      unsigned int count)
+{
+	/* First configure domain power down transition count ... */
+	regmap_write(pmu->regmap, domain_reg_offset, count);
+	/* ... and then power up count. */
+	regmap_write(pmu->regmap, domain_reg_offset + 4, count);
+}
+
+static int rockchip_power_domain_probe(struct udevice *dev)
+{
+	struct udevice *parent;
+	struct rockchip_pmu *pmu = dev_get_priv(dev);
+	const struct rockchip_pmu_info *pmu_info;
+	ulong data;
+
+	parent = dev->parent;
+	if (!parent) {
+		dev_err(dev, "no parent for syscon devices\n");
+		return -ENODEV;
+	}
+
+	pmu->regmap = syscon_node_to_regmap(parent->node_);
+	if (IS_ERR(pmu->regmap)) {
+		dev_err(dev, "no regmap available\n");
+		return PTR_ERR(pmu->regmap);
+	}
+	data = dev_get_driver_data(dev);
+	if (data > ARRAY_SIZE(rockchip_pmu_infos)) {
+		dev_err(dev, "missing pmu_info\n");
+		return -EINVAL;
+	}
+	pmu_info = rockchip_pmu_infos[data];
+	pmu->info = pmu_info;
+
+	/*
+	 * Configure power up and down transition delays for CORE
+	 * and GPU domains.
+	 */
+
+	if (pmu_info->core_power_transition_time)
+		rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset,
+					  pmu_info->core_power_transition_time);
+	if (pmu_info->gpu_pwrcnt_offset)
+		rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset,
+					  pmu_info->gpu_power_transition_time);
+
+	return 0;
+}
+
+static int clk_get_bulk_for_node(struct udevice *dev, ofnode node, struct clk_bulk *bulk)
+{
+	int i, ret, err, count;
+
+	bulk->count = 0;
+
+	count = ofnode_count_phandle_with_args(node, "clocks", "#clock-cells", 0);
+	if (count < 1)
+		return count;
+
+	bulk->clks = devm_kcalloc(dev, count, sizeof(struct clk), GFP_KERNEL);
+	if (!bulk->clks)
+		return -ENOMEM;
+
+	for (i = 0; i < count; i++) {
+		ret = clk_get_by_index_nodev(node, i, &bulk->clks[i]);
+		if (ret < 0)
+			goto bulk_get_err;
+
+		++bulk->count;
+	}
+
+	return 0;
+
+bulk_get_err:
+	err = clk_release_all(bulk->clks, bulk->count);
+	if (err)
+		debug("%s: couldn't release all clocks for %p\n",
+		      __func__, dev);
+
+	return ret;
+}
+
+static void clk_put_bulk(struct udevice *dev, struct clk_bulk *bulk)
+{
+	int i;
+
+	for (i = 0; i < bulk->count; i++) {
+		devm_clk_put(dev, &bulk->clks[i]);
+	}
+}
+
+static int rockchip_power_domain_of_xlate(struct power_domain *power_domain,
+					  struct ofnode_phandle_args *args)
+{
+	struct udevice *dev = power_domain->dev;
+	struct rockchip_pm_domain *pd;
+	struct udevice *parent;
+	int j, error;
+	ofnode pd_node;
+	fdt32_t fdt32_index;
+
+	error = power_domain_of_xlate_default(power_domain, args);
+	if (error)
+		return error;
+
+	if (!dev) {
+		pr_err("Device not found\n");
+		return -ENODEV;
+	}
+
+	if (!ofnode_valid(args->node)) {
+		dev_err(dev, "domain tree node not found\n");
+		return -ENODEV;
+	}
+	fdt32_index = cpu_to_fdt32(power_domain->id);
+	pd_node = ofnode_by_prop_value(args->node, "reg",
+				       &fdt32_index, sizeof(fdt32_index));
+	if (!ofnode_valid(pd_node)) {
+		dev_err(dev, "child with reg=%ld  not found\n", power_domain->id);
+			return -ENODEV;
+	}
+
+	pd = devm_kzalloc(dev, sizeof(struct rockchip_pm_domain), GFP_KERNEL);
+	power_domain->priv = pd;
+	if (!pd) {
+		dev_err(dev, "no memory left for domain %ld\n", power_domain->id);
+		return -ENOMEM;
+	}
+
+	pd->pmu = dev_get_priv(dev);
+	if (power_domain->id > pd->pmu->info->num_domains) {
+		dev_err(dev, "no domain info for domain id %ld\n", power_domain->id);
+		return -EINVAL;
+	}
+	pd->info = &pd->pmu->info->domain_info[power_domain->id];
+
+	error = clk_get_bulk_for_node(dev, pd_node, &pd->clks);
+	if (error) {
+		dev_err(dev, "clk_get_bulk failed for domain id %ld with error %d\n",
+			power_domain->id, error);
+		return error;
+	}
+
+	pd->num_qos = ofnode_count_phandle_with_args(pd_node, "pm_qos", NULL, 0);
+	if (pd->num_qos > 0) {
+		pd->qos_regmap = devm_kcalloc(pd->pmu->dev, pd->num_qos,
+					      sizeof(*pd->qos_regmap),
+					      GFP_KERNEL);
+		if (!pd->qos_regmap) {
+			error = -ENOMEM;
+			goto err_out;
+		}
+
+		for (j = 0; j < MAX_QOS_REGS_NUM; j++) {
+			pd->qos_save_regs[j] = devm_kcalloc(parent,
+							    pd->num_qos,
+							    sizeof(u32),
+							    GFP_KERNEL);
+			if (!pd->qos_save_regs[j]) {
+				error = -ENOMEM;
+				goto err_out;
+			}
+		}
+		for (j = 0; j < pd->num_qos; j++) {
+			struct ofnode_phandle_args qos_args;
+
+			error = ofnode_parse_phandle_with_args(pd_node, "pm_qos",
+							       NULL, 0, j, &qos_args);
+			if (error) {
+				debug("qos %d phandle error\n", j);
+				goto err_out;
+			}
+			pd->qos_regmap[j] = syscon_node_to_regmap(qos_args.node);
+			of_node_put(qos_args.node.np);
+			if (IS_ERR(pd->qos_regmap[j])) {
+				debug("regmap error j=%d %p\n", j, pd->qos_regmap[j]);
+				error = -ENODEV;
+				goto err_out;
+			}
+		}
+		rockchip_pmu_save_qos(pd);
+	}
+	return 0;
+err_out:
+	clk_put_bulk(dev, &pd->clks);
+	return error;
+}
+
+static int rockchip_power_domain_request(struct power_domain *pd)
+{
+	return 0;
+}
+
+static struct power_domain_ops rockchip_power_domain_ops = {
+	.on = rockchip_pd_power_on,
+	.off = rockchip_pd_power_off,
+	.of_xlate = rockchip_power_domain_of_xlate,
+	.request = rockchip_power_domain_request,
+	.rfree = rockchip_power_domain_free,
+};
+
+U_BOOT_DRIVER(rockchip_power_domains) = {
+	.name = "rockchip-pm-domains",
+	.id = UCLASS_POWER_DOMAIN,
+	.of_match = rockchip_pm_domain_of_match,
+	.probe = rockchip_power_domain_probe,
+	.priv_auto = sizeof(struct rockchip_pmu),
+	.ops = &rockchip_power_domain_ops,
+};
diff --git a/include/dt-bindings/power/rk3036-power.h b/include/dt-bindings/power/rk3036-power.h
new file mode 100644
index 0000000000..0bc6b5d507
--- /dev/null
+++ b/include/dt-bindings/power/rk3036-power.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_BINDINGS_POWER_RK3036_POWER_H__
+#define __DT_BINDINGS_POWER_RK3036_POWER_H__
+
+#define RK3036_PD_MSCH		0
+#define RK3036_PD_CORE		1
+#define RK3036_PD_PERI		2
+#define RK3036_PD_VIO		3
+#define RK3036_PD_VPU		4
+#define RK3036_PD_GPU		5
+#define RK3036_PD_SYS		6
+
+#endif
diff --git a/include/dt-bindings/power/rk3128-power.h b/include/dt-bindings/power/rk3128-power.h
new file mode 100644
index 0000000000..c051dc3108
--- /dev/null
+++ b/include/dt-bindings/power/rk3128-power.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_BINDINGS_POWER_RK3128_POWER_H__
+#define __DT_BINDINGS_POWER_RK3128_POWER_H__
+
+/* VD_CORE */
+#define RK3128_PD_CORE		0
+
+/* VD_LOGIC */
+#define RK3128_PD_VIO		1
+#define RK3128_PD_VIDEO		2
+#define RK3128_PD_GPU		3
+#define RK3128_PD_MSCH		4
+
+#endif
diff --git a/include/dt-bindings/power/rk3366-power.h b/include/dt-bindings/power/rk3366-power.h
new file mode 100644
index 0000000000..223a3dce04
--- /dev/null
+++ b/include/dt-bindings/power/rk3366-power.h
@@ -0,0 +1,24 @@
+#ifndef __DT_BINDINGS_POWER_RK3366_POWER_H__
+#define __DT_BINDINGS_POWER_RK3366_POWER_H__
+
+/* VD_CORE */
+#define RK3366_PD_A53_0		0
+#define RK3366_PD_A53_1		1
+#define RK3366_PD_A53_2		2
+#define RK3366_PD_A53_3		3
+
+/* VD_LOGIC */
+#define RK3366_PD_BUS		4
+#define RK3366_PD_PERI		5
+#define RK3366_PD_VIO		6
+#define RK3366_PD_VIDEO		7
+#define RK3366_PD_RKVDEC	8
+#define RK3366_PD_WIFIBT	9
+#define RK3366_PD_VPU		10
+#define RK3366_PD_GPU		11
+#define RK3366_PD_ALIVE		12
+
+/* VD_PMU */
+#define RK3366_PD_PMU		13
+
+#endif
diff --git a/include/dt-bindings/power/rk3368-power.h b/include/dt-bindings/power/rk3368-power.h
new file mode 100644
index 0000000000..5e602dbd64
--- /dev/null
+++ b/include/dt-bindings/power/rk3368-power.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_BINDINGS_POWER_RK3368_POWER_H__
+#define __DT_BINDINGS_POWER_RK3368_POWER_H__
+
+/* VD_CORE */
+#define RK3368_PD_A53_L0	0
+#define RK3368_PD_A53_L1	1
+#define RK3368_PD_A53_L2	2
+#define RK3368_PD_A53_L3	3
+#define RK3368_PD_SCU_L		4
+#define RK3368_PD_A53_B0	5
+#define RK3368_PD_A53_B1	6
+#define RK3368_PD_A53_B2	7
+#define RK3368_PD_A53_B3	8
+#define RK3368_PD_SCU_B		9
+
+/* VD_LOGIC */
+#define RK3368_PD_BUS		10
+#define RK3368_PD_PERI		11
+#define RK3368_PD_VIO		12
+#define RK3368_PD_ALIVE		13
+#define RK3368_PD_VIDEO		14
+#define RK3368_PD_GPU_0		15
+#define RK3368_PD_GPU_1		16
+
+/* VD_PMU */
+#define RK3368_PD_PMU		17
+
+#endif
diff --git a/include/dt-bindings/power/rk3568-power.h b/include/dt-bindings/power/rk3568-power.h
new file mode 100644
index 0000000000..6cc1af1a9d
--- /dev/null
+++ b/include/dt-bindings/power/rk3568-power.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DT_BINDINGS_POWER_RK3568_POWER_H__
+#define __DT_BINDINGS_POWER_RK3568_POWER_H__
+
+/* VD_CORE */
+#define RK3568_PD_CPU_0		0
+#define RK3568_PD_CPU_1		1
+#define RK3568_PD_CPU_2		2
+#define RK3568_PD_CPU_3		3
+#define RK3568_PD_CORE_ALIVE	4
+
+/* VD_PMU */
+#define RK3568_PD_PMU		5
+
+/* VD_NPU */
+#define RK3568_PD_NPU		6
+
+/* VD_GPU */
+#define RK3568_PD_GPU		7
+
+/* VD_LOGIC */
+#define RK3568_PD_VI		8
+#define RK3568_PD_VO		9
+#define RK3568_PD_RGA		10
+#define RK3568_PD_VPU		11
+#define RK3568_PD_CENTER	12
+#define RK3568_PD_RKVDEC	13
+#define RK3568_PD_RKVENC	14
+#define RK3568_PD_PIPE		15
+#define RK3568_PD_LOGIC_ALIVE	16
+
+#endif
diff --git a/include/power-domain-uclass.h b/include/power-domain-uclass.h
index acf749b38e..19a010ec80 100644
--- a/include/power-domain-uclass.h
+++ b/include/power-domain-uclass.h
@@ -78,4 +78,36 @@ struct power_domain_ops {
 	int (*off)(struct power_domain *power_domain);
 };
 
+/**
+ * power_domain_of_xlate_default - Minimally translate a client's
+ * device-tree (OF) power domain specifier.
+ *
+ * This is the default implementation for the of_xlate member of
+ * struct power_domain_ops, which assumes #power-domain-cells = <1>,
+ * and that the DT cell contains a simple integer power domain ID.
+ * It simply assigns the power domain id member from it or complains
+ * if no arguments present.
+ *
+ * The power domain core calls this operation as the first step in
+ * implementing a client's power_domain_get() call and each power
+ * domain driver can either assign this function as the operation,
+ * assign its own function which calls this one or do it their own way.
+ *
+ * @power_domain:	The power domain struct to hold the
+ *			translation result.
+ * @args:		The power domain specifier values from device
+ *			tree.
+ * @return 0 if OK, or a negative error code.
+ */
+#if CONFIG_IS_ENABLED(OF_REAL) && CONFIG_IS_ENABLED(POWER_DOMAIN)
+int power_domain_of_xlate_default(struct power_domain *power_domain,
+				  struct ofnode_phandle_args *args);
+#else
+int power_domain_of_xlate_default(struct power_domain *power_domain,
+				  struct ofnode_phandle_args *args)
+{
+	return 0;
+}
+#endif
+
 #endif
-- 
2.20.1


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

end of thread, other threads:[~2022-06-02 11:21 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-06-02 11:02 [PATCH 0/3] arm: rockchip: rk3399: rock-pi-4: power domain driver to boot from MMCSD Xavier Drudis Ferran
2022-06-02 11:05 ` [PATCH 1/3] Reserve some RAM at 0 for bl31_0x00000000.bin for Radxa Rock Pi 4 Xavier Drudis Ferran
2022-06-02 11:06 ` [PATCH 2/3] Add a couple of missing clocks for MMCSD Xavier Drudis Ferran
2022-06-02 11:08 ` [PATCH 3/3] arm: rockchip: rk3399: Power domain driver for rockchip Xavier Drudis Ferran

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.