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