1 /*
2  * Copyright (c) 2025 Basalte bv
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 /**
8  * @file
9  * @brief SYSCON shell commands.
10  */
11 
12 #include <zephyr/shell/shell.h>
13 #include <zephyr/drivers/syscon.h>
14 
cmd_base(const struct shell * sh,size_t argc,char ** argv)15 static int cmd_base(const struct shell *sh, size_t argc, char **argv)
16 {
17 	const struct device *dev;
18 	uintptr_t base;
19 	int ret;
20 
21 	dev = shell_device_get_binding(argv[1]);
22 	if (!device_is_ready(dev)) {
23 		shell_error(sh, "SYSCON device not ready");
24 		return -ENODEV;
25 	}
26 
27 	ret = syscon_get_base(dev, &base);
28 	if (ret < 0) {
29 		shell_error(sh, "Failed to get SYSCON base (%d)", ret);
30 		return ret;
31 	}
32 
33 	shell_print(sh, "0x%lx", base);
34 	return 0;
35 }
36 
cmd_read(const struct shell * sh,size_t argc,char ** argv)37 static int cmd_read(const struct shell *sh, size_t argc, char **argv)
38 {
39 	const struct device *dev;
40 	unsigned long addr;
41 	uint32_t val;
42 	int ret;
43 
44 	dev = shell_device_get_binding(argv[1]);
45 	if (!device_is_ready(dev)) {
46 		shell_error(sh, "SYSCON device not ready");
47 		return -ENODEV;
48 	}
49 
50 	addr = shell_strtoul(argv[2], 0, &ret);
51 	if (ret < 0 || addr > UINT16_MAX) {
52 		shell_error(sh, "Invalid address %s (%d)", argv[2], ret);
53 		return -EINVAL;
54 	}
55 
56 	ret = syscon_read_reg(dev, addr, &val);
57 	if (ret < 0) {
58 		shell_error(sh, "Failed to read (%d)", ret);
59 		return ret;
60 	}
61 
62 	shell_print(sh, "0x%x", val);
63 	return 0;
64 }
65 
cmd_write(const struct shell * sh,size_t argc,char ** argv)66 static int cmd_write(const struct shell *sh, size_t argc, char **argv)
67 {
68 	const struct device *dev;
69 	unsigned long addr, val;
70 	int ret;
71 
72 	dev = shell_device_get_binding(argv[1]);
73 	if (!device_is_ready(dev)) {
74 		shell_error(sh, "SYSCON device not ready");
75 		return -ENODEV;
76 	}
77 
78 	addr = shell_strtoul(argv[2], 0, &ret);
79 	if (ret < 0 || addr > UINT16_MAX) {
80 		shell_error(sh, "Invalid address %s (%d)", argv[2], ret);
81 		return -EINVAL;
82 	}
83 
84 	val = shell_strtoul(argv[3], 0, &ret);
85 	if (ret < 0 || val > UINT32_MAX) {
86 		shell_error(sh, "Invalid value %s (%d)", argv[3], ret);
87 		return -EINVAL;
88 	}
89 
90 	ret = syscon_write_reg(dev, addr, val);
91 	if (ret < 0) {
92 		shell_error(sh, "Failed to write (%d)", ret);
93 		return ret;
94 	}
95 
96 	return 0;
97 }
98 
cmd_size(const struct shell * sh,size_t argc,char ** argv)99 static int cmd_size(const struct shell *sh, size_t argc, char **argv)
100 {
101 	const struct device *dev;
102 	size_t size;
103 	int ret;
104 
105 	dev = shell_device_get_binding(argv[1]);
106 	if (!device_is_ready(dev)) {
107 		shell_error(sh, "SYSCON device not ready");
108 		return -ENODEV;
109 	}
110 
111 	ret = syscon_get_size(dev, &size);
112 	if (ret < 0) {
113 		shell_error(sh, "Failed to get SYSCON size (%d)", ret);
114 		return ret;
115 	}
116 
117 	shell_print(sh, "%zu bytes", size);
118 	return 0;
119 }
120 
device_is_syscon(const struct device * dev)121 static bool device_is_syscon(const struct device *dev)
122 {
123 	return DEVICE_API_IS(syscon, dev);
124 }
125 
126 /* Device name autocompletion support */
device_name_get(size_t idx,struct shell_static_entry * entry)127 static void device_name_get(size_t idx, struct shell_static_entry *entry)
128 {
129 	const struct device *dev = shell_device_filter(idx, device_is_syscon);
130 
131 	entry->syntax = (dev != NULL) ? dev->name : NULL;
132 	entry->handler = NULL;
133 	entry->help = NULL;
134 	entry->subcmd = NULL;
135 }
136 
137 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
138 
139 SHELL_STATIC_SUBCMD_SET_CREATE(syscon_cmds,
140 	SHELL_CMD_ARG(base, &dsub_device_name,
141 		      SHELL_HELP("Get the SYSCON device base address", "<device>"),
142 		      cmd_base, 2, 0),
143 	SHELL_CMD_ARG(read, &dsub_device_name,
144 		      SHELL_HELP("Read from a SYSCON device register", "<device> <address>"),
145 		      cmd_read, 3, 0),
146 	SHELL_CMD_ARG(write, &dsub_device_name,
147 		      SHELL_HELP("Write to a SYSCON device register", "<device> <address> <value>"),
148 		      cmd_write, 4, 0),
149 	SHELL_CMD_ARG(size, &dsub_device_name,
150 		      SHELL_HELP("Print the SYSCON device size in bytes", "<device>"),
151 		      cmd_size, 2, 0),
152 	SHELL_SUBCMD_SET_END
153 );
154 
155 SHELL_CMD_REGISTER(syscon, &syscon_cmds, "SYSCON shell commands", NULL);
156