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