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