1 /*
2  * Copyright 2025 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/shell/shell.h>
8 #include <stdlib.h>
9 #include <zephyr/drivers/ptp_clock.h>
10 #include <string.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/devicetree.h>
13 
14 #include <zephyr/logging/log.h>
15 LOG_MODULE_REGISTER(ptp_clock_shell, CONFIG_LOG_DEFAULT_LEVEL);
16 
device_is_ptp_clock(const struct device * dev)17 static bool device_is_ptp_clock(const struct device *dev)
18 {
19 	return DEVICE_API_IS(ptp_clock, dev);
20 }
21 
device_name_get(size_t idx,struct shell_static_entry * entry)22 static void device_name_get(size_t idx, struct shell_static_entry *entry)
23 {
24 	const struct device *dev = shell_device_filter(idx, device_is_ptp_clock);
25 
26 	entry->syntax = (dev != NULL) ? dev->name : NULL;
27 	entry->handler = NULL;
28 	entry->help = NULL;
29 	entry->subcmd = NULL;
30 }
31 
32 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
33 
parse_device_arg(const struct shell * sh,char ** argv,const struct device ** dev)34 static int parse_device_arg(const struct shell *sh, char **argv, const struct device **dev)
35 {
36 	*dev = shell_device_get_binding(argv[1]);
37 	if (!*dev) {
38 		shell_error(sh, "device %s not found", argv[1]);
39 		return -ENODEV;
40 	}
41 	return 0;
42 }
43 
44 /* ptp_clock get <device> */
cmd_ptp_clock_get(const struct shell * sh,size_t argc,char ** argv)45 static int cmd_ptp_clock_get(const struct shell *sh, size_t argc, char **argv)
46 {
47 	struct net_ptp_time tm = {0};
48 	const struct device *dev;
49 	int ret;
50 
51 	ret = parse_device_arg(sh, argv, &dev);
52 	if (ret < 0) {
53 		return ret;
54 	}
55 
56 	ret = ptp_clock_get(dev, &tm);
57 	if (ret < 0) {
58 		return ret;
59 	}
60 
61 	shell_print(sh, "%"PRIu64".%09u", tm.second, tm.nanosecond);
62 
63 	return 0;
64 }
65 
66 /* ptp_clock set <device> <seconds> */
cmd_ptp_clock_set(const struct shell * sh,size_t argc,char ** argv)67 static int cmd_ptp_clock_set(const struct shell *sh, size_t argc, char **argv)
68 {
69 	struct net_ptp_time tm = {0};
70 	const struct device *dev;
71 	int ret;
72 
73 	ret = parse_device_arg(sh, argv, &dev);
74 	if (ret < 0) {
75 		return ret;
76 	}
77 
78 	tm.second = shell_strtoull(argv[2], 10, &ret);
79 	if (ret < 0) {
80 		return ret;
81 	}
82 
83 	ret = ptp_clock_set(dev, &tm);
84 	if (ret < 0) {
85 		return ret;
86 	}
87 
88 	return 0;
89 }
90 
91 /* ptp_clock adj <device> <seconds> */
cmd_ptp_clock_adj(const struct shell * sh,size_t argc,char ** argv)92 static int cmd_ptp_clock_adj(const struct shell *sh, size_t argc, char **argv)
93 {
94 	const struct device *dev;
95 	int adj;
96 	int ret;
97 
98 	ret = parse_device_arg(sh, argv, &dev);
99 	if (ret < 0) {
100 		return ret;
101 	}
102 
103 	adj = shell_strtol(argv[2], 10, &ret);
104 	if (ret < 0) {
105 		return ret;
106 	}
107 
108 	ret = ptp_clock_adjust(dev, adj);
109 	if (ret < 0) {
110 		return ret;
111 	}
112 
113 	return 0;
114 }
115 
116 /* ptp_clock freq <device> <ppb> */
cmd_ptp_clock_freq(const struct shell * sh,size_t argc,char ** argv)117 static int cmd_ptp_clock_freq(const struct shell *sh, size_t argc, char **argv)
118 {
119 	const struct device *dev;
120 	int ppb;
121 	int ret;
122 
123 	ret = parse_device_arg(sh, argv, &dev);
124 	if (ret < 0) {
125 		return ret;
126 	}
127 
128 	ppb = shell_strtol(argv[2], 10, &ret);
129 	if (ret < 0) {
130 		return ret;
131 	}
132 
133 	ret = ptp_clock_rate_adjust(dev, 1.0 + ((double)ppb / 1000000000.0));
134 	if (ret < 0) {
135 		return ret;
136 	}
137 
138 	return 0;
139 }
140 
141 /* ptp_clock selftest <device> <time> <freq> <delay> <adj> */
cmd_ptp_clock_selftest(const struct shell * sh,size_t argc,char ** argv)142 static int cmd_ptp_clock_selftest(const struct shell *sh, size_t argc, char **argv)
143 {
144 	struct net_ptp_time tm = {0};
145 	const struct device *dev;
146 	int freq, delay, adj, ret;
147 	uint64_t seconds;
148 
149 	ret = parse_device_arg(sh, argv, &dev);
150 	if (ret < 0) {
151 		return ret;
152 	}
153 
154 	seconds = shell_strtoull(argv[2], 10, &ret);
155 	if (ret < 0) {
156 		return ret;
157 	}
158 
159 	freq = shell_strtol(argv[3], 10, &ret);
160 	if (ret < 0) {
161 		return ret;
162 	}
163 
164 	delay = shell_strtol(argv[4], 10, &ret);
165 	if (ret < 0) {
166 		return ret;
167 	}
168 
169 	adj = shell_strtol(argv[5], 10, &ret);
170 	if (ret < 0) {
171 		return ret;
172 	}
173 
174 	/* set 'time' seconds and read back to verify clock setting/getting */
175 	tm.second = seconds;
176 	ret = ptp_clock_set(dev, &tm);
177 	if (ret < 0) {
178 		shell_print(sh, "failed to set time");
179 		return ret;
180 	}
181 	shell_print(sh, "test1: set time %"PRIu64".%09u", tm.second, tm.nanosecond);
182 
183 	ret = ptp_clock_get(dev, &tm);
184 	if (ret < 0) {
185 		shell_print(sh, "failed to get time");
186 		return ret;
187 	}
188 	shell_print(sh, "  result: read back time %"PRIu64".%09u", tm.second, tm.nanosecond);
189 
190 	/* set 'freq' with ppb value, sleep 'delay' seconds, and read back time */
191 	ret = ptp_clock_rate_adjust(dev, 1.0 + ((double)freq / 1000000000.0));
192 	if (ret < 0) {
193 		shell_print(sh, "failed to adjust rate");
194 		return ret;
195 	}
196 	shell_print(sh, "test2: adjust frequency %d ppb (ratio %f), delay %d seconds...",
197 		    freq, 1.0 + ((double)freq / 1000000000.0), delay);
198 
199 	k_sleep(K_SECONDS(delay));
200 
201 	ret = ptp_clock_get(dev, &tm);
202 	if (ret < 0) {
203 		shell_print(sh, "failed to get time");
204 		return ret;
205 	}
206 	shell_print(sh, "  result: read back time %"PRIu64".%09u", tm.second, tm.nanosecond);
207 
208 	/* set 'adj' seconds and read back time to verify time adjustment */
209 	ret = ptp_clock_adjust(dev, adj);
210 	if (ret < 0) {
211 		shell_print(sh, "failed to adjtime");
212 		return ret;
213 	}
214 	shell_print(sh, "test3: adjust time %d seconds", adj);
215 
216 	ret = ptp_clock_get(dev, &tm);
217 	if (ret < 0) {
218 		shell_print(sh, "failed to get time");
219 		return ret;
220 	}
221 	shell_print(sh, "  result: read back time %"PRIu64".%09u", tm.second, tm.nanosecond);
222 
223 	return 0;
224 }
225 
226 SHELL_STATIC_SUBCMD_SET_CREATE(sub_ptp_clock_cmds,
227 	SHELL_CMD_ARG(get, &dsub_device_name,
228 		"Get time: get <device>",
229 		cmd_ptp_clock_get, 2, 0),
230 	SHELL_CMD_ARG(set, &dsub_device_name,
231 		"Set time: set <device> <seconds>",
232 		cmd_ptp_clock_set, 3, 0),
233 	SHELL_CMD_ARG(adj, &dsub_device_name,
234 		"Adjust time: adj <device> <seconds>",
235 		cmd_ptp_clock_adj, 3, 0),
236 	SHELL_CMD_ARG(freq, &dsub_device_name,
237 		"Adjust frequency: freq <device> <ppb>",
238 		cmd_ptp_clock_freq, 3, 0),
239 	SHELL_CMD_ARG(selftest, &dsub_device_name,
240 		"selftest <device> <time> <freq> <delay> <adj>\n"
241 		"The selftest will do following steps:\n"
242 		"1. set 'time' with seconds and read back to\n"
243 		"   verify clock setting/getting.\n"
244 		"2. set 'freq' with ppb value, sleep 'delay' seconds,\n"
245 		"    and read back time to verify rate adjustment.\n"
246 		"3. set 'adj' seconds and read back time to\n"
247 		"   verify time adjustment.\n"
248 		"Example:\n"
249 		"   ptp_clock selftest ptp_clock 1000 100000000 10 10",
250 		cmd_ptp_clock_selftest, 6, 0),
251 	SHELL_SUBCMD_SET_END     /* Array terminated. */
252 );
253 
254 SHELL_CMD_REGISTER(ptp_clock, &sub_ptp_clock_cmds, "PTP clock commands", NULL);
255