blob: 97457cbca505cea8966a0fa025b72230f30fae77 [file] [log] [blame]
/*
* Copyright (c) 2020 Intel Corporation
* Copyright (c) 2022 Microchip Technology Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT microchip_xec_tach
#include <errno.h>
#include <zephyr/device.h>
#ifdef CONFIG_SOC_SERIES_MEC172X
#include <zephyr/drivers/clock_control/mchp_xec_clock_control.h>
#include <zephyr/drivers/interrupt_controller/intc_mchp_xec_ecia.h>
#endif
#ifdef CONFIG_PINCTRL
#include <zephyr/drivers/pinctrl.h>
#endif
#include <zephyr/drivers/sensor.h>
#include <soc.h>
#include <zephyr/sys/sys_io.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(tach_xec, CONFIG_SENSOR_LOG_LEVEL);
struct tach_xec_config {
struct tach_regs * const regs;
uint8_t girq;
uint8_t girq_pos;
uint8_t pcr_idx;
uint8_t pcr_pos;
#ifdef CONFIG_PINCTRL
const struct pinctrl_dev_config *pcfg;
#endif
};
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)
int tach_xec_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
ARG_UNUSED(chan);
const struct tach_xec_config * const cfg = dev->config;
struct tach_xec_data * const data = dev->data;
struct tach_regs * const tach = cfg->regs;
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 interpret 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 * const data = dev->data;
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 void tach_xec_sleep_clr(const struct device *dev)
{
const struct tach_xec_config * const cfg = dev->config;
struct pcr_regs * const pcr = (struct pcr_regs * const)(
DT_REG_ADDR_BY_IDX(DT_NODELABEL(pcr), 0));
#ifdef CONFIG_SOC_SERIES_MEC172X
pcr->SLP_EN[cfg->pcr_idx] &= ~BIT(cfg->pcr_pos);
#else
uintptr_t addr = (uintptr_t)&pcr->SLP_EN0 + (4u * cfg->pcr_idx);
uint32_t pcr_val = sys_read32(addr) & ~BIT(cfg->pcr_pos);
sys_write32(pcr_val, addr);
#endif
}
static int tach_xec_init(const struct device *dev)
{
const struct tach_xec_config * const cfg = dev->config;
struct tach_regs * const tach = cfg->regs;
#ifdef CONFIG_PINCTRL
int ret = pinctrl_apply_state(cfg->pcfg, PINCTRL_STATE_DEFAULT);
if (ret != 0) {
LOG_ERR("XEC TACH pinctrl init failed (%d)", ret);
return ret;
}
#endif
tach_xec_sleep_clr(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,
};
#ifdef CONFIG_PINCTRL
#define XEC_TACH_PINCTRL_DEF(inst) PINCTRL_DT_INST_DEFINE(inst)
#define XEC_TACH_CONFIG(inst) \
static const struct tach_xec_config tach_xec_config_##inst = { \
.regs = (struct tach_regs * const)DT_INST_REG_ADDR(inst), \
.girq = DT_INST_PROP_BY_IDX(inst, girqs, 0), \
.girq_pos = DT_INST_PROP_BY_IDX(inst, girqs, 1), \
.pcr_idx = DT_INST_PROP_BY_IDX(inst, pcrs, 0), \
.pcr_pos = DT_INST_PROP_BY_IDX(inst, pcrs, 1), \
.pcfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
}
#else
#define XEC_TACH_PINCTRL_DEF(inst)
#define XEC_TACH_CONFIG(inst) \
static const struct tach_xec_config tach_xec_config_##inst = { \
.regs = (struct tach_regs * const)DT_INST_REG_ADDR(inst), \
.girq = DT_INST_PROP_BY_IDX(inst, girqs, 0), \
.girq_pos = DT_INST_PROP_BY_IDX(inst, girqs, 1), \
.pcr_idx = DT_INST_PROP_BY_IDX(inst, pcrs, 0), \
.pcr_pos = DT_INST_PROP_BY_IDX(inst, pcrs, 1), \
}
#endif
#define TACH_XEC_DEVICE(id) \
static struct tach_xec_data tach_xec_data_##id; \
\
XEC_TACH_PINCTRL_DEF(id); \
\
XEC_TACH_CONFIG(id); \
\
DEVICE_DT_INST_DEFINE(id, \
tach_xec_init, \
NULL, \
&tach_xec_data_##id, \
&tach_xec_config_##id, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&tach_xec_driver_api);
DT_INST_FOREACH_STATUS_OKAY(TACH_XEC_DEVICE)