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