/* * Copyright (c) 2024 Aurelien Jarno * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT st_stm32_digi_temp #include #include #include #include #include #include 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 DEVICE_API(sensor, 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)