1 /*
2  * Copyright (c) 2022-2024 HPMicro
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  *
6  * Change Logs:
7  * Date         Author      Notes
8  * 2022-05-09   HPMicro     First version
9  * 2023-04-12   HPMicro     Adapt hpm_sdk v1.0.0
10  * 2023-05-13   HPMicro     Fix compiling error on HPM6360/HPM6200
11  * 2023-06-10	HPMicro     Add PWMv2 support
12  */
13 
14 #include <rtthread.h>
15 
16 #if defined(BSP_USING_PWM) || defined(BSP_USING_PWMV2)
17 #if defined(BSP_USING_PWMV2)
18 #define HPMSOC_HAS_HPMSDK_PWMV2
19 #endif
20 #include <rthw.h>
21 #include <rtdevice.h>
22 #include "board.h"
23 #include "drv_gpio.h"
24 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
25 #include "hpm_pwmv2_drv.h"
26 #else
27 #include "hpm_pwm_drv.h"
28 #endif
29 #include "hpm_clock_drv.h"
30 
31 #ifdef HPM_PWM3
32 #define PWM_INSTANCE_NUM 4
33 #elif defined(HPM_PWM2)
34 #define PWM_INSTANCE_NUM 3
35 #elif defined(HPM_PWM1)
36 #define PWM_INSTANCE_NUM 2
37 #else
38 #define PWM_INSTANCE_NUM 1
39 #endif
40 
41 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
42 static PWMV2_Type * pwm_base_tbl[PWM_INSTANCE_NUM] = {
43 #else
44 static PWM_Type * pwm_base_tbl[PWM_INSTANCE_NUM] = {
45 #endif
46     HPM_PWM0,
47 #ifdef HPM_PWM1
48     HPM_PWM1,
49 #endif
50 #ifdef HPM_PWM2
51     HPM_PWM2,
52 #endif
53 #ifdef HPM_PWM3
54     HPM_PWM3
55 #endif
56     };
57 
58 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
59 
60 #ifdef PWMV2_CNT_3
61 #define PWMV2_CNT_NUM 4
62 #elif PWMV2_CNT_2
63 #define PWMV2_CNT_NUM 3
64 #elif PWMV2_CNT_1
65 #define PWMV2_CNT_NUM 2
66 #else
67 #define PWMV2_CNT_NUM 1
68 #endif
69 
70 static pwm_counter_t pwmv2_counter_tbl[PWMV2_CNT_NUM * 2] = {
71     pwm_counter_0,
72     pwm_counter_0,
73 #ifdef PWMV2_CNT_1
74     pwm_counter_1,
75     pwm_counter_1,
76 #endif
77 #ifdef PWMV2_CNT_2
78     pwm_counter_2,
79     pwm_counter_2,
80 #endif
81 #ifdef PWMV2_CNT_3
82     pwm_counter_3,
83     pwm_counter_3,
84 #endif
85 };
86 #endif
87 
hpm_generate_central_aligned_waveform(uint8_t pwm_index,uint8_t channel,uint32_t period,uint32_t pulse)88 rt_err_t hpm_generate_central_aligned_waveform(uint8_t pwm_index, uint8_t channel, uint32_t period, uint32_t pulse)
89 {
90 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
91     PWMV2_Type * pwm_base;
92     pwm_counter_t pwm_counter;
93 #else
94     PWM_Type * pwm_base;
95     pwm_cmp_config_t cmp_config[2] = {0};
96     pwm_config_t pwm_config = {0};
97 #endif
98     uint32_t duty;
99     uint32_t reload = 0;
100     uint32_t freq;
101     pwm_base = pwm_base_tbl[pwm_index];
102 
103     init_pwm_pins(pwm_base);
104     freq = board_init_pwm_clock(pwm_base);
105     if(period != 0) {
106         reload = (uint64_t)freq * period / 1000000000;
107     } else {
108         reload = 0;
109     }
110     duty = (uint64_t)freq * pulse / 1000000000;
111 
112 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
113 
114     pwm_counter = pwmv2_counter_tbl[channel];
115 
116     pwmv2_disable_counter(pwm_base, pwm_counter);
117     pwmv2_reset_counter(pwm_base, pwm_counter);
118     pwmv2_shadow_register_unlock(pwm_base);
119 
120     pwmv2_set_shadow_val(pwm_base, channel / 2, reload, 0, false);  /**< cnt use 0-3 shadow */
121     pwmv2_set_shadow_val(pwm_base, channel * 2 + 4, reload + 1, 0, false);
122     pwmv2_set_shadow_val(pwm_base, channel * 2 + 5, reload, 0, false);
123 
124     pwmv2_counter_select_data_offset_from_shadow_value(pwm_base, pwm_counter, channel / 2);
125     pwmv2_counter_burst_disable(pwm_base, pwm_counter);
126     pwmv2_set_reload_update_time(pwm_base, pwm_counter, pwm_reload_update_on_reload);
127 
128     pwmv2_select_cmp_source(pwm_base, channel * 2, cmp_value_from_shadow_val, channel * 2 + 4);
129     pwmv2_select_cmp_source(pwm_base, channel * 2 + 1, cmp_value_from_shadow_val, channel * 2 + 5);
130 
131     pwmv2_shadow_register_lock(pwm_base);
132     pwmv2_disable_four_cmp(pwm_base, channel);
133     pwmv2_channel_enable_output(pwm_base, channel);
134     pwmv2_enable_counter(pwm_base, pwm_counter);
135     pwmv2_start_pwm_output(pwm_base, pwm_counter);
136 
137     pwmv2_shadow_register_unlock(pwm_base);
138     pwmv2_set_shadow_val(pwm_base, channel * 2, (reload - duty) >> 1, 0, false);
139     pwmv2_set_shadow_val(pwm_base, channel * 2, (reload + duty) >> 1, 0, false);
140     pwmv2_shadow_register_lock(pwm_base);
141 #else
142 
143     pwm_stop_counter(pwm_base);
144     pwm_get_default_pwm_config(pwm_base, &pwm_config);
145 
146     /*
147      * reload and start counter
148      */
149     pwm_set_reload(pwm_base, 0, reload);
150     pwm_set_start_count(pwm_base, 0, 0);
151 
152     /*
153      * config cmp1 and cmp2
154      */
155 
156     cmp_config[0].mode = pwm_cmp_mode_output_compare;
157     cmp_config[0].cmp = (reload - duty) >> 1;
158     cmp_config[0].update_trigger = pwm_shadow_register_update_on_shlk;
159 
160     cmp_config[1].mode = pwm_cmp_mode_output_compare;
161     cmp_config[1].cmp = (reload + duty) >> 1;
162     cmp_config[1].update_trigger = pwm_shadow_register_update_on_shlk;
163 
164 
165     pwm_config.enable_output = true;
166     pwm_config.dead_zone_in_half_cycle = 0;
167     pwm_config.invert_output = false;
168     /*
169      * config pwm
170      */
171     if (status_success != pwm_setup_waveform(pwm_base, channel, &pwm_config, channel * 2, cmp_config, 2)) {
172         return -RT_ERROR;
173     }
174     pwm_start_counter(pwm_base);
175     pwm_issue_shadow_register_lock_event(pwm_base);
176 
177 #endif
178     return RT_EOK;
179 
180 }
181 
hpm_set_central_aligned_waveform(uint8_t pwm_index,uint8_t channel,uint32_t period,uint32_t pulse)182 rt_err_t hpm_set_central_aligned_waveform(uint8_t pwm_index, uint8_t channel, uint32_t period, uint32_t pulse)
183 {
184 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
185     PWMV2_Type * pwm_base;
186 #else
187     PWM_Type * pwm_base;
188     pwm_config_t pwm_config = {0};
189 #endif
190 
191     uint32_t duty;
192     uint32_t reload = 0;
193     uint32_t freq;
194 
195     pwm_base = pwm_base_tbl[pwm_index];
196     freq = board_init_pwm_clock(pwm_base);
197     if(period != 0) {
198         reload = (uint64_t)freq * period / 1000000000;
199     } else {
200         reload = 0;
201     }
202     duty = (uint64_t)freq * pulse / 1000000000;
203 
204 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
205     pwmv2_shadow_register_unlock(pwm_base);
206     pwmv2_set_shadow_val(pwm_base, channel / 2, reload, 0, false);  /**< cnt use 0-3 shadow */
207     pwmv2_set_shadow_val(pwm_base, channel * 2 + 4, (reload - duty) >> 1, 0, false);
208     pwmv2_set_shadow_val(pwm_base, channel * 2 + 5, (reload + duty) >> 1, 0, false);
209     pwmv2_shadow_register_lock(pwm_base);
210 #else
211     pwm_get_default_pwm_config(pwm_base, &pwm_config);
212     pwm_set_reload(pwm_base, 0, reload);
213     pwm_update_raw_cmp_central_aligned(pwm_base, channel * 2, channel * 2 + 1, (reload - duty) >> 1, (reload + duty) >> 1);
214     pwm_issue_shadow_register_lock_event(pwm_base);
215 #endif
216 
217     return RT_EOK;
218 }
219 
hpm_disable_pwm(uint8_t pwm_index,uint8_t channel)220 rt_err_t hpm_disable_pwm(uint8_t pwm_index, uint8_t channel)
221 {
222 #if defined(HPMSOC_HAS_HPMSDK_PWMV2)
223     PWMV2_Type * pwm_base;
224 
225     pwm_base = pwm_base_tbl[pwm_index];
226     pwmv2_shadow_register_unlock(pwm_base);
227     pwmv2_set_shadow_val(pwm_base, channel * 2 + 4, 0, 0, false);
228     pwmv2_set_shadow_val(pwm_base, channel * 2 + 5, 0, 0, false);
229     pwmv2_shadow_register_lock(pwm_base);
230 #else
231     pwm_disable_output(pwm_base_tbl[pwm_index], channel);
232 #endif
233     return RT_EOK;
234 
235 }
236 
hpm_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)237 rt_err_t hpm_pwm_control(struct rt_device_pwm * device, int cmd, void *arg)
238 {
239     uint8_t channel;
240     uint32_t period;
241     uint32_t pulse;
242     rt_err_t sta = RT_EOK;
243     unsigned char pwm_name;
244     struct rt_pwm_configuration * configuration;
245     configuration = (struct rt_pwm_configuration * )arg;
246     channel = configuration->channel;
247     period  = configuration->period;
248     pulse   = configuration->pulse;
249     if (strcmp("pwm0", device->parent.parent.name) == 0) {
250         pwm_name = 0;
251     } else if (strcmp("pwm1", device->parent.parent.name) == 0) {
252         pwm_name = 1;
253     } else if (strcmp("pwm2", device->parent.parent.name) == 0) {
254         pwm_name = 2;
255     } else if (strcmp("pwm3", device->parent.parent.name) == 0) {
256         pwm_name = 3;
257     } else {
258         return -RT_ERROR;
259     }
260 
261     switch(cmd) {
262         case PWM_CMD_ENABLE: {
263             sta = hpm_generate_central_aligned_waveform(pwm_name, channel, period, pulse);
264             break;
265         }
266         case PWM_CMD_DISABLE: {
267             hpm_disable_pwm(pwm_name, channel);
268             break;
269         }
270         case PWM_CMD_SET: {
271             sta = hpm_set_central_aligned_waveform(pwm_name, channel, period, pulse);
272             break;
273         }
274         case PWM_CMD_GET: {
275             sta = RT_EOK;
276             break;
277         }
278         default: {
279             sta = -RT_ERROR;
280             break;
281         }
282     }
283     return sta;
284 }
285 
hpm_pwm_dev_control(rt_device_t device,int cmd,void * arg)286 rt_err_t hpm_pwm_dev_control(rt_device_t device, int cmd, void *arg)
287 {
288     uint8_t channel;
289     uint32_t period;
290     uint32_t pulse;
291     rt_err_t sta = RT_EOK;
292     uint8_t pwm_name;
293     struct rt_pwm_configuration * configuration;
294     configuration = (struct rt_pwm_configuration * )arg;
295     channel = configuration->channel;
296     period  = configuration->period;
297     pulse   = configuration->pulse;
298     if (strcmp("pwm0", device->parent.name) == 0) {
299         pwm_name = 0;
300     } else if (strcmp("pwm1", device->parent.name) == 0) {
301         pwm_name = 1;
302     } else if (strcmp("pwm2", device->parent.name) == 0) {
303         pwm_name = 2;
304     } else if (strcmp("pwm3", device->parent.name) == 0) {
305         pwm_name = 3;
306     } else {
307         return -RT_ERROR;
308     }
309 
310     switch(cmd) {
311         case PWM_CMD_ENABLE: {
312             sta = hpm_generate_central_aligned_waveform(pwm_name, channel, period, pulse);
313             break;
314         }
315         case PWM_CMD_DISABLE: {
316             hpm_disable_pwm(pwm_name, channel);
317             break;
318         }
319         case PWM_CMD_SET: {
320             sta = hpm_set_central_aligned_waveform(pwm_name, channel, period, pulse);
321             break;
322         }
323         case PWM_CMD_GET: {
324             sta = RT_EOK;
325             break;
326         }
327         default: {
328             sta = -RT_ERROR;
329             break;
330         }
331     }
332     return sta;
333 }
334 
335 const static struct rt_pwm_ops hpm_pwm_ops = {
336         .control = &hpm_pwm_control
337 };
338 
339 static struct rt_device hpm_pwm_parent = {
340         .control = hpm_pwm_dev_control
341 };
342 
343 #ifdef HPM_PWM0
344 static struct rt_device_pwm hpm_dev_pwm0 = {
345         .ops = &hpm_pwm_ops,
346 };
347 #endif
348 
349 #ifdef HPM_PWM1
350 static struct rt_device_pwm hpm_dev_pwm1 = {
351         .ops = &hpm_pwm_ops,
352 };
353 #endif
354 
355 #ifdef HPM_PWM2
356 static struct rt_device_pwm hpm_dev_pwm2 = {
357         .ops = &hpm_pwm_ops,
358 };
359 #endif
360 
361 #ifdef HPM_PWM3
362 static struct rt_device_pwm hpm_dev_pwm3 = {
363         .ops = &hpm_pwm_ops,
364 };
365 #endif
366 
367 
rt_hw_pwm_init(void)368 int rt_hw_pwm_init(void)
369 {
370     int ret = RT_EOK;
371 
372 #ifdef HPM_PWM0
373     hpm_dev_pwm0.parent = hpm_pwm_parent;
374     ret = rt_device_pwm_register(&hpm_dev_pwm0, "pwm0", &hpm_pwm_ops, RT_NULL);
375 #endif
376 
377 #ifdef HPM_PWM1
378     hpm_dev_pwm1.parent = hpm_pwm_parent;
379     ret = rt_device_pwm_register(&hpm_dev_pwm1, "pwm1", &hpm_pwm_ops, RT_NULL);
380 #endif
381 #ifdef HPM_PWM2
382     hpm_dev_pwm2.parent = hpm_pwm_parent;
383     ret = rt_device_pwm_register(&hpm_dev_pwm2, "pwm2", &hpm_pwm_ops, RT_NULL);
384 #endif
385 #ifdef HPM_PWM3
386     hpm_dev_pwm3.parent = hpm_pwm_parent;
387     ret = rt_device_pwm_register(&hpm_dev_pwm3, "pwm3", &hpm_pwm_ops, RT_NULL);
388 #endif
389 
390     return ret;
391 }
392 
393 INIT_BOARD_EXPORT(rt_hw_pwm_init);
394 
395 #endif /* BSP_USING_PWM */
396