diff --git a/drivers/sensor/CMakeLists.txt b/drivers/sensor/CMakeLists.txt index 0354430e37b..26d30767e74 100644 --- a/drivers/sensor/CMakeLists.txt +++ b/drivers/sensor/CMakeLists.txt @@ -1,3 +1,4 @@ +add_subdirectory_ifdef(CONFIG_ADT7420 adt7420) add_subdirectory_ifdef(CONFIG_ADXL362 adxl362) add_subdirectory_ifdef(CONFIG_AK8975 ak8975) add_subdirectory_ifdef(CONFIG_AMG88XX amg88xx) diff --git a/drivers/sensor/Kconfig b/drivers/sensor/Kconfig index 76b0f8e8ed9..7bd7c0f01a7 100644 --- a/drivers/sensor/Kconfig +++ b/drivers/sensor/Kconfig @@ -37,6 +37,8 @@ config SENSOR_INIT_PRIORITY comment "Device Drivers" +source "drivers/sensor/adt7420/Kconfig" + source "drivers/sensor/adxl362/Kconfig" source "drivers/sensor/ak8975/Kconfig" diff --git a/drivers/sensor/adt7420/CMakeLists.txt b/drivers/sensor/adt7420/CMakeLists.txt new file mode 100644 index 00000000000..e3199f7843e --- /dev/null +++ b/drivers/sensor/adt7420/CMakeLists.txt @@ -0,0 +1,4 @@ +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_ADT7420 adt7420.c) +zephyr_library_sources_ifdef(CONFIG_ADT7420_TRIGGER adt7420_trigger.c) diff --git a/drivers/sensor/adt7420/Kconfig b/drivers/sensor/adt7420/Kconfig new file mode 100644 index 00000000000..9741c2a83d0 --- /dev/null +++ b/drivers/sensor/adt7420/Kconfig @@ -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 diff --git a/drivers/sensor/adt7420/adt7420.c b/drivers/sensor/adt7420/adt7420.c new file mode 100644 index 00000000000..a666287ce1a --- /dev/null +++ b/drivers/sensor/adt7420/adt7420.c @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2018 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#include +#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); diff --git a/drivers/sensor/adt7420/adt7420.h b/drivers/sensor/adt7420/adt7420.h new file mode 100644 index 00000000000..d6b24e60c67 --- /dev/null +++ b/drivers/sensor/adt7420/adt7420.h @@ -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 +#include +#include + +/* 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 + +#endif /* __SENSOR_ADT7420_H__ */ diff --git a/drivers/sensor/adt7420/adt7420_trigger.c b/drivers/sensor/adt7420/adt7420_trigger.c new file mode 100644 index 00000000000..f24f251d563 --- /dev/null +++ b/drivers/sensor/adt7420/adt7420_trigger.c @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018 Analog Devices Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include +#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; +} diff --git a/dts/bindings/sensor/adi,adt7420.yaml b/dts/bindings/sensor/adi,adt7420.yaml new file mode 100644 index 00000000000..f9702f1b6ff --- /dev/null +++ b/dts/bindings/sensor/adi,adt7420.yaml @@ -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 + +...