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