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