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