1 /*
2 * Copyright (c) 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
13 #define SYSCTL_CPU_RELEASE_KEY(cpu) (0xC0BEF1A9UL | ((cpu & 1) << 24))
14
sysctl_get_cpu0_gpr(SYSCTL_Type * ptr,uint32_t * data,uint32_t size)15 hpm_stat_t sysctl_get_cpu0_gpr(SYSCTL_Type *ptr, uint32_t *data, uint32_t size)
16 {
17 uint32_t i;
18 for (i = 0; i < size; i++) {
19 *(data + i) = ptr->CPU[0].GPR[i];
20 }
21 return status_success;
22 }
23
sysctl_cpu0_get_gpr(SYSCTL_Type * ptr,uint8_t start,uint8_t count,uint32_t * data)24 hpm_stat_t sysctl_cpu0_get_gpr(SYSCTL_Type *ptr, uint8_t start, uint8_t count, uint32_t *data)
25 {
26 uint8_t i, size = ARRAY_SIZE(ptr->CPU[0].GPR);
27 if ((data == NULL) || !count || start > size || count > size ||
28 (start + count) > size) {
29 return status_invalid_argument;
30 }
31 for (i = 0; i < count; i++) {
32 *(data + i) = ptr->CPU[0].GPR[start + i];
33 }
34 return status_success;
35 }
36
sysctl_cpu0_set_gpr(SYSCTL_Type * ptr,uint8_t start,uint8_t count,uint32_t * data,bool lock)37 hpm_stat_t sysctl_cpu0_set_gpr(SYSCTL_Type *ptr, uint8_t start, uint8_t count, uint32_t *data, bool lock)
38 {
39 uint8_t i, size = ARRAY_SIZE(ptr->CPU[0].GPR);
40 uint32_t gpr_mask;
41 if ((data == NULL) || !count || start > size || count > size ||
42 (start + count) > size) {
43 return status_invalid_argument;
44 }
45 for (i = 0; i < count; i++) {
46 ptr->CPU[0].GPR[start + i] = *(data + i);
47 }
48 if (lock) {
49 gpr_mask = ((1 << count) - 1) << start;
50 sysctl_cpu0_lock_gpr_with_mask(ptr, gpr_mask);
51 }
52 return status_success;
53 }
54
sysctl_monitor_get_default_config(SYSCTL_Type * ptr,monitor_config_t * config)55 void sysctl_monitor_get_default_config(SYSCTL_Type *ptr, monitor_config_t *config)
56 {
57 (void) ptr;
58 config->mode = monitor_work_mode_record;
59 config->accuracy = monitor_accuracy_1khz;
60 config->reference = monitor_reference_24mhz;
61 config->divide_by = 1;
62 config->high_limit = 0;
63 config->low_limit = 0;
64 config->start_measure = true;
65 config->enable_output = false;
66 config->target = monitor_target_clk_top_cpu0;
67 }
68
sysctl_monitor_init(SYSCTL_Type * ptr,uint8_t slice,monitor_config_t * config)69 void sysctl_monitor_init(SYSCTL_Type *ptr, uint8_t slice, monitor_config_t *config)
70 {
71 ptr->MONITOR[slice].CONTROL &= ~(SYSCTL_MONITOR_CONTROL_START_MASK | SYSCTL_MONITOR_CONTROL_OUTEN_MASK);
72
73 if (config->mode == monitor_work_mode_compare) {
74 ptr->MONITOR[slice].HIGH_LIMIT = SYSCTL_MONITOR_HIGH_LIMIT_FREQUENCY_SET(config->high_limit);
75 ptr->MONITOR[slice].LOW_LIMIT = SYSCTL_MONITOR_LOW_LIMIT_FREQUENCY_SET(config->low_limit);
76 }
77
78 ptr->MONITOR[slice].CONTROL = (ptr->MONITOR[slice].CONTROL &
79 ~(SYSCTL_MONITOR_CONTROL_DIV_MASK | SYSCTL_MONITOR_CONTROL_MODE_MASK | SYSCTL_MONITOR_CONTROL_ACCURACY_MASK |
80 SYSCTL_MONITOR_CONTROL_REFERENCE_MASK | SYSCTL_MONITOR_CONTROL_SELECTION_MASK)) |
81 (SYSCTL_MONITOR_CONTROL_DIV_SET(config->divide_by - 1) | SYSCTL_MONITOR_CONTROL_MODE_SET(config->mode) |
82 SYSCTL_MONITOR_CONTROL_ACCURACY_SET(config->accuracy) |
83 SYSCTL_MONITOR_CONTROL_REFERENCE_SET(config->reference) |
84 SYSCTL_MONITOR_CONTROL_START_SET(config->start_measure) |
85 SYSCTL_MONITOR_CONTROL_OUTEN_SET(config->enable_output) |
86 SYSCTL_MONITOR_CONTROL_SELECTION_SET(config->target));
87 }
88
89 uint32_t
sysctl_monitor_measure_frequency(SYSCTL_Type * ptr,uint8_t monitor_index,monitor_target_t target,bool enable_output)90 sysctl_monitor_measure_frequency(SYSCTL_Type *ptr, uint8_t monitor_index, monitor_target_t target, bool enable_output)
91 {
92 uint32_t frequency = 0;
93 monitor_config_t monitor = { 0 };
94 sysctl_monitor_get_default_config(ptr, &monitor);
95 monitor.target = target;
96 monitor.enable_output = enable_output;
97 sysctl_monitor_init(ptr, monitor_index, &monitor);
98 if (monitor_index < SYSCTL_SOC_MONITOR_SLICE_COUNT) {
99 frequency = sysctl_monitor_get_current_result(ptr, monitor_index);
100 }
101 return frequency;
102 }
103
sysctl_set_cpu0_entry(SYSCTL_Type * ptr,uint32_t entry)104 hpm_stat_t sysctl_set_cpu0_entry(SYSCTL_Type *ptr, uint32_t entry)
105 {
106 ptr->CPU[0].GPR[0] = entry;
107 ptr->CPU[0].GPR[1] = SYSCTL_CPU_RELEASE_KEY(0);
108 return status_success;
109 }
110
sysctl_set_cpu0_lp_mode(SYSCTL_Type * ptr,cpu_lp_mode_t mode)111 hpm_stat_t sysctl_set_cpu0_lp_mode(SYSCTL_Type *ptr, cpu_lp_mode_t mode)
112 {
113 ptr->CPU[0].LP = (ptr->CPU[0].LP & ~(SYSCTL_CPU_LP_MODE_MASK)) | (mode);
114 return status_success;
115 }
116
117 hpm_stat_t
sysctl_enable_group_resource(SYSCTL_Type * ptr,uint8_t group,sysctl_resource_t linkable_resource,bool enable)118 sysctl_enable_group_resource(SYSCTL_Type *ptr, uint8_t group, sysctl_resource_t linkable_resource, bool enable)
119 {
120 uint32_t index, offset;
121 if (linkable_resource < sysctl_resource_linkable_start) {
122 return status_invalid_argument;
123 }
124
125 index = (linkable_resource - sysctl_resource_linkable_start) / 32;
126 offset = (linkable_resource - sysctl_resource_linkable_start) % 32;
127 switch (group) {
128 case SYSCTL_RESOURCE_GROUP0:
129 ptr->GROUP0[index].VALUE = (ptr->GROUP0[index].VALUE & ~(1UL << offset)) | (enable ? (1UL << offset) : 0);
130 if (enable) {
131 while (sysctl_resource_target_is_busy(ptr, linkable_resource)) {
132 ;
133 }
134 }
135 break;
136 default:
137 return status_invalid_argument;
138 }
139
140 return status_success;
141 }
142
sysctl_check_group_resource_enable(SYSCTL_Type * ptr,uint8_t group,sysctl_resource_t linkable_resource)143 bool sysctl_check_group_resource_enable(SYSCTL_Type *ptr,
144 uint8_t group,
145 sysctl_resource_t linkable_resource)
146 {
147 uint32_t index, offset;
148 bool enable;
149
150 index = (linkable_resource - sysctl_resource_linkable_start) / 32;
151 offset = (linkable_resource - sysctl_resource_linkable_start) % 32;
152 switch (group) {
153 case SYSCTL_RESOURCE_GROUP0:
154 enable = ((ptr->GROUP0[index].VALUE & (1UL << offset)) != 0) ? true : false;
155 break;
156 default:
157 enable = false;
158 break;
159 }
160
161 return enable;
162 }
163
sysctl_get_group_resource_value(SYSCTL_Type * ptr,uint8_t group,uint8_t index)164 uint32_t sysctl_get_group_resource_value(SYSCTL_Type *ptr, uint8_t group, uint8_t index)
165 {
166 uint32_t value;
167 switch (group) {
168 case SYSCTL_RESOURCE_GROUP0:
169 value = ptr->GROUP0[index].VALUE;
170 break;
171 default:
172 value = 0;
173 break;
174 }
175 return value;
176 }
177
sysctl_add_resource_to_cpu0(SYSCTL_Type * ptr,sysctl_resource_t resource)178 hpm_stat_t sysctl_add_resource_to_cpu0(SYSCTL_Type *ptr, sysctl_resource_t resource)
179 {
180 return sysctl_enable_group_resource(ptr, SYSCTL_RESOURCE_GROUP0, resource, true);
181 }
182
sysctl_remove_resource_from_cpu0(SYSCTL_Type * ptr,sysctl_resource_t resource)183 hpm_stat_t sysctl_remove_resource_from_cpu0(SYSCTL_Type *ptr, sysctl_resource_t resource)
184 {
185 return sysctl_enable_group_resource(ptr, SYSCTL_RESOURCE_GROUP0, resource, false);
186 }
187
sysctl_update_divider(SYSCTL_Type * ptr,clock_node_t node_index,uint32_t divide_by)188 hpm_stat_t sysctl_update_divider(SYSCTL_Type *ptr, clock_node_t node_index, uint32_t divide_by)
189 {
190 uint32_t node = (uint32_t) node_index;
191 if (node >= clock_node_adc_start) {
192 return status_invalid_argument;
193 }
194
195 ptr->CLOCK[node] = (ptr->CLOCK[node] & ~(SYSCTL_CLOCK_DIV_MASK)) | SYSCTL_CLOCK_DIV_SET(divide_by - 1);
196 while (sysctl_clock_target_is_busy(ptr, node)) {
197 }
198 return status_success;
199 }
200
sysctl_config_clock(SYSCTL_Type * ptr,clock_node_t node_index,clock_source_t source,uint32_t divide_by)201 hpm_stat_t sysctl_config_clock(SYSCTL_Type *ptr, clock_node_t node_index, clock_source_t source, uint32_t divide_by)
202 {
203 uint32_t node = (uint32_t) node_index;
204 if (node >= clock_node_adc_start) {
205 return status_invalid_argument;
206 }
207
208 if (source >= clock_source_general_source_end) {
209 return status_invalid_argument;
210 }
211 ptr->CLOCK[node] = (ptr->CLOCK[node] & ~(SYSCTL_CLOCK_MUX_MASK | SYSCTL_CLOCK_DIV_MASK)) |
212 (SYSCTL_CLOCK_MUX_SET(source) | SYSCTL_CLOCK_DIV_SET(divide_by - 1));
213 while (sysctl_clock_target_is_busy(ptr, node)) {
214 }
215 return status_success;
216 }
217
sysctl_set_adc_clock_mux(SYSCTL_Type * ptr,clock_node_t node,clock_source_adc_t source)218 hpm_stat_t sysctl_set_adc_clock_mux(SYSCTL_Type *ptr, clock_node_t node, clock_source_adc_t source)
219 {
220 if (source >= clock_source_adc_clk_end) {
221 return status_invalid_argument;
222 }
223 uint32_t adc_index = (uint32_t)(node - clock_node_adc_start);
224 if (adc_index >= ARRAY_SIZE(ptr->ADCCLK)) {
225 return status_invalid_argument;
226 }
227
228 ptr->ADCCLK[adc_index] = (ptr->ADCCLK[adc_index] & ~SYSCTL_ADCCLK_MUX_MASK) | SYSCTL_ADCCLK_MUX_SET(source);
229
230 return status_success;
231 }
232
sysctl_set_i2s_clock_mux(SYSCTL_Type * ptr,clock_node_t node,clock_source_i2s_t source)233 hpm_stat_t sysctl_set_i2s_clock_mux(SYSCTL_Type *ptr, clock_node_t node, clock_source_i2s_t source)
234 {
235 if (source >= clock_source_i2s_clk_end) {
236 return status_invalid_argument;
237 }
238 uint32_t i2s_index = (uint32_t)(node - clock_node_i2s_start);
239 if (i2s_index >= ARRAY_SIZE(ptr->I2SCLK)) {
240 return status_invalid_argument;
241 }
242
243 ptr->I2SCLK[i2s_index] = (ptr->I2SCLK[i2s_index] & ~SYSCTL_I2SCLK_MUX_MASK) | SYSCTL_I2SCLK_MUX_SET(source);
244
245 return status_success;
246 }
247