Commit 5ec6486d authored by Martin Kelly's avatar Martin Kelly Committed by Jonathan Cameron

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: default avatarMartin Kelly <mkelly@xevo.com>
Acked-by: default avatarJean-Baptiste Maneyrol <jmaneyrol@invensense.com>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 17623d9a
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/iio/iio.h> #include <linux/iio/iio.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/platform_device.h>
#include "inv_mpu_iio.h" #include "inv_mpu_iio.h"
/* /*
...@@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = { ...@@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
.raw_accl = INV_MPU6050_REG_RAW_ACCEL, .raw_accl = INV_MPU6050_REG_RAW_ACCEL,
.temperature = INV_MPU6050_REG_TEMPERATURE, .temperature = INV_MPU6050_REG_TEMPERATURE,
.int_enable = INV_MPU6050_REG_INT_ENABLE, .int_enable = INV_MPU6050_REG_INT_ENABLE,
.int_status = INV_MPU6050_REG_INT_STATUS,
.pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1, .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
.pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2, .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG, .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
...@@ -287,6 +289,10 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev) ...@@ -287,6 +289,10 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
if (result) if (result)
return result; return result;
result = regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
if (result)
return result;
memcpy(&st->chip_config, hw_info[st->chip_type].config, memcpy(&st->chip_config, hw_info[st->chip_type].config,
sizeof(struct inv_mpu6050_chip_config)); sizeof(struct inv_mpu6050_chip_config));
result = inv_mpu6050_set_power_itg(st, false); 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, ...@@ -892,6 +898,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
struct inv_mpu6050_platform_data *pdata; struct inv_mpu6050_platform_data *pdata;
struct device *dev = regmap_get_device(regmap); struct device *dev = regmap_get_device(regmap);
int result; int result;
struct irq_data *desc;
int irq_type;
indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev) if (!indio_dev)
...@@ -923,6 +931,29 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, ...@@ -923,6 +931,29 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
st->plat_data = *pdata; st->plat_data = *pdata;
} }
desc = irq_get_irq_data(irq);
if (!desc) {
dev_err(dev, "Could not find IRQ %d\n", irq);
return -EINVAL;
}
irq_type = irqd_get_trigger_type(desc);
if (irq_type == IRQF_TRIGGER_RISING)
st->irq_mask = INV_MPU6050_ACTIVE_HIGH;
else if (irq_type == IRQF_TRIGGER_FALLING)
st->irq_mask = INV_MPU6050_ACTIVE_LOW;
else if (irq_type == IRQF_TRIGGER_HIGH)
st->irq_mask = INV_MPU6050_ACTIVE_HIGH |
INV_MPU6050_LATCH_INT_EN;
else if (irq_type == IRQF_TRIGGER_LOW)
st->irq_mask = INV_MPU6050_ACTIVE_LOW |
INV_MPU6050_LATCH_INT_EN;
else {
dev_err(dev, "Invalid interrupt type 0x%x specified\n",
irq_type);
return -EINVAL;
}
/* power is turned on inside check chip type*/ /* power is turned on inside check chip type*/
result = inv_check_and_setup_chip(st); result = inv_check_and_setup_chip(st);
if (result) if (result)
...@@ -958,7 +989,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, ...@@ -958,7 +989,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
dev_err(dev, "configure buffer fail %d\n", result); dev_err(dev, "configure buffer fail %d\n", result);
return result; return result;
} }
result = inv_mpu6050_probe_trigger(indio_dev); result = inv_mpu6050_probe_trigger(indio_dev, irq_type);
if (result) { if (result) {
dev_err(dev, "trigger probe fail %d\n", result); dev_err(dev, "trigger probe fail %d\n", result);
goto out_unreg_ring; goto out_unreg_ring;
......
...@@ -38,8 +38,7 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id) ...@@ -38,8 +38,7 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
goto error_unlock; goto error_unlock;
ret = regmap_write(st->map, st->reg->int_pin_cfg, ret = regmap_write(st->map, st->reg->int_pin_cfg,
INV_MPU6050_INT_PIN_CFG | st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
INV_MPU6050_BIT_BYPASS_EN);
error_unlock: error_unlock:
mutex_unlock(&st->lock); mutex_unlock(&st->lock);
...@@ -55,7 +54,7 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id) ...@@ -55,7 +54,7 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
mutex_lock(&st->lock); mutex_lock(&st->lock);
/* It doesn't really mattter, if any of the calls fails */ /* It doesn't really mattter, if any of the calls fails */
regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG); regmap_write(st->map, st->reg->int_pin_cfg, st->irq_mask);
inv_mpu6050_set_power_itg(st, false); inv_mpu6050_set_power_itg(st, false);
mutex_unlock(&st->lock); mutex_unlock(&st->lock);
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
* @raw_accl: Address of first accel register. * @raw_accl: Address of first accel register.
* @temperature: temperature register * @temperature: temperature register
* @int_enable: Interrupt enable register. * @int_enable: Interrupt enable register.
* @int_status: Interrupt status register.
* @pwr_mgmt_1: Controls chip's power state and clock source. * @pwr_mgmt_1: Controls chip's power state and clock source.
* @pwr_mgmt_2: Controls power state of individual sensors. * @pwr_mgmt_2: Controls power state of individual sensors.
* @int_pin_cfg; Controls interrupt pin configuration. * @int_pin_cfg; Controls interrupt pin configuration.
...@@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map { ...@@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map {
u8 raw_accl; u8 raw_accl;
u8 temperature; u8 temperature;
u8 int_enable; u8 int_enable;
u8 int_status;
u8 pwr_mgmt_1; u8 pwr_mgmt_1;
u8 pwr_mgmt_2; u8 pwr_mgmt_2;
u8 int_pin_cfg; u8 int_pin_cfg;
...@@ -126,6 +128,7 @@ struct inv_mpu6050_hw { ...@@ -126,6 +128,7 @@ struct inv_mpu6050_hw {
* @timestamps: kfifo queue to store time stamp. * @timestamps: kfifo queue to store time stamp.
* @map regmap pointer. * @map regmap pointer.
* @irq interrupt number. * @irq interrupt number.
* @irq_mask the int_pin_cfg mask to configure interrupt type.
*/ */
struct inv_mpu6050_state { struct inv_mpu6050_state {
#define TIMESTAMP_FIFO_SIZE 16 #define TIMESTAMP_FIFO_SIZE 16
...@@ -144,6 +147,7 @@ struct inv_mpu6050_state { ...@@ -144,6 +147,7 @@ struct inv_mpu6050_state {
DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE); DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
struct regmap *map; struct regmap *map;
int irq; int irq;
u8 irq_mask;
}; };
/*register and associated bit definition*/ /*register and associated bit definition*/
...@@ -167,6 +171,9 @@ struct inv_mpu6050_state { ...@@ -167,6 +171,9 @@ struct inv_mpu6050_state {
#define INV_MPU6050_REG_TEMPERATURE 0x41 #define INV_MPU6050_REG_TEMPERATURE 0x41
#define INV_MPU6050_REG_RAW_GYRO 0x43 #define INV_MPU6050_REG_RAW_GYRO 0x43
#define INV_MPU6050_REG_INT_STATUS 0x3A
#define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01
#define INV_MPU6050_REG_USER_CTRL 0x6A #define INV_MPU6050_REG_USER_CTRL 0x6A
#define INV_MPU6050_BIT_FIFO_RST 0x04 #define INV_MPU6050_BIT_FIFO_RST 0x04
#define INV_MPU6050_BIT_DMP_RST 0x08 #define INV_MPU6050_BIT_DMP_RST 0x08
...@@ -216,8 +223,12 @@ struct inv_mpu6050_state { ...@@ -216,8 +223,12 @@ struct inv_mpu6050_state {
#define INV_MPU6050_OUTPUT_DATA_SIZE 24 #define INV_MPU6050_OUTPUT_DATA_SIZE 24
#define INV_MPU6050_REG_INT_PIN_CFG 0x37 #define INV_MPU6050_REG_INT_PIN_CFG 0x37
#define INV_MPU6050_ACTIVE_HIGH 0x00
#define INV_MPU6050_ACTIVE_LOW 0x80
/* enable level triggering */
#define INV_MPU6050_LATCH_INT_EN 0x20
#define INV_MPU6050_BIT_BYPASS_EN 0x2 #define INV_MPU6050_BIT_BYPASS_EN 0x2
#define INV_MPU6050_INT_PIN_CFG 0
/* init parameters */ /* init parameters */
#define INV_MPU6050_INIT_FIFO_RATE 50 #define INV_MPU6050_INIT_FIFO_RATE 50
...@@ -289,7 +300,7 @@ enum inv_mpu6050_clock_sel_e { ...@@ -289,7 +300,7 @@ enum inv_mpu6050_clock_sel_e {
irqreturn_t inv_mpu6050_irq_handler(int irq, void *p); irqreturn_t inv_mpu6050_irq_handler(int irq, void *p);
irqreturn_t inv_mpu6050_read_fifo(int irq, void *p); irqreturn_t inv_mpu6050_read_fifo(int irq, void *p);
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev); int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type);
void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st); void inv_mpu6050_remove_trigger(struct inv_mpu6050_state *st);
int inv_reset_fifo(struct iio_dev *indio_dev); int inv_reset_fifo(struct iio_dev *indio_dev);
int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask); int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
......
...@@ -127,8 +127,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) ...@@ -127,8 +127,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
u16 fifo_count; u16 fifo_count;
s64 timestamp; s64 timestamp;
int int_status;
mutex_lock(&st->lock); mutex_lock(&st->lock);
/* ack interrupt and check status */
result = regmap_read(st->map, st->reg->int_status, &int_status);
if (result) {
dev_err(regmap_get_device(st->map),
"failed to ack interrupt\n");
goto flush_fifo;
}
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
dev_warn(regmap_get_device(st->map),
"spurious interrupt with status 0x%x\n", int_status);
goto end_session;
}
if (!(st->chip_config.accl_fifo_enable | if (!(st->chip_config.accl_fifo_enable |
st->chip_config.gyro_fifo_enable)) st->chip_config.gyro_fifo_enable))
goto end_session; goto end_session;
......
...@@ -117,7 +117,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = { ...@@ -117,7 +117,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
.set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state,
}; };
int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type)
{ {
int ret; int ret;
struct inv_mpu6050_state *st = iio_priv(indio_dev); struct inv_mpu6050_state *st = iio_priv(indio_dev);
...@@ -131,7 +131,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev) ...@@ -131,7 +131,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev)
ret = devm_request_irq(&indio_dev->dev, st->irq, ret = devm_request_irq(&indio_dev->dev, st->irq,
&iio_trigger_generic_data_rdy_poll, &iio_trigger_generic_data_rdy_poll,
IRQF_TRIGGER_RISING, irq_type,
"inv_mpu", "inv_mpu",
st->trig); st->trig);
if (ret) if (ret)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment