drivers: sensors: adt7420: Add driver for ADT7420 Temperature Sensor

This patch adds support for the Analog Devices ADT7420 High-Accuracy
16-bit Digital I2C Temperature Sensors. Optionally sensor threshold
events are supported.

Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
This commit is contained in:
Michael Hennerich 2018-07-09 17:48:09 +02:00 committed by Maureen Helm
commit 21fd91e11e
8 changed files with 619 additions and 0 deletions

View file

@ -1,3 +1,4 @@
add_subdirectory_ifdef(CONFIG_ADT7420 adt7420)
add_subdirectory_ifdef(CONFIG_ADXL362 adxl362) add_subdirectory_ifdef(CONFIG_ADXL362 adxl362)
add_subdirectory_ifdef(CONFIG_AK8975 ak8975) add_subdirectory_ifdef(CONFIG_AK8975 ak8975)
add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx) add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx)

View file

@ -37,6 +37,8 @@ config SENSOR_INIT_PRIORITY
comment "Device Drivers" comment "Device Drivers"
source "drivers/sensor/adt7420/Kconfig"
source "drivers/sensor/adxl362/Kconfig" source "drivers/sensor/adxl362/Kconfig"
source "drivers/sensor/ak8975/Kconfig" source "drivers/sensor/ak8975/Kconfig"

View file

@ -0,0 +1,4 @@
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_ADT7420 adt7420.c)
zephyr_library_sources_ifdef(CONFIG_ADT7420_TRIGGER adt7420_trigger.c)

View file

@ -0,0 +1,123 @@
# Kconfig - ADT7420 temperature sensor configuration options
#
# Copyright (c) 2018 Analog Devices Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
menuconfig ADT7420
bool
prompt "ADT7420 Temperature Sensor"
depends on I2C
help
Enable the driver for Analog Devices ADT7420 High-Accuracy
16-bit Digital I2C Temperature Sensors.
if ADT7420
if !HAS_DTS_I2C_DEVICE
config ADT7420_NAME
string
prompt "Driver name"
default "ADT7420"
help
Device name with which the ADT7420 sensor is identified.
config ADT7420_I2C_ADDR
hex "I2C address for ADT7420"
default 0x48
help
I2C address of the ADT7420 sensor.
0x48: A0 connected GND and A1 connected to GND.
0x49: A0 connected VDD and A1 connected to GND.
0x4A: A0 connected GND and A1 connected to VDD.
0x4B: A0 connected VDD and A1 connected to VDD.
config ADT7420_I2C_MASTER_DEV_NAME
string
prompt "I2C master where ADT7420 is connected"
default "I2C_0"
help
Specify the device name of the I2C master device to which the
ADT7420 chip is connected.
endif # !HAS_DTS_I2C_DEVICE
config ADT7420_TEMP_HYST
int "Temperature hysteresis in °C"
range 0 15
default 5
help
Specify the temperature hysteresis in °C for the THIGH, TLOW,
and TCRIT temperature limits.
config ADT7420_TEMP_CRIT
int "Critical overtemperature in °C"
range -40 150
default 147
help
The critical overtemperature pin asserts when the temperature
exceeds this value. The default value of 147 is the reset default
of the ADT7420.
choice
prompt "Trigger mode"
default ADT7420_TRIGGER_NONE
help
Specify the type of triggering used by the driver.
config ADT7420_TRIGGER_NONE
bool "No trigger"
config ADT7420_TRIGGER_GLOBAL_THREAD
bool "Use global thread"
depends on GPIO
select ADT7420_TRIGGER
config ADT7420_TRIGGER_OWN_THREAD
bool "Use own thread"
depends on GPIO
select ADT7420_TRIGGER
endchoice
config ADT7420_TRIGGER
bool
if !HAS_DTS_GPIO_DEVICE
config ADT7420_GPIO_DEV_NAME
string "GPIO device"
default "GPIO_0"
depends on ADT7420_TRIGGER
help
The GPIO device's name where the ADT7420 interrupt (alert) pin is
connected.
config ADT7420_GPIO_PIN_NUM
int "Interrupt GPIO pin number"
default 0
depends on ADT7420_TRIGGER
help
The GPIO pin number receiving the interrupt signal from the
ADT7420 sensor.
endif # !HAS_DTS_GPIO_DEVICE
config ADT7420_THREAD_PRIORITY
int "Thread priority"
depends on ADT7420_TRIGGER_OWN_THREAD && ADT7420_TRIGGER
default 10
help
Priority of thread used by the driver to handle interrupts.
config ADT7420_THREAD_STACK_SIZE
int "Thread stack size"
depends on ADT7420_TRIGGER_OWN_THREAD && ADT7420_TRIGGER
default 1024
help
Stack size of thread used by the driver to handle interrupts.
endif #ADT7420

View file

@ -0,0 +1,224 @@
/*
* Copyright (c) 2018 Analog Devices Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <i2c.h>
#include <misc/byteorder.h>
#include <misc/util.h>
#include <kernel.h>
#include <sensor.h>
#include <misc/__assert.h>
#include "adt7420.h"
static int adt7420_temp_reg_read(struct device *dev, u8_t reg, s16_t *val)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
if (i2c_burst_read(drv_data->i2c, cfg->i2c_addr,
reg, (u8_t *) val, 2) < 0) {
return -EIO;
}
*val = sys_be16_to_cpu(*val);
return 0;
}
static int adt7420_temp_reg_write(struct device *dev, u8_t reg, s16_t val)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
u8_t buf[3] = {reg, val >> 8, val & 0xFF};
return i2c_write(drv_data->i2c, buf, sizeof(buf), cfg->i2c_addr);
}
static int adt7420_attr_set(struct device *dev,
enum sensor_channel chan,
enum sensor_attribute attr,
const struct sensor_value *val)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
u8_t val8, reg = 0;
u16_t rate;
s64_t value;
if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
switch (attr) {
case SENSOR_ATTR_SAMPLING_FREQUENCY:
rate = val->val1 * 1000 + val->val2 / 1000; /* rate in mHz */
switch (rate) {
case 240:
val8 = ADT7420_OP_MODE_CONT_CONV;
break;
case 1000:
val8 = ADT7420_OP_MODE_1_SPS;
break;
default:
return -EINVAL;
}
if (i2c_reg_update_byte(drv_data->i2c, cfg->i2c_addr,
ADT7420_REG_CONFIG,
ADT7420_CONFIG_OP_MODE(~0),
ADT7420_CONFIG_OP_MODE(val8)) < 0) {
SYS_LOG_DBG("Failed to set attribute!");
return -EIO;
}
return 0;
case SENSOR_ATTR_UPPER_THRESH:
reg = ADT7420_REG_T_HIGH_MSB;
/* Fallthrough */
case SENSOR_ATTR_LOWER_THRESH:
if (!reg) {
reg = ADT7420_REG_T_LOW_MSB;
}
if ((val->val1 > 150) || (val->val1 < -40)) {
return -EINVAL;
}
value = (s64_t)val->val1 * 1000000 + val->val2;
value = (value / ADT7420_TEMP_SCALE) << 1;
if (adt7420_temp_reg_write(dev, reg, value) < 0) {
SYS_LOG_DBG("Failed to set attribute!");
return -EIO;
}
return 0;
default:
return -ENOTSUP;
}
return 0;
}
static int adt7420_sample_fetch(struct device *dev, enum sensor_channel chan)
{
struct adt7420_data *drv_data = dev->driver_data;
s16_t value;
__ASSERT_NO_MSG(chan == SENSOR_CHAN_ALL ||
chan == SENSOR_CHAN_AMBIENT_TEMP);
if (adt7420_temp_reg_read(dev, ADT7420_REG_TEMP_MSB, &value) < 0) {
return -EIO;
}
drv_data->sample = value >> 1; /* use 15-bit only */
return 0;
}
static int adt7420_channel_get(struct device *dev,
enum sensor_channel chan,
struct sensor_value *val)
{
struct adt7420_data *drv_data = dev->driver_data;
s32_t value;
if (chan != SENSOR_CHAN_AMBIENT_TEMP) {
return -ENOTSUP;
}
value = (s32_t)drv_data->sample * ADT7420_TEMP_SCALE;
val->val1 = value / 1000000;
val->val2 = value % 1000000;
return 0;
}
static const struct sensor_driver_api adt7420_driver_api = {
.attr_set = adt7420_attr_set,
.sample_fetch = adt7420_sample_fetch,
.channel_get = adt7420_channel_get,
#ifdef CONFIG_ADT7420_TRIGGER
.trigger_set = adt7420_trigger_set,
#endif
};
static int adt7420_probe(struct device *dev)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
u8_t value;
int ret;
ret = i2c_reg_read_byte(drv_data->i2c, cfg->i2c_addr,
ADT7420_REG_ID, &value);
if (ret) {
return ret;
}
if (value != ADT7420_DEFAULT_ID)
return -ENODEV;
ret = i2c_reg_write_byte(drv_data->i2c, cfg->i2c_addr,
ADT7420_REG_CONFIG, ADT7420_CONFIG_RESOLUTION |
ADT7420_CONFIG_OP_MODE(ADT7420_OP_MODE_CONT_CONV));
if (ret) {
return ret;
}
ret = i2c_reg_write_byte(drv_data->i2c, cfg->i2c_addr,
ADT7420_REG_HIST, CONFIG_ADT7420_TEMP_HYST);
if (ret) {
return ret;
}
ret = adt7420_temp_reg_write(dev, ADT7420_REG_T_CRIT_MSB,
(CONFIG_ADT7420_TEMP_CRIT * 1000000 /
ADT7420_TEMP_SCALE) << 1);
if (ret) {
return ret;
}
#ifdef CONFIG_ADT7420_TRIGGER
if (adt7420_init_interrupt(dev) < 0) {
SYS_LOG_ERR("Failed to initialize interrupt!");
return -EIO;
}
#endif
return 0;
}
static int adt7420_init(struct device *dev)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
drv_data->i2c = device_get_binding(cfg->i2c_port);
if (drv_data->i2c == NULL) {
SYS_LOG_DBG("Failed to get pointer to %s device!",
cfg->i2c_port);
return -EINVAL;
}
return adt7420_probe(dev);
}
static struct adt7420_data adt7420_driver;
static const struct adt7420_dev_config adt7420_config = {
.i2c_port = CONFIG_ADT7420_I2C_MASTER_DEV_NAME,
.i2c_addr = CONFIG_ADT7420_I2C_ADDR,
#ifdef CONFIG_ADT7420_TRIGGER
.gpio_port = CONFIG_ADT7420_GPIO_DEV_NAME,
.int_gpio = CONFIG_ADT7420_GPIO_PIN_NUM,
#endif
};
DEVICE_AND_API_INIT(adt7420, CONFIG_ADT7420_NAME, adt7420_init, &adt7420_driver,
&adt7420_config, POST_KERNEL, CONFIG_SENSOR_INIT_PRIORITY,
&adt7420_driver_api);

View file

@ -0,0 +1,104 @@
/*
* Copyright (c) 2018 Analog Devices Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef __SENSOR_ADT7420_H__
#define __SENSOR_ADT7420_H__
#include <zephyr/types.h>
#include <device.h>
#include <gpio.h>
/* ADT7420 registers */
#define ADT7420_REG_TEMP_MSB 0x00 /* Temperature value MSB */
#define ADT7420_REG_TEMP_LSB 0x01 /* Temperature value LSB */
#define ADT7420_REG_STATUS 0x02 /* Status */
#define ADT7420_REG_CONFIG 0x03 /* Configuration */
#define ADT7420_REG_T_HIGH_MSB 0x04 /* Temperature HIGH setpoint MSB */
#define ADT7420_REG_T_HIGH_LSB 0x05 /* Temperature HIGH setpoint LSB */
#define ADT7420_REG_T_LOW_MSB 0x06 /* Temperature LOW setpoint MSB */
#define ADT7420_REG_T_LOW_LSB 0x07 /* Temperature LOW setpoint LSB */
#define ADT7420_REG_T_CRIT_MSB 0x08 /* Temperature CRIT setpoint MSB */
#define ADT7420_REG_T_CRIT_LSB 0x09 /* Temperature CRIT setpoint LSB */
#define ADT7420_REG_HIST 0x0A /* Temperature HYST setpoint */
#define ADT7420_REG_ID 0x0B /* ID */
#define ADT7420_REG_RESET 0x2F /* Software reset */
/* ADT7420_REG_STATUS definition */
#define ADT7420_STATUS_T_LOW BIT(4)
#define ADT7420_STATUS_T_HIGH BIT(5)
#define ADT7420_STATUS_T_CRIT BIT(6)
#define ADT7420_STATUS_RDY BIT(7)
/* ADT7420_REG_CONFIG definition */
#define ADT7420_CONFIG_FAULT_QUEUE(x) ((x) & 0x3)
#define ADT7420_CONFIG_CT_POL BIT(2)
#define ADT7420_CONFIG_INT_POL BIT(3)
#define ADT7420_CONFIG_INT_CT_MODE BIT(4)
#define ADT7420_CONFIG_OP_MODE(x) (((x) & 0x3) << 5)
#define ADT7420_CONFIG_RESOLUTION BIT(7)
/* ADT7420_CONFIG_FAULT_QUEUE(x) options */
#define ADT7420_FAULT_QUEUE_1_FAULT 0
#define ADT7420_FAULT_QUEUE_2_FAULTS 1
#define ADT7420_FAULT_QUEUE_3_FAULTS 2
#define ADT7420_FAULT_QUEUE_4_FAULTS 3
/* ADT7420_CONFIG_OP_MODE(x) options */
#define ADT7420_OP_MODE_CONT_CONV 0
#define ADT7420_OP_MODE_ONE_SHOT 1
#define ADT7420_OP_MODE_1_SPS 2
#define ADT7420_OP_MODE_SHUTDOWN 3
/* ADT7420 default ID */
#define ADT7420_DEFAULT_ID 0xCB
/* scale in micro degrees Celsius */
#define ADT7420_TEMP_SCALE 15625
struct adt7420_data {
struct device *i2c;
s16_t sample;
#ifdef CONFIG_ADT7420_TRIGGER
struct device *gpio;
struct gpio_callback gpio_cb;
sensor_trigger_handler_t th_handler;
struct sensor_trigger th_trigger;
#if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
K_THREAD_STACK_MEMBER(thread_stack, CONFIG_ADT7420_THREAD_STACK_SIZE);
struct k_sem gpio_sem;
struct k_thread thread;
#elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
struct k_work work;
struct device *dev;
#endif
#endif /* CONFIG_ADT7420_TRIGGER */
};
struct adt7420_dev_config {
const char *i2c_port;
u16_t i2c_addr;
#ifdef CONFIG_ADT7420_TRIGGER
const char *gpio_port;
u8_t int_gpio;
#endif
};
#ifdef CONFIG_ADT7420_TRIGGER
int adt7420_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
int adt7420_init_interrupt(struct device *dev);
#endif /* CONFIG_ADT7420_TRIGGER */
#define SYS_LOG_DOMAIN "ADT7420"
#define SYS_LOG_LEVEL CONFIG_SYS_LOG_SENSOR_LEVEL
#include <logging/sys_log.h>
#endif /* __SENSOR_ADT7420_H__ */

View file

@ -0,0 +1,135 @@
/*
* Copyright (c) 2018 Analog Devices Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <gpio.h>
#include <i2c.h>
#include <misc/util.h>
#include <kernel.h>
#include <sensor.h>
#include "adt7420.h"
static void adt7420_thread_cb(void *arg)
{
struct device *dev = arg;
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
u8_t status;
/* Clear the status */
if (i2c_reg_read_byte(drv_data->i2c, cfg->i2c_addr,
ADT7420_REG_STATUS, &status) < 0) {
return;
}
if (drv_data->th_handler != NULL) {
drv_data->th_handler(dev, &drv_data->th_trigger);
}
gpio_pin_enable_callback(drv_data->gpio, cfg->int_gpio);
}
static void adt7420_gpio_callback(struct device *dev,
struct gpio_callback *cb, u32_t pins)
{
struct adt7420_data *drv_data =
CONTAINER_OF(cb, struct adt7420_data, gpio_cb);
const struct adt7420_dev_config *cfg = dev->config->config_info;
gpio_pin_disable_callback(dev, cfg->int_gpio);
#if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
k_sem_give(&drv_data->gpio_sem);
#elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
k_work_submit(&drv_data->work);
#endif
}
#if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
static void adt7420_thread(int dev_ptr, int unused)
{
struct device *dev = INT_TO_POINTER(dev_ptr);
struct adt7420_data *drv_data = dev->driver_data;
ARG_UNUSED(unused);
while (true) {
k_sem_take(&drv_data->gpio_sem, K_FOREVER);
adt7420_thread_cb(dev);
}
}
#elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
static void adt7420_work_cb(struct k_work *work)
{
struct adt7420_data *drv_data =
CONTAINER_OF(work, struct adt7420_data, work);
adt7420_thread_cb(drv_data->dev);
}
#endif
int adt7420_trigger_set(struct device *dev,
const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
gpio_pin_disable_callback(drv_data->gpio, cfg->int_gpio);
if (trig->type == SENSOR_TRIG_THRESHOLD) {
drv_data->th_handler = handler;
drv_data->th_trigger = *trig;
} else {
SYS_LOG_ERR("Unsupported sensor trigger");
return -ENOTSUP;
}
gpio_pin_enable_callback(drv_data->gpio, cfg->int_gpio);
return 0;
}
int adt7420_init_interrupt(struct device *dev)
{
struct adt7420_data *drv_data = dev->driver_data;
const struct adt7420_dev_config *cfg = dev->config->config_info;
drv_data->gpio = device_get_binding(cfg->gpio_port);
if (drv_data->gpio == NULL) {
SYS_LOG_DBG("Failed to get pointer to %s device!",
cfg->gpio_port);
return -EINVAL;
}
gpio_pin_configure(drv_data->gpio, cfg->int_gpio,
GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE |
GPIO_INT_ACTIVE_LOW | GPIO_INT_DEBOUNCE);
gpio_init_callback(&drv_data->gpio_cb,
adt7420_gpio_callback,
BIT(cfg->int_gpio));
if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) {
SYS_LOG_DBG("Failed to set gpio callback!");
return -EIO;
}
#if defined(CONFIG_ADT7420_TRIGGER_OWN_THREAD)
k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX);
k_thread_create(&drv_data->thread, drv_data->thread_stack,
CONFIG_ADT7420_THREAD_STACK_SIZE,
(k_thread_entry_t)adt7420_thread, dev,
0, NULL, K_PRIO_COOP(CONFIG_ADT7420_THREAD_PRIORITY),
0, 0);
#elif defined(CONFIG_ADT7420_TRIGGER_GLOBAL_THREAD)
drv_data->work.handler = adt7420_work_cb;
drv_data->dev = dev;
#endif
return 0;
}

View file

@ -0,0 +1,26 @@
#
# Copyright (c) 2018 Analog Devices Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
---
title: ADT7420 16-Bit Digital I2C Temperature Sensor
id: adi,adt7420
version: 0.1
description: >
This is a representation of the ADT7420 16-Bit Digital I2C Temperature Sensor
inherits:
!include i2c-device.yaml
properties:
compatible:
constraint: "adi,adt7420"
int-gpios:
type: compound
category: optional
generation: define, use-prop-name
...