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