1 /*
2 * Copyright (c) 2022 IoT.bzh
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT renesas_pwm_rcar
8
9 #include <errno.h>
10
11 #include <zephyr/device.h>
12 #include <zephyr/devicetree.h>
13 #include <zephyr/drivers/clock_control/renesas_cpg_mssr.h>
14 #include <zephyr/drivers/pinctrl.h>
15 #include <zephyr/drivers/pwm.h>
16
17 #include <soc.h>
18
19 #define LOG_LEVEL CONFIG_PWM_LOG_LEVEL
20 #include <zephyr/logging/log.h>
21 LOG_MODULE_REGISTER(pwm_rcar);
22
23 /* PWM Controller capabilities */
24 #define RCAR_PWM_MAX_CYCLE 1023U
25 #define RCAR_PWM_MAX_DIV 24U
26 #define RCAR_PWM_MAX_CHANNEL 6
27
28 /* Registers */
29 #define RCAR_PWM_REG_SHIFT 0x1000
30 #define RCAR_PWM_CR(channel) \
31 ((uint32_t)((channel * RCAR_PWM_REG_SHIFT)) + 0x00) /* PWM Control Register */
32 #define RCAR_PWM_CNT(channel) \
33 ((uint32_t)((channel * RCAR_PWM_REG_SHIFT)) + 0x04) /* PWM Count Register */
34
35 /* PWMCR (PWM Control Register) */
36 #define RCAR_PWM_CR_CC_MASK 0x000f0000 /* Clock Control */
37 #define RCAR_PWM_CR_CC_SHIFT 16
38 #define RCAR_PWM_CR_CCMD BIT(15) /* Frequency Division Mode */
39 #define RCAR_PWM_CR_SYNC BIT(11)
40 #define RCAR_PWM_CR_SS BIT(4) /* Single Pulse Output */
41 #define RCAR_PWM_CR_EN BIT(0) /* Channel Enable */
42
43 /* PWM Diviser is on 5 bits (CC combined with CCMD) */
44 #define RCAR_PWM_DIVISER_MASK (RCAR_PWM_CR_CC_MASK | RCAR_PWM_CR_CCMD)
45 #define RCAR_PWM_DIVISER_SHIFT 15
46
47 /* PWMCNT (PWM Count Register) */
48 #define RCAR_PWM_CNT_CYC_MASK 0x03ff0000 /* PWM Cycle */
49 #define RCAR_PWM_CNT_CYC_SHIFT 16
50 #define RCAR_PWM_CNT_PH_MASK 0x000003ff /* PWM High-Level Period */
51 #define RCAR_PWM_CNT_PH_SHIFT 0
52
53 struct pwm_rcar_cfg {
54 uint32_t reg_addr;
55 const struct device *clock_dev;
56 struct rcar_cpg_clk core_clk;
57 struct rcar_cpg_clk mod_clk;
58 const struct pinctrl_dev_config *pcfg;
59 };
60
61 struct pwm_rcar_data {
62 uint32_t clk_rate;
63 };
64
pwm_rcar_read(const struct pwm_rcar_cfg * config,uint32_t offs)65 static uint32_t pwm_rcar_read(const struct pwm_rcar_cfg *config, uint32_t offs)
66 {
67 return sys_read32(config->reg_addr + offs);
68 }
69
pwm_rcar_write(const struct pwm_rcar_cfg * config,uint32_t offs,uint32_t value)70 static void pwm_rcar_write(const struct pwm_rcar_cfg *config, uint32_t offs, uint32_t value)
71 {
72 sys_write32(value, config->reg_addr + offs);
73 }
74
pwm_rcar_write_bit(const struct pwm_rcar_cfg * config,uint32_t offs,uint32_t bits,bool value)75 static void pwm_rcar_write_bit(const struct pwm_rcar_cfg *config, uint32_t offs, uint32_t bits,
76 bool value)
77 {
78 uint32_t reg_val = pwm_rcar_read(config, offs);
79
80 if (value) {
81 reg_val |= bits;
82 } else {
83 reg_val &= ~(bits);
84 }
85
86 pwm_rcar_write(config, offs, reg_val);
87 }
88
pwm_rcar_update_clk(const struct pwm_rcar_cfg * config,uint32_t channel,uint32_t * period_cycles,uint32_t * pulse_cycles)89 static int pwm_rcar_update_clk(const struct pwm_rcar_cfg *config, uint32_t channel,
90 uint32_t *period_cycles, uint32_t *pulse_cycles)
91 {
92 uint32_t reg_val, power, diviser;
93
94 power = pwm_rcar_read(config, RCAR_PWM_CR(channel)) & RCAR_PWM_DIVISER_MASK;
95 power = power >> RCAR_PWM_DIVISER_SHIFT;
96 diviser = 1 << power;
97
98 LOG_DBG("Found old diviser : 2^%d=%d", power, diviser);
99
100 /* Looking for the best possible clock diviser */
101 if (*period_cycles > RCAR_PWM_MAX_CYCLE) {
102 /* Reducing clock speed */
103 while (*period_cycles > RCAR_PWM_MAX_CYCLE) {
104 diviser *= 2;
105 *period_cycles /= 2;
106 *pulse_cycles /= 2;
107 power++;
108 if (power > RCAR_PWM_MAX_DIV) {
109 return -ENOTSUP;
110 }
111 }
112 } else {
113 /* Increasing clock speed */
114 while (*period_cycles < (RCAR_PWM_MAX_CYCLE / 2)) {
115 if (power == 0) {
116 return -ENOTSUP;
117 }
118 diviser /= 2;
119 *period_cycles *= 2;
120 *pulse_cycles *= 2;
121 power--;
122 }
123 }
124 LOG_DBG("Found new diviser : 2^%d=%d\n", power, diviser);
125
126 /* Set new clock Diviser */
127 reg_val = pwm_rcar_read(config, RCAR_PWM_CR(channel));
128 reg_val &= ~RCAR_PWM_DIVISER_MASK;
129 reg_val |= (power << RCAR_PWM_DIVISER_SHIFT);
130 pwm_rcar_write(config, RCAR_PWM_CR(channel), reg_val);
131
132 return 0;
133 }
134
pwm_rcar_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)135 static int pwm_rcar_set_cycles(const struct device *dev, uint32_t channel, uint32_t period_cycles,
136 uint32_t pulse_cycles, pwm_flags_t flags)
137 {
138 const struct pwm_rcar_cfg *config = dev->config;
139 uint32_t reg_val;
140 int ret = 0;
141
142 if (channel > RCAR_PWM_MAX_CHANNEL) {
143 return -ENOTSUP;
144 }
145
146 if (flags != PWM_POLARITY_NORMAL) {
147 return -ENOTSUP;
148 }
149
150 /* Prohibited values */
151 if (period_cycles == 0U || pulse_cycles == 0U || pulse_cycles > period_cycles) {
152 return -EINVAL;
153 }
154
155 LOG_DBG("base_reg=0x%x, pulse_cycles=%d, period_cycles=%d,"
156 " duty_cycle=%d",
157 config->reg_addr, pulse_cycles, period_cycles,
158 (pulse_cycles * 100U / period_cycles));
159
160 /* Disable PWM */
161 pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_EN, false);
162
163 /* Set continuous mode */
164 pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_SS, false);
165
166 /* Enable SYNC mode */
167 pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_SYNC, true);
168
169 /*
170 * Set clock counter according to the requested period_cycles
171 * if period_cycles is less than half of the counter, then the
172 * clock diviser could be updated as the diviser is a modulo 2.
173 */
174 if (period_cycles > RCAR_PWM_MAX_CYCLE || period_cycles < (RCAR_PWM_MAX_CYCLE / 2)) {
175 LOG_DBG("Adapting frequency diviser...");
176 ret = pwm_rcar_update_clk(config, channel, &period_cycles, &pulse_cycles);
177 if (ret != 0) {
178 return ret;
179 }
180 }
181
182 /* Set total period cycle */
183 reg_val = pwm_rcar_read(config, RCAR_PWM_CNT(channel));
184 reg_val &= ~(RCAR_PWM_CNT_CYC_MASK);
185 reg_val |= (period_cycles << RCAR_PWM_CNT_CYC_SHIFT);
186 pwm_rcar_write(config, RCAR_PWM_CNT(channel), reg_val);
187
188 /* Set high level period cycle */
189 reg_val = pwm_rcar_read(config, RCAR_PWM_CNT(channel));
190 reg_val &= ~(RCAR_PWM_CNT_PH_MASK);
191 reg_val |= (pulse_cycles << RCAR_PWM_CNT_PH_SHIFT);
192 pwm_rcar_write(config, RCAR_PWM_CNT(channel), reg_val);
193
194 /* Enable PWM */
195 pwm_rcar_write_bit(config, RCAR_PWM_CR(channel), RCAR_PWM_CR_EN, true);
196
197 return ret;
198 }
199
pwm_rcar_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)200 static int pwm_rcar_get_cycles_per_sec(const struct device *dev, uint32_t channel, uint64_t *cycles)
201 {
202 const struct pwm_rcar_cfg *config = dev->config;
203 struct pwm_rcar_data *data = dev->data;
204 uint32_t diviser;
205
206 if (channel > RCAR_PWM_MAX_CHANNEL) {
207 return -ENOTSUP;
208 }
209
210 diviser = pwm_rcar_read(config, RCAR_PWM_CR(channel)) & RCAR_PWM_DIVISER_MASK;
211 diviser = diviser >> RCAR_PWM_DIVISER_SHIFT;
212 *cycles = data->clk_rate >> diviser;
213
214 LOG_DBG("Actual division: %d and Frequency: %d Hz", diviser, (uint32_t)*cycles);
215
216 return 0;
217 }
218
pwm_rcar_init(const struct device * dev)219 static int pwm_rcar_init(const struct device *dev)
220 {
221 const struct pwm_rcar_cfg *config = dev->config;
222 struct pwm_rcar_data *data = dev->data;
223 int ret;
224
225 /* Configure dt provided device signals when available */
226 ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
227 if (ret < 0) {
228 return ret;
229 }
230
231 ret = clock_control_on(config->clock_dev, (clock_control_subsys_t)&config->mod_clk);
232 if (ret < 0) {
233 return ret;
234 }
235
236 ret = clock_control_get_rate(config->clock_dev, (clock_control_subsys_t)&config->core_clk,
237 &data->clk_rate);
238
239 if (ret < 0) {
240 return ret;
241 }
242
243 return 0;
244 }
245
246 static const struct pwm_driver_api pwm_rcar_driver_api = {
247 .set_cycles = pwm_rcar_set_cycles,
248 .get_cycles_per_sec = pwm_rcar_get_cycles_per_sec,
249 };
250
251 /* Device Instantiation */
252 #define PWM_DEVICE_RCAR_INIT(n) \
253 PINCTRL_DT_INST_DEFINE(n); \
254 static const struct pwm_rcar_cfg pwm_rcar_cfg_##n = { \
255 .reg_addr = DT_INST_REG_ADDR(n), \
256 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
257 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
258 .mod_clk.module = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, module), \
259 .mod_clk.domain = DT_INST_CLOCKS_CELL_BY_IDX(n, 0, domain), \
260 .core_clk.module = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, module), \
261 .core_clk.domain = DT_INST_CLOCKS_CELL_BY_IDX(n, 1, domain), \
262 }; \
263 static struct pwm_rcar_data pwm_rcar_data_##n; \
264 DEVICE_DT_INST_DEFINE(n, pwm_rcar_init, NULL, &pwm_rcar_data_##n, &pwm_rcar_cfg_##n, \
265 POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
266 &pwm_rcar_driver_api);
267
268 DT_INST_FOREACH_STATUS_OKAY(PWM_DEVICE_RCAR_INIT)
269