1 /*
2  * Copyright (C) 2015-2020 Alibaba Group Holding Limited
3  */
4 
5 #include <drv/pwm.h>
6 #include <aos/pwm_core.h>
7 #include "device.h"
8 #include "objects.h"
9 #include "pinmap.h"
10 
11 #define BIT_PWM_TIM_IDX_FLAG BIT(7)
12 #define BIT_PWM_TIM_IDX_SHIFT 7
13 #define PWM_TIMER 5
14 
15 #define DIV_ROUND_CLOSEST(x, divisor)(          \
16 {                                               \
17     typeof(x) __x = x;                          \
18     typeof(divisor) __d = divisor;              \
19     (((typeof(x))-1) > 0 ||                     \
20      ((typeof(divisor))-1) > 0 ||               \
21      (((__x) > 0) == ((__d) > 0))) ?            \
22         (((__x) + ((__d) / 2)) / (__d)) :       \
23         (((__x) - ((__d) / 2)) / (__d));        \
24 }                                               \
25 )
26 
27 
28 typedef struct {
29    aos_pwm_t aos_pwm;
30    uint8_t idx;
31    void           *priv;
32 } rtl872xd_pwm_t;
33 /* Table elements express the pin to PWM channel number, they are:
34  * {pinName, km0_pin2chan, km4_pin2chan}
35  */
36 static const u32 pin2chan[18][2] = {
37     { PA_12, 0 },
38     { PA_13, 1 },
39     { PA_23, 2 },
40     { PA_24, 3 },
41     { PA_25, 4 },
42     { PA_26, 5 },
43     { PA_28, 6 },
44     { PA_30, 7 },
45     { PB_4, 8 },
46     { PB_5, 9 },
47     { PB_18, 10 },
48     { PB_19, 11 },
49     { PB_20, 12 },
50     { PB_21, 13 },
51     { PB_22, 14 },
52     { PB_23, 15 },
53     { PB_24, 16 },
54     { PB_7, 17 } // this channel also can be PB_7
55 };
56 
57 static RTIM_TypeDef *RTL872xD_PWM_TIM[2] = { TIM5, TIMM05 };
58 
59 static u8 km4_ch_start[18] = { 0 };
60 
rtl872xd_pwm_uninit(rtl872xd_pwm_t * pwm_dev)61 static void rtl872xd_pwm_uninit(rtl872xd_pwm_t *pwm_dev)
62 {
63     uint32_t pwm_chan = pwm_dev->idx & (~BIT_PWM_TIM_IDX_FLAG);
64     uint8_t pwm_tim_idx = pwm_dev->idx >> BIT_PWM_TIM_IDX_SHIFT;
65 
66     if (km4_ch_start[pwm_chan]) {
67         km4_ch_start[pwm_chan] = 0;
68         RTIM_CCxCmd(RTL872xD_PWM_TIM[pwm_tim_idx], pwm_chan, TIM_CCx_Disable);
69 
70         /* stop timer5 if no pwm channels starts */
71         for (pwm_chan = 0; pwm_chan < 18; pwm_chan++) {
72             if (km4_ch_start[pwm_chan]) {
73                 return;
74             }
75         }
76         RTIM_Cmd(RTL872xD_PWM_TIM[pwm_tim_idx], DISABLE);
77     }
78 
79     if (pwm_dev && pwm_dev->priv) {
80         free(pwm_dev->priv);
81         pwm_dev->priv = NULL;
82     }
83 }
84 
rtl872xd_pwm_out_config(rtl872xd_pwm_t * pwm_dev,uint32_t channel,uint32_t period_ns,uint32_t pulse_width_ns,csi_pwm_polarity_t polarity)85 static int rtl872xd_pwm_out_config(rtl872xd_pwm_t *pwm_dev, uint32_t channel, uint32_t period_ns,
86                                    uint32_t pulse_width_ns, csi_pwm_polarity_t polarity)
87 {
88     uint32_t arr, tmp, ccrx;
89     uint32_t period;
90     float pulse, value, dc;
91     uint8_t pwm_tim_idx = pwm_dev->idx >> BIT_PWM_TIM_IDX_SHIFT;
92     TIM_CCInitTypeDef *TIM_CCInitStruct = (TIM_CCInitTypeDef *)pwm_dev->priv;
93     u32 prescaler = 0;
94 
95     if (channel > 18) {
96         return 0;
97     }
98     RTIM_CCStructInit(TIM_CCInitStruct);
99     RTIM_CCxInit(RTL872xD_PWM_TIM[pwm_tim_idx], TIM_CCInitStruct, channel);
100     RTIM_CCxCmd(RTL872xD_PWM_TIM[pwm_tim_idx], channel, TIM_CCx_Enable);
101     PinName pin = pin2chan[channel][0];
102     if (km4_ch_start[channel] == 0) {
103         if (pwm_tim_idx) {
104             Pinmux_Config(pin, PINMUX_FUNCTION_PWM_LP);
105         } else {
106             Pinmux_Config(pin, PINMUX_FUNCTION_PWM_HS);
107         }
108     }
109     km4_ch_start[channel] = 1;
110 
111     tmp = DIV_ROUND_CLOSEST(((uint64_t)period_ns * 40), 1000);
112     tmp = tmp / (prescaler + 1);
113     /*
114      *  psr is 8bits
115     */
116     if (tmp > 0x10000) {
117         prescaler = DIV_ROUND_CLOSEST(((uint64_t)period_ns * 40), 1000);
118         prescaler = DIV_ROUND_CLOSEST(prescaler, 0x10000);
119         if (prescaler > 0xff) {
120             prescaler = 0xff;
121         }
122         RTIM_PrescalerConfig(RTL872xD_PWM_TIM[pwm_tim_idx], prescaler, TIM_PSCReloadMode_Update);
123     }
124     /*
125      *     arr is 16bits
126     */
127 
128     /*
129      * 40M oscilator
130     */
131     arr = DIV_ROUND_CLOSEST(((uint64_t)period_ns * 40), 1000);
132     arr = DIV_ROUND_CLOSEST(arr, (prescaler + 1)) - 1;
133     if (arr > 0xffff) {
134         arr = 0xffff;
135     }
136     RTIM_ChangePeriod(RTL872xD_PWM_TIM[pwm_tim_idx], arr);
137     ccrx = DIV_ROUND_CLOSEST(((uint64_t)(period_ns - pulse_width_ns) * 40), 1000);
138     ccrx = (ccrx / (prescaler + 1)) & 0x0000ffff;
139     RTIM_CCRxSet(RTL872xD_PWM_TIM[pwm_tim_idx], ccrx, channel);
140 
141     if (0 == polarity)
142         RTIM_CCxPolarityConfig(RTL872xD_PWM_TIM[pwm_tim_idx], TIM_CCPolarity_Low, channel);
143     else
144         RTIM_CCxPolarityConfig(RTL872xD_PWM_TIM[pwm_tim_idx], TIM_CCPolarity_High, channel);
145     return 0;
146 }
147 
rtl872xd_pwm_out_start(rtl872xd_pwm_t * pwm_dev,uint32_t channel)148 static int rtl872xd_pwm_out_start(rtl872xd_pwm_t *pwm_dev, uint32_t channel)
149 {
150     uint32_t pwm_chan = channel;
151     uint8_t pwm_tim_idx = pwm_dev->idx >> BIT_PWM_TIM_IDX_SHIFT;
152 
153     RTIM_CCxCmd(RTL872xD_PWM_TIM[pwm_tim_idx], pwm_chan, TIM_CCx_Enable);
154     return 0;
155 }
156 
rtl872xd_pwm_out_stop(rtl872xd_pwm_t * pwm_dev,uint32_t channel)157 static int rtl872xd_pwm_out_stop(rtl872xd_pwm_t *pwm_dev, uint32_t channel)
158 {
159     uint32_t pwm_chan = channel;
160     uint8_t pwm_tim_idx = pwm_dev->idx >> BIT_PWM_TIM_IDX_SHIFT;
161 
162     RTIM_CCxCmd(RTL872xD_PWM_TIM[pwm_tim_idx], pwm_chan, TIM_CCx_Disable);
163     return 0;
164 }
165 
rtl872xd_pwm_unregister(aos_pwm_t * pwm)166 static void rtl872xd_pwm_unregister(aos_pwm_t *pwm)
167 {
168     rtl872xd_pwm_t *pwm_dev = aos_container_of(pwm, rtl872xd_pwm_t, aos_pwm);
169 
170     rtl872xd_pwm_uninit(pwm_dev);
171 }
172 
rtl872xd_pwm_startup(aos_pwm_t * pwm)173 static int rtl872xd_pwm_startup(aos_pwm_t *pwm)
174 {
175     return 0;
176 }
177 
rtl872xd_pwm_shutdown(aos_pwm_t * pwm)178 static void rtl872xd_pwm_shutdown(aos_pwm_t *pwm)
179 {
180     rtl872xd_pwm_t *pwm_dev = aos_container_of(pwm, rtl872xd_pwm_t, aos_pwm);
181     rtl872xd_pwm_out_stop(pwm_dev, pwm_dev->aos_pwm.dev.id);
182 }
183 
rtl872xd_pwm_apply(aos_pwm_t * pwm,aos_pwm_attr_t const * attr)184 static int rtl872xd_pwm_apply(aos_pwm_t *pwm, aos_pwm_attr_t const *attr)
185 {
186     uint32_t period;
187     uint32_t duty_cycle;
188 
189     rtl872xd_pwm_t *pwm_dev = aos_container_of(pwm, rtl872xd_pwm_t, aos_pwm);
190     period = attr->period;
191     duty_cycle = attr->duty_cycle;
192     rtl872xd_pwm_out_config(pwm_dev, pwm_dev->aos_pwm.dev.id,
193                             period, duty_cycle, attr->polarity);
194     if (attr->enabled)
195         rtl872xd_pwm_out_start(pwm_dev, pwm_dev->aos_pwm.dev.id);
196     else
197         rtl872xd_pwm_out_stop(pwm_dev, pwm_dev->aos_pwm.dev.id);
198     return 0;
199 }
200 
201 static const aos_pwm_ops_t rtl872xd_pwm_ops = {
202     .unregister = rtl872xd_pwm_unregister,
203     .apply      = rtl872xd_pwm_apply,
204     .shutdown   = rtl872xd_pwm_shutdown,
205     .startup    = rtl872xd_pwm_startup
206 };
207 
rtl872xd_pwm_init(void)208 static int rtl872xd_pwm_init(void)
209 {
210     int ret;
211     static rtl872xd_pwm_t pwm_dev[CONFIG_PWM_NUM];
212     int idx = 0, i;
213     RTIM_TimeBaseInitTypeDef TIM_InitStruct;
214     RTIM_TimeBaseStructInit(&TIM_InitStruct);
215     TIM_InitStruct.TIM_Idx = PWM_TIMER;
216     RTIM_TimeBaseInit(RTL872xD_PWM_TIM[idx], &TIM_InitStruct, TIMER5_IRQ, NULL, (u32)&TIM_InitStruct);
217     RTIM_Cmd(RTL872xD_PWM_TIM[idx], ENABLE);
218     for (i = 0; i < CONFIG_PWM_NUM; i++) {
219         pwm_dev[i].priv = (TIM_CCInitTypeDef *)malloc(sizeof(TIM_CCInitTypeDef));
220         pwm_dev[i].idx = idx << BIT_PWM_TIM_IDX_SHIFT;
221         pwm_dev[i].idx |= (i) & (~BIT_PWM_TIM_IDX_FLAG);
222         if (ret != 0) {
223             return ret;
224         }
225         pwm_dev[i].aos_pwm.dev.id = i;
226         pwm_dev[i].aos_pwm.ops = &rtl872xd_pwm_ops;
227         ret = aos_pwm_register(&(pwm_dev[i].aos_pwm));
228         if  (ret != 0) {
229             return ret;
230         }
231     }
232     return 0;
233 }
234 
235 LEVEL1_DRIVER_ENTRY(rtl872xd_pwm_init)
236