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
20struct {
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 */
39static 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 */
58static 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 */
86static 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 */
110static 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 */
169static 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 */
227static 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 */
291static 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
331static 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 */
355static 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
372static char scmi_help_text[] =
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
383U_BOOT_CMD(scmi, CONFIG_SYS_MAXARGS, 0, do_scmi, "SCMI utility",
384	   scmi_help_text);
385