1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2021, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     DWT driver implementation which implements PMI interface
9  *     defined in mod_pmi.h
10  */
11 
12 #include <mod_dwt_pmi.h>
13 
14 #include <fwk_assert.h>
15 #include <fwk_log.h>
16 #include <fwk_mm.h>
17 #include <fwk_module.h>
18 #include <fwk_module_idx.h>
19 #include <fwk_status.h>
20 
21 #include <string.h>
22 
23 struct cntbase_reg {
24     FWK_R uint32_t PCTL;
25     FWK_R uint32_t PCTH;
26 };
27 
28 /*
29  * Enable DWT and ITM through DEMCR register
30  * See Debug system register in ARMv7-M Architecture Reference Manual
31  * C1.6.5 Debug Exception and Monitor Control Register, DEMCR
32  */
33 #define DEBUG_SYSTEM_DEMCR_TRCENA (UINT32_C(1) << 24U)
34 
35 /*
36  * Enables CYCCNT in Control register, DWT_CTRL
37  * See ARMv7-M Architecture Reference Manual
38  */
39 #define DWT_CTRL_CYCCNTENA (UINT32_C(1) << 0U)
40 
41 /*
42  * Flag to check if cycle count register is enabled
43  * in Control register, DWT_CTRL
44  * See  ARMv7-M Architecture Reference Manual.
45  */
46 #define DWT_CTRL_NOCYCCNT (UINT32_C(1) << 25U)
47 
cycle_diff(uint64_t cstart,uint64_t cend)48 static uint32_t cycle_diff(uint64_t cstart, uint64_t cend)
49 {
50     return (0xFFFFFFFF - (uint32_t)(cstart) + (uint32_t)(cend));
51 }
52 
53 struct mod_dwt_pmi_ctx {
54     /* Platform specific DWT configuration data */
55     const struct mod_dwt_pmi_config *config;
56     bool dwt_enabled;
57     bool dwt_cyccnt_enable;
58 };
59 
60 static struct mod_dwt_pmi_ctx ctx;
61 
check_cyccnt_supported(void)62 static bool check_cyccnt_supported(void)
63 {
64     uint32_t val = *(ctx.config->dwt_ctrl_addr);
65 
66     /* Bit NOCYCCNT is set if no cycle count is supported */
67     if ((DWT_CTRL_NOCYCCNT & val) == 0) {
68         return true;
69     }
70 
71     return false;
72 }
73 
enable_dwt(void)74 static void enable_dwt(void)
75 {
76     if (!ctx.dwt_enabled) {
77         *(ctx.config->debug_sys_demcr_addr) |= DEBUG_SYSTEM_DEMCR_TRCENA;
78         ctx.dwt_enabled = true;
79     }
80 }
81 
enable_cycle_count(void)82 static int enable_cycle_count(void)
83 {
84     bool supported;
85 
86     supported = check_cyccnt_supported();
87     /* If DWT_CYCNT feature is not available then do not enable cycle count */
88     if (!supported) {
89         return FWK_E_DEVICE;
90     }
91 
92     if (!ctx.dwt_cyccnt_enable) {
93         *(ctx.config->dwt_ctrl_addr) |= DWT_CTRL_CYCCNTENA;
94         ctx.dwt_cyccnt_enable = true;
95     }
96 
97     return FWK_SUCCESS;
98 }
99 
disable_cycle_count(void)100 static int disable_cycle_count(void)
101 {
102     if (ctx.dwt_cyccnt_enable) {
103         *(ctx.config->dwt_ctrl_addr) &= ~DWT_CTRL_CYCCNTENA;
104         ctx.dwt_cyccnt_enable = false;
105     }
106 
107     return FWK_SUCCESS;
108 }
109 
get_dwt_cycle_count(uint64_t * cycle_count)110 static int get_dwt_cycle_count(uint64_t *cycle_count)
111 {
112     if (!ctx.dwt_cyccnt_enable) {
113         return FWK_E_DEVICE;
114     }
115 
116     *cycle_count = (uint64_t)(*(ctx.config->dwt_cyccnt));
117 
118     return FWK_SUCCESS;
119 }
120 
set_dwt_cycle_count(uint64_t cycle_count)121 static int set_dwt_cycle_count(uint64_t cycle_count)
122 {
123     if (!ctx.dwt_cyccnt_enable) {
124         return FWK_E_DEVICE;
125     }
126 
127     /* DWT cycle count register support only 32 bit count */
128     *(ctx.config->dwt_cyccnt) = (uint32_t)cycle_count;
129 
130     return FWK_SUCCESS;
131 }
132 
cycle_count_diff(uint64_t start,uint64_t end)133 static uint64_t cycle_count_diff(uint64_t start, uint64_t end)
134 {
135     /* Handle wrap codition */
136     if (end < start) {
137         return (uint64_t)cycle_diff(start, end);
138     }
139 
140     return end - start;
141 }
142 
143 /*
144  * This function does not use DWT for getting the time,
145  * instead it uses generic timer available on ARM platforms.
146  * However for completeness, the PMI driver provides this
147  * function. For more time related use cases, See fwk_time.h
148  * and mod_gtimer.h. In the absence of generic timer, platform
149  * owners should try to implement this function in their respective
150  * PMI driver.
151  */
get_current_time(void)152 static uint64_t get_current_time(void)
153 {
154     uint32_t counter_low;
155     uint32_t counter_high;
156 
157     /*
158      * To avoid race conditions where the high half of the counter increments
159      * after it has been sampled but before the low half is sampled, the values
160      * are resampled until the high half has stabilized. This assumes that the
161      * loop is faster than the high half incrementation.
162      */
163 
164     do {
165         counter_high = ctx.config->hw_timer->PCTH;
166         counter_low = ctx.config->hw_timer->PCTL;
167     } while (counter_high != ctx.config->hw_timer->PCTH);
168 
169     return ((uint64_t)counter_high << 32U) | counter_low;
170 }
171 
172 static const struct mod_pmi_driver_api dwt_pmi_driver_api = {
173     .start_cycle_count = enable_cycle_count,
174     .stop_cycle_count = disable_cycle_count,
175     .get_cycle_count = get_dwt_cycle_count,
176     .set_cycle_count = set_dwt_cycle_count,
177     .cycle_count_diff = cycle_count_diff,
178     .get_current_time = get_current_time
179 };
180 
dwt_pmi_init(fwk_id_t module_id,unsigned int element_count,const void * data)181 static int dwt_pmi_init(
182     fwk_id_t module_id,
183     unsigned int element_count,
184     const void *data)
185 {
186     ctx.config = (struct mod_dwt_pmi_config *)data;
187     int status;
188 
189     enable_dwt();
190 
191     /* Start cycle count with 0 */
192     set_dwt_cycle_count(0);
193 
194     status = enable_cycle_count();
195     if (status != FWK_SUCCESS) {
196         return status;
197     }
198 
199     return FWK_SUCCESS;
200 }
201 
process_bind_request(fwk_id_t source_id,fwk_id_t target_id,fwk_id_t api_id,const void ** api)202 static int process_bind_request(
203     fwk_id_t source_id,
204     fwk_id_t target_id,
205     fwk_id_t api_id,
206     const void **api)
207 {
208     /* Only allow binding to the module */
209     if (!fwk_id_is_equal(target_id, fwk_module_id_dwt_pmi)) {
210         return FWK_E_PARAM;
211     }
212 
213     *api = &dwt_pmi_driver_api;
214 
215     return FWK_SUCCESS;
216 }
217 
218 const struct fwk_module module_dwt_pmi = {
219     .type = FWK_MODULE_TYPE_DRIVER,
220     .init = dwt_pmi_init,
221     .process_bind_request = process_bind_request,
222     .api_count = MOD_DWT_PMI_API_IDX_COUNT,
223 };
224