zephyr/drivers/sensor/ite_tach_it8xxx2/tach_ite_it8xxx2.c

247 lines
7.8 KiB
C

/*
* 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 <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_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)