1 /*
2  * Copyright (c) 2021 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_npcx_tach
8 
9 /**
10  * @file
11  * @brief Nuvoton NPCX tachometer sensor module driver
12  *
13  * This file contains a driver for the tachometer sensor module which contains
14  * two independent timers (counter 1 and 2). They are used to capture a counter
15  * value when the signals via external pins match the condition. The following
16  * is block diagram of this module when it set to mode 5.
17  *
18  *                            |          Capture A
19  *                            |              |         +-----------+  TA Pin
20  *           +-----------+    |        +-----+-----+   |   _   _   |   |
21  * APB_CLK-->| Prescaler |--->|---+--->| Counter 1 |<--| _| |_| |_ |<--+
22  *           +-----------+    |   |    +-----------+   +-----------+
23  *                            | CLK_SEL                Edge Detection
24  *                            |          Capture B
25  * LFCLK--------------------->|              |         +-----------+  TB Pin
26  *                            |        +-----+-----+   |   _   _   |   |
27  *                            |---+--->| Counter 2 |<--| _| |_| |_ |<--+
28  *                            |   |    +-----------+   +-----------+
29  *                            | CLK_SEL                Edge Detection
30  *                            |
31  *                            | TACH_CLK
32  *                            +----------
33  *          (NPCX Tachometer Mode 5, Dual-Independent Input Capture)
34  *
35  * This mode is used to measure either the frequency of two external clocks
36  * (via TA or TB pins) that are slower than TACH_CLK. A transition event (rising
37  * or falling edge) received on TAn/TBn pin causes a transfer of timer 1/2
38  * contents to Capture register and reload counter. Based on this value, we can
39  * compute the current RPM of external signal from encoders.
40  */
41 
42 #include <errno.h>
43 #include <zephyr/device.h>
44 #include <zephyr/drivers/clock_control.h>
45 #include <zephyr/drivers/pinctrl.h>
46 #include <zephyr/drivers/sensor.h>
47 #include <zephyr/dt-bindings/sensor/npcx_tach.h>
48 #include <soc.h>
49 #include <zephyr/logging/log.h>
50 
51 LOG_MODULE_REGISTER(tach_npcx, CONFIG_SENSOR_LOG_LEVEL);
52 
53 /* Device config */
54 struct tach_npcx_config {
55 	/* tachometer controller base address */
56 	uintptr_t base;
57 	/* clock configuration */
58 	struct npcx_clk_cfg clk_cfg;
59 	/* sampling clock frequency of tachometer */
60 	uint32_t sample_clk;
61 	/* selected port of tachometer */
62 	int port;
63 	/* number of pulses (holes) per round of tachometer's input (encoder) */
64 	int pulses_per_round;
65 	/* pinmux configuration */
66 	const struct pinctrl_dev_config *pcfg;
67 };
68 
69 /* Driver data */
70 struct tach_npcx_data {
71 	/* Input clock for tachometers */
72 	uint32_t input_clk;
73 	/* Captured counts of tachometer */
74 	uint32_t capture;
75 };
76 
77 /* Driver convenience defines */
78 #define HAL_INSTANCE(dev)                                                                          \
79 	((struct tach_reg *)((const struct tach_npcx_config *)(dev)->config)->base)
80 
81 /* Maximum count of prescaler */
82 #define NPCX_TACHO_PRSC_MAX 0xff
83 /* Maximum count of counter */
84 #define NPCX_TACHO_CNT_MAX 0xffff
85 /* Operation mode used for tachometer */
86 #define NPCX_TACH_MDSEL 4
87 /* Clock selection for tachometer */
88 #define NPCX_CLKSEL_APBCLK 1
89 #define NPCX_CLKSEL_LFCLK 4
90 
91 /* TACH inline local functions */
tach_npcx_start_port_a(const struct device * dev)92 static inline void tach_npcx_start_port_a(const struct device *dev)
93 {
94 	struct tach_npcx_data *const data = dev->data;
95 	struct tach_reg *const inst = HAL_INSTANCE(dev);
96 
97 	/* Set the default value of counter and capture register of timer 1. */
98 	inst->TCNT1 = NPCX_TACHO_CNT_MAX;
99 	inst->TCRA  = NPCX_TACHO_CNT_MAX;
100 
101 	/*
102 	 * Set the edge detection polarity of port A to falling (high-to-low
103 	 * transition) and enable the functionality to capture TCNT1 into TCRA
104 	 * and preset TCNT1 when event is triggered.
105 	 */
106 	inst->TMCTRL |= BIT(NPCX_TMCTRL_TAEN);
107 
108 	/* Enable input debounce logic into TA pin. */
109 	inst->TCFG |= BIT(NPCX_TCFG_TADBEN);
110 
111 	/* Select clock source of timer 1 from no clock and start to count. */
112 	SET_FIELD(inst->TCKC, NPCX_TCKC_C1CSEL_FIELD, data->input_clk == LFCLK
113 		  ? NPCX_CLKSEL_LFCLK : NPCX_CLKSEL_APBCLK);
114 }
115 
tach_npcx_start_port_b(const struct device * dev)116 static inline void tach_npcx_start_port_b(const struct device *dev)
117 {
118 	struct tach_reg *const inst = HAL_INSTANCE(dev);
119 	struct tach_npcx_data *const data = dev->data;
120 
121 	/* Set the default value of counter and capture register of timer 2. */
122 	inst->TCNT2 = NPCX_TACHO_CNT_MAX;
123 	inst->TCRB  = NPCX_TACHO_CNT_MAX;
124 
125 	/*
126 	 * Set the edge detection polarity of port B to falling (high-to-low
127 	 * transition) and enable the functionality to capture TCNT2 into TCRB
128 	 * and preset TCNT2 when event is triggered.
129 	 */
130 	inst->TMCTRL |= BIT(NPCX_TMCTRL_TBEN);
131 
132 	/* Enable input debounce logic into TB pin. */
133 	inst->TCFG |= BIT(NPCX_TCFG_TBDBEN);
134 
135 	/* Select clock source of timer 2 from no clock and start to count. */
136 	SET_FIELD(inst->TCKC, NPCX_TCKC_C2CSEL_FIELD, data->input_clk == LFCLK
137 		  ? NPCX_CLKSEL_LFCLK : NPCX_CLKSEL_APBCLK);
138 }
139 
tach_npcx_is_underflow(const struct device * dev)140 static inline bool tach_npcx_is_underflow(const struct device *dev)
141 {
142 	const struct tach_npcx_config *const config = dev->config;
143 	struct tach_reg *const inst = HAL_INSTANCE(dev);
144 
145 	LOG_DBG("port A is underflow %d, port b is underflow %d",
146 		IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TCPND),
147 		IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TDPND));
148 
149 	/*
150 	 * In mode 5, the flag TCPND or TDPND indicates the TCNT1 or TCNT2
151 	 * is under underflow situation. (No edges are detected.)
152 	 */
153 	if (config->port == NPCX_TACH_PORT_A) {
154 		return IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TCPND);
155 	} else {
156 		return IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TDPND);
157 	}
158 }
159 
tach_npcx_clear_underflow_flag(const struct device * dev)160 static inline void tach_npcx_clear_underflow_flag(const struct device *dev)
161 {
162 	const struct tach_npcx_config *const config = dev->config;
163 	struct tach_reg *const inst = HAL_INSTANCE(dev);
164 
165 	if (config->port == NPCX_TACH_PORT_A) {
166 		inst->TECLR = BIT(NPCX_TECLR_TCCLR);
167 	} else {
168 		inst->TECLR = BIT(NPCX_TECLR_TDCLR);
169 	}
170 }
171 
tach_npcx_is_captured(const struct device * dev)172 static inline bool tach_npcx_is_captured(const struct device *dev)
173 {
174 	const struct tach_npcx_config *const config = dev->config;
175 	struct tach_reg *const inst = HAL_INSTANCE(dev);
176 
177 	LOG_DBG("port A is captured %d, port b is captured %d",
178 		IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TAPND),
179 		IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TBPND));
180 
181 	/*
182 	 * In mode 5, the flag TAPND or TBPND indicates a input captured on
183 	 * TAn or TBn transition.
184 	 */
185 	if (config->port == NPCX_TACH_PORT_A) {
186 		return IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TAPND);
187 	} else {
188 		return IS_BIT_SET(inst->TECTRL, NPCX_TECTRL_TBPND);
189 	}
190 }
191 
tach_npcx_clear_captured_flag(const struct device * dev)192 static inline void tach_npcx_clear_captured_flag(const struct device *dev)
193 {
194 	const struct tach_npcx_config *const config = dev->config;
195 	struct tach_reg *const inst = HAL_INSTANCE(dev);
196 
197 	if (config->port == NPCX_TACH_PORT_A) {
198 		inst->TECLR = BIT(NPCX_TECLR_TACLR);
199 	} else {
200 		inst->TECLR = BIT(NPCX_TECLR_TBCLR);
201 	}
202 }
203 
tach_npcx_get_captured_count(const struct device * dev)204 static inline uint16_t tach_npcx_get_captured_count(const struct device *dev)
205 {
206 	const struct tach_npcx_config *const config = dev->config;
207 	struct tach_reg *const inst = HAL_INSTANCE(dev);
208 
209 	if (config->port == NPCX_TACH_PORT_A) {
210 		return inst->TCRA;
211 	} else {
212 		return inst->TCRB;
213 	}
214 }
215 
216 /* TACH local functions */
tach_npcx_configure(const struct device * dev)217 static int tach_npcx_configure(const struct device *dev)
218 {
219 	const struct tach_npcx_config *const config = dev->config;
220 	struct tach_npcx_data *const data = dev->data;
221 	struct tach_reg *const inst = HAL_INSTANCE(dev);
222 
223 	/* Set mode 5 to tachometer module */
224 	SET_FIELD(inst->TMCTRL, NPCX_TMCTRL_MDSEL_FIELD, NPCX_TACH_MDSEL);
225 
226 	/* Configure clock module and its frequency of tachometer */
227 	if (config->sample_clk == 0) {
228 		return -EINVAL;
229 	} else if (data->input_clk == LFCLK) {
230 		/* Enable low power mode */
231 		inst->TCKC |= BIT(NPCX_TCKC_LOW_PWR);
232 		if (config->sample_clk != data->input_clk) {
233 			LOG_ERR("%s operate freq is %d not fixed to 32kHz",
234 				dev->name, data->input_clk);
235 			return -EINVAL;
236 		}
237 	} else {
238 		/* Configure sampling freq by setting prescaler of APB1 */
239 		uint16_t prescaler = data->input_clk / config->sample_clk;
240 
241 		if (data->input_clk > config->sample_clk) {
242 			LOG_ERR("%s operate freq exceeds APB1 clock",
243 				dev->name);
244 			return -EINVAL;
245 		}
246 		inst->TPRSC = MIN(NPCX_TACHO_PRSC_MAX, MAX(prescaler, 1));
247 	}
248 
249 	return 0;
250 }
251 
252 /* TACH api functions */
tach_npcx_sample_fetch(const struct device * dev,enum sensor_channel chan)253 int tach_npcx_sample_fetch(const struct device *dev, enum sensor_channel chan)
254 {
255 	ARG_UNUSED(chan);
256 	struct tach_npcx_data *const data = dev->data;
257 
258 	/* Check whether underflow flag of tachometer is occurred */
259 	if (tach_npcx_is_underflow(dev)) {
260 		/* Clear pending flags */
261 		tach_npcx_clear_underflow_flag(dev);
262 		/* Clear stale captured data */
263 		tach_npcx_clear_captured_flag(dev);
264 		data->capture = 0;
265 
266 		return 0;
267 	}
268 
269 	/* Check whether capture flag of tachometer is set */
270 	if (tach_npcx_is_captured(dev)) {
271 		/* Clear pending flags */
272 		tach_npcx_clear_captured_flag(dev);
273 		/* Save captured count */
274 		data->capture = NPCX_TACHO_CNT_MAX -
275 					tach_npcx_get_captured_count(dev);
276 	}
277 
278 	return 0;
279 }
280 
tach_npcx_channel_get(const struct device * dev,enum sensor_channel chan,struct sensor_value * val)281 static int tach_npcx_channel_get(const struct device *dev,
282 				enum sensor_channel chan,
283 				struct sensor_value *val)
284 {
285 	const struct tach_npcx_config *const config = dev->config;
286 	struct tach_npcx_data *const data = dev->data;
287 
288 	if (chan != SENSOR_CHAN_RPM) {
289 		return -ENOTSUP;
290 	}
291 
292 	if (data->capture > 0) {
293 		/*
294 		 * RPM = (f * 60) / (n * TACH)
295 		 *   n = Pulses per round
296 		 *   f = Tachometer operation frequency (Hz)
297 		 *   TACH = Captured counts of tachometer
298 		 */
299 		val->val1 = (config->sample_clk * 60) /
300 			(config->pulses_per_round * data->capture);
301 	} else {
302 		val->val1 = 0U;
303 	}
304 
305 	val->val2 = 0U;
306 
307 	return 0;
308 }
309 
310 
311 /* TACH driver registration */
tach_npcx_init(const struct device * dev)312 static int tach_npcx_init(const struct device *dev)
313 {
314 	const struct tach_npcx_config *const config = dev->config;
315 	struct tach_npcx_data *const data = dev->data;
316 	const struct device *const clk_dev = DEVICE_DT_GET(NPCX_CLK_CTRL_NODE);
317 	int ret;
318 
319 	if (!device_is_ready(clk_dev)) {
320 		LOG_ERR("clock control device not ready");
321 		return -ENODEV;
322 	}
323 
324 	/* Turn on device clock first and get source clock freq. */
325 	ret = clock_control_on(clk_dev, (clock_control_subsys_t)
326 							&config->clk_cfg);
327 	if (ret < 0) {
328 		LOG_ERR("Turn on tachometer clock fail %d", ret);
329 		return ret;
330 	}
331 
332 	ret = clock_control_get_rate(clk_dev, (clock_control_subsys_t)
333 					&config->clk_cfg, &data->input_clk);
334 	if (ret < 0) {
335 		LOG_ERR("Get tachometer clock rate error %d", ret);
336 		return ret;
337 	}
338 
339 	/* Configure pin-mux for tachometer device */
340 	ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
341 	if (ret < 0) {
342 		LOG_ERR("Tacho pinctrl setup failed (%d)", ret);
343 		return ret;
344 	}
345 
346 
347 	/* Configure tachometer and its operate freq. */
348 	ret = tach_npcx_configure(dev);
349 	if (ret < 0) {
350 		LOG_ERR("Config tachometer port %d failed", config->port);
351 		return ret;
352 	}
353 
354 	/* Start tachometer sensor */
355 	if (config->port == NPCX_TACH_PORT_A) {
356 		tach_npcx_start_port_a(dev);
357 	} else if (config->port == NPCX_TACH_PORT_B) {
358 		tach_npcx_start_port_b(dev);
359 	} else {
360 		return -EINVAL;
361 	}
362 
363 	return 0;
364 }
365 
366 static DEVICE_API(sensor, tach_npcx_driver_api) = {
367 	.sample_fetch = tach_npcx_sample_fetch,
368 	.channel_get = tach_npcx_channel_get,
369 };
370 
371 #define NPCX_TACH_INIT(inst)                                                   \
372 	PINCTRL_DT_INST_DEFINE(inst);					       \
373 									       \
374 	static const struct tach_npcx_config tach_cfg_##inst = {               \
375 		.base = DT_INST_REG_ADDR(inst),                                \
376 		.clk_cfg = NPCX_DT_CLK_CFG_ITEM(inst),                         \
377 		.sample_clk = DT_INST_PROP(inst, sample_clk),                  \
378 		.port = DT_INST_PROP(inst, port),                              \
379 		.pulses_per_round = DT_INST_PROP(inst, pulses_per_round),      \
380 		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                  \
381 	};                                                                     \
382 									       \
383 	static struct tach_npcx_data tach_data_##inst;                         \
384 									       \
385 	SENSOR_DEVICE_DT_INST_DEFINE(inst,                                     \
386 			      tach_npcx_init,                                  \
387 			      NULL,                                            \
388 			      &tach_data_##inst,                               \
389 			      &tach_cfg_##inst,                                \
390 			      POST_KERNEL,                                     \
391 			      CONFIG_SENSOR_INIT_PRIORITY,                     \
392 			      &tach_npcx_driver_api);
393 
394 DT_INST_FOREACH_STATUS_OKAY(NPCX_TACH_INIT)
395