1 /*
2 * Copyright (c) 2022, Joep Buruma
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #define DT_DRV_COMPAT raspberrypi_pico_pwm
7
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/clock_control.h>
10 #include <zephyr/drivers/pwm.h>
11 #include <zephyr/drivers/pinctrl.h>
12 #include <zephyr/drivers/reset.h>
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(pwm_rpi_pico, CONFIG_PWM_LOG_LEVEL);
15
16 /* pico-sdk includes */
17 #include <hardware/pwm.h>
18 #include <hardware/structs/pwm.h>
19
20 #define PWM_RPI_PICO_COUNTER_TOP_MAX UINT16_MAX
21 #define PWM_RPI_NUM_CHANNELS (16U)
22
23 struct pwm_rpi_slice_config {
24 uint8_t integral;
25 uint8_t frac;
26 bool phase_correct;
27 };
28
29 struct pwm_rpi_config {
30 /*
31 * pwm_controller is the start address of the pwm peripheral.
32 */
33 pwm_hw_t *pwm_controller;
34 struct pwm_rpi_slice_config slice_configs[NUM_PWM_SLICES];
35 const struct pinctrl_dev_config *pcfg;
36 const struct reset_dt_spec reset;
37 const struct device *clk_dev;
38 const clock_control_subsys_t clk_id;
39 };
40
pwm_rpi_channel_to_slice(uint32_t channel)41 static inline uint32_t pwm_rpi_channel_to_slice(uint32_t channel)
42 {
43 return channel / 2;
44 }
45
pwm_rpi_channel_to_pico_channel(uint32_t channel)46 static inline uint32_t pwm_rpi_channel_to_pico_channel(uint32_t channel)
47 {
48 return channel % 2;
49 }
50
pwm_rpi_get_cycles_per_sec(const struct device * dev,uint32_t ch,uint64_t * cycles)51 static int pwm_rpi_get_cycles_per_sec(const struct device *dev, uint32_t ch, uint64_t *cycles)
52 {
53 const struct pwm_rpi_config *cfg = dev->config;
54 int slice = pwm_rpi_channel_to_slice(ch);
55 uint32_t pclk;
56 int ret;
57
58 if (ch >= PWM_RPI_NUM_CHANNELS) {
59 return -EINVAL;
60 }
61
62 const struct pwm_rpi_slice_config *slice_config = &cfg->slice_configs[slice];
63
64 ret = clock_control_get_rate(cfg->clk_dev, cfg->clk_id, &pclk);
65 if (ret < 0 || pclk == 0) {
66 return -EINVAL;
67 }
68
69 if (slice_config->integral == 0) {
70 *cycles = pclk;
71 } else {
72 /* No need to check for divide by 0 since the minimum value of
73 * pwm_rpi_get_clkdiv is 1
74 */
75 *cycles = (uint64_t)pclk * 16 /
76 ((uint64_t)slice_config->integral * 16 + slice_config->frac);
77 }
78 return 0;
79 }
80
81 /* The pico_sdk only allows setting the polarity of both channels at once.
82 * This is a convenience function to make setting the polarity of a single
83 * channel easier.
84 */
pwm_rpi_set_channel_polarity(const struct device * dev,int slice,int pico_channel,bool inverted)85 static void pwm_rpi_set_channel_polarity(const struct device *dev, int slice,
86 int pico_channel, bool inverted)
87 {
88 const struct pwm_rpi_config *cfg = dev->config;
89
90 bool pwm_polarity_a = (cfg->pwm_controller->slice[slice].csr & PWM_CH0_CSR_A_INV_BITS) > 0;
91 bool pwm_polarity_b = (cfg->pwm_controller->slice[slice].csr & PWM_CH0_CSR_B_INV_BITS) > 0;
92
93 if (pico_channel == PWM_CHAN_A) {
94 pwm_polarity_a = inverted;
95 } else if (pico_channel == PWM_CHAN_B) {
96 pwm_polarity_b = inverted;
97 }
98
99 pwm_set_output_polarity(slice, pwm_polarity_a, pwm_polarity_b);
100 }
101
pwm_rpi_set_cycles(const struct device * dev,uint32_t ch,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)102 static int pwm_rpi_set_cycles(const struct device *dev, uint32_t ch, uint32_t period_cycles,
103 uint32_t pulse_cycles, pwm_flags_t flags)
104 {
105 const struct pwm_rpi_config *cfg = dev->config;
106 int slice = pwm_rpi_channel_to_slice(ch);
107
108 /* this is the channel within a pwm slice */
109 int pico_channel = pwm_rpi_channel_to_pico_channel(ch);
110 int div_int;
111 int div_frac;
112
113 if (ch >= PWM_RPI_NUM_CHANNELS) {
114 return -EINVAL;
115 }
116
117 div_int = cfg->slice_configs[slice].integral;
118 div_frac = cfg->slice_configs[slice].frac;
119
120 if (div_int == 0) {
121 div_int = 1;
122 div_frac = 0;
123 while ((period_cycles / div_int - 1) > PWM_RPI_PICO_COUNTER_TOP_MAX) {
124 div_int *= 2;
125 }
126
127 if (div_int > (UINT8_MAX + 1)) {
128 return -EINVAL;
129 }
130
131 period_cycles /= div_int;
132 pulse_cycles /= div_int;
133 }
134
135 if (period_cycles - 1 > PWM_RPI_PICO_COUNTER_TOP_MAX ||
136 pulse_cycles > PWM_RPI_PICO_COUNTER_TOP_MAX) {
137 return -EINVAL;
138 }
139
140 pwm_rpi_set_channel_polarity(dev, slice, pico_channel,
141 (flags & PWM_POLARITY_MASK) == PWM_POLARITY_INVERTED);
142 pwm_set_wrap(slice, period_cycles - 1);
143 pwm_set_chan_level(slice, pico_channel, pulse_cycles);
144 pwm_set_clkdiv_int_frac(slice, div_int, div_frac);
145
146 return 0;
147 };
148
149 static DEVICE_API(pwm, pwm_rpi_driver_api) = {
150 .get_cycles_per_sec = pwm_rpi_get_cycles_per_sec,
151 .set_cycles = pwm_rpi_set_cycles,
152 };
153
pwm_rpi_init(const struct device * dev)154 static int pwm_rpi_init(const struct device *dev)
155 {
156 const struct pwm_rpi_config *cfg = dev->config;
157 pwm_config slice_cfg;
158 size_t slice_idx;
159 int err;
160
161 err = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
162 if (err) {
163 LOG_ERR("Failed to configure pins for PWM. err=%d", err);
164 return err;
165 }
166
167 err = clock_control_on(cfg->clk_dev, cfg->clk_id);
168 if (err < 0) {
169 return err;
170 }
171
172 err = reset_line_toggle_dt(&cfg->reset);
173 if (err < 0) {
174 return err;
175 }
176
177 for (slice_idx = 0; slice_idx < NUM_PWM_SLICES; slice_idx++) {
178 slice_cfg = pwm_get_default_config();
179 pwm_config_set_clkdiv_mode(&slice_cfg, PWM_DIV_FREE_RUNNING);
180
181 pwm_init(slice_idx, &slice_cfg, false);
182
183 if (cfg->slice_configs[slice_idx].integral == 0) {
184 pwm_set_clkdiv_int_frac(slice_idx, 1, 0);
185 } else {
186 pwm_set_clkdiv_int_frac(slice_idx,
187 cfg->slice_configs[slice_idx].integral,
188 cfg->slice_configs[slice_idx].frac);
189 }
190 pwm_set_enabled(slice_idx, true);
191 }
192
193 return 0;
194 }
195
196 #define PWM_INST_RPI_SLICE_DIVIDER(idx, n) \
197 { \
198 .integral = DT_INST_PROP_OR(idx, UTIL_CAT(divider_int_, n), 0), \
199 .frac = DT_INST_PROP_OR(idx, UTIL_CAT(divider_frac_, n), 0), \
200 }
201
202 #define PWM_RPI_INIT(idx) \
203 \
204 PINCTRL_DT_INST_DEFINE(idx); \
205 static const struct pwm_rpi_config pwm_rpi_config_##idx = { \
206 .pwm_controller = (pwm_hw_t *)DT_INST_REG_ADDR(idx), \
207 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(idx), \
208 .slice_configs = { \
209 PWM_INST_RPI_SLICE_DIVIDER(idx, 0), \
210 PWM_INST_RPI_SLICE_DIVIDER(idx, 1), \
211 PWM_INST_RPI_SLICE_DIVIDER(idx, 2), \
212 PWM_INST_RPI_SLICE_DIVIDER(idx, 3), \
213 PWM_INST_RPI_SLICE_DIVIDER(idx, 4), \
214 PWM_INST_RPI_SLICE_DIVIDER(idx, 5), \
215 PWM_INST_RPI_SLICE_DIVIDER(idx, 6), \
216 PWM_INST_RPI_SLICE_DIVIDER(idx, 7), \
217 }, \
218 .reset = RESET_DT_SPEC_INST_GET(idx), \
219 .clk_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(idx)), \
220 .clk_id = (clock_control_subsys_t)DT_INST_PHA_BY_IDX(idx, clocks, 0, clk_id), \
221 }; \
222 \
223 DEVICE_DT_INST_DEFINE(idx, pwm_rpi_init, NULL, NULL, &pwm_rpi_config_##idx, POST_KERNEL, \
224 CONFIG_PWM_INIT_PRIORITY, &pwm_rpi_driver_api);
225
226 DT_INST_FOREACH_STATUS_OKAY(PWM_RPI_INIT);
227