1 /*
2 * Copyright (c) 2023 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/drivers/counter.h>
8 #include <zephyr/shell/shell.h>
9 #include <stdio.h>
10 #include <ctype.h>
11 #include <stdlib.h>
12
13 #define ARGV_DEV 1
14 #define ARGV_CHN 2
15 #define ARGV_PERIODIC_TIME 2
16 #define ARGV_ONESHOT_TIME 3
17
18 /* number of periodic interrupts */
19 #define PERIODIC_CYCLES 10
20 #define MAX_DELAY UINT32_MAX
21 #define MAX_CHANNEL 255U
22
23 static struct k_sem timer_sem;
24
timer_top_handler(const struct device * counter_dev,void * user_data)25 static void timer_top_handler(const struct device *counter_dev, void *user_data)
26 {
27 ARG_UNUSED(counter_dev);
28
29 k_sem_give(&timer_sem);
30 }
31
timer_alarm_handler(const struct device * counter_dev,uint8_t chan_id,uint32_t ticks,void * user_data)32 static void timer_alarm_handler(const struct device *counter_dev, uint8_t chan_id,
33 uint32_t ticks, void *user_data)
34 {
35 ARG_UNUSED(counter_dev);
36
37 k_sem_give(&timer_sem);
38 }
39
parse_device(const struct shell * shctx,size_t argc,char * argv[],const struct device ** timer_dev)40 static inline int parse_device(const struct shell *shctx, size_t argc, char *argv[],
41 const struct device **timer_dev)
42 {
43 ARG_UNUSED(argc);
44
45 *timer_dev = shell_device_get_binding(argv[ARGV_DEV]);
46 if (*timer_dev == NULL) {
47 shell_error(shctx, "Timer: Device %s not found", argv[ARGV_DEV]);
48 return -ENODEV;
49 }
50
51 return 0;
52 }
53
cmd_timer_free_running(const struct shell * shctx,size_t argc,char ** argv)54 static int cmd_timer_free_running(const struct shell *shctx, size_t argc, char **argv)
55 {
56 const struct device *timer_dev;
57 int err = parse_device(shctx, argc, argv, &timer_dev);
58
59 if (err != 0) {
60 return err;
61 }
62
63 /* start timer in free running mode */
64 err = counter_start(timer_dev);
65 if (err != 0) {
66 shell_error(shctx, "%s is not available err:%d", argv[ARGV_DEV], err);
67 return err;
68 }
69
70 shell_info(shctx, "%s: Timer is freerunning", argv[ARGV_DEV]);
71
72 return 0;
73 }
74
cmd_timer_stop(const struct shell * shctx,size_t argc,char ** argv)75 static int cmd_timer_stop(const struct shell *shctx, size_t argc, char **argv)
76 {
77 uint32_t ticks1 = 0, ticks2 = 0;
78 const struct device *timer_dev;
79 int err = parse_device(shctx, argc, argv, &timer_dev);
80
81 if (err != 0) {
82 return err;
83 }
84
85 counter_stop(timer_dev);
86
87 counter_get_value(timer_dev, &ticks1);
88 counter_get_value(timer_dev, &ticks2);
89
90 if (ticks1 == ticks2) {
91 shell_info(shctx, "Timer Stopped");
92 } else {
93 shell_error(shctx, "Failed to stop timer");
94 return -EIO;
95 }
96
97 return 0;
98 }
99
cmd_timer_oneshot(const struct shell * shctx,size_t argc,char ** argv)100 static int cmd_timer_oneshot(const struct shell *shctx, size_t argc, char **argv)
101 {
102 unsigned long delay = 0;
103 unsigned long channel = 0;
104 const struct device *timer_dev;
105 int err = parse_device(shctx, argc, argv, &timer_dev);
106 struct counter_alarm_cfg alarm_cfg;
107
108 k_sem_init(&timer_sem, 0, 1);
109
110 if (err != 0) {
111 return err;
112 }
113
114 delay = shell_strtoul(argv[ARGV_ONESHOT_TIME], 10, &err);
115 if (err != 0) {
116 shell_error(shctx, "invalid delay parameter");
117 return err;
118 } else if (delay > MAX_DELAY) {
119 shell_error(shctx, "delay out of range");
120 return -ERANGE;
121 }
122
123 channel = shell_strtoul(argv[ARGV_CHN], 10, &err);
124 if (err != 0) {
125 shell_error(shctx, "invalid channel parameter");
126 return err;
127 } else if (channel > MAX_CHANNEL) {
128 shell_error(shctx, "channel out of range");
129 return -ERANGE;
130 }
131
132 alarm_cfg.flags = 0;
133 alarm_cfg.ticks = counter_us_to_ticks(timer_dev, (uint64_t)delay);
134 alarm_cfg.callback = timer_alarm_handler;
135 alarm_cfg.user_data = NULL;
136
137 /* set an alarm */
138 err = counter_set_channel_alarm(timer_dev, (uint8_t)channel, &alarm_cfg);
139 if (err != 0) {
140 shell_error(shctx, "%s:Failed to set channel alarm, err:%d", argv[ARGV_DEV], err);
141 return err;
142 }
143
144 k_sem_take(&timer_sem, K_FOREVER);
145
146 shell_info(shctx, "%s: Alarm triggered", argv[ARGV_DEV]);
147
148 return 0;
149 }
150
cmd_timer_periodic(const struct shell * shctx,size_t argc,char ** argv)151 static int cmd_timer_periodic(const struct shell *shctx, size_t argc, char **argv)
152 {
153 ARG_UNUSED(argc);
154 uint32_t count = 0;
155 unsigned long delay = 0;
156 const struct device *timer_dev;
157 int err = parse_device(shctx, argc, argv, &timer_dev);
158 struct counter_top_cfg top_cfg;
159
160 k_sem_init(&timer_sem, 0, 1);
161
162 if (err != 0) {
163 return err;
164 }
165
166 delay = shell_strtoul(argv[ARGV_PERIODIC_TIME], 10, &err);
167 if (err != 0) {
168 shell_error(shctx, "invalid delay parameter");
169 return err;
170 } else if (delay > MAX_DELAY) {
171 shell_error(shctx, "delay out of range");
172 return -ERANGE;
173 }
174
175 top_cfg.flags = 0;
176 top_cfg.ticks = counter_us_to_ticks(timer_dev, (uint64_t)delay);
177 /* interrupt will be triggered periodically */
178 top_cfg.callback = timer_top_handler;
179 top_cfg.user_data = NULL;
180
181 /* set top value */
182 err = counter_set_top_value(timer_dev, &top_cfg);
183 if (err != 0) {
184 shell_error(shctx, "%s: failed to set top value, err: %d", argv[ARGV_DEV], err);
185 return err;
186 }
187
188 /* Checking periodic interrupt for PERIODIC_CYCLES times and then unblocking shell.
189 * Timer is still running and interrupt is triggered periodically.
190 */
191 while (++count < PERIODIC_CYCLES) {
192 k_sem_take(&timer_sem, K_FOREVER);
193 }
194
195 shell_info(shctx, "%s: periodic timer triggered for %d times", argv[ARGV_DEV], count);
196
197 return 0;
198 }
199
200 /* Device name autocompletion support */
device_name_get(size_t idx,struct shell_static_entry * entry)201 static void device_name_get(size_t idx, struct shell_static_entry *entry)
202 {
203 const struct device *dev = shell_device_lookup(idx, "timer");
204
205 entry->syntax = (dev != NULL) ? dev->name : NULL;
206 entry->handler = NULL;
207 entry->help = NULL;
208 entry->subcmd = NULL;
209 }
210
211 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
212
213 SHELL_STATIC_SUBCMD_SET_CREATE(sub_timer,
214 SHELL_CMD_ARG(periodic, &dsub_device_name,
215 "timer periodic <timer_instance_node_id> <time_in_us>",
216 cmd_timer_periodic, 3, 0),
217 SHELL_CMD_ARG(oneshot, &dsub_device_name,
218 "timer oneshot <timer_instance_node_id> <channel_id> <time_in_us>",
219 cmd_timer_oneshot, 4, 0),
220 SHELL_CMD_ARG(freerun, &dsub_device_name,
221 "timer freerun <timer_instance_node_id>",
222 cmd_timer_free_running, 2, 0),
223 SHELL_CMD_ARG(stop, &dsub_device_name,
224 "timer stop <timer_instance_node_id>",
225 cmd_timer_stop, 2, 0),
226 SHELL_SUBCMD_SET_END /* array terminated. */
227 );
228
229 SHELL_CMD_REGISTER(timer, &sub_timer, "Timer commands", NULL);
230