1 /*
2 * Copyright (c) 2006-2025, RT-Thread Development Team
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2021-10-25 KevinXu first version
9 */
10
11 #include "drv_pwm.h"
12
13 #ifdef BSP_USING_PWM
14
15 /* Declare the control function first */
16 static rt_err_t drv_pwm_control(struct rt_device_pwm *, int, void *);
17 static struct rt_pwm_ops drv_ops =
18 {
19 drv_pwm_control
20 };
21
22 static struct ra_pwm ra6m4_pwm_obj[BSP_PWMS_NUM] =
23 {
24 #ifdef BSP_USING_PWM0
25 [BSP_PWM0_INDEX] = PWM_DRV_INITIALIZER(0),
26 #endif
27 #ifdef BSP_USING_PWM1
28 [BSP_PWM1_INDEX] = PWM_DRV_INITIALIZER(1),
29 #endif
30 #ifdef BSP_USING_PWM2
31 [BSP_PWM2_INDEX] = PWM_DRV_INITIALIZER(2),
32 #endif
33 #ifdef BSP_USING_PWM3
34 [BSP_PWM3_INDEX] = PWM_DRV_INITIALIZER(3),
35 #endif
36 #ifdef BSP_USING_PWM4
37 [BSP_PWM4_INDEX] = PWM_DRV_INITIALIZER(4),
38 #endif
39 #ifdef BSP_USING_PWM5
40 [BSP_PWM5_INDEX] = PWM_DRV_INITIALIZER(5),
41 #endif
42 #ifdef BSP_USING_PWM6
43 [BSP_PWM6_INDEX] = PWM_DRV_INITIALIZER(6),
44 #endif
45 #ifdef BSP_USING_PWM7
46 [BSP_PWM7_INDEX] = PWM_DRV_INITIALIZER(7),
47 #endif
48 #ifdef BSP_USING_PWM8
49 [BSP_PWM8_INDEX] = PWM_DRV_INITIALIZER(8),
50 #endif
51 #ifdef BSP_USING_PWM9
52 [BSP_PWM9_INDEX] = PWM_DRV_INITIALIZER(9),
53 #endif
54 };
55
56 #ifdef SOC_SERIES_R9A07G0
57 #define FSP_PRIV_CLOCK FSP_PRIV_CLOCK_PCLKGPTL
58 #else
59 #define FSP_PRIV_CLOCK FSP_PRIV_CLOCK_PCLKD
60 #endif
61
62 /* Convert the raw PWM period counts into ns */
_convert_counts_ns(uint32_t source_div,uint32_t raw)63 static rt_uint32_t _convert_counts_ns(uint32_t source_div, uint32_t raw)
64 {
65 uint32_t pclkd_freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK) >> source_div;
66 uint32_t ns = (uint32_t)(((uint64_t)raw * 1000000000ULL) / pclkd_freq_hz);
67 return ns;
68 }
69
70 /* Convert ns into raw PWM period counts */
_convert_ns_counts(uint32_t source_div,uint32_t raw)71 static rt_uint32_t _convert_ns_counts(uint32_t source_div, uint32_t raw)
72 {
73 uint32_t pclkd_freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK) >> source_div;
74 uint32_t counts = (uint32_t)(((uint64_t)raw * (uint64_t)pclkd_freq_hz) / 1000000000ULL);
75 return counts;
76 }
77
78
79 /* PWM_CMD_ENABLE or PWM_CMD_DISABLE */
drv_pwm_enable(struct ra_pwm * device,struct rt_pwm_configuration * configuration,rt_bool_t enable)80 static rt_err_t drv_pwm_enable(struct ra_pwm *device,
81 struct rt_pwm_configuration *configuration,
82 rt_bool_t enable)
83 {
84 fsp_err_t err = FSP_SUCCESS;
85
86 if (enable)
87 {
88 err = R_GPT_Start(device->g_ctrl);
89 }
90 else
91 {
92 err = R_GPT_Stop(device->g_ctrl);
93 }
94
95 return (err == FSP_SUCCESS) ? RT_EOK : -RT_ERROR;
96 }
97
98 /* PWM_CMD_GET */
drv_pwm_get(struct ra_pwm * device,struct rt_pwm_configuration * configuration)99 static rt_err_t drv_pwm_get(struct ra_pwm *device,
100 struct rt_pwm_configuration *configuration)
101 {
102 timer_info_t info;
103 if (R_GPT_InfoGet(device->g_ctrl, &info) != FSP_SUCCESS)
104 return -RT_ERROR;
105
106 configuration->pulse =
107 _convert_counts_ns(device->g_cfg->source_div, device->g_cfg->duty_cycle_counts);
108 configuration->period =
109 _convert_counts_ns(device->g_cfg->source_div, info.period_counts);
110 configuration->channel = device->g_cfg->channel;
111
112 return RT_EOK;
113 }
114
115 /* PWM_CMD_SET */
drv_pwm_set(struct ra_pwm * device,struct rt_pwm_configuration * conf)116 static rt_err_t drv_pwm_set(struct ra_pwm *device,
117 struct rt_pwm_configuration *conf)
118 {
119 uint32_t counts;
120 fsp_err_t fsp_erra;
121 fsp_err_t fsp_errb;
122 rt_err_t rt_err;
123 uint32_t pulse;
124 uint32_t period;
125 struct rt_pwm_configuration orig_conf;
126
127 rt_err = drv_pwm_get(device, &orig_conf);
128 if (rt_err != RT_EOK)
129 {
130 return rt_err;
131 }
132
133 /* Pulse cannot last longer than period. */
134 period = conf->period;
135 pulse = (period >= conf->pulse) ? conf->pulse : period;
136
137 /* Not to set period again if it's not changed. */
138 if (period != orig_conf.period)
139 {
140 counts = _convert_ns_counts(device->g_cfg->source_div, period);
141 fsp_erra = R_GPT_PeriodSet(device->g_ctrl, counts);
142 if (fsp_erra != FSP_SUCCESS)
143 {
144 return -RT_ERROR;
145 }
146 }
147
148 /* Two pins of a channel will not be separated. */
149 counts = _convert_ns_counts(device->g_cfg->source_div, pulse);
150 fsp_erra = R_GPT_DutyCycleSet(device->g_ctrl, counts, GPT_IO_PIN_GTIOCA);
151 fsp_errb = R_GPT_DutyCycleSet(device->g_ctrl, counts, GPT_IO_PIN_GTIOCB);
152 if (fsp_erra != FSP_SUCCESS || fsp_errb != FSP_SUCCESS)
153 {
154 return -RT_ERROR;
155 }
156
157 return RT_EOK;
158 }
159
160 /**
161 * Implement of control method in struct rt_pwm_ops.
162 */
drv_pwm_control(struct rt_device_pwm * device,int cmd,void * arg)163 static rt_err_t drv_pwm_control(struct rt_device_pwm *device, int cmd, void *arg)
164 {
165 struct rt_pwm_configuration *configuration = (struct rt_pwm_configuration *)arg;
166 struct ra_pwm *pwm_device = (struct ra_pwm *)device->parent.user_data;
167
168 /**
169 * There's actually only one GPT timer with 10 channels. In this case, the
170 * timer is separated into 10 PWM devices, so each device has only one
171 * channel.
172 */
173 if (configuration->channel != 0)
174 {
175 return -RT_EINVAL;
176 }
177
178 switch (cmd)
179 {
180 case PWM_CMD_ENABLE:
181 return drv_pwm_enable(pwm_device, configuration, RT_TRUE);
182 case PWM_CMD_DISABLE:
183 return drv_pwm_enable(pwm_device, configuration, RT_FALSE);
184 case PWM_CMD_GET:
185 return drv_pwm_get(pwm_device, configuration);
186 case PWM_CMD_SET:
187 return drv_pwm_set(pwm_device, configuration);
188 default:
189 return -RT_EINVAL;
190 }
191
192 return RT_EOK;
193 }
194
195 /**
196 * This is to register the PWM device
197 *
198 * Note that the PWM driver only supports one fixed pin.
199 */
rt_hw_pwm_init(void)200 int rt_hw_pwm_init(void)
201 {
202 rt_err_t ret = RT_EOK;
203 rt_err_t rt_err = RT_EOK;
204 fsp_err_t fsp_err = FSP_SUCCESS;
205
206 for (int i = 0; i < BSP_PWMS_NUM; i++)
207 {
208 fsp_err = R_GPT_Open(ra6m4_pwm_obj[i].g_ctrl,
209 ra6m4_pwm_obj[i].g_cfg);
210
211 rt_err = rt_device_pwm_register(&ra6m4_pwm_obj[i].pwm_device,
212 ra6m4_pwm_obj[i].name,
213 &drv_ops,
214 &ra6m4_pwm_obj[i]);
215
216 if (fsp_err != FSP_SUCCESS || rt_err != RT_EOK)
217 {
218 ret = -RT_ERROR;
219 }
220 }
221
222 return ret;
223 }
224 INIT_BOARD_EXPORT(rt_hw_pwm_init);
225 #endif /* BSP_USING_PWM */
226