1 /*
2 * Arm SCP/MCP Software
3 * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
4 *
5 * SPDX-License-Identifier: BSD-3-Clause
6 */
7
8 #include "gtimer_reg.h"
9
10 #include <mod_clock.h>
11 #include <mod_gtimer.h>
12 #include <mod_timer.h>
13
14 #include <fwk_assert.h>
15 #include <fwk_event.h>
16 #include <fwk_id.h>
17 #include <fwk_macros.h>
18 #include <fwk_mm.h>
19 #include <fwk_module.h>
20 #include <fwk_notification.h>
21 #include <fwk_status.h>
22 #include <fwk_time.h>
23
24 #include <stddef.h>
25 #include <stdint.h>
26
27 #define GTIMER_FREQUENCY_MIN_HZ UINT32_C(1)
28 #define GTIMER_FREQUENCY_MAX_HZ UINT32_C(1000000000)
29 #define GTIMER_MIN_TIMESTAMP 2000
30
31 /* Device content */
32 struct gtimer_dev_ctx {
33 struct cntbase_reg *hw_timer;
34 struct cntctl_reg *hw_counter;
35 struct cntcontrol_reg *control;
36 const struct mod_gtimer_dev_config *config;
37 };
38
39 static struct mod_gtimer_mod_ctx {
40 bool initialized; /* Whether the device context has been initialized */
41
42 struct gtimer_dev_ctx *table; /* Device context table */
43 } mod_gtimer_ctx;
44
mod_gtimer_get_counter(const struct cntbase_reg * hw_timer)45 static uint64_t mod_gtimer_get_counter(const struct cntbase_reg *hw_timer)
46 {
47 uint32_t counter_low;
48 uint32_t counter_high;
49
50 /*
51 * To avoid race conditions where the high half of the counter increments
52 * after it has been sampled but before the low half is sampled, the values
53 * are resampled until the high half has stabilized. This assumes that the
54 * loop is faster than the high half incrementation.
55 */
56
57 do {
58 counter_high = hw_timer->PCTH;
59 counter_low = hw_timer->PCTL;
60 } while (counter_high != hw_timer->PCTH);
61
62 return ((uint64_t)counter_high << 32) | counter_low;
63 }
64
65 /*
66 * Functions fulfilling the Timer module's driver interface
67 */
68
enable(fwk_id_t dev_id)69 static int enable(fwk_id_t dev_id)
70 {
71 struct gtimer_dev_ctx *ctx;
72
73 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(dev_id);
74
75 ctx->hw_timer->P_CTL &= ~CNTBASE_P_CTL_IMASK;
76 ctx->hw_timer->P_CTL |= CNTBASE_P_CTL_ENABLE;
77
78 return FWK_SUCCESS;
79 }
80
disable(fwk_id_t dev_id)81 static int disable(fwk_id_t dev_id)
82 {
83 struct gtimer_dev_ctx *ctx;
84
85 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(dev_id);
86
87 ctx->hw_timer->P_CTL |= CNTBASE_P_CTL_IMASK;
88 ctx->hw_timer->P_CTL &= ~CNTBASE_P_CTL_ENABLE;
89
90 return FWK_SUCCESS;
91 }
92
get_counter(fwk_id_t dev_id,uint64_t * value)93 static int get_counter(fwk_id_t dev_id, uint64_t *value)
94 {
95 const struct gtimer_dev_ctx *ctx;
96
97 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(dev_id);
98
99 *value = mod_gtimer_get_counter(ctx->hw_timer);
100
101 return FWK_SUCCESS;
102 }
103
set_timer(fwk_id_t dev_id,uint64_t timestamp)104 static int set_timer(fwk_id_t dev_id, uint64_t timestamp)
105 {
106 struct gtimer_dev_ctx *ctx;
107 uint64_t counter;
108 int status;
109
110 status = get_counter(dev_id, &counter);
111 if (status != FWK_SUCCESS) {
112 return status;
113 }
114
115 /*
116 * If an alarm's period is very small, the timer device could be configured
117 * to interrupt on a timestamp that is "in the past". In this case, with the
118 * current FVP implementation an interrupt will not be generated. To avoid
119 * this issue, the minimum timestamp is GTIMER_MIN_TIMESTAMP ticks from now.
120 *
121 * It is assumed here that the 64-bit counter will never loop back during
122 * the course of the execution (@1GHz it would loop back after ~585 years).
123 */
124 timestamp = FWK_MAX(counter + GTIMER_MIN_TIMESTAMP, timestamp);
125
126 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(dev_id);
127
128 ctx->hw_timer->P_CVALL = (uint32_t)(timestamp & 0xFFFFFFFFUL);
129 ctx->hw_timer->P_CVALH = (uint32_t)(timestamp >> 32UL);
130
131 return FWK_SUCCESS;
132 }
133
get_timer(fwk_id_t dev_id,uint64_t * timestamp)134 static int get_timer(fwk_id_t dev_id, uint64_t *timestamp)
135 {
136 struct gtimer_dev_ctx *ctx;
137 uint32_t counter_low;
138 uint32_t counter_high;
139
140 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(dev_id);
141
142 /* Read 64-bit timer value */
143 counter_low = ctx->hw_timer->P_CVALL;
144 counter_high = ctx->hw_timer->P_CVALH;
145
146 *timestamp = ((uint64_t)counter_high << 32) | counter_low;
147
148 return FWK_SUCCESS;
149 }
150
get_frequency(fwk_id_t dev_id,uint32_t * frequency)151 static int get_frequency(fwk_id_t dev_id, uint32_t *frequency)
152 {
153 struct gtimer_dev_ctx *ctx;
154
155 if (frequency == NULL) {
156 return FWK_E_PARAM;
157 }
158
159 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(dev_id);
160
161 *frequency = ctx->config->frequency;
162
163 return FWK_SUCCESS;
164 }
165
166 static const struct mod_timer_driver_api module_api = {
167 .enable = enable,
168 .disable = disable,
169 .set_timer = set_timer,
170 .get_timer = get_timer,
171 .get_counter = get_counter,
172 .get_frequency = get_frequency,
173 };
174
175 /*
176 * Functions fulfilling the framework's module interface
177 */
178
gtimer_init(fwk_id_t module_id,unsigned int element_count,const void * data)179 static int gtimer_init(fwk_id_t module_id,
180 unsigned int element_count,
181 const void *data)
182 {
183 mod_gtimer_ctx.table =
184 fwk_mm_alloc(element_count, sizeof(struct gtimer_dev_ctx));
185
186 return FWK_SUCCESS;
187 }
188
gtimer_device_init(fwk_id_t element_id,unsigned int unused,const void * data)189 static int gtimer_device_init(fwk_id_t element_id, unsigned int unused,
190 const void *data)
191 {
192 int status;
193 struct gtimer_dev_ctx *ctx;
194
195 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(element_id);
196
197 ctx->config = data;
198 if (ctx->config->hw_timer == 0 ||
199 ctx->config->hw_counter == 0 ||
200 ctx->config->control == 0 ||
201 ctx->config->frequency < GTIMER_FREQUENCY_MIN_HZ ||
202 ctx->config->frequency > GTIMER_FREQUENCY_MAX_HZ) {
203
204 return FWK_E_DEVICE;
205 }
206
207 ctx->hw_timer = (struct cntbase_reg *)ctx->config->hw_timer;
208 ctx->hw_counter = (struct cntctl_reg *)ctx->config->hw_counter;
209 ctx->control = (struct cntcontrol_reg *)ctx->config->control;
210
211 status = disable(element_id);
212 fwk_assert(status == FWK_SUCCESS);
213
214 ctx->hw_counter->ACR = CNTCTL_ACR_RPCT |
215 CNTCTL_ACR_RVCT |
216 CNTCTL_ACR_RFRQ |
217 CNTCTL_ACR_RVOFF|
218 CNTCTL_ACR_RWPT;
219 ctx->hw_counter->FRQ = ctx->config->frequency;
220
221 return FWK_SUCCESS;
222 }
223
gtimer_process_bind_request(fwk_id_t requester_id,fwk_id_t id,fwk_id_t api_type,const void ** api)224 static int gtimer_process_bind_request(fwk_id_t requester_id,
225 fwk_id_t id,
226 fwk_id_t api_type,
227 const void **api)
228 {
229 /* No binding to the module */
230 if (fwk_module_is_valid_module_id(id)) {
231 return FWK_E_ACCESS;
232 }
233
234 *api = &module_api;
235
236 return FWK_SUCCESS;
237 }
238
gtimer_control_init(struct gtimer_dev_ctx * ctx)239 static void gtimer_control_init(struct gtimer_dev_ctx *ctx)
240 {
241 /* Set primary counter update frequency and enable counter. */
242 ctx->control->CR &= ~CNTCONTROL_CR_EN;
243 ctx->control->FID0 = ctx->config->frequency;
244 ctx->control->CR |= CNTCONTROL_CR_FCREQ | CNTCONTROL_CR_EN;
245 }
246
gtimer_start(fwk_id_t id)247 static int gtimer_start(fwk_id_t id)
248 {
249 struct gtimer_dev_ctx *ctx;
250
251 if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) {
252 return FWK_SUCCESS;
253 }
254
255 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(id);
256
257 if (ctx->config->skip_cntcontrol_init) {
258 return FWK_SUCCESS;
259 }
260
261 if (!fwk_id_is_type(ctx->config->clock_id, FWK_ID_TYPE_NONE)) {
262 /* Register for clock state notifications */
263 return fwk_notification_subscribe(
264 mod_clock_notification_id_state_changed,
265 ctx->config->clock_id,
266 id);
267 } else {
268 gtimer_control_init(ctx);
269 }
270
271 return FWK_SUCCESS;
272 }
273
gtimer_process_notification(const struct fwk_event * event,struct fwk_event * resp_event)274 static int gtimer_process_notification(
275 const struct fwk_event *event,
276 struct fwk_event *resp_event)
277 {
278 struct clock_notification_params *params;
279 struct gtimer_dev_ctx *ctx;
280
281 fwk_assert(
282 fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed));
283 fwk_assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT));
284
285 params = (struct clock_notification_params *)event->params;
286
287 if (params->new_state == MOD_CLOCK_STATE_RUNNING) {
288 ctx = mod_gtimer_ctx.table + fwk_id_get_element_idx(event->target_id);
289 gtimer_control_init(ctx);
290 }
291
292 return FWK_SUCCESS;
293 }
294
295 /*
296 * Module descriptor
297 */
298 const struct fwk_module module_gtimer = {
299 .api_count = 1,
300 .event_count = 0,
301 .type = FWK_MODULE_TYPE_DRIVER,
302 .init = gtimer_init,
303 .element_init = gtimer_device_init,
304 .start = gtimer_start,
305 .process_bind_request = gtimer_process_bind_request,
306 .process_notification = gtimer_process_notification,
307 };
308
mod_gtimer_timestamp(const void * ctx)309 static fwk_timestamp_t mod_gtimer_timestamp(const void *ctx)
310 {
311 fwk_timestamp_t timestamp;
312
313 const struct mod_gtimer_dev_config *cfg = ctx;
314 const struct cntbase_reg *hw_timer = (const void *)cfg->hw_timer;
315
316 uint32_t frequency = cfg->frequency;
317 uint64_t counter = mod_gtimer_get_counter(hw_timer);
318
319 timestamp = (FWK_S(1) / frequency) * counter;
320
321 return timestamp;
322 }
323
mod_gtimer_driver(const void ** ctx,const struct mod_gtimer_dev_config * cfg)324 struct fwk_time_driver mod_gtimer_driver(
325 const void **ctx,
326 const struct mod_gtimer_dev_config *cfg)
327 {
328 *ctx = cfg;
329
330 return (struct fwk_time_driver){
331 .timestamp = mod_gtimer_timestamp,
332 };
333 }
334