1 /*
2 * Copyright (c) 2006-2024, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2024/02/19 flyingcys first version
9 */
10 #include <rtthread.h>
11 #include <rtdevice.h>
12 #include "drv_pwm.h"
13 #include "drv_pinmux.h"
14 #include "drv_ioremap.h"
15
16 #define DBG_LEVEL DBG_LOG
17 #include <rtdbg.h>
18 #define LOG_TAG "DRV.PWM"
19
20 struct cvi_pwm_dev
21 {
22 struct rt_device_pwm device;
23 const char *name;
24 rt_ubase_t reg_base;
25 };
26
27 static const uint64_t count_unit = 100000000; // 100M count per second
28 static const uint64_t NSEC_COUNT = 1000000000; // ns
29
cvi_pwm_set_config(rt_ubase_t reg_base,struct rt_pwm_configuration * cfg)30 static void cvi_pwm_set_config(rt_ubase_t reg_base, struct rt_pwm_configuration *cfg)
31 {
32 unsigned long long duty_clk, period_clk;
33
34 cvi_pwm_set_polarity_high_ch(reg_base, (cfg->channel & PWM_MAX_CH));
35
36 duty_clk = (cfg->pulse * count_unit) / NSEC_COUNT;
37 cvi_pwm_set_high_period_ch(reg_base, (cfg->channel & PWM_MAX_CH), duty_clk);
38
39 period_clk = (cfg->period * count_unit) / NSEC_COUNT;
40 cvi_pwm_set_period_ch(reg_base, (cfg->channel & PWM_MAX_CH), period_clk);
41
42 cvi_pwm_output_en_ch(reg_base, cfg->channel & PWM_MAX_CH);
43 }
44
cvi_pwm_get_config(rt_ubase_t reg_base,struct rt_pwm_configuration * cfg)45 static void cvi_pwm_get_config(rt_ubase_t reg_base, struct rt_pwm_configuration *cfg)
46 {
47 unsigned long long duty_clk, period_clk;
48
49 duty_clk = cvi_pwm_get_high_period_ch(reg_base, (cfg->channel & PWM_MAX_CH));
50 cfg->pulse = duty_clk * NSEC_COUNT / count_unit;
51
52 period_clk = cvi_pwm_get_period_ch(reg_base, (cfg->channel & PWM_MAX_CH));
53 cfg->period = period_clk * NSEC_COUNT / count_unit;
54 }
55
_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)56 static rt_err_t _pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
57 {
58 struct rt_pwm_configuration *cfg = (struct rt_pwm_configuration *)arg;
59 struct cvi_pwm_dev *pwm_dev = (struct cvi_pwm_dev *)device->parent.user_data;
60 unsigned long long duty_clk, period_clk;
61 const uint64_t count_unit = 100000000; // 100M count per second
62 const uint64_t NSEC_COUNT = 1000000000; // ns
63
64 if (cfg->channel >= PWM_CHANNEL_NUM)
65 return -RT_EINVAL;
66
67 switch (cmd)
68 {
69 case PWM_CMD_ENABLE:
70 cvi_pwm_start_en_ch(pwm_dev->reg_base, cfg->channel & PWM_MAX_CH);
71 break;
72
73 case PWM_CMD_DISABLE:
74 cvi_pwm_start_dis_ch(pwm_dev->reg_base, cfg->channel & PWM_MAX_CH);
75 break;
76
77 case PWM_CMD_SET:
78 cvi_pwm_set_config(pwm_dev->reg_base, cfg);
79 break;
80
81 case PWM_CMD_GET:
82 cvi_pwm_get_config(pwm_dev->reg_base, cfg);
83 break;
84
85 case PWM_CMD_SET_PERIOD:
86 period_clk = (cfg->period * count_unit) / NSEC_COUNT;
87 cvi_pwm_set_period_ch(pwm_dev->reg_base, (cfg->channel & PWM_MAX_CH), period_clk);
88 break;
89
90 case PWM_CMD_SET_PULSE:
91 duty_clk = (cfg->pulse * count_unit) / NSEC_COUNT;
92 cvi_pwm_set_high_period_ch(pwm_dev->reg_base, (cfg->channel & PWM_MAX_CH), duty_clk);
93 break;
94
95 default:
96 LOG_D("cmd: %x channel: %d period: %d pulse: %d", cmd, cfg->channel, cfg->period, cfg->pulse);
97 break;
98 }
99
100 return RT_EOK;
101 }
102
103 const static struct rt_pwm_ops cvi_pwm_ops =
104 {
105 .control = &_pwm_control
106 };
107
108 static struct cvi_pwm_dev cvi_pwm[] =
109 {
110 #ifdef BSP_USING_PWM0
111 {
112 .name = "pwm0",
113 .reg_base = CVI_PWM0_BASE,
114 },
115 #endif
116
117 #ifdef BSP_USING_PWM1
118 {
119 .name = "pwm1",
120 .reg_base = CVI_PWM1_BASE,
121 },
122 #endif
123
124 #ifdef BSP_USING_PWM2
125 {
126 .name = "pwm2",
127 .reg_base = CVI_PWM2_BASE,
128 },
129 #endif
130
131 #ifdef BSP_USING_PWM3
132 {
133 .name = "pwm3",
134 .reg_base = CVI_PWM3_BASE,
135 },
136 #endif
137 };
138
139
140 #if defined(BOARD_TYPE_MILKV_DUO)
141
142 #ifdef BSP_USING_PWM0
143 static const char *pinname_whitelist_pwm0[] = {
144 NULL,
145 };
146 static const char *pinname_whitelist_pwm1[] = {
147 NULL,
148 };
149 static const char *pinname_whitelist_pwm2[] = {
150 NULL,
151 };
152 static const char *pinname_whitelist_pwm3[] = {
153 NULL,
154 };
155 #endif
156
157 #ifdef BSP_USING_PWM1
158 static const char *pinname_whitelist_pwm4[] = {
159 "SD1_D3",
160 "UART0_TX",
161 NULL,
162 };
163 static const char *pinname_whitelist_pwm5[] = {
164 "SD1_D2",
165 "UART0_RX",
166 NULL,
167 };
168 static const char *pinname_whitelist_pwm6[] = {
169 "SD1_D1",
170 NULL,
171 };
172 static const char *pinname_whitelist_pwm7[] = {
173 "SD1_D0",
174 NULL,
175 };
176 #endif
177
178 #ifdef BSP_USING_PWM2
179 static const char *pinname_whitelist_pwm8[] = {
180 "SD1_CMD",
181 NULL,
182 };
183 static const char *pinname_whitelist_pwm9[] = {
184 "SD1_CLK",
185 NULL,
186 };
187 static const char *pinname_whitelist_pwm10[] = {
188 "SD1_GPIO1",
189 NULL,
190 };
191 static const char *pinname_whitelist_pwm11[] = {
192 "SD1_GPIO0",
193 NULL,
194 };
195 #endif
196
197 #ifdef BSP_USING_PWM3
198 static const char *pinname_whitelist_pwm12[] = {
199 NULL,
200 };
201 static const char *pinname_whitelist_pwm13[] = {
202 NULL,
203 };
204 static const char *pinname_whitelist_pwm14[] = {
205 NULL,
206 };
207 static const char *pinname_whitelist_pwm15[] = {
208 NULL,
209 };
210 #endif
211
212 #elif defined(BOARD_TYPE_MILKV_DUO256M)
213
214 #ifdef BSP_USING_PWM0
215 static const char *pinname_whitelist_pwm0[] = {
216 NULL,
217 };
218 static const char *pinname_whitelist_pwm1[] = {
219 NULL,
220 };
221 static const char *pinname_whitelist_pwm2[] = {
222 NULL,
223 };
224 static const char *pinname_whitelist_pwm3[] = {
225 NULL,
226 };
227 #endif
228
229 #ifdef BSP_USING_PWM1
230 static const char *pinname_whitelist_pwm4[] = {
231 "SD1_D3",
232 "UART0_TX",
233 NULL,
234 };
235 static const char *pinname_whitelist_pwm5[] = {
236 "SD1_D2",
237 "UART0_RX",
238 NULL,
239 };
240 static const char *pinname_whitelist_pwm6[] = {
241 "JTAG_CPU_TCK",
242 "SD1_D1",
243 NULL,
244 };
245 static const char *pinname_whitelist_pwm7[] = {
246 "JTAG_CPU_TMS",
247 "SD1_D0",
248 NULL,
249 };
250 #endif
251
252 #ifdef BSP_USING_PWM2
253 static const char *pinname_whitelist_pwm8[] = {
254 "SD1_CMD",
255 NULL,
256 };
257 static const char *pinname_whitelist_pwm9[] = {
258 "SD1_CLK",
259 NULL,
260 };
261 static const char *pinname_whitelist_pwm10[] = {
262 "PAD_MIPI_TXM1",
263 NULL,
264 };
265 static const char *pinname_whitelist_pwm11[] = {
266 "PAD_MIPI_TXP1",
267 NULL,
268 };
269 #endif
270
271 #ifdef BSP_USING_PWM3
272 static const char *pinname_whitelist_pwm12[] = {
273 NULL,
274 };
275 static const char *pinname_whitelist_pwm13[] = {
276 NULL,
277 };
278 static const char *pinname_whitelist_pwm14[] = {
279 NULL,
280 };
281 static const char *pinname_whitelist_pwm15[] = {
282 NULL,
283 };
284 #endif
285
286 #else
287 #error "Unsupported board type!"
288 #endif
289
rt_hw_pwm_pinmux_config()290 static void rt_hw_pwm_pinmux_config()
291 {
292 #ifdef BSP_USING_PWM0
293 pinmux_config(BSP_PWM0_0_PINNAME, PWM_0, pinname_whitelist_pwm0);
294 pinmux_config(BSP_PWM0_1_PINNAME, PWM_1, pinname_whitelist_pwm1);
295 pinmux_config(BSP_PWM0_2_PINNAME, PWM_2, pinname_whitelist_pwm2);
296 pinmux_config(BSP_PWM0_3_PINNAME, PWM_3, pinname_whitelist_pwm3);
297 #endif /* BSP_USING_PWM0 */
298
299 #ifdef BSP_USING_PWM1
300 pinmux_config(BSP_PWM1_4_PINNAME, PWM_4, pinname_whitelist_pwm4);
301 pinmux_config(BSP_PWM1_5_PINNAME, PWM_5, pinname_whitelist_pwm5);
302 pinmux_config(BSP_PWM1_6_PINNAME, PWM_6, pinname_whitelist_pwm6);
303 pinmux_config(BSP_PWM1_7_PINNAME, PWM_7, pinname_whitelist_pwm7);
304 #endif /* BSP_USING_PWM1 */
305
306 #ifdef BSP_USING_PWM2
307 pinmux_config(BSP_PWM2_8_PINNAME, PWM_8, pinname_whitelist_pwm8);
308 pinmux_config(BSP_PWM2_9_PINNAME, PWM_9, pinname_whitelist_pwm9);
309 pinmux_config(BSP_PWM2_10_PINNAME, PWM_10, pinname_whitelist_pwm10);
310 pinmux_config(BSP_PWM2_11_PINNAME, PWM_11, pinname_whitelist_pwm11);
311 #endif /* BSP_USING_PWM2 */
312
313 #ifdef BSP_USING_PWM3
314 pinmux_config(BSP_PWM3_12_PINNAME, PWM_12, pinname_whitelist_pwm12);
315 pinmux_config(BSP_PWM3_13_PINNAME, PWM_13, pinname_whitelist_pwm13);
316 pinmux_config(BSP_PWM3_14_PINNAME, PWM_14, pinname_whitelist_pwm14);
317 pinmux_config(BSP_PWM3_15_PINNAME, PWM_15, pinname_whitelist_pwm15);
318 #endif /* BSP_USING_PWM3 */
319 }
320
rt_hw_pwm_init(void)321 int rt_hw_pwm_init(void)
322 {
323 int result = RT_EOK;
324 uint8_t i;
325
326 rt_hw_pwm_pinmux_config();
327
328 for (i = 0; i < sizeof(cvi_pwm) / sizeof(cvi_pwm[0]); i++)
329 {
330 cvi_pwm[i].device.base = (rt_ubase_t)DRV_IOREMAP((void *)cvi_pwm[i].device.base, 0x1000);
331
332 result = rt_device_pwm_register(&cvi_pwm[i].device, cvi_pwm[i].name, &cvi_pwm_ops, &cvi_pwm[i]);
333 if (result != RT_EOK)
334 {
335 LOG_E("device %s register failed", cvi_pwm[i].name);
336 return -RT_ERROR;
337 }
338 }
339 return RT_EOK;
340 }
341 INIT_DEVICE_EXPORT(rt_hw_pwm_init);
342