1 /*
2  * Copyright (c) 2019 Intel Corporation
3  * Copyright (c) 2022 Microchip Technololgy Inc.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT microchip_xec_pwm
9 
10 #include <errno.h>
11 #include <stdlib.h>
12 #include <stdint.h>
13 
14 #include <zephyr/device.h>
15 #include <zephyr/drivers/pwm.h>
16 #ifdef CONFIG_SOC_SERIES_MEC172X
17 #include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
18 #include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
19 #endif
20 #include <zephyr/drivers/pinctrl.h>
21 #include <zephyr/logging/log.h>
22 #include <zephyr/pm/device.h>
23 
24 #include <soc.h>
25 
26 LOG_MODULE_REGISTER(pwm_mchp_xec, CONFIG_PWM_LOG_LEVEL);
27 
28 /* Minimal on/off are 1 & 1 both are incremented, so 4.
29  * 0 cannot be set (used for full low/high output) so a
30  * combination of on_off of 2 is not possible.
31  */
32 #define XEC_PWM_LOWEST_ON_OFF	4U
33 /* Maximal on/off are UINT16_T, both are incremented.
34  * Multiplied by the highest divider: 16
35  */
36 #define XEC_PWM_HIGHEST_ON_OFF	(2U * (UINT16_MAX + 1U) * 16U)
37 
38 #define XEC_PWM_MIN_HIGH_CLK_FREQ			\
39 	(MCHP_PWM_INPUT_FREQ_HI / XEC_PWM_HIGHEST_ON_OFF)
40 #define XEC_PWM_MAX_LOW_CLK_FREQ			\
41 	(MCHP_PWM_INPUT_FREQ_LO / XEC_PWM_LOWEST_ON_OFF)
42 /* Precision factor for frequency calculation
43  * To mitigate frequency comparision up to the first digit after 0.
44  */
45 #define XEC_PWM_FREQ_PF		10U
46 /* Precision factor for DC calculation
47  * To avoid losing some digits after 0.
48  */
49 #define XEC_PWM_DC_PF		100000U
50 /* Lowest reachable frequency */
51 #define XEC_PWM_FREQ_LIMIT	1 /* 0.1hz * XEC_PWM_FREQ_PF */
52 
53 struct pwm_xec_config {
54 	struct pwm_regs * const regs;
55 	uint8_t pcr_idx;
56 	uint8_t pcr_pos;
57 	const struct pinctrl_dev_config *pcfg;
58 };
59 
60 struct xec_params {
61 	uint32_t on;
62 	uint32_t off;
63 	uint8_t div;
64 };
65 
66 struct pwm_xec_data {
67 	uint32_t config;
68 };
69 
70 #define NUM_DIV_ELEMS		16
71 
72 static const uint32_t max_freq_high_on_div[NUM_DIV_ELEMS] = {
73 	48000000,
74 	24000000,
75 	16000000,
76 	12000000,
77 	9600000,
78 	8000000,
79 	6857142,
80 	6000000,
81 	5333333,
82 	4800000,
83 	4363636,
84 	4000000,
85 	3692307,
86 	3428571,
87 	3200000,
88 	3000000,
89 };
90 
91 static const uint32_t max_freq_low_on_div[NUM_DIV_ELEMS] = {
92 	100000,
93 	50000,
94 	33333,
95 	25000,
96 	20000,
97 	16666,
98 	14285,
99 	12500,
100 	11111,
101 	10000,
102 	9090,
103 	8333,
104 	7692,
105 	7142,
106 	6666,
107 	6250,
108 };
109 
xec_compute_frequency(uint32_t clk,uint32_t on,uint32_t off)110 static uint32_t xec_compute_frequency(uint32_t clk, uint32_t on, uint32_t off)
111 {
112 	return ((clk * XEC_PWM_FREQ_PF)/((on + 1) + (off + 1)));
113 }
114 
xec_select_div(uint32_t freq,const uint32_t max_freq[16])115 static uint16_t xec_select_div(uint32_t freq, const uint32_t max_freq[16])
116 {
117 	uint8_t i;
118 
119 	if (freq >= max_freq[3]) {
120 		return 0;
121 	}
122 
123 	freq *= XEC_PWM_LOWEST_ON_OFF;
124 
125 	for (i = 0; i < 15; i++) {
126 		if (freq >= max_freq[i]) {
127 			break;
128 		}
129 	}
130 
131 	return i;
132 }
133 
xec_compute_on_off(uint32_t freq,uint32_t dc,uint32_t clk,uint32_t * on,uint32_t * off)134 static void xec_compute_on_off(uint32_t freq, uint32_t dc, uint32_t clk,
135 			       uint32_t *on, uint32_t *off)
136 {
137 	uint64_t on_off;
138 
139 	on_off = (clk * 10) / freq;
140 
141 	*on = ((on_off * dc) / XEC_PWM_DC_PF) - 1;
142 	*off = on_off - *on - 2;
143 }
144 
xec_compute_dc(uint32_t on,uint32_t off)145 static uint32_t xec_compute_dc(uint32_t on, uint32_t off)
146 {
147 	int dc = (on + 1) + (off + 1);
148 
149 	/* Make calculation in uint64_t since XEC_PWM_DC_PF is large */
150 	dc = (((uint64_t)(on + 1) * XEC_PWM_DC_PF) / dc);
151 
152 	return (uint32_t)dc;
153 }
154 
xec_compare_div_on_off(uint32_t target_freq,uint32_t dc,const uint32_t max_freq[16],uint8_t div_a,uint8_t div_b,uint32_t * on_a,uint32_t * off_a)155 static uint16_t xec_compare_div_on_off(uint32_t target_freq, uint32_t dc,
156 				       const uint32_t max_freq[16],
157 				       uint8_t div_a, uint8_t div_b,
158 				       uint32_t *on_a, uint32_t *off_a)
159 {
160 	uint32_t freq_a, freq_b, on_b, off_b;
161 
162 	xec_compute_on_off(target_freq, dc, max_freq[div_a],
163 			   on_a, off_a);
164 
165 	freq_a = xec_compute_frequency(max_freq[div_a], *on_a, *off_a);
166 
167 	xec_compute_on_off(target_freq, dc, max_freq[div_b],
168 			   &on_b, &off_b);
169 
170 	freq_b = xec_compute_frequency(max_freq[div_b], on_b, off_b);
171 
172 	if ((target_freq - freq_a) < (target_freq - freq_b)) {
173 		if ((*on_a <= UINT16_MAX) && (*off_a <= UINT16_MAX)) {
174 			return div_a;
175 		}
176 	}
177 
178 	if ((on_b <= UINT16_MAX) && (off_b <= UINT16_MAX)) {
179 		*on_a = on_b;
180 		*off_a = off_b;
181 
182 		return div_b;
183 	}
184 
185 	return div_a;
186 }
187 
xec_select_best_div_on_off(uint32_t target_freq,uint32_t dc,const uint32_t max_freq[16],uint32_t * on,uint32_t * off)188 static uint8_t xec_select_best_div_on_off(uint32_t target_freq, uint32_t dc,
189 					  const uint32_t max_freq[16],
190 					  uint32_t *on, uint32_t *off)
191 {
192 	int div_comp;
193 	uint8_t div;
194 
195 	div = xec_select_div(target_freq, max_freq);
196 
197 	for (div_comp = (int)div - 1; div_comp >= 0; div_comp--) {
198 		div = xec_compare_div_on_off(target_freq, dc, max_freq,
199 					     div, div_comp, on, off);
200 	}
201 
202 	return div;
203 }
204 
xec_compare_params(uint32_t target_freq,struct xec_params * hc_params,struct xec_params * lc_params)205 static struct xec_params *xec_compare_params(uint32_t target_freq,
206 					     struct xec_params *hc_params,
207 					     struct xec_params *lc_params)
208 {
209 	struct xec_params *params;
210 	uint32_t freq_h = 0;
211 	uint32_t freq_l = 0;
212 
213 	if (hc_params->div < NUM_DIV_ELEMS) {
214 		freq_h = xec_compute_frequency(
215 				max_freq_high_on_div[hc_params->div],
216 				hc_params->on,
217 				hc_params->off);
218 	}
219 
220 	if (lc_params->div < NUM_DIV_ELEMS) {
221 		freq_l = xec_compute_frequency(
222 				max_freq_low_on_div[lc_params->div],
223 				lc_params->on,
224 				lc_params->off);
225 	}
226 
227 	if (abs((int)target_freq - (int)freq_h) <
228 	    abs((int)target_freq - (int)freq_l)) {
229 		params = hc_params;
230 	} else {
231 		params = lc_params;
232 	}
233 
234 	LOG_DBG("\tFrequency (x%u): %u", XEC_PWM_FREQ_PF, freq_h);
235 	LOG_DBG("\tOn %s clock, ON %u OFF %u DIV %u",
236 		params == hc_params ? "High" : "Low",
237 		params->on, params->off, params->div);
238 
239 	return params;
240 }
241 
xec_compute_and_set_parameters(const struct device * dev,uint32_t target_freq,uint32_t on,uint32_t off)242 static void xec_compute_and_set_parameters(const struct device *dev,
243 					   uint32_t target_freq,
244 					   uint32_t on, uint32_t off)
245 {
246 	const struct pwm_xec_config * const cfg = dev->config;
247 	struct pwm_regs * const regs = cfg->regs;
248 	bool compute_high, compute_low;
249 	struct xec_params hc_params;
250 	struct xec_params lc_params;
251 	struct xec_params *params;
252 	uint32_t dc, cfgval;
253 
254 	dc = xec_compute_dc(on, off);
255 
256 	compute_high = (target_freq >= XEC_PWM_MIN_HIGH_CLK_FREQ);
257 	compute_low = (target_freq <= XEC_PWM_MAX_LOW_CLK_FREQ);
258 
259 	LOG_DBG("Target freq (x%u): %u and DC %u per-cent",
260 		XEC_PWM_FREQ_PF, target_freq, (dc / 1000));
261 
262 	if (compute_high) {
263 		if (!compute_low
264 		    && (on <= UINT16_MAX)
265 		    && (off <= UINT16_MAX)) {
266 			hc_params.on = on;
267 			hc_params.off = off;
268 			hc_params.div = 0;
269 			lc_params.div = UINT8_MAX;
270 
271 			goto done;
272 		}
273 
274 		hc_params.div = xec_select_best_div_on_off(
275 					target_freq, dc,
276 					max_freq_high_on_div,
277 					&hc_params.on,
278 					&hc_params.off);
279 		LOG_DBG("Best div high: %u (on/off: %u/%u)",
280 			hc_params.div, hc_params.on, hc_params.off);
281 	} else {
282 		hc_params.div = UINT8_MAX;
283 	}
284 
285 	if (compute_low) {
286 		lc_params.div = xec_select_best_div_on_off(
287 					target_freq, dc,
288 					max_freq_low_on_div,
289 					&lc_params.on,
290 					&lc_params.off);
291 		LOG_DBG("Best div low: %u (on/off: %u/%u)",
292 			lc_params.div, lc_params.on, lc_params.off);
293 	} else {
294 		lc_params.div = UINT8_MAX;
295 	}
296 done:
297 	regs->CONFIG &= ~MCHP_PWM_CFG_ENABLE;
298 
299 	cfgval = regs->CONFIG;
300 
301 	params = xec_compare_params(target_freq, &hc_params, &lc_params);
302 	if (params == &hc_params) {
303 		cfgval &= ~MCHP_PWM_CFG_CLK_SEL_100K;
304 	} else {
305 		cfgval |= MCHP_PWM_CFG_CLK_SEL_100K;
306 	}
307 
308 	regs->COUNT_ON = params->on;
309 	regs->COUNT_OFF = params->off;
310 	cfgval &= ~MCHP_PWM_CFG_CLK_PRE_DIV(0xF);
311 	cfgval |= MCHP_PWM_CFG_CLK_PRE_DIV(params->div);
312 	cfgval |= MCHP_PWM_CFG_ENABLE;
313 
314 	regs->CONFIG = cfgval;
315 }
316 
pwm_xec_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)317 static int pwm_xec_set_cycles(const struct device *dev, uint32_t channel,
318 			      uint32_t period_cycles, uint32_t pulse_cycles,
319 			      pwm_flags_t flags)
320 {
321 	const struct pwm_xec_config * const cfg = dev->config;
322 	struct pwm_regs * const regs = cfg->regs;
323 	uint32_t target_freq;
324 	uint32_t on, off;
325 
326 	if (channel > 0) {
327 		return -EIO;
328 	}
329 
330 	if (flags & PWM_POLARITY_INVERTED) {
331 		regs->CONFIG |= MCHP_PWM_CFG_ON_POL_LO;
332 	}
333 
334 	on = pulse_cycles;
335 	off = period_cycles - pulse_cycles;
336 
337 	target_freq = xec_compute_frequency(MCHP_PWM_INPUT_FREQ_HI, on, off);
338 	if (target_freq < XEC_PWM_FREQ_LIMIT) {
339 		LOG_DBG("Target frequency below limit");
340 		return -EINVAL;
341 	}
342 
343 	if ((pulse_cycles == 0U) && (period_cycles == 0U)) {
344 		regs->CONFIG &= ~MCHP_PWM_CFG_ENABLE;
345 	} else if ((pulse_cycles == 0U) && (period_cycles > 0U)) {
346 		regs->COUNT_ON = 0;
347 		regs->COUNT_OFF = 1;
348 	} else if ((pulse_cycles > 0U) && (period_cycles == 0U)) {
349 		regs->COUNT_ON = 1;
350 		regs->COUNT_OFF = 0;
351 	} else {
352 		xec_compute_and_set_parameters(dev, target_freq, on, off);
353 	}
354 
355 	return 0;
356 }
357 
pwm_xec_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)358 static int pwm_xec_get_cycles_per_sec(const struct device *dev,
359 				      uint32_t channel, uint64_t *cycles)
360 {
361 	ARG_UNUSED(dev);
362 
363 	if (channel > 0) {
364 		return -EIO;
365 	}
366 
367 	if (cycles) {
368 		/* User does not have to know about lowest clock,
369 		 * the driver will select the most relevant one.
370 		 */
371 		*cycles = MCHP_PWM_INPUT_FREQ_HI;
372 	}
373 
374 	return 0;
375 }
376 
377 #ifdef CONFIG_PM_DEVICE
pwm_xec_pm_action(const struct device * dev,enum pm_device_action action)378 static int pwm_xec_pm_action(const struct device *dev, enum pm_device_action action)
379 {
380 	const struct pwm_xec_config *const devcfg = dev->config;
381 	struct pwm_regs * const regs = devcfg->regs;
382 	struct pwm_xec_data * const data = dev->data;
383 	int ret = 0;
384 
385 	switch (action) {
386 	case PM_DEVICE_ACTION_RESUME:
387 		ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_DEFAULT);
388 		if (ret != 0) {
389 			LOG_ERR("XEC PWM pinctrl setup failed (%d)", ret);
390 		}
391 
392 		/* Turn on PWM only if it is ON before sleep */
393 		if ((data->config & MCHP_PWM_CFG_ENABLE) == MCHP_PWM_CFG_ENABLE) {
394 
395 			regs->CONFIG |= MCHP_PWM_CFG_ENABLE;
396 
397 			data->config &= (~MCHP_PWM_CFG_ENABLE);
398 		}
399 	break;
400 	case PM_DEVICE_ACTION_SUSPEND:
401 		if ((regs->CONFIG & MCHP_PWM_CFG_ENABLE) == MCHP_PWM_CFG_ENABLE) {
402 			/* Do copy first, then clear mode. */
403 			data->config = regs->CONFIG;
404 
405 			regs->CONFIG &= ~(MCHP_PWM_CFG_ENABLE);
406 		}
407 
408 		ret = pinctrl_apply_state(devcfg->pcfg, PINCTRL_STATE_SLEEP);
409 		/* pinctrl-1 does not exist. */
410 		if (ret == -ENOENT) {
411 			ret = 0;
412 		}
413 	break;
414 	default:
415 		ret = -ENOTSUP;
416 	}
417 	return ret;
418 }
419 #endif /* CONFIG_PM_DEVICE */
420 
421 static DEVICE_API(pwm, pwm_xec_driver_api) = {
422 	.set_cycles = pwm_xec_set_cycles,
423 	.get_cycles_per_sec = pwm_xec_get_cycles_per_sec,
424 };
425 
pwm_xec_init(const struct device * dev)426 static int pwm_xec_init(const struct device *dev)
427 {
428 	const struct pwm_xec_config * const cfg = dev->config;
429 	int ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
430 
431 	if (ret != 0) {
432 		LOG_ERR("XEC PWM pinctrl init failed (%d)", ret);
433 		return ret;
434 	}
435 
436 	return 0;
437 }
438 
439 #define XEC_PWM_CONFIG(inst)							\
440 	static struct pwm_xec_config pwm_xec_config_##inst = {			\
441 		.regs = (struct pwm_regs * const)DT_INST_REG_ADDR(inst),	\
442 		.pcr_idx = (uint8_t)DT_INST_PROP_BY_IDX(inst, pcrs, 0),		\
443 		.pcr_pos = (uint8_t)DT_INST_PROP_BY_IDX(inst, pcrs, 1),		\
444 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),			\
445 	};
446 
447 #define XEC_PWM_DEVICE_INIT(index)					\
448 									\
449 	static struct pwm_xec_data pwm_xec_data_##index;		\
450 									\
451 	PINCTRL_DT_INST_DEFINE(index);					\
452 									\
453 	XEC_PWM_CONFIG(index);						\
454 									\
455 	PM_DEVICE_DT_INST_DEFINE(index, pwm_xec_pm_action);		\
456 									\
457 	DEVICE_DT_INST_DEFINE(index, &pwm_xec_init,			\
458 			      PM_DEVICE_DT_INST_GET(index),		\
459 			      &pwm_xec_data_##index,			\
460 			      &pwm_xec_config_##index, POST_KERNEL,	\
461 			      CONFIG_PWM_INIT_PRIORITY,			\
462 			      &pwm_xec_driver_api);
463 
464 DT_INST_FOREACH_STATUS_OKAY(XEC_PWM_DEVICE_INIT)
465