1
2 /* Copyright (c) 2019-2025 Allwinner Technology Co., Ltd. ALL rights reserved.
3
4 * Allwinner is a trademark of Allwinner Technology Co.,Ltd., registered in
5 * the the People's Republic of China and other countries.
6 * All Allwinner Technology Co.,Ltd. trademarks are used with permission.
7
8 * DISCLAIMER
9 * THIRD PARTY LICENCES MAY BE REQUIRED TO IMPLEMENT THE SOLUTION/PRODUCT.
10 * IF YOU NEED TO INTEGRATE THIRD PARTY’S TECHNOLOGY (SONY, DTS, DOLBY, AVS OR MPEGLA, ETC.)
11 * IN ALLWINNERS’SDK OR PRODUCTS, YOU SHALL BE SOLELY RESPONSIBLE TO OBTAIN
12 * ALL APPROPRIATELY REQUIRED THIRD PARTY LICENCES.
13 * ALLWINNER SHALL HAVE NO WARRANTY, INDEMNITY OR OTHER OBLIGATIONS WITH RESPECT TO MATTERS
14 * COVERED UNDER ANY REQUIRED THIRD PARTY LICENSE.
15 * YOU ARE SOLELY RESPONSIBLE FOR YOUR USAGE OF THIRD PARTY’S TECHNOLOGY.
16
17
18 * THIS SOFTWARE IS PROVIDED BY ALLWINNER"AS IS" AND TO THE MAXIMUM EXTENT
19 * PERMITTED BY LAW, ALLWINNER EXPRESSLY DISCLAIMS ALL WARRANTIES OF ANY KIND,
20 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION REGARDING
21 * THE TITLE, NON-INFRINGEMENT, ACCURACY, CONDITION, COMPLETENESS, PERFORMANCE
22 * OR MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23 * IN NO EVENT SHALL ALLWINNER BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 * LOSS OF USE, DATA, OR PROFITS, OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 * OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33
34 /*
35 * ===========================================================================================
36 *
37 * Filename: hal_pwm.c
38 *
39 * Description: spi driver core hal,be used by drv_pwm.c
40 *
41 * Version: Melis3.0
42 * Create: 2019-12-23
43 * Revision: none
44 * Compiler: GCC:version 9.2.1 20170904 (release),ARM/embedded-7-branch revision 255204
45 *
46 * Author: liuyus@allwinnertech.com
47 * Organization: SWC-BPD
48 * Last Modified: 2019-12-31 17:55
49 *
50 * ===========================================================================================
51 */
52
53 #include <hal_log.h>
54 #include <stdio.h>
55 #include <stdint.h>
56 #include <hal_clk.h>
57 #include <hal_reset.h>
58 #include <sunxi_hal_common.h>
59 #include <sunxi_hal_pwm.h>
60 #include "sunxi/clk.h"
61 #include <hal_cfg.h>
62 #include <script.h>
63 // #include <standby/standby.h>
64
65 hal_pwm_t sunxi_pwm;
66 static int pwm_init = 0;
67
68 #define SET_REG_VAL(reg_val, shift, width, set_val) ((reg_val & ~((-1UL) >> (32 - width) << shift)) | (set_val << shift))
69
70 #define pwm_do_div(n,base) ({ \
71 u32 __base = (base); \
72 u32 __rem; \
73 __rem = ((u64)(n)) % __base; \
74 (n) = ((u64)(n)) / __base; \
75 if (__rem > __base / 2) \
76 ++(n); \
77 __rem; \
78 })
79
80 /************** config *************************/
81 /*
82 * pwm_set_clk_src(): pwm clock source selection
83 *
84 * @channel_in: pwm channel number
85 * pwm01 pwm23 pwm45 pwm67 pwm89
86 *
87 * @clk_src: The clock you want to set
88 * 0:OSC24M 1:APB1
89 */
hal_pwm_clk_src_set(uint32_t channel_in,hal_pwm_clk_src clk_src)90 void hal_pwm_clk_src_set(uint32_t channel_in, hal_pwm_clk_src clk_src)
91 {
92 unsigned long reg_addr = PWM_BASE + PWM_PCCR_BASE;
93 uint32_t reg_val;
94
95 uint32_t channel = channel_in / 2;
96 reg_addr += 4 * channel;
97 /*set clock source OSC24M or apb1*/
98 reg_val = hal_readl(reg_addr);
99 reg_val = SET_REG_VAL(reg_val, PWM_CLK_SRC_SHIFT, PWM_CLK_SRC_WIDTH, clk_src);
100 hal_writel(reg_val, reg_addr);
101 }
102
103 /*
104 * pwm_clk_div_m(): pwm clock divide
105 *
106 * @div_m: 1 2 4 8 16 32 64 128 256
107 */
hal_pwm_clk_div_m(uint32_t channel_in,uint32_t div_m)108 void hal_pwm_clk_div_m(uint32_t channel_in, uint32_t div_m)
109 {
110 unsigned long reg_addr = PWM_BASE + PWM_PCCR_BASE;
111 uint32_t reg_val;
112
113 uint32_t channel = channel_in / 2;
114 reg_addr += 4 * channel;
115 /*set clock div_m*/
116 reg_val = hal_readl(reg_addr);
117 reg_val = SET_REG_VAL(reg_val, PWM_DIV_M_SHIFT, PWM_DIV_M_WIDTH, div_m);
118 hal_writel(reg_val, reg_addr);
119 }
120
121
hal_pwm_prescal_set(uint32_t channel_in,uint32_t prescal)122 void hal_pwm_prescal_set(uint32_t channel_in, uint32_t prescal)
123 {
124 unsigned long reg_addr = PWM_BASE + PWM_PCR;
125 uint32_t reg_val;
126
127 uint32_t channel = channel_in;
128 reg_addr += 0x20 * channel;
129 /*set prescal*/
130 reg_val = hal_readl(reg_addr);
131 reg_val = SET_REG_VAL(reg_val, PWM_PRESCAL_SHIFT, PWM_PRESCAL_WIDTH, prescal);
132 hal_writel(reg_val, reg_addr);
133 }
134
135
136
137 /* active cycles */
hal_pwm_set_active_cycles(uint32_t channel_in,uint32_t active_cycles)138 void hal_pwm_set_active_cycles(uint32_t channel_in, uint32_t active_cycles) //64
139 {
140 unsigned long reg_addr = PWM_BASE + PWM_PPR ;
141 uint32_t reg_val;
142
143 uint32_t channel = channel_in;
144 reg_addr += 0x20 * channel;
145 /*set active*/
146 reg_val = hal_readl(reg_addr);
147 reg_val = SET_REG_VAL(reg_val, PWM_ACTIVE_CYCLES_SHIFT, PWM_ACTIVE_CYCLES_WIDTH, active_cycles);
148 hal_writel(reg_val, reg_addr);
149 }
150
151 /* entire cycles */
hal_pwm_set_period_cycles(uint32_t channel_in,uint32_t period_cycles)152 void hal_pwm_set_period_cycles(uint32_t channel_in, uint32_t period_cycles)
153 {
154 unsigned long reg_addr = PWM_BASE + PWM_PPR ;
155 uint32_t reg_val;
156
157 uint32_t channel = channel_in;
158 reg_addr += 0x20 * channel;
159 /*set clock BYPASS*/
160 reg_val = hal_readl(reg_addr);
161 reg_val = SET_REG_VAL(reg_val, PWM_PERIOD_SHIFT, PWM_PERIOD_WIDTH, period_cycles);
162 hal_writel(reg_val, reg_addr);
163 }
164
get_pccr_reg_offset(uint32_t channel)165 static uint32_t get_pccr_reg_offset(uint32_t channel)
166 {
167 uint32_t val;
168
169 switch (channel)
170 {
171 case 0:
172 case 1:
173 return PWM_PCCR01;
174 break;
175 case 2:
176 case 3:
177 return PWM_PCCR23;
178 break;
179 case 4:
180 case 5:
181 return PWM_PCCR45;
182 break;
183 case 6:
184 case 7:
185 return PWM_PCCR67;
186 break;
187 default :
188 PWM_ERR("channel is error \n");
189 return PWM_PCCR01;
190 break;
191 }
192 }
193
194 /************ enable **************/
195
hal_pwm_enable_clk_gating(uint32_t channel_in)196 void hal_pwm_enable_clk_gating(uint32_t channel_in)
197 {
198 unsigned long reg_addr = PWM_BASE + PWM_PCGR;
199 uint32_t reg_val;
200
201 uint32_t channel = channel_in / 2;
202 /*enable clk_gating*/
203 reg_addr += 4 * channel;
204 reg_val = hal_readl(reg_addr);
205 reg_val = SET_REG_VAL(reg_val, PWM_CLK_GATING_SHIFT, PWM_CLK_GATING_WIDTH, 1);
206 hal_writel(reg_val, reg_addr);
207 }
208
hal_pwm_enable_controller(uint32_t channel_in)209 void hal_pwm_enable_controller(uint32_t channel_in)
210 {
211 unsigned long reg_addr = PWM_BASE + PWM_PER;
212 uint32_t reg_val;
213
214 reg_val = readl(reg_addr);
215 reg_val |= 1 << channel_in;
216
217 writel(reg_val, reg_addr);
218 }
219
220 /************ disable **************/
hal_pwm_disable_controller(uint32_t channel_in)221 void hal_pwm_disable_controller(uint32_t channel_in)
222 {
223 unsigned long reg_val;
224 unsigned long reg_addr = PWM_BASE + PWM_PER;
225
226 reg_val = readl(reg_addr);
227 reg_val |= 1 << channel_in;
228
229 writel(reg_val, reg_addr);
230 }
231
232
233 /*************** polarity *****************/
hal_pwm_porality(uint32_t channel_in,hal_pwm_polarity polarity)234 void hal_pwm_porality(uint32_t channel_in, hal_pwm_polarity polarity)
235 {
236 uint32_t reg_val;
237 unsigned long reg_addr = PWM_BASE + PWM_PCR;
238
239 uint32_t channel = channel_in;
240 reg_addr += 0x20 * channel;
241 /*set polarity*/
242 reg_val = hal_readl(reg_addr);
243 reg_val = SET_REG_VAL(reg_val, PWM_ACT_STA_SHIFT, PWM_ACT_STA_WIDTH, polarity);
244 hal_writel(reg_val, reg_addr);
245 }
246
hal_pwm_pinctrl_init(hal_pwm_t sunxi_pwm,int channel)247 static int hal_pwm_pinctrl_init(hal_pwm_t sunxi_pwm, int channel)
248 {
249 user_gpio_set_t gpio_cfg = {0};
250 char pwm_name[16];
251 int count, ret;
252
253 sprintf(pwm_name, "pwm%d", channel);
254
255 count = Hal_Cfg_GetGPIOSecKeyCount(pwm_name);
256 if (!count)
257 {
258 PWM_ERR("[pwm%d] not support in sys_config\n", channel);
259 return -1;
260 }
261
262 Hal_Cfg_GetGPIOSecData(pwm_name, &gpio_cfg, count);
263
264 sunxi_pwm.pin[channel] = (gpio_cfg.port - 1) * 32 + gpio_cfg.port_num;
265 sunxi_pwm.enable_muxsel[channel] = gpio_cfg.mul_sel;
266 ret = hal_gpio_pinmux_set_function(sunxi_pwm.pin[channel], sunxi_pwm.enable_muxsel[channel]);
267 if (ret)
268 {
269 PWM_ERR("[pwm%d] PIN%u set function failed! return %d\n", channel, sunxi_pwm.pin[channel], ret);
270 return -1;
271 }
272
273 ret = hal_gpio_set_driving_level(sunxi_pwm.pin[channel], gpio_cfg.drv_level);
274 if (ret)
275 {
276 PWM_ERR("[spi%d] PIN%u set driving level failed! return %d\n", channel, gpio_cfg.drv_level, ret);
277 return -1;
278 }
279
280 if (gpio_cfg.pull)
281 {
282 return hal_gpio_set_pull(sunxi_pwm.pin[channel], gpio_cfg.pull);
283 }
284
285 return 0;
286 }
287
hal_pwm_pinctrl_exit(hal_pwm_t sunxi_pwm,uint32_t channel)288 static int hal_pwm_pinctrl_exit(hal_pwm_t sunxi_pwm, uint32_t channel)
289 {
290 if (sunxi_pwm.pin[channel]) //sys_config
291 {
292 return hal_gpio_pinmux_set_function(sunxi_pwm.pin[channel], 0); //gpio_in
293 }
294 else
295 {
296 return hal_gpio_pinmux_set_function(pwm_gpio[channel].pwm_pin, 0);
297 }
298 }
299
300 /****the function provide for pwm driverr******************************************/
hal_pwm_init(void)301 pwm_status_t hal_pwm_init(void)
302 {
303 PWM_INFO("pwm init start");
304
305 if (pwm_init)
306 {
307 pwm_init++;
308 return 0;
309 }
310
311 sunxi_pwm.pwm_clk_type = SUNXI_PWM_CLK_TYPE;
312 sunxi_pwm.pwm_bus_clk_id = SUNXI_PWM_CLK_ID;
313 sunxi_pwm.pwm_reset_type = SUNXI_PWM_RESET_TYPE;
314 sunxi_pwm.pwm_reset_id = SUNXI_PWM_RESET_ID;
315
316 if (!sunxi_pwm.pwm_reset)
317 {
318 sunxi_pwm.pwm_reset = hal_reset_control_get(sunxi_pwm.pwm_reset_type, sunxi_pwm.pwm_reset_id);
319 }
320
321 hal_reset_control_deassert(sunxi_pwm.pwm_reset);
322
323 if (!sunxi_pwm.pwm_bus_clk)
324 {
325 sunxi_pwm.pwm_bus_clk = hal_clock_get(sunxi_pwm.pwm_clk_type, sunxi_pwm.pwm_bus_clk_id);
326 PWM_INFO("pwm_bus_clk name:%s", sunxi_pwm.pwm_bus_clk->name);
327 }
328
329 if (hal_clock_enable(sunxi_pwm.pwm_bus_clk))
330 {
331 return -1;
332 }
333
334 #ifdef CONFIG_STANDBY
335 register_pm_dev_notify(hal_pwm_suspend, hal_pwm_resume, NULL);
336 #endif
337
338 PWM_INFO("pwm init end ");
339
340 pwm_init++;
341 return 0;
342 }
343
hal_pwm_deinit(void)344 pwm_status_t hal_pwm_deinit(void)
345 {
346 if (pwm_init)
347 {
348 pwm_init--;
349 if (!pwm_init)
350 {
351 hal_reset_control_assert(sunxi_pwm.pwm_reset);
352 hal_reset_control_put(sunxi_pwm.pwm_reset);
353
354 hal_clock_disable(sunxi_pwm.pwm_bus_clk);
355 hal_clock_put(sunxi_pwm.pwm_bus_clk);
356 }
357 }
358 PWM_INFO("pwm deinit end");
359 return 0;
360 }
361
hal_pwm_control(int channel,struct pwm_config * config_pwm)362 pwm_status_t hal_pwm_control(int channel, struct pwm_config *config_pwm)
363 {
364 PWM_INFO("pwm control start");
365
366 uint32_t ret;
367 unsigned int temp;
368 unsigned long long c = 0;
369 unsigned long entire_cycles = 256, active_cycles = 192;
370 unsigned int reg_offset, reg_shift, reg_width;
371 unsigned int reg_bypass_shift /*, group_reg_offset*/;
372 unsigned int reg_clk_src_shift, reg_clk_src_width;
373 unsigned int reg_div_m_shift, reg_div_m_width, value;
374
375 PWM_INFO("period_ns = %ld", config_pwm->period_ns);
376 PWM_INFO("duty_ns = %ld", config_pwm->duty_ns);
377 PWM_INFO("polarity = %d", config_pwm->polarity);
378 PWM_INFO("channel = %d", channel);
379
380 if ((config_pwm->period_ns < config_pwm->duty_ns) || (!config_pwm->period_ns))
381 {
382 PWM_ERR("paremeter error : period_ns can't greater than duty_ns and period_ns can't be 0");
383 return -1;
384 }
385
386 /* pwm set port */
387 ret = hal_pwm_pinctrl_init(sunxi_pwm, channel);
388 if (ret)
389 {
390 hal_gpio_pinmux_set_function(pwm_gpio[channel].pwm_pin, pwm_gpio[channel].pwm_function);
391 }
392
393 /* pwm enable controller */
394 hal_pwm_enable_controller(channel);
395
396 /* pwm set polarity */
397 hal_pwm_porality(channel, config_pwm->polarity);
398
399 /* pwm config function */
400 uint32_t pre_scal_id = 0, div_m = 0, prescale = 0;
401 uint32_t pre_scal[][2] =
402 {
403 /*reg_val clk_pre_div*/
404 {0, 1},
405 {1, 2},
406 {2, 4},
407 {3, 8},
408 {4, 16},
409 {5, 32},
410 {6, 64},
411 {7, 128},
412 {8, 256},
413 };
414
415 reg_clk_src_shift = PWM_CLK_SRC_SHIFT;
416 reg_clk_src_width = PWM_CLK_SRC_WIDTH;
417 reg_offset = get_pccr_reg_offset(channel);
418
419 if (config_pwm->period_ns > 0 && config_pwm->period_ns <= 10)
420 {
421 /* if freq lt 100M, then direct output 100M clock,set by pass. */
422 c = 100000000;
423 reg_bypass_shift = channel;
424 reg_offset = get_pccr_reg_offset(channel);
425
426 temp = hal_readl(PWM_BASE + PWM_PCGR);
427 temp = SET_BITS(reg_bypass_shift, 1, temp, 1); /* bypass set */
428 hal_writel(temp, PWM_BASE + PWM_PCGR);
429 /*clk_src_reg*/
430 temp = hal_readl(PWM_BASE + reg_offset);
431 temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);/*clock source*/
432 hal_writel(temp, PWM_BASE + reg_offset);
433
434 return 0;
435 }
436 else if (config_pwm->period_ns > 10 && config_pwm->period_ns <= 334)
437 {
438 /* if freq between 3M~100M, then select 100M as clock */
439 c = 100000000;
440
441 /*clk_src_reg : use APB1 clock */
442 temp = hal_readl(PWM_BASE + reg_offset);
443 temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 1);
444 hal_writel(temp, PWM_BASE + reg_offset);
445 }
446 else if (config_pwm->period_ns > 334)
447 {
448 /* if freq < 3M, then select 24M clock */
449 c = 24000000;
450
451 /*clk_src_reg : use OSC24M clock */
452 temp = hal_readl(PWM_BASE + reg_offset);
453 temp = SET_BITS(reg_clk_src_shift, reg_clk_src_width, temp, 0);
454 hal_writel(temp, PWM_BASE + reg_offset);
455 }
456
457 c = c * config_pwm->period_ns;
458 pwm_do_div(c, 1000000000);
459 entire_cycles = (unsigned long)c;
460
461 for (pre_scal_id = 0; pre_scal_id < 9; pre_scal_id++)
462 {
463 if (entire_cycles <= 65536)
464 {
465 break;
466 }
467 for (prescale = 0; prescale < PRESCALE_MAX + 1; prescale++)
468 {
469 entire_cycles = ((unsigned long)c / pre_scal[pre_scal_id][1]) / (prescale + 1);
470 if (entire_cycles <= 65536)
471 {
472 div_m = pre_scal[pre_scal_id][0];
473 break;
474 }
475 }
476 }
477
478 c = (unsigned long long)entire_cycles * config_pwm->duty_ns;
479 pwm_do_div(c, config_pwm->period_ns);
480 active_cycles = c;
481 if (entire_cycles == 0)
482 {
483 entire_cycles++;
484 }
485
486 /* config clk div_m */
487 reg_div_m_shift = PWM_DIV_M_SHIFT;
488 reg_div_m_width = PWM_DIV_M_WIDTH;
489 temp = hal_readl(PWM_BASE + reg_offset);
490 temp = SET_BITS(reg_div_m_shift, reg_div_m_width, temp, div_m);
491 hal_writel(temp, PWM_BASE + reg_offset);
492
493 /* config gating */
494 reg_shift = channel;
495 value = hal_readl(PWM_BASE + PWM_PCGR);
496 value = SET_BITS(reg_shift, 1, value, 1);/* set gating */
497 hal_writel(value, PWM_BASE + PWM_PCGR);
498
499 /* config prescal */
500 reg_offset = PWM_PCR + 0x20 * channel;
501 reg_shift = PWM_PRESCAL_SHIFT;
502 reg_width = PWM_PRESCAL_WIDTH;
503 temp = hal_readl(PWM_BASE + reg_offset);
504 temp = SET_BITS(reg_shift, reg_width, temp, prescale);
505 hal_writel(temp, PWM_BASE + reg_offset);
506
507 /* config active cycles */
508 reg_offset = PWM_PPR + 0x20 * channel;
509 reg_shift = PWM_ACT_CYCLES_SHIFT;
510 reg_width = PWM_ACT_CYCLES_WIDTH;
511 temp = hal_readl(PWM_BASE + reg_offset);
512 temp = SET_BITS(reg_shift, reg_width, temp, active_cycles);
513 hal_writel(temp, PWM_BASE + reg_offset);
514
515 /* config period cycles */
516 reg_offset = PWM_PPR + 0x20 * channel;
517 reg_shift = PWM_PERIOD_CYCLES_SHIFT;
518 reg_width = PWM_PERIOD_CYCLES_WIDTH;
519 temp = hal_readl(PWM_BASE + reg_offset);
520 temp = SET_BITS(reg_shift, reg_width, temp, (entire_cycles - 1));
521 hal_writel(temp, PWM_BASE + reg_offset);
522
523 PWM_INFO("pwm control end ");
524
525 return 0;
526 }
527
hal_pwm_resume(void)528 pwm_status_t hal_pwm_resume(void)
529 {
530 if (hal_reset_control_assert(sunxi_pwm.pwm_reset))
531 {
532 return -1;
533 }
534
535 if (hal_reset_control_deassert(sunxi_pwm.pwm_reset))
536 {
537 return -1;
538 }
539
540 if (hal_clock_enable(sunxi_pwm.pwm_bus_clk))
541 {
542 return -1;
543 }
544
545 PWM_INFO("hal pwm resume");
546 return 0;
547 }
548
hal_pwm_suspend(void)549 pwm_status_t hal_pwm_suspend(void)
550 {
551 if (hal_reset_control_assert(sunxi_pwm.pwm_reset))
552 {
553 return -1;
554 }
555
556 if (hal_clock_disable(sunxi_pwm.pwm_bus_clk))
557 {
558 return -1;
559 }
560
561 PWM_INFO("hal pwm suspend");
562 return 0;
563 }
564