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