1 /*
2  * Copyright (c) 2020 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_npcx_pwm
8 
9 #include <zephyr/drivers/pinctrl.h>
10 #include <zephyr/drivers/pwm.h>
11 #include <zephyr/dt-bindings/clock/npcx_clock.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/kernel.h>
14 #include <soc.h>
15 
16 #include <zephyr/logging/log.h>
17 
18 LOG_MODULE_REGISTER(pwm_npcx, CONFIG_PWM_LOG_LEVEL);
19 
20 /* 16-bit period cycles/prescaler in NPCX PWM modules */
21 #define NPCX_PWM_MAX_PRESCALER      (1UL << (16))
22 #define NPCX_PWM_MAX_PERIOD_CYCLES  (1UL << (16))
23 
24 /* PWM clock sources */
25 #define NPCX_PWM_CLOCK_APB2_LFCLK   0
26 #define NPCX_PWM_CLOCK_FX           1
27 #define NPCX_PWM_CLOCK_FR           2
28 #define NPCX_PWM_CLOCK_RESERVED     3
29 
30 /* PWM heart-beat mode selection */
31 #define NPCX_PWM_HBM_NORMAL         0
32 #define NPCX_PWM_HBM_25             1
33 #define NPCX_PWM_HBM_50             2
34 #define NPCX_PWM_HBM_100            3
35 
36 /* Device config */
37 struct pwm_npcx_config {
38 	/* pwm controller base address */
39 	struct pwm_reg *base;
40 	/* clock configuration */
41 	struct npcx_clk_cfg clk_cfg;
42 	/* pinmux configuration */
43 	const struct pinctrl_dev_config *pcfg;
44 };
45 
46 /* Driver data */
47 struct pwm_npcx_data {
48 	/* PWM cycles per second */
49 	uint32_t cycles_per_sec;
50 };
51 
52 /* PWM local functions */
pwm_npcx_configure(const struct device * dev,int clk_bus)53 static void pwm_npcx_configure(const struct device *dev, int clk_bus)
54 {
55 	const struct pwm_npcx_config *config = dev->config;
56 	struct pwm_reg *inst = config->base;
57 
58 	/* Disable PWM for module configuration first */
59 	inst->PWMCTL &= ~BIT(NPCX_PWMCTL_PWR);
60 
61 	/* Set default PWM polarity to normal */
62 	inst->PWMCTL &= ~BIT(NPCX_PWMCTL_INVP);
63 
64 	/* Turn off PWM heart-beat mode */
65 	SET_FIELD(inst->PWMCTL, NPCX_PWMCTL_HB_DC_CTL_FIELD,
66 			NPCX_PWM_HBM_NORMAL);
67 
68 	/* Select APB CLK/LFCLK clock sources to PWM module by default */
69 	SET_FIELD(inst->PWMCTLEX, NPCX_PWMCTLEX_FCK_SEL_FIELD,
70 			NPCX_PWM_CLOCK_APB2_LFCLK);
71 
72 	/* Select clock source to LFCLK by flag, otherwise APB clock source */
73 	if (clk_bus == NPCX_CLOCK_BUS_LFCLK) {
74 		inst->PWMCTL |= BIT(NPCX_PWMCTL_CKSEL);
75 	} else {
76 		inst->PWMCTL &= ~BIT(NPCX_PWMCTL_CKSEL);
77 	}
78 }
79 
80 /* PWM api functions */
pwm_npcx_set_cycles(const struct device * dev,uint32_t channel,uint32_t period_cycles,uint32_t pulse_cycles,pwm_flags_t flags)81 static int pwm_npcx_set_cycles(const struct device *dev, uint32_t channel,
82 			       uint32_t period_cycles, uint32_t pulse_cycles,
83 			       pwm_flags_t flags)
84 {
85 	/* Single channel for each pwm device */
86 	ARG_UNUSED(channel);
87 	const struct pwm_npcx_config *config = dev->config;
88 	struct pwm_npcx_data *const data = dev->data;
89 	struct pwm_reg *inst = config->base;
90 	int prescaler;
91 	uint32_t ctl;
92 	uint32_t ctr;
93 	uint32_t dcr;
94 	uint32_t prsc;
95 
96 	ctl = inst->PWMCTL | BIT(NPCX_PWMCTL_PWR);
97 
98 	/* Select PWM inverted polarity (ie. active-low pulse). */
99 	if (flags & PWM_POLARITY_INVERTED) {
100 		ctl |= BIT(NPCX_PWMCTL_INVP);
101 	} else {
102 		ctl &= ~BIT(NPCX_PWMCTL_INVP);
103 	}
104 
105 	/* If pulse_cycles is 0, switch PWM off and return. */
106 	if (pulse_cycles == 0) {
107 		ctl &= ~BIT(NPCX_PWMCTL_PWR);
108 		inst->PWMCTL = ctl;
109 		return 0;
110 	}
111 
112 	/*
113 	 * Calculate PWM prescaler that let period_cycles map to
114 	 * maximum pwm period cycles and won't exceed it.
115 	 * Then prescaler = ceil (period_cycles / pwm_max_period_cycles)
116 	 */
117 	prescaler = DIV_ROUND_UP(period_cycles, NPCX_PWM_MAX_PERIOD_CYCLES);
118 	if (prescaler > NPCX_PWM_MAX_PRESCALER) {
119 		return -EINVAL;
120 	}
121 
122 	/* Set PWM prescaler. */
123 	prsc = prescaler - 1;
124 
125 	/* Set PWM period cycles. */
126 	ctr = (period_cycles / prescaler) - 1;
127 
128 	/* Set PWM pulse cycles. */
129 	dcr = (pulse_cycles / prescaler) - 1;
130 
131 	LOG_DBG("freq %d, pre %d, period %d, pulse %d",
132 		data->cycles_per_sec / period_cycles, prsc, ctr, dcr);
133 
134 	/* Reconfigure only if necessary. */
135 	if (inst->PWMCTL != ctl || inst->PRSC != prsc || inst->CTR != ctr) {
136 		/* Disable PWM before configuring. */
137 		inst->PWMCTL &= ~BIT(NPCX_PWMCTL_PWR);
138 
139 		inst->PRSC = prsc;
140 		inst->CTR = ctr;
141 		inst->DCR = dcr;
142 
143 		/* Enable PWM now. */
144 		inst->PWMCTL = ctl;
145 
146 		return 0;
147 	}
148 
149 	inst->DCR = dcr;
150 
151 	return 0;
152 }
153 
pwm_npcx_get_cycles_per_sec(const struct device * dev,uint32_t channel,uint64_t * cycles)154 static int pwm_npcx_get_cycles_per_sec(const struct device *dev,
155 				       uint32_t channel, uint64_t *cycles)
156 {
157 	/* Single channel for each pwm device */
158 	ARG_UNUSED(channel);
159 	struct pwm_npcx_data *const data = dev->data;
160 
161 	*cycles = data->cycles_per_sec;
162 	return 0;
163 }
164 
165 /* PWM driver registration */
166 static DEVICE_API(pwm, pwm_npcx_driver_api) = {
167 	.set_cycles = pwm_npcx_set_cycles,
168 	.get_cycles_per_sec = pwm_npcx_get_cycles_per_sec
169 };
170 
pwm_npcx_init(const struct device * dev)171 static int pwm_npcx_init(const struct device *dev)
172 {
173 	const struct pwm_npcx_config *const config = dev->config;
174 	struct pwm_npcx_data *const data = dev->data;
175 	struct pwm_reg *const inst = config->base;
176 	const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
177 	int ret;
178 
179 	/*
180 	 * NPCX PWM module mixes byte and word registers together. Make sure
181 	 * word reg access via structure won't break into two byte reg accesses
182 	 * unexpectedly by toolchains options or attributes. If so, stall here.
183 	 */
184 	NPCX_REG_WORD_ACCESS_CHECK(inst->PRSC, 0xA55A);
185 
186 
187 	if (!device_is_ready(clk_dev)) {
188 		LOG_ERR("clock control device not ready");
189 		return -ENODEV;
190 	}
191 
192 	/* Turn on device clock first and get source clock freq. */
193 	ret = clock_control_on(clk_dev, (clock_control_subsys_t)
194 							&config->clk_cfg);
195 	if (ret < 0) {
196 		LOG_ERR("Turn on PWM clock fail %d", ret);
197 		return ret;
198 	}
199 
200 	ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t)
201 			&config->clk_cfg, &data->cycles_per_sec);
202 	if (ret < 0) {
203 		LOG_ERR("Get PWM clock rate error %d", ret);
204 		return ret;
205 	}
206 
207 	/* Configure PWM device initially */
208 	pwm_npcx_configure(dev, config->clk_cfg.bus);
209 
210 	/* Configure pin-mux for PWM device */
211 	ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
212 	if (ret < 0) {
213 		LOG_ERR("PWM pinctrl setup failed (%d)", ret);
214 		return ret;
215 	}
216 
217 	return 0;
218 }
219 
220 #define NPCX_PWM_INIT(inst)                                                    \
221 	PINCTRL_DT_INST_DEFINE(inst);					       \
222 									       \
223 	static const struct pwm_npcx_config pwm_npcx_cfg_##inst = {            \
224 		.base = (struct pwm_reg *)DT_INST_REG_ADDR(inst),              \
225 		.clk_cfg = NPCX_DT_CLK_CFG_ITEM(inst),                         \
226 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                  \
227 	};                                                                     \
228 									       \
229 	static struct pwm_npcx_data pwm_npcx_data_##inst;                      \
230 									       \
231 	DEVICE_DT_INST_DEFINE(inst,					       \
232 			    &pwm_npcx_init, NULL,			       \
233 			    &pwm_npcx_data_##inst, &pwm_npcx_cfg_##inst,       \
234 			    PRE_KERNEL_1, CONFIG_PWM_INIT_PRIORITY,	       \
235 			    &pwm_npcx_driver_api);
236 
237 DT_INST_FOREACH_STATUS_OKAY(NPCX_PWM_INIT)
238