blob: d862bb7a29a39137829737e43761c9dc293f0bf4 [file] [log] [blame]
/*
* Copyright (c) 2024 Aurelien Jarno
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT st_stm32_digi_temp
#include <zephyr/device.h>
#include <zephyr/pm/device.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(stm32_digi_temp, CONFIG_SENSOR_LOG_LEVEL);
/* Constants */
#define ONE_MHZ 1000000 /* Hz */
#define TS1_T0_VAL0 30 /* °C */
#define TS1_T0_VAL1 130 /* °C */
#define SAMPLING_TIME 15 /* best precision */
struct stm32_digi_temp_data {
struct k_sem sem_isr;
struct k_mutex mutex;
/* Peripheral clock frequency */
uint32_t pclk_freq;
/* Engineering value of the frequency measured at T0 in Hz */
uint32_t t0_freq;
/* Engineering value of the T0 temperature in °C */
uint16_t t0;
/* Engineering value of the ramp coefficient in Hz / °C */
uint16_t ramp_coeff;
/* Raw sensor value */
uint16_t raw;
};
struct stm32_digi_temp_config {
/* DTS instance. */
DTS_TypeDef *base;
/* Clock configuration. */
struct stm32_pclken pclken;
/* Interrupt configuration. */
void (*irq_config)(const struct device *dev);
};
static void stm32_digi_temp_isr(const struct device *dev)
{
struct stm32_digi_temp_data *data = dev->data;
const struct stm32_digi_temp_config *cfg = dev->config;
DTS_TypeDef *dts = cfg->base;
/* Clear interrupt */
SET_BIT(dts->ICIFR, DTS_ICIFR_TS1_CITEF);
/* Give semaphore */
k_sem_give(&data->sem_isr);
}
static int stm32_digi_temp_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
const struct stm32_digi_temp_config *cfg = dev->config;
struct stm32_digi_temp_data *data = dev->data;
DTS_TypeDef *dts = cfg->base;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}
k_mutex_lock(&data->mutex, K_FOREVER);
/* Wait for the sensor to be ready (~40µS delay after enabling it) */
while (READ_BIT(dts->SR, DTS_SR_TS1_RDY) == 0) {
k_yield();
}
/* Trigger a measurement */
SET_BIT(dts->CFGR1, DTS_CFGR1_TS1_START);
CLEAR_BIT(dts->CFGR1, DTS_CFGR1_TS1_START);
/* Wait for interrupt */
k_sem_take(&data->sem_isr, K_FOREVER);
/* Read value */
data->raw = READ_REG(dts->DR);
k_mutex_unlock(&data->mutex);
return 0;
}
static int stm32_digi_temp_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
struct stm32_digi_temp_data *data = dev->data;
float meas_freq, temp;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_DIE_TEMP) {
return -ENOTSUP;
}
meas_freq = ((float)data->pclk_freq * SAMPLING_TIME) / data->raw;
temp = data->t0 + (meas_freq - data->t0_freq) / data->ramp_coeff;
return sensor_value_from_float(val, temp);
}
static void stm32_digi_temp_configure(const struct device *dev)
{
const struct stm32_digi_temp_config *cfg = dev->config;
struct stm32_digi_temp_data *data = dev->data;
DTS_TypeDef *dts = cfg->base;
int clk_div;
/* Use the prescaler to obtain an internal frequency lower than 1 MHz.
* Allowed values are between 0 and 127.
*/
clk_div = MIN(DIV_ROUND_UP(data->pclk_freq, ONE_MHZ), 127);
MODIFY_REG(dts->CFGR1, DTS_CFGR1_HSREF_CLK_DIV_Msk,
clk_div << DTS_CFGR1_HSREF_CLK_DIV_Pos);
/* Select PCLK as reference clock */
MODIFY_REG(dts->CFGR1, DTS_CFGR1_REFCLK_SEL_Msk,
0 << DTS_CFGR1_REFCLK_SEL_Pos);
/* Select trigger */
MODIFY_REG(dts->CFGR1, DTS_CFGR1_TS1_INTRIG_SEL_Msk,
0 << DTS_CFGR1_TS1_INTRIG_SEL_Pos);
/* Set sampling time */
MODIFY_REG(dts->CFGR1, DTS_CFGR1_TS1_SMP_TIME_Msk,
SAMPLING_TIME << DTS_CFGR1_TS1_SMP_TIME_Pos);
}
static void stm32_digi_temp_enable(const struct device *dev)
{
const struct stm32_digi_temp_config *cfg = dev->config;
DTS_TypeDef *dts = cfg->base;
/* Enable the sensor */
SET_BIT(dts->CFGR1, DTS_CFGR1_TS1_EN);
/* Enable interrupt */
SET_BIT(dts->ITENR, DTS_ITENR_TS1_ITEEN);
}
#ifdef CONFIG_PM_DEVICE
static void stm32_digi_temp_disable(const struct device *dev)
{
const struct stm32_digi_temp_config *cfg = dev->config;
DTS_TypeDef *dts = cfg->base;
/* Disable interrupt */
CLEAR_BIT(dts->ITENR, DTS_ITENR_TS1_ITEEN);
/* Disable the sensor */
CLEAR_BIT(dts->CFGR1, DTS_CFGR1_TS1_EN);
}
#endif
static int stm32_digi_temp_init(const struct device *dev)
{
const struct stm32_digi_temp_config *cfg = dev->config;
struct stm32_digi_temp_data *data = dev->data;
DTS_TypeDef *dts = cfg->base;
/* enable clock for subsystem */
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
if (!device_is_ready(clk)) {
LOG_ERR("Clock control device not ready");
return -ENODEV;
}
if (clock_control_on(clk, (clock_control_subsys_t) &cfg->pclken) != 0) {
LOG_ERR("Could not enable DTS clock");
return -EIO;
}
/* Save the peripheral clock frequency in the data structure to avoid
* querying it for each call to the channel_get method.
*/
if (clock_control_get_rate(clk, (clock_control_subsys_t) &cfg->pclken,
&data->pclk_freq) < 0) {
LOG_ERR("Failed call clock_control_get_rate(pclken)");
return -EIO;
}
/* Save the calibration data in the data structure to avoid reading
* them for each call to the channel_get method, as this requires
* enabling the peripheral clock.
*/
data->ramp_coeff = dts->RAMPVALR & DTS_RAMPVALR_TS1_RAMP_COEFF;
data->t0_freq = (dts->T0VALR1 & DTS_T0VALR1_TS1_FMT0) * 100; /* 0.1 kHz -> Hz */
/* T0 temperature from the datasheet */
switch (dts->T0VALR1 >> DTS_T0VALR1_TS1_T0_Pos) {
case 0:
data->t0 = TS1_T0_VAL0;
break;
case 1:
data->t0 = TS1_T0_VAL1;
break;
default:
LOG_ERR("Unknown T0 temperature value");
return -EIO;
}
/* Init mutex and semaphore */
k_mutex_init(&data->mutex);
k_sem_init(&data->sem_isr, 0, 1);
/* Configure and enable the sensor */
cfg->irq_config(dev);
stm32_digi_temp_configure(dev);
stm32_digi_temp_enable(dev);
return 0;
}
#ifdef CONFIG_PM_DEVICE
static int stm32_digi_temp_pm_action(const struct device *dev, enum pm_device_action action)
{
const struct stm32_digi_temp_config *cfg = dev->config;
const struct device *const clk = DEVICE_DT_GET(STM32_CLOCK_CONTROL_NODE);
int err;
switch (action) {
case PM_DEVICE_ACTION_RESUME:
/* enable clock */
err = clock_control_on(clk, (clock_control_subsys_t)&cfg->pclken);
if (err != 0) {
LOG_ERR("Could not enable DTS clock");
return err;
}
/* Enable sensor */
stm32_digi_temp_enable(dev);
break;
case PM_DEVICE_ACTION_SUSPEND:
/* Disable sensor */
stm32_digi_temp_disable(dev);
/* Stop device clock */
err = clock_control_off(clk, (clock_control_subsys_t)&cfg->pclken);
if (err != 0) {
LOG_ERR("Could not disable DTS clock");
return err;
}
break;
default:
return -ENOTSUP;
}
return 0;
}
#endif /* CONFIG_PM_DEVICE */
static const struct sensor_driver_api stm32_digi_temp_driver_api = {
.sample_fetch = stm32_digi_temp_sample_fetch,
.channel_get = stm32_digi_temp_channel_get,
};
#define STM32_DIGI_TEMP_INIT(index) \
static void stm32_digi_temp_irq_config_func_##index(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(index), \
DT_INST_IRQ(index, priority), \
stm32_digi_temp_isr, DEVICE_DT_INST_GET(index), 0); \
irq_enable(DT_INST_IRQN(index)); \
} \
\
static struct stm32_digi_temp_data stm32_digi_temp_dev_data_##index; \
\
static const struct stm32_digi_temp_config stm32_digi_temp_dev_config_##index = { \
.base = (DTS_TypeDef *)DT_INST_REG_ADDR(index), \
.pclken = { \
.enr = DT_INST_CLOCKS_CELL(index, bits), \
.bus = DT_INST_CLOCKS_CELL(index, bus) \
}, \
.irq_config = stm32_digi_temp_irq_config_func_##index, \
}; \
\
PM_DEVICE_DT_INST_DEFINE(index, stm32_digi_temp_pm_action); \
\
SENSOR_DEVICE_DT_INST_DEFINE(index, stm32_digi_temp_init, \
PM_DEVICE_DT_INST_GET(index), \
&stm32_digi_temp_dev_data_##index, \
&stm32_digi_temp_dev_config_##index, \
POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY, \
&stm32_digi_temp_driver_api); \
DT_INST_FOREACH_STATUS_OKAY(STM32_DIGI_TEMP_INIT)