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