1 /*
2  * Copyright (c) 2022-2023 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 monitor_index,monitor_config_t * config)117 void sysctl_monitor_init(SYSCTL_Type *ptr, uint8_t monitor_index, monitor_config_t *config)
118 {
119     ptr->MONITOR[monitor_index].CONTROL &= ~(SYSCTL_MONITOR_CONTROL_START_MASK | SYSCTL_MONITOR_CONTROL_OUTEN_MASK);
120 
121     if (config->mode == monitor_work_mode_compare) {
122         ptr->MONITOR[monitor_index].HIGH_LIMIT = SYSCTL_MONITOR_HIGH_LIMIT_FREQUENCY_SET(config->high_limit);
123         ptr->MONITOR[monitor_index].LOW_LIMIT = SYSCTL_MONITOR_LOW_LIMIT_FREQUENCY_SET(config->low_limit);
124     }
125 
126     ptr->MONITOR[monitor_index].CONTROL = (ptr->MONITOR[monitor_index].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 resource,bool enable)174 hpm_stat_t sysctl_enable_group_resource(SYSCTL_Type *ptr,
175                                         uint8_t group,
176                                         sysctl_resource_t resource,
177                                         bool enable)
178 {
179     uint32_t index, offset;
180     if (resource < sysctl_resource_linkable_start) {
181         return status_invalid_argument;
182     }
183 
184     index = (resource - sysctl_resource_linkable_start) / 32;
185     offset = (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, 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, 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 resource)210 bool sysctl_check_group_resource_enable(SYSCTL_Type *ptr,
211                                         uint8_t group,
212                                         sysctl_resource_t resource)
213 {
214     uint32_t index, offset;
215     bool enable;
216 
217     index = (resource - sysctl_resource_linkable_start) / 32;
218     offset = (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,uint32_t divide_by)271 hpm_stat_t sysctl_update_divider(SYSCTL_Type *ptr, clock_node_t node, uint32_t divide_by)
272 {
273     if (node >= clock_node_adc_start) {
274         return status_invalid_argument;
275     }
276 
277     ptr->CLOCK[node] = (ptr->CLOCK[node] & ~(SYSCTL_CLOCK_DIV_MASK)) | SYSCTL_CLOCK_DIV_SET(divide_by - 1);
278     while (sysctl_clock_target_is_busy(ptr, node)) {
279     }
280     return status_success;
281 }
282 
sysctl_config_clock(SYSCTL_Type * ptr,clock_node_t node,clock_source_t source,uint32_t divide_by)283 hpm_stat_t sysctl_config_clock(SYSCTL_Type *ptr, clock_node_t node, clock_source_t source, uint32_t divide_by)
284 {
285     if (node >= clock_node_adc_start) {
286         return status_invalid_argument;
287     }
288 
289     if (source >= clock_source_general_source_end) {
290         return status_invalid_argument;
291     }
292     ptr->CLOCK[node] = (ptr->CLOCK[node] & ~(SYSCTL_CLOCK_MUX_MASK | SYSCTL_CLOCK_DIV_MASK)) |
293         (SYSCTL_CLOCK_MUX_SET(source) | SYSCTL_CLOCK_DIV_SET(divide_by - 1));
294     while (sysctl_clock_target_is_busy(ptr, node)) {
295     }
296     return status_success;
297 }
298 
sysctl_config_cpu0_domain_clock(SYSCTL_Type * ptr,clock_source_t source,uint32_t cpu_div,uint32_t axi_sub_div,uint32_t ahb_sub_div)299 hpm_stat_t sysctl_config_cpu0_domain_clock(SYSCTL_Type *ptr,
300                                            clock_source_t source,
301                                            uint32_t cpu_div,
302                                            uint32_t axi_sub_div,
303                                            uint32_t ahb_sub_div)
304 {
305     if (source >= clock_source_general_source_end) {
306         return status_invalid_argument;
307     }
308 
309     uint32_t origin_cpu_div = SYSCTL_CLOCK_CPU_DIV_GET(ptr->CLOCK_CPU[0]) + 1U;
310     if (origin_cpu_div == cpu_div) {
311         ptr->CLOCK_CPU[0] = SYSCTL_CLOCK_CPU_MUX_SET(source) | SYSCTL_CLOCK_CPU_DIV_SET(cpu_div) |
312             SYSCTL_CLOCK_CPU_SUB0_DIV_SET(axi_sub_div - 1) | SYSCTL_CLOCK_CPU_SUB1_DIV_SET(ahb_sub_div - 1);
313         while (sysctl_cpu_clock_any_is_busy(ptr)) {
314         }
315     }
316     ptr->CLOCK_CPU[0] = SYSCTL_CLOCK_CPU_MUX_SET(source) | SYSCTL_CLOCK_CPU_DIV_SET(cpu_div - 1) |
317         SYSCTL_CLOCK_CPU_SUB0_DIV_SET(axi_sub_div - 1) | SYSCTL_CLOCK_CPU_SUB1_DIV_SET(ahb_sub_div - 1);
318 
319     while (sysctl_cpu_clock_any_is_busy(ptr)) {
320     }
321 
322     return status_success;
323 }
324 
sysctl_set_adc_clock_mux(SYSCTL_Type * ptr,clock_node_t node,clock_source_adc_t source)325 hpm_stat_t sysctl_set_adc_clock_mux(SYSCTL_Type *ptr, clock_node_t node, clock_source_adc_t source)
326 {
327     if (source >= clock_source_adc_clk_end) {
328         return status_invalid_argument;
329     }
330     uint32_t adc_index = (uint32_t) (node - clock_node_adc_start);
331     if (adc_index >= ARRAY_SIZE(ptr->ADCCLK)) {
332         return status_invalid_argument;
333     }
334 
335     ptr->ADCCLK[adc_index] = (ptr->ADCCLK[adc_index] & ~SYSCTL_ADCCLK_MUX_MASK) | SYSCTL_ADCCLK_MUX_SET(source);
336 
337     return status_success;
338 }
339 
sysctl_set_dac_clock_mux(SYSCTL_Type * ptr,clock_node_t node,clock_source_dac_t source)340 hpm_stat_t sysctl_set_dac_clock_mux(SYSCTL_Type *ptr, clock_node_t node, clock_source_dac_t source)
341 {
342     if (source >= clock_source_dac_clk_end) {
343         return status_invalid_argument;
344     }
345     uint32_t dac_index = (uint32_t) (node - clock_node_dac_start);
346     if (dac_index >= ARRAY_SIZE(ptr->DACCLK)) {
347         return status_invalid_argument;
348     }
349 
350     ptr->DACCLK[dac_index] = (ptr->DACCLK[dac_index] & ~SYSCTL_DACCLK_MUX_MASK) | SYSCTL_DACCLK_MUX_SET(source);
351 
352     return status_success;
353 }
354