1 /*
2 * Copyright (c) 2021 Teslabs Engineering S.L.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT gd_gd32_pwm
8
9 #include <errno.h>
10
11 #include <zephyr/drivers/clock_control.h>
12 #include <zephyr/drivers/clock_control/gd32.h>
13 #include <zephyr/drivers/pwm.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/drivers/reset.h>
16 #include <zephyr/sys/util_macro.h>
17
18 #include <gd32_timer.h>
19
20 #include <zephyr/logging/log.h>
21
22 LOG_MODULE_REGISTER(pwm_gd32, CONFIG_PWM_LOG_LEVEL);
23
24 /** PWM data. */
25 struct pwm_gd32_data {
26 /** Timer clock (Hz). */
27 uint32_t tim_clk;
28 };
29
30 /** PWM configuration. */
31 struct pwm_gd32_config {
32 /** Timer register. */
33 uint32_t reg;
34 /** Number of channels */
35 uint8_t channels;
36 /** Flag to indicate if timer has 32-bit counter */
37 bool is_32bit;
38 /** Flag to indicate if timer is advanced */
39 bool is_advanced;
40 /** Prescaler. */
41 uint16_t prescaler;
42 /** Clock id. */
43 uint16_t clkid;
44 /** Reset. */
45 struct reset_dt_spec reset;
46 /** pinctrl configurations. */
47 const struct pinctrl_dev_config *pcfg;
48 };
49
50 /** Obtain channel enable bit for the given channel */
51 #define TIMER_CHCTL2_CHXEN(ch) BIT(4U * (ch))
52 /** Obtain polarity bit for the given channel */
53 #define TIMER_CHCTL2_CHXP(ch) BIT(1U + (4U * (ch)))
54 /** Obtain CHCTL0/1 mask for the given channel (0 or 1) */
55 #define TIMER_CHCTLX_MSK(ch) (0xFU << (8U * (ch)))
56
57 /** Obtain RCU register offset from RCU clock value */
58 #define RCU_CLOCK_OFFSET(rcu_clock) ((rcu_clock) >> 6U)
59
pwm_gd32_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)60 static int pwm_gd32_set_cycles(const struct device *dev, uint32_t channel,
61 uint32_t period_cycles, uint32_t pulse_cycles,
62 pwm_flags_t flags)
63 {
64 const struct pwm_gd32_config *config = dev->config;
65
66 if (channel >= config->channels) {
67 return -EINVAL;
68 }
69
70 /* 16-bit timers can count up to UINT16_MAX */
71 if (!config->is_32bit && (period_cycles > UINT16_MAX)) {
72 return -ENOTSUP;
73 }
74
75 /* disable channel output if period is zero */
76 if (period_cycles == 0U) {
77 TIMER_CHCTL2(config->reg) &= ~TIMER_CHCTL2_CHXEN(channel);
78 return 0;
79 }
80
81 /* update polarity */
82 if ((flags & PWM_POLARITY_INVERTED) != 0U) {
83 TIMER_CHCTL2(config->reg) |= TIMER_CHCTL2_CHXP(channel);
84 } else {
85 TIMER_CHCTL2(config->reg) &= ~TIMER_CHCTL2_CHXP(channel);
86 }
87
88 /* update pulse */
89 switch (channel) {
90 case 0U:
91 TIMER_CH0CV(config->reg) = pulse_cycles;
92 break;
93 case 1U:
94 TIMER_CH1CV(config->reg) = pulse_cycles;
95 break;
96 case 2U:
97 TIMER_CH2CV(config->reg) = pulse_cycles;
98 break;
99 case 3U:
100 TIMER_CH3CV(config->reg) = pulse_cycles;
101 break;
102 default:
103 __ASSERT_NO_MSG(NULL);
104 break;
105 }
106
107 /* update period */
108 TIMER_CAR(config->reg) = period_cycles;
109
110 /* channel not enabled: configure it */
111 if ((TIMER_CHCTL2(config->reg) & TIMER_CHCTL2_CHXEN(channel)) == 0U) {
112 volatile uint32_t *chctl;
113
114 /* select PWM1 mode, enable OC shadowing */
115 if (channel < 2U) {
116 chctl = &TIMER_CHCTL0(config->reg);
117 } else {
118 chctl = &TIMER_CHCTL1(config->reg);
119 }
120
121 *chctl &= ~TIMER_CHCTLX_MSK(channel);
122 *chctl |= (TIMER_OC_MODE_PWM1 | TIMER_OC_SHADOW_ENABLE) <<
123 (8U * (channel % 2U));
124
125 /* enable channel output */
126 TIMER_CHCTL2(config->reg) |= TIMER_CHCTL2_CHXEN(channel);
127
128 /* generate update event (to load shadow values) */
129 TIMER_SWEVG(config->reg) |= TIMER_SWEVG_UPG;
130 }
131
132 return 0;
133 }
134
pwm_gd32_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)135 static int pwm_gd32_get_cycles_per_sec(const struct device *dev,
136 uint32_t channel, uint64_t *cycles)
137 {
138 struct pwm_gd32_data *data = dev->data;
139 const struct pwm_gd32_config *config = dev->config;
140
141 *cycles = (uint64_t)(data->tim_clk / (config->prescaler + 1U));
142
143 return 0;
144 }
145
146 static const struct pwm_driver_api pwm_gd32_driver_api = {
147 .set_cycles = pwm_gd32_set_cycles,
148 .get_cycles_per_sec = pwm_gd32_get_cycles_per_sec,
149 };
150
pwm_gd32_init(const struct device * dev)151 static int pwm_gd32_init(const struct device *dev)
152 {
153 const struct pwm_gd32_config *config = dev->config;
154 struct pwm_gd32_data *data = dev->data;
155 int ret;
156
157 (void)clock_control_on(GD32_CLOCK_CONTROLLER,
158 (clock_control_subsys_t)&config->clkid);
159
160 (void)reset_line_toggle_dt(&config->reset);
161
162 /* apply pin configuration */
163 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
164 if (ret < 0) {
165 return ret;
166 }
167
168 /* cache timer clock value */
169 (void)clock_control_get_rate(GD32_CLOCK_CONTROLLER,
170 (clock_control_subsys_t)&config->clkid,
171 &data->tim_clk);
172
173 /* basic timer operation: edge aligned, up counting, shadowed CAR */
174 TIMER_CTL0(config->reg) = TIMER_CKDIV_DIV1 | TIMER_COUNTER_EDGE |
175 TIMER_COUNTER_UP | TIMER_CTL0_ARSE;
176 TIMER_PSC(config->reg) = config->prescaler;
177
178 /* enable primary output for advanced timers */
179 if (config->is_advanced) {
180 TIMER_CCHP(config->reg) |= TIMER_CCHP_POEN;
181 }
182
183 /* enable timer counter */
184 TIMER_CTL0(config->reg) |= TIMER_CTL0_CEN;
185
186 return 0;
187 }
188
189 #define PWM_GD32_DEFINE(i) \
190 static struct pwm_gd32_data pwm_gd32_data_##i; \
191 \
192 PINCTRL_DT_INST_DEFINE(i); \
193 \
194 static const struct pwm_gd32_config pwm_gd32_config_##i = { \
195 .reg = DT_REG_ADDR(DT_INST_PARENT(i)), \
196 .clkid = DT_CLOCKS_CELL(DT_INST_PARENT(i), id), \
197 .reset = RESET_DT_SPEC_GET(DT_INST_PARENT(i)), \
198 .prescaler = DT_PROP(DT_INST_PARENT(i), prescaler), \
199 .channels = DT_PROP(DT_INST_PARENT(i), channels), \
200 .is_32bit = DT_PROP(DT_INST_PARENT(i), is_32bit), \
201 .is_advanced = DT_PROP(DT_INST_PARENT(i), is_advanced), \
202 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(i), \
203 }; \
204 \
205 DEVICE_DT_INST_DEFINE(i, &pwm_gd32_init, NULL, &pwm_gd32_data_##i, \
206 &pwm_gd32_config_##i, POST_KERNEL, \
207 CONFIG_PWM_INIT_PRIORITY, \
208 &pwm_gd32_driver_api);
209
210 DT_INST_FOREACH_STATUS_OKAY(PWM_GD32_DEFINE)
211