Re: [PATCH v7 1/2] iio:imu: inv_mpu6050: support more interrupt types

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri, 20 Apr 2018 19:04:17 +0200
Jean-Baptiste Maneyrol <jmaneyrol@xxxxxxxxxxxxxx> wrote:

> On 20/04/2018 18:54, Martin Kelly wrote:
> > 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 <mkelly@xxxxxxxx>
> > ---
> > v7:
> > - Actually flush the FIFO when we fail to ACK the interrupt, and end the session
> >    when we get a spurious interrupt.
> > v6:
> > - Use -EINVAL if devicetree interrupt configuration is missing.
> > - Use u8 for irq_mask.
> > - Stop mixing register and bit defines, and group them separately.
> > - If we fail to ACK the interrupt, flush the FIFO.
> > v5:
> > - Check interrupt status in all cases rather than only in the level triggered
> >     case, and warn if we get spurious interrupts.
> > - Rename INV_MPU6050_LEVEL_TRIGGERED to INV_MPU6050_LATCH_INT_EN to match
> >     datasheet naming.
> > - Write st->irq_mask prior to device power off to make sure it is fully set.
> > - Write st->irq_mask instead of 0 in inv_mpu6050_deselect_bypass.
> > - Make irq_type local instead of part of the driver state, as we use it only at
> >     probe time and never again.
> > - Remove the comment about bus lockups, as I have determined them to be
> >     unrelated.
> > - Add missing documentation for irq_type and irq_mask.
> > v4:
> > - Moved the ACK inside the mutex.
> > v3:
> > - Sent version 2 too quickly. Now that the ACK is moved to the top of the
> >     function, the "goto out" logic is unnecessary, so we can clean it up.
> > v2:
> > - Changed to ACK level interrupts at the start of the bottom half thread instead
> >     of at the end of it. Without this, the sample timestamps get distorted because
> >     the time to handle the bottom half thread delays future interrupts. With this
> >     change, the timestamps appear evenly distributed at the right frequency.
> > 
> >   drivers/iio/imu/inv_mpu6050/inv_mpu_core.c    | 33 ++++++++++++++++++++++++++-
> >   drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c     |  5 ++--
> >   drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h     | 15 ++++++++++--
> >   drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c    | 15 ++++++++++++
> >   drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c |  4 ++--
> >   5 files changed, 64 insertions(+), 8 deletions(-)
> > 
> > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
> > index 7d64be353403..c2cec7508451 100644
> > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
> > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
> > @@ -24,6 +24,7 @@
> >   #include <linux/spinlock.h>
> >   #include <linux/iio/iio.h>
> >   #include <linux/acpi.h>
> > +#include <linux/platform_device.h>
> >   #include "inv_mpu_iio.h"
> >   
> >   /*
> > @@ -52,6 +53,7 @@ static const struct inv_mpu6050_reg_map reg_set_6500 = {
> >   	.raw_accl               = INV_MPU6050_REG_RAW_ACCEL,
> >   	.temperature            = INV_MPU6050_REG_TEMPERATURE,
> >   	.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_2             = INV_MPU6050_REG_PWR_MGMT_2,
> >   	.int_pin_cfg		= INV_MPU6050_REG_INT_PIN_CFG,
> > @@ -278,6 +280,10 @@ static int inv_mpu6050_init_config(struct iio_dev *indio_dev)
> >   	if (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,
> >   	       sizeof(struct inv_mpu6050_chip_config));
> >   	result = inv_mpu6050_set_power_itg(st, false);
> > @@ -882,6 +888,8 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
> >   	struct inv_mpu6050_platform_data *pdata;
> >   	struct device *dev = regmap_get_device(regmap);
> >   	int result;
> > +	struct irq_data *desc;
> > +	int irq_type;
> >   
> >   	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> >   	if (!indio_dev)
> > @@ -913,6 +921,29 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
> >   		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*/
> >   	result = inv_check_and_setup_chip(st);
> >   	if (result)
> > @@ -948,7 +979,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
> >   		dev_err(dev, "configure buffer fail %d\n", result);
> >   		return result;
> >   	}
> > -	result = inv_mpu6050_probe_trigger(indio_dev);
> > +	result = inv_mpu6050_probe_trigger(indio_dev, irq_type);
> >   	if (result) {
> >   		dev_err(dev, "trigger probe fail %d\n", result);
> >   		goto out_unreg_ring;
> > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
> > index fcd7a92b6cf8..1b02d2b69174 100644
> > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
> > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
> > @@ -44,8 +44,7 @@ static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
> >   	if (!ret) {
> >   		st->powerup_count++;
> >   		ret = regmap_write(st->map, st->reg->int_pin_cfg,
> > -				   INV_MPU6050_INT_PIN_CFG |
> > -				   INV_MPU6050_BIT_BYPASS_EN);
> > +			   st->irq_mask | INV_MPU6050_BIT_BYPASS_EN);
> >   	}
> >   write_error:
> >   	mutex_unlock(&st->lock);
> > @@ -60,7 +59,7 @@ static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
> >   
> >   	mutex_lock(&st->lock);
> >   	/* 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);
> >   	st->powerup_count--;
> >   	if (!st->powerup_count)
> >   		regmap_write(st->map, st->reg->pwr_mgmt_1,
> > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
> > index 065794162d65..eaa9158ac753 100644
> > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
> > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
> > @@ -40,6 +40,7 @@
> >    *  @raw_accl:		Address of first accel register.
> >    *  @temperature:	temperature register
> >    *  @int_enable:	Interrupt enable register.
> > + *  @int_status:	Interrupt status register.
> >    *  @pwr_mgmt_1:	Controls chip's power state and clock source.
> >    *  @pwr_mgmt_2:	Controls power state of individual sensors.
> >    *  @int_pin_cfg;	Controls interrupt pin configuration.
> > @@ -60,6 +61,7 @@ struct inv_mpu6050_reg_map {
> >   	u8 raw_accl;
> >   	u8 temperature;
> >   	u8 int_enable;
> > +	u8 int_status;
> >   	u8 pwr_mgmt_1;
> >   	u8 pwr_mgmt_2;
> >   	u8 int_pin_cfg;
> > @@ -125,6 +127,7 @@ struct inv_mpu6050_hw {
> >    *  @timestamps:        kfifo queue to store time stamp.
> >    *  @map		regmap pointer.
> >    *  @irq		interrupt number.
> > + *  @irq_mask		the int_pin_cfg mask to configure interrupt type.
> >    */
> >   struct inv_mpu6050_state {
> >   #define TIMESTAMP_FIFO_SIZE 16
> > @@ -143,6 +146,7 @@ struct inv_mpu6050_state {
> >   	DECLARE_KFIFO(timestamps, long long, TIMESTAMP_FIFO_SIZE);
> >   	struct regmap *map;
> >   	int irq;
> > +	u8 irq_mask;
> >   };
> >   
> >   /*register and associated bit definition*/
> > @@ -166,6 +170,9 @@ struct inv_mpu6050_state {
> >   #define INV_MPU6050_REG_TEMPERATURE         0x41
> >   #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_BIT_FIFO_RST            0x04
> >   #define INV_MPU6050_BIT_DMP_RST             0x08
> > @@ -215,8 +222,12 @@ struct inv_mpu6050_state {
> >   #define INV_MPU6050_OUTPUT_DATA_SIZE         24
> >   
> >   #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_INT_PIN_CFG		0
> > +
> >   
> >   /* init parameters */
> >   #define INV_MPU6050_INIT_FIFO_RATE           50
> > @@ -287,7 +298,7 @@ enum inv_mpu6050_clock_sel_e {
> >   
> >   irqreturn_t inv_mpu6050_irq_handler(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);
> >   int inv_reset_fifo(struct iio_dev *indio_dev);
> >   int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, u32 mask);
> > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
> > index ff81c6aa009d..672c3df2d1d1 100644
> > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
> > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
> > @@ -127,8 +127,23 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
> >   	u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
> >   	u16 fifo_count;
> >   	s64 timestamp;
> > +	int int_status;
> >   
> >   	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 |
> >   		st->chip_config.gyro_fifo_enable))
> >   		goto end_session;
> > diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
> > index f963f9fc98c0..b8c5584e4252 100644
> > --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
> > +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
> > @@ -117,7 +117,7 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = {
> >   	.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;
> >   	struct inv_mpu6050_state *st = iio_priv(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,
> >   			       &iio_trigger_generic_data_rdy_poll,
> > -			       IRQF_TRIGGER_RISING,
> > +			       irq_type,
> >   			       "inv_mpu",
> >   			       st->trig);
> >   	if (ret)
> >   
> 
> All OK for me now.
> 
> Acked-by: Jean-Baptiste Maneyrol <jmaneyrol@xxxxxxxxxxxxxx>
Applied to the togreg branch of iio.git.  Note there was some fuzz
so please check I resolved things correctly.

Pushed out as testing for the autobuilders to play with it.

Thanks,

Jonathan


--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux