1 /*
2 * Copyright (c) 2021 HPMicro
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "hpm_sysctl_drv.h"
9 #include "hpm_soc_feature.h"
10
11 #define SYSCTL_RESOURCE_GROUP0 0
12 #define SYSCTL_RESOURCE_GROUP1 1
13
14 #define SYSCTL_CPU_RELEASE_KEY(cpu) (0xC0BEF1A9UL | ((cpu & 1) << 24))
15
sysctl_valid_cpu_index(uint8_t cpu)16 static inline bool sysctl_valid_cpu_index(uint8_t cpu)
17 {
18 if ((cpu != SYSCTL_CPU_CPU0) && (cpu != SYSCTL_CPU_CPU1)) {
19 return false;
20 }
21 return true;
22 }
23
sysctl_get_cpu_gpr(SYSCTL_Type * ptr,uint8_t cpu,uint32_t * data,uint32_t size)24 hpm_stat_t sysctl_get_cpu_gpr(SYSCTL_Type *ptr, uint8_t cpu,
25 uint32_t *data, uint32_t size)
26 {
27 uint32_t i;
28 if ((!sysctl_valid_cpu_index(cpu))
29 || (size > ARRAY_SIZE(ptr->CPU[cpu].GPR))) {
30 return status_invalid_argument;
31 }
32 for (i = 0; i < size; i++) {
33 *(data + i) = ptr->CPU[cpu].GPR[i];
34 }
35 return status_success;
36 }
37
_sysctl_cpu_get_gpr(SYSCTL_Type * ptr,uint8_t cpu,uint8_t start,uint8_t count,uint32_t * data)38 static hpm_stat_t _sysctl_cpu_get_gpr(SYSCTL_Type *ptr,
39 uint8_t cpu,
40 uint8_t start,
41 uint8_t count,
42 uint32_t *data)
43 {
44 uint8_t i, size = ARRAY_SIZE(ptr->CPU[cpu].GPR);
45 if (!sysctl_valid_cpu_index(cpu)
46 || (data == NULL)
47 || !count || start > size || count > size
48 || (start + count) > size) {
49 return status_invalid_argument;
50 }
51 for (i = 0; i < count; i++) {
52 *(data + i) = ptr->CPU[cpu].GPR[start + i];
53 }
54 return status_success;
55 }
56
sysctl_cpu0_get_gpr(SYSCTL_Type * ptr,uint8_t start,uint8_t count,uint32_t * data)57 hpm_stat_t sysctl_cpu0_get_gpr(SYSCTL_Type *ptr,
58 uint8_t start,
59 uint8_t count,
60 uint32_t *data)
61 {
62 return _sysctl_cpu_get_gpr(ptr, 0, start, count, data);
63 }
64
sysctl_cpu1_get_gpr(SYSCTL_Type * ptr,uint8_t start,uint8_t count,uint32_t * data)65 hpm_stat_t sysctl_cpu1_get_gpr(SYSCTL_Type *ptr,
66 uint8_t start,
67 uint8_t count,
68 uint32_t *data)
69 {
70 return _sysctl_cpu_get_gpr(ptr, 1, start, count, data);
71 }
72
_sysctl_cpu_set_gpr(SYSCTL_Type * ptr,uint8_t cpu,uint8_t start,uint8_t count,uint32_t * data)73 static hpm_stat_t _sysctl_cpu_set_gpr(SYSCTL_Type *ptr,
74 uint8_t cpu,
75 uint8_t start,
76 uint8_t count,
77 uint32_t *data)
78 {
79 uint8_t i, size = ARRAY_SIZE(ptr->CPU[cpu].GPR);
80 if (!sysctl_valid_cpu_index(cpu)
81 || (data == NULL)
82 || !count || start > size || count > size
83 || (start + count) > size) {
84 return status_invalid_argument;
85 }
86 for (i = 0; i < count; i++) {
87 ptr->CPU[cpu].GPR[start + i] = *(data + i);
88 }
89 return status_success;
90 }
91
sysctl_cpu0_set_gpr(SYSCTL_Type * ptr,uint8_t start,uint8_t count,uint32_t * data,bool lock)92 hpm_stat_t sysctl_cpu0_set_gpr(SYSCTL_Type *ptr,
93 uint8_t start,
94 uint8_t count,
95 uint32_t *data,
96 bool lock)
97 {
98 hpm_stat_t stat = status_success;
99 uint16_t gpr_mask;
100 stat = _sysctl_cpu_set_gpr(ptr, 0, start, count, data);
101 if (stat != status_success) {
102 return stat;
103 }
104 if (lock) {
105 gpr_mask = ((1 << count) - 1) << start;
106 sysctl_cpu0_lock_gpr_with_mask(ptr, gpr_mask);
107 }
108 return stat;
109 }
110
sysctl_cpu1_set_gpr(SYSCTL_Type * ptr,uint8_t start,uint8_t count,uint32_t * data,bool lock)111 hpm_stat_t sysctl_cpu1_set_gpr(SYSCTL_Type *ptr,
112 uint8_t start,
113 uint8_t count,
114 uint32_t *data,
115 bool lock)
116 {
117 hpm_stat_t stat = status_success;
118 uint16_t gpr_mask;
119 stat = _sysctl_cpu_set_gpr(ptr, 1, start, count, data);
120 if (stat != status_success) {
121 return stat;
122 }
123 if (lock) {
124 gpr_mask = ((1 << count) - 1) << start;
125 sysctl_cpu1_lock_gpr_with_mask(ptr, gpr_mask);
126 }
127 return stat;
128 }
129
sysctl_monitor_get_default_config(SYSCTL_Type * ptr,monitor_config_t * config)130 void sysctl_monitor_get_default_config(SYSCTL_Type *ptr, monitor_config_t *config)
131 {
132 (void) ptr;
133 config->mode = monitor_work_mode_record;
134 config->accuracy = monitor_accuracy_1khz;
135 config->reference = monitor_reference_24mhz;
136 config->divide_by = 1;
137 config->high_limit = 0;
138 config->low_limit = 0;
139 config->start_measure = true;
140 config->enable_output = false;
141 config->target = monitor_target_clk_top_cpu0;
142 }
143
sysctl_monitor_init(SYSCTL_Type * ptr,uint8_t slice,monitor_config_t * config)144 void sysctl_monitor_init(SYSCTL_Type *ptr,
145 uint8_t slice, monitor_config_t *config)
146 {
147 ptr->MONITOR[slice].CONTROL &= ~(SYSCTL_MONITOR_CONTROL_START_MASK | SYSCTL_MONITOR_CONTROL_OUTEN_MASK);
148
149 if (config->mode == monitor_work_mode_compare) {
150 ptr->MONITOR[slice].HIGH_LIMIT = SYSCTL_MONITOR_HIGH_LIMIT_FREQUENCY_SET(config->high_limit);
151 ptr->MONITOR[slice].LOW_LIMIT = SYSCTL_MONITOR_LOW_LIMIT_FREQUENCY_SET(config->low_limit);
152 }
153
154 ptr->MONITOR[slice].CONTROL = (ptr->MONITOR[slice].CONTROL
155 & ~(SYSCTL_MONITOR_CONTROL_DIV_MASK | SYSCTL_MONITOR_CONTROL_MODE_MASK
156 | SYSCTL_MONITOR_CONTROL_ACCURACY_MASK | SYSCTL_MONITOR_CONTROL_REFERENCE_MASK
157 | SYSCTL_MONITOR_CONTROL_SELECTION_MASK))
158 | (SYSCTL_MONITOR_CONTROL_DIV_SET(config->divide_by - 1)
159 | SYSCTL_MONITOR_CONTROL_MODE_SET(config->mode)
160 | SYSCTL_MONITOR_CONTROL_ACCURACY_SET(config->accuracy)
161 | SYSCTL_MONITOR_CONTROL_REFERENCE_SET(config->reference)
162 | SYSCTL_MONITOR_CONTROL_START_SET(config->start_measure)
163 | SYSCTL_MONITOR_CONTROL_OUTEN_SET(config->enable_output)
164 | SYSCTL_MONITOR_CONTROL_SELECTION_SET(config->target));
165 }
166
sysctl_monitor_measure_frequency(SYSCTL_Type * ptr,uint8_t monitor_index,monitor_target_t target,bool enable_output)167 uint32_t sysctl_monitor_measure_frequency(SYSCTL_Type *ptr,
168 uint8_t monitor_index,
169 monitor_target_t target,
170 bool enable_output)
171 {
172 uint32_t frequency = 0;
173 monitor_config_t monitor = {0};
174 sysctl_monitor_get_default_config(ptr, &monitor);
175 monitor.target = target;
176 monitor.enable_output = enable_output;
177 sysctl_monitor_init(ptr, monitor_index, &monitor);
178 if (monitor_index < SYSCTL_SOC_MONITOR_SLICE_COUNT) {
179 frequency = sysctl_monitor_get_current_result(ptr, monitor_index);
180 }
181 return frequency;
182 }
183
sysctl_set_cpu_entry(SYSCTL_Type * ptr,uint8_t cpu,uint32_t entry)184 hpm_stat_t sysctl_set_cpu_entry(SYSCTL_Type *ptr, uint8_t cpu, uint32_t entry)
185 {
186 if (!sysctl_valid_cpu_index(cpu)) {
187 return status_invalid_argument;
188 }
189 ptr->CPU[cpu].GPR[0] = entry;
190 ptr->CPU[cpu].GPR[1] = SYSCTL_CPU_RELEASE_KEY(cpu);
191 return status_success;
192 }
193
sysctl_set_cpu1_entry(SYSCTL_Type * ptr,uint32_t entry)194 hpm_stat_t sysctl_set_cpu1_entry(SYSCTL_Type *ptr, uint32_t entry)
195 {
196 return sysctl_set_cpu_entry(ptr, 1, entry);
197 }
198
sysctl_set_cpu0_wakeup_entry(SYSCTL_Type * ptr,uint32_t entry)199 hpm_stat_t sysctl_set_cpu0_wakeup_entry(SYSCTL_Type *ptr, uint32_t entry)
200 {
201 return sysctl_set_cpu_entry(ptr, 0, entry);
202 }
203
sysctl_enable_group_resource(SYSCTL_Type * ptr,uint8_t group,sysctl_resource_t resource,bool enable)204 hpm_stat_t sysctl_enable_group_resource(SYSCTL_Type *ptr,
205 uint8_t group,
206 sysctl_resource_t resource,
207 bool enable)
208 {
209 uint32_t index, offset;
210 if (resource < sysctl_resource_linkable_start) {
211 return status_invalid_argument;
212 }
213
214 index = (resource - sysctl_resource_linkable_start) / 32;
215 offset = (resource - sysctl_resource_linkable_start) % 32;
216 switch (group) {
217 case SYSCTL_RESOURCE_GROUP0:
218 ptr->GROUP0[index].VALUE = (ptr->GROUP0[index].VALUE & ~(1UL << offset))
219 | (enable ? (1UL << offset) : 0);
220 if (enable) {
221 while (sysctl_resource_target_is_busy(ptr, resource)) {
222 ;
223 }
224 }
225 break;
226 case SYSCTL_RESOURCE_GROUP1:
227 ptr->GROUP1[index].VALUE = (ptr->GROUP1[index].VALUE & ~(1UL << offset))
228 | (enable ? (1UL << offset) : 0);
229 if (enable) {
230 while (sysctl_resource_target_is_busy(ptr, resource)) {
231 ;
232 }
233 }
234 break;
235 default:
236 return status_invalid_argument;
237 }
238
239 return status_success;
240 }
241
sysctl_check_group_resource_enable(SYSCTL_Type * ptr,uint8_t group,sysctl_resource_t resource)242 bool sysctl_check_group_resource_enable(SYSCTL_Type *ptr,
243 uint8_t group,
244 sysctl_resource_t resource)
245 {
246 uint32_t index, offset;
247 bool enable;
248
249 index = (resource - sysctl_resource_linkable_start) / 32;
250 offset = (resource - sysctl_resource_linkable_start) % 32;
251 switch (group) {
252 case SYSCTL_RESOURCE_GROUP0:
253 enable = ((ptr->GROUP0[index].VALUE & (1UL << offset)) != 0) ? true : false;
254 break;
255 case SYSCTL_RESOURCE_GROUP1:
256 enable = ((ptr->GROUP1[index].VALUE & (1UL << offset)) != 0) ? true : false;
257 break;
258 default:
259 enable = false;
260 break;
261 }
262
263 return enable;
264 }
265
sysctl_get_group_resource_value(SYSCTL_Type * ptr,uint8_t group,uint8_t index)266 uint32_t sysctl_get_group_resource_value(SYSCTL_Type *ptr, uint8_t group, uint8_t index)
267 {
268 uint32_t value;
269 switch (group) {
270 case SYSCTL_RESOURCE_GROUP0:
271 value = ptr->GROUP0[index].VALUE;
272 break;
273 case SYSCTL_RESOURCE_GROUP1:
274 value = ptr->GROUP1[index].VALUE;
275 break;
276 default:
277 value = 0;
278 break;
279 }
280 return value;
281 }
282
sysctl_add_resource_to_cpu0(SYSCTL_Type * ptr,sysctl_resource_t resource)283 hpm_stat_t sysctl_add_resource_to_cpu0(SYSCTL_Type *ptr, sysctl_resource_t resource)
284 {
285 return sysctl_enable_group_resource(ptr, SYSCTL_RESOURCE_GROUP0, resource, true);
286 }
287
sysctl_remove_resource_from_cpu0(SYSCTL_Type * ptr,sysctl_resource_t resource)288 hpm_stat_t sysctl_remove_resource_from_cpu0(SYSCTL_Type *ptr, sysctl_resource_t resource)
289 {
290 return sysctl_enable_group_resource(ptr, SYSCTL_RESOURCE_GROUP0, resource, false);
291 }
292
sysctl_add_resource_to_cpu1(SYSCTL_Type * ptr,sysctl_resource_t resource)293 hpm_stat_t sysctl_add_resource_to_cpu1(SYSCTL_Type *ptr, sysctl_resource_t resource)
294 {
295 return sysctl_enable_group_resource(ptr, SYSCTL_RESOURCE_GROUP1, resource, true);
296 }
297
sysctl_remove_resource_from_cpu1(SYSCTL_Type * ptr,sysctl_resource_t resource)298 hpm_stat_t sysctl_remove_resource_from_cpu1(SYSCTL_Type *ptr, sysctl_resource_t resource)
299 {
300 return sysctl_enable_group_resource(ptr, SYSCTL_RESOURCE_GROUP1, resource, false);
301 }
302
sysctl_set_adc_i2s_clock_mux(SYSCTL_Type * ptr,clock_node_t node,clock_source_adc_i2s_t source)303 hpm_stat_t sysctl_set_adc_i2s_clock_mux(SYSCTL_Type *ptr,
304 clock_node_t node,
305 clock_source_adc_i2s_t source)
306 {
307 uint32_t index;
308 if ((node < clock_node_adc_i2s_start)
309 || (source >= clock_source_adc_i2s_clk_end)) {
310 return status_invalid_argument;
311 }
312
313 switch (node) {
314 case clock_node_adc3:
315 case clock_node_adc2:
316 case clock_node_adc1:
317 case clock_node_adc0:
318 index = node - clock_node_adc0;
319 ptr->ADCCLK[index]
320 = (ptr->ADCCLK[index] & ~SYSCTL_ADCCLK_MUX_MASK)
321 | SYSCTL_ADCCLK_MUX_SET(source);
322 break;
323 case clock_node_i2s3:
324 case clock_node_i2s2:
325 case clock_node_i2s1:
326 case clock_node_i2s0:
327 index = node - clock_node_i2s0;
328 ptr->I2SCLK[index]
329 = (ptr->I2SCLK[index] & ~SYSCTL_I2SCLK_MUX_MASK)
330 | SYSCTL_I2SCLK_MUX_SET(source);
331 break;
332 default:
333 return status_invalid_argument;
334 }
335
336 return status_success;
337 }
338
sysctl_update_divider(SYSCTL_Type * ptr,clock_node_t node,uint32_t divide_by)339 hpm_stat_t sysctl_update_divider(SYSCTL_Type *ptr, clock_node_t node, uint32_t divide_by)
340 {
341 if (node >= clock_node_adc_i2s_start) {
342 return status_invalid_argument;
343 }
344
345 ptr->CLOCK[node] = (ptr->CLOCK[node] & ~(SYSCTL_CLOCK_DIV_MASK)) | SYSCTL_CLOCK_DIV_SET(divide_by - 1);
346 while (sysctl_clock_target_is_busy(ptr, node)) {
347 }
348 return status_success;
349 }
350
sysctl_config_clock(SYSCTL_Type * ptr,clock_node_t node,clock_source_t source,uint32_t divide_by)351 hpm_stat_t sysctl_config_clock(SYSCTL_Type *ptr, clock_node_t node,
352 clock_source_t source, uint32_t divide_by)
353 {
354 if (node >= clock_node_adc_i2s_start) {
355 return status_invalid_argument;
356 }
357
358 if (source >= clock_source_general_source_end) {
359 return status_invalid_argument;
360 }
361 ptr->CLOCK[node] = (ptr->CLOCK[node] &
362 ~(SYSCTL_CLOCK_MUX_MASK | SYSCTL_CLOCK_DIV_MASK))
363 | (SYSCTL_CLOCK_MUX_SET(source) | SYSCTL_CLOCK_DIV_SET(divide_by - 1));
364 while (sysctl_clock_target_is_busy(ptr, node)) {
365 }
366 return status_success;
367 }
368