]> Git Repo - linux.git/blob - drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
Linux 6.14-rc3
[linux.git] / drivers / iio / imu / inv_mpu6050 / inv_mpu_ring.c
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
17 #include <linux/iio/common/inv_sensors_timestamp.h>
18
19 #include "inv_mpu_iio.h"
20
21 static int inv_reset_fifo(struct iio_dev *indio_dev)
22 {
23         int result;
24         struct inv_mpu6050_state  *st = iio_priv(indio_dev);
25
26         /* disable fifo and reenable it */
27         inv_mpu6050_prepare_fifo(st, false);
28         result = inv_mpu6050_prepare_fifo(st, true);
29         if (result)
30                 goto reset_fifo_fail;
31
32         return 0;
33
34 reset_fifo_fail:
35         dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result);
36         return regmap_update_bits(st->map, st->reg->int_enable,
37                         INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN);
38 }
39
40 /*
41  * inv_mpu6050_read_fifo() - Transfer data from hardware FIFO to KFIFO.
42  */
43 irqreturn_t inv_mpu6050_read_fifo(int irq, void *p)
44 {
45         struct iio_poll_func *pf = p;
46         struct iio_dev *indio_dev = pf->indio_dev;
47         struct inv_mpu6050_state *st = iio_priv(indio_dev);
48         size_t bytes_per_datum;
49         int result;
50         u16 fifo_count;
51         u32 fifo_period;
52         s64 timestamp;
53         u8 data[INV_MPU6050_OUTPUT_DATA_SIZE];
54         size_t i, nb;
55
56         mutex_lock(&st->lock);
57
58         if (!(st->chip_config.accl_fifo_enable |
59                 st->chip_config.gyro_fifo_enable |
60                 st->chip_config.magn_fifo_enable))
61                 goto end_session;
62         bytes_per_datum = 0;
63         if (st->chip_config.accl_fifo_enable)
64                 bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
65
66         if (st->chip_config.gyro_fifo_enable)
67                 bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
68
69         if (st->chip_config.temp_fifo_enable)
70                 bytes_per_datum += INV_MPU6050_BYTES_PER_TEMP_SENSOR;
71
72         if (st->chip_config.magn_fifo_enable)
73                 bytes_per_datum += INV_MPU9X50_BYTES_MAGN;
74
75         /*
76          * read fifo_count register to know how many bytes are inside the FIFO
77          * right now
78          */
79         result = regmap_bulk_read(st->map, st->reg->fifo_count_h,
80                                   st->data, INV_MPU6050_FIFO_COUNT_BYTE);
81         if (result)
82                 goto end_session;
83         fifo_count = be16_to_cpup((__be16 *)&st->data[0]);
84
85         /*
86          * Handle fifo overflow by resetting fifo.
87          * Reset if there is only 3 data set free remaining to mitigate
88          * possible delay between reading fifo count and fifo data.
89          */
90         nb = 3 * bytes_per_datum;
91         if (fifo_count >= st->hw->fifo_size - nb) {
92                 dev_warn(regmap_get_device(st->map), "fifo overflow reset\n");
93                 goto flush_fifo;
94         }
95
96         /* compute and process only all complete datum */
97         nb = fifo_count / bytes_per_datum;
98         fifo_count = nb * bytes_per_datum;
99         if (nb == 0)
100                 goto end_session;
101         /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */
102         fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider);
103         inv_sensors_timestamp_interrupt(&st->timestamp, 1, pf->timestamp);
104         inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, 1, 0);
105
106         /* clear internal data buffer for avoiding kernel data leak */
107         memset(data, 0, sizeof(data));
108
109         /* read all data once and process every samples */
110         result = regmap_noinc_read(st->map, st->reg->fifo_r_w, st->data, fifo_count);
111         if (result)
112                 goto flush_fifo;
113         for (i = 0; i < nb; ++i) {
114                 /* skip first samples if needed */
115                 if (st->skip_samples) {
116                         st->skip_samples--;
117                         continue;
118                 }
119                 memcpy(data, &st->data[i * bytes_per_datum], bytes_per_datum);
120                 timestamp = inv_sensors_timestamp_pop(&st->timestamp);
121                 iio_push_to_buffers_with_timestamp(indio_dev, data, timestamp);
122         }
123
124 end_session:
125         mutex_unlock(&st->lock);
126         iio_trigger_notify_done(indio_dev->trig);
127
128         return IRQ_HANDLED;
129
130 flush_fifo:
131         /* Flush HW and SW FIFOs. */
132         inv_reset_fifo(indio_dev);
133         mutex_unlock(&st->lock);
134         iio_trigger_notify_done(indio_dev->trig);
135
136         return IRQ_HANDLED;
137 }
This page took 0.039966 seconds and 4 git commands to generate.