1 /*
2 * Copyright (c) 2021 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/drivers/entropy.h>
7 #include <zephyr/drivers/counter.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/busy_sim.h>
10 #include <zephyr/sys/ring_buffer.h>
11 #include <zephyr/random/random.h>
12
13 #define BUFFER_SIZE 32
14
15 struct busy_sim_data {
16 uint32_t idle_avg;
17 uint32_t active_avg;
18 uint16_t idle_delta;
19 uint16_t active_delta;
20 uint32_t us_tick;
21 struct counter_alarm_cfg alarm_cfg;
22 busy_sim_cb_t cb;
23 };
24
25 static struct k_work sim_work;
26 static struct ring_buf rnd_rbuf;
27 static uint8_t rnd_buf[BUFFER_SIZE];
28
29 struct busy_sim_config {
30 const struct device *entropy;
31 const struct device *counter;
32 struct gpio_dt_spec pin_spec;
33 };
34
35 #define DT_BUSY_SIM DT_COMPAT_GET_ANY_STATUS_OKAY(vnd_busy_sim)
36
37 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(vnd_busy_sim) == 1,
38 "add exactly one vnd,busy-sim node to the devicetree");
39
40 #if defined(CONFIG_XOSHIRO_RANDOM_GENERATOR) || defined(CONFIG_TIMER_RANDOM_GENERATOR)
41 #define USE_TEST_RANDOM 1
42 #endif
43
44 static const struct busy_sim_config sim_config = {
45 .entropy = COND_CODE_1(USE_TEST_RANDOM,
46 (NULL),
47 (DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy)))),
48 .counter = DEVICE_DT_GET(DT_PHANDLE(DT_BUSY_SIM, counter)),
49 .pin_spec = GPIO_DT_SPEC_GET_OR(DT_BUSY_SIM, active_gpios, {0}),
50 };
51
52 static struct busy_sim_data sim_data;
53 static const struct device *const busy_sim_dev = DEVICE_DT_GET_ONE(vnd_busy_sim);
54
rng_pool_work_handler(struct k_work * work)55 static void rng_pool_work_handler(struct k_work *work)
56 {
57 uint8_t *buf;
58 uint32_t len;
59 const struct busy_sim_config *config = busy_sim_dev->config;
60
61 len = ring_buf_put_claim(&rnd_rbuf, &buf, BUFFER_SIZE - 1);
62 if (len) {
63 int err = entropy_get_entropy(config->entropy, buf, len);
64
65 if (err == 0) {
66 ring_buf_put_finish(&rnd_rbuf, len);
67 return;
68 }
69 ring_buf_put_finish(&rnd_rbuf, 0);
70 }
71
72 k_work_submit(work);
73 }
74
75
get_timeout(bool idle,bool use_rand)76 static uint32_t get_timeout(bool idle, bool use_rand)
77 {
78 struct busy_sim_data *data = busy_sim_dev->data;
79 uint32_t avg = idle ? data->idle_avg : data->active_avg;
80 uint32_t delta = idle ? data->idle_delta : data->active_delta;
81 uint16_t rand_val;
82 uint32_t len;
83
84 if (use_rand) {
85 sys_rand_get(&rand_val, sizeof(rand_val));
86 } else {
87 len = ring_buf_get(&rnd_rbuf,
88 (uint8_t *)&rand_val,
89 sizeof(rand_val));
90 if (len < sizeof(rand_val)) {
91 k_work_submit(&sim_work);
92 rand_val = 0;
93 }
94 }
95
96 avg *= data->us_tick;
97 delta *= data->us_tick;
98
99 return avg - delta + 2 * (rand_val % delta);
100 }
101
counter_alarm_callback(const struct device * dev,uint8_t chan_id,uint32_t ticks,void * user_data)102 static void counter_alarm_callback(const struct device *dev,
103 uint8_t chan_id, uint32_t ticks,
104 void *user_data)
105 {
106 int err;
107 const struct busy_sim_config *config = busy_sim_dev->config;
108 struct busy_sim_data *data = busy_sim_dev->data;
109
110 data->alarm_cfg.ticks = get_timeout(true, !config->entropy);
111
112 if (config->pin_spec.port) {
113 err = gpio_pin_set_dt(&config->pin_spec, 1);
114 __ASSERT_NO_MSG(err >= 0);
115 }
116
117 /* Busy loop */
118 if (data->cb) {
119 data->cb();
120 }
121
122 k_busy_wait(get_timeout(false, !config->entropy) / data->us_tick);
123
124 if (config->pin_spec.port) {
125 err = gpio_pin_set_dt(&config->pin_spec, 0);
126 __ASSERT_NO_MSG(err >= 0);
127 }
128
129 err = counter_set_channel_alarm(config->counter, 0, &data->alarm_cfg);
130 __ASSERT_NO_MSG(err == 0);
131
132 }
133
busy_sim_start(uint32_t active_avg,uint32_t active_delta,uint32_t idle_avg,uint32_t idle_delta,busy_sim_cb_t cb)134 void busy_sim_start(uint32_t active_avg, uint32_t active_delta,
135 uint32_t idle_avg, uint32_t idle_delta, busy_sim_cb_t cb)
136 {
137 int err;
138 const struct busy_sim_config *config = busy_sim_dev->config;
139 struct busy_sim_data *data = busy_sim_dev->data;
140
141 data->cb = cb;
142 data->active_avg = active_avg;
143 data->active_delta = active_delta;
144 data->idle_avg = idle_avg;
145 data->idle_delta = idle_delta;
146
147 if (config->entropy) {
148 err = k_work_submit(&sim_work);
149 __ASSERT_NO_MSG(err >= 0);
150 }
151
152 data->alarm_cfg.ticks = counter_us_to_ticks(config->counter, 100);
153 err = counter_set_channel_alarm(config->counter, 0, &data->alarm_cfg);
154 __ASSERT_NO_MSG(err == 0);
155
156 err = counter_start(config->counter);
157 __ASSERT_NO_MSG(err == 0);
158 }
159
busy_sim_stop(void)160 void busy_sim_stop(void)
161 {
162 int err;
163 const struct busy_sim_config *config = busy_sim_dev->config;
164
165 if (config->entropy) {
166 k_work_cancel(&sim_work);
167 }
168
169 err = counter_cancel_channel_alarm(config->counter, 0);
170 __ASSERT_NO_MSG(err == 0);
171
172 err = counter_stop(config->counter);
173 __ASSERT_NO_MSG(err == 0);
174 }
175
busy_sim_init(const struct device * dev)176 static int busy_sim_init(const struct device *dev)
177 {
178 uint32_t freq;
179 const struct busy_sim_config *config = dev->config;
180 struct busy_sim_data *data = dev->data;
181
182 if ((config->pin_spec.port && !gpio_is_ready_dt(&config->pin_spec)) ||
183 !device_is_ready(config->counter) ||
184 (config->entropy && !device_is_ready(config->entropy))) {
185 __ASSERT(0, "Devices needed by busy simulator not ready.");
186 return -EIO;
187 }
188
189 if (config->pin_spec.port) {
190 gpio_pin_configure_dt(&config->pin_spec, GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW);
191 }
192
193 freq = counter_get_frequency(config->counter);
194 if (freq < 1000000) {
195 __ASSERT(0, "Counter device has too low frequency for busy simulator.");
196 return -EINVAL;
197 }
198
199 if (config->entropy) {
200 k_work_init(&sim_work, rng_pool_work_handler);
201 ring_buf_init(&rnd_rbuf, BUFFER_SIZE, rnd_buf);
202 }
203
204 data->us_tick = freq / 1000000;
205 data->alarm_cfg.callback = counter_alarm_callback;
206 data->alarm_cfg.flags = COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;
207
208 return 0;
209 }
210
211 DEVICE_DT_DEFINE(DT_BUSY_SIM, busy_sim_init, NULL,
212 &sim_data, &sim_config,
213 POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY,
214 NULL);
215