1 /*
2  * Copyright (c) 2021 ITE Corporation. All Rights Reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT ite_it8xxx2_pwm
8 
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/pwm.h>
11 #include <zephyr/drivers/pinctrl.h>
12 #include <zephyr/dt-bindings/pwm/it8xxx2_pwm.h>
13 #include <errno.h>
14 #include <zephyr/kernel.h>
15 #include <soc.h>
16 #include <soc_dt.h>
17 #include <stdlib.h>
18 
19 #include <zephyr/logging/log.h>
20 
21 LOG_MODULE_REGISTER(pwm_ite_it8xxx2, CONFIG_PWM_LOG_LEVEL);
22 
23 #define PWM_CTRX_MIN	100
24 #define PWM_FREQ	EC_FREQ
25 #define PCSSG_MASK	0x3
26 
27 struct pwm_it8xxx2_cfg {
28 	/* PWM channel duty cycle register */
29 	uintptr_t reg_dcr;
30 	/* PWM channel clock source selection register */
31 	uintptr_t reg_pcssg;
32 	/* PWM channel clock source gating register */
33 	uintptr_t reg_pcsgr;
34 	/* PWM channel output polarity register */
35 	uintptr_t reg_pwmpol;
36 	/* PWM channel */
37 	int channel;
38 	/* PWM prescaler control register base */
39 	struct pwm_it8xxx2_regs *base;
40 	/* Select PWM prescaler that output to PWM channel */
41 	int prs_sel;
42 	/* PWM alternate configuration */
43 	const struct pinctrl_dev_config *pcfg;
44 };
45 
46 struct pwm_it8xxx2_data {
47 	uint32_t ctr;
48 	uint32_t cxcprs;
49 	uint32_t target_freq_prev;
50 };
51 
pwm_enable(const struct device * dev,int enabled)52 static void pwm_enable(const struct device *dev, int enabled)
53 {
54 	const struct pwm_it8xxx2_cfg *config = dev->config;
55 	volatile uint8_t *reg_pcsgr = (uint8_t *)config->reg_pcsgr;
56 	int ch = config->channel;
57 
58 	if (enabled) {
59 		/* PWM channel clock source not gating */
60 		*reg_pcsgr &= ~BIT(ch);
61 	} else {
62 		/* PWM channel clock source gating */
63 		*reg_pcsgr |= BIT(ch);
64 	}
65 }
66 
pwm_it8xxx2_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)67 static int pwm_it8xxx2_get_cycles_per_sec(const struct device *dev,
68 					  uint32_t channel, uint64_t *cycles)
69 {
70 	ARG_UNUSED(channel);
71 
72 	/*
73 	 * There are three ways to call pwm_it8xxx2_set_cycles() from pwm api:
74 	 * 1) pwm_set_cycles_usec() -> pwm_set_cycles_cycles() -> pwm_it8xxx2_set_cycles()
75 	 *    target_freq = pwm_clk_src / period_cycles
76 	 *                = cycles / (period * cycles / USEC_PER_SEC)
77 	 *                = USEC_PER_SEC / period
78 	 * 2) pwm_set_cycles_nsec() -> pwm_set_cycles_cycles() -> pwm_it8xxx2_set_cycles()
79 	 *    target_freq = pwm_clk_src / period_cycles
80 	 *                = cycles / (period * cycles / NSEC_PER_SEC)
81 	 *                = NSEC_PER_SEC / period
82 	 * 3) pwm_set_cycles_cycles() -> pwm_it8xxx2_set_cycles()
83 	 *    target_freq = pwm_clk_src / period_cycles
84 	 *                = cycles / period
85 	 *
86 	 * If we need to pwm output in EC power saving mode, then we will switch
87 	 * the prescaler clock source (cycles) from 8MHz to 32.768kHz. In order
88 	 * to get the same target_freq in the 3) case, we always return PWM_FREQ.
89 	 */
90 	*cycles = (uint64_t) PWM_FREQ;
91 
92 	return 0;
93 }
94 
pwm_it8xxx2_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)95 static int pwm_it8xxx2_set_cycles(const struct device *dev,
96 				  uint32_t channel, uint32_t period_cycles,
97 				  uint32_t pulse_cycles, pwm_flags_t flags)
98 {
99 	const struct pwm_it8xxx2_cfg *config = dev->config;
100 	struct pwm_it8xxx2_regs *const inst = config->base;
101 	struct pwm_it8xxx2_data *data = dev->data;
102 	volatile uint8_t *reg_dcr = (uint8_t *)config->reg_dcr;
103 	volatile uint8_t *reg_pwmpol = (uint8_t *)config->reg_pwmpol;
104 	int ch = config->channel;
105 	int prs_sel = config->prs_sel;
106 	uint32_t actual_freq = 0xffffffff, target_freq, deviation;
107 	uint64_t pwm_clk_src;
108 
109 	/* Select PWM inverted polarity (ex. active-low pulse) */
110 	if (flags & PWM_POLARITY_INVERTED) {
111 		*reg_pwmpol |= BIT(ch);
112 	} else {
113 		*reg_pwmpol &= ~BIT(ch);
114 	}
115 
116 	/* Enable PWM output open-drain */
117 	if (flags & PWM_IT8XXX2_OPEN_DRAIN) {
118 		inst->PWMODENR |= BIT(ch);
119 	}
120 
121 	/* If pulse cycles is 0, set duty cycle 0 and enable pwm channel */
122 	if (pulse_cycles == 0) {
123 		*reg_dcr = 0;
124 		pwm_enable(dev, 1);
125 		return 0;
126 	}
127 
128 	pwm_it8xxx2_get_cycles_per_sec(dev, channel, &pwm_clk_src);
129 	target_freq = ((uint32_t) pwm_clk_src) / period_cycles;
130 
131 	/*
132 	 * Support PWM output frequency:
133 	 * 1) 8MHz clock source: 1Hz <= target_freq <= 79207Hz
134 	 * 2) 32.768KHz clock source: 1Hz <= target_freq <= 324Hz
135 	 * NOTE: PWM output signal maximum supported frequency comes from
136 	 *       [8MHz or 32.768KHz] / 1 / (PWM_CTRX_MIN + 1).
137 	 *       PWM output signal minimum supported frequency comes from
138 	 *       [8MHz or 32.768KHz] / 65536 / 256, the minimum integer is 1.
139 	 */
140 	if (target_freq < 1) {
141 		LOG_ERR("PWM output frequency is < 1 !");
142 		return -EINVAL;
143 	}
144 
145 	deviation = (target_freq / 100) + 1;
146 
147 	/*
148 	 * Default clock source setting is 8MHz, when ITE chip is in power
149 	 * saving mode, clock source 8MHz will be gated (32.768KHz won't).
150 	 * So if we still need pwm output in mode, then we should set frequency
151 	 * <=324Hz in board dts. Now change prescaler clock source from 8MHz to
152 	 * 32.768KHz to support pwm output in mode.
153 	 */
154 	if (target_freq <= 324) {
155 		if (inst->PCFSR & BIT(prs_sel)) {
156 			inst->PCFSR &= ~BIT(prs_sel);
157 		}
158 
159 		pwm_clk_src = (uint64_t) 32768;
160 	}
161 
162 	/*
163 	 * PWM output signal frequency is
164 	 * pwm_clk_src / ((CxCPRS[15:0] + 1) * (CTRx[7:0] + 1))
165 	 * NOTE: 1) define CTR minimum is 100 for more precisely when
166 	 *          calculate DCR
167 	 *       2) CxCPRS[15:0] value 0001h results in a divisor 2
168 	 *          CxCPRS[15:0] value FFFFh results in a divisor 65536
169 	 *          CTRx[7:0] value 00h results in a divisor 1
170 	 *          CTRx[7:0] value FFh results in a divisor 256
171 	 */
172 	if (target_freq != data->target_freq_prev) {
173 		uint32_t ctr, cxcprs;
174 
175 		for (ctr = 0xFF; ctr >= PWM_CTRX_MIN; ctr--) {
176 			cxcprs = (((uint32_t) pwm_clk_src) / (ctr + 1) / target_freq);
177 			/*
178 			 * Make sure cxcprs isn't zero, or we will have
179 			 * divide-by-zero on calculating actual_freq.
180 			 */
181 			if (cxcprs != 0) {
182 				actual_freq = ((uint32_t) pwm_clk_src) / (ctr + 1) / cxcprs;
183 				if (abs(actual_freq - target_freq) < deviation) {
184 					/* CxCPRS[15:0] = cxcprs - 1 */
185 					cxcprs--;
186 					break;
187 				}
188 			}
189 		}
190 
191 		if (cxcprs > UINT16_MAX) {
192 			LOG_ERR("PWM prescaler CxCPRS only support 2 bytes !");
193 			return -EINVAL;
194 		}
195 
196 		/* Store ctr and cxcprs with successful frequency change */
197 		data->ctr = ctr;
198 		data->cxcprs = cxcprs;
199 	}
200 
201 	/* Set PWM prescaler clock divide and cycle time register */
202 	if (prs_sel == PWM_PRESCALER_C4) {
203 		inst->C4CPRS = data->cxcprs & 0xFF;
204 		inst->C4MCPRS = (data->cxcprs >> 8) & 0xFF;
205 		inst->CTR1 = data->ctr;
206 	} else if (prs_sel == PWM_PRESCALER_C6) {
207 		inst->C6CPRS = data->cxcprs & 0xFF;
208 		inst->C6MCPRS = (data->cxcprs >> 8) & 0xFF;
209 		inst->CTR2 = data->ctr;
210 	} else if (prs_sel == PWM_PRESCALER_C7) {
211 		inst->C7CPRS = data->cxcprs & 0xFF;
212 		inst->C7MCPRS = (data->cxcprs >> 8) & 0xFF;
213 		inst->CTR3 = data->ctr;
214 	}
215 
216 	/* Set PWM channel duty cycle register */
217 	*reg_dcr = (data->ctr * pulse_cycles) / period_cycles;
218 
219 	/* PWM channel clock source not gating */
220 	pwm_enable(dev, 1);
221 
222 	/* Store the frequency to be compared */
223 	data->target_freq_prev = target_freq;
224 
225 	LOG_DBG("clock source freq %d, target freq %d",
226 		(uint32_t) pwm_clk_src, target_freq);
227 
228 	return 0;
229 }
230 
pwm_it8xxx2_init(const struct device * dev)231 static int pwm_it8xxx2_init(const struct device *dev)
232 {
233 	const struct pwm_it8xxx2_cfg *config = dev->config;
234 	struct pwm_it8xxx2_regs *const inst = config->base;
235 	volatile uint8_t *reg_pcssg = (uint8_t *)config->reg_pcssg;
236 	int ch = config->channel;
237 	int prs_sel = config->prs_sel;
238 	int pcssg_shift;
239 	int pcssg_mask;
240 	int status;
241 
242 	/* PWM channel clock source gating before configuring */
243 	pwm_enable(dev, 0);
244 
245 	/* Select clock source 8MHz for prescaler */
246 	inst->PCFSR |= BIT(prs_sel);
247 
248 	/* Bit shift and mask of prescaler clock source select group register */
249 	pcssg_shift = (ch % 4) * 2;
250 	pcssg_mask = (prs_sel & PCSSG_MASK) << pcssg_shift;
251 
252 	/* Select which prescaler output to PWM channel */
253 	*reg_pcssg &= ~(PCSSG_MASK << pcssg_shift);
254 	*reg_pcssg |= pcssg_mask;
255 
256 	/*
257 	 * The cycle timer1 of it8320 later series was enhanced from
258 	 * 8bits to 10bits resolution, and others are still 8bit resolution.
259 	 * Because the cycle timer1 high byte default value is not zero,
260 	 * we clear cycle timer1 high byte at init and use it as 8-bit
261 	 * resolution like others.
262 	 */
263 	inst->CTR1M = 0;
264 
265 	/* Enable PWMs clock counter */
266 	inst->ZTIER |= IT8XXX2_PWM_PCCE;
267 
268 	/* Set alternate mode of PWM pin */
269 	status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
270 	if (status < 0) {
271 		LOG_ERR("Failed to configure PWM pins");
272 		return status;
273 	}
274 
275 	return 0;
276 }
277 
278 static DEVICE_API(pwm, pwm_it8xxx2_api) = {
279 	.set_cycles = pwm_it8xxx2_set_cycles,
280 	.get_cycles_per_sec = pwm_it8xxx2_get_cycles_per_sec,
281 };
282 
283 /* Device Instance */
284 #define PWM_IT8XXX2_INIT(inst)								\
285 	PINCTRL_DT_INST_DEFINE(inst);							\
286 											\
287 	static const struct pwm_it8xxx2_cfg pwm_it8xxx2_cfg_##inst = {			\
288 		.reg_dcr = DT_INST_REG_ADDR_BY_IDX(inst, 0),				\
289 		.reg_pcssg = DT_INST_REG_ADDR_BY_IDX(inst, 1),				\
290 		.reg_pcsgr = DT_INST_REG_ADDR_BY_IDX(inst, 2),				\
291 		.reg_pwmpol = DT_INST_REG_ADDR_BY_IDX(inst, 3),				\
292 		.channel = DT_PROP(DT_INST(inst, ite_it8xxx2_pwm), channel),		\
293 		.base = (struct pwm_it8xxx2_regs *) DT_REG_ADDR(DT_NODELABEL(prs)),	\
294 		.prs_sel = DT_PROP(DT_INST(inst, ite_it8xxx2_pwm), prescaler_cx),	\
295 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),				\
296 	};										\
297 											\
298 	static struct pwm_it8xxx2_data pwm_it8xxx2_data_##inst;                         \
299 											\
300 	DEVICE_DT_INST_DEFINE(inst,							\
301 			      &pwm_it8xxx2_init,					\
302 			      NULL,							\
303 			      &pwm_it8xxx2_data_##inst,					\
304 			      &pwm_it8xxx2_cfg_##inst,					\
305 			      PRE_KERNEL_1,						\
306 			      CONFIG_PWM_INIT_PRIORITY,					\
307 			      &pwm_it8xxx2_api);
308 
309 DT_INST_FOREACH_STATUS_OKAY(PWM_IT8XXX2_INIT)
310