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