Added support for the AMS AS621x series of temperature sensors as a variant of the TI TMP108 temperature sensor. Signed-off-by: Jared Baumann <jared.baumann8@t-mobile.com> Signed-off-by: James Johnson <james.johnson672@t-mobile.com>
415 lines
9.2 KiB
C
415 lines
9.2 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;
|
|
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(dev),
|
|
TI_TMP108_MODE_ONE_SHOT(dev));
|
|
|
|
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(dev)));
|
|
|
|
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) * TMP108_TEMP_MULTIPLIER(dev)) >> 4U;
|
|
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;
|
|
int32_t uval;
|
|
|
|
if (chan != SENSOR_CHAN_AMBIENT_TEMP && chan != SENSOR_CHAN_ALL) {
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
switch ((int) attr) {
|
|
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 = val->val1 * 1000000 + val->val2;
|
|
reg_value = (uval << 4U) / TMP108_TEMP_MULTIPLIER(dev);
|
|
result = tmp108_reg_write(dev,
|
|
TI_TMP108_REG_LOW_LIMIT,
|
|
reg_value);
|
|
break;
|
|
|
|
case SENSOR_ATTR_UPPER_THRESH:
|
|
uval = val->val1 * 1000000 + val->val2;
|
|
reg_value = (uval << 4U) / TMP108_TEMP_MULTIPLIER(dev);
|
|
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(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;
|
|
|
|
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;
|
|
|
|
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);
|
|
|
|
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), \
|
|
.alert_gpio = GPIO_DT_SPEC_INST_GET_OR(inst, \
|
|
alert_gpios, { 0 }),\
|
|
.reg_def = t##_CONF \
|
|
}; \
|
|
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)
|