From fad78a2c078cb753c0d9ddc84b9abb0abee7581f Mon Sep 17 00:00:00 2001 From: Ruibin Chang Date: Tue, 19 Oct 2021 18:33:38 +0800 Subject: [PATCH] ITE drivers/sensor: add tachometer driver for it8xxx2_evb Add tachometer driver for it8xxx2_evb. Signed-off-by: Ruibin Chang --- boards/riscv/it8xxx2_evb/it8xxx2_evb.dts | 8 + drivers/sensor/CMakeLists.txt | 1 + drivers/sensor/Kconfig | 2 + .../sensor/ite_tach_it8xxx2/CMakeLists.txt | 5 + drivers/sensor/ite_tach_it8xxx2/Kconfig | 12 + .../ite_tach_it8xxx2/tach_ite_it8xxx2.c | 255 ++++++++++++++++++ dts/bindings/tach/ite,it8xxx2-tach.yaml | 40 +++ dts/riscv/it8xxx2-alts-map.dtsi | 14 + dts/riscv/it8xxx2.dtsi | 26 ++ include/dt-bindings/sensor/it8xxx2_tach.h | 20 ++ soc/riscv/riscv-ite/common/chip_chipregs.h | 12 + .../it8xxx2/Kconfig.defconfig.series | 4 + 12 files changed, 399 insertions(+) create mode 100644 drivers/sensor/ite_tach_it8xxx2/CMakeLists.txt create mode 100644 drivers/sensor/ite_tach_it8xxx2/Kconfig create mode 100644 drivers/sensor/ite_tach_it8xxx2/tach_ite_it8xxx2.c create mode 100644 dts/bindings/tach/ite,it8xxx2-tach.yaml create mode 100644 include/dt-bindings/sensor/it8xxx2_tach.h diff --git a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts index c179aa93a15..b5c100d578f 100644 --- a/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts +++ b/boards/riscv/it8xxx2_evb/it8xxx2_evb.dts @@ -66,6 +66,7 @@ &ite_uart2_wrapper { status = "okay"; }; +/* pwm for test */ &pwm0 { status = "okay"; prescaler-cx = ; @@ -75,11 +76,18 @@ */ pwm-output-frequency = <324>; }; +/* pwm for fan */ &pwm7 { status = "okay"; prescaler-cx = ; pwm-output-frequency = <30000>; }; +/* fan tachometer sensor */ +&tach0 { + status = "okay"; + channel = ; + pulses-per-round = <2>; +}; &kscan0 { status = "okay"; }; diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 6418309fef1..f2eebcf7334 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -98,6 +98,7 @@ add_subdirectory_ifdef(CONFIG_TACH_XEC mchp_tach_xec) add_subdirectory_ifdef(CONFIG_ITDS wsen_itds) add_subdirectory_ifdef(CONFIG_MCUX_ACMP mcux_acmp) add_subdirectory_ifdef(CONFIG_TACH_NPCX nuvoton_tach_npcx) +add_subdirectory_ifdef(CONFIG_TACH_IT8XXX2 ite_tach_it8xxx2) if(CONFIG_USERSPACE OR CONFIG_SENSOR_SHELL OR CONFIG_SENSOR_SHELL_BATTERY) # The above if() is needed or else CMake would complain about diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 59b7d0c9c10..e5eed6b3e18 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -232,4 +232,6 @@ source "drivers/sensor/mcux_acmp/Kconfig" source "drivers/sensor/nuvoton_tach_npcx/Kconfig" +source "drivers/sensor/ite_tach_it8xxx2/Kconfig" + endif # SENSOR diff --git a/drivers/sensor/ite_tach_it8xxx2/CMakeLists.txt b/drivers/sensor/ite_tach_it8xxx2/CMakeLists.txt new file mode 100644 index 00000000000..9e7d3458979 --- /dev/null +++ b/drivers/sensor/ite_tach_it8xxx2/CMakeLists.txt @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library() + +zephyr_library_sources(tach_ite_it8xxx2.c) diff --git a/drivers/sensor/ite_tach_it8xxx2/Kconfig b/drivers/sensor/ite_tach_it8xxx2/Kconfig new file mode 100644 index 00000000000..5e9f3fe0aed --- /dev/null +++ b/drivers/sensor/ite_tach_it8xxx2/Kconfig @@ -0,0 +1,12 @@ +# ITE tachometer sensor configuration options + +# Copyright (c) 2021 ITE Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +config TACH_IT8XXX2 + bool "ITE it8xxx2 Tachometer sensor" + depends on SOC_IT8XXX2 + help + Enable the ITE it8xxx2 tachometer sensor, + it8xxx2 supports two 16-bit tachometer sensor, each sensor has two + input channel and we can select one input from two channel. diff --git a/drivers/sensor/ite_tach_it8xxx2/tach_ite_it8xxx2.c b/drivers/sensor/ite_tach_it8xxx2/tach_ite_it8xxx2.c new file mode 100644 index 00000000000..e721f800ca9 --- /dev/null +++ b/drivers/sensor/ite_tach_it8xxx2/tach_ite_it8xxx2.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2021 ITE Technology Corporation. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ite_it8xxx2_tach + +/** + * @file + * @brief ITE it8xxx2 tachometer sensor module driver + * + * This file contains a driver for the tachometer sensor module which contains + * two independent counters (F1TL/MRR and F2TL/MRR). The content of the + * Tachometer Reading Register is still update based on the sampling counter + * that samples the tachometer input (T0A, T0B, T1A or T1B pins). + * The following is block diagram of this module: + * + * Sample Rate = TACH_FREQ / 128 + * | + * | Tachometer 0 | T0A (GPD6) + * | | | +-----------+ | + * | +-----+-----+ | | _ _ |<--+ + * |------>| F1TL/MRR |<-+-| | |_| |_ |<--+ + * | +-----------+ +-----------+ | + * | capture pulses T0B (GPJ2) + * | in sample rate + * | period + * +-----------+ | + * Crystal-->| Prescaler |--->| Tachometer 1 T1A (GPD7) + * 32.768k +-----------+ | | +-----------+ | + * | +-----+-----+ | _ _ |<--+ + * |------>| F2TL/MRR |<-+-| | |_| |_ |<--+ + * | +-----------+ +-----------+ | + * | capture pulses T1B (GPJ3) + * | in one second + * | 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_it8xxx2, CONFIG_SENSOR_LOG_LEVEL); + +/* + * NOTE: The PWM output maximum is 324Hz in EC LPM, so if we need fan to work + * then don't let EC enter LPM. + */ +#define TACH_FREQ EC_FREQ + +/* Device config */ +struct tach_alt_cfg { + /* Pinmux control device structure */ + const struct device *pinctrls; + /* GPIO pin */ + uint8_t pin; + /* Alternate function */ + uint8_t alt_fun; +}; + +struct tach_it8xxx2_config { + /* Fan x tachometer LSB reading register */ + uintptr_t reg_fxtlrr; + /* Fan x tachometer MSB reading register */ + uintptr_t reg_fxtmrr; + /* Tachometer switch control register */ + uintptr_t reg_tswctlr; + /* Tachometer data valid bit of tswctlr register */ + int dvs_bit; + /* Tachometer data valid status bit of tswctlr register */ + int chsel_bit; + /* Tachometer alternate configuration list */ + const struct tach_alt_cfg *alt_list; + /* Select channel of tachometer */ + int channel; + /* Number of pulses per round of tachometer's input */ + int pulses_per_round; +}; + +/* Driver data */ +struct tach_it8xxx2_data { + /* Captured counts of tachometer */ + uint32_t capture; +}; + +static bool tach_ch_is_valid(const struct device *dev, int tach_ch) +{ + const struct tach_it8xxx2_config *const config = dev->config; + volatile uint8_t *reg_tswctlr = (uint8_t *)config->reg_tswctlr; + int dvs_bit = config->dvs_bit; + int chsel_bit = config->chsel_bit; + int mask = (dvs_bit | chsel_bit); + bool valid = false; + + switch (tach_ch) { + case IT8XXX2_TACH_CHANNEL_A: + if ((*reg_tswctlr & mask) == dvs_bit) { + valid = true; + } + break; + case IT8XXX2_TACH_CHANNEL_B: + if ((*reg_tswctlr & mask) == mask) { + valid = true; + } + break; + default: + break; + } + + return valid; +} + +static int tach_it8xxx2_sample_fetch(const struct device *dev, + enum sensor_channel chan) +{ + const struct tach_it8xxx2_config *const config = dev->config; + volatile uint8_t *reg_fxtlrr = (uint8_t *)config->reg_fxtlrr; + volatile uint8_t *reg_fxtmrr = (uint8_t *)config->reg_fxtmrr; + volatile uint8_t *reg_tswctlr = (uint8_t *)config->reg_tswctlr; + int tach_ch = config->channel; + struct tach_it8xxx2_data *const data = dev->data; + + 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 = ((*reg_fxtmrr) << 8) | (*reg_fxtlrr); + /* Clear tachometer data valid status */ + *reg_tswctlr |= config->dvs_bit; + } else { + /* If channel data of tachometer isn't valid, then clear it */ + data->capture = 0; + } + + return 0; +} + +static int tach_it8xxx2_channel_get(const struct device *dev, + enum sensor_channel chan, + struct sensor_value *val) +{ + const struct tach_it8xxx2_config *const config = dev->config; + int tachx = ((config->dvs_bit) == IT8XXX2_PWM_T0DVS) ? 0 : 1; + int p = config->pulses_per_round; + struct tach_it8xxx2_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) { + if (tachx == 0) { + /* + * Fan Speed (RPM) = 60 / (1/fs * {F1TMRR, F1TLRR} * P) + * - P denotes the numbers of pulses per round + * - {F1TMRR, F1TLRR} = 0000h denotes Fan Speed is zero + * - The sampling rate (fs) is TACH_FREQ / 128 + */ + val->val1 = (60 * TACH_FREQ / 128 / p / (data->capture)); + } else { + /* + * Fan Speed (RPM) = {F2TMRR, F2TLRR} * 60 / P + * - P denotes the numbers of pulses per round + * - {F2TMRR, F2TLRR} = 0000h denotes Fan Speed is zero + */ + val->val1 = ((data->capture) * 120 / (p * 2)); + } + } else { + val->val1 = 0U; + } + + val->val2 = 0U; + + return 0; +} + +static int tach_it8xxx2_init(const struct device *dev) +{ + const struct tach_it8xxx2_config *const config = dev->config; + volatile uint8_t *reg_tswctlr = (uint8_t *)config->reg_tswctlr; + int tach_ch = config->channel; + + if (tach_ch > IT8XXX2_TACH_CHANNEL_B) { + LOG_ERR("Tach channel %d, only support 0 or 1", tach_ch); + return -EINVAL; + } + + /* Select pin to alternate mode for tachometer */ + pinmux_pin_set(config->alt_list[tach_ch].pinctrls, + config->alt_list[tach_ch].pin, + config->alt_list[tach_ch].alt_fun); + + if (tach_ch == IT8XXX2_TACH_CHANNEL_A) { + /* Select IT8XXX2_TACH_CHANNEL_A output to tachometer */ + *reg_tswctlr &= ~(config->chsel_bit); + /* Clear tachometer data valid status */ + *reg_tswctlr |= config->dvs_bit; + } else { + /* Select IT8XXX2_TACH_CHANNEL_B output to tachometer */ + *reg_tswctlr |= config->chsel_bit; + /* Clear tachometer data valid status */ + *reg_tswctlr |= config->dvs_bit; + } + + /* Tachometer sensor already start */ + return 0; +} + +static const struct sensor_driver_api tach_it8xxx2_driver_api = { + .sample_fetch = tach_it8xxx2_sample_fetch, + .channel_get = tach_it8xxx2_channel_get, +}; + +#define TACH_IT8XXX2_INIT(inst) \ + static const struct tach_alt_cfg \ + tach_alt_##inst[DT_INST_NUM_PINCTRLS_BY_IDX(inst, 0)] = \ + IT8XXX2_DT_ALT_ITEMS_LIST(inst); \ + \ + static const struct tach_it8xxx2_config tach_it8xxx2_cfg_##inst = { \ + .reg_fxtlrr = DT_INST_REG_ADDR_BY_IDX(inst, 0), \ + .reg_fxtmrr = DT_INST_REG_ADDR_BY_IDX(inst, 1), \ + .reg_tswctlr = DT_INST_REG_ADDR_BY_IDX(inst, 2), \ + .dvs_bit = DT_INST_PROP(inst, dvs_bit), \ + .chsel_bit = DT_INST_PROP(inst, chsel_bit), \ + .alt_list = tach_alt_##inst, \ + .channel = DT_INST_PROP(inst, channel), \ + .pulses_per_round = DT_INST_PROP(inst, pulses_per_round), \ + }; \ + \ + static struct tach_it8xxx2_data tach_it8xxx2_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, \ + tach_it8xxx2_init, \ + NULL, \ + &tach_it8xxx2_data_##inst, \ + &tach_it8xxx2_cfg_##inst, \ + POST_KERNEL, \ + CONFIG_SENSOR_INIT_PRIORITY, \ + &tach_it8xxx2_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(TACH_IT8XXX2_INIT) diff --git a/dts/bindings/tach/ite,it8xxx2-tach.yaml b/dts/bindings/tach/ite,it8xxx2-tach.yaml new file mode 100644 index 00000000000..02e34689d46 --- /dev/null +++ b/dts/bindings/tach/ite,it8xxx2-tach.yaml @@ -0,0 +1,40 @@ +# Copyright (c) 2021 ITE Technology Corporation. +# SPDX-License-Identifier: Apache-2.0 + +description: ITE, it8xxx2 Tachometer node + +compatible: "ite,it8xxx2-tach" + +include: tach.yaml + +properties: + reg: + required: true + + dvs-bit: + type: int + required: true + description: tachometer data valid bit of tswctlr register + + chsel-bit: + type: int + required: true + description: tachometer data valid status bit of tswctlr register + + pinctrl-0: + type: phandles + required: true + description: configuration of tachometer pinmux controller + + channel: + type: int + required: true + enum: + - 0 + - 1 + description: 0 = TACH_CHANNEL_A, 1 = TACH_CHANNEL_B + + pulses-per-round: + type: int + required: true + description: number of pulses per round of tachometer's input diff --git a/dts/riscv/it8xxx2-alts-map.dtsi b/dts/riscv/it8xxx2-alts-map.dtsi index a96837599f9..3b0943b4207 100644 --- a/dts/riscv/it8xxx2-alts-map.dtsi +++ b/dts/riscv/it8xxx2-alts-map.dtsi @@ -62,6 +62,20 @@ pinctrls = <&pinmuxa 7 IT8XXX2_PINMUX_FUNC_1>; }; + /* Tachometer alternate function */ + pinctrl_tach0a: tach0a { + pinctrls = <&pinmuxd 6 IT8XXX2_PINMUX_FUNC_1>; + }; + pinctrl_tach0b: tach0b { + pinctrls = <&pinmuxj 2 IT8XXX2_PINMUX_FUNC_3>; + }; + pinctrl_tach1a: tach1a { + pinctrls = <&pinmuxd 7 IT8XXX2_PINMUX_FUNC_1>; + }; + pinctrl_tach1b: tach1b { + pinctrls = <&pinmuxj 3 IT8XXX2_PINMUX_FUNC_3>; + }; + /* I2C alternate function */ pinctrl_i2c_clk0: i2c_clk0 { pinctrls = <&pinmuxb 3 IT8XXX2_PINMUX_FUNC_1>; diff --git a/dts/riscv/it8xxx2.dtsi b/dts/riscv/it8xxx2.dtsi index 39b8190517e..40cc8d2e56c 100644 --- a/dts/riscv/it8xxx2.dtsi +++ b/dts/riscv/it8xxx2.dtsi @@ -6,11 +6,13 @@ */ #include +#include #include #include #include #include #include +#include #include "it8xxx2-alts-map.dtsi" / { @@ -850,6 +852,30 @@ pinctrl-0 = <&pinctrl_pwm7>; /* GPA7 */ #pwm-cells = <2>; }; + tach0: tach@f0181e { + compatible = "ite,it8xxx2-tach"; + reg = <0x00f0181e 1 /* F1TLRR */ + 0x00f0181f 1 /* F1TMRR */ + 0x00f01848 1>; /* TSWCTLR */ + dvs-bit = ; + chsel-bit = ; + label = "TACH_0"; + status = "disabled"; + pinctrl-0 = <&pinctrl_tach0a /* GPD6 */ + &pinctrl_tach0b>; /* GPJ2 */ + }; + tach1: tach@f01820 { + compatible = "ite,it8xxx2-tach"; + reg = <0x00f01820 1 /* F2TLRR */ + 0x00f01821 1 /* F2TMRR */ + 0x00f01848 1>; /* TSWCTLR */ + dvs-bit = ; + chsel-bit = ; + label = "TACH_1"; + status = "disabled"; + pinctrl-0 = <&pinctrl_tach1a /* GPD7 */ + &pinctrl_tach1b>; /* GPJ3 */ + }; gctrl: general-control@f02000 { compatible = "ite,it8xxx2-gctrl"; diff --git a/include/dt-bindings/sensor/it8xxx2_tach.h b/include/dt-bindings/sensor/it8xxx2_tach.h new file mode 100644 index 00000000000..ad4f3627a87 --- /dev/null +++ b/include/dt-bindings/sensor/it8xxx2_tach.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2021 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 channels + * @{ + */ + +/** Tachometer channel A */ +#define IT8XXX2_TACH_CHANNEL_A 0 +/** Tachometer channel B */ +#define IT8XXX2_TACH_CHANNEL_B 1 + +/** @} */ +#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_SENSOR_ITE_TACH_H_ */ diff --git a/soc/riscv/riscv-ite/common/chip_chipregs.h b/soc/riscv/riscv-ite/common/chip_chipregs.h index d9f1ef3e9cd..6f8049dda75 100644 --- a/soc/riscv/riscv-ite/common/chip_chipregs.h +++ b/soc/riscv/riscv-ite/common/chip_chipregs.h @@ -44,6 +44,13 @@ #define REG_BASE_ADDR EC_REG_BASE_ADDR #endif +/* Common definition */ +/* + * EC clock frequency (PWM and tachometer driver need it to reply + * to api or calculate RPM) + */ +#define EC_FREQ MHZ(8) + /** * (10XXh) Shared Memory Flash Interface Bridge (SMFI) */ @@ -640,6 +647,11 @@ struct pwm_it8xxx2_regs { /* PWM register fields */ /* 0x023: PWM Clock Control */ #define IT8XXX2_PWM_PCCE BIT(1) +/* 0x048: Tachometer Switch Control */ +#define IT8XXX2_PWM_T0DVS BIT(3) +#define IT8XXX2_PWM_T0CHSEL BIT(2) +#define IT8XXX2_PWM_T1DVS BIT(1) +#define IT8XXX2_PWM_T1CHSEL BIT(0) /** * diff --git a/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series b/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series index 9c4b5a34681..14fb8dd4085 100644 --- a/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series +++ b/soc/riscv/riscv-ite/it8xxx2/Kconfig.defconfig.series @@ -72,6 +72,10 @@ config PWM_ITE_IT8XXX2 default y depends on PWM +config TACH_IT8XXX2 + default y + depends on SENSOR + if ITE_IT8XXX2_INTC config NUM_IRQS default 185