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