linux/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3* Copyright (C) 2012 Invensense, Inc.
   4*/
   5
   6#include <linux/module.h>
   7#include <linux/slab.h>
   8#include <linux/err.h>
   9#include <linux/delay.h>
  10#include <linux/sysfs.h>
  11#include <linux/jiffies.h>
  12#include <linux/irq.h>
  13#include <linux/interrupt.h>
  14#include <linux/poll.h>
  15#include <linux/math64.h>
  16#include <asm/unaligned.h>
  17#include "inv_mpu_iio.h"
  18
  19/**
  20 *  inv_mpu6050_update_period() - Update chip internal period estimation
  21 *
  22 *  @st:                driver state
  23 *  @timestamp:         the interrupt timestamp
  24 *  @nb:                number of data set in the fifo
  25 *
  26 *  This function uses interrupt timestamps to estimate the chip period and
  27 *  to choose the data timestamp to come.
  28 */
  29static void inv_mpu6050_update_period(struct inv_mpu6050_state *st,
  30                                      s64 timestamp, size_t nb)
  31{
  32        /* Period boundaries for accepting timestamp */
  33        const s64 period_min =
  34                (NSEC_PER_MSEC * (100 - INV_MPU6050_TS_PERIOD_JITTER)) / 100;
  35        const s64 period_max =
  36                (NSEC_PER_MSEC * (100 + INV_MPU6050_TS_PERIOD_JITTER)) / 100;
  37        const s32 divider = INV_MPU6050_FREQ_DIVIDER(st);
  38        s64 delta, interval;
  39        bool use_it_timestamp = false;
  40
  41        if (st->it_timestamp == 0) {
  42                /* not initialized, forced to use it_timestamp */
  43                use_it_timestamp = true;
  44        } else if (nb == 1) {
  45                /*
  46                 * Validate the use of it timestamp by checking if interrupt
  47                 * has been delayed.
  48                 * nb > 1 means interrupt was delayed for more than 1 sample,
  49                 * so it's obviously not good.
  50                 * Compute the chip period between 2 interrupts for validating.
  51                 */
  52                delta = div_s64(timestamp - st->it_timestamp, divider);
  53                if (delta > period_min && delta < period_max) {
  54                        /* update chip period and use it timestamp */
  55                        st->chip_period = (st->chip_period + delta) / 2;
  56                        use_it_timestamp = true;
  57                }
  58        }
  59
  60        if (use_it_timestamp) {
  61                /*
  62                 * Manage case of multiple samples in the fifo (nb > 1):
  63                 * compute timestamp corresponding to the first sample using
  64                 * estimated chip period.
  65                 */
  66                interval = (nb - 1) * st->chip_period * divider;
  67                st->data_timestamp = timestamp - interval;
  68        }
  69
  70        /* save it timestamp */
  71        st->it_timestamp = timestamp;
  72}
  73
  74/**
  75 *  inv_mpu6050_get_timestamp() - Return the current data timestamp
  76 *
  77 *  @st:                driver state
  78 *  @return:            current data timestamp
  79 *
  80 *  This function returns the current data timestamp and prepares for next one.
  81 */
  82static s64 inv_mpu6050_get_timestamp(struct inv_mpu6050_state *st)
  83{
  84        s64 ts;
  85
  86        /* return current data timestamp and increment */
  87        ts = st->data_timestamp;
  88        st->data_timestamp += st->chip_period * INV_MPU6050_FREQ_DIVIDER(st);
  89
  90        return ts;
  91}
  92
  93static int inv_reset_fifo(struct iio_dev *indio_dev)
  94{
  95        int result;
  96        struct inv_mpu6050_state  *st = iio_priv(indio_dev);
  97
  98        /* disable fifo and reenable it */
  99        inv_mpu6050_prepare_fifo(st, false);
 100        result = inv_mpu6050_prepare_fifo(st, true);
 101        if (result)
 102                goto reset_fifo_fail;
 103
 104        return 0;
 105
 106reset_fifo_fail:
 107        dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
 108        result = regmap_write(st->map, st->reg->int_enable,
 109                              INV_MPU6050_BIT_DATA_RDY_EN);
 110
 111        return result;
 112}
 113
 114/**
 115 * inv_mpu6050_read_fifo() - Transfer data from hardware FIFO to KFIFO.
 116 */
 117irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
 118{
 119        struct iio_poll_func *pf = p;
 120        struct iio_dev *indio_dev = pf->indio_dev;
 121        struct inv_mpu6050_state *st = iio_priv(indio_dev);
 122        size_t bytes_per_datum;
 123        int result;
 124        u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
 125        u16 fifo_count;
 126        s64 timestamp;
 127        int int_status;
 128        size_t i, nb;
 129
 130        mutex_lock(&st->lock);
 131
 132        /* ack interrupt and check status */
 133        result = regmap_read(st->map, st->reg->int_status, &int_status);
 134        if (result) {
 135                dev_err(regmap_get_device(st->map),
 136                        "failed to ack interrupt\n");
 137                goto flush_fifo;
 138        }
 139        if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT))
 140                goto end_session;
 141
 142        if (!(st->chip_config.accl_fifo_enable |
 143                st->chip_config.gyro_fifo_enable |
 144                st->chip_config.magn_fifo_enable))
 145                goto end_session;
 146        bytes_per_datum = 0;
 147        if (st->chip_config.accl_fifo_enable)
 148                bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
 149
 150        if (st->chip_config.gyro_fifo_enable)
 151                bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
 152
 153        if (st->chip_config.temp_fifo_enable)
 154                bytes_per_datum += INV_MPU6050_BYTES_PER_TEMP_SENSOR;
 155
 156        if (st->chip_config.magn_fifo_enable)
 157                bytes_per_datum += INV_MPU9X50_BYTES_MAGN;
 158
 159        /*
 160         * read fifo_count register to know how many bytes are inside the FIFO
 161         * right now
 162         */
 163        result = regmap_bulk_read(st->map, st->reg->fifo_count_h, data,
 164                                  INV_MPU6050_FIFO_COUNT_BYTE);
 165        if (result)
 166                goto end_session;
 167        fifo_count = get_unaligned_be16(&data[0]);
 168
 169        /*
 170         * Handle fifo overflow by resetting fifo.
 171         * Reset if there is only 3 data set free remaining to mitigate
 172         * possible delay between reading fifo count and fifo data.
 173         */
 174        nb = 3 * bytes_per_datum;
 175        if (fifo_count >= st->hw->fifo_size - nb) {
 176                dev_warn(regmap_get_device(st->map), "fifo overflow reset\n");
 177                goto flush_fifo;
 178        }
 179
 180        /* compute and process all complete datum */
 181        nb = fifo_count / bytes_per_datum;
 182        inv_mpu6050_update_period(st, pf->timestamp, nb);
 183        for (i = 0; i < nb; ++i) {
 184                result = regmap_bulk_read(st->map, st->reg->fifo_r_w,
 185                                          data, bytes_per_datum);
 186                if (result)
 187                        goto flush_fifo;
 188                /* skip first samples if needed */
 189                if (st->skip_samples) {
 190                        st->skip_samples--;
 191                        continue;
 192                }
 193                timestamp = inv_mpu6050_get_timestamp(st);
 194                iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
 195        }
 196
 197end_session:
 198        mutex_unlock(&st->lock);
 199        iio_trigger_notify_done(indio_dev->trig);
 200
 201        return IRQ_HANDLED;
 202
 203flush_fifo:
 204        /* Flush HW and SW FIFOs. */
 205        inv_reset_fifo(indio_dev);
 206        mutex_unlock(&st->lock);
 207        iio_trigger_notify_done(indio_dev->trig);
 208
 209        return IRQ_HANDLED;
 210}
 211