zephyr/drivers/sensor/ti/tmp108/tmp108.c
Clément Laigle b921db6608 driver: sensor: as6212: fix sampling frequency
AS6212 supports 0.25Hz, 1Hz, 4Hz, and 8Hz sampling frequencies, but the
current driver supports 0.25Hz, 1Hz, 4Hz, and 16Hz sampling frequencies.

Signed-off-by: Clément Laigle <c.laigle@catie.fr>
2025-06-10 12:09:51 +02:00

441 lines
10 KiB
C

/*
* Copyright (c) 2021 Jimmy Johnson <catch22@fastmail.net>
* Copyright (c) 2022 T-Mobile USA, Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ti_tmp108
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/util.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include <zephyr/kernel.h>
#include "tmp108.h"
LOG_MODULE_REGISTER(TMP108, CONFIG_SENSOR_LOG_LEVEL);
int tmp108_reg_read(const struct device *dev, uint8_t reg, uint16_t *val)
{
const struct tmp108_config *cfg = dev->config;
int result;
result = i2c_burst_read_dt(&cfg->i2c_spec, reg, (uint8_t *) val, 2);
if (result < 0) {
return result;
}
*val = sys_be16_to_cpu(*val);
return 0;
}
int tmp108_reg_write(const struct device *dev, uint8_t reg, uint16_t val)
{
const struct tmp108_config *cfg = dev->config;
uint8_t tx_buf[3];
int result;
tx_buf[0] = reg;
sys_put_be16(val, &tx_buf[1]);
result = i2c_write_dt(&cfg->i2c_spec, tx_buf, sizeof(tx_buf));
if (result < 0) {
return result;
}
return 0;
}
int tmp108_write_config(const struct device *dev, uint16_t mask, uint16_t conf)
{
uint16_t config = 0;
int result;
result = tmp108_reg_read(dev, TI_TMP108_REG_CONF, &config);
if (result < 0) {
return result;
}
config &= mask;
config |= conf;
result = tmp108_reg_write(dev, TI_TMP108_REG_CONF, config);
if (result < 0) {
return result;
}
return 0;
}
int ti_tmp108_read_temp(const struct device *dev)
{
struct tmp108_data *drv_data = dev->data;
int result;
/* clear previous temperature readings */
drv_data->sample = 0U;
/* Get the most recent temperature measurement */
result = tmp108_reg_read(dev, TI_TMP108_REG_TEMP, &drv_data->sample);
if (result < 0) {
return result;
}
return 0;
}
static int tmp108_sample_fetch(const struct device *dev,
enum sensor_channel chan)
{
struct tmp108_data *drv_data = dev->data;
uint16_t config, converting_mask;
int result;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
if (!drv_data->one_shot_mode) {
/* Read the latest temperature result */
return ti_tmp108_read_temp(dev);
}
/* Trigger the conversion */
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK(dev),
TI_TMP108_MODE_ONE_SHOT(dev));
if (result < 0) {
return result;
}
/* Typical conversion time:
* TMP108: 27ms
* AS6212: 36ms
* Maximum conversion time:
* TMP108: 35ms
* AS6212: 51ms
*/
const uint32_t conv_time_min = 25;
const uint32_t conv_time_max = 100;
const uint32_t poll_period = 5;
k_sleep(K_MSEC(conv_time_min));
converting_mask = TI_TMP108_CONF_M1(dev) | TI_TMP108_CONF_M0(dev);
for (int i = conv_time_min; i < conv_time_max; i += poll_period) {
/* Read the config register */
result = tmp108_reg_read(dev, TI_TMP108_REG_CONF, &config);
if (result < 0) {
return result;
}
if ((config & converting_mask) == 0) {
/* Conversion has finished */
LOG_DBG("Conversion complete after %d ms", i);
return ti_tmp108_read_temp(dev);
}
/* Wait before reading again */
k_sleep(K_MSEC(poll_period));
}
/* Conversion timed out */
return -EAGAIN;
}
static int tmp108_channel_get(const struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct tmp108_data *drv_data = dev->data;
int32_t uval;
if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
uval = ((int32_t)drv_data->sample * TMP108_TEMP_MULTIPLIER(dev)) / TMP108_TEMP_DIVISOR(dev);
return sensor_value_from_micro(val, uval);
}
static int tmp108_attr_get(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
struct sensor_value *val)
{
int result;
uint16_t tmp_val;
if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) {
return -ENOTSUP;
}
switch ((int) attr) {
case SENSOR_ATTR_CONFIGURATION:
result = tmp108_reg_read(dev,
TI_TMP108_REG_CONF,
&tmp_val);
val->val1 = tmp_val;
val->val2 = 0;
break;
default:
return -ENOTSUP;
}
return result;
}
static int tmp108_attr_set(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct tmp108_data *drv_data = dev->data;
const struct tmp108_config *config = dev->config;
__maybe_unused uint16_t reg_value;
__maybe_unused int32_t uval;
uint16_t mode;
int result = 0;
if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) {
return -ENOTSUP;
}
switch ((int) attr) {
#ifdef CONFIG_TMP108_ALERT_INTERRUPTS
case SENSOR_ATTR_HYSTERESIS:
if (TI_TMP108_HYSTER_0_C(dev) == TI_TMP108_CONF_NA) {
LOG_WRN("AS621x Series lacks Hysterisis setttings");
return -ENOTSUP;
}
if (val->val1 < 1) {
mode = TI_TMP108_HYSTER_0_C(dev);
} else if (val->val1 < 2) {
mode = TI_TMP108_HYSTER_1_C(dev);
} else if (val->val1 < 4) {
mode = TI_TMP108_HYSTER_2_C(dev);
} else {
mode = TI_TMP108_HYSTER_4_C(dev);
}
result = tmp108_write_config(dev,
TI_TMP108_HYSTER_MASK(dev),
mode);
break;
case SENSOR_ATTR_ALERT:
/* Spec Sheet Errata: TM is set on reset not cleared */
if (val->val1 == 1) {
mode = TI_TMP108_CONF_TM_INT(dev);
} else {
mode = TI_TMP108_CONF_TM_CMP(dev);
}
result = tmp108_write_config(dev,
TI_TMP108_CONF_TM_MASK(dev),
mode);
break;
case SENSOR_ATTR_LOWER_THRESH:
uval = sensor_value_to_micro(val);
reg_value = (uval * TMP108_TEMP_DIVISOR(dev)) / TMP108_TEMP_MULTIPLIER(dev);
result = tmp108_reg_write(dev,
TI_TMP108_REG_LOW_LIMIT,
reg_value);
break;
case SENSOR_ATTR_UPPER_THRESH:
uval = sensor_value_to_micro(val);
reg_value = (uval * TMP108_TEMP_DIVISOR(dev)) / TMP108_TEMP_MULTIPLIER(dev);
result = tmp108_reg_write(dev,
TI_TMP108_REG_HIGH_LIMIT,
reg_value);
break;
case SENSOR_ATTR_TMP108_ALERT_POLARITY:
if (val->val1 == 1) {
mode = TI_TMP108_CONF_POL_HIGH(dev);
} else {
mode = TI_TMP108_CONF_POL_LOW(dev);
}
result = tmp108_write_config(dev,
TI_TMP108_CONF_POL_MASK(dev),
mode);
break;
#endif /* CONFIG_TMP108_ALERT_INTERRUPTS */
case SENSOR_ATTR_SAMPLING_FREQUENCY: {
struct tmp_108_reg_def ams_as6212_reg_def = AMS_AS6212_CONF;
if (memcmp(&config->reg_def, &ams_as6212_reg_def, sizeof(struct tmp_108_reg_def)) ==
0) {
if (val->val1 < 1) {
mode = TI_TMP108_FREQ_4_SECS(dev);
} else if (val->val1 < 4) {
mode = TI_TMP108_FREQ_1_HZ(dev);
} else if (val->val1 < 8) {
mode = TI_TMP108_FREQ_4_HZ(dev);
} else {
mode = AMS_AS6212_FREQ_8_HZ(dev);
}
} else {
if (val->val1 < 1) {
mode = TI_TMP108_FREQ_4_SECS(dev);
} else if (val->val1 < 4) {
mode = TI_TMP108_FREQ_1_HZ(dev);
} else if (val->val1 < 16) {
mode = TI_TMP108_FREQ_4_HZ(dev);
} else {
mode = TI_TMP108_FREQ_16_HZ(dev);
}
}
result = tmp108_write_config(dev,
TI_TMP108_FREQ_MASK(dev),
mode);
break;
}
case SENSOR_ATTR_TMP108_SHUTDOWN_MODE:
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK(dev),
TI_TMP108_MODE_SHUTDOWN(dev));
drv_data->one_shot_mode = false;
break;
case SENSOR_ATTR_TMP108_CONTINUOUS_CONVERSION_MODE:
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK(dev),
TI_TMP108_MODE_CONTINUOUS(dev));
drv_data->one_shot_mode = false;
break;
case SENSOR_ATTR_TMP108_ONE_SHOT_MODE:
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK(dev),
TI_TMP108_MODE_ONE_SHOT(dev));
drv_data->one_shot_mode = true;
break;
default:
return -ENOTSUP;
}
if (result < 0) {
return result;
}
return 0;
}
static DEVICE_API(sensor, tmp108_driver_api) = {
.attr_set = tmp108_attr_set,
.attr_get = tmp108_attr_get,
.sample_fetch = tmp108_sample_fetch,
.channel_get = tmp108_channel_get,
#ifdef CONFIG_TMP108_ALERT_INTERRUPTS
.trigger_set = tmp_108_trigger_set,
#endif
};
#ifdef CONFIG_TMP108_ALERT_INTERRUPTS
static int setup_interrupts(const struct device *dev)
{
struct tmp108_data *drv_data = dev->data;
const struct tmp108_config *config = dev->config;
const struct gpio_dt_spec *alert_gpio = &config->alert_gpio;
int result;
if (!device_is_ready(alert_gpio->port)) {
LOG_ERR("tmp108: gpio controller %s not ready",
alert_gpio->port->name);
return -ENODEV;
}
result = gpio_pin_configure_dt(alert_gpio, GPIO_INPUT);
if (result < 0) {
return result;
}
gpio_init_callback(&drv_data->temp_alert_gpio_cb,
tmp108_trigger_handle_alert,
BIT(alert_gpio->pin));
result = gpio_add_callback(alert_gpio->port,
&drv_data->temp_alert_gpio_cb);
if (result < 0) {
return result;
}
result = gpio_pin_interrupt_configure_dt(alert_gpio,
GPIO_INT_EDGE_BOTH);
if (result < 0) {
return result;
}
return 0;
}
#endif
static int tmp108_init(const struct device *dev)
{
const struct tmp108_config *cfg = dev->config;
int result = 0;
if (!device_is_ready(cfg->i2c_spec.bus)) {
LOG_ERR("I2C dev %s not ready", cfg->i2c_spec.bus->name);
return -ENODEV;
}
#ifdef CONFIG_TMP108_ALERT_INTERRUPTS
struct tmp108_data *drv_data = dev->data;
/* save this driver instance for passing to other functions */
drv_data->tmp108_dev = dev;
result = setup_interrupts(dev);
if (result < 0) {
return result;
}
#endif
/* clear and set configuration registers back to default values */
result = tmp108_write_config(dev,
0x0000,
TMP108_CONF_RST(dev));
return result;
}
#define TMP108_DEFINE(inst, t) \
static struct tmp108_data tmp108_prv_data_##inst##t; \
static const struct tmp108_config tmp108_config_##inst##t = { \
.i2c_spec = I2C_DT_SPEC_INST_GET(inst), \
IF_ENABLED(CONFIG_TMP108_ALERT_INTERRUPTS, \
(.alert_gpio = GPIO_DT_SPEC_INST_GET(inst, alert_gpios),)) \
.reg_def = t##_CONF}; \
SENSOR_DEVICE_DT_INST_DEFINE(inst, &tmp108_init, NULL, &tmp108_prv_data_##inst##t, \
&tmp108_config_##inst##t, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &tmp108_driver_api);
#define TMP108_INIT(n) TMP108_DEFINE(n, TI_TMP108)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT ti_tmp108
DT_INST_FOREACH_STATUS_OKAY(TMP108_INIT)
#define AS6212_INIT(n) TMP108_DEFINE(n, AMS_AS6212)
#undef DT_DRV_COMPAT
#define DT_DRV_COMPAT ams_as6212
DT_INST_FOREACH_STATUS_OKAY(AS6212_INIT)