From 11295c190b81db77f5f7b3642bf64a45faaf83ed Mon Sep 17 00:00:00 2001 From: Henrik Malvik Halvorsen Date: Fri, 8 Mar 2019 12:27:54 +0100 Subject: [PATCH] drivers: sensor: Add ADXL362 interrupt handling The ADXL362 driver is expanded to support interrupt handling. Signed-off-by: Henrik Malvik Halvorsen --- drivers/sensor/adxl362/CMakeLists.txt | 1 + drivers/sensor/adxl362/Kconfig | 71 +++++++ drivers/sensor/adxl362/adxl362.c | 169 +++++++++++++++- drivers/sensor/adxl362/adxl362.h | 49 +++++ drivers/sensor/adxl362/adxl362_trigger.c | 189 ++++++++++++++++++ dts/bindings/sensor/adi,adxl362.yaml | 5 + .../build_all/sensors_trigger_a_h.conf | 2 + 7 files changed, 483 insertions(+), 3 deletions(-) create mode 100644 drivers/sensor/adxl362/adxl362_trigger.c diff --git a/drivers/sensor/adxl362/CMakeLists.txt b/drivers/sensor/adxl362/CMakeLists.txt index a6c75f73be3..ee4fa6b1135 100644 --- a/drivers/sensor/adxl362/CMakeLists.txt +++ b/drivers/sensor/adxl362/CMakeLists.txt @@ -3,3 +3,4 @@ zephyr_library() zephyr_library_sources_ifdef(CONFIG_ADXL362 adxl362.c) +zephyr_library_sources_ifdef(CONFIG_ADXL362_TRIGGER adxl362_trigger.c) diff --git a/drivers/sensor/adxl362/Kconfig b/drivers/sensor/adxl362/Kconfig index 0d69fa8520f..8af4789a817 100644 --- a/drivers/sensor/adxl362/Kconfig +++ b/drivers/sensor/adxl362/Kconfig @@ -58,4 +58,75 @@ config ADXL362_ACCEL_ODR_400 endchoice +choice + prompt "Trigger mode" + default ADXL362_TRIGGER_NONE + help + Specify the type of triggering used by the driver. + +config ADXL362_TRIGGER_NONE + bool "No trigger" + +config ADXL362_TRIGGER_GLOBAL_THREAD + bool "Use global thread" + depends on GPIO + select ADXL362_TRIGGER + +config ADXL362_TRIGGER_OWN_THREAD + bool "Use own thread" + depends on GPIO + select ADXL362_TRIGGER + +endchoice + +config ADXL362_TRIGGER + bool + +config ADXL362_THREAD_PRIORITY + int "Thread priority" + depends on ADXL362_TRIGGER_OWN_THREAD && ADXL362_TRIGGER + default 10 + help + Priority of thread used by the driver to handle interrupts. + +config ADXL362_THREAD_STACK_SIZE + int "Thread stack size" + depends on ADXL362_TRIGGER_OWN_THREAD && ADXL362_TRIGGER + default 1024 + help + Stack size of thread used by the driver to handle interrupts. + +config ADXL362_ACTIVITY_THRESHOLD + int "Upper threshold value" + default 1000 + help + Unsigned value that the adxl362 samples are + compared to in activity trigger mode. + +config ADXL362_INACTIVITY_THRESHOLD + int "Lower threshold value" + default 100 + help + Unsigned value that the adxl362 samples are + compared to in inactivity trigger mode. + +config ADXL362_INTERRUPT_MODE + int "Activity and inactivity interrupt mode" + default 0 + help + Unsigned value that sets the ADXL362 in different + interrupt modes. + 0 - Default mode + 1 - Linked mode + 3 - Loop mode + +config ADXL362_ABS_REF_MODE + int "Absolute or referenced interrupt" + default 0 + help + Unsigned value that sets the ADXL362 interrupt + mode in either absolute or referenced mode. + 0 - Absolute mode + 1 - Referenced mode + endif # ADXL362 diff --git a/drivers/sensor/adxl362/adxl362.c b/drivers/sensor/adxl362/adxl362.c index ef34192d6bf..269af08e3af 100644 --- a/drivers/sensor/adxl362/adxl362.c +++ b/drivers/sensor/adxl362/adxl362.c @@ -69,6 +69,33 @@ static inline int adxl362_set_reg(struct device *dev, u16_t register_value, count); } +int adxl362_reg_write_mask(struct device *dev, u8_t register_address, + u8_t mask, u8_t data) +{ + int ret; + u8_t tmp; + struct adxl362_data *adxl362_data = dev->driver_data; + + ret = adxl362_reg_access(adxl362_data, + ADXL362_READ_REG, + register_address, + &tmp, + 1); + + if (ret) { + return ret; + } + + tmp &= ~mask; + tmp |= data; + + return adxl362_reg_access(adxl362_data, + ADXL362_WRITE_REG, + register_address, + &tmp, + 1); +} + static inline int adxl362_get_reg(struct device *dev, u8_t *read_buf, u8_t register_address, u8_t count) { @@ -80,6 +107,37 @@ static inline int adxl362_get_reg(struct device *dev, u8_t *read_buf, read_buf, count); } +#if defined(CONFIG_ADXL362_TRIGGER) +static int adxl362_interrupt_config(struct device *dev, + u8_t int1, + u8_t int2) +{ + int ret; + struct adxl362_data *adxl362_data = dev->driver_data; + + ret = adxl362_reg_access(adxl362_data, + ADXL362_WRITE_REG, + ADXL362_REG_INTMAP1, + &int1, + 1); + + if (ret) { + return ret; + } + + return ret = adxl362_reg_access(adxl362_data, + ADXL362_WRITE_REG, + ADXL362_REG_INTMAP2, + &int2, + 1); +} + +int adxl362_get_status(struct device *dev, u8_t *status) +{ + return adxl362_get_reg(dev, status, ADXL362_REG_STATUS, 1); +} +#endif + static int adxl362_software_reset(struct device *dev) { return adxl362_set_reg(dev, ADXL362_RESET_KEY, @@ -248,16 +306,54 @@ static int axl362_acc_config(struct device *dev, enum sensor_channel chan, return 0; } +static int adxl362_attr_set_thresh(struct device *dev, enum sensor_channel chan, + enum sensor_attribute attr, + const struct sensor_value *val) +{ + u8_t reg; + u16_t threshold = val->val1; + size_t ret; + + if (chan != SENSOR_CHAN_ACCEL_X && + chan != SENSOR_CHAN_ACCEL_Y && + chan != SENSOR_CHAN_ACCEL_Z) { + return -EINVAL; + } + + if (threshold > 2047) { + return -EINVAL; + } + + /* Configure motion threshold. */ + if (attr == SENSOR_ATTR_UPPER_THRESH) { + reg = ADXL362_REG_THRESH_ACT_L; + } else { + reg = ADXL362_REG_THRESH_INACT_L; + } + + ret = adxl362_set_reg(dev, (threshold & 0x7FF), reg, 2); + + return ret; +} + static int adxl362_attr_set(struct device *dev, enum sensor_channel chan, enum sensor_attribute attr, const struct sensor_value *val) { + switch (attr) { + case SENSOR_ATTR_UPPER_THRESH: + case SENSOR_ATTR_LOWER_THRESH: + return adxl362_attr_set_thresh(dev, chan, attr, val); + default: + /* Do nothing */ + break; + } + switch (chan) { case SENSOR_CHAN_ACCEL_X: case SENSOR_CHAN_ACCEL_Y: case SENSOR_CHAN_ACCEL_Z: case SENSOR_CHAN_ACCEL_XYZ: return axl362_acc_config(dev, chan, attr, val); - default: LOG_DBG("attr_set() not supported on this channel."); return -ENOTSUP; @@ -407,6 +503,43 @@ static int adxl362_setup_inactivity_detection(struct device *dev, return 0; } +int adxl362_set_interrupt_mode(struct device *dev, u8_t mode) +{ + u8_t old_act_inact_reg; + u8_t new_act_inact_reg; + int ret; + + printk("Mode: %d \n", mode); + + if (mode != ADXL362_MODE_DEFAULT && + mode != ADXL362_MODE_LINK && + mode != ADXL362_MODE_LOOP) { + printk("Wrong mode \n"); + return -EINVAL; + } + + /* Select desired interrupt mode. */ + ret = adxl362_get_reg(dev, &old_act_inact_reg, + ADXL362_REG_ACT_INACT_CTL, 1); + if (ret) { + return ret; + } + + new_act_inact_reg = old_act_inact_reg & + ~ADXL362_ACT_INACT_CTL_LINKLOOP(3); + new_act_inact_reg |= old_act_inact_reg | + ADXL362_ACT_INACT_CTL_LINKLOOP(mode); + + ret = adxl362_set_reg(dev, new_act_inact_reg, + ADXL362_REG_ACT_INACT_CTL, 1); + + if (ret) { + return ret; + } + + return 0; +} + static int adxl362_sample_fetch(struct device *dev, enum sensor_channel chan) { struct adxl362_data *data = dev->driver_data; @@ -484,6 +617,9 @@ static const struct sensor_driver_api adxl362_api_funcs = { .attr_set = adxl362_attr_set, .sample_fetch = adxl362_sample_fetch, .channel_get = adxl362_channel_get, +#ifdef CONFIG_ADXL362_TRIGGER + .trigger_set = adxl362_trigger_set, +#endif }; static int adxl362_chip_init(struct device *dev) @@ -503,7 +639,11 @@ static int adxl362_chip_init(struct device *dev) * time / ODR, * where ODR - is the output data rate. */ - ret = adxl362_setup_activity_detection(dev, 0, 250, 1); + ret = + adxl362_setup_activity_detection(dev, + CONFIG_ADXL362_ABS_REF_MODE, + CONFIG_ADXL362_ACTIVITY_THRESHOLD, + 1); if (ret) { return ret; } @@ -521,7 +661,11 @@ static int adxl362_chip_init(struct device *dev) * time / ODR, * where ODR - is the output data rate. */ - ret = adxl362_setup_inactivity_detection(dev, 0, 100, 1); + ret = + adxl362_setup_inactivity_detection(dev, + CONFIG_ADXL362_ABS_REF_MODE, + CONFIG_ADXL362_INACTIVITY_THRESHOLD, + 1); if (ret) { return ret; } @@ -624,6 +768,21 @@ static int adxl362_init(struct device *dev) return -ENODEV; } +#if defined(CONFIG_ADXL362_TRIGGER) + if (adxl362_init_interrupt(dev) < 0) { + LOG_ERR("Failed to initialize interrupt!"); + return -EIO; + } + + err = adxl362_interrupt_config(dev, + config->int1_config, + config->int2_config); +#endif + + if (err) { + return err; + } + return 0; } @@ -635,6 +794,10 @@ static const struct adxl362_config adxl362_config = { .gpio_cs_port = DT_ADI_ADXL362_0_CS_GPIO_CONTROLLER, .cs_gpio = DT_ADI_ADXL362_0_CS_GPIO_PIN, #endif +#if defined(CONFIG_ADXL362_TRIGGER) + .gpio_port = DT_ADI_ADXL362_0_INT1_GPIOS_CONTROLLER, + .int_gpio = DT_ADI_ADXL362_0_INT1_GPIOS_PIN, +#endif }; DEVICE_AND_API_INIT(adxl362, DT_ADI_ADXL362_0_LABEL, adxl362_init, diff --git a/drivers/sensor/adxl362/adxl362.h b/drivers/sensor/adxl362/adxl362.h index e10b3eb6171..eac776fb9cd 100644 --- a/drivers/sensor/adxl362/adxl362.h +++ b/drivers/sensor/adxl362/adxl362.h @@ -9,6 +9,8 @@ #include #include +#include +#include #define ADXL362_SLAVE_ID 1 @@ -154,6 +156,10 @@ /* ADXL362 Reset settings */ #define ADXL362_RESET_KEY 0x52 +/* ADXL362 Status check */ +#define ADXL362_STATUS_CHECK_INACT(x) (((x) >> 5) & 0x1) +#define ADXL362_STATUS_CHECK_ACTIVITY(x) (((x) >> 4) & 0x1) + struct adxl362_config { char *spi_name; u32_t spi_max_frequency; @@ -162,6 +168,12 @@ struct adxl362_config { const char *gpio_cs_port; u8_t cs_gpio; #endif +#if defined(CONFIG_ADXL362_TRIGGER) + const char *gpio_port; + u8_t int_gpio; + u8_t int1_config; + u8_t int2_config; +#endif }; struct adxl362_data { @@ -175,6 +187,26 @@ struct adxl362_data { s32_t acc_z; s32_t temp; u8_t selected_range; + +#if defined(CONFIG_ADXL362_TRIGGER) + struct device *gpio; + struct gpio_callback gpio_cb; + struct k_mutex trigger_mutex; + + sensor_trigger_handler_t th_handler; + struct sensor_trigger th_trigger; + sensor_trigger_handler_t drdy_handler; + struct sensor_trigger drdy_trigger; + +#if defined(CONFIG_ADXL362_TRIGGER_OWN_THREAD) + K_THREAD_STACK_MEMBER(thread_stack, CONFIG_ADXL362_THREAD_STACK_SIZE); + struct k_sem gpio_sem; + struct k_thread thread; +#elif defined(CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD) + struct k_work work; + struct device *dev; +#endif +#endif /* CONFIG_ADXL362_TRIGGER */ }; #if defined(CONFIG_ADXL362_ACCEL_RANGE_RUNTIME) ||\ @@ -201,4 +233,21 @@ struct adxl362_data { # define ADXL362_DEFAULT_ODR_ACC ADXL362_ODR_400_HZ #endif +#ifdef CONFIG_ADXL362_TRIGGER +int adxl362_reg_write_mask(struct device *dev, + u8_t reg_addr, u8_t mask, u8_t data); + +int adxl362_get_status(struct device *dev, u8_t *status); + +int adxl362_interrupt_activity_enable(struct device *dev); + +int adxl362_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler); + +int adxl362_init_interrupt(struct device *dev); + +int adxl362_set_interrupt_mode(struct device *dev, u8_t mode); +#endif /* CONFIG_ADT7420_TRIGGER */ + #endif /* ZEPHYR_DRIVERS_SENSOR_ADXL362_ADXL362_H_ */ diff --git a/drivers/sensor/adxl362/adxl362_trigger.c b/drivers/sensor/adxl362/adxl362_trigger.c new file mode 100644 index 00000000000..aae373dff64 --- /dev/null +++ b/drivers/sensor/adxl362/adxl362_trigger.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include + +#include "adxl362.h" + +#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL +#include +LOG_MODULE_DECLARE(ADXL362); + +static void adxl362_thread_cb(void *arg) +{ + struct device *dev = arg; + struct adxl362_data *drv_data = dev->driver_data; + const struct adxl362_config *cfg = dev->config->config_info; + u8_t status_buf; + int ret; + + k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER); + if (drv_data->th_handler != NULL) { + ret = adxl362_get_status(dev, &status_buf); + + if (ret) { + LOG_ERR("Unable to get status from ADXL362.\n"); + } + + if (ADXL362_STATUS_CHECK_INACT(status_buf) || + ADXL362_STATUS_CHECK_ACTIVITY(status_buf)) { + drv_data->th_handler(dev, &drv_data->th_trigger); + } + } + + if (drv_data->drdy_handler != NULL) { + drv_data->drdy_handler(dev, &drv_data->drdy_trigger); + } + k_mutex_unlock(&drv_data->trigger_mutex); + + gpio_pin_enable_callback(drv_data->gpio, cfg->int_gpio); +} + +static void adxl362_gpio_callback(struct device *dev, + struct gpio_callback *cb, u32_t pins) +{ + struct adxl362_data *drv_data = + CONTAINER_OF(cb, struct adxl362_data, gpio_cb); + const struct adxl362_config *cfg = dev->config->config_info; + + gpio_pin_disable_callback(dev, cfg->int_gpio); + +#if defined(CONFIG_ADXL362_TRIGGER_OWN_THREAD) + k_sem_give(&drv_data->gpio_sem); +#elif defined(CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD) + k_work_submit(&drv_data->work); +#endif +} + +#if defined(CONFIG_ADXL362_TRIGGER_OWN_THREAD) +static void adxl362_thread(int dev_ptr, int unused) +{ + struct device *dev = INT_TO_POINTER(dev_ptr); + struct adxl362_data *drv_data = dev->driver_data; + + ARG_UNUSED(unused); + + while (true) { + k_sem_take(&drv_data->gpio_sem, K_FOREVER); + adxl362_thread_cb(dev); + } +} +#elif defined(CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD) +static void adxl362_work_cb(struct k_work *work) +{ + struct adxl362_data *drv_data = + CONTAINER_OF(work, struct adxl362_data, work); + + adxl362_thread_cb(drv_data->dev); +} +#endif + +int adxl362_trigger_set(struct device *dev, + const struct sensor_trigger *trig, + sensor_trigger_handler_t handler) +{ + struct adxl362_data *drv_data = dev->driver_data; + const struct adxl362_config *cfg = dev->config->config_info; + u8_t int_mask, int_en, status_buf; + int ret; + + gpio_pin_disable_callback(drv_data->gpio, cfg->int_gpio); + + k_mutex_lock(&drv_data->trigger_mutex, K_FOREVER); + switch (trig->type) { + case SENSOR_TRIG_THRESHOLD: + drv_data->th_handler = handler; + drv_data->th_trigger = *trig; + int_mask = ADXL362_INTMAP1_ACT | + ADXL362_INTMAP1_INACT; + break; + case SENSOR_TRIG_DATA_READY: + drv_data->drdy_handler = handler; + drv_data->drdy_trigger = *trig; + int_mask = ADXL362_INTMAP1_DATA_READY; + break; + default: + LOG_ERR("Unsupported sensor trigger"); + ret = -ENOTSUP; + goto out; + } + k_mutex_unlock(&drv_data->trigger_mutex); + + if (handler) { + int_en = int_mask; + } else { + int_en = 0U; + } + + ret = adxl362_reg_write_mask(dev, ADXL362_REG_INTMAP1, + int_mask, int_en); + + if (ret) { + return -EFAULT; + } + + ret = adxl362_get_status(dev, &status_buf); + +out: + gpio_pin_enable_callback(drv_data->gpio, cfg->int_gpio); + + return ret; +} + +int adxl362_init_interrupt(struct device *dev) +{ + struct adxl362_data *drv_data = dev->driver_data; + const struct adxl362_config *cfg = dev->config->config_info; + int ret; + + k_mutex_init(&drv_data->trigger_mutex); + + drv_data->gpio = device_get_binding(cfg->gpio_port); + if (drv_data->gpio == NULL) { + LOG_ERR("Failed to get pointer to %s device!", + cfg->gpio_port); + return -EINVAL; + } + + /* Configures the inactivity and activity interrupts to be linked. */ + ret = adxl362_set_interrupt_mode(dev, ADXL362_MODE_LINK); + + if (ret) { + return -EFAULT; + } + + gpio_pin_configure(drv_data->gpio, cfg->int_gpio, + GPIO_DIR_IN | GPIO_INT | GPIO_INT_EDGE | + GPIO_INT_ACTIVE_HIGH | GPIO_INT_DEBOUNCE); + + gpio_init_callback(&drv_data->gpio_cb, + adxl362_gpio_callback, + BIT(cfg->int_gpio)); + + if (gpio_add_callback(drv_data->gpio, &drv_data->gpio_cb) < 0) { + LOG_ERR("Failed to set gpio callback!"); + return -EIO; + } + +#if defined(CONFIG_ADXL362_TRIGGER_OWN_THREAD) + k_sem_init(&drv_data->gpio_sem, 0, UINT_MAX); + + k_thread_create(&drv_data->thread, drv_data->thread_stack, + CONFIG_ADXL362_THREAD_STACK_SIZE, + (k_thread_entry_t)adxl362_thread, dev, + 0, NULL, K_PRIO_COOP(CONFIG_ADXL362_THREAD_PRIORITY), + 0, 0); +#elif defined(CONFIG_ADXL362_TRIGGER_GLOBAL_THREAD) + drv_data->work.handler = adxl362_work_cb; + drv_data->dev = dev; +#endif + + return 0; +} diff --git a/dts/bindings/sensor/adi,adxl362.yaml b/dts/bindings/sensor/adi,adxl362.yaml index cb2519761e4..4db58b954f4 100644 --- a/dts/bindings/sensor/adi,adxl362.yaml +++ b/dts/bindings/sensor/adi,adxl362.yaml @@ -16,4 +16,9 @@ inherits: properties: compatible: constraint: "adi,adxl362" + + int1-gpios: + type: compound + category: optional + generation: define, use-prop-name ... diff --git a/tests/drivers/build_all/sensors_trigger_a_h.conf b/tests/drivers/build_all/sensors_trigger_a_h.conf index 12ad43b2fed..12bf5a5aa6a 100644 --- a/tests/drivers/build_all/sensors_trigger_a_h.conf +++ b/tests/drivers/build_all/sensors_trigger_a_h.conf @@ -7,6 +7,8 @@ CONFIG_SPI=y CONFIG_SENSOR=y CONFIG_ADT7420=y CONFIG_ADT7420_TRIGGER_OWN_THREAD=y +CONFIG_ADXL362=y +CONFIG_ADXL362_TRIGGER_OWN_THREAD=y CONFIG_ADXL372=y CONFIG_ADXL372_TRIGGER_OWN_THREAD=y CONFIG_AMG88XX=y