zephyr/drivers/sensor/mchp_tach_xec/tach_mchp_xec.c

137 lines
3.0 KiB
C

/*
* Copyright (c) 2020 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_tach
#include <errno.h>
#include <device.h>
#include <drivers/sensor.h>
#include <soc.h>
#include <logging/log.h>
LOG_MODULE_REGISTER(tach_xec, CONFIG_SENSOR_LOG_LEVEL);
struct tach_xec_config {
uint32_t base_address;
};
struct tach_xec_data {
uint16_t count;
};
#define FAN_STOPPED 0xFFFFU
#define COUNT_100KHZ_SEC 100000U
#define SEC_TO_MINUTE 60U
#define PIN_STS_TIMEOUT 20U
#define TACH_CTRL_EDGES (CONFIG_TACH_XEC_EDGES << \
MCHP_TACH_CTRL_NUM_EDGES_POS)
#define TACH_XEC_REG_BASE(_dev) \
((TACH_Type *) \
((const struct tach_xec_config * const) \
_dev->config)->base_address)
#define TACH_XEC_CONFIG(_dev) \
(((const struct counter_xec_config * const) \
_dev->config))
#define TACH_XEC_DATA(_dev) \
((struct tach_xec_data *)_dev->data)
int tach_xec_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
ARG_UNUSED(chan);
TACH_Type *tach = TACH_XEC_REG_BASE(dev);
struct tach_xec_data *data = TACH_XEC_DATA(dev);
uint8_t poll_count = 0;
while (poll_count < PIN_STS_TIMEOUT) {
/* See whether internal counter is already latched */
if (tach->STATUS & MCHP_TACH_STS_CNT_RDY) {
data->count =
tach->CONTROL >> MCHP_TACH_CTRL_COUNTER_POS;
break;
}
poll_count++;
/* Allow other threads to run while we sleep */
k_usleep(USEC_PER_MSEC);
}
if (poll_count == PIN_STS_TIMEOUT) {
return -EINVAL;
}
/* We interprept a fan stopped or jammed as 0 */
if (data->count == FAN_STOPPED) {
data->count = 0U;
}
return 0;
}
static int tach_xec_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct tach_xec_data *data = TACH_XEC_DATA(dev);
if (chan != SENSOR_CHAN_RPM) {
return -ENOTSUP;
}
/* Convert the count per 100khz cycles to rpm */
if (data->count != FAN_STOPPED && data->count != 0U) {
val->val1 = (SEC_TO_MINUTE * COUNT_100KHZ_SEC)/data->count;
val->val2 = 0U;
} else {
val->val1 = 0U;
}
val->val2 = 0U;
return 0;
}
static int tach_xec_init(const struct device *dev)
{
TACH_Type *tach = TACH_XEC_REG_BASE(dev);
tach->CONTROL = MCHP_TACH_CTRL_READ_MODE_100K_CLOCK |
TACH_CTRL_EDGES |
MCHP_TACH_CTRL_FILTER_EN |
MCHP_TACH_CTRL_EN;
return 0;
}
static const struct sensor_driver_api tach_xec_driver_api = {
.sample_fetch = tach_xec_sample_fetch,
.channel_get = tach_xec_channel_get,
};
#define TACH_XEC_DEVICE(id) \
static const struct tach_xec_config tach_xec_dev_config##id = { \
.base_address = \
DT_INST_REG_ADDR(id), \
}; \
\
static struct tach_xec_data tach_xec_dev_data##id; \
\
DEVICE_DT_INST_DEFINE(id, \
tach_xec_init, \
device_pm_control_nop, \
&tach_xec_dev_data##id, \
&tach_xec_dev_config##id, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&tach_xec_driver_api);
DT_INST_FOREACH_STATUS_OKAY(TACH_XEC_DEVICE)