drivers: sensor: lis2dh: add tap interrupt

add interrupt for single tap on ST LIS2DH
shared interrupt with any motion

Signed-off-by: Mathias Storck <mathias.storck@gwa-hygiene.de>
This commit is contained in:
Mathias Storck 2023-09-14 13:15:19 +02:00 committed by Maureen Helm
commit 4431c4755c
2 changed files with 182 additions and 75 deletions

View file

@ -29,11 +29,9 @@
#define LIS2DH_AUTOINCREMENT_ADDR BIT(7)
#define LIS2DH_REG_CTRL0 0x1e
#define LIS2DH_SDO_PU_DISC_SHIFT 7
#define LIS2DH_SDO_PU_DISC_MASK BIT(LIS2DH_SDO_PU_DISC_SHIFT)
#define LIS2DH_SDO_PU_DISC_MASK BIT(7)
#define LIS2DH_REG_CTRL1 0x20
#define LIS2DH_ACCEL_XYZ_SHIFT 0
#define LIS2DH_ACCEL_X_EN_BIT BIT(0)
#define LIS2DH_ACCEL_Y_EN_BIT BIT(1)
#define LIS2DH_ACCEL_Z_EN_BIT BIT(2)
@ -91,14 +89,15 @@
#define LIS2DH_HPIS2_EN_BIT BIT(1)
#define LIS2DH_FDS_EN_BIT BIT(3)
#define LIS2DH_HPIS_EN_SHIFT 0
#define LIS2DH_HPIS_EN_MASK BIT_MASK(2) << LIS2DH_HPIS_EN_SHIFT
#define LIS2DH_HPIS_EN_MASK BIT_MASK(2)
#define LIS2DH_REG_CTRL3 0x22
#define LIS2DH_EN_DRDY1_INT1_SHIFT 4
#define LIS2DH_EN_DRDY1_INT1 BIT(LIS2DH_EN_DRDY1_INT1_SHIFT)
#define LIS2DH_EN_CLICK_INT1 BIT(7)
#define LIS2DH_EN_IA_INT1 BIT(6)
#define LIS2DH_EN_DRDY1_INT1 BIT(4)
#define LIS2DH_REG_CTRL4 0x23
#define LIS2DH_CTRL4_BDU_BIT BIT(7)
#define LIS2DH_FS_SHIFT 4
#define LIS2DH_FS_MASK (BIT_MASK(2) << LIS2DH_FS_SHIFT)
@ -122,16 +121,12 @@
#endif
#define LIS2DH_REG_CTRL5 0x24
#define LIS2DH_LIR_INT2_SHIFT 1
#define LIS2DH_LIR_INT1_SHIFT 3
#define LIS2DH_EN_LIR_INT2 BIT(LIS2DH_LIR_INT2_SHIFT)
#define LIS2DH_EN_LIR_INT1 BIT(LIS2DH_LIR_INT1_SHIFT)
#define LIS2DH_EN_LIR_INT2 BIT(1)
#define LIS2DH_EN_LIR_INT1 BIT(3)
#define LIS2DH_REG_CTRL6 0x25
#define LIS2DH_EN_INT2_INT2_SHIFT 5
#define LIS2DH_EN_INT2_INT2 BIT(LIS2DH_EN_INT2_INT2_SHIFT)
#define LIS2DH_EN_INT1_INT1_SHIFT 6
#define LIS2DH_EN_INT1_INT1 BIT(LIS2DH_EN_INT1_INT1_SHIFT)
#define LIS2DH_EN_CLICK_INT2 BIT(7)
#define LIS2DH_EN_IA_INT2 BIT(5)
#define LIS2DH_REG_REFERENCE 0x26
@ -164,8 +159,8 @@
#define LIS2DH_REG_INT2_DUR 0x37
#define LIS2DH_INT_CFG_MODE_SHIFT 6
#define LIS2DH_INT_CFG_AOI_CFG BIT(LIS2DH_INT_CFG_MODE_SHIFT + 1)
#define LIS2DH_INT_CFG_6D_CFG BIT(LIS2DH_INT_CFG_MODE_SHIFT)
#define LIS2DH_INT_CFG_AOI_CFG BIT(7)
#define LIS2DH_INT_CFG_6D_CFG BIT(6)
#define LIS2DH_INT_CFG_ZHIE_ZUPE BIT(5)
#define LIS2DH_INT_CFG_ZLIE_ZDOWNE BIT(4)
#define LIS2DH_INT_CFG_YHIE_YUPE BIT(3)
@ -173,11 +168,26 @@
#define LIS2DH_INT_CFG_XHIE_XUPE BIT(1)
#define LIS2DH_INT_CFG_XLIE_XDOWNE BIT(0)
#define LIS2DH_REG_CFG_CLICK 0x38
#define LIS2DH_EN_CLICK_ZD BIT(5)
#define LIS2DH_EN_CLICK_ZS BIT(4)
#define LIS2DH_EN_CLICK_YD BIT(3)
#define LIS2DH_EN_CLICK_YS BIT(2)
#define LIS2DH_EN_CLICK_XD BIT(1)
#define LIS2DH_EN_CLICK_XS BIT(0)
#define LIS2DH_REG_CLICK_SRC 0x39
#define LIS2DH_CLICK_SRC_DCLICK BIT(5)
#define LIS2DH_CLICK_SRC_SCLICK BIT(4)
#define LIS2DH_REG_CFG_CLICK_THS 0x3A
#define LIS2DH_CLICK_LIR BIT(7)
#define LIS2DH_REG_TIME_LIMIT 0x3B
/* sample buffer size includes status register */
#define LIS2DH_BUF_SZ 7
#define LIS2DH_CTRL4_BDU_BIT BIT(7)
union lis2dh_sample {
uint8_t raw[LIS2DH_BUF_SZ];
struct {
@ -260,6 +270,8 @@ struct lis2dh_data {
const struct sensor_trigger *trig_drdy;
sensor_trigger_handler_t handler_anymotion;
const struct sensor_trigger *trig_anymotion;
sensor_trigger_handler_t handler_tap;
const struct sensor_trigger *trig_tap;
atomic_t trig_flags;
enum sensor_channel chan_drdy;

View file

@ -127,9 +127,10 @@ static inline void setup_int2(const struct device *dev,
: GPIO_INT_DISABLE);
}
static int lis2dh_trigger_anym_set(const struct device *dev,
sensor_trigger_handler_t handler,
const struct sensor_trigger *trig)
/* common handler for any motion and tap triggers */
static int lis2dh_trigger_anym_tap_set(const struct device *dev,
sensor_trigger_handler_t handler,
const struct sensor_trigger *trig)
{
const struct lis2dh_config *cfg = dev->config;
struct lis2dh_data *lis2dh = dev->data;
@ -152,19 +153,34 @@ static int lis2dh_trigger_anym_set(const struct device *dev,
}
/* disable any movement interrupt events */
status = lis2dh->hw_tf->write_reg(
dev,
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_CFG : LIS2DH_REG_INT2_CFG,
0);
status = lis2dh->hw_tf->write_reg(dev,
cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_CFG :
LIS2DH_REG_INT2_CFG,
0);
/* disable any click interrupt events */
status = lis2dh->hw_tf->write_reg(dev,
LIS2DH_REG_CFG_CLICK,
0);
/* make sure any pending interrupt is cleared */
status = lis2dh->hw_tf->read_reg(
dev,
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_SRC : LIS2DH_REG_INT2_SRC,
&reg_val);
status = lis2dh->hw_tf->read_reg(dev,
cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_SRC :
LIS2DH_REG_INT2_SRC,
&reg_val);
status = lis2dh->hw_tf->read_reg(dev,
LIS2DH_REG_CLICK_SRC,
&reg_val);
if (trig->type == SENSOR_TRIG_DELTA) {
lis2dh->handler_anymotion = handler;
lis2dh->trig_anymotion = trig;
} else if (trig->type == SENSOR_TRIG_TAP) {
lis2dh->handler_tap = handler;
lis2dh->trig_tap = trig;
}
lis2dh->handler_anymotion = handler;
lis2dh->trig_anymotion = trig;
if ((handler == NULL) || (status < 0)) {
return status;
}
@ -181,17 +197,71 @@ static int lis2dh_trigger_anym_set(const struct device *dev,
return 0;
}
static int lis2dh_trigger_anym_set(const struct device *dev,
sensor_trigger_handler_t handler,
const struct sensor_trigger *trig)
{
return lis2dh_trigger_anym_tap_set(dev, handler, trig);
}
static int lis2dh_trigger_tap_set(const struct device *dev,
sensor_trigger_handler_t handler,
const struct sensor_trigger *trig)
{
return lis2dh_trigger_anym_tap_set(dev, handler, trig);
}
static int lis2dh_start_trigger_int2(const struct device *dev)
{
struct lis2dh_data *lis2dh = dev->data;
const struct lis2dh_config *cfg = dev->config;
int status = 0;
uint8_t reg = 0, mask = 0, val = 0;
setup_int2(dev, true);
return lis2dh->hw_tf->write_reg(
dev,
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_CFG : LIS2DH_REG_INT2_CFG,
(cfg->hw.anym_mode << LIS2DH_INT_CFG_MODE_SHIFT) | LIS2DH_ANYM_CFG);
bool has_anyt = (lis2dh->handler_tap != NULL);
bool has_anym = (lis2dh->handler_anymotion != NULL);
/* configure any motion interrupt */
reg = cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_CFG : LIS2DH_REG_INT2_CFG;
val = (cfg->hw.anym_mode << LIS2DH_INT_CFG_MODE_SHIFT) | LIS2DH_ANYM_CFG;
status = lis2dh->hw_tf->write_reg(dev, reg, val);
if (status < 0) {
LOG_ERR("Failed to configure any motion interrupt");
return status;
}
/* enable any motion detection on int line */
reg = cfg->hw.anym_on_int1 ? LIS2DH_REG_CTRL3 : LIS2DH_REG_CTRL6;
mask = cfg->hw.anym_on_int1 ? LIS2DH_EN_IA_INT1 : LIS2DH_EN_IA_INT2;
val = has_anym ? mask : 0;
status = lis2dh->hw_tf->update_reg(dev, reg, mask, val);
if (status < 0) {
LOG_ERR("Failed to enable any motion detection on int line");
return status;
}
/* configure tap interrupt on all axes */
reg = LIS2DH_REG_CFG_CLICK;
mask = LIS2DH_EN_CLICK_XS | LIS2DH_EN_CLICK_YS | LIS2DH_EN_CLICK_ZS;
val = has_anyt ? mask : 0;
status = lis2dh->hw_tf->update_reg(dev, reg, mask, val);
if (status < 0) {
LOG_ERR("Failed to configure tap interrupt");
return status;
}
/* set click detection on int line */
reg = cfg->hw.anym_on_int1 ? LIS2DH_REG_CTRL3 : LIS2DH_REG_CTRL6;
mask = cfg->hw.anym_on_int1 ? LIS2DH_EN_CLICK_INT1 : LIS2DH_EN_CLICK_INT2;
val = has_anyt ? mask : 0;
status = lis2dh->hw_tf->update_reg(dev, reg, mask, val);
if (status < 0) {
LOG_ERR("Failed to enable click detection on int line");
return status;
}
return 0;
}
int lis2dh_trigger_set(const struct device *dev,
@ -203,6 +273,8 @@ int lis2dh_trigger_set(const struct device *dev,
return lis2dh_trigger_drdy_set(dev, trig->chan, handler, trig);
} else if (trig->type == SENSOR_TRIG_DELTA) {
return lis2dh_trigger_anym_set(dev, handler, trig);
} else if (trig->type == SENSOR_TRIG_TAP) {
return lis2dh_trigger_tap_set(dev, handler, trig);
}
return -ENOTSUP;
@ -243,11 +315,17 @@ int lis2dh_acc_slope_config(const struct device *dev,
LOG_INF("int2_ths=0x%x range_g=%d ums2=%u", reg_val,
range_g, slope_th_ums2 - 1);
/* Configure threshold for the any motion recognition */
status = lis2dh->hw_tf->write_reg(dev,
cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_THS :
LIS2DH_REG_INT2_THS,
LIS2DH_REG_INT1_THS :
LIS2DH_REG_INT2_THS,
reg_val);
/* Configure threshold for the Click recognition */
status = lis2dh->hw_tf->write_reg(dev,
LIS2DH_REG_CFG_CLICK_THS,
LIS2DH_CLICK_LIR | reg_val);
} else { /* SENSOR_ATTR_SLOPE_DUR */
/*
* slope duration is measured in number of samples:
@ -259,10 +337,16 @@ int lis2dh_acc_slope_config(const struct device *dev,
LOG_INF("int2_dur=0x%x", val->val1);
/* Configure time limit for the any motion recognition */
status = lis2dh->hw_tf->write_reg(dev,
cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_DUR :
LIS2DH_REG_INT2_DUR,
LIS2DH_REG_INT1_DUR :
LIS2DH_REG_INT2_DUR,
val->val1);
/* Configure time limit for the Click recognition */
status = lis2dh->hw_tf->write_reg(dev,
LIS2DH_REG_TIME_LIMIT,
val->val1);
}
@ -358,7 +442,6 @@ static void lis2dh_thread_cb(const struct device *dev)
TRIGGED_INT1)) {
if (likely(lis2dh->handler_drdy != NULL)) {
lis2dh->handler_drdy(dev, lis2dh->trig_drdy);
}
/* Reactivate level triggered interrupt if handler did not
@ -374,7 +457,7 @@ static void lis2dh_thread_cb(const struct device *dev)
if (cfg->gpio_int.port &&
atomic_test_and_clear_bit(&lis2dh->trig_flags,
TRIGGED_INT2)) {
uint8_t reg_val;
uint8_t reg_val = 0;
if (cfg->hw.anym_latch) {
/* clear interrupt to de-assert int line */
@ -387,18 +470,34 @@ static void lis2dh_thread_cb(const struct device *dev)
LOG_ERR("clearing interrupt 2 failed: %d", status);
return;
}
}
if (likely(lis2dh->handler_anymotion != NULL) &&
(reg_val >> LIS2DH_INT_CFG_MODE_SHIFT)) {
lis2dh->handler_anymotion(dev, lis2dh->trig_anymotion);
LOG_DBG("@tick=%u int2_src=0x%x", k_cycle_get_32(), reg_val);
}
if (likely(lis2dh->handler_anymotion != NULL)) {
lis2dh->handler_anymotion(dev, lis2dh->trig_anymotion);
/* read click interrupt */
status = lis2dh->hw_tf->read_reg(dev, LIS2DH_REG_CLICK_SRC,
&reg_val);
if (status < 0) {
LOG_ERR("clearing interrupt 2 failed: %d", status);
return;
}
if (likely(lis2dh->handler_tap != NULL) &&
(reg_val & LIS2DH_CLICK_SRC_SCLICK)) {
lis2dh->handler_tap(dev, lis2dh->trig_tap);
LOG_DBG("@tick=%u click_src=0x%x", k_cycle_get_32(), reg_val);
}
/* Reactivate level triggered interrupt if handler did not
* disable itself
*/
if (lis2dh->handler_anymotion != NULL) {
if (lis2dh->handler_anymotion || lis2dh->handler_tap) {
setup_int2(dev, true);
}
@ -522,49 +621,45 @@ check_gpio_int:
}
LOG_INF("%s: int2 on %s.%02u", dev->name,
cfg->gpio_int.port->name,
cfg->gpio_int.pin);
cfg->gpio_int.port->name,
cfg->gpio_int.pin);
/* disable interrupt in case of warm (re)boot */
status = lis2dh->hw_tf->write_reg(
dev,
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_CFG : LIS2DH_REG_INT2_CFG,
0);
status = lis2dh->hw_tf->write_reg(dev,
cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_CFG :
LIS2DH_REG_INT2_CFG,
0);
if (status < 0) {
LOG_ERR("Interrupt disable reg write failed (%d)", status);
return status;
}
status = lis2dh->hw_tf->write_reg(dev,
LIS2DH_REG_CFG_CLICK,
0);
if (status < 0) {
LOG_ERR("Interrupt disable reg write failed (%d)", status);
return status;
}
(void)memset(raw, 0, sizeof(raw));
status = lis2dh->hw_tf->write_data(
dev,
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_THS : LIS2DH_REG_INT2_THS,
raw, sizeof(raw));
status = lis2dh->hw_tf->write_data(dev,
cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_THS :
LIS2DH_REG_INT2_THS,
raw, sizeof(raw));
if (status < 0) {
LOG_ERR("Burst write to THS failed (%d)", status);
return status;
}
if (cfg->hw.anym_on_int1) {
/* enable interrupt 1 on int1 line */
status = lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL3,
LIS2DH_EN_INT1_INT1,
LIS2DH_EN_INT1_INT1);
if (cfg->hw.anym_latch) {
/* latch int1 line interrupt */
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL5,
LIS2DH_EN_LIR_INT1);
}
} else {
/* enable interrupt 2 on int2 line */
status = lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL6,
LIS2DH_EN_INT2_INT2,
LIS2DH_EN_INT2_INT2);
if (cfg->hw.anym_latch) {
/* latch int2 line interrupt */
status = lis2dh->hw_tf->write_reg(dev, LIS2DH_REG_CTRL5,
LIS2DH_EN_LIR_INT2);
}
if (cfg->hw.anym_latch) {
/* latch line interrupt */
status = lis2dh->hw_tf->write_reg(dev,
LIS2DH_REG_CTRL5,
cfg->hw.anym_on_int1 ?
LIS2DH_EN_LIR_INT1 :
LIS2DH_EN_LIR_INT2);
}
if (status < 0) {