drivers: add iis328dq stmemsc driver

Based on the iis2dlpc driver, with some significant differences in
interrupt handling.

Signed-off-by: Armin Brauns <armin.brauns@embedded-solutions.at>
This commit is contained in:
Armin Brauns 2024-03-07 15:50:06 +00:00 committed by Maureen Helm
commit f4c596ae28
13 changed files with 1041 additions and 7 deletions

View file

@ -66,6 +66,7 @@ add_subdirectory_ifdef(CONFIG_IIS2DLPC iis2dlpc)
add_subdirectory_ifdef(CONFIG_IIS2ICLX iis2iclx)
add_subdirectory_ifdef(CONFIG_IIS2MDC iis2mdc)
add_subdirectory_ifdef(CONFIG_IIS3DHHC iis3dhhc)
add_subdirectory_ifdef(CONFIG_IIS328DQ iis328dq)
add_subdirectory_ifdef(CONFIG_INA219 ina219)
add_subdirectory_ifdef(CONFIG_INA23X ina23x)
add_subdirectory_ifdef(CONFIG_INA3221 ina3221)

View file

@ -145,6 +145,7 @@ source "drivers/sensor/iis2dlpc/Kconfig"
source "drivers/sensor/iis2iclx/Kconfig"
source "drivers/sensor/iis2mdc/Kconfig"
source "drivers/sensor/iis3dhhc/Kconfig"
source "drivers/sensor/iis328dq/Kconfig"
source "drivers/sensor/ina219/Kconfig"
source "drivers/sensor/ina23x/Kconfig"
source "drivers/sensor/ina3221/Kconfig"

View file

@ -0,0 +1,13 @@
# ST Microelectronics IIS328DQ 3-axis accelerometer driver
#
# Copyright (c) 2020 STMicroelectronics
# Copyright (c) 2024 SILA Embedded Solutions GmbH
#
# SPDX-License-Identifier: Apache-2.0
#
zephyr_library()
zephyr_library_sources(iis328dq.c)
zephyr_library_sources_ifdef(CONFIG_IIS328DQ_TRIGGER iis328dq_trigger.c)
zephyr_library_include_directories(../stmemsc)

View file

@ -0,0 +1,35 @@
# ST Microelectronics IIS328DQ 3-axis accelerometer driver
# Copyright (c) 2020 STMicroelectronics
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# SPDX-License-Identifier: Apache-2.0
menuconfig IIS328DQ
bool "IIS328DQ I2C/SPI accelerometer sensor driver"
default y
depends on DT_HAS_ST_IIS328DQ_ENABLED
depends on ZEPHYR_HAL_ST_MODULE
select I2C if $(dt_compat_on_bus,$(DT_COMPAT_ST_IIS328DQ),i2c)
select SPI if $(dt_compat_on_bus,$(DT_COMPAT_ST_IIS328DQ),spi)
select HAS_STMEMSC
select USE_STDC_IIS328DQ
help
Enable driver for IIS328DQ accelerometer sensor driver
if IIS328DQ
module = IIS328DQ
thread_priority = 10
thread_stack_size = 1024
source "drivers/sensor/Kconfig.trigger_template"
if IIS328DQ_TRIGGER
config IIS328DQ_THRESHOLD
bool "Threshold detection"
help
Enable threshold interrupts
endif # IIS328DQ_TRIGGER
endif # IIS328DQ

View file

@ -0,0 +1,460 @@
/* ST Microelectronics IIS328DQ 3-axis accelerometer driver
*
* Copyright (c) 2020 STMicroelectronics
* Copyright (c) 2024 SILA Embedded Solutions GmbH
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/iis328dq.pdf
*/
#define DT_DRV_COMPAT st_iis328dq
#include <string.h>
#include <zephyr/init.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/sys/byteorder.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/sensor.h>
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#include <zephyr/drivers/spi.h>
#elif DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
#include <zephyr/drivers/i2c.h>
#endif
#include "iis328dq.h"
LOG_MODULE_REGISTER(IIS328DQ, CONFIG_SENSOR_LOG_LEVEL);
/**
* iis328dq_set_odr - set new Full Scale (in ±g)
*/
static int iis328dq_set_range(const struct device *dev, uint8_t fs)
{
int err;
struct iis328dq_data *iis328dq = dev->data;
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
iis328dq_fs_t fs_reg;
uint8_t gain;
if (fs <= 2) {
fs_reg = IIS328DQ_2g;
gain = 1;
} else if (fs <= 4) {
fs_reg = IIS328DQ_4g;
gain = 2;
} else if (fs <= 8) {
fs_reg = IIS328DQ_8g;
gain = 4;
} else {
LOG_ERR("FS too high");
return -ENOTSUP;
}
err = iis328dq_full_scale_set(ctx, fs_reg);
if (!err) {
iis328dq->gain = gain;
}
return err;
}
/**
* iis328dq_set_odr - set new Output Data Rate/sampling frequency (in Hz)
*/
static int iis328dq_set_odr(const struct device *dev, uint16_t odr)
{
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
iis328dq_dr_t odr_reg;
if (odr == 0U) {
odr_reg = IIS328DQ_ODR_OFF;
} else if (odr <= 1) {
odr_reg = IIS328DQ_ODR_1Hz;
} else if (odr <= 2) {
/* not sure what "5Hz2" is about, datasheet says PM=0b100 is 2Hz
* https://github.com/STMicroelectronics/STMems_Standard_C_drivers/issues/162
*/
odr_reg = IIS328DQ_ODR_5Hz2;
} else if (odr <= 5) {
odr_reg = IIS328DQ_ODR_5Hz;
} else if (odr <= 10) {
odr_reg = IIS328DQ_ODR_10Hz;
} else if (odr <= 50) {
odr_reg = IIS328DQ_ODR_50Hz;
} else if (odr <= 100) {
odr_reg = IIS328DQ_ODR_100Hz;
} else if (odr <= 400) {
odr_reg = IIS328DQ_ODR_400Hz;
} else if (odr <= 1000) {
odr_reg = IIS328DQ_ODR_1kHz;
} else {
LOG_ERR("ODR too high");
return -ENOTSUP;
}
if (iis328dq_data_rate_set(ctx, odr_reg) != 0) {
LOG_ERR("Failed to set ODR");
return -EIO;
}
return 0;
}
static inline void iis328dq_convert(struct sensor_value *val, int raw_val, uint8_t gain)
{
int64_t dval;
/* Gain is in mg/LSB */
/* Convert to μm/s^2 */
dval = ((int64_t)raw_val * gain * SENSOR_G) / 1000LL;
val->val1 = dval / 1000000LL;
val->val2 = dval % 1000000LL;
}
static inline void iis328dq_channel_get_acc(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
int i;
uint8_t ofs_start, ofs_stop;
struct iis328dq_data *iis328dq = dev->data;
struct sensor_value *pval = val;
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
ofs_start = ofs_stop = 0U;
break;
case SENSOR_CHAN_ACCEL_Y:
ofs_start = ofs_stop = 1U;
break;
case SENSOR_CHAN_ACCEL_Z:
ofs_start = ofs_stop = 2U;
break;
default:
ofs_start = 0U;
ofs_stop = 2U;
break;
}
for (i = ofs_start; i <= ofs_stop; i++) {
iis328dq_convert(pval++, iis328dq->acc[i], iis328dq->gain);
}
}
static int iis328dq_channel_get(const struct device *dev, enum sensor_channel chan,
struct sensor_value *val)
{
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
case SENSOR_CHAN_ACCEL_Y:
case SENSOR_CHAN_ACCEL_Z:
case SENSOR_CHAN_ACCEL_XYZ:
iis328dq_channel_get_acc(dev, chan, val);
return 0;
default:
LOG_DBG("Channel not supported");
break;
}
return -ENOTSUP;
}
#ifdef CONFIG_IIS328DQ_THRESHOLD
static int iis328dq_set_threshold(const struct device *dev, bool is_lower,
const struct sensor_value *val)
{
int err;
const struct iis328dq_config *cfg = dev->config;
struct iis328dq_data *iis328dq = dev->data;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
if (val->val1 < 0 || val->val2 < 0) {
/* thresholds are absolute */
return -EINVAL;
}
int64_t micro_ms2 = (val->val1 * INT64_C(1000000)) + val->val2;
/* factor guessed from similar-looking LIS2DH12 datasheet */
uint8_t mg_per_digit = iis328dq->gain * 16;
int16_t val_raw = (micro_ms2 * 1000LL) / SENSOR_G / mg_per_digit;
if (is_lower) {
/* internal INT1 handles lower threshold */
err = iis328dq_int1_treshold_set(ctx, val_raw);
if (err) {
LOG_ERR("Could not set INT1_THS to 0x%02X, error %d", val_raw, err);
return err;
}
} else {
/* internal INT2 handles lower threshold */
err = iis328dq_int2_treshold_set(ctx, val_raw);
if (err) {
LOG_ERR("Could not set INT2_THS to 0x%02X, error %d", val_raw, err);
return err;
}
}
return 0;
}
static int iis328dq_set_duration(const struct device *dev, uint16_t dur)
{
int err;
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
if (dur > 0x7F) {
LOG_WRN("Duration value %u too large", dur);
return -EINVAL;
}
err = iis328dq_int1_dur_set(ctx, dur);
if (err) {
LOG_ERR("Could not set INT1_DUR to 0x%02X, error %d", dur, err);
return err;
}
err = iis328dq_int2_dur_set(ctx, dur);
if (err) {
LOG_ERR("Could not set INT2_DUR to 0x%02X, error %d", dur, err);
return err;
}
return 0;
}
#endif /* CONFIG_IIS328DQ_THRESHOLD */
#define IIS328DQ_ATTR_DURATION SENSOR_ATTR_PRIV_START
static int iis328dq_dev_config(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
switch (attr) {
case SENSOR_ATTR_FULL_SCALE:
return iis328dq_set_range(dev, sensor_ms2_to_g(val));
case SENSOR_ATTR_SAMPLING_FREQUENCY:
return iis328dq_set_odr(dev, val->val1);
#ifdef CONFIG_IIS328DQ_THRESHOLD
case SENSOR_ATTR_LOWER_THRESH:
case SENSOR_ATTR_UPPER_THRESH:
if (chan != SENSOR_CHAN_ACCEL_XYZ) {
LOG_ERR("Threshold cannot be set per-channel");
return -ENOTSUP;
}
return iis328dq_set_threshold(dev, attr == SENSOR_ATTR_LOWER_THRESH, val);
case IIS328DQ_ATTR_DURATION:
if (chan != SENSOR_CHAN_ACCEL_XYZ) {
LOG_ERR("Duration cannot be set per-channel");
return -ENOTSUP;
}
return iis328dq_set_duration(dev, val->val1);
#endif /* CONFIG_IIS328DQ_THRESHOLD */
default:
LOG_DBG("Acc attribute not supported");
break;
}
return -ENOTSUP;
}
static int iis328dq_attr_set(const struct device *dev, enum sensor_channel chan,
enum sensor_attribute attr, const struct sensor_value *val)
{
switch (chan) {
case SENSOR_CHAN_ACCEL_X:
case SENSOR_CHAN_ACCEL_Y:
case SENSOR_CHAN_ACCEL_Z:
case SENSOR_CHAN_ACCEL_XYZ:
case SENSOR_CHAN_ALL:
return iis328dq_dev_config(dev, chan, attr, val);
default:
LOG_DBG("Attr not supported on %d channel", chan);
break;
}
return -ENOTSUP;
}
static int iis328dq_sample_fetch(const struct device *dev, enum sensor_channel chan)
{
struct iis328dq_data *iis328dq = dev->data;
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
int16_t buf[3];
/* fetch raw data sample */
if (iis328dq_acceleration_raw_get(ctx, buf) < 0) {
LOG_DBG("Failed to fetch raw data sample");
return -EIO;
}
iis328dq->acc[0] = buf[0] >> 4;
iis328dq->acc[1] = buf[1] >> 4;
iis328dq->acc[2] = buf[2] >> 4;
return 0;
}
static const struct sensor_driver_api iis328dq_driver_api = {
.attr_set = iis328dq_attr_set,
#if CONFIG_IIS328DQ_TRIGGER
.trigger_set = iis328dq_trigger_set,
#endif /* CONFIG_IIS328DQ_TRIGGER */
.sample_fetch = iis328dq_sample_fetch,
.channel_get = iis328dq_channel_get,
};
static int iis328dq_init(const struct device *dev)
{
struct iis328dq_data *iis328dq = dev->data;
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
uint8_t reg_value;
iis328dq->dev = dev;
/* check chip ID */
if (iis328dq_device_id_get(ctx, &reg_value) < 0) {
return -EIO;
}
if (reg_value != IIS328DQ_ID) {
LOG_ERR("Invalid chip ID");
return -EINVAL;
}
/* reset device */
if (iis328dq_boot_set(ctx, PROPERTY_ENABLE) < 0) {
return -EIO;
}
k_sleep(K_MSEC(100));
if (iis328dq_boot_get(ctx, &reg_value) < 0) {
return -EIO;
}
if (reg_value != PROPERTY_DISABLE) {
LOG_ERR("BOOT did not deassert");
return -EIO;
}
if (iis328dq_block_data_update_set(ctx, PROPERTY_ENABLE) < 0) {
return -EIO;
}
/* set default odr to 12.5Hz acc */
if (iis328dq_set_odr(dev, 12) < 0) {
LOG_ERR("odr init error (12.5 Hz)");
return -EIO;
}
if (iis328dq_set_range(dev, cfg->range) < 0) {
LOG_ERR("range init error %d", cfg->range);
return -EIO;
}
#ifdef CONFIG_IIS328DQ_TRIGGER
if (iis328dq_init_interrupt(dev) < 0) {
LOG_ERR("Failed to initialize interrupts");
return -EIO;
}
#endif /* CONFIG_IIS328DQ_TRIGGER */
return 0;
}
/*
* Device creation macro, shared by IIS328DQ_DEFINE_SPI() and
* IIS328DQ_DEFINE_I2C().
*/
#define IIS328DQ_DEVICE_INIT(inst) \
SENSOR_DEVICE_DT_INST_DEFINE(inst, iis328dq_init, NULL, &iis328dq_data_##inst, \
&iis328dq_config_##inst, POST_KERNEL, \
CONFIG_SENSOR_INIT_PRIORITY, &iis328dq_driver_api);
#ifdef CONFIG_IIS328DQ_TRIGGER
#ifdef CONFIG_IIS328DQ_THRESHOLD
#define IIS328DQ_CFG_IRQ_THRESHOLD(inst) \
.threshold_pad = DT_INST_PROP_OR(inst, threshold_int_pad, -1),
#else
#define IIS328DQ_CFG_IRQ_THRESHOLD(inst)
#endif /* CONFIG_IIS328DQ_THRESHOLD */
#define IIS328DQ_CFG_IRQ(inst) \
.gpio_int1 = GPIO_DT_SPEC_INST_GET_OR(inst, int1_gpios, {0}), \
.gpio_int2 = GPIO_DT_SPEC_INST_GET_OR(inst, int2_gpios, {0}), \
.drdy_pad = DT_INST_PROP_OR(inst, drdy_int_pad, -1), IIS328DQ_CFG_IRQ_THRESHOLD(inst)
#else
#define IIS328DQ_CFG_IRQ(inst)
#endif /* CONFIG_IIS328DQ_TRIGGER */
#define IIS328DQ_CONFIG_COMMON(inst) \
.range = DT_INST_PROP(inst, range), \
IF_ENABLED(UTIL_OR(DT_INST_NODE_HAS_PROP(inst, int1_gpios), \
DT_INST_NODE_HAS_PROP(inst, int2_gpios)), \
(IIS328DQ_CFG_IRQ(inst)))
/*
* Instantiation macros used when a device is on a SPI bus.
*/
#define IIS328DQ_SPI_OPERATION \
(SPI_WORD_SET(8) | SPI_OP_MODE_MASTER | SPI_MODE_CPOL | SPI_MODE_CPHA)
#define IIS328DQ_CONFIG_SPI(inst) \
{ \
STMEMSC_CTX_SPI_INCR(&iis328dq_config_##inst.stmemsc_cfg), \
.stmemsc_cfg = \
{ \
.spi = SPI_DT_SPEC_INST_GET(inst, IIS328DQ_SPI_OPERATION, \
0), \
}, \
IIS328DQ_CONFIG_COMMON(inst) \
}
/*
* Instantiation macros used when a device is on an I2C bus.
*/
#define IIS328DQ_CONFIG_I2C(inst) \
{ \
STMEMSC_CTX_I2C_INCR(&iis328dq_config_##inst.stmemsc_cfg), \
.stmemsc_cfg = \
{ \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
}, \
IIS328DQ_CONFIG_COMMON(inst) \
}
/*
* Main instantiation macro. Use of COND_CODE_1() selects the right
* bus-specific macro at preprocessor time.
*/
#define IIS328DQ_DEFINE(inst) \
static struct iis328dq_data iis328dq_data_##inst; \
static const struct iis328dq_config iis328dq_config_##inst = \
COND_CODE_1(DT_INST_ON_BUS(inst, spi), (IIS328DQ_CONFIG_SPI(inst)), \
(IIS328DQ_CONFIG_I2C(inst))); \
IIS328DQ_DEVICE_INIT(inst) \
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, drdy_int_pad), \
(BUILD_ASSERT( \
DT_INST_NODE_HAS_PROP( \
inst, CONCAT(int, DT_INST_PROP(inst, drdy_int_pad), _gpios)), \
"No GPIO pin defined for IIS328DQ DRDY interrupt");)) \
IF_ENABLED(DT_INST_NODE_HAS_PROP(inst, threshold_int_pad), \
(BUILD_ASSERT(DT_INST_NODE_HAS_PROP( \
inst, CONCAT(int, DT_INST_PROP(inst, threshold_int_pad), \
_gpios)), \
"No GPIO pin defined for IIS328DQ threshold interrupt");)) \
IF_ENABLED( \
UTIL_AND(DT_INST_NODE_HAS_PROP(inst, drdy_int_pad), \
DT_INST_NODE_HAS_PROP(inst, threshold_int_pad)), \
(BUILD_ASSERT( \
DT_INST_PROP(inst, drdy_int_pad) != \
DT_INST_PROP(inst, threshold_int_pad), \
"IIS328DQ DRDY interrupt and threshold interrupt cannot share a pin");))
DT_INST_FOREACH_STATUS_OKAY(IIS328DQ_DEFINE)

View file

@ -0,0 +1,93 @@
/* ST Microelectronics IIS328DQ 3-axis accelerometer driver
*
* Copyright (c) 2020 STMicroelectronics
* Copyright (c) 2024 SILA Embedded Solutions GmbH
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/iis328dq.pdf
*/
#ifndef ZEPHYR_DRIVERS_SENSOR_IIS328DQ_IIS328DQ_H_
#define ZEPHYR_DRIVERS_SENSOR_IIS328DQ_IIS328DQ_H_
#include <zephyr/drivers/gpio.h>
#include <zephyr/sys/util.h>
#include <zephyr/drivers/sensor.h>
#include <stmemsc.h>
#include "iis328dq_reg.h"
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
#include <zephyr/drivers/spi.h>
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(spi) */
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
#include <zephyr/drivers/i2c.h>
#endif /* DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c) */
/**
* struct iis328dq_dev_config - iis328dq hw configuration
* @bus_name: Pointer to bus master identifier.
* @pm: Power mode (lis2dh_powermode).
* @irq_dev_name: Pointer to GPIO PORT identifier.
* @irq_pin: GPIO pin number connected to sensor int pin.
* @drdy_int: Sensor drdy int (int1/int2).
*/
struct iis328dq_config {
stmdev_ctx_t ctx;
union {
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(i2c)
const struct i2c_dt_spec i2c;
#endif
#if DT_ANY_INST_ON_BUS_STATUS_OKAY(spi)
const struct spi_dt_spec spi;
#endif
} stmemsc_cfg;
uint8_t range;
#ifdef CONFIG_IIS328DQ_TRIGGER
const struct gpio_dt_spec gpio_int1;
const struct gpio_dt_spec gpio_int2;
/* interrupt pad to be used for DRDY interrupts, or -1 if not configured */
int8_t drdy_pad;
#ifdef CONFIG_IIS328DQ_THRESHOLD
/* interrupt pad to be used for threshold interrupts, or -1 if not configured */
int8_t threshold_pad;
#endif /* CONFIG_IIS328DQ_THRESHOLD */
#endif /* CONFIG_IIS328DQ_TRIGGER */
};
/* sensor data */
struct iis328dq_data {
const struct device *dev;
int16_t acc[3];
/* sensitivity in mg/LSB */
uint8_t gain;
#ifdef CONFIG_IIS328DQ_TRIGGER
struct gpio_callback int1_cb;
struct gpio_callback int2_cb;
sensor_trigger_handler_t drdy_handler;
const struct sensor_trigger *drdy_trig;
#ifdef CONFIG_IIS328DQ_THRESHOLD
sensor_trigger_handler_t threshold_handler;
const struct sensor_trigger *threshold_trig;
#endif /* CONFIG_IIS328DQ_THRESHOLD */
#if defined(CONFIG_IIS328DQ_TRIGGER_OWN_THREAD)
K_KERNEL_STACK_MEMBER(thread_stack, CONFIG_IIS328DQ_THREAD_STACK_SIZE);
struct k_thread thread;
struct k_sem gpio_sem;
#elif defined(CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD)
struct k_work work;
#endif /* CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD */
#endif /* CONFIG_IIS328DQ_TRIGGER */
};
#ifdef CONFIG_IIS328DQ_TRIGGER
int iis328dq_init_interrupt(const struct device *dev);
int iis328dq_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler);
#endif /* CONFIG_IIS328DQ_TRIGGER */
#endif /* ZEPHYR_DRIVERS_SENSOR_IIS328DQ_IIS328DQ_H_ */

View file

@ -0,0 +1,332 @@
/* ST Microelectronics IIS328DQ 3-axis accelerometer driver
*
* Copyright (c) 2020 STMicroelectronics
* Copyright (c) 2024 SILA Embedded Solutions GmbH
*
* SPDX-License-Identifier: Apache-2.0
*
* Datasheet:
* https://www.st.com/resource/en/datasheet/iis328dq.pdf
*/
#define DT_DRV_COMPAT st_iis328dq
#include <zephyr/kernel.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
#include "iis328dq.h"
LOG_MODULE_DECLARE(IIS328DQ, CONFIG_SENSOR_LOG_LEVEL);
static int iis328dq_set_int_pad_state(const struct device *dev, uint8_t pad, bool enable)
{
const struct iis328dq_config *cfg = dev->config;
int state = enable ? GPIO_INT_EDGE_TO_ACTIVE : GPIO_INT_DISABLE;
if (pad == 1) {
return gpio_pin_interrupt_configure_dt(&cfg->gpio_int1, state);
} else if (pad == 2) {
return gpio_pin_interrupt_configure_dt(&cfg->gpio_int2, state);
} else {
return -EINVAL;
}
}
/**
* iis328dq_enable_int - enable selected int pin to generate interrupt
*/
static int iis328dq_enable_int(const struct device *dev, const struct sensor_trigger *trig,
int enable)
{
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
switch (trig->type) {
case SENSOR_TRIG_DATA_READY:
if (cfg->drdy_pad == 1) {
/* route DRDY to PAD1 */
if (iis328dq_pin_int1_route_set(ctx, IIS328DQ_PAD1_DRDY) != 0) {
return -EIO;
}
} else if (cfg->drdy_pad == 2) {
/* route DRDY to PAD2 */
if (iis328dq_pin_int2_route_set(ctx, IIS328DQ_PAD2_DRDY) != 0) {
return -EIO;
}
} else {
LOG_ERR("No interrupt pin configured for DRDY in devicetree");
return -ENOTSUP;
}
return iis328dq_set_int_pad_state(dev, cfg->drdy_pad, enable);
#ifdef CONFIG_IIS328DQ_THRESHOLD
case SENSOR_TRIG_THRESHOLD: {
/* set up internal INT1 for lower thresholds */
int1_on_th_conf_t int1_conf = {0};
switch (trig->chan) {
case SENSOR_CHAN_ACCEL_X:
int1_conf.int1_xlie = 1;
break;
case SENSOR_CHAN_ACCEL_Y:
int1_conf.int1_ylie = 1;
break;
case SENSOR_CHAN_ACCEL_Z:
int1_conf.int1_zlie = 1;
break;
case SENSOR_CHAN_ACCEL_XYZ:
int1_conf.int1_xlie = 1;
int1_conf.int1_ylie = 1;
int1_conf.int1_zlie = 1;
break;
default:
LOG_ERR("Invalid sensor channel %d", trig->chan);
return -EINVAL;
}
if (iis328dq_int1_on_threshold_conf_set(ctx, int1_conf) != 0) {
return -EIO;
}
/* set up internal INT2 for uppper thresholds */
int2_on_th_conf_t int2_conf = {0};
int2_conf.int2_xhie = int1_conf.int1_xlie;
int2_conf.int2_yhie = int1_conf.int1_ylie;
int2_conf.int2_zhie = int1_conf.int1_zlie;
if (iis328dq_int2_on_threshold_conf_set(ctx, int2_conf) != 0) {
return -EIO;
}
if (cfg->threshold_pad == 1) {
/* route both internal interrupts to PAD1 */
if (iis328dq_pin_int1_route_set(ctx, IIS328DQ_PAD1_INT1_OR_INT2_SRC) != 0) {
return -EIO;
}
} else if (cfg->threshold_pad == 2) {
/* route both internal interrupts to PAD2 */
if (iis328dq_pin_int2_route_set(ctx, IIS328DQ_PAD2_INT1_OR_INT2_SRC) != 0) {
return -EIO;
}
} else {
LOG_ERR("No interrupt pin configured for DRDY in devicetree");
return -ENOTSUP;
}
return iis328dq_set_int_pad_state(dev, cfg->threshold_pad, enable);
}
#endif /* CONFIG_IIS328DQ_THRESHOLD */
default:
LOG_ERR("Unsupported trigger interrupt route %d", trig->type);
return -ENOTSUP;
}
return 0;
}
/**
* iis328dq_trigger_set - link external trigger to event data ready
*/
int iis328dq_trigger_set(const struct device *dev, const struct sensor_trigger *trig,
sensor_trigger_handler_t handler)
{
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
struct iis328dq_data *iis328dq = dev->data;
int16_t raw[3];
int state = (handler != NULL) ? PROPERTY_ENABLE : PROPERTY_DISABLE;
if (!cfg->gpio_int1.port && !cfg->gpio_int2.port) {
/* no interrupts configured */
return -ENOTSUP;
}
switch (trig->type) {
case SENSOR_TRIG_DATA_READY:
iis328dq->drdy_handler = handler;
iis328dq->drdy_trig = trig;
if (state) {
/* dummy read: re-trigger interrupt */
iis328dq_acceleration_raw_get(ctx, raw);
}
break;
#ifdef CONFIG_IIS328DQ_THRESHOLD
case SENSOR_TRIG_THRESHOLD:
iis328dq->threshold_handler = handler;
iis328dq->threshold_trig = trig;
break;
#endif /* CONFIG_IIS328DQ_THRESHOLD */
default:
LOG_ERR("Unsupported sensor trigger");
return -ENOTSUP;
}
return iis328dq_enable_int(dev, trig, state);
}
/**
* iis328dq_handle_interrupt - handle the drdy event
* read data and call handler if registered any
*/
static void iis328dq_handle_interrupt(const struct device *dev)
{
const struct iis328dq_config *cfg = dev->config;
struct iis328dq_data *data = dev->data;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
iis328dq_status_reg_t status;
iis328dq_int1_src_t sources1;
iis328dq_int2_src_t sources2;
iis328dq_status_reg_get(ctx, &status);
if (status.zyxda) {
if (data->drdy_handler) {
data->drdy_handler(dev, data->drdy_trig);
}
iis328dq_set_int_pad_state(dev, cfg->drdy_pad, true);
}
#ifdef CONFIG_IIS328DQ_THRESHOLD
iis328dq_int1_src_get(ctx, &sources1);
iis328dq_int2_src_get(ctx, &sources2);
if (sources1.ia || sources2.ia) {
if (data->threshold_handler) {
data->threshold_handler(dev, data->threshold_trig);
}
iis328dq_set_int_pad_state(dev, cfg->threshold_pad, true);
}
#endif /* CONFIG_IIS328DQ_THRESHOLD */
}
static void iis328dq_int1_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct iis328dq_data *iis328dq = CONTAINER_OF(cb, struct iis328dq_data, int1_cb);
ARG_UNUSED(pins);
iis328dq_set_int_pad_state(iis328dq->dev, 1, true);
#if defined(CONFIG_IIS328DQ_TRIGGER_OWN_THREAD)
k_sem_give(&iis328dq->gpio_sem);
#elif defined(CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD)
k_work_submit(&iis328dq->work);
#endif /* CONFIG_IIS328DQ_TRIGGER_OWN_THREAD */
}
static void iis328dq_int2_gpio_callback(const struct device *dev, struct gpio_callback *cb,
uint32_t pins)
{
struct iis328dq_data *iis328dq = CONTAINER_OF(cb, struct iis328dq_data, int2_cb);
ARG_UNUSED(pins);
iis328dq_set_int_pad_state(iis328dq->dev, 2, true);
#if defined(CONFIG_IIS328DQ_TRIGGER_OWN_THREAD)
k_sem_give(&iis328dq->gpio_sem);
#elif defined(CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD)
k_work_submit(&iis328dq->work);
#endif /* CONFIG_IIS328DQ_TRIGGER_OWN_THREAD */
}
#ifdef CONFIG_IIS328DQ_TRIGGER_OWN_THREAD
static void iis328dq_thread(void *p1, void *p2, void *p3)
{
ARG_UNUSED(p2);
ARG_UNUSED(p3);
struct iis328dq_data *iis328dq = p1;
while (1) {
k_sem_take(&iis328dq->gpio_sem, K_FOREVER);
iis328dq_handle_interrupt(iis328dq->dev);
}
}
#endif /* CONFIG_IIS328DQ_TRIGGER_OWN_THREAD */
#ifdef CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD
static void iis328dq_work_cb(struct k_work *work)
{
struct iis328dq_data *iis328dq = CONTAINER_OF(work, struct iis328dq_data, work);
iis328dq_handle_interrupt(iis328dq->dev);
}
#endif /* CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD */
int iis328dq_init_interrupt(const struct device *dev)
{
struct iis328dq_data *iis328dq = dev->data;
const struct iis328dq_config *cfg = dev->config;
stmdev_ctx_t *ctx = (stmdev_ctx_t *)&cfg->ctx;
int ret;
if (!cfg->gpio_int1.port && !cfg->gpio_int2.port) {
/* no interrupts configured, nothing to do */
return 0;
}
/* setup data ready gpio interrupt (INT1 and INT2) */
if (cfg->gpio_int1.port) {
if (!gpio_is_ready_dt(&cfg->gpio_int1)) {
LOG_ERR("INT_1 pin is not ready");
return -EINVAL;
}
}
if (cfg->gpio_int2.port) {
if (!gpio_is_ready_dt(&cfg->gpio_int2)) {
LOG_ERR("INT_2 pin is not ready");
return -EINVAL;
}
}
#if defined(CONFIG_IIS328DQ_TRIGGER_OWN_THREAD)
k_sem_init(&iis328dq->gpio_sem, 0, K_SEM_MAX_LIMIT);
k_thread_create(&iis328dq->thread, iis328dq->thread_stack,
CONFIG_IIS328DQ_THREAD_STACK_SIZE, iis328dq_thread, iis328dq, NULL, NULL,
K_PRIO_COOP(CONFIG_IIS328DQ_THREAD_PRIORITY), 0, K_NO_WAIT);
#elif defined(CONFIG_IIS328DQ_TRIGGER_GLOBAL_THREAD)
iis328dq->work.handler = iis328dq_work_cb;
#endif /* CONFIG_IIS328DQ_TRIGGER_OWN_THREAD */
if (cfg->gpio_int1.port) {
ret = gpio_pin_configure_dt(&cfg->gpio_int1, GPIO_INPUT);
if (ret < 0) {
LOG_ERR("Could not configure INT_1 gpio");
return ret;
}
gpio_init_callback(&iis328dq->int1_cb, iis328dq_int1_gpio_callback,
BIT(cfg->gpio_int1.pin));
if (gpio_add_callback(cfg->gpio_int1.port, &iis328dq->int1_cb) < 0) {
LOG_ERR("Could not set INT1 callback");
return -EIO;
}
}
if (cfg->gpio_int2.port) {
ret = gpio_pin_configure_dt(&cfg->gpio_int2, GPIO_INPUT);
if (ret < 0) {
LOG_ERR("Could not configure INT_2 gpio");
return ret;
}
gpio_init_callback(&iis328dq->int2_cb, iis328dq_int2_gpio_callback,
BIT(cfg->gpio_int2.pin));
if (gpio_add_callback(cfg->gpio_int2.port, &iis328dq->int2_cb) < 0) {
LOG_ERR("Could not set INT2 callback");
return -EIO;
}
}
if (iis328dq_int1_notification_set(ctx, IIS328DQ_INT1_PULSED) != 0) {
return -EIO;
}
if (iis328dq_int2_notification_set(ctx, IIS328DQ_INT2_PULSED) != 0) {
return -EIO;
}
return 0;
}

View file

@ -0,0 +1,61 @@
# Copyright (c) 2018 STMicroelectronics
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# SPDX-License-Identifier: Apache-2.0
include: sensor-device.yaml
properties:
int1-gpios:
type: phandle-array
description: |
INT_1 pin
This pin defaults to active high when produced by the sensor. The property value should ensure
the flags properly describe the signal that is presented to the driver.
int2-gpios:
type: phandle-array
description: |
INT_2 pin
This pin defaults to active high when produced by the sensor. The property value should ensure
the flags properly describe the signal that is presented to the driver.
drdy-int-pad:
type: int
enum: [1, 2]
description: |
Select DRDY pin number (1 or 2).
This number represents which of the two interrupt pins (INT_1 or INT_2), if any, the DRDY
interrupt should be generated from. If this property is not specified, no data ready
interrupts can be registered.
- 1 # drdy is generated on INT1
- 2 # drdy is generated on INT2
threshold-int-pad:
type: int
enum: [1, 2]
description: |
Select threshold interrupt pin number (1 or 2).
This number represents which of the two interrupt pins (INT_1 or INT_2), if any, the threshold
interrupt should be generated from. If this property is not specified, no threshold interrupts
can be registered.
- 1 # threshold interrupt is generated on INT1
- 2 # threshold interrupt is generated on INT2
range:
type: int
default: 2
description: |
Range in g. Default is power-up configuration.
- 16 # 16g (1.952 mg/LSB)
- 8 # 8g (0.976 mg/LSB)
- 4 # 4g (0.488 mg/LSB)
- 2 # 2g (0.244 mg/LSB)
enum: [16, 8, 4, 2]

View file

@ -0,0 +1,10 @@
# Copyright (c) 2018 STMicroelectronics
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# SPDX-License-Identifier: Apache-2.0
description: |
STMicroelectronics IIS328DQ accelerometer accessed through I2C bus
compatible: "st,iis328dq"
include: ["i2c-device.yaml", "st,iis328dq-common.yaml"]

View file

@ -0,0 +1,10 @@
# Copyright (c) 2019 STMicroelectronics
# Copyright (c) 2024 SILA Embedded Solutions GmbH
# SPDX-License-Identifier: Apache-2.0
description: |
STMicroelectronics IIS328DQ accelerometer accessed through SPI bus
compatible: "st,iis328dq"
include: ["spi-device.yaml", "st,iis328dq-common.yaml"]

View file

@ -120,13 +120,14 @@
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>, /* 0x25 */
<&test_gpio 0 0>, /* 0x26 */
<&test_gpio 0 0>, /* 0x27 */
<&test_gpio 0 0>, /* 0x28 */
<&test_gpio 0 0>, /* 0x29 */
<&test_gpio 0 0>, /* 0x2A */
<&test_gpio 0 0>; /* 0x2B */
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>,
<&test_gpio 0 0>; /* 0x2C */
#include "spi.dtsi"
};

View file

@ -1011,3 +1011,11 @@ test_i2c_am2301b: am2301b@89 {
reg = <0x89>;
status = "okay";
};
test_i2c_iis328dq: iis328dq@8a {
compatible = "st,iis328dq";
status = "okay";
reg = <0x8a>;
int2-gpios = <&test_gpio 0 0>;
threshold-int-pad = <2>;
};

View file

@ -352,3 +352,12 @@ test_spi_bd8lb600fs: bd8lb600fs@2b {
#sensor-cells = <0>;
};
};
test_spi_iis328dq: iis328dq@2c {
compatible = "st,iis328dq";
status = "okay";
reg = <0x2c>;
spi-max-frequency = <0>;
int1-gpios = <&test_gpio 0 0>;
drdy-int-pad = <1>;
};