drivers: sensor: Add ADXL362 interrupt handling

The ADXL362 driver is expanded to support interrupt handling.

Signed-off-by: Henrik Malvik Halvorsen <henrik.halvorsen@nordicsemi.no>
This commit is contained in:
Henrik Malvik Halvorsen 2019-03-08 12:27:54 +01:00 committed by Maureen Helm
commit 11295c190b
7 changed files with 483 additions and 3 deletions

View file

@ -3,3 +3,4 @@
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_ADXL362 adxl362.c)
zephyr_library_sources_ifdef(CONFIG_ADXL362_TRIGGER adxl362_trigger.c)

View file

@ -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

View file

@ -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,

View file

@ -9,6 +9,8 @@
#include <zephyr/types.h>
#include <device.h>
#include <gpio.h>
#include <spi.h>
#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_ */

View file

@ -0,0 +1,189 @@
/*
* Copyright (c) 2019 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <device.h>
#include <gpio.h>
#include <misc/util.h>
#include <kernel.h>
#include <sensor.h>
#include "adxl362.h"
#define LOG_LEVEL CONFIG_SENSOR_LOG_LEVEL
#include <logging/log.h>
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;
}

View file

@ -16,4 +16,9 @@ inherits:
properties:
compatible:
constraint: "adi,adxl362"
int1-gpios:
type: compound
category: optional
generation: define, use-prop-name
...

View file

@ -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