Skip to content

Commit 5ec6486

Browse files
Martin Kellyjic23
authored andcommitted
iio:imu: inv_mpu6050: support more interrupt types
Currently, we support only rising edge interrupts, and in fact we assume that the interrupt we're given is rising edge (and things won't work if it's not). However, the device supports rising edge, falling edge, level low, and level high interrupts. Empirically, on my system, switching to level interrupts has fixed a problem I had with significant (~40%) interrupt loss with edge interrupts. This issue is likely related to the SoC I'm using (Allwinner H3), but being able to switch the interrupt type is still a very useful workaround. I tested this with each interrupt type and verified correct behavior in a logic analyzer. Add support for these interrupt types while also eliminating the error case of the device tree and driver using different interrupt types. Signed-off-by: Martin Kelly <[email protected]> Acked-by: Jean-Baptiste Maneyrol <[email protected]> Signed-off-by: Jonathan Cameron <[email protected]>
1 parent 17623d9 commit 5ec6486

File tree

5 files changed

+64
-8
lines changed

5 files changed

+64
-8
lines changed

drivers/iio/imu/inv_mpu6050/inv_mpu_core.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <linux/spinlock.h>
2525
#include <linux/iio/iio.h>
2626
#include <linux/acpi.h>
27+
#include <linux/platform_device.h>
2728
#include "inv_mpu_iio.h"
2829

2930
/*
@@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
5253
.raw_accl = INV_MPU6050_REG_RAW_ACCEL,
5354
.temperature = INV_MPU6050_REG_TEMPERATURE,
5455
.int_enable = INV_MPU6050_REG_INT_ENABLE,
56+
.int_status = INV_MPU6050_REG_INT_STATUS,
5557
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
5658
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
5759
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
@@ -287,6 +289,10 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
287289
if (result)
288290
return result;
289291

292+
result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
293+
if (result)
294+
return result;
295+
290296
memcpy(&st->chip_config, hw_info[st->chip_type].config,
291297
sizeof(struct inv_mpu6050_chip_config));
292298
result = inv_mpu6050_set_power_itg(st, false);
@@ -892,6 +898,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
892898
struct inv_mpu6050_platform_data *pdata;
893899
struct device *dev = regmap_get_device(regmap);
894900
int result;
901+
struct irq_data *desc;
902+
int irq_type;
895903

896904
indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
897905
if (!indio_dev)
@@ -923,6 +931,29 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
923931
st->plat_data = *pdata;
924932
}
925933

934+
desc = irq_get_irq_data(irq);
935+
if (!desc) {
936+
dev_err(dev, "Could not find IRQ %d\n", irq);
937+
return -EINVAL;
938+
}
939+
940+
irq_type = irqd_get_trigger_type(desc);
941+
if (irq_type == IRQF_TRIGGER_RISING)
942+
st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
943+
else if (irq_type == IRQF_TRIGGER_FALLING)
944+
st->irq_mask = INV_MPU6050_ACTIVE_LOW;
945+
else if (irq_type == IRQF_TRIGGER_HIGH)
946+
st->irq_mask = INV_MPU6050_ACTIVE_HIGH |
947+
INV_MPU6050_LATCH_INT_EN;
948+
else if (irq_type == IRQF_TRIGGER_LOW)
949+
st->irq_mask = INV_MPU6050_ACTIVE_LOW |
950+
INV_MPU6050_LATCH_INT_EN;
951+
else {
952+
dev_err(dev, "Invalid interrupt type 0x%x specified\n",
953+
irq_type);
954+
return -EINVAL;
955+
}
956+
926957
/* power is turned on inside check chip type*/
927958
result = inv_check_and_setup_chip(st);
928959
if (result)
@@ -958,7 +989,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
958989
dev_err(dev, "configure buffer fail %d\n", result);
959990
return result;
960991
}
961-
result = inv_mpu6050_probe_trigger(indio_dev);
992+
result = inv_mpu6050_probe_trigger(indio_dev, irq_type);
962993
if (result) {
963994
dev_err(dev, "trigger probe fail %d\n", result);
964995
goto out_unreg_ring;

drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
3838
goto error_unlock;
3939

4040
ret = regmap_write(st->map, st->reg->int_pin_cfg,
41-
INV_MPU6050_INT_PIN_CFG |
42-
INV_MPU6050_BIT_BYPASS_EN);
41+
st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
4342

4443
error_unlock:
4544
mutex_unlock(&st->lock);
@@ -55,7 +54,7 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
5554
mutex_lock(&st->lock);
5655

5756
/* It doesn't really mattter, if any of the calls fails */
58-
regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG);
57+
regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
5958
inv_mpu6050_set_power_itg(st, false);
6059

6160
mutex_unlock(&st->lock);

drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
* @raw_accl: Address of first accel register.
4141
* @temperature: temperature register
4242
* @int_enable: Interrupt enable register.
43+
* @int_status: Interrupt status register.
4344
* @pwr_mgmt_1: Controls chip's power state and clock source.
4445
* @pwr_mgmt_2: Controls power state of individual sensors.
4546
* @int_pin_cfg; Controls interrupt pin configuration.
@@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map {
6061
u8 raw_accl;
6162
u8 temperature;
6263
u8 int_enable;
64+
u8 int_status;
6365
u8 pwr_mgmt_1;
6466
u8 pwr_mgmt_2;
6567
u8 int_pin_cfg;
@@ -126,6 +128,7 @@ struct inv_mpu6050_hw {
126128
* @timestamps: kfifo queue to store time stamp.
127129
* @map regmap pointer.
128130
* @irq interrupt number.
131+
* @irq_mask the int_pin_cfg mask to configure interrupt type.
129132
*/
130133
struct inv_mpu6050_state {
131134
#define TIMESTAMP_FIFO_SIZE 16
@@ -144,6 +147,7 @@ struct inv_mpu6050_state {
144147
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
145148
struct regmap *map;
146149
int irq;
150+
u8 irq_mask;
147151
};
148152

149153
/*register and associated bit definition*/
@@ -167,6 +171,9 @@ struct inv_mpu6050_state {
167171
#define INV_MPU6050_REG_TEMPERATURE 0x41
168172
#define INV_MPU6050_REG_RAW_GYRO 0x43
169173

174+
#define INV_MPU6050_REG_INT_STATUS 0x3A
175+
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
176+
170177
#define INV_MPU6050_REG_USER_CTRL 0x6A
171178
#define INV_MPU6050_BIT_FIFO_RST 0x04
172179
#define INV_MPU6050_BIT_DMP_RST 0x08
@@ -216,8 +223,12 @@ struct inv_mpu6050_state {
216223
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
217224

218225
#define INV_MPU6050_REG_INT_PIN_CFG 0x37
226+
#define INV_MPU6050_ACTIVE_HIGH 0x00
227+
#define INV_MPU6050_ACTIVE_LOW 0x80
228+
/* enable level triggering */
229+
#define INV_MPU6050_LATCH_INT_EN 0x20
219230
#define INV_MPU6050_BIT_BYPASS_EN 0x2
220-
#define INV_MPU6050_INT_PIN_CFG 0
231+
221232

222233
/* init parameters */
223234
#define INV_MPU6050_INIT_FIFO_RATE 50
@@ -289,7 +300,7 @@ enum inv_mpu6050_clock_sel_e {
289300

290301
irqreturn_t inv_mpu6050_irq_handler(int irq, void *p);
291302
irqreturn_t inv_mpu6050_read_fifo(int irq, void *p);
292-
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev);
303+
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type);
293304
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st);
294305
int inv_reset_fifo(struct iio_dev *indio_dev);
295306
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);

drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
127127
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
128128
u16 fifo_count;
129129
s64 timestamp;
130+
int int_status;
130131

131132
mutex_lock(&st->lock);
133+
134+
/* ack interrupt and check status */
135+
result = regmap_read(st->map, st->reg->int_status, &int_status);
136+
if (result) {
137+
dev_err(regmap_get_device(st->map),
138+
"failed to ack interrupt\n");
139+
goto flush_fifo;
140+
}
141+
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
142+
dev_warn(regmap_get_device(st->map),
143+
"spurious interrupt with status 0x%x\n", int_status);
144+
goto end_session;
145+
}
146+
132147
if (!(st->chip_config.accl_fifo_enable |
133148
st->chip_config.gyro_fifo_enable))
134149
goto end_session;

drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
117117
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
118118
};
119119

120-
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
120+
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
121121
{
122122
int ret;
123123
struct inv_mpu6050_state *st = iio_priv(indio_dev);
@@ -131,7 +131,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
131131

132132
ret = devm_request_irq(&indio_dev->dev, st->irq,
133133
&iio_trigger_generic_data_rdy_poll,
134-
IRQF_TRIGGER_RISING,
134+
irq_type,
135135
"inv_mpu",
136136
st->trig);
137137
if (ret)

0 commit comments

Comments
 (0)