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