Skip to content

Commit 3d3593c

Browse files
committed
power: domain: Add Rockchip RK3576 power domain driver
Add a power domain driver for the Rockchip RK3576 SoC, ported from the Linux kernel driver. This enables U-Boot to control power domains for peripherals like USB, GPU, NPU, video encoders/decoders, etc. The driver implements: - Power domain on/off control via PMU registers - Bus idle request/acknowledge handshaking - Clock ungating during power transitions - Domain status checking via repair status or idle status registers Supported power domains (matching dt-bindings/power/rockchip,rk3576-power.h): - RK3576_PD_NPU, RK3576_PD_NPUTOP, RK3576_PD_NPU0, RK3576_PD_NPU1 - RK3576_PD_NVM, RK3576_PD_SDGMAC, RK3576_PD_AUDIO - RK3576_PD_PHP, RK3576_PD_SUBPHP - RK3576_PD_VOP, RK3576_PD_VO0, RK3576_PD_VO1 - RK3576_PD_USB, RK3576_PD_VI - RK3576_PD_VEPU0, RK3576_PD_VEPU1, RK3576_PD_VDEC, RK3576_PD_VPU - RK3576_PD_GPU The power sequence follows the hardware requirements: 1. Ungate clocks for the domain 2. Set power control register (0 = on, 1 = off) 3. Wait for power state via repair status polling 4. Set/clear bus idle request 5. Wait for idle acknowledge and status 6. Re-gate clocks This driver is required for enabling peripherals on RK3576 platforms that have their power domains disabled by default or by firmware. Change-Id: c5659beb-c1ef-4a41-8972-66c9a4bd123d Signed-off-by: Anton Burticica <mouse@ya.ru>
1 parent cb2e54d commit 3d3593c

File tree

3 files changed

+298
-0
lines changed

3 files changed

+298
-0
lines changed

drivers/power/domain/Kconfig

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,12 @@ config APPLE_PMGR_POWER_DOMAIN
1818
This driver is needed to power on parts of the SoC that have
1919
not been powered on by previous boot stages.
2020

21+
config POWER_DOMAIN_RK3576
22+
bool "Enable RK3576 power domain driver"
23+
depends on POWER_DOMAIN && ARCH_ROCKCHIP
24+
help
25+
Enable support for power domains on Rockchip RK3576 SoC.
26+
2127
config AGILEX5_PMGR_POWER_DOMAIN
2228
bool "Enable the Agilex5 PMGR power domain driver"
2329
depends on SPL_POWER_DOMAIN && TARGET_SOCFPGA_SOC64

drivers/power/domain/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
obj-$(CONFIG_$(PHASE_)POWER_DOMAIN) += power-domain-uclass.o
77
obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o
8+
obj-$(CONFIG_POWER_DOMAIN_RK3576) += rk3576-power-domain.o
89
obj-$(CONFIG_AGILEX5_PMGR_POWER_DOMAIN) += altr-pmgr-agilex5.o
910
obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o
1011
obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Rockchip RK3576 Power Domain Driver for U-Boot
4+
*
5+
* Copyright (c) 2024
6+
*/
7+
8+
#include <dm.h>
9+
#include <power-domain-uclass.h>
10+
#include <asm/io.h>
11+
#include <linux/bitops.h>
12+
#include <linux/iopoll.h>
13+
#include <dt-bindings/power/rockchip,rk3576-power.h>
14+
15+
/* PMU register offsets */
16+
#define RK3576_PMU_PWR_CON 0x210
17+
#define RK3576_PMU_PWR_STATUS 0x230
18+
#define RK3576_PMU_CHAIN_STATUS 0x248
19+
#define RK3576_PMU_MEM_STATUS 0x250
20+
#define RK3576_PMU_MEM_PWR 0x300
21+
#define RK3576_PMU_BIU_IDLE_REQ 0x110
22+
#define RK3576_PMU_BIU_IDLE_ACK 0x120
23+
#define RK3576_PMU_BIU_IDLE_STATUS 0x128
24+
#define RK3576_PMU_REPAIR_STATUS 0x570
25+
#define RK3576_PMU_CLK_UNGATE 0x140
26+
27+
#define DOMAIN_TIMEOUT_US 10000
28+
29+
struct rk3576_pd_info {
30+
const char *name;
31+
u32 pwr_offset;
32+
u32 pwr_mask;
33+
u32 pwr_w_mask;
34+
u32 status_mask;
35+
u32 req_offset;
36+
u32 req_mask;
37+
u32 req_w_mask;
38+
u32 idle_mask;
39+
u32 repair_status_mask;
40+
u32 clk_ungate_mask;
41+
};
42+
43+
#define DOMAIN(_name, p_off, pwr, sts, r_sts, r_off, req, idle, g_mask) \
44+
{ \
45+
.name = _name, \
46+
.pwr_offset = p_off, \
47+
.pwr_mask = pwr, \
48+
.pwr_w_mask = (pwr) << 16, \
49+
.status_mask = sts, \
50+
.req_offset = r_off, \
51+
.req_mask = req, \
52+
.req_w_mask = (req) << 16, \
53+
.idle_mask = idle, \
54+
.repair_status_mask = r_sts, \
55+
.clk_ungate_mask = g_mask, \
56+
}
57+
58+
static const struct rk3576_pd_info rk3576_pd_info[] = {
59+
[RK3576_PD_NPU] = DOMAIN("npu", 0x0, BIT(0), BIT(0), 0, 0x0, 0, 0, 0),
60+
[RK3576_PD_NVM] = DOMAIN("nvm", 0x0, BIT(6), 0, BIT(6), 0x4, BIT(2), BIT(18), BIT(2)),
61+
[RK3576_PD_SDGMAC] = DOMAIN("sdgmac", 0x0, BIT(7), 0, BIT(7), 0x4, BIT(1), BIT(17), 0x6),
62+
[RK3576_PD_AUDIO] = DOMAIN("audio", 0x0, BIT(8), 0, BIT(8), 0x4, BIT(0), BIT(16), BIT(0)),
63+
[RK3576_PD_PHP] = DOMAIN("php", 0x0, BIT(9), 0, BIT(9), 0x0, BIT(15), BIT(15), BIT(15)),
64+
[RK3576_PD_SUBPHP] = DOMAIN("subphp", 0x0, BIT(10), 0, BIT(10), 0x0, 0, 0, 0),
65+
[RK3576_PD_VOP] = DOMAIN("vop", 0x0, BIT(11), 0, BIT(11), 0x0, 0x6000, 0x6000, 0x6000),
66+
[RK3576_PD_VO1] = DOMAIN("vo1", 0x0, BIT(14), 0, BIT(14), 0x0, BIT(12), BIT(12), 0x7000),
67+
[RK3576_PD_VO0] = DOMAIN("vo0", 0x0, BIT(15), 0, BIT(15), 0x0, BIT(11), BIT(11), 0x6800),
68+
[RK3576_PD_USB] = DOMAIN("usb", 0x4, BIT(0), 0, BIT(16), 0x0, BIT(10), BIT(10), 0x6400),
69+
[RK3576_PD_VI] = DOMAIN("vi", 0x4, BIT(1), 0, BIT(17), 0x0, BIT(9), BIT(9), BIT(9)),
70+
[RK3576_PD_VEPU0] = DOMAIN("vepu0", 0x4, BIT(2), 0, BIT(18), 0x0, BIT(7), BIT(7), 0x280),
71+
[RK3576_PD_VEPU1] = DOMAIN("vepu1", 0x4, BIT(3), 0, BIT(19), 0x0, BIT(8), BIT(8), BIT(8)),
72+
[RK3576_PD_VDEC] = DOMAIN("vdec", 0x4, BIT(4), 0, BIT(20), 0x0, BIT(6), BIT(6), BIT(6)),
73+
[RK3576_PD_VPU] = DOMAIN("vpu", 0x4, BIT(5), 0, BIT(21), 0x0, BIT(5), BIT(5), BIT(5)),
74+
[RK3576_PD_NPUTOP] = DOMAIN("nputop", 0x4, BIT(6), 0, BIT(22), 0x0, 0x18, 0x18, 0x18),
75+
[RK3576_PD_NPU0] = DOMAIN("npu0", 0x4, BIT(7), 0, BIT(23), 0x0, BIT(1), BIT(1), 0x1a),
76+
[RK3576_PD_NPU1] = DOMAIN("npu1", 0x4, BIT(8), 0, BIT(24), 0x0, BIT(2), BIT(2), 0x1c),
77+
[RK3576_PD_GPU] = DOMAIN("gpu", 0x4, BIT(9), 0, BIT(25), 0x0, BIT(0), BIT(0), BIT(0)),
78+
};
79+
80+
struct rk3576_pmu_priv {
81+
void __iomem *base;
82+
};
83+
84+
static bool rk3576_pmu_domain_is_on(struct rk3576_pmu_priv *priv,
85+
const struct rk3576_pd_info *info)
86+
{
87+
u32 val;
88+
89+
if (info->repair_status_mask) {
90+
val = readl(priv->base + RK3576_PMU_REPAIR_STATUS);
91+
return val & info->repair_status_mask;
92+
}
93+
94+
if (info->status_mask == 0) {
95+
/* Check idle status for idle-only domains */
96+
val = readl(priv->base + RK3576_PMU_BIU_IDLE_STATUS);
97+
return !(val & info->idle_mask);
98+
}
99+
100+
val = readl(priv->base + RK3576_PMU_PWR_STATUS);
101+
return !(val & info->status_mask);
102+
}
103+
104+
static int rk3576_pmu_set_idle_request(struct rk3576_pmu_priv *priv,
105+
const struct rk3576_pd_info *info,
106+
bool idle)
107+
{
108+
u32 val, target;
109+
int ret;
110+
111+
if (info->req_mask == 0)
112+
return 0;
113+
114+
val = idle ? (info->req_mask | info->req_w_mask) : info->req_w_mask;
115+
writel(val, priv->base + RK3576_PMU_BIU_IDLE_REQ + info->req_offset);
116+
117+
/* Wait for ack */
118+
target = idle ? info->idle_mask : 0;
119+
ret = readl_poll_timeout(priv->base + RK3576_PMU_BIU_IDLE_ACK,
120+
val, (val & info->idle_mask) == target,
121+
DOMAIN_TIMEOUT_US);
122+
if (ret) {
123+
pr_err("%s: failed to get ack for %s, val=0x%x\n",
124+
__func__, info->name, val);
125+
return ret;
126+
}
127+
128+
/* Wait for idle status */
129+
ret = readl_poll_timeout(priv->base + RK3576_PMU_BIU_IDLE_STATUS,
130+
val, (val & info->idle_mask) == target,
131+
DOMAIN_TIMEOUT_US);
132+
if (ret) {
133+
pr_err("%s: failed to set idle for %s, val=0x%x\n",
134+
__func__, info->name, val);
135+
return ret;
136+
}
137+
138+
return 0;
139+
}
140+
141+
static int rk3576_pmu_ungate_clk(struct rk3576_pmu_priv *priv,
142+
const struct rk3576_pd_info *info,
143+
bool ungate)
144+
{
145+
u32 val;
146+
u32 clk_ungate_w_mask;
147+
148+
if (!info->clk_ungate_mask)
149+
return 0;
150+
151+
clk_ungate_w_mask = info->clk_ungate_mask << 16;
152+
val = ungate ? (info->clk_ungate_mask | clk_ungate_w_mask) : clk_ungate_w_mask;
153+
writel(val, priv->base + RK3576_PMU_CLK_UNGATE);
154+
155+
return 0;
156+
}
157+
158+
static int rk3576_pmu_set_power(struct rk3576_pmu_priv *priv,
159+
const struct rk3576_pd_info *info, bool on)
160+
{
161+
u32 val;
162+
bool is_on;
163+
int ret;
164+
165+
if (info->pwr_mask == 0)
166+
return 0;
167+
168+
/* Write power control - 0 = power on, 1 = power off */
169+
val = on ? info->pwr_w_mask : (info->pwr_mask | info->pwr_w_mask);
170+
writel(val, priv->base + RK3576_PMU_PWR_CON + info->pwr_offset);
171+
172+
/* Wait for power state */
173+
ret = readl_poll_timeout(priv->base + RK3576_PMU_REPAIR_STATUS,
174+
val, !!(val & info->repair_status_mask) == on,
175+
DOMAIN_TIMEOUT_US);
176+
if (ret) {
177+
is_on = rk3576_pmu_domain_is_on(priv, info);
178+
pr_err("%s: failed to set %s %s, is_on=%d\n",
179+
__func__, info->name, on ? "on" : "off", is_on);
180+
return ret;
181+
}
182+
183+
return 0;
184+
}
185+
186+
static int rk3576_power_domain_on(struct power_domain *pd)
187+
{
188+
struct rk3576_pmu_priv *priv = dev_get_priv(pd->dev);
189+
const struct rk3576_pd_info *info;
190+
int ret;
191+
192+
if (pd->id >= ARRAY_SIZE(rk3576_pd_info))
193+
return -EINVAL;
194+
195+
info = &rk3576_pd_info[pd->id];
196+
if (!info->name)
197+
return -EINVAL;
198+
199+
debug("%s: enabling %s\n", __func__, info->name);
200+
201+
if (rk3576_pmu_domain_is_on(priv, info))
202+
return 0;
203+
204+
rk3576_pmu_ungate_clk(priv, info, true);
205+
206+
ret = rk3576_pmu_set_power(priv, info, true);
207+
if (ret)
208+
goto out;
209+
210+
ret = rk3576_pmu_set_idle_request(priv, info, false);
211+
212+
out:
213+
rk3576_pmu_ungate_clk(priv, info, false);
214+
return ret;
215+
}
216+
217+
static int rk3576_power_domain_off(struct power_domain *pd)
218+
{
219+
struct rk3576_pmu_priv *priv = dev_get_priv(pd->dev);
220+
const struct rk3576_pd_info *info;
221+
int ret;
222+
223+
if (pd->id >= ARRAY_SIZE(rk3576_pd_info))
224+
return -EINVAL;
225+
226+
info = &rk3576_pd_info[pd->id];
227+
if (!info->name)
228+
return -EINVAL;
229+
230+
debug("%s: disabling %s\n", __func__, info->name);
231+
232+
if (!rk3576_pmu_domain_is_on(priv, info))
233+
return 0;
234+
235+
rk3576_pmu_ungate_clk(priv, info, true);
236+
237+
ret = rk3576_pmu_set_idle_request(priv, info, true);
238+
if (ret)
239+
goto out;
240+
241+
ret = rk3576_pmu_set_power(priv, info, false);
242+
243+
out:
244+
rk3576_pmu_ungate_clk(priv, info, false);
245+
return ret;
246+
}
247+
248+
static int rk3576_power_domain_request(struct power_domain *pd)
249+
{
250+
debug("%s: domain id=%lu\n", __func__, pd->id);
251+
return 0;
252+
}
253+
254+
static int rk3576_power_domain_rfree(struct power_domain *pd)
255+
{
256+
debug("%s: domain id=%lu\n", __func__, pd->id);
257+
return 0;
258+
}
259+
260+
static int rk3576_power_domain_probe(struct udevice *dev)
261+
{
262+
struct rk3576_pmu_priv *priv = dev_get_priv(dev);
263+
264+
priv->base = dev_read_addr_ptr(dev->parent);
265+
if (!priv->base)
266+
return -EINVAL;
267+
268+
debug("%s: base=%p\n", __func__, priv->base);
269+
return 0;
270+
}
271+
272+
static const struct udevice_id rk3576_power_domain_ids[] = {
273+
{ .compatible = "rockchip,rk3576-power-controller" },
274+
{ }
275+
};
276+
277+
struct power_domain_ops rk3576_power_domain_ops = {
278+
.request = rk3576_power_domain_request,
279+
.rfree = rk3576_power_domain_rfree,
280+
.on = rk3576_power_domain_on,
281+
.off = rk3576_power_domain_off,
282+
};
283+
284+
U_BOOT_DRIVER(rk3576_power_domain) = {
285+
.name = "rk3576_power_domain",
286+
.id = UCLASS_POWER_DOMAIN,
287+
.of_match = rk3576_power_domain_ids,
288+
.probe = rk3576_power_domain_probe,
289+
.priv_auto = sizeof(struct rk3576_pmu_priv),
290+
.ops = &rk3576_power_domain_ops,
291+
};

0 commit comments

Comments
 (0)