1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  SCMI (System Control and Management Interface) utility command
4  *
5  *  Copyright (c) 2023 Linaro Limited
6  *		Author: AKASHI Takahiro
7  */
8 
9 #include <command.h>
10 #include <exports.h>
11 #include <scmi_agent.h>
12 #include <scmi_agent-uclass.h>
13 #include <stdlib.h>
14 #include <asm/types.h>
15 #include <dm/device.h>
16 #include <dm/uclass.h> /* uclass_get_device */
17 #include <linux/bitfield.h>
18 #include <linux/bitops.h>
19 
20 struct {
21 	enum scmi_std_protocol id;
22 	const char *name;
23 } protocol_name[] = {
24 	{SCMI_PROTOCOL_ID_BASE, "Base"},
25 	{SCMI_PROTOCOL_ID_POWER_DOMAIN, "Power domain management"},
26 	{SCMI_PROTOCOL_ID_SYSTEM, "System power management"},
27 	{SCMI_PROTOCOL_ID_PERF, "Performance domain management"},
28 	{SCMI_PROTOCOL_ID_CLOCK, "Clock management"},
29 	{SCMI_PROTOCOL_ID_SENSOR, "Sensor management"},
30 	{SCMI_PROTOCOL_ID_RESET_DOMAIN, "Reset domain management"},
31 	{SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN, "Voltage domain management"},
32 };
33 
34 /**
35  * get_agent() - get SCMI agent device
36  *
37  * Return:	Pointer to SCMI agent device on success, NULL on failure
38  */
get_agent(void)39 static struct udevice *get_agent(void)
40 {
41 	struct udevice *agent;
42 
43 	if (uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent)) {
44 		printf("Cannot find any SCMI agent\n");
45 		return NULL;
46 	}
47 
48 	return agent;
49 }
50 
51 /**
52  * get_base_proto() - get SCMI base protocol device
53  * @agent:	SCMI agent device
54  *
55  * Return:	Pointer to SCMI base protocol device on success,
56  *		NULL on failure
57  */
get_base_proto(struct udevice * agent)58 static struct udevice *get_base_proto(struct udevice *agent)
59 {
60 	struct udevice *base_proto;
61 
62 	if (!agent) {
63 		agent = get_agent();
64 		if (!agent)
65 			return NULL;
66 	}
67 
68 	base_proto = scmi_get_protocol(agent, SCMI_PROTOCOL_ID_BASE);
69 	if (!base_proto) {
70 		printf("SCMI base protocol not found\n");
71 		return NULL;
72 	}
73 
74 	return base_proto;
75 }
76 
77 /**
78  * get_proto_name() - get the name of SCMI protocol
79  *
80  * @id:		SCMI Protocol ID
81  *
82  * Get the printable name of the protocol, @id
83  *
84  * Return:	Name string on success, NULL on failure
85  */
get_proto_name(enum scmi_std_protocol id)86 static const char *get_proto_name(enum scmi_std_protocol id)
87 {
88 	int i;
89 
90 	for (i = 0; i < ARRAY_SIZE(protocol_name); i++)
91 		if (id == protocol_name[i].id)
92 			return protocol_name[i].name;
93 
94 	return NULL;
95 }
96 
97 /**
98  * do_scmi_info() - get the information of SCMI services
99  *
100  * @cmdtp:	Command table
101  * @flag:	Command flag
102  * @argc:	Number of arguments
103  * @argv:	Argument array
104  *
105  * Get the information of SCMI services using various interfaces
106  * provided by the Base protocol.
107  *
108  * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
109  */
do_scmi_info(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])110 static int do_scmi_info(struct cmd_tbl *cmdtp, int flag, int argc,
111 			char * const argv[])
112 {
113 	struct udevice *agent, *base_proto;
114 	u32 agent_id, num_protocols;
115 	u8 *agent_name, *protocols;
116 	int i, ret;
117 
118 	if (argc != 1)
119 		return CMD_RET_USAGE;
120 
121 	agent = get_agent();
122 	if (!agent)
123 		return CMD_RET_FAILURE;
124 	base_proto = get_base_proto(agent);
125 	if (!base_proto)
126 		return CMD_RET_FAILURE;
127 
128 	printf("SCMI device: %s\n", agent->name);
129 	printf("  protocol version: 0x%x\n", scmi_version(agent));
130 	printf("  # of agents: %d\n", scmi_num_agents(agent));
131 	for (i = 0; i < scmi_num_agents(agent); i++) {
132 		ret = scmi_base_discover_agent(base_proto, i, &agent_id,
133 					       &agent_name);
134 		if (ret) {
135 			if (ret != -EOPNOTSUPP)
136 				printf("base_discover_agent() failed for id: %d (%d)\n",
137 				       i, ret);
138 			break;
139 		}
140 		printf("    %c%2d: %s\n", i == scmi_agent_id(agent) ? '>' : ' ',
141 		       i, agent_name);
142 		free(agent_name);
143 	}
144 	printf("  # of protocols: %d\n", scmi_num_protocols(agent));
145 	num_protocols = scmi_num_protocols(agent);
146 	protocols = scmi_protocols(agent);
147 	if (protocols)
148 		for (i = 0; i < num_protocols; i++)
149 			printf("      %s\n", get_proto_name(protocols[i]));
150 	printf("  vendor: %s\n", scmi_vendor(agent));
151 	printf("  sub vendor: %s\n", scmi_sub_vendor(agent));
152 	printf("  impl version: 0x%x\n", scmi_impl_version(agent));
153 
154 	return CMD_RET_SUCCESS;
155 }
156 
157 /**
158  * do_scmi_set_dev() - set access permission to device
159  *
160  * @cmdtp:	Command table
161  * @flag:	Command flag
162  * @argc:	Number of arguments
163  * @argv:	Argument array
164  *
165  * Set access permission to device with SCMI_BASE_SET_DEVICE_PERMISSIONS
166  *
167  * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
168  */
do_scmi_set_dev(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])169 static int do_scmi_set_dev(struct cmd_tbl *cmdtp, int flag, int argc,
170 			   char * const argv[])
171 {
172 	u32 agent_id, device_id, flags, attributes;
173 	char *end;
174 	struct udevice *base_proto;
175 	int ret;
176 
177 	if (argc != 4)
178 		return CMD_RET_USAGE;
179 
180 	agent_id = simple_strtoul(argv[1], &end, 16);
181 	if (*end != '\0')
182 		return CMD_RET_USAGE;
183 
184 	device_id = simple_strtoul(argv[2], &end, 16);
185 	if (*end != '\0')
186 		return CMD_RET_USAGE;
187 
188 	flags = simple_strtoul(argv[3], &end, 16);
189 	if (*end != '\0')
190 		return CMD_RET_USAGE;
191 
192 	base_proto = get_base_proto(NULL);
193 	if (!base_proto)
194 		return CMD_RET_FAILURE;
195 
196 	ret = scmi_base_protocol_message_attrs(base_proto,
197 					       SCMI_BASE_SET_DEVICE_PERMISSIONS,
198 					       &attributes);
199 	if (ret) {
200 		printf("This operation is not supported\n");
201 		return CMD_RET_FAILURE;
202 	}
203 
204 	ret = scmi_base_set_device_permissions(base_proto, agent_id,
205 					       device_id, flags);
206 	if (ret) {
207 		printf("%s access to device:%u failed (%d)\n",
208 		       flags ? "Allowing" : "Denying", device_id, ret);
209 		return CMD_RET_FAILURE;
210 	}
211 
212 	return CMD_RET_SUCCESS;
213 }
214 
215 /**
216  * do_scmi_set_proto() - set protocol permission to device
217  *
218  * @cmdtp:	Command table
219  * @flag:	Command flag
220  * @argc:	Number of arguments
221  * @argv:	Argument array
222  *
223  * Set protocol permission to device with SCMI_BASE_SET_PROTOCOL_PERMISSIONS
224  *
225  * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
226  */
do_scmi_set_proto(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])227 static int do_scmi_set_proto(struct cmd_tbl *cmdtp, int flag, int argc,
228 			     char * const argv[])
229 {
230 	u32 agent_id, device_id, protocol_id, flags, attributes;
231 	char *end;
232 	struct udevice *base_proto;
233 	int ret;
234 
235 	if (argc != 5)
236 		return CMD_RET_USAGE;
237 
238 	agent_id = simple_strtoul(argv[1], &end, 16);
239 	if (*end != '\0')
240 		return CMD_RET_USAGE;
241 
242 	device_id = simple_strtoul(argv[2], &end, 16);
243 	if (*end != '\0')
244 		return CMD_RET_USAGE;
245 
246 	protocol_id = simple_strtoul(argv[3], &end, 16);
247 	if (*end != '\0')
248 		return CMD_RET_USAGE;
249 
250 	flags = simple_strtoul(argv[4], &end, 16);
251 	if (*end != '\0')
252 		return CMD_RET_USAGE;
253 
254 	base_proto = get_base_proto(NULL);
255 	if (!base_proto)
256 		return CMD_RET_FAILURE;
257 
258 	ret = scmi_base_protocol_message_attrs(base_proto,
259 					       SCMI_BASE_SET_PROTOCOL_PERMISSIONS,
260 					       &attributes);
261 	if (ret) {
262 		printf("This operation is not supported\n");
263 		return CMD_RET_FAILURE;
264 	}
265 
266 	ret = scmi_base_set_protocol_permissions(base_proto, agent_id,
267 						 device_id, protocol_id,
268 						 flags);
269 	if (ret) {
270 		printf("%s access to protocol:0x%x on device:%u failed (%d)\n",
271 		       flags ? "Allowing" : "Denying", protocol_id, device_id,
272 		       ret);
273 		return CMD_RET_FAILURE;
274 	}
275 
276 	return CMD_RET_SUCCESS;
277 }
278 
279 /**
280  * do_scmi_reset() - reset platform resource settings
281  *
282  * @cmdtp:	Command table
283  * @flag:	Command flag
284  * @argc:	Number of arguments
285  * @argv:	Argument array
286  *
287  * Reset platform resource settings with BASE_RESET_AGENT_CONFIGURATION
288  *
289  * Return:	CMD_RET_SUCCESS on success, CMD_RET_RET_FAILURE on failure
290  */
do_scmi_reset(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])291 static int do_scmi_reset(struct cmd_tbl *cmdtp, int flag, int argc,
292 			 char * const argv[])
293 {
294 	u32 agent_id, flags, attributes;
295 	char *end;
296 	struct udevice *base_proto;
297 	int ret;
298 
299 	if (argc != 3)
300 		return CMD_RET_USAGE;
301 
302 	agent_id = simple_strtoul(argv[1], &end, 16);
303 	if (*end != '\0')
304 		return CMD_RET_USAGE;
305 
306 	flags = simple_strtoul(argv[2], &end, 16);
307 	if (*end != '\0')
308 		return CMD_RET_USAGE;
309 
310 	base_proto = get_base_proto(NULL);
311 	if (!base_proto)
312 		return CMD_RET_FAILURE;
313 
314 	ret = scmi_base_protocol_message_attrs(base_proto,
315 					       SCMI_BASE_RESET_AGENT_CONFIGURATION,
316 					       &attributes);
317 	if (ret) {
318 		printf("Reset is not supported\n");
319 		return CMD_RET_FAILURE;
320 	}
321 
322 	ret = scmi_base_reset_agent_configuration(base_proto, agent_id, flags);
323 	if (ret) {
324 		printf("Reset failed (%d)\n", ret);
325 		return CMD_RET_FAILURE;
326 	}
327 
328 	return CMD_RET_SUCCESS;
329 }
330 
331 static struct cmd_tbl cmd_scmi_sub[] = {
332 	U_BOOT_CMD_MKENT(info, CONFIG_SYS_MAXARGS, 1,
333 			 do_scmi_info, "", ""),
334 	U_BOOT_CMD_MKENT(perm_dev, CONFIG_SYS_MAXARGS, 1,
335 			 do_scmi_set_dev, "", ""),
336 	U_BOOT_CMD_MKENT(perm_proto, CONFIG_SYS_MAXARGS, 1,
337 			 do_scmi_set_proto, "", ""),
338 	U_BOOT_CMD_MKENT(reset, CONFIG_SYS_MAXARGS, 1,
339 			 do_scmi_reset, "", ""),
340 };
341 
342 /**
343  * do_scmi() - SCMI utility
344  *
345  * @cmdtp:	Command table
346  * @flag:	Command flag
347  * @argc:	Number of arguments
348  * @argv:	Argument array
349  *
350  * Provide user interfaces to SCMI protocols.
351  *
352  * Return:	CMD_RET_SUCCESS on success,
353  *		CMD_RET_USAGE or CMD_RET_RET_FAILURE on failure
354  */
do_scmi(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])355 static int do_scmi(struct cmd_tbl *cmdtp, int flag,
356 		   int argc, char *const argv[])
357 {
358 	struct cmd_tbl *cp;
359 
360 	if (argc < 2)
361 		return CMD_RET_USAGE;
362 
363 	argc--; argv++;
364 
365 	cp = find_cmd_tbl(argv[0], cmd_scmi_sub, ARRAY_SIZE(cmd_scmi_sub));
366 	if (!cp)
367 		return CMD_RET_USAGE;
368 
369 	return cp->cmd(cmdtp, flag, argc, argv);
370 }
371 
372 U_BOOT_LONGHELP(scmi,
373 	" - SCMI utility\n"
374 	" info - get the info of SCMI services\n"
375 	" perm_dev <agent-id in hex> <device-id in hex> <flags in hex>\n"
376 	"   - set access permission to device\n"
377 	" perm_proto <agent-id in hex> <device-id in hex> <protocol-id in hex> <flags in hex>\n"
378 	"   - set protocol permission to device\n"
379 	" reset <agent-id in hex> <flags in hex>\n"
380 	"   - reset platform resource settings\n");
381 
382 U_BOOT_CMD(scmi, CONFIG_SYS_MAXARGS, 0, do_scmi, "SCMI utility",
383 	   scmi_help_text);
384