diff --git a/boards/ite/it515xx_evb/it515xx_evb.dts b/boards/ite/it515xx_evb/it515xx_evb.dts index 500a8618b36..4d0ccfb7485 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 = ; + 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 68883d9bc0c..85642b93ee6 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 8d0bf169cea..e03616c5061 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 00000000000..6fc9f192390 --- /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 00000000000..0054c992e25 --- /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 00000000000..94c6d44525f --- /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 +#include +#include +#include +#include +#include +#include + +#include +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 00000000000..c46a634d23a --- /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 1b14d9a16cc..3538a62be56 100644 --- a/dts/riscv/ite/it51xxx.dtsi +++ b/dts/riscv/ite/it51xxx.dtsi @@ -13,6 +13,7 @@ #include #include #include +#include / { #address-cells = <1>; @@ -1023,5 +1024,23 @@ interrupts = ; /* 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 00000000000..c8b9fa28441 --- /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 d589c59460a..10f13429566 100644 --- a/soc/ite/ec/it51xxx/chip_chipregs.h +++ b/soc/ite/ec/it51xxx/chip_chipregs.h @@ -9,6 +9,8 @@ #include +#define IT51XXX_EC_FREQ KHZ(9200) + #ifdef _ASMLANGUAGE #define ECREG(x) x #else