/* * 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 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 */ 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; }; /* 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; int status; 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 */ status = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT); if (status < 0) { LOG_ERR("Failed to configure TACH pins"); return status; } 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) \ PINCTRL_DT_INST_DEFINE(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), \ .pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(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; \ \ SENSOR_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)