1*dbc13050SXiaoDong Huang // SPDX-License-Identifier: GPL-2.0+
2*dbc13050SXiaoDong Huang /*
3*dbc13050SXiaoDong Huang * Copyright (C) 2023 Rockchip Electronics Co., Ltd
4*dbc13050SXiaoDong Huang */
5*dbc13050SXiaoDong Huang
6*dbc13050SXiaoDong Huang #include <common.h>
7*dbc13050SXiaoDong Huang #include <dm.h>
8*dbc13050SXiaoDong Huang #include <misc.h>
9*dbc13050SXiaoDong Huang #include <asm/io.h>
10*dbc13050SXiaoDong Huang #include <linux/bitops.h>
11*dbc13050SXiaoDong Huang #include <asm/arch/rockchip_smccc.h>
12*dbc13050SXiaoDong Huang
13*dbc13050SXiaoDong Huang DECLARE_GLOBAL_DATA_PTR;
14*dbc13050SXiaoDong Huang
15*dbc13050SXiaoDong Huang #define RK_ATAG_MCU_SLP_CORE 0x526b0001
16*dbc13050SXiaoDong Huang #define RK_ATAG_MCU_SLP_MAX 0x526b00ff
17*dbc13050SXiaoDong Huang #define RK_ATAG_NONE 0x00000000
18*dbc13050SXiaoDong Huang
19*dbc13050SXiaoDong Huang /* rk_tag related defines */
20*dbc13050SXiaoDong Huang #define sleep_tag_next(t) \
21*dbc13050SXiaoDong Huang ((struct rk_sleep_tag *)((__u32 *)(t) + (t)->hdr.size))
22*dbc13050SXiaoDong Huang
23*dbc13050SXiaoDong Huang struct rk_tag_header {
24*dbc13050SXiaoDong Huang u32 size;
25*dbc13050SXiaoDong Huang u32 tag;
26*dbc13050SXiaoDong Huang };
27*dbc13050SXiaoDong Huang
28*dbc13050SXiaoDong Huang struct rk_sleep_tag {
29*dbc13050SXiaoDong Huang struct rk_tag_header hdr;
30*dbc13050SXiaoDong Huang u32 params[];
31*dbc13050SXiaoDong Huang };
32*dbc13050SXiaoDong Huang
33*dbc13050SXiaoDong Huang struct rk_mcu_sleep_core_tag {
34*dbc13050SXiaoDong Huang struct rk_tag_header hdr;
35*dbc13050SXiaoDong Huang u32 total_size;
36*dbc13050SXiaoDong Huang u32 reserve[13];
37*dbc13050SXiaoDong Huang };
38*dbc13050SXiaoDong Huang
39*dbc13050SXiaoDong Huang struct rk_mcu_sleep_tags {
40*dbc13050SXiaoDong Huang struct rk_mcu_sleep_core_tag core;
41*dbc13050SXiaoDong Huang struct rk_sleep_tag slp_tags;
42*dbc13050SXiaoDong Huang };
43*dbc13050SXiaoDong Huang
rockchip_pm_config_ioctl(struct udevice * dev,unsigned long request,void * buf)44*dbc13050SXiaoDong Huang static int rockchip_pm_config_ioctl(struct udevice *dev,
45*dbc13050SXiaoDong Huang unsigned long request,
46*dbc13050SXiaoDong Huang void *buf)
47*dbc13050SXiaoDong Huang {
48*dbc13050SXiaoDong Huang int ret = -EINVAL;
49*dbc13050SXiaoDong Huang
50*dbc13050SXiaoDong Huang switch (request) {
51*dbc13050SXiaoDong Huang case IOCTL_REQ_START:
52*dbc13050SXiaoDong Huang break;
53*dbc13050SXiaoDong Huang default:
54*dbc13050SXiaoDong Huang printf("Unsupported ioctl: %ld\n", (ulong)request);
55*dbc13050SXiaoDong Huang break;
56*dbc13050SXiaoDong Huang }
57*dbc13050SXiaoDong Huang
58*dbc13050SXiaoDong Huang return ret;
59*dbc13050SXiaoDong Huang }
60*dbc13050SXiaoDong Huang
61*dbc13050SXiaoDong Huang static const struct misc_ops rockchip_pm_config_ops = {
62*dbc13050SXiaoDong Huang .ioctl = rockchip_pm_config_ioctl,
63*dbc13050SXiaoDong Huang };
64*dbc13050SXiaoDong Huang
parse_mcu_sleep_config(ofnode node)65*dbc13050SXiaoDong Huang static int parse_mcu_sleep_config(ofnode node)
66*dbc13050SXiaoDong Huang {
67*dbc13050SXiaoDong Huang int ret, len;
68*dbc13050SXiaoDong Huang ofnode mcu_sleep_node;
69*dbc13050SXiaoDong Huang ofnode child;
70*dbc13050SXiaoDong Huang struct arm_smccc_res res;
71*dbc13050SXiaoDong Huang struct rk_mcu_sleep_tags *config;
72*dbc13050SXiaoDong Huang struct rk_sleep_tag *slp_tag;
73*dbc13050SXiaoDong Huang char *end;
74*dbc13050SXiaoDong Huang
75*dbc13050SXiaoDong Huang mcu_sleep_node = ofnode_find_subnode(node, "rockchip-mcu-sleep-cfg");
76*dbc13050SXiaoDong Huang if (ofnode_valid(mcu_sleep_node) == 0) {
77*dbc13050SXiaoDong Huang ret = -ENODEV;
78*dbc13050SXiaoDong Huang goto out;
79*dbc13050SXiaoDong Huang }
80*dbc13050SXiaoDong Huang
81*dbc13050SXiaoDong Huang child = ofnode_first_subnode(mcu_sleep_node);
82*dbc13050SXiaoDong Huang if (ofnode_valid(child) == 0) {
83*dbc13050SXiaoDong Huang pr_err("%s: no valid child node in rockchip-mcu-sleep-cfg\n",
84*dbc13050SXiaoDong Huang __func__);
85*dbc13050SXiaoDong Huang ret = -ENODEV;
86*dbc13050SXiaoDong Huang goto out;
87*dbc13050SXiaoDong Huang }
88*dbc13050SXiaoDong Huang
89*dbc13050SXiaoDong Huang /*
90*dbc13050SXiaoDong Huang * 4kb for sleep parameters
91*dbc13050SXiaoDong Huang */
92*dbc13050SXiaoDong Huang res = sip_smc_request_share_mem(1, SHARE_PAGE_TYPE_SLEEP);
93*dbc13050SXiaoDong Huang if (res.a0 != 0) {
94*dbc13050SXiaoDong Huang pr_err("%s: no trust memory for mcu_sleep\n", __func__);
95*dbc13050SXiaoDong Huang ret = -ENOMEM;
96*dbc13050SXiaoDong Huang goto out;
97*dbc13050SXiaoDong Huang }
98*dbc13050SXiaoDong Huang
99*dbc13050SXiaoDong Huang /* Initialize core tag */
100*dbc13050SXiaoDong Huang memset((void *)res.a1, 0, sizeof(struct rk_mcu_sleep_tags));
101*dbc13050SXiaoDong Huang config = (struct rk_mcu_sleep_tags *)res.a1;
102*dbc13050SXiaoDong Huang config->core.hdr.tag = RK_ATAG_MCU_SLP_CORE;
103*dbc13050SXiaoDong Huang config->core.hdr.size = sizeof(struct rk_mcu_sleep_core_tag) / sizeof(u32);
104*dbc13050SXiaoDong Huang config->core.total_size = sizeof(struct rk_mcu_sleep_tags) -
105*dbc13050SXiaoDong Huang sizeof(struct rk_sleep_tag);
106*dbc13050SXiaoDong Huang
107*dbc13050SXiaoDong Huang slp_tag = &config->slp_tags;
108*dbc13050SXiaoDong Huang
109*dbc13050SXiaoDong Huang /* End point of sleep data */
110*dbc13050SXiaoDong Huang end = (char *)config + PAGE_SIZE - sizeof(struct rk_sleep_tag);
111*dbc13050SXiaoDong Huang
112*dbc13050SXiaoDong Huang ofnode_for_each_subnode(child, mcu_sleep_node) {
113*dbc13050SXiaoDong Huang /* Is overflow? */
114*dbc13050SXiaoDong Huang if ((char *)slp_tag->params >= end)
115*dbc13050SXiaoDong Huang break;
116*dbc13050SXiaoDong Huang
117*dbc13050SXiaoDong Huang ret = ofnode_read_u32_array(child, "rockchip,tag",
118*dbc13050SXiaoDong Huang &slp_tag->hdr.tag, 1);
119*dbc13050SXiaoDong Huang if (ret ||
120*dbc13050SXiaoDong Huang slp_tag->hdr.tag <= RK_ATAG_MCU_SLP_CORE ||
121*dbc13050SXiaoDong Huang slp_tag->hdr.tag >= RK_ATAG_MCU_SLP_MAX) {
122*dbc13050SXiaoDong Huang pr_err("%s: no or invalid rockchip,tag in %s\n",
123*dbc13050SXiaoDong Huang __func__, ofnode_get_name(child));
124*dbc13050SXiaoDong Huang
125*dbc13050SXiaoDong Huang continue;
126*dbc13050SXiaoDong Huang }
127*dbc13050SXiaoDong Huang
128*dbc13050SXiaoDong Huang len = ofnode_read_size(child, "rockchip,params");
129*dbc13050SXiaoDong Huang if (len > 0) {
130*dbc13050SXiaoDong Huang /* Is overflow? */
131*dbc13050SXiaoDong Huang if ((char *)(slp_tag->params + len) >= end) {
132*dbc13050SXiaoDong Huang pr_warn("%s: no more space for rockchip,tag in %s\n",
133*dbc13050SXiaoDong Huang __func__, ofnode_get_name(child));
134*dbc13050SXiaoDong Huang break;
135*dbc13050SXiaoDong Huang }
136*dbc13050SXiaoDong Huang
137*dbc13050SXiaoDong Huang ret = ofnode_read_u32_array(child, "rockchip,params",
138*dbc13050SXiaoDong Huang slp_tag->params,
139*dbc13050SXiaoDong Huang len / sizeof(u32));
140*dbc13050SXiaoDong Huang if (ret) {
141*dbc13050SXiaoDong Huang pr_err("%s: read rockchip,params error in %s\n",
142*dbc13050SXiaoDong Huang __func__, ofnode_get_name(child));
143*dbc13050SXiaoDong Huang break;
144*dbc13050SXiaoDong Huang }
145*dbc13050SXiaoDong Huang
146*dbc13050SXiaoDong Huang slp_tag->hdr.size =
147*dbc13050SXiaoDong Huang (len + sizeof(struct rk_tag_header)) / sizeof(u32);
148*dbc13050SXiaoDong Huang } else if (len == 0) {
149*dbc13050SXiaoDong Huang slp_tag->hdr.size = 0;
150*dbc13050SXiaoDong Huang } else {
151*dbc13050SXiaoDong Huang continue;
152*dbc13050SXiaoDong Huang }
153*dbc13050SXiaoDong Huang
154*dbc13050SXiaoDong Huang config->core.total_size += slp_tag->hdr.size * sizeof(u32);
155*dbc13050SXiaoDong Huang
156*dbc13050SXiaoDong Huang slp_tag = sleep_tag_next(slp_tag);
157*dbc13050SXiaoDong Huang }
158*dbc13050SXiaoDong Huang
159*dbc13050SXiaoDong Huang /* Add none tag.
160*dbc13050SXiaoDong Huang * Compiler will combine the follow code as "str xzr, [x28]", but
161*dbc13050SXiaoDong Huang * "slp->hdr" may not be 8-byte alignment. So we use memset_io instead:
162*dbc13050SXiaoDong Huang * slp_tag->hdr.size = 0;
163*dbc13050SXiaoDong Huang * slp_tag->hdr.tag = RK_ATAG_NONE;
164*dbc13050SXiaoDong Huang */
165*dbc13050SXiaoDong Huang memset_io(&slp_tag->hdr, 0, sizeof(slp_tag->hdr));
166*dbc13050SXiaoDong Huang
167*dbc13050SXiaoDong Huang config->core.total_size += sizeof(struct rk_sleep_tag);
168*dbc13050SXiaoDong Huang
169*dbc13050SXiaoDong Huang ret = 0;
170*dbc13050SXiaoDong Huang
171*dbc13050SXiaoDong Huang out:
172*dbc13050SXiaoDong Huang return ret;
173*dbc13050SXiaoDong Huang }
174*dbc13050SXiaoDong Huang
rockchip_pm_config_probe(struct udevice * dev)175*dbc13050SXiaoDong Huang static int rockchip_pm_config_probe(struct udevice *dev)
176*dbc13050SXiaoDong Huang {
177*dbc13050SXiaoDong Huang parse_mcu_sleep_config(dev_ofnode(dev));
178*dbc13050SXiaoDong Huang
179*dbc13050SXiaoDong Huang return 0;
180*dbc13050SXiaoDong Huang }
181*dbc13050SXiaoDong Huang
182*dbc13050SXiaoDong Huang static const struct udevice_id rockchip_pm_config_ids[] = {
183*dbc13050SXiaoDong Huang { .compatible = "rockchip,pm-rk3588" },
184*dbc13050SXiaoDong Huang {}
185*dbc13050SXiaoDong Huang };
186*dbc13050SXiaoDong Huang
187*dbc13050SXiaoDong Huang U_BOOT_DRIVER(rockchip_pm_config) = {
188*dbc13050SXiaoDong Huang .name = "rockchip_pm_config",
189*dbc13050SXiaoDong Huang .id = UCLASS_MISC,
190*dbc13050SXiaoDong Huang .of_match = rockchip_pm_config_ids,
191*dbc13050SXiaoDong Huang .probe = rockchip_pm_config_probe,
192*dbc13050SXiaoDong Huang .ops = &rockchip_pm_config_ops,
193*dbc13050SXiaoDong Huang };
194