xref: /rk3399_rockchip-uboot/drivers/misc/rockchip_pm_config.c (revision dbc130507a9db6b26df08dff9aa6a2d48a41691c)
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