1 /*
2 * Copyright (c) 2022 Microchip Technololgy Inc.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT microchip_xec_pwmbbled
8
9 #include <errno.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12
13 #include <zephyr/device.h>
14 #include <zephyr/drivers/pwm.h>
15 #ifdef CONFIG_SOC_SERIES_MEC172X
16 #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
17 #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
18 #endif
19 #include <zephyr/drivers/pinctrl.h>
20 #include <zephyr/logging/log.h>
21 #include <zephyr/pm/device.h>
22
23 #include <soc.h>
24
25 LOG_MODULE_REGISTER(pwmbbled_mchp_xec, CONFIG_PWM_LOG_LEVEL);
26
27 /* We will choose frequency from Device Tree */
28 #define XEC_PWM_BBLED_INPUT_FREQ_HI 48000000
29 #define XEC_PWM_BBLED_INPUT_FREQ_LO 32768
30
31 #define XEC_PWM_BBLED_MAX_FREQ_DIV 256U
32 #define XEC_PWM_BBLED_MIN_FREQ_DIV (256U * 4066U)
33
34 /* Maximum frequency BBLED-PWM can generate is scaled by
35 * 256 * (LD+1) where LD is in [0, 4065].
36 */
37 #define XEC_PWM_BBLED_MAX_PWM_FREQ_AHB_CLK \
38 (XEC_PWM_BBLED_INPUT_FREQ_HI / XEC_PWM_BBLED_MAX_FREQ_DIV)
39 #define XEC_PWM_BBLED_MAX_PWM_FREQ_32K_CLK \
40 (XEC_PWM_BBLED_INPUT_FREQ_LO / XEC_PWM_BBLED_MAX_FREQ_DIV)
41
42 /* BBLED PWM mode uses the duty cycle to set the PWM frequency:
43 * Fpwm = Fclock / (256 * (LD + 1)) OR
44 * Tpwm = (256 * (LD + 1)) / Fclock
45 * Fclock is 48MHz or 32KHz
46 * LD = Delay register, LOW_DELAY field: bits[11:0]
47 * Pulse_ON_width = (1/Fpwm) * (duty_cycle/256) seconds
48 * Puse_OFF_width = (1/Fpwm) * (256 - duty_cycle) seconds
49 * where duty_cycle is an 8-bit value 0 to 255.
50 * Prescale is derived from DELAY register LOW_DELAY 12-bit field
51 * Duty cycle is derived from LIMITS register MINIMUM 8-bit field
52 *
53 * Fc in Hz, Tp in seconds
54 * Fc / Fp = 256 * (LD+1)
55 * Tp / Tc = 256 * (LD+1)
56 *
57 * API passes pulse period and pulse width in nanoseconds.
58 * BBLED PWM mode duty cycle specified by 8-bit MIN field of the LIMITS register
59 * MIN=0 is OFF, pin driven low
60 * MIN=255 is ON, pin driven high
61 */
62
63 /* Same BBLED hardware block in MEC15xx and MEC172x families
64 * Config register
65 */
66 #define XEC_PWM_BBLED_CFG_MSK 0x1ffffu
67 #define XEC_PWM_BBLED_CFG_MODE_POS 0
68 #define XEC_PWM_BBLED_CFG_MODE_MSK 0x3u
69 #define XEC_PWM_BBLED_CFG_MODE_OFF 0
70 #define XEC_PWM_BBLED_CFG_MODE_PWM 0x2u
71 #define XEC_PWM_BBLED_CFG_MODE_ALWAYS_ON 0x3u
72 #define XEC_PWM_BBLED_CFG_CLK_SRC_48M_POS 2
73 #define XEC_PWM_BBLED_CFG_EN_UPDATE_POS 6
74 #define XEC_PWM_BBLED_CFG_RST_PWM_POS 7
75 #define XEC_PWM_BBLED_CFG_WDT_RLD_POS 8
76 #define XEC_PWM_BBLED_CFG_WDT_RLD_MSK0 0xffu
77 #define XEC_PWM_BBLED_CFG_WDT_RLD_MSK 0xff00u
78 #define XEC_PWM_BBLED_CFG_WDT_RLD_DFLT 0x1400u
79
80 /* Limits register */
81 #define XEC_PWM_BBLED_LIM_MSK 0xffffu
82 #define XEC_PWM_BBLED_LIM_MIN_POS 0
83 #define XEC_PWM_BBLED_LIM_MIN_MSK 0xffu
84 #define XEC_PWM_BBLED_LIM_MAX_POS 8
85 #define XEC_PWM_BBLED_LIM_MAX_MSK 0xff00u
86
87 /* Delay register */
88 #define XEC_PWM_BBLED_DLY_MSK 0xffffffu
89 #define XEC_PWM_BBLED_DLY_LO_POS 0
90 #define XEC_PWM_BBLED_DLY_LO_MSK 0xfffu
91 #define XEC_PWM_BBLED_DLY_HI_POS 12
92 #define XEC_PWM_BBLED_DLY_HI_MSK 0xfff000u
93
94 /* Output delay in clocks for initial enable and enable on resume from sleep
95 * Clocks are either 48MHz or 32KHz selected in CONFIG register.
96 */
97 #define XEC_PWM_BBLED_OUT_DLY_MSK 0xffu
98
99 /* DT enum values */
100 #define XEC_PWM_BBLED_CLKSEL_32K 0
101 #define XEC_PWM_BBLED_CLKSEL_PCR_SLOW 1
102 #define XEC_PWM_BBLED_CLKSEL_AHB_48M 2
103
104 #define XEC_PWM_BBLED_CLKSEL_0 XEC_PWM_BBLED_CLKSEL_32K
105 #define XEC_PWM_BBLED_CLKSEL_1 XEC_PWM_BBLED_CLKSEL_PCR_SLOW
106 #define XEC_PWM_BBLED_CLKSEL_2 XEC_PWM_BBLED_CLKSEL_AHB_48M
107
108
109 struct bbled_regs {
110 volatile uint32_t config;
111 volatile uint32_t limits;
112 volatile uint32_t delay;
113 volatile uint32_t update_step_size;
114 volatile uint32_t update_interval;
115 volatile uint32_t output_delay;
116 };
117
118 #define XEC_PWM_BBLED_CLK_SEL_48M 0
119 #define XEC_PWM_BBLED_CLK_SEL_32K 1
120
121 struct pwm_bbled_xec_config {
122 struct bbled_regs * const regs;
123 const struct pinctrl_dev_config *pcfg;
124 uint8_t girq;
125 uint8_t girq_pos;
126 uint8_t pcr_idx;
127 uint8_t pcr_pos;
128 uint8_t clk_sel;
129 bool enable_low_power_32K;
130 };
131
132 struct bbled_xec_data {
133 uint32_t config;
134 };
135
136 /* Compute BBLED PWM delay factor to produce requested frequency.
137 * Fpwm = Fclk / (256 * (LD+1)) where Fclk is 48MHz or 32KHz and
138 * LD is a 12-bit value in [0, 4096].
139 * We expect 256 <= pulse_cycles <= (256 * 4096)
140 * period_cycles = (period * cycles_per_sec) / NSEC_PER_SEC;
141 * period_cycles = (Tpwm * Fclk) = Fclk / Fpwm
142 * period_cycles = Fclk * (256 * (LD+1)) / Fclk = (256 * (LD+1))
143 * (LD+1) = period_cycles / 256
144 */
xec_pwmbb_compute_ld(const struct device * dev,uint32_t period_cycles)145 static uint32_t xec_pwmbb_compute_ld(const struct device *dev, uint32_t period_cycles)
146 {
147 uint32_t ld = 0;
148
149 ld = period_cycles / 256U;
150 if (ld > 0) {
151 if (ld > 4096U) {
152 ld = 4096U;
153 }
154 ld--;
155 }
156
157 return ld;
158 }
159
160 /* BBLED-PWM duty cycle set in 8-bit MINIMUM field of BBLED LIMITS register.
161 * Limits.Minimum == 0 (alwyas off, output driven low)
162 * == 255 (always on, output driven high)
163 * 1 <= Limits.Minimum <= 254 duty cycle
164 */
xec_pwmbb_compute_dc(uint32_t period_cycles,uint32_t pulse_cycles)165 static uint32_t xec_pwmbb_compute_dc(uint32_t period_cycles, uint32_t pulse_cycles)
166 {
167 uint32_t dc;
168
169 if (pulse_cycles >= period_cycles) {
170 return 255U; /* always on */
171 }
172
173 if (period_cycles < 256U) {
174 return 0; /* always off */
175 }
176
177 dc = (256U * pulse_cycles) / period_cycles;
178
179 return dc;
180 }
181
182 /* Issue: two separate registers must be updated.
183 * LIMITS.MIN = duty cycle = [1, 254]
184 * LIMITS register update takes effect immediately.
185 * DELAY.LO = pre-scaler = [0, 4095]
186 * Writing DELAY stores value in an internal holding register.
187 * Writing bit[6]=1 causes HW to update DELAY at the beginning of
188 * the next HW PWM period.
189 */
xec_pwmbb_progam_pwm(const struct device * dev,uint32_t ld,uint32_t dc)190 static void xec_pwmbb_progam_pwm(const struct device *dev, uint32_t ld, uint32_t dc)
191 {
192 const struct pwm_bbled_xec_config * const cfg = dev->config;
193 struct bbled_regs * const regs = cfg->regs;
194 uint32_t val;
195
196 val = regs->delay & ~(XEC_PWM_BBLED_DLY_LO_MSK);
197 val |= ((ld << XEC_PWM_BBLED_DLY_LO_POS) & XEC_PWM_BBLED_DLY_LO_MSK);
198 regs->delay = val;
199
200 val = regs->limits & ~(XEC_PWM_BBLED_LIM_MIN_MSK);
201 val |= ((dc << XEC_PWM_BBLED_LIM_MIN_POS) & XEC_PWM_BBLED_LIM_MIN_MSK);
202 regs->limits = val;
203
204 /* transfer new delay value from holding register */
205 regs->config |= BIT(XEC_PWM_BBLED_CFG_EN_UPDATE_POS);
206
207 val = regs->config & ~(XEC_PWM_BBLED_CFG_MODE_MSK);
208 val |= XEC_PWM_BBLED_CFG_MODE_PWM;
209 regs->config = val;
210 }
211
212 /* API implementation: Set the period and pulse width for a single PWM.
213 * channel must be 0 as each PWM instance implements one output.
214 * period in clock cycles of currently configured clock.
215 * pulse width in clock cycles of currently configured clock.
216 * flags b[7:0] defined by zephyr. b[15:8] can be SoC specific.
217 * Bit[0] = 1 inverted, bits[7:1] specify capture features not implemented in XEC PWM.
218 * Note: macro PWM_MSEC() and others convert from other units to nanoseconds.
219 * BBLED output state is Active High. If Active low is required the GPIO pin invert
220 * bit must be set. The XEC PWM block also defaults to Active High but it has a bit
221 * to select Active Low.
222 * PWM API exposes this function as pwm_set_cycles and has wrapper API defined in
223 * pwm.h, named pwm_set which:
224 * Calls pwm_get_cycles_per_second to get current maximum HW frequency as cycles_per_sec
225 * Computes period_cycles = (period * cycles_per_sec) / NSEC_PER_SEC
226 * pulse_cycles = (pulse * cycles_per_sec) / NSEC_PER_SEC
227 * Call pwm_set_cycles passing period_cycles and pulse_cycles.
228 *
229 * BBLED PWM input frequency is 32KHz (POR default) or 48MHz selected by device tree
230 * at application build time.
231 * BBLED Fpwm = Fin / (256 * (LD + 1)) where LD = [0, 4095]
232 * This equation tells use the maximum number of cycles of Fin the hardware can
233 * generate is 256 whereas the mininum number of cycles is 256 * 4096.
234 *
235 * Fin = 32KHz
236 * Fpwm-min = 32768 / (256 * 4096) = 31.25 mHz = 31250000 nHz = 0x01DC_D650 nHz
237 * Fpwm-max = 32768 / 256 = 128 Hz = 128e9 nHz = 0x1D_CD65_0000 nHz
238 * Tpwm-min = 32e9 ns = 0x0007_7359_4000 ns
239 * Tpmw-max = 7812500 ns = 0x0077_3594 ns
240 *
241 * Fin = 48MHz
242 * Fpwm-min = 48e6 / (256 * 4096) = 45.7763 Hz = 45776367188 nHz = 0x000A_A87B_EE53 nHz
243 * Fpwm-max = 48e6 / 256 = 187500 = 1.875e14 = 0xAA87_BEE5_3800 nHz
244 * Tpwm-min = 5334 ns = 0x14D6 ns
245 * Tpwm-max = 21845333 ns = 0x014D_5555 ns
246 */
pwm_bbled_xec_check_cycles(uint32_t period_cycles,uint32_t pulse_cycles)247 static int pwm_bbled_xec_check_cycles(uint32_t period_cycles, uint32_t pulse_cycles)
248 {
249 if ((period_cycles < 256U) || (period_cycles > (4096U * 256U))) {
250 return -EINVAL;
251 }
252
253 if ((pulse_cycles < 256U) || (pulse_cycles > (4096U * 256U))) {
254 return -EINVAL;
255 }
256
257 return 0;
258 }
259
pwm_bbled_xec_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)260 static int pwm_bbled_xec_set_cycles(const struct device *dev, uint32_t channel,
261 uint32_t period_cycles, uint32_t pulse_cycles,
262 pwm_flags_t flags)
263 {
264 const struct pwm_bbled_xec_config * const cfg = dev->config;
265 struct bbled_regs * const regs = cfg->regs;
266 uint32_t dc, ld;
267 int ret;
268
269 if (channel > 0) {
270 return -EIO;
271 }
272
273 if (flags) {
274 /* PWM polarity not supported (yet?) */
275 return -ENOTSUP;
276 }
277
278 if ((pulse_cycles == 0U) && (period_cycles == 0U)) { /* Controller off, clocks gated */
279 regs->config = (regs->config & ~XEC_PWM_BBLED_CFG_MODE_MSK)
280 | XEC_PWM_BBLED_CFG_MODE_OFF;
281 } else if ((pulse_cycles == 0U) && (period_cycles > 0U)) {
282 /* PWM mode: Limits minimum duty cycle == 0 -> LED output is fully OFF */
283 regs->limits &= ~XEC_PWM_BBLED_LIM_MIN_MSK;
284 } else if ((pulse_cycles > 0U) && (period_cycles == 0U)) {
285 /* PWM mode: Limits minimum duty cycle == full value -> LED output is fully ON */
286 regs->limits |= XEC_PWM_BBLED_LIM_MIN_MSK;
287 } else {
288 ret = pwm_bbled_xec_check_cycles(period_cycles, pulse_cycles);
289 if (ret) {
290 LOG_DBG("Target frequency out of range");
291 return ret;
292 }
293
294 ld = xec_pwmbb_compute_ld(dev, period_cycles);
295 dc = xec_pwmbb_compute_dc(period_cycles, pulse_cycles);
296 xec_pwmbb_progam_pwm(dev, ld, dc);
297 }
298
299 return 0;
300 }
301
302 /* API implementation: Get the clock rate (cycles per second) for a single PWM output.
303 * BBLED in PWM mode (same as blink mode) PWM frequency = Source Frequency / (256 * (LP + 1))
304 * where Source Frequency is either 48 MHz or 32768 Hz and LP is the 12-bit low delay
305 * field of the DELAY register.
306 */
pwm_bbled_xec_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)307 static int pwm_bbled_xec_get_cycles_per_sec(const struct device *dev,
308 uint32_t channel, uint64_t *cycles)
309 {
310 const struct pwm_bbled_xec_config * const cfg = dev->config;
311 struct bbled_regs * const regs = cfg->regs;
312
313 if (channel > 0) {
314 return -EIO;
315 }
316
317 if (cycles) {
318 if (regs->config & BIT(XEC_PWM_BBLED_CFG_CLK_SRC_48M_POS)) {
319 *cycles = XEC_PWM_BBLED_INPUT_FREQ_HI;
320 } else {
321 *cycles = XEC_PWM_BBLED_INPUT_FREQ_LO;
322 }
323 }
324
325 return 0;
326 }
327
328
329 #ifdef CONFIG_PM_DEVICE
pwm_bbled_xec_pm_action(const struct device * dev,enum pm_device_action action)330 static int pwm_bbled_xec_pm_action(const struct device *dev, enum pm_device_action action)
331 {
332 const struct pwm_bbled_xec_config *const devcfg = dev->config;
333 struct bbled_regs * const regs = devcfg->regs;
334 struct bbled_xec_data * const data = dev->data;
335 int ret = 0;
336
337 /* 32K core clock is not gated by PCR in sleep, so BBLED can blink the LED even
338 * in sleep, if it is configured to use 32K clock. If we want to control it
339 * we shall use flag "enable_low_power_32K".
340 * This flag dont have effect on 48M clock. Since it is gated by PCR in sleep, BBLED
341 * will not get clock during sleep.
342 */
343 if ((!devcfg->enable_low_power_32K) &&
344 (!(regs->config & BIT(XEC_PWM_BBLED_CFG_CLK_SRC_48M_POS)))) {
345 return ret;
346 }
347
348 switch (action) {
349 case PM_DEVICE_ACTION_RESUME:
350 ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
351 if (ret != 0) {
352 LOG_ERR("XEC BBLED pinctrl setup failed (%d)", ret);
353 }
354
355 /* Turn on BBLED only if it is ON before sleep */
356 if ((data->config & XEC_PWM_BBLED_CFG_MODE_MSK) != XEC_PWM_BBLED_CFG_MODE_OFF) {
357
358 regs->config |= (data->config & XEC_PWM_BBLED_CFG_MODE_MSK);
359 regs->config |= BIT(XEC_PWM_BBLED_CFG_EN_UPDATE_POS);
360
361 data->config = XEC_PWM_BBLED_CFG_MODE_OFF;
362 }
363 break;
364 case PM_DEVICE_ACTION_SUSPEND:
365 if ((regs->config & XEC_PWM_BBLED_CFG_MODE_MSK) != XEC_PWM_BBLED_CFG_MODE_OFF) {
366 /* Do copy first, then clear mode. */
367 data->config = regs->config;
368
369 regs->config &= ~(XEC_PWM_BBLED_CFG_MODE_MSK);
370 }
371
372 ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_SLEEP);
373 /* pinctrl-1 does not exist. */
374 if (ret == -ENOENT) {
375 ret = 0;
376 }
377 break;
378 default:
379 ret = -ENOTSUP;
380 }
381 return ret;
382 }
383 #endif /* CONFIG_PM_DEVICE */
384
385 static const struct pwm_driver_api pwm_bbled_xec_driver_api = {
386 .set_cycles = pwm_bbled_xec_set_cycles,
387 .get_cycles_per_sec = pwm_bbled_xec_get_cycles_per_sec,
388 };
389
pwm_bbled_xec_init(const struct device * dev)390 static int pwm_bbled_xec_init(const struct device *dev)
391 {
392 const struct pwm_bbled_xec_config * const cfg = dev->config;
393 struct bbled_regs * const regs = cfg->regs;
394 int ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
395
396 if (ret != 0) {
397 LOG_ERR("XEC PWM-BBLED pinctrl init failed (%d)", ret);
398 return ret;
399 }
400
401 /* BBLED PWM WDT is enabled by default. Disable it and select 32KHz */
402 regs->config = BIT(XEC_PWM_BBLED_CFG_RST_PWM_POS);
403 regs->config = 0U;
404 if (cfg->clk_sel == XEC_PWM_BBLED_CLKSEL_AHB_48M) {
405 regs->config |= BIT(XEC_PWM_BBLED_CFG_CLK_SRC_48M_POS);
406 }
407
408 return 0;
409 }
410
411 #define XEC_PWM_BBLED_CLKSEL(n) \
412 COND_CODE_1(DT_INST_NODE_HAS_PROP(n, clock_select), \
413 (DT_INST_ENUM_IDX(n, clock_select)), (0))
414
415 #define XEC_PWM_BBLED_CONFIG(inst) \
416 static struct pwm_bbled_xec_config pwm_bbled_xec_config_##inst = { \
417 .regs = (struct bbled_regs * const)DT_INST_REG_ADDR(inst), \
418 .girq = (uint8_t)(DT_INST_PROP_BY_IDX(0, girqs, 0)), \
419 .girq_pos = (uint8_t)(DT_INST_PROP_BY_IDX(0, girqs, 1)), \
420 .pcr_idx = (uint8_t)DT_INST_PROP_BY_IDX(inst, pcrs, 0), \
421 .pcr_pos = (uint8_t)DT_INST_PROP_BY_IDX(inst, pcrs, 1), \
422 .clk_sel = UTIL_CAT(XEC_PWM_BBLED_CLKSEL_, XEC_PWM_BBLED_CLKSEL(inst)), \
423 .enable_low_power_32K = DT_INST_PROP(inst, enable_low_power_32k),\
424 .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
425 };
426
427 #define XEC_PWM_BBLED_DEVICE_INIT(index) \
428 \
429 static struct bbled_xec_data bbled_xec_data_##index; \
430 \
431 PINCTRL_DT_INST_DEFINE(index); \
432 \
433 XEC_PWM_BBLED_CONFIG(index); \
434 \
435 PM_DEVICE_DT_INST_DEFINE(index, pwm_bbled_xec_pm_action); \
436 \
437 DEVICE_DT_INST_DEFINE(index, &pwm_bbled_xec_init, \
438 PM_DEVICE_DT_INST_GET(index), \
439 &bbled_xec_data_##index, \
440 &pwm_bbled_xec_config_##index, POST_KERNEL, \
441 CONFIG_PWM_INIT_PRIORITY, \
442 &pwm_bbled_xec_driver_api);
443
444 DT_INST_FOREACH_STATUS_OKAY(XEC_PWM_BBLED_DEVICE_INIT)
445