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

View file

@ -127,9 +127,10 @@ static inline void setup_int2(const struct device *dev,
: GPIO_INT_DISABLE); : GPIO_INT_DISABLE);
} }
static int lis2dh_trigger_anym_set(const struct device *dev, /* common handler for any motion and tap triggers */
sensor_trigger_handler_t handler, static int lis2dh_trigger_anym_tap_set(const struct device *dev,
const struct sensor_trigger *trig) sensor_trigger_handler_t handler,
const struct sensor_trigger *trig)
{ {
const struct lis2dh_config *cfg = dev->config; const struct lis2dh_config *cfg = dev->config;
struct lis2dh_data *lis2dh = dev->data; 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 */ /* disable any movement interrupt events */
status = lis2dh->hw_tf->write_reg( status = lis2dh->hw_tf->write_reg(dev,
dev, cfg->hw.anym_on_int1 ?
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_CFG : LIS2DH_REG_INT2_CFG, LIS2DH_REG_INT1_CFG :
0); 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 */ /* make sure any pending interrupt is cleared */
status = lis2dh->hw_tf->read_reg( status = lis2dh->hw_tf->read_reg(dev,
dev, cfg->hw.anym_on_int1 ?
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_SRC : LIS2DH_REG_INT2_SRC, LIS2DH_REG_INT1_SRC :
&reg_val); 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)) { if ((handler == NULL) || (status < 0)) {
return status; return status;
} }
@ -181,17 +197,71 @@ static int lis2dh_trigger_anym_set(const struct device *dev,
return 0; 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) static int lis2dh_start_trigger_int2(const struct device *dev)
{ {
struct lis2dh_data *lis2dh = dev->data; struct lis2dh_data *lis2dh = dev->data;
const struct lis2dh_config *cfg = dev->config; const struct lis2dh_config *cfg = dev->config;
int status = 0;
uint8_t reg = 0, mask = 0, val = 0;
setup_int2(dev, true); setup_int2(dev, true);
return lis2dh->hw_tf->write_reg( bool has_anyt = (lis2dh->handler_tap != NULL);
dev, bool has_anym = (lis2dh->handler_anymotion != NULL);
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); /* 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, 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); return lis2dh_trigger_drdy_set(dev, trig->chan, handler, trig);
} else if (trig->type == SENSOR_TRIG_DELTA) { } else if (trig->type == SENSOR_TRIG_DELTA) {
return lis2dh_trigger_anym_set(dev, handler, trig); 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; 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, LOG_INF("int2_ths=0x%x range_g=%d ums2=%u", reg_val,
range_g, slope_th_ums2 - 1); range_g, slope_th_ums2 - 1);
/* Configure threshold for the any motion recognition */
status = lis2dh->hw_tf->write_reg(dev, status = lis2dh->hw_tf->write_reg(dev,
cfg->hw.anym_on_int1 ? cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_THS : LIS2DH_REG_INT1_THS :
LIS2DH_REG_INT2_THS, LIS2DH_REG_INT2_THS,
reg_val); 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 */ } else { /* SENSOR_ATTR_SLOPE_DUR */
/* /*
* slope duration is measured in number of samples: * 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); LOG_INF("int2_dur=0x%x", val->val1);
/* Configure time limit for the any motion recognition */
status = lis2dh->hw_tf->write_reg(dev, status = lis2dh->hw_tf->write_reg(dev,
cfg->hw.anym_on_int1 ? cfg->hw.anym_on_int1 ?
LIS2DH_REG_INT1_DUR : LIS2DH_REG_INT1_DUR :
LIS2DH_REG_INT2_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); val->val1);
} }
@ -358,7 +442,6 @@ static void lis2dh_thread_cb(const struct device *dev)
TRIGGED_INT1)) { TRIGGED_INT1)) {
if (likely(lis2dh->handler_drdy != NULL)) { if (likely(lis2dh->handler_drdy != NULL)) {
lis2dh->handler_drdy(dev, lis2dh->trig_drdy); lis2dh->handler_drdy(dev, lis2dh->trig_drdy);
} }
/* Reactivate level triggered interrupt if handler did not /* 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 && if (cfg->gpio_int.port &&
atomic_test_and_clear_bit(&lis2dh->trig_flags, atomic_test_and_clear_bit(&lis2dh->trig_flags,
TRIGGED_INT2)) { TRIGGED_INT2)) {
uint8_t reg_val; uint8_t reg_val = 0;
if (cfg->hw.anym_latch) { if (cfg->hw.anym_latch) {
/* clear interrupt to de-assert int line */ /* 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); LOG_ERR("clearing interrupt 2 failed: %d", status);
return; 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); LOG_DBG("@tick=%u int2_src=0x%x", k_cycle_get_32(), reg_val);
} }
if (likely(lis2dh->handler_anymotion != NULL)) { /* read click interrupt */
lis2dh->handler_anymotion(dev, lis2dh->trig_anymotion); 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 /* Reactivate level triggered interrupt if handler did not
* disable itself * disable itself
*/ */
if (lis2dh->handler_anymotion != NULL) { if (lis2dh->handler_anymotion || lis2dh->handler_tap) {
setup_int2(dev, true); setup_int2(dev, true);
} }
@ -522,49 +621,45 @@ check_gpio_int:
} }
LOG_INF("%s: int2 on %s.%02u", dev->name, LOG_INF("%s: int2 on %s.%02u", dev->name,
cfg->gpio_int.port->name, cfg->gpio_int.port->name,
cfg->gpio_int.pin); cfg->gpio_int.pin);
/* disable interrupt in case of warm (re)boot */ /* disable interrupt in case of warm (re)boot */
status = lis2dh->hw_tf->write_reg( status = lis2dh->hw_tf->write_reg(dev,
dev, cfg->hw.anym_on_int1 ?
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_CFG : LIS2DH_REG_INT2_CFG, LIS2DH_REG_INT1_CFG :
0); 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) { if (status < 0) {
LOG_ERR("Interrupt disable reg write failed (%d)", status); LOG_ERR("Interrupt disable reg write failed (%d)", status);
return status; return status;
} }
(void)memset(raw, 0, sizeof(raw)); (void)memset(raw, 0, sizeof(raw));
status = lis2dh->hw_tf->write_data( status = lis2dh->hw_tf->write_data(dev,
dev, cfg->hw.anym_on_int1 ?
cfg->hw.anym_on_int1 ? LIS2DH_REG_INT1_THS : LIS2DH_REG_INT2_THS, LIS2DH_REG_INT1_THS :
raw, sizeof(raw)); LIS2DH_REG_INT2_THS,
raw, sizeof(raw));
if (status < 0) { if (status < 0) {
LOG_ERR("Burst write to THS failed (%d)", status); LOG_ERR("Burst write to THS failed (%d)", status);
return status; return status;
} }
if (cfg->hw.anym_on_int1) { if (cfg->hw.anym_latch) {
/* enable interrupt 1 on int1 line */ /* latch line interrupt */
status = lis2dh->hw_tf->update_reg(dev, LIS2DH_REG_CTRL3, status = lis2dh->hw_tf->write_reg(dev,
LIS2DH_EN_INT1_INT1, LIS2DH_REG_CTRL5,
LIS2DH_EN_INT1_INT1); cfg->hw.anym_on_int1 ?
if (cfg->hw.anym_latch) { LIS2DH_EN_LIR_INT1 :
/* latch int1 line interrupt */ LIS2DH_EN_LIR_INT2);
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 (status < 0) { if (status < 0) {