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 static const struct busy_sim_config sim_config = {
41 .entropy = COND_CODE_1(CONFIG_XOSHIRO_RANDOM_GENERATOR,
42 (NULL),
43 (DEVICE_DT_GET(DT_CHOSEN(zephyr_entropy)))),
44 .counter = DEVICE_DT_GET(DT_PHANDLE(DT_BUSY_SIM, counter)),
45 .pin_spec = GPIO_DT_SPEC_GET_OR(DT_BUSY_SIM, active_gpios, {0}),
46 };
47
48 static struct busy_sim_data sim_data;
49 static const struct device *const busy_sim_dev = DEVICE_DT_GET_ONE(vnd_busy_sim);
50
rng_pool_work_handler(struct k_work * work)51 static void rng_pool_work_handler(struct k_work *work)
52 {
53 uint8_t *buf;
54 uint32_t len;
55 const struct busy_sim_config *config = busy_sim_dev->config;
56
57 len = ring_buf_put_claim(&rnd_rbuf, &buf, BUFFER_SIZE - 1);
58 if (len) {
59 int err = entropy_get_entropy(config->entropy, buf, len);
60
61 if (err == 0) {
62 ring_buf_put_finish(&rnd_rbuf, len);
63 return;
64 }
65 ring_buf_put_finish(&rnd_rbuf, 0);
66 }
67
68 k_work_submit(work);
69 }
70
71
get_timeout(bool idle)72 static uint32_t get_timeout(bool idle)
73 {
74 struct busy_sim_data *data = busy_sim_dev->data;
75 uint32_t avg = idle ? data->idle_avg : data->active_avg;
76 uint32_t delta = idle ? data->idle_delta : data->active_delta;
77 uint16_t rand_val;
78 uint32_t len;
79
80 if (IS_ENABLED(CONFIG_XOSHIRO_RANDOM_GENERATOR)) {
81 sys_rand_get(&rand_val, sizeof(rand_val));
82 } else {
83 len = ring_buf_get(&rnd_rbuf,
84 (uint8_t *)&rand_val,
85 sizeof(rand_val));
86 if (len < sizeof(rand_val)) {
87 k_work_submit(&sim_work);
88 rand_val = 0;
89 }
90 }
91
92 avg *= data->us_tick;
93 delta *= data->us_tick;
94
95 return avg - delta + 2 * (rand_val % delta);
96 }
97
counter_alarm_callback(const struct device * dev,uint8_t chan_id,uint32_t ticks,void * user_data)98 static void counter_alarm_callback(const struct device *dev,
99 uint8_t chan_id, uint32_t ticks,
100 void *user_data)
101 {
102 int err;
103 const struct busy_sim_config *config = busy_sim_dev->config;
104 struct busy_sim_data *data = busy_sim_dev->data;
105
106 data->alarm_cfg.ticks = get_timeout(true);
107
108 if (config->pin_spec.port) {
109 err = gpio_pin_set_dt(&config->pin_spec, 1);
110 __ASSERT_NO_MSG(err >= 0);
111 }
112
113 /* Busy loop */
114 if (data->cb) {
115 data->cb();
116 }
117
118 k_busy_wait(get_timeout(false) / data->us_tick);
119
120 if (config->pin_spec.port) {
121 err = gpio_pin_set_dt(&config->pin_spec, 0);
122 __ASSERT_NO_MSG(err >= 0);
123 }
124
125 err = counter_set_channel_alarm(config->counter, 0, &data->alarm_cfg);
126 __ASSERT_NO_MSG(err == 0);
127
128 }
129
busy_sim_start(uint32_t active_avg,uint32_t active_delta,uint32_t idle_avg,uint32_t idle_delta,busy_sim_cb_t cb)130 void busy_sim_start(uint32_t active_avg, uint32_t active_delta,
131 uint32_t idle_avg, uint32_t idle_delta, busy_sim_cb_t cb)
132 {
133 int err;
134 const struct busy_sim_config *config = busy_sim_dev->config;
135 struct busy_sim_data *data = busy_sim_dev->data;
136
137 data->cb = cb;
138 data->active_avg = active_avg;
139 data->active_delta = active_delta;
140 data->idle_avg = idle_avg;
141 data->idle_delta = idle_delta;
142
143 if (!IS_ENABLED(CONFIG_XOSHIRO_RANDOM_GENERATOR)) {
144 err = k_work_submit(&sim_work);
145 __ASSERT_NO_MSG(err >= 0);
146 }
147
148 data->alarm_cfg.ticks = counter_us_to_ticks(config->counter, 100);
149 err = counter_set_channel_alarm(config->counter, 0, &data->alarm_cfg);
150 __ASSERT_NO_MSG(err == 0);
151
152 err = counter_start(config->counter);
153 __ASSERT_NO_MSG(err == 0);
154 }
155
busy_sim_stop(void)156 void busy_sim_stop(void)
157 {
158 int err;
159 const struct busy_sim_config *config = busy_sim_dev->config;
160
161 if (!IS_ENABLED(CONFIG_XOSHIRO_RANDOM_GENERATOR)) {
162 k_work_cancel(&sim_work);
163 }
164
165 err = counter_stop(config->counter);
166 __ASSERT_NO_MSG(err == 0);
167 }
168
busy_sim_init(const struct device * dev)169 static int busy_sim_init(const struct device *dev)
170 {
171 uint32_t freq;
172 const struct busy_sim_config *config = dev->config;
173 struct busy_sim_data *data = dev->data;
174
175 if ((config->pin_spec.port && !gpio_is_ready_dt(&config->pin_spec)) ||
176 !device_is_ready(config->counter) ||
177 (!IS_ENABLED(CONFIG_XOSHIRO_RANDOM_GENERATOR) &&
178 !device_is_ready(config->entropy))) {
179 __ASSERT(0, "Devices needed by busy simulator not ready.");
180 return -EIO;
181 }
182
183 if (config->pin_spec.port) {
184 gpio_pin_configure_dt(&config->pin_spec, GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW);
185 }
186
187 freq = counter_get_frequency(config->counter);
188 if (freq < 1000000) {
189 __ASSERT(0, "Counter device has too low frequency for busy simulator.");
190 return -EINVAL;
191 }
192
193 if (!IS_ENABLED(CONFIG_XOSHIRO_RANDOM_GENERATOR)) {
194 k_work_init(&sim_work, rng_pool_work_handler);
195 ring_buf_init(&rnd_rbuf, BUFFER_SIZE, rnd_buf);
196 }
197
198 data->us_tick = freq / 1000000;
199 data->alarm_cfg.callback = counter_alarm_callback;
200 data->alarm_cfg.flags = COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;
201
202 return 0;
203 }
204
205 DEVICE_DT_DEFINE(DT_BUSY_SIM, busy_sim_init, NULL,
206 &sim_data, &sim_config,
207 POST_KERNEL, CONFIG_APPLICATION_INIT_PRIORITY,
208 NULL);
209