191 lines
4.8 KiB
C
191 lines
4.8 KiB
C
/*
|
|
* 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)
|