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