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 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 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 
cmd_timer_free_running(const struct shell * shctx,size_t argc,char ** argv)40 static int cmd_timer_free_running(const struct shell *shctx, size_t argc, char **argv)
41 {
42 	ARG_UNUSED(argc);
43 	int err = 0;
44 	const struct device *timer_dev;
45 
46 	timer_dev = device_get_binding(argv[ARGV_DEV]);
47 	if (!timer_dev) {
48 		shell_error(shctx, "Timer: Device %s not found", argv[ARGV_DEV]);
49 		return -ENODEV;
50 	}
51 
52 	/* start timer in free running mode */
53 	err = counter_start(timer_dev);
54 	if (err != 0) {
55 		shell_error(shctx, "%s is not available err:%d", argv[ARGV_DEV], err);
56 		return err;
57 	}
58 
59 	shell_info(shctx, "%s: Timer is freerunning", argv[ARGV_DEV]);
60 
61 	return  0;
62 }
63 
cmd_timer_stop(const struct shell * shctx,size_t argc,char ** argv)64 static int cmd_timer_stop(const struct shell *shctx, size_t argc, char **argv)
65 {
66 	ARG_UNUSED(argc);
67 	uint32_t ticks1 = 0, ticks2 = 0;
68 	const struct device *timer_dev;
69 
70 	timer_dev = device_get_binding(argv[ARGV_DEV]);
71 	if (!timer_dev) {
72 		shell_error(shctx, "Timer: Device %s not found", argv[ARGV_DEV]);
73 		return -ENODEV;
74 	}
75 
76 	counter_stop(timer_dev);
77 
78 	counter_get_value(timer_dev, &ticks1);
79 	counter_get_value(timer_dev, &ticks2);
80 
81 	if (ticks1 == ticks2) {
82 		shell_info(shctx, "Timer Stopped");
83 	} else {
84 		shell_error(shctx, "Failed to stop timer");
85 		return -EIO;
86 	}
87 
88 	return 0;
89 }
90 
cmd_timer_oneshot(const struct shell * shctx,size_t argc,char ** argv)91 static int cmd_timer_oneshot(const struct shell *shctx, size_t argc, char **argv)
92 {
93 	ARG_UNUSED(argc);
94 	int err = 0;
95 	unsigned long delay = 0;
96 	unsigned long channel = 0;
97 	const struct device *timer_dev;
98 	struct counter_alarm_cfg alarm_cfg;
99 
100 	k_sem_init(&timer_sem, 0, 1);
101 
102 	timer_dev = device_get_binding(argv[ARGV_DEV]);
103 	if (!timer_dev) {
104 		shell_error(shctx, "Timer: Device %s not found", argv[ARGV_DEV]);
105 		return -ENODEV;
106 	}
107 
108 	delay = shell_strtoul(argv[ARGV_ONESHOT_TIME], 10, &err);
109 	if (err != 0) {
110 		shell_error(shctx, "invalid delay parameter");
111 		return err;
112 	} else if (delay > MAX_DELAY) {
113 		shell_error(shctx, "delay out of range");
114 		return -ERANGE;
115 	}
116 
117 	channel = shell_strtoul(argv[ARGV_CHN], 10, &err);
118 	if (err != 0) {
119 		shell_error(shctx, "invalid channel parameter");
120 		return err;
121 	} else if (channel > MAX_CHANNEL) {
122 		shell_error(shctx, "channel out of range");
123 		return -ERANGE;
124 	}
125 
126 	alarm_cfg.flags = 0;
127 	alarm_cfg.ticks = counter_us_to_ticks(timer_dev, (uint64_t)delay);
128 	alarm_cfg.callback = timer_alarm_handler;
129 	alarm_cfg.user_data = NULL;
130 
131 	/* set an alarm */
132 	err = counter_set_channel_alarm(timer_dev, (uint8_t)channel, &alarm_cfg);
133 	if (err != 0) {
134 		shell_error(shctx, "%s:Failed to set channel alarm, err:%d", argv[ARGV_DEV], err);
135 		return err;
136 	}
137 
138 	k_sem_take(&timer_sem, K_FOREVER);
139 
140 	shell_info(shctx, "%s: Alarm triggered", argv[ARGV_DEV]);
141 
142 	return 0;
143 }
144 
cmd_timer_periodic(const struct shell * shctx,size_t argc,char ** argv)145 static int cmd_timer_periodic(const struct shell *shctx, size_t argc, char **argv)
146 {
147 	ARG_UNUSED(argc);
148 	uint32_t count = 0;
149 	int err = 0;
150 	unsigned long delay = 0;
151 	const struct device *timer_dev;
152 	struct counter_top_cfg top_cfg;
153 
154 	k_sem_init(&timer_sem, 0, 1);
155 
156 	timer_dev = device_get_binding(argv[ARGV_DEV]);
157 	if (!timer_dev) {
158 		shell_error(shctx, "Timer: Device %s not found", argv[ARGV_DEV]);
159 		return -ENODEV;
160 	}
161 
162 	delay = shell_strtoul(argv[ARGV_PERIODIC_TIME], 10, &err);
163 	if (err != 0) {
164 		shell_error(shctx, "invalid delay parameter");
165 		return err;
166 	} else if (delay > MAX_DELAY) {
167 		shell_error(shctx, "delay out of range");
168 		return -ERANGE;
169 	}
170 
171 	top_cfg.flags = 0;
172 	top_cfg.ticks = counter_us_to_ticks(timer_dev, (uint64_t)delay);
173 	/* interrupt will be triggered periodically */
174 	top_cfg.callback = timer_top_handler;
175 	top_cfg.user_data = NULL;
176 
177 	/* set top value */
178 	err = counter_set_top_value(timer_dev, &top_cfg);
179 	if (err != 0) {
180 		shell_error(shctx, "%s: failed to set top value, err: %d", argv[ARGV_DEV], err);
181 		return err;
182 	}
183 
184 	/* Checking periodic interrupt for PERIODIC_CYCLES times and then unblocking shell.
185 	 * Timer is still running and interrupt is triggered periodically.
186 	 */
187 	while (++count < PERIODIC_CYCLES) {
188 		k_sem_take(&timer_sem, K_FOREVER);
189 	}
190 
191 	shell_info(shctx, "%s: periodic timer triggered for %d times", argv[ARGV_DEV], count);
192 
193 	return 0;
194 }
195 
196 SHELL_STATIC_SUBCMD_SET_CREATE(sub_timer,
197 			SHELL_CMD_ARG(periodic, NULL,
198 			"timer periodic <timer_instance_node_id> <time_in_us>",
199 			cmd_timer_periodic, 3, 0),
200 			SHELL_CMD_ARG(oneshot, NULL,
201 			"timer oneshot <timer_instance_node_id> <channel_id> <time_in_us>",
202 			cmd_timer_oneshot, 4, 0),
203 			SHELL_CMD_ARG(freerun, NULL,
204 			"timer freerun <timer_instance_node_id>",
205 			cmd_timer_free_running, 2, 0),
206 			SHELL_CMD_ARG(stop, NULL,
207 			"timer stop <timer_instance_node_id>",
208 			cmd_timer_stop, 2, 0),
209 			SHELL_SUBCMD_SET_END /* array terminated. */
210 			);
211 
212 SHELL_CMD_REGISTER(timer, &sub_timer, "Timer commands", NULL);
213