1 /*
2  * Copyright (c) 2020-2021, Bluetrum Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2021-01-28     greedyhao         first version
9  */
10 
11 #include <board.h>
12 
13 #ifdef RT_USING_PWM
14 #include "pwm_config.h"
15 
16 //#define DRV_DEBUG
17 #define LOG_TAG             "drv.pwm"
18 #include <drv_log.h>
19 
20 #define MAX_PERIOD 65535
21 #define MIN_PERIOD 3
22 #define MIN_PULSE 2
23 
24 void hal_pwm_mspinit(void);
25 
26 enum
27 {
28 #ifdef BSP_USING_T3_PWM
29     T3_PWM_INDEX,
30 #endif
31 #ifdef BSP_USING_T4_PWM
32     T4_PWM_INDEX,
33 #endif
34 #ifdef BSP_USING_T5_PWM
35     T5_PWM_INDEX,
36 #endif
37 #ifdef BSP_USING_LPWM0
38     LPWM0_INDEX,
39 #endif
40 #ifdef BSP_USING_LPWM1
41     LPWM1_INDEX,
42 #endif
43 #ifdef BSP_USING_LPWM2
44     LPWM2_INDEX,
45 #endif
46 #ifdef BSP_USING_LPWM3
47     LPWM3_INDEX,
48 #endif
49 };
50 
51 struct ab32_pwm
52 {
53     struct rt_device_pwm pwm_device;
54     hal_sfr_t   pwm_handle;
55     char *name;
56     rt_uint8_t  channel;
57     rt_uint32_t period;
58     rt_uint32_t pulse;
59 };
60 
61 static struct ab32_pwm ab32_pwm_obj[] =
62 {
63 #ifdef BSP_USING_T3_PWM
64     T3_PWM_CONFIG,
65 #endif
66 #ifdef BSP_USING_T4_PWM
67     T4_PWM_CONFIG,
68 #endif
69 #ifdef BSP_USING_T5_PWM
70     T5_PWM_CONFIG,
71 #endif
72 #ifdef BSP_USING_LPWM0
73     LPWM0_CONFIG,
74 #endif
75 #ifdef BSP_USING_LPWM1
76     LPWM1_CONFIG,
77 #endif
78 #ifdef BSP_USING_LPWM2
79     LPWM2_CONFIG,
80 #endif
81 #ifdef BSP_USING_LPWM3
82     LPWM3_CONFIG,
83 #endif
84 };
85 
drv_pwm_enable(hal_sfr_t pwm,char * name,struct rt_pwm_configuration * configuration,rt_bool_t enable)86 static rt_err_t drv_pwm_enable(hal_sfr_t pwm, char *name, struct rt_pwm_configuration *configuration, rt_bool_t enable)
87 {
88     rt_uint8_t channel = configuration->channel;
89     rt_uint8_t pwm_num = 0;
90 
91     if (!configuration->complementary) {
92         if (name[0] == 'l') {
93             pwm[PWMxCON] &= ~BIT(5);
94         }
95     } else {
96         if (name[0] == 'l') {
97             pwm[PWMxCON] |= BIT(5);
98         } else {
99             LOG_W("Timer no support complementary PWM output!");
100         }
101     }
102 
103     if (!enable) {
104         if (name[0] == 'l') {
105             pwm_num = name[4] - '0';
106             pwm[PWMxCON] &= ~(1 << (pwm_num));
107         } else {
108             if (channel & 0x1) { /* pwm0 */
109                 pwm[TMRxCON] &= ~(1 << (9 + 0));
110             }
111             if (channel & 0x2) { /* pwm1 */
112                 pwm[TMRxCON] &= ~(1 << (9 + 1));
113             }
114             if (channel & 0x4) { /* pwm2 */
115                 pwm[TMRxCON] &= ~(1 << (9 + 2));
116             }
117         }
118     } else {
119         if (name[0] == 'l') {
120             pwm_num = name[4] - '0';
121             pwm[PWMxCON] |= 1 << (pwm_num);
122         } else {
123             if (channel & 0x1) { /* pwm0 */
124                 pwm[TMRxCON] |= (1 << (9 + 0));
125             }
126             if (channel & 0x2) { /* pwm1 */
127                 pwm[TMRxCON] |= (1 << (9 + 1));
128             }
129             if (channel & 0x4) { /* pwm2 */
130                 pwm[TMRxCON] |= (1 << (9 + 2));
131             }
132         }
133     }
134 
135     return RT_EOK;
136 }
137 
drv_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)138 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
139 {
140     struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
141     struct ab32_pwm *pwm_obj = (struct ab32_pwm *)device->parent.user_data;
142     hal_sfr_t pwm = pwm_obj->pwm_handle;
143     char *name = pwm_obj->name;
144     rt_uint8_t channel = configuration->channel;
145     rt_uint32_t period, pulse;
146     rt_uint64_t tim_clock, psc;
147 
148     if (name[0] == 'l') {
149         tim_clock = 6500; /* lpwm clock is 6.5MHz */
150     } else {
151         tim_clock = get_sysclk_nhz() / 1000ul;
152     }
153 
154     switch (cmd)
155     {
156     case PWMN_CMD_ENABLE:
157         configuration->complementary = RT_TRUE;
158     case PWM_CMD_ENABLE:
159         return drv_pwm_enable(pwm, name, configuration, RT_TRUE);
160     case PWMN_CMD_DISABLE:
161         configuration->complementary = RT_FALSE;
162     case PWM_CMD_DISABLE:
163         return drv_pwm_enable(pwm, name, configuration, RT_FALSE);
164     case PWM_CMD_SET:
165         pwm_obj->pulse = configuration->pulse;
166         pwm_obj->period = configuration->period;
167 
168         period = pwm_obj->period * tim_clock / 1000000ul;
169         psc = period / MAX_PERIOD + 1;
170         period = period / psc;
171 
172         if (period < MIN_PERIOD)
173         {
174             period = MIN_PERIOD;
175         }
176 
177         pulse = pwm_obj->pulse * tim_clock / psc / 1000000ul;
178         if (pulse < MIN_PULSE)
179         {
180             pulse = MIN_PULSE;
181         }
182         else if (pulse > period)
183         {
184             pulse = period;
185         }
186 
187         if (name[0] == 'l') {
188             pwm[PWMxPR] = period - 1;
189             switch (name[4] - '0')
190             {
191             case 0: /* lpwm0 */
192                 pwm[PWMxxDUT] = pulse - 1;
193                 break;
194             case 1: /* lpwm1 */
195                 pwm[PWMxxDUT] = (pulse - 1) << 16;
196                 break;
197             case 2: /* lpwm2 */
198                 pwm[PWMyyDUT] = pulse - 1;
199                 break;
200             case 3: /* lpwm3 */
201                 pwm[PWMyyDUT] = (pulse - 1) << 16;
202                 break;
203             default:
204                 break;
205             }
206         } else {
207             pwm[TMRxPR] = period - 1;
208             if (channel & 0x1) { /* pwm0 */
209                 pwm[TMRxDUTY0] = pulse - 1;
210             }
211             if (channel & 0x2) { /* pwm1 */
212                 pwm[TMRxDUTY1] = pulse - 1;
213             }
214             if (channel & 0x4) { /* pwm2 */
215                 pwm[TMRxDUTY2] = pulse - 1;
216             }
217         }
218         return RT_EOK;
219     case PWM_CMD_GET:
220         configuration->pulse = pwm_obj->pulse;
221         configuration->period = pwm_obj->period;
222         return RT_EOK;
223     default:
224         return -RT_EINVAL;
225     }
226 }
227 
ab32_hw_pwm_init(struct ab32_pwm * device)228 static rt_err_t ab32_hw_pwm_init(struct ab32_pwm *device)
229 {
230     rt_err_t result = RT_EOK;
231     hal_sfr_t pwm = RT_NULL;
232     char *name = RT_NULL;
233 
234     RT_ASSERT(device != RT_NULL);
235 
236     pwm = (hal_sfr_t)device->pwm_handle;
237     name = device->name;
238 
239     if (name[0] == 'l') {
240         pwm[PWMxCON] = 0;
241     } else {
242         pwm[TMRxCON] &= ~(7 << 9);
243     }
244 
245     return result;
246 }
247 
pwm_get_channel(void)248 static void pwm_get_channel(void)
249 {
250 #ifdef BSP_USING_T3_PWM0
251     ab32_pwm_obj[T3_PWM_INDEX].channel |= 1 << 0;
252 #endif
253 #ifdef BSP_USING_T3_PWM1
254     ab32_pwm_obj[T3_PWM_INDEX].channel |= 1 << 1;
255 #endif
256 #ifdef BSP_USING_T3_PWM2
257     ab32_pwm_obj[T3_PWM_INDEX].channel |= 1 << 2;
258 #endif
259 #ifdef BSP_USING_T4_PWM0
260     ab32_pwm_obj[T4_PWM_INDEX].channel |= 1 << 0;
261 #endif
262 #ifdef BSP_USING_T4_PWM1
263     ab32_pwm_obj[T4_PWM_INDEX].channel |= 1 << 1;
264 #endif
265 #ifdef BSP_USING_T4_PWM2
266     ab32_pwm_obj[T4_PWM_INDEX].channel |= 1 << 2;
267 #endif
268 #ifdef BSP_USING_T5_PWM0
269     ab32_pwm_obj[T5_PWM_INDEX].channel |= 1 << 0;
270 #endif
271 #ifdef BSP_USING_T5_PWM1
272     ab32_pwm_obj[T5_PWM_INDEX].channel |= 1 << 1;
273 #endif
274 #ifdef BSP_USING_T5_PWM2
275     ab32_pwm_obj[T5_PWM_INDEX].channel |= 1 << 2;
276 #endif
277 #ifdef BSP_USING_LPWM0
278     ab32_pwm_obj[LPWM0_INDEX].channel |= 1 << 0;
279 #endif
280 #ifdef BSP_USING_LPWM1
281     ab32_pwm_obj[LPWM1_INDEX].channel |= 1 << 0;
282 #endif
283 #ifdef BSP_USING_LPWM2
284     ab32_pwm_obj[LPWM2_INDEX].channel |= 1 << 0;
285 #endif
286 #ifdef BSP_USING_LPWM3
287     ab32_pwm_obj[LPWM3_INDEX].channel |= 1 << 0;
288 #endif
289 }
290 
291 static struct rt_pwm_ops drv_ops =
292 {
293     drv_pwm_control
294 };
295 
ab32_pwm_init(void)296 static int ab32_pwm_init(void)
297 {
298     int i = 0;
299     int result = RT_EOK;
300 
301     pwm_get_channel();
302     hal_pwm_mspinit();
303 
304     for (i = 0; i < sizeof(ab32_pwm_obj) / sizeof(ab32_pwm_obj[0]); i++)
305     {
306         /* pwm init */
307         if (ab32_hw_pwm_init(&ab32_pwm_obj[i]) != RT_EOK)
308         {
309             LOG_E("%s init failed", ab32_pwm_obj[i].name);
310             result = -RT_ERROR;
311             goto __exit;
312         }
313         else
314         {
315             LOG_D("%s init success", ab32_pwm_obj[i].name);
316 
317             /* register pwm device */
318             if (rt_device_pwm_register(&ab32_pwm_obj[i].pwm_device, ab32_pwm_obj[i].name, &drv_ops, (void *)&ab32_pwm_obj[i]) == RT_EOK)
319             {
320                 LOG_D("%s register success", ab32_pwm_obj[i].name);
321             }
322             else
323             {
324                 LOG_E("%s register failed", ab32_pwm_obj[i].name);
325                 result = -RT_ERROR;
326             }
327         }
328     }
329 
330 __exit:
331     return result;
332 }
333 INIT_DEVICE_EXPORT(ab32_pwm_init);
334 #endif /* RT_USING_PWM */
335