Repository: zephyrproject-rtos/zephyr
Pull request #88472: drivers/sensor/ite/tach/it51xxx: implement tachometer driver
Author: login: RuibinChang
Labels: name: area: Sensors
description: Sensors
name: area: RISCV
description: RISCV Architecture (32-bit & 64-bit)
name: platform: ITE
description: ITE
Created: 2025-04-11T03:30:13Z | Merged: 2025-04-24T09:56:45Z

Implement tachometer driver for ITE it51xxx series chip.


Diff:
diff --git a/boards/ite/it515xx_evb/it515xx_evb.dts b/boards/ite/it515xx_evb/it515xx_evb.dts
index 500a8618b36d8..4d0ccfb7485f0 100644
--- a/boards/ite/it515xx_evb/it515xx_evb.dts
+++ b/boards/ite/it515xx_evb/it515xx_evb.dts
@@ -110,3 +110,12 @@
 	pinctrl-0 = <&adc0_ch3_gpi3_default>;
 	pinctrl-names = "default";
 };
+
+/* test fan tachometer sensor */
+&tach0 {
+	status = "okay";
+	input-pin = <IT51XXX_TACH_INPUT_PIN_A>;
+	pulses-per-round = <2>;
+	pinctrl-0 = <&tach0a_gpd6_default>;
+	pinctrl-names = "default";
+};
diff --git a/drivers/sensor/ite/CMakeLists.txt b/drivers/sensor/ite/CMakeLists.txt
index 68883d9bc0ca8..85642b93ee6c6 100644
--- a/drivers/sensor/ite/CMakeLists.txt
+++ b/drivers/sensor/ite/CMakeLists.txt
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: Apache-2.0
 
 # zephyr-keep-sorted-start
+add_subdirectory_ifdef(CONFIG_TACH_IT51XXX ite_tach_it51xxx)
 add_subdirectory_ifdef(CONFIG_TACH_IT8XXX2 ite_tach_it8xxx2)
 add_subdirectory_ifdef(CONFIG_VCMP_IT8XXX2 ite_vcmp_it8xxx2)
 # zephyr-keep-sorted-stop
diff --git a/drivers/sensor/ite/Kconfig b/drivers/sensor/ite/Kconfig
index 8d0bf169cea39..e03616c50611a 100644
--- a/drivers/sensor/ite/Kconfig
+++ b/drivers/sensor/ite/Kconfig
@@ -2,6 +2,7 @@
 # SPDX-License-Identifier: Apache-2.0
 
 # zephyr-keep-sorted-start
+source "drivers/sensor/ite/ite_tach_it51xxx/Kconfig"
 source "drivers/sensor/ite/ite_tach_it8xxx2/Kconfig"
 source "drivers/sensor/ite/ite_vcmp_it8xxx2/Kconfig"
 # zephyr-keep-sorted-stop
diff --git a/drivers/sensor/ite/ite_tach_it51xxx/CMakeLists.txt b/drivers/sensor/ite/ite_tach_it51xxx/CMakeLists.txt
new file mode 100644
index 0000000000000..6fc9f1923903d
--- /dev/null
+++ b/drivers/sensor/ite/ite_tach_it51xxx/CMakeLists.txt
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: Apache-2.0
+
+zephyr_library()
+
+zephyr_library_sources(tach_ite_it51xxx.c)
diff --git a/drivers/sensor/ite/ite_tach_it51xxx/Kconfig b/drivers/sensor/ite/ite_tach_it51xxx/Kconfig
new file mode 100644
index 0000000000000..0054c992e25d3
--- /dev/null
+++ b/drivers/sensor/ite/ite_tach_it51xxx/Kconfig
@@ -0,0 +1,13 @@
+# Copyright (c) 2025 ITE Technology Corporation.
+# SPDX-License-Identifier: Apache-2.0
+
+config TACH_IT51XXX
+	bool "ITE it51xxx Tachometer sensor"
+	default y
+	depends on DT_HAS_ITE_IT51XXX_TACH_ENABLED
+	depends on SOC_IT51XXX
+	select PINCTRL
+	help
+	  Enable the ITE it51xxx tachometer sensor,
+	  it51xxx supports three 16-bit tachometer sensor, each sensor has two
+	  input pin, and we need to select one input from them.
diff --git a/drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c b/drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
new file mode 100644
index 0000000000000..94c6d44525f15
--- /dev/null
+++ b/drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 tachometer speed (2 bytes value) */
+#define REG_TACH_CH       0x00
+/* 0xC6/0xD6/0xE6: Tach channel 0~2 control 1 */
+#define REG_TACH_CH_CTRL1 0x06
+#define TACH_CH_DVS       BIT(1)
+#define TACH_CH_SEL       BIT(0)
+
+struct tach_it51xxx_config {
+	/* Tach channel register base address */
+	uintptr_t base;
+	/* Tachometer alternate configuration */
+	const struct pinctrl_dev_config *pcfg;
+	/* Select input pin to tachometer */
+	int input_pin;
+	/* Number of pulses per round of tachometer's input */
+	int pulses_per_round;
+};
+
+struct tach_it51xxx_data {
+	/* Captured counts of tachometer */
+	uint16_t capture;
+};
+
+static bool tach_ch_is_valid(const struct device *dev, int input_pin)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	uint8_t reg_val;
+
+	if (input_pin > IT51XXX_TACH_INPUT_PIN_B) {
+		LOG_ERR("Tach input pin %d invalid, only support 0(A) or 1(B)", input_pin);
+		return false;
+	}
+
+	reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+	if (((reg_val & TACH_CH_SEL) == input_pin) && (reg_val & TACH_CH_DVS)) {
+		/* Input pin match register setting and tachometer data valid */
+		return true;
+	}
+
+	return false;
+}
+
+static int tach_it51xxx_sample_fetch(const struct device *dev, enum sensor_channel chan)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	int input_pin = config->input_pin;
+	struct tach_it51xxx_data *const data = dev->data;
+	uint8_t reg_val;
+
+	if ((chan != SENSOR_CHAN_RPM) && (chan != SENSOR_CHAN_ALL)) {
+		return -ENOTSUP;
+	}
+
+	if (tach_ch_is_valid(dev, input_pin)) {
+		/* If tachometer data is valid, then save it */
+		data->capture = sys_read16(base + REG_TACH_CH);
+		/* Clear tachometer data valid status */
+		reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+		sys_write8(reg_val | TACH_CH_DVS, base + REG_TACH_CH_CTRL1);
+	} else {
+		/* If tachometer data isn't valid, then clear it */
+		data->capture = 0;
+	}
+
+	return 0;
+}
+
+static int tach_it51xxx_channel_get(const struct device *dev, enum sensor_channel chan,
+				    struct sensor_value *val)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	int p = config->pulses_per_round;
+	struct tach_it51xxx_data *const data = dev->data;
+
+	__ASSERT(p > 0, "pulses_per_round must be bigger than 0");
+
+	if (chan != SENSOR_CHAN_RPM) {
+		LOG_ERR("Sensor chan %d, only support SENSOR_CHAN_RPM", chan);
+		return -ENOTSUP;
+	}
+
+	/* Transform count unit to RPM */
+	if (data->capture > 0) {
+		/*
+		 * Fan Speed (RPM) = 60 / (1/fs * {TACH_CH_(H & L)} * P)
+		 * - P denotes the numbers of pulses per round
+		 * - {TACH_CH_(H & L)} = 0000h denotes Fan Speed is zero
+		 * - The sampling rate (fs) is TACH_FREQ / 128
+		 */
+		val->val1 = (60 * TACH_FREQ / 128 / p / (data->capture));
+	} else {
+		val->val1 = 0U;
+	}
+
+	val->val2 = 0U;
+
+	return 0;
+}
+
+static int tach_it51xxx_init(const struct device *dev)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	int input_pin = config->input_pin;
+	int status;
+	uint8_t reg_val;
+
+	if (input_pin > IT51XXX_TACH_INPUT_PIN_B) {
+		LOG_ERR("Tach input pin %d invalid, only support 0(A) or 1(B)", input_pin);
+		return -EINVAL;
+	}
+
+	/* Select input pin to tachometer alternate mode */
+	status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
+	if (status < 0) {
+		LOG_ERR("Failed to configure TACH pins");
+		return status;
+	}
+
+	reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+	if (input_pin == IT51XXX_TACH_INPUT_PIN_A) {
+		/* Select TACH_INPUT_PIN_A to tachometer */
+		reg_val &= ~TACH_CH_SEL;
+	} else {
+		/* Select TACH_INPUT_PIN_B to tachometer */
+		reg_val |= TACH_CH_SEL;
+	}
+	/* Clear tachometer data valid status */
+	sys_write8(reg_val | TACH_CH_DVS, base + REG_TACH_CH_CTRL1);
+
+	/* Tachometer sensor already start */
+	return 0;
+}
+
+static DEVICE_API(sensor, tach_it51xxx_driver_api) = {
+	.sample_fetch = tach_it51xxx_sample_fetch,
+	.channel_get = tach_it51xxx_channel_get,
+};
+
+#define TACH_IT51XXX_INIT(inst)                                                                    \
+	PINCTRL_DT_INST_DEFINE(inst);                                                              \
+                                                                                                   \
+	static const struct tach_it51xxx_config tach_it51xxx_cfg_##inst = {                        \
+		.base = DT_INST_REG_ADDR(inst),                                                    \
+		.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst),                                      \
+		.input_pin = DT_INST_PROP(inst, input_pin),                                        \
+		.pulses_per_round = DT_INST_PROP(inst, pulses_per_round),                          \
+	};                                                                                         \
+                                                                                                   \
+	static struct tach_it51xxx_data tach_it51xxx_data_##inst;                                  \
+                                                                                                   \
+	SENSOR_DEVICE_DT_INST_DEFINE(inst, tach_it51xxx_init, NULL, &tach_it51xxx_data_##inst,     \
+				     &tach_it51xxx_cfg_##inst, POST_KERNEL,                        \
+				     CONFIG_SENSOR_INIT_PRIORITY, &tach_it51xxx_driver_api);
+
+DT_INST_FOREACH_STATUS_OKAY(TACH_IT51XXX_INIT)
diff --git a/dts/bindings/tach/ite,it51xxx-tach.yaml b/dts/bindings/tach/ite,it51xxx-tach.yaml
new file mode 100644
index 0000000000000..c46a634d23a45
--- /dev/null
+++ b/dts/bindings/tach/ite,it51xxx-tach.yaml
@@ -0,0 +1,31 @@
+# Copyright (c) 2025 ITE Technology Corporation.
+# SPDX-License-Identifier: Apache-2.0
+
+description: ITE, it51xxx Tachometer node
+
+compatible: "ite,it51xxx-tach"
+
+include: [tach.yaml, pinctrl-device.yaml]
+
+properties:
+  reg:
+    required: true
+
+  input-pin:
+    type: int
+    required: true
+    enum:
+      - 0
+      - 1
+    description: 0 = TACH_INPUT_PIN_A, 1 = TACH_INPUT_PIN_B
+
+  pulses-per-round:
+    type: int
+    required: true
+    description: number of pulses per round of tachometer's input
+
+  pinctrl-0:
+    required: true
+
+  pinctrl-names:
+    required: true
diff --git a/dts/riscv/ite/it51xxx.dtsi b/dts/riscv/ite/it51xxx.dtsi
index 1b14d9a16cc84..3538a62be5671 100644
--- a/dts/riscv/ite/it51xxx.dtsi
+++ b/dts/riscv/ite/it51xxx.dtsi
@@ -13,6 +13,7 @@
 #include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-intc.h>
 #include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-wuc.h>
 #include <zephyr/dt-bindings/pinctrl/it8xxx2-pinctrl.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
 
 / {
 	#address-cells = <1>;
@@ -1023,5 +1024,23 @@
 			interrupts = <IT51XXX_IRQ_TIMER1_DW IRQ_TYPE_EDGE_RISING>; /* Warn timer */
 			interrupt-parent = <&intc>;
 		};
+
+		tach0: tach@f046c0 {
+			compatible = "ite,it51xxx-tach";
+			reg = <0x00f046c0 0xf>;
+			status = "disabled";
+		};
+
+		tach1: tach@f046d0 {
+			compatible = "ite,it51xxx-tach";
+			reg = <0x00f046d0 0xf>;
+			status = "disabled";
+		};
+
+		tach2: tach@f046e0 {
+			compatible = "ite,it51xxx-tach";
+			reg = <0x00f046e0 0xf>;
+			status = "disabled";
+		};
 	};
 };
diff --git a/include/zephyr/dt-bindings/sensor/it51xxx_tach.h b/include/zephyr/dt-bindings/sensor/it51xxx_tach.h
new file mode 100644
index 0000000000000..c8b9fa2844125
--- /dev/null
+++ b/include/zephyr/dt-bindings/sensor/it51xxx_tach.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_ITE_TACH_H_
+#define ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_ITE_TACH_H_
+
+/**
+ * @name Tachometer input pin
+ * @{
+ */
+
+/** Tachometer input from pin A */
+#define IT51XXX_TACH_INPUT_PIN_A	0
+/** Tachometer input from pin B */
+#define IT51XXX_TACH_INPUT_PIN_B	1
+
+/** @} */
+#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_ITE_TACH_H_ */
diff --git a/soc/ite/ec/it51xxx/chip_chipregs.h b/soc/ite/ec/it51xxx/chip_chipregs.h
index d589c59460a36..10f134295668d 100644
--- a/soc/ite/ec/it51xxx/chip_chipregs.h
+++ b/soc/ite/ec/it51xxx/chip_chipregs.h
@@ -9,6 +9,8 @@
 
 #include <zephyr/sys/util.h>
 
+#define IT51XXX_EC_FREQ KHZ(9200)
+
 #ifdef _ASMLANGUAGE
 #define ECREG(x) x
 #else


Reviews:
user: login: MaureenHelm
body: Needs rebase
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-11T13:55:49Z
user: login: Dino-Li
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T03:08:12Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T10:14:39Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T10:14:46Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T10:14:57Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T10:15:42Z
user: login: Dino-Li
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-21T10:50:11Z
user: login: fabiobaltieri
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-22T10:19:27Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T02:57:52Z
user: login: fabiobaltieri
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-23T09:33:29Z
user: login: Dino-Li
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T09:50:05Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This pull request implements a new tachometer driver for the ITE it51xxx series chip. The changes include adding a frequency macro in chip_chipregs.h, providing DT and YAML binding definitions for the tachometer sensor, and introducing the new driver implementation in tach_ite_it51xxx.c.

### Reviewed Changes

Copilot reviewed 4 out of 10 changed files in this pull request and generated 2 comments.

| File                                                      | Description                                                            |
| --------------------------------------------------------- | ---------------------------------------------------------------------- |
| soc/ite/ec/it51xxx/chip_chipregs.h                        | Adds the IT51XXX_EC_FREQ macro for the EC frequency.                   |
| include/zephyr/dt-bindings/sensor/it51xxx_tach.h          | Defines tachometer channel constants for device tree bindings.         |
| dts/bindings/tach/ite,it51xxx-tach.yaml                   | Provides YAML binding definitions for the tachometer device.           |
| drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c     | Implements the driver logic, including sampling and RPM calculations.  |

<details>
<summary>Files not reviewed (6)</summary>

* **boards/ite/it515xx_evb/it515xx_evb.dts**: Language not supported
* **drivers/sensor/ite/CMakeLists.txt**: Language not supported
* **drivers/sensor/ite/Kconfig**: Language not supported
* **drivers/sensor/ite/ite_tach_it51xxx/CMakeLists.txt**: Language not supported
* **drivers/sensor/ite/ite_tach_it51xxx/Kconfig**: Language not supported
* **dts/riscv/ite/it51xxx.dtsi**: Language not supported
</details>



state: COMMENTED
author_association: NONE
submitted_at: 2025-04-23T13:01:11Z
user: login: kartben
body: Looks like copilot feedback is worth addressing (at least the missing check or assert for pulses per round == 0) -- thanks!

And there is a merge conflict anyway!
state: CHANGES_REQUESTED
author_association: MEMBER
submitted_at: 2025-04-23T13:02:06Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T07:22:09Z
user: login: RuibinChang
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T07:22:21Z
user: login: fabiobaltieri
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-24T09:00:35Z
user: login: kartben
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-24T09:13:44Z
user: login: Dino-Li
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T09:46:46Z

Review comments:
diff_hunk: @@ -104,3 +104,12 @@
 	row-size = <8>;
 	col-size = <16>;
 };
+
+/* test fan tachometer sensor */
+&tach0 {
+	status = "okay";
+	channel = <IT8XXX2_TACH_CHANNEL_A>;
path: boards/ite/it515xx_evb/it515xx_evb.dts
user: login: Dino-Li
body: Since it515xx series has its own tachometer driver, it is recommended to add it515xx prefix channel definition macro to avoid confusion.
created_at: 2025-04-21T02:47:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9,6 +9,8 @@
 
 #include <zephyr/sys/util.h>
 
+#define EC_FREQ KHZ(9200)
path: soc/ite/ec/it51xxx/chip_chipregs.h
user: login: Dino-Li
body: ```suggestion
#define IT51XXX_EC_FREQ KHZ(9200)
```
created_at: 2025-04-21T02:52:36Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,6 +12,8 @@
 #include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-intc.h>
 #include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-wuc.h>
 #include <zephyr/dt-bindings/pinctrl/it8xxx2-pinctrl.h>
+#include <zephyr/dt-bindings/pwm/it8xxx2_pwm.h>
path: dts/riscv/ite/it51xxx.dtsi
user: login: Dino-Li
body: Is this header file required?
created_at: 2025-04-21T02:53:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it8xxx2_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+/*
+ * NOTE: The PWM output maximum is 324Hz in EC power saving mode, so if we need
+ *       fan to work, then don't let EC enter the mode.
+ */
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: Dino-Li
body: Is it more appropriate to put this NOTE in the pwm driver?
created_at: 2025-04-21T03:04:15Z
author_association: CONTRIBUTOR
diff_hunk: @@ -104,3 +104,12 @@
 	row-size = <8>;
 	col-size = <16>;
 };
+
+/* test fan tachometer sensor */
+&tach0 {
+	status = "okay";
+	channel = <IT8XXX2_TACH_CHANNEL_A>;
path: boards/ite/it515xx_evb/it515xx_evb.dts
user: login: RuibinChang
body: Done
created_at: 2025-04-21T10:14:39Z
author_association: CONTRIBUTOR
diff_hunk: @@ -9,6 +9,8 @@
 
 #include <zephyr/sys/util.h>
 
+#define EC_FREQ KHZ(9200)
path: soc/ite/ec/it51xxx/chip_chipregs.h
user: login: RuibinChang
body: Done
created_at: 2025-04-21T10:14:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -12,6 +12,8 @@
 #include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-intc.h>
 #include <zephyr/dt-bindings/interrupt-controller/ite-it51xxx-wuc.h>
 #include <zephyr/dt-bindings/pinctrl/it8xxx2-pinctrl.h>
+#include <zephyr/dt-bindings/pwm/it8xxx2_pwm.h>
path: dts/riscv/ite/it51xxx.dtsi
user: login: RuibinChang
body: Removed
created_at: 2025-04-21T10:14:57Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it8xxx2_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+/*
+ * NOTE: The PWM output maximum is 324Hz in EC power saving mode, so if we need
+ *       fan to work, then don't let EC enter the mode.
+ */
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: RuibinChang
body: Removed and it's already in pwm driver.
created_at: 2025-04-21T10:15:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 achometer speed (2 bytes value) */
+#define REG_TACH_CH       0x00
+/* 0xC6/0xD6/0xE6: Tach channel 0~2 control 1 */
+#define REG_TACH_CH_CTRL1 0x06
+#define TACH_CH_DVS       BIT(1)
+#define TACH_CH_SEL       BIT(0)
+
+struct tach_it51xxx_config {
+	/* Tach channel register base address */
+	uintptr_t base;
+	/* Tachometer alternate configuration */
+	const struct pinctrl_dev_config *pcfg;
+	/* Select channel of tachometer */
+	int channel;
+	/* Number of pulses per round of tachometer's input */
+	int pulses_per_round;
+};
+
+struct tach_it51xxx_data {
+	/* Captured counts of tachometer */
+	uint16_t capture;
+};
+
+static bool tach_ch_is_valid(const struct device *dev, int tach_ch)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	bool valid = false;
+	uint8_t reg_val;
+
+	if (tach_ch > IT51XXX_TACH_CHANNEL_B) {
+		LOG_ERR("Tach channel %d invalid, only support 0(A) or 1(B)", tach_ch);
+		return false;
+	}
+
+	reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+	if (((reg_val & TACH_CH_SEL) == tach_ch) && (reg_val & TACH_CH_DVS)) {
+		/* Tachometer channel match register setting and data valid */
+		valid = true;
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: fabiobaltieri
body: how about just `return true` here and `return false` at the end?
created_at: 2025-04-22T10:18:43Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 achometer speed (2 bytes value) */
+#define REG_TACH_CH       0x00
+/* 0xC6/0xD6/0xE6: Tach channel 0~2 control 1 */
+#define REG_TACH_CH_CTRL1 0x06
+#define TACH_CH_DVS       BIT(1)
+#define TACH_CH_SEL       BIT(0)
+
+struct tach_it51xxx_config {
+	/* Tach channel register base address */
+	uintptr_t base;
+	/* Tachometer alternate configuration */
+	const struct pinctrl_dev_config *pcfg;
+	/* Select channel of tachometer */
+	int channel;
+	/* Number of pulses per round of tachometer's input */
+	int pulses_per_round;
+};
+
+struct tach_it51xxx_data {
+	/* Captured counts of tachometer */
+	uint16_t capture;
+};
+
+static bool tach_ch_is_valid(const struct device *dev, int tach_ch)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	bool valid = false;
+	uint8_t reg_val;
+
+	if (tach_ch > IT51XXX_TACH_CHANNEL_B) {
+		LOG_ERR("Tach channel %d invalid, only support 0(A) or 1(B)", tach_ch);
+		return false;
+	}
+
+	reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+	if (((reg_val & TACH_CH_SEL) == tach_ch) && (reg_val & TACH_CH_DVS)) {
+		/* Tachometer channel match register setting and data valid */
+		valid = true;
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: RuibinChang
body: Done
created_at: 2025-04-23T02:57:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 achometer speed (2 bytes value) */
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: Copilot
body: The comment indicates a tachometer channel range of 0 to 2, but the driver logic only supports channels 0 and 1. Update the comment to accurately reflect the supported channels.
```suggestion
/* 0xC0/0xD0: Tach channel 0~1 tachometer speed (2 bytes value) */
```
created_at: 2025-04-23T13:01:10Z
author_association: NONE
diff_hunk: @@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 achometer speed (2 bytes value) */
+#define REG_TACH_CH       0x00
+/* 0xC6/0xD6/0xE6: Tach channel 0~2 control 1 */
+#define REG_TACH_CH_CTRL1 0x06
+#define TACH_CH_DVS       BIT(1)
+#define TACH_CH_SEL       BIT(0)
+
+struct tach_it51xxx_config {
+	/* Tach channel register base address */
+	uintptr_t base;
+	/* Tachometer alternate configuration */
+	const struct pinctrl_dev_config *pcfg;
+	/* Select channel of tachometer */
+	int channel;
+	/* Number of pulses per round of tachometer's input */
+	int pulses_per_round;
+};
+
+struct tach_it51xxx_data {
+	/* Captured counts of tachometer */
+	uint16_t capture;
+};
+
+static bool tach_ch_is_valid(const struct device *dev, int tach_ch)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	uint8_t reg_val;
+
+	if (tach_ch > IT51XXX_TACH_CHANNEL_B) {
+		LOG_ERR("Tach channel %d invalid, only support 0(A) or 1(B)", tach_ch);
+		return false;
+	}
+
+	reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+	if (((reg_val & TACH_CH_SEL) == tach_ch) && (reg_val & TACH_CH_DVS)) {
+		/* Tachometer channel match register setting and data valid */
+		return true;
+	}
+
+	return false;
+}
+
+static int tach_it51xxx_sample_fetch(const struct device *dev, enum sensor_channel chan)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	int tach_ch = config->channel;
+	struct tach_it51xxx_data *const data = dev->data;
+	uint8_t reg_val;
+
+	if ((chan != SENSOR_CHAN_RPM) && (chan != SENSOR_CHAN_ALL)) {
+		return -ENOTSUP;
+	}
+
+	if (tach_ch_is_valid(dev, tach_ch)) {
+		/* If channel data of tachometer is valid, then save it */
+		data->capture = sys_read16(base + REG_TACH_CH);
+		/* Clear tachometer data valid status */
+		reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+		sys_write8(reg_val | TACH_CH_DVS, base + REG_TACH_CH_CTRL1);
+	} else {
+		/* If channel data of tachometer isn't valid, then clear it */
+		data->capture = 0;
+	}
+
+	return 0;
+}
+
+static int tach_it51xxx_channel_get(const struct device *dev, enum sensor_channel chan,
+				    struct sensor_value *val)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	int p = config->pulses_per_round;
+	struct tach_it51xxx_data *const data = dev->data;
+
+	if (chan != SENSOR_CHAN_RPM) {
+		LOG_ERR("Sensor chan %d, only support SENSOR_CHAN_RPM", chan);
+		return -ENOTSUP;
+	}
+
+	/* Transform count unit to RPM */
+	if (data->capture > 0) {
+		/*
+		 * Fan Speed (RPM) = 60 / (1/fs * {TACH_CH_(H & L)} * P)
+		 * - P denotes the numbers of pulses per round
+		 * - {TACH_CH_(H & L)} = 0000h denotes Fan Speed is zero
+		 * - The sampling rate (fs) is TACH_FREQ / 128
+		 */
+		val->val1 = (60 * TACH_FREQ / 128 / p / (data->capture));
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: Copilot
body: The use of 'p' (pulses_per_round) as a divisor may lead to a division by zero if its value is 0. Introduce a validation check to ensure 'p' is non-zero before performing the division.
created_at: 2025-04-23T13:01:11Z
author_association: NONE
diff_hunk: @@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 achometer speed (2 bytes value) */
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: RuibinChang
body: Our it51xxx series chip supports 3 tach nodes, and each node has two input pins that can be selected (IT51XXX_TACH_CHANNEL_A / IT51XXX_TACH_CHANNEL_B), so I think this is a misjudgment. 
But anyway, I renamed "channel A/B" to "input_pin A/B" to avoid misunderstanding.
created_at: 2025-04-24T07:22:09Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 2025 ITE Technology Corporation.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#define DT_DRV_COMPAT ite_it51xxx_tach
+
+/**
+ * @file
+ * @brief ITE it51xxx tachometer sensor module driver
+ *
+ * This file contains a driver for the tachometer sensor module which contains
+ * three independent counters (T0L/MR, T1L/MR and T2L/MR). The content of the
+ * Tachometer Reading Register is still update based on the sampling counter
+ * that samples the tachometer input (T0A, T0B, T1A, T1B, T2A or T2B pins).
+ * The following is block diagram of this module:
+ *
+ *                                    Sample Rate = TACH_FREQ / 128
+ *                                                   |
+ *                            |        Tachometer 0  |                T0A (GPD6)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T0L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T0B (GPC6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *           +-----------+    |                      |
+ * Crystal-->| Prescaler |--->|        Tachometer 1  |                T1A (GPD7)
+ * 32.768k   +-----------+    |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T1L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T1B (GPJ6)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *                            |        Sample Rate = TACH_FREQ / 128
+ *                            |                      |
+ *                            |        Tachometer 2  |                T2A (GPJ0)
+ *                            |             |        | +-----------+   |
+ *                            |       +-----+-----+  | |   _   _   |<--+
+ *                            |------>|  T2L/MR   |<-+-|  | |_| |_ |<--+
+ *                            |       +-----------+    +-----------+   |
+ *                            |       capture pulses                  T2B (GPJ1)
+ *                            |       in sample rate
+ *                            |       period
+ *                            |
+ *
+ *
+ * Based on the counter value, we can compute the current RPM of external signal
+ * from encoders.
+ */
+
+#include <zephyr/device.h>
+#include <zephyr/drivers/pinctrl.h>
+#include <zephyr/drivers/sensor.h>
+#include <zephyr/dt-bindings/sensor/it51xxx_tach.h>
+#include <errno.h>
+#include <soc.h>
+#include <soc_dt.h>
+
+#include <zephyr/logging/log.h>
+LOG_MODULE_REGISTER(tach_ite_it51xxx, CONFIG_SENSOR_LOG_LEVEL);
+
+#define TACH_FREQ IT51XXX_EC_FREQ
+
+/* 0xC0/0xD0/0xE0: Tach channel 0~2 achometer speed (2 bytes value) */
+#define REG_TACH_CH       0x00
+/* 0xC6/0xD6/0xE6: Tach channel 0~2 control 1 */
+#define REG_TACH_CH_CTRL1 0x06
+#define TACH_CH_DVS       BIT(1)
+#define TACH_CH_SEL       BIT(0)
+
+struct tach_it51xxx_config {
+	/* Tach channel register base address */
+	uintptr_t base;
+	/* Tachometer alternate configuration */
+	const struct pinctrl_dev_config *pcfg;
+	/* Select channel of tachometer */
+	int channel;
+	/* Number of pulses per round of tachometer's input */
+	int pulses_per_round;
+};
+
+struct tach_it51xxx_data {
+	/* Captured counts of tachometer */
+	uint16_t capture;
+};
+
+static bool tach_ch_is_valid(const struct device *dev, int tach_ch)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	uint8_t reg_val;
+
+	if (tach_ch > IT51XXX_TACH_CHANNEL_B) {
+		LOG_ERR("Tach channel %d invalid, only support 0(A) or 1(B)", tach_ch);
+		return false;
+	}
+
+	reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+	if (((reg_val & TACH_CH_SEL) == tach_ch) && (reg_val & TACH_CH_DVS)) {
+		/* Tachometer channel match register setting and data valid */
+		return true;
+	}
+
+	return false;
+}
+
+static int tach_it51xxx_sample_fetch(const struct device *dev, enum sensor_channel chan)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	const uintptr_t base = config->base;
+	int tach_ch = config->channel;
+	struct tach_it51xxx_data *const data = dev->data;
+	uint8_t reg_val;
+
+	if ((chan != SENSOR_CHAN_RPM) && (chan != SENSOR_CHAN_ALL)) {
+		return -ENOTSUP;
+	}
+
+	if (tach_ch_is_valid(dev, tach_ch)) {
+		/* If channel data of tachometer is valid, then save it */
+		data->capture = sys_read16(base + REG_TACH_CH);
+		/* Clear tachometer data valid status */
+		reg_val = sys_read8(base + REG_TACH_CH_CTRL1);
+		sys_write8(reg_val | TACH_CH_DVS, base + REG_TACH_CH_CTRL1);
+	} else {
+		/* If channel data of tachometer isn't valid, then clear it */
+		data->capture = 0;
+	}
+
+	return 0;
+}
+
+static int tach_it51xxx_channel_get(const struct device *dev, enum sensor_channel chan,
+				    struct sensor_value *val)
+{
+	const struct tach_it51xxx_config *const config = dev->config;
+	int p = config->pulses_per_round;
+	struct tach_it51xxx_data *const data = dev->data;
+
+	if (chan != SENSOR_CHAN_RPM) {
+		LOG_ERR("Sensor chan %d, only support SENSOR_CHAN_RPM", chan);
+		return -ENOTSUP;
+	}
+
+	/* Transform count unit to RPM */
+	if (data->capture > 0) {
+		/*
+		 * Fan Speed (RPM) = 60 / (1/fs * {TACH_CH_(H & L)} * P)
+		 * - P denotes the numbers of pulses per round
+		 * - {TACH_CH_(H & L)} = 0000h denotes Fan Speed is zero
+		 * - The sampling rate (fs) is TACH_FREQ / 128
+		 */
+		val->val1 = (60 * TACH_FREQ / 128 / p / (data->capture));
path: drivers/sensor/ite/ite_tach_it51xxx/tach_ite_it51xxx.c
user: login: RuibinChang
body: Done
created_at: 2025-04-24T07:22:21Z
author_association: CONTRIBUTOR

PR comments:
user: login: RuibinChang
created_at: 2025-04-14T02:44:16Z
body: > Needs rebase

Done
author_association: CONTRIBUTOR
user: login: RuibinChang
created_at: 2025-04-16T05:50:25Z
body: rebase to resolve conflicts
author_association: CONTRIBUTOR
user: login: fabiobaltieri
created_at: 2025-04-22T10:19:39Z
body: just a nitpick, good sfuff
author_association: MEMBER
user: login: RuibinChang
created_at: 2025-04-24T07:23:46Z
body: > Looks like copilot feedback is worth addressing (at least the missing check or assert for pulses per round == 0) -- thanks!
> 
> And there is a merge conflict anyway!

Done.
author_association: CONTRIBUTOR
user: login: RuibinChang
created_at: 2025-04-24T07:25:03Z
body: @fabiobaltieri @Dino-Li @kartben  could you revisit? Thanks!
author_association: CONTRIBUTOR