.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
---|
1 | 2 | /* |
---|
2 | 3 | * Copyright (C) 2012 Invensense, Inc. |
---|
3 | | -* |
---|
4 | | -* This software is licensed under the terms of the GNU General Public |
---|
5 | | -* License version 2, as published by the Free Software Foundation, and |
---|
6 | | -* may be copied, distributed, and modified under those terms. |
---|
7 | | -* |
---|
8 | | -* This program is distributed in the hope that it will be useful, |
---|
9 | | -* but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
10 | | -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
11 | | -* GNU General Public License for more details. |
---|
12 | 4 | */ |
---|
13 | 5 | |
---|
| 6 | +#include <linux/pm_runtime.h> |
---|
14 | 7 | #include "inv_mpu_iio.h" |
---|
15 | 8 | |
---|
16 | | -static void inv_scan_query(struct iio_dev *indio_dev) |
---|
| 9 | +static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev) |
---|
17 | 10 | { |
---|
18 | 11 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
---|
| 12 | + unsigned int mask; |
---|
19 | 13 | |
---|
20 | 14 | st->chip_config.gyro_fifo_enable = |
---|
21 | 15 | test_bit(INV_MPU6050_SCAN_GYRO_X, |
---|
.. | .. |
---|
32 | 26 | indio_dev->active_scan_mask) || |
---|
33 | 27 | test_bit(INV_MPU6050_SCAN_ACCL_Z, |
---|
34 | 28 | indio_dev->active_scan_mask); |
---|
| 29 | + |
---|
| 30 | + st->chip_config.temp_fifo_enable = |
---|
| 31 | + test_bit(INV_MPU6050_SCAN_TEMP, indio_dev->active_scan_mask); |
---|
| 32 | + |
---|
| 33 | + mask = 0; |
---|
| 34 | + if (st->chip_config.gyro_fifo_enable) |
---|
| 35 | + mask |= INV_MPU6050_SENSOR_GYRO; |
---|
| 36 | + if (st->chip_config.accl_fifo_enable) |
---|
| 37 | + mask |= INV_MPU6050_SENSOR_ACCL; |
---|
| 38 | + if (st->chip_config.temp_fifo_enable) |
---|
| 39 | + mask |= INV_MPU6050_SENSOR_TEMP; |
---|
| 40 | + |
---|
| 41 | + return mask; |
---|
| 42 | +} |
---|
| 43 | + |
---|
| 44 | +static unsigned int inv_scan_query_mpu9x50(struct iio_dev *indio_dev) |
---|
| 45 | +{ |
---|
| 46 | + struct inv_mpu6050_state *st = iio_priv(indio_dev); |
---|
| 47 | + unsigned int mask; |
---|
| 48 | + |
---|
| 49 | + mask = inv_scan_query_mpu6050(indio_dev); |
---|
| 50 | + |
---|
| 51 | + /* no magnetometer if i2c auxiliary bus is used */ |
---|
| 52 | + if (st->magn_disabled) |
---|
| 53 | + return mask; |
---|
| 54 | + |
---|
| 55 | + st->chip_config.magn_fifo_enable = |
---|
| 56 | + test_bit(INV_MPU9X50_SCAN_MAGN_X, |
---|
| 57 | + indio_dev->active_scan_mask) || |
---|
| 58 | + test_bit(INV_MPU9X50_SCAN_MAGN_Y, |
---|
| 59 | + indio_dev->active_scan_mask) || |
---|
| 60 | + test_bit(INV_MPU9X50_SCAN_MAGN_Z, |
---|
| 61 | + indio_dev->active_scan_mask); |
---|
| 62 | + if (st->chip_config.magn_fifo_enable) |
---|
| 63 | + mask |= INV_MPU6050_SENSOR_MAGN; |
---|
| 64 | + |
---|
| 65 | + return mask; |
---|
| 66 | +} |
---|
| 67 | + |
---|
| 68 | +static unsigned int inv_scan_query(struct iio_dev *indio_dev) |
---|
| 69 | +{ |
---|
| 70 | + struct inv_mpu6050_state *st = iio_priv(indio_dev); |
---|
| 71 | + |
---|
| 72 | + switch (st->chip_type) { |
---|
| 73 | + case INV_MPU9150: |
---|
| 74 | + case INV_MPU9250: |
---|
| 75 | + case INV_MPU9255: |
---|
| 76 | + return inv_scan_query_mpu9x50(indio_dev); |
---|
| 77 | + default: |
---|
| 78 | + return inv_scan_query_mpu6050(indio_dev); |
---|
| 79 | + } |
---|
| 80 | +} |
---|
| 81 | + |
---|
| 82 | +static unsigned int inv_compute_skip_samples(const struct inv_mpu6050_state *st) |
---|
| 83 | +{ |
---|
| 84 | + unsigned int gyro_skip = 0; |
---|
| 85 | + unsigned int magn_skip = 0; |
---|
| 86 | + unsigned int skip_samples; |
---|
| 87 | + |
---|
| 88 | + /* gyro first sample is out of specs, skip it */ |
---|
| 89 | + if (st->chip_config.gyro_fifo_enable) |
---|
| 90 | + gyro_skip = 1; |
---|
| 91 | + |
---|
| 92 | + /* mag first sample is always not ready, skip it */ |
---|
| 93 | + if (st->chip_config.magn_fifo_enable) |
---|
| 94 | + magn_skip = 1; |
---|
| 95 | + |
---|
| 96 | + /* compute first samples to skip */ |
---|
| 97 | + skip_samples = gyro_skip; |
---|
| 98 | + if (magn_skip > skip_samples) |
---|
| 99 | + skip_samples = magn_skip; |
---|
| 100 | + |
---|
| 101 | + return skip_samples; |
---|
| 102 | +} |
---|
| 103 | + |
---|
| 104 | +int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) |
---|
| 105 | +{ |
---|
| 106 | + uint8_t d; |
---|
| 107 | + int ret; |
---|
| 108 | + |
---|
| 109 | + if (enable) { |
---|
| 110 | + st->it_timestamp = 0; |
---|
| 111 | + /* reset FIFO */ |
---|
| 112 | + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_RST; |
---|
| 113 | + ret = regmap_write(st->map, st->reg->user_ctrl, d); |
---|
| 114 | + if (ret) |
---|
| 115 | + return ret; |
---|
| 116 | + /* enable sensor output to FIFO */ |
---|
| 117 | + d = 0; |
---|
| 118 | + if (st->chip_config.gyro_fifo_enable) |
---|
| 119 | + d |= INV_MPU6050_BITS_GYRO_OUT; |
---|
| 120 | + if (st->chip_config.accl_fifo_enable) |
---|
| 121 | + d |= INV_MPU6050_BIT_ACCEL_OUT; |
---|
| 122 | + if (st->chip_config.temp_fifo_enable) |
---|
| 123 | + d |= INV_MPU6050_BIT_TEMP_OUT; |
---|
| 124 | + if (st->chip_config.magn_fifo_enable) |
---|
| 125 | + d |= INV_MPU6050_BIT_SLAVE_0; |
---|
| 126 | + ret = regmap_write(st->map, st->reg->fifo_en, d); |
---|
| 127 | + if (ret) |
---|
| 128 | + return ret; |
---|
| 129 | + /* enable FIFO reading */ |
---|
| 130 | + d = st->chip_config.user_ctrl | INV_MPU6050_BIT_FIFO_EN; |
---|
| 131 | + ret = regmap_write(st->map, st->reg->user_ctrl, d); |
---|
| 132 | + if (ret) |
---|
| 133 | + return ret; |
---|
| 134 | + /* enable interrupt */ |
---|
| 135 | + ret = regmap_write(st->map, st->reg->int_enable, |
---|
| 136 | + INV_MPU6050_BIT_DATA_RDY_EN); |
---|
| 137 | + } else { |
---|
| 138 | + ret = regmap_write(st->map, st->reg->int_enable, 0); |
---|
| 139 | + if (ret) |
---|
| 140 | + return ret; |
---|
| 141 | + ret = regmap_write(st->map, st->reg->fifo_en, 0); |
---|
| 142 | + if (ret) |
---|
| 143 | + return ret; |
---|
| 144 | + /* restore user_ctrl for disabling FIFO reading */ |
---|
| 145 | + ret = regmap_write(st->map, st->reg->user_ctrl, |
---|
| 146 | + st->chip_config.user_ctrl); |
---|
| 147 | + } |
---|
| 148 | + |
---|
| 149 | + return ret; |
---|
35 | 150 | } |
---|
36 | 151 | |
---|
37 | 152 | /** |
---|
.. | .. |
---|
42 | 157 | static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) |
---|
43 | 158 | { |
---|
44 | 159 | struct inv_mpu6050_state *st = iio_priv(indio_dev); |
---|
| 160 | + struct device *pdev = regmap_get_device(st->map); |
---|
| 161 | + unsigned int scan; |
---|
45 | 162 | int result; |
---|
46 | 163 | |
---|
47 | 164 | if (enable) { |
---|
48 | | - result = inv_mpu6050_set_power_itg(st, true); |
---|
49 | | - if (result) |
---|
| 165 | + scan = inv_scan_query(indio_dev); |
---|
| 166 | + result = pm_runtime_get_sync(pdev); |
---|
| 167 | + if (result < 0) { |
---|
| 168 | + pm_runtime_put_noidle(pdev); |
---|
50 | 169 | return result; |
---|
51 | | - inv_scan_query(indio_dev); |
---|
52 | | - st->skip_samples = 0; |
---|
53 | | - if (st->chip_config.gyro_fifo_enable) { |
---|
54 | | - result = inv_mpu6050_switch_engine(st, true, |
---|
55 | | - INV_MPU6050_BIT_PWR_GYRO_STBY); |
---|
56 | | - if (result) |
---|
57 | | - goto error_power_off; |
---|
58 | | - /* gyro first sample is out of specs, skip it */ |
---|
59 | | - st->skip_samples = 1; |
---|
60 | 170 | } |
---|
61 | | - if (st->chip_config.accl_fifo_enable) { |
---|
62 | | - result = inv_mpu6050_switch_engine(st, true, |
---|
63 | | - INV_MPU6050_BIT_PWR_ACCL_STBY); |
---|
64 | | - if (result) |
---|
65 | | - goto error_gyro_off; |
---|
66 | | - } |
---|
67 | | - result = inv_reset_fifo(indio_dev); |
---|
68 | | - if (result) |
---|
69 | | - goto error_accl_off; |
---|
70 | | - } else { |
---|
71 | | - result = regmap_write(st->map, st->reg->fifo_en, 0); |
---|
72 | | - if (result) |
---|
73 | | - goto error_accl_off; |
---|
74 | | - |
---|
75 | | - result = regmap_write(st->map, st->reg->int_enable, 0); |
---|
76 | | - if (result) |
---|
77 | | - goto error_accl_off; |
---|
78 | | - |
---|
79 | | - result = regmap_write(st->map, st->reg->user_ctrl, |
---|
80 | | - st->chip_config.user_ctrl); |
---|
81 | | - if (result) |
---|
82 | | - goto error_accl_off; |
---|
83 | | - |
---|
84 | | - result = inv_mpu6050_switch_engine(st, false, |
---|
85 | | - INV_MPU6050_BIT_PWR_ACCL_STBY); |
---|
86 | | - if (result) |
---|
87 | | - goto error_accl_off; |
---|
88 | | - |
---|
89 | | - result = inv_mpu6050_switch_engine(st, false, |
---|
90 | | - INV_MPU6050_BIT_PWR_GYRO_STBY); |
---|
91 | | - if (result) |
---|
92 | | - goto error_gyro_off; |
---|
93 | | - |
---|
94 | | - result = inv_mpu6050_set_power_itg(st, false); |
---|
| 171 | + /* |
---|
| 172 | + * In case autosuspend didn't trigger, turn off first not |
---|
| 173 | + * required sensors. |
---|
| 174 | + */ |
---|
| 175 | + result = inv_mpu6050_switch_engine(st, false, ~scan); |
---|
95 | 176 | if (result) |
---|
96 | 177 | goto error_power_off; |
---|
| 178 | + result = inv_mpu6050_switch_engine(st, true, scan); |
---|
| 179 | + if (result) |
---|
| 180 | + goto error_power_off; |
---|
| 181 | + st->skip_samples = inv_compute_skip_samples(st); |
---|
| 182 | + result = inv_mpu6050_prepare_fifo(st, true); |
---|
| 183 | + if (result) |
---|
| 184 | + goto error_power_off; |
---|
| 185 | + } else { |
---|
| 186 | + result = inv_mpu6050_prepare_fifo(st, false); |
---|
| 187 | + if (result) |
---|
| 188 | + goto error_power_off; |
---|
| 189 | + pm_runtime_mark_last_busy(pdev); |
---|
| 190 | + pm_runtime_put_autosuspend(pdev); |
---|
97 | 191 | } |
---|
98 | 192 | |
---|
99 | 193 | return 0; |
---|
100 | 194 | |
---|
101 | | -error_accl_off: |
---|
102 | | - if (st->chip_config.accl_fifo_enable) |
---|
103 | | - inv_mpu6050_switch_engine(st, false, |
---|
104 | | - INV_MPU6050_BIT_PWR_ACCL_STBY); |
---|
105 | | -error_gyro_off: |
---|
106 | | - if (st->chip_config.gyro_fifo_enable) |
---|
107 | | - inv_mpu6050_switch_engine(st, false, |
---|
108 | | - INV_MPU6050_BIT_PWR_GYRO_STBY); |
---|
109 | 195 | error_power_off: |
---|
110 | | - inv_mpu6050_set_power_itg(st, false); |
---|
| 196 | + pm_runtime_put_autosuspend(pdev); |
---|
111 | 197 | return result; |
---|
112 | 198 | } |
---|
113 | 199 | |
---|