drivers: sensor: add ti tmp108 driver support

Adding support for the TI TMP108 temperature sensor. This includes
over/under temp interrupt support as well as one shot, continuous
conversion and power down modes.

Signed-off-by: Jimmy Johnson <catch22@fastmail.net>
This commit is contained in:
Jimmy Johnson 2021-10-28 09:29:18 -07:00 committed by Carles Cufí
commit 5c28b218fa
11 changed files with 690 additions and 0 deletions

View file

@ -89,6 +89,7 @@ add_subdirectory_ifdef(CONFIG_STTS751 stts751)
add_subdirectory_ifdef(CONFIG_SX9500 sx9500)
add_subdirectory_ifdef(CONFIG_TH02 th02)
add_subdirectory_ifdef(CONFIG_TMP007 tmp007)
add_subdirectory_ifdef(CONFIG_TMP108 tmp108)
add_subdirectory_ifdef(CONFIG_TMP112 tmp112)
add_subdirectory_ifdef(CONFIG_TMP116 tmp116)
add_subdirectory_ifdef(CONFIG_VCNL4040 vcnl4040)

View file

@ -216,6 +216,8 @@ source "drivers/sensor/th02/Kconfig"
source "drivers/sensor/tmp007/Kconfig"
source "drivers/sensor/tmp108/Kconfig"
source "drivers/sensor/tmp112/Kconfig"
source "drivers/sensor/tmp116/Kconfig"

View file

@ -0,0 +1,5 @@
# SPDX-License-Identifier: Apache-2.0
zephyr_library()
zephyr_library_sources(tmp108.c tmp108_trigger.c)

View file

@ -0,0 +1,20 @@
# TMP108 temperature sensor configuration options
# Copyright (c) 2021 Jimmy Johnson <catch22@fastmail.net>
# SPDX-License-Identifier: Apache-2.0
menuconfig TMP108
bool "TMP108 Temperature Sensor"
depends on I2C
help
Enable driver for the TMP108 temperature sensor.
if TMP108
config TMP108_ALERT_INTERRUPTS
bool "Allow interrupts to service over and under temp alerts"
help
This will set up interrupts to service under and over temp alerts
see TMP108 spec sheet for more information on how these work.
endif # TMP108

View file

@ -0,0 +1,401 @@
/*
* Copyright (c) 2021 Jimmy Johnson <catch22@fastmail.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT ti_tmp108
#include <device.h>
#include <drivers/i2c.h>
#include <drivers/sensor.h>
#include <sys/util.h>
#include <sys/byteorder.h>
#include <logging/log.h>
#include <kernel.h>
#include "tmp108.h"
LOG_MODULE_REGISTER(TMP108, CONFIG_SENSOR_LOG_LEVEL);
/** TI conversion scale from 16 bit int temp value to float */
#define TMP108_TEMP_MULTIPLIER 62500
/** TMP typical conversion time of 27 ms after waking from sleep */
#define TMP108_WAKEUP_TIME_IN_MS 30
struct tmp108_config {
const struct i2c_dt_spec i2c_spec;
const struct gpio_dt_spec alert_gpio;
};
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;
int result;
if (chan != SENSOR_CHAN_ALL && chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
/* If one shot mode is set, query chip for reading
* should be finished 30 ms later
*/
if (drv_data->one_shot_mode == true) {
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK,
TI_TMP108_MODE_ONE_SHOT);
if (result < 0) {
return result;
}
/* Schedule read to start in 30 ms if mode change was successful
* the typical wakeup time given in the data sheet is 27
*/
result = k_work_schedule(&drv_data->scheduled_work,
K_MSEC(TMP108_WAKEUP_TIME_IN_MS));
if (result < 0) {
return result;
}
return 0;
}
result = ti_tmp108_read_temp(dev);
if (result < 0) {
return result;
}
return 0;
}
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 >> 4U) * TMP108_TEMP_MULTIPLIER;
val->val1 = uval / 1000000U;
val->val2 = uval % 1000000U;
return 0;
}
static int tmp108_attr_get(const struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
struct sensor_value *val)
{
int result;
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,
(uint16_t *) &(val->val1));
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;
uint16_t mode = 0;
uint16_t reg_value = 0;
int result = 0;
if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) {
return -ENOTSUP;
}
switch ((int) attr) {
case SENSOR_ATTR_HYSTERESIS:
if (val->val1 < 1) {
mode = TI_TMP108_HYSTER_0_C;
} else if (val->val1 < 2) {
mode = TI_TMP108_HYSTER_1_C;
} else if (val->val1 < 4) {
mode = TI_TMP108_HYSTER_2_C;
} else {
mode = TI_TMP108_HYSTER_4_C;
}
result = tmp108_write_config(dev,
TI_TMP108_HYSTER_MASK,
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;
} else {
mode = TI_TMP108_CONF_TM_CMP;
}
result = tmp108_write_config(dev,
TI_TMP108_CONF_TM_MASK,
mode);
break;
case SENSOR_ATTR_LOWER_THRESH:
reg_value = (val->val1 << 8) | (0x00FF & val->val2);
result = tmp108_reg_write(dev,
TI_TMP108_REG_LOW_LIMIT,
reg_value);
break;
case SENSOR_ATTR_UPPER_THRESH:
reg_value = (val->val1 << 8) | (0x00FF & val->val2);
result = tmp108_reg_write(dev,
TI_TMP108_REG_HIGH_LIMIT,
reg_value);
break;
case SENSOR_ATTR_SAMPLING_FREQUENCY:
if (val->val1 < 1) {
mode = TI_TMP108_FREQ_4_SECS;
} else if (val->val1 < 4) {
mode = TI_TMP108_FREQ_1_HZ;
} else if (val->val1 < 16) {
mode = TI_TMP108_FREQ_4_HZ;
} else {
mode = TI_TMP108_FREQ_16_HZ;
}
result = tmp108_write_config(dev,
TI_TMP108_FREQ_MASK,
mode);
break;
case SENSOR_ATTR_TMP108_SHUTDOWN_MODE:
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK,
TI_TMP108_MODE_SHUTDOWN);
drv_data->one_shot_mode = false;
break;
case SENSOR_ATTR_TMP108_CONTINUOUS_CONVERSION_MODE:
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK,
TI_TMP108_MODE_CONTINUOUS);
drv_data->one_shot_mode = false;
break;
case SENSOR_ATTR_TMP108_ONE_SHOT_MODE:
result = tmp108_write_config(dev,
TI_TMP108_MODE_MASK,
TI_TMP108_MODE_ONE_SHOT);
drv_data->one_shot_mode = true;
break;
case SENSOR_ATTR_TMP108_ALERT_POLARITY:
if (val->val1 == 1) {
mode = TI_TMP108_CONF_POL_HIGH;
} else {
mode = TI_TMP108_CONF_POL_LOW;
}
result = tmp108_write_config(dev,
TI_TMP108_CONF_POL_MASK,
mode);
break;
default:
return -ENOTSUP;
}
if (result < 0) {
return result;
}
return 0;
}
static const struct sensor_driver_api tmp108_driver_api = {
.attr_set = tmp108_attr_set,
.attr_get = tmp108_attr_get,
.sample_fetch = tmp108_sample_fetch,
.channel_get = tmp108_channel_get,
.trigger_set = tmp_108_trigger_set,
};
#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;
struct tmp108_data *drv_data = dev->data;
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;
}
drv_data->scheduled_work.work.handler = tmp108_trigger_handle_one_shot;
/* save this driver instance for passing to other functions */
drv_data->tmp108_dev = dev;
#ifdef CONFIG_TMP108_ALERT_INTERRUPTS
result = setup_interrupts(dev);
#endif
return result;
}
#define TMP108_DEFINE(inst) \
static struct tmp108_data tmp108_prv_data_##inst; \
static const struct tmp108_config tmp108_config_##inst = { \
.i2c_spec = I2C_DT_SPEC_INST_GET(inst), \
.alert_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, \
alert_gpios, { 0 }) \
}; \
DEVICE_DT_INST_DEFINE(inst, \
&tmp108_init, \
NULL, \
&tmp108_prv_data_##inst, \
&tmp108_config_##inst, \
POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, \
&tmp108_driver_api);
DT_INST_FOREACH_STATUS_OKAY(TMP108_DEFINE)

View file

@ -0,0 +1,88 @@
/*
* Copyright (c) 2021 Jimmy Johnson <catch22@fastmail.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_TMP108_TMP108_H_
#define ZEPHYR_DRIVERS_SENSOR_TMP108_TMP108_H_
#include <stdint.h>
#include <drivers/sensor.h>
#include <drivers/gpio.h>
#include <drivers/sensor/tmp108.h>
#define TI_TMP108_REG_TEMP 0x00 /** Temperature register */
#define TI_TMP108_REG_CONF 0x01 /** Configuration register */
#define TI_TMP108_REG_LOW_LIMIT 0x02 /** Low alert set register */
#define TI_TMP108_REG_HIGH_LIMIT 0x03 /** High alert set register */
#define TI_TMP108_CONF_M0 0x0100 /** Mode 1 configuration bit */
#define TI_TMP108_CONF_M1 0x0200 /** Mode 2 configuration bit */
#define TI_TMP108_CONF_CR0 0x2000 /** Conversion rate 1 configuration bit */
#define TI_TMP108_CONF_CR1 0x4000 /** Conversion rate 2 configuration bit */
#define TI_TMP108_CONF_POL 0x0080 /** Alert pin Polarity configuration bit */
#define TI_TMP108_CONF_TM 0x0400 /** Thermostat mode setting bit */
#define TI_TMP108_CONF_HYS1 0x0020 /** Temperature hysteresis config 1 bit */
#define TI_TMP108_CONF_HYS0 0x0010 /** Temperature hysteresis config 2 bit */
#define TI_TMP108_CONF_WFH OVER_TEMP_MASK
#define TI_TMP108_CONF_WFL UNDER_TEMP_MASK
#define TI_TMP108_MODE_SHUTDOWN 0
#define TI_TMP108_MODE_ONE_SHOT TI_TMP108_CONF_M0
#define TI_TMP108_MODE_CONTINUOUS TI_TMP108_CONF_M1
#define TI_TMP108_MODE_MASK ~(TI_TMP108_CONF_M1 | TI_TMP108_CONF_M1)
#define TI_TMP108_FREQ_4_SECS 0
#define TI_TMP108_FREQ_1_HZ TI_TMP108_CONF_CR0
#define TI_TMP108_FREQ_4_HZ TI_TMP108_CONF_CR1
#define TI_TMP108_FREQ_16_HZ (TI_TMP108_CONF_CR1 | TI_TMP108_CONF_CR0)
#define TI_TMP108_FREQ_MASK ~(TI_TMP108_CONF_M1 | TI_TMP108_CONF_M1)
#define TI_TMP108_CONF_POL_LOW 0
#define TI_TMP108_CONF_POL_HIGH TI_TMP108_CONF_POL
#define TI_TMP108_CONF_POL_MASK ~(TI_TMP108_CONF_POL)
#define TI_TMP108_CONF_TM_CMP 0
#define TI_TMP108_CONF_TM_INT TI_TMP108_CONF_TM
#define TI_TMP108_CONF_TM_MASK ~(TI_TMP108_CONF_TM)
#define TI_TMP108_HYSTER_0_C 0
#define TI_TMP108_HYSTER_1_C TI_TMP108_CONF_HYS0
#define TI_TMP108_HYSTER_2_C TI_TMP108_CONF_HYS1
#define TI_TMP108_HYSTER_4_C (TI_TMP108_CONF_HYS1 | TI_TMP108_CONF_HYS0)
#define TI_TMP108_HYSTER_MASK ~(TI_TMP108_CONF_HYS1 | TI_TMP108_CONF_HYS0)
struct tmp108_data {
const struct device *tmp108_dev;
uint16_t sample;
bool one_shot_mode;
struct k_work_delayable scheduled_work;
struct sensor_trigger temp_alert_trigger;
sensor_trigger_handler_t temp_alert_handler;
sensor_trigger_handler_t data_ready_handler;
struct sensor_trigger data_ready_trigger;
struct gpio_callback temp_alert_gpio_cb;
};
int tmp_108_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int tmp108_reg_read(const struct device *dev, uint8_t reg, uint16_t *val);
int ti_tmp108_read_temp(const struct device *dev);
void tmp108_trigger_handle_one_shot(struct k_work *work);
void tmp108_trigger_handle_alert(const struct device *port,
struct gpio_callback *cb,
gpio_port_pins_t pins);
#endif /* ZEPHYR_DRIVERS_SENSOR_TMP108_TMP108_H_ */

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2021 Jimmy Johnson <catch22@fastmail.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <drivers/sensor.h>
#include <logging/log.h>
#include <kernel.h>
#include "tmp108.h"
#define TMP108_ONE_SHOT_RETRY_TIME_IN_MS 10
LOG_MODULE_DECLARE(TMP108, CONFIG_SENSOR_LOG_LEVEL);
void tmp108_trigger_handle_one_shot(struct k_work *work)
{
struct k_work_delayable *delayable_work = CONTAINER_OF(work,
struct k_work_delayable,
work);
struct tmp108_data *drv_data = CONTAINER_OF(delayable_work,
struct tmp108_data,
scheduled_work);
struct sensor_trigger sensor_trigger_type = {
.chan = SENSOR_CHAN_AMBIENT_TEMP,
.type = SENSOR_TRIG_DATA_READY
};
uint16_t config = 0;
bool shutdown_mode = false;
tmp108_reg_read(drv_data->tmp108_dev, TI_TMP108_REG_CONF, &config);
/* check shutdown mode which indicates a one shot read was successful */
shutdown_mode = (config & (TI_TMP108_CONF_M1 | TI_TMP108_CONF_M0)) == 0;
if (shutdown_mode == true) {
ti_tmp108_read_temp(drv_data->tmp108_dev);
} else {
LOG_ERR("Temperature one shot mode read failed, retrying");
/* Typical wake up time is 27 ms, retry if the read fails
* assuming the chip should wake up and take a reading by the time
* 27 ms for the initial wake up time and call of this thread
* plus 10 ms time has passed
*/
k_work_reschedule(&drv_data->scheduled_work,
K_MSEC(TMP108_ONE_SHOT_RETRY_TIME_IN_MS));
return;
}
/* Successful read, call set callbacks */
if (drv_data->data_ready_handler) {
drv_data->data_ready_handler(drv_data->tmp108_dev,
&sensor_trigger_type);
}
}
void tmp108_trigger_handle_alert(const struct device *gpio,
struct gpio_callback *cb,
gpio_port_pins_t pins)
{
struct tmp108_data *drv_data = CONTAINER_OF(cb,
struct tmp108_data,
temp_alert_gpio_cb);
struct sensor_trigger sensor_trigger_type = {
.chan = SENSOR_CHAN_AMBIENT_TEMP,
.type = SENSOR_TRIG_THRESHOLD
};
/* Successful read, call set callbacks */
if (drv_data->temp_alert_handler) {
drv_data->temp_alert_handler(drv_data->tmp108_dev,
&sensor_trigger_type);
}
}
int tmp_108_trigger_set(const struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct tmp108_data *drv_data = dev->data;
if (trig->type == SENSOR_TRIG_DATA_READY) {
drv_data->data_ready_handler = handler;
drv_data->data_ready_trigger = *trig;
return 0;
}
if (trig->type == SENSOR_TRIG_THRESHOLD) {
drv_data->temp_alert_handler = handler;
drv_data->temp_alert_trigger = *trig;
return 0;
}
return -ENOTSUP;
}

View file

@ -0,0 +1,18 @@
# Copyright (c) 2021 Jimmy Johnson <catch22@fastmail.net>
# SPDX-License-Identifier: Apache-2.0
description: |
TMP108 Digital Temperature Sensor. See more info at
https://www.ti.com/product/TMP108
compatible: "ti,tmp108"
include: i2c-device.yaml
properties:
alert-gpios:
type: phandle-array
required: false
description: |
Identifies the ALERT signal, which is active-low open drain when
produced by the sensor.

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2021, Jimmy Johnson <catch22@fastmail.net>
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Extended public API for TI's TMP108 temperature sensor
*
* This exposes attributes for the TMP108 which can be used for
* setting the on-chip Temperature Mode and alert parameters.
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_SENSOR_TMP108_H_
#define ZEPHYR_INCLUDE_DRIVERS_SENSOR_TMP108_H_
#ifdef __cplusplus
extern "C" {
#endif
enum sensor_attribute_tmp_108 {
/** Turn on power saving/one shot mode */
SENSOR_ATTR_TMP108_ONE_SHOT_MODE = SENSOR_ATTR_PRIV_START,
/** Shutdown the sensor */
SENSOR_ATTR_TMP108_SHUTDOWN_MODE,
/** Turn on continuous conversion */
SENSOR_ATTR_TMP108_CONTINUOUS_CONVERSION_MODE,
/** Set the alert pin polarity */
SENSOR_ATTR_TMP108_ALERT_POLARITY
};
/** a mask for the over temp alert bit in the status word*/
#define OVER_TEMP_MASK 0x1000U
/** a mask for the under temp alert bit in the status word*/
#define UNDER_TEMP_MASK 0x0800U
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_SENSOR_TMP108_H_ */

View file

@ -526,6 +526,13 @@ test_i2c_tmp007: tmp007@44 {
int-gpios = <&test_gpio 0 0>;
};
test_i2c_tmp108: tmp108@48 {
compatible = "ti,tmp108";
label = "TMP108";
reg = <0x48>;
alert-gpios = <&test_gpio 0 0>;
};
test_i2c_tmp112: tmp112@45 {
compatible = "ti,tmp112";
label = "TMP112";

View file

@ -94,6 +94,7 @@ CONFIG_TH02=y
CONFIG_TI_HDC=y
CONFIG_TI_HDC20XX=y
CONFIG_TMP007=y
CONFIG_TMP108=y
CONFIG_TMP112=y
CONFIG_TMP116=y
CONFIG_VCNL4040=y