1c19ae6beSMike Looijmans // SPDX-License-Identifier: GPL-2.0
2c19ae6beSMike Looijmans /*
3c19ae6beSMike Looijmans * 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
4c19ae6beSMike Looijmans * - BMI088
538f0bd4cSJun Yan * - BMI085
638f0bd4cSJun Yan * - BMI090L
7c19ae6beSMike Looijmans *
8c19ae6beSMike Looijmans * Copyright (c) 2018-2021, Topic Embedded Products
9c19ae6beSMike Looijmans */
10c19ae6beSMike Looijmans
1173314772SLI Qingwu #include <linux/bitfield.h>
12c19ae6beSMike Looijmans #include <linux/delay.h>
13c19ae6beSMike Looijmans #include <linux/iio/iio.h>
14c19ae6beSMike Looijmans #include <linux/iio/sysfs.h>
15c19ae6beSMike Looijmans #include <linux/interrupt.h>
16c19ae6beSMike Looijmans #include <linux/module.h>
17c19ae6beSMike Looijmans #include <linux/pm.h>
18c19ae6beSMike Looijmans #include <linux/pm_runtime.h>
19c19ae6beSMike Looijmans #include <linux/regmap.h>
20c19ae6beSMike Looijmans #include <linux/slab.h>
21*5f60d5f6SAl Viro #include <linux/unaligned.h>
22c19ae6beSMike Looijmans
23c19ae6beSMike Looijmans #include "bmi088-accel.h"
24c19ae6beSMike Looijmans
25c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_CHIP_ID 0x00
26c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ERROR 0x02
27c19ae6beSMike Looijmans
28c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT_STATUS 0x1D
29c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_STATUS_BIT_DRDY BIT(7)
30c19ae6beSMike Looijmans
31c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_RESET 0x7E
32c19ae6beSMike Looijmans #define BMI088_ACCEL_RESET_VAL 0xB6
33c19ae6beSMike Looijmans
34c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_PWR_CTRL 0x7D
35c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_PWR_CONF 0x7C
36c19ae6beSMike Looijmans
37c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT_MAP_DATA 0x58
38c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY BIT(2)
39c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM BIT(5)
40c19ae6beSMike Looijmans
41c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT1_IO_CONF 0x53
42c19ae6beSMike Looijmans #define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT BIT(3)
43c19ae6beSMike Looijmans #define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL BIT(1)
44c19ae6beSMike Looijmans
45c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT2_IO_CONF 0x54
46c19ae6beSMike Looijmans #define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT BIT(3)
47c19ae6beSMike Looijmans #define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL BIT(1)
48c19ae6beSMike Looijmans
49c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ACC_CONF 0x40
50c19ae6beSMike Looijmans #define BMI088_ACCEL_MODE_ODR_MASK 0x0f
51c19ae6beSMike Looijmans
52c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ACC_RANGE 0x41
53c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_3G 0x00
54c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_6G 0x01
55c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_12G 0x02
56c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_24G 0x03
57c19ae6beSMike Looijmans
58c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_TEMP 0x22
59c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_TEMP_SHIFT 5
60c19ae6beSMike Looijmans #define BMI088_ACCEL_TEMP_UNIT 125
61c19ae6beSMike Looijmans #define BMI088_ACCEL_TEMP_OFFSET 23000
62c19ae6beSMike Looijmans
63c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_XOUT_L 0x12
64c19ae6beSMike Looijmans #define BMI088_ACCEL_AXIS_TO_REG(axis) \
65c19ae6beSMike Looijmans (BMI088_ACCEL_REG_XOUT_L + (axis * 2))
66c19ae6beSMike Looijmans
67c19ae6beSMike Looijmans #define BMI088_ACCEL_MAX_STARTUP_TIME_US 1000
68c19ae6beSMike Looijmans #define BMI088_AUTO_SUSPEND_DELAY_MS 2000
69c19ae6beSMike Looijmans
70c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_STATUS 0x0E
71c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_CONFIG0 0x48
72c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_CONFIG1 0x49
73c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_DATA 0x3F
74c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_LENGTH 100
75c19ae6beSMike Looijmans
76c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_MODE_FIFO 0x40
77c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_MODE_STREAM 0x80
78c19ae6beSMike Looijmans
7973314772SLI Qingwu #define BMIO088_ACCEL_ACC_RANGE_MSK GENMASK(1, 0)
8073314772SLI Qingwu
81c19ae6beSMike Looijmans enum bmi088_accel_axis {
82c19ae6beSMike Looijmans AXIS_X,
83c19ae6beSMike Looijmans AXIS_Y,
84c19ae6beSMike Looijmans AXIS_Z,
85c19ae6beSMike Looijmans };
86c19ae6beSMike Looijmans
87c19ae6beSMike Looijmans static const int bmi088_sample_freqs[] = {
88c19ae6beSMike Looijmans 12, 500000,
89c19ae6beSMike Looijmans 25, 0,
90c19ae6beSMike Looijmans 50, 0,
91c19ae6beSMike Looijmans 100, 0,
92c19ae6beSMike Looijmans 200, 0,
93c19ae6beSMike Looijmans 400, 0,
94c19ae6beSMike Looijmans 800, 0,
95c19ae6beSMike Looijmans 1600, 0,
96c19ae6beSMike Looijmans };
97c19ae6beSMike Looijmans
98c19ae6beSMike Looijmans /* Available OSR (over sampling rate) sets the 3dB cut-off frequency */
99c19ae6beSMike Looijmans enum bmi088_osr_modes {
100c19ae6beSMike Looijmans BMI088_ACCEL_MODE_OSR_NORMAL = 0xA,
101c19ae6beSMike Looijmans BMI088_ACCEL_MODE_OSR_2 = 0x9,
102c19ae6beSMike Looijmans BMI088_ACCEL_MODE_OSR_4 = 0x8,
103c19ae6beSMike Looijmans };
104c19ae6beSMike Looijmans
105c19ae6beSMike Looijmans /* Available ODR (output data rates) in Hz */
106c19ae6beSMike Looijmans enum bmi088_odr_modes {
107c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_12_5 = 0x5,
108c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_25 = 0x6,
109c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_50 = 0x7,
110c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_100 = 0x8,
111c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_200 = 0x9,
112c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_400 = 0xa,
113c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_800 = 0xb,
114c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_1600 = 0xc,
115c19ae6beSMike Looijmans };
116c19ae6beSMike Looijmans
117c19ae6beSMike Looijmans struct bmi088_accel_chip_info {
118c19ae6beSMike Looijmans const char *name;
119c19ae6beSMike Looijmans u8 chip_id;
120c19ae6beSMike Looijmans const struct iio_chan_spec *channels;
121c19ae6beSMike Looijmans int num_channels;
12273314772SLI Qingwu const int scale_table[4][2];
123c19ae6beSMike Looijmans };
124c19ae6beSMike Looijmans
125c19ae6beSMike Looijmans struct bmi088_accel_data {
126c19ae6beSMike Looijmans struct regmap *regmap;
127c19ae6beSMike Looijmans const struct bmi088_accel_chip_info *chip_info;
128a794b340SJonathan Cameron u8 buffer[2] __aligned(IIO_DMA_MINALIGN); /* shared DMA safe buffer */
129c19ae6beSMike Looijmans };
130c19ae6beSMike Looijmans
131c19ae6beSMike Looijmans static const struct regmap_range bmi088_volatile_ranges[] = {
132c19ae6beSMike Looijmans /* All registers below 0x40 are volatile, except the CHIP ID. */
133c19ae6beSMike Looijmans regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f),
134c19ae6beSMike Looijmans /* Mark the RESET as volatile too, it is self-clearing */
135c19ae6beSMike Looijmans regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET),
136c19ae6beSMike Looijmans };
137c19ae6beSMike Looijmans
138c19ae6beSMike Looijmans static const struct regmap_access_table bmi088_volatile_table = {
139c19ae6beSMike Looijmans .yes_ranges = bmi088_volatile_ranges,
140c19ae6beSMike Looijmans .n_yes_ranges = ARRAY_SIZE(bmi088_volatile_ranges),
141c19ae6beSMike Looijmans };
142c19ae6beSMike Looijmans
143c19ae6beSMike Looijmans const struct regmap_config bmi088_regmap_conf = {
144c19ae6beSMike Looijmans .reg_bits = 8,
145c19ae6beSMike Looijmans .val_bits = 8,
146c19ae6beSMike Looijmans .max_register = 0x7E,
147c19ae6beSMike Looijmans .volatile_table = &bmi088_volatile_table,
148c19ae6beSMike Looijmans .cache_type = REGCACHE_RBTREE,
149c19ae6beSMike Looijmans };
1503bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088);
151c19ae6beSMike Looijmans
bmi088_accel_power_up(struct bmi088_accel_data * data)152c19ae6beSMike Looijmans static int bmi088_accel_power_up(struct bmi088_accel_data *data)
153c19ae6beSMike Looijmans {
154c19ae6beSMike Looijmans int ret;
155c19ae6beSMike Looijmans
156c19ae6beSMike Looijmans /* Enable accelerometer and temperature sensor */
157c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4);
158c19ae6beSMike Looijmans if (ret)
159c19ae6beSMike Looijmans return ret;
160c19ae6beSMike Looijmans
161c19ae6beSMike Looijmans /* Datasheet recommends to wait at least 5ms before communication */
162c19ae6beSMike Looijmans usleep_range(5000, 6000);
163c19ae6beSMike Looijmans
164c19ae6beSMike Looijmans /* Disable suspend mode */
165c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0);
166c19ae6beSMike Looijmans if (ret)
167c19ae6beSMike Looijmans return ret;
168c19ae6beSMike Looijmans
169c19ae6beSMike Looijmans /* Recommended at least 1ms before further communication */
170c19ae6beSMike Looijmans usleep_range(1000, 1200);
171c19ae6beSMike Looijmans
172c19ae6beSMike Looijmans return 0;
173c19ae6beSMike Looijmans }
174c19ae6beSMike Looijmans
bmi088_accel_power_down(struct bmi088_accel_data * data)175c19ae6beSMike Looijmans static int bmi088_accel_power_down(struct bmi088_accel_data *data)
176c19ae6beSMike Looijmans {
177c19ae6beSMike Looijmans int ret;
178c19ae6beSMike Looijmans
179c19ae6beSMike Looijmans /* Enable suspend mode */
180c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3);
181c19ae6beSMike Looijmans if (ret)
182c19ae6beSMike Looijmans return ret;
183c19ae6beSMike Looijmans
184c19ae6beSMike Looijmans /* Recommended at least 1ms before further communication */
185c19ae6beSMike Looijmans usleep_range(1000, 1200);
186c19ae6beSMike Looijmans
187c19ae6beSMike Looijmans /* Disable accelerometer and temperature sensor */
188c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0);
189c19ae6beSMike Looijmans if (ret)
190c19ae6beSMike Looijmans return ret;
191c19ae6beSMike Looijmans
192c19ae6beSMike Looijmans /* Datasheet recommends to wait at least 5ms before communication */
193c19ae6beSMike Looijmans usleep_range(5000, 6000);
194c19ae6beSMike Looijmans
195c19ae6beSMike Looijmans return 0;
196c19ae6beSMike Looijmans }
197c19ae6beSMike Looijmans
bmi088_accel_get_sample_freq(struct bmi088_accel_data * data,int * val,int * val2)198c19ae6beSMike Looijmans static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data,
199c19ae6beSMike Looijmans int *val, int *val2)
200c19ae6beSMike Looijmans {
201c19ae6beSMike Looijmans unsigned int value;
202c19ae6beSMike Looijmans int ret;
203c19ae6beSMike Looijmans
204c19ae6beSMike Looijmans ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF,
205c19ae6beSMike Looijmans &value);
206c19ae6beSMike Looijmans if (ret)
207c19ae6beSMike Looijmans return ret;
208c19ae6beSMike Looijmans
209c19ae6beSMike Looijmans value &= BMI088_ACCEL_MODE_ODR_MASK;
210c19ae6beSMike Looijmans value -= BMI088_ACCEL_MODE_ODR_12_5;
211c19ae6beSMike Looijmans value <<= 1;
212c19ae6beSMike Looijmans
213c19ae6beSMike Looijmans if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1)
214c19ae6beSMike Looijmans return -EINVAL;
215c19ae6beSMike Looijmans
216c19ae6beSMike Looijmans *val = bmi088_sample_freqs[value];
217c19ae6beSMike Looijmans *val2 = bmi088_sample_freqs[value + 1];
218c19ae6beSMike Looijmans
219c19ae6beSMike Looijmans return IIO_VAL_INT_PLUS_MICRO;
220c19ae6beSMike Looijmans }
221c19ae6beSMike Looijmans
bmi088_accel_set_sample_freq(struct bmi088_accel_data * data,int val)222c19ae6beSMike Looijmans static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val)
223c19ae6beSMike Looijmans {
224c19ae6beSMike Looijmans unsigned int regval;
225c19ae6beSMike Looijmans int index = 0;
226c19ae6beSMike Looijmans
227c19ae6beSMike Looijmans while (index < ARRAY_SIZE(bmi088_sample_freqs) &&
228c19ae6beSMike Looijmans bmi088_sample_freqs[index] != val)
229c19ae6beSMike Looijmans index += 2;
230c19ae6beSMike Looijmans
231c19ae6beSMike Looijmans if (index >= ARRAY_SIZE(bmi088_sample_freqs))
232c19ae6beSMike Looijmans return -EINVAL;
233c19ae6beSMike Looijmans
234c19ae6beSMike Looijmans regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5;
235c19ae6beSMike Looijmans
236c19ae6beSMike Looijmans return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF,
237c19ae6beSMike Looijmans BMI088_ACCEL_MODE_ODR_MASK, regval);
238c19ae6beSMike Looijmans }
239c19ae6beSMike Looijmans
bmi088_accel_set_scale(struct bmi088_accel_data * data,int val,int val2)24048d07b3bSLI Qingwu static int bmi088_accel_set_scale(struct bmi088_accel_data *data, int val, int val2)
24148d07b3bSLI Qingwu {
24248d07b3bSLI Qingwu unsigned int i;
24348d07b3bSLI Qingwu
24448d07b3bSLI Qingwu for (i = 0; i < 4; i++)
24548d07b3bSLI Qingwu if (val == data->chip_info->scale_table[i][0] &&
24648d07b3bSLI Qingwu val2 == data->chip_info->scale_table[i][1])
24748d07b3bSLI Qingwu break;
24848d07b3bSLI Qingwu
24948d07b3bSLI Qingwu if (i == 4)
25048d07b3bSLI Qingwu return -EINVAL;
25148d07b3bSLI Qingwu
25248d07b3bSLI Qingwu return regmap_write(data->regmap, BMI088_ACCEL_REG_ACC_RANGE, i);
25348d07b3bSLI Qingwu }
25448d07b3bSLI Qingwu
bmi088_accel_get_temp(struct bmi088_accel_data * data,int * val)255c19ae6beSMike Looijmans static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val)
256c19ae6beSMike Looijmans {
257c19ae6beSMike Looijmans int ret;
258c19ae6beSMike Looijmans s16 temp;
259c19ae6beSMike Looijmans
260c19ae6beSMike Looijmans ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP,
261c19ae6beSMike Looijmans &data->buffer, sizeof(__be16));
262c19ae6beSMike Looijmans if (ret)
263c19ae6beSMike Looijmans return ret;
264c19ae6beSMike Looijmans
265c19ae6beSMike Looijmans /* data->buffer is cacheline aligned */
266c19ae6beSMike Looijmans temp = be16_to_cpu(*(__be16 *)data->buffer);
267c19ae6beSMike Looijmans
268c19ae6beSMike Looijmans *val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT;
269c19ae6beSMike Looijmans
270c19ae6beSMike Looijmans return IIO_VAL_INT;
271c19ae6beSMike Looijmans }
272c19ae6beSMike Looijmans
bmi088_accel_get_axis(struct bmi088_accel_data * data,struct iio_chan_spec const * chan,int * val)273c19ae6beSMike Looijmans static int bmi088_accel_get_axis(struct bmi088_accel_data *data,
274c19ae6beSMike Looijmans struct iio_chan_spec const *chan,
275c19ae6beSMike Looijmans int *val)
276c19ae6beSMike Looijmans {
277c19ae6beSMike Looijmans int ret;
278c19ae6beSMike Looijmans s16 raw_val;
279c19ae6beSMike Looijmans
280c19ae6beSMike Looijmans ret = regmap_bulk_read(data->regmap,
281c19ae6beSMike Looijmans BMI088_ACCEL_AXIS_TO_REG(chan->scan_index),
282c19ae6beSMike Looijmans data->buffer, sizeof(__le16));
283c19ae6beSMike Looijmans if (ret)
284c19ae6beSMike Looijmans return ret;
285c19ae6beSMike Looijmans
286c19ae6beSMike Looijmans raw_val = le16_to_cpu(*(__le16 *)data->buffer);
287c19ae6beSMike Looijmans *val = raw_val;
288c19ae6beSMike Looijmans
289c19ae6beSMike Looijmans return IIO_VAL_INT;
290c19ae6beSMike Looijmans }
291c19ae6beSMike Looijmans
bmi088_accel_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)292c19ae6beSMike Looijmans static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
293c19ae6beSMike Looijmans struct iio_chan_spec const *chan,
294c19ae6beSMike Looijmans int *val, int *val2, long mask)
295c19ae6beSMike Looijmans {
296c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev);
297c19ae6beSMike Looijmans struct device *dev = regmap_get_device(data->regmap);
298c19ae6beSMike Looijmans int ret;
29973314772SLI Qingwu int reg;
300c19ae6beSMike Looijmans
301c19ae6beSMike Looijmans switch (mask) {
302c19ae6beSMike Looijmans case IIO_CHAN_INFO_RAW:
303c19ae6beSMike Looijmans switch (chan->type) {
304c19ae6beSMike Looijmans case IIO_TEMP:
3059a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev);
3069a20795cSJonathan Cameron if (ret)
3079a20795cSJonathan Cameron return ret;
3089a20795cSJonathan Cameron
309c19ae6beSMike Looijmans ret = bmi088_accel_get_temp(data, val);
310c19ae6beSMike Looijmans goto out_read_raw_pm_put;
311c19ae6beSMike Looijmans case IIO_ACCEL:
3129a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev);
3139a20795cSJonathan Cameron if (ret)
3149a20795cSJonathan Cameron return ret;
3159a20795cSJonathan Cameron
316c19ae6beSMike Looijmans ret = iio_device_claim_direct_mode(indio_dev);
317c19ae6beSMike Looijmans if (ret)
318c19ae6beSMike Looijmans goto out_read_raw_pm_put;
319c19ae6beSMike Looijmans
320c19ae6beSMike Looijmans ret = bmi088_accel_get_axis(data, chan, val);
321c19ae6beSMike Looijmans iio_device_release_direct_mode(indio_dev);
322c19ae6beSMike Looijmans if (!ret)
323c19ae6beSMike Looijmans ret = IIO_VAL_INT;
324c19ae6beSMike Looijmans
325c19ae6beSMike Looijmans goto out_read_raw_pm_put;
326c19ae6beSMike Looijmans default:
327c19ae6beSMike Looijmans return -EINVAL;
328c19ae6beSMike Looijmans }
329c19ae6beSMike Looijmans case IIO_CHAN_INFO_OFFSET:
330c19ae6beSMike Looijmans switch (chan->type) {
331c19ae6beSMike Looijmans case IIO_TEMP:
332c19ae6beSMike Looijmans /* Offset applies before scale */
333c19ae6beSMike Looijmans *val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT;
334c19ae6beSMike Looijmans return IIO_VAL_INT;
335c19ae6beSMike Looijmans default:
336c19ae6beSMike Looijmans return -EINVAL;
337c19ae6beSMike Looijmans }
338c19ae6beSMike Looijmans case IIO_CHAN_INFO_SCALE:
339c19ae6beSMike Looijmans switch (chan->type) {
340c19ae6beSMike Looijmans case IIO_TEMP:
341c19ae6beSMike Looijmans /* 0.125 degrees per LSB */
342c19ae6beSMike Looijmans *val = BMI088_ACCEL_TEMP_UNIT;
343c19ae6beSMike Looijmans return IIO_VAL_INT;
344c19ae6beSMike Looijmans case IIO_ACCEL:
3459a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev);
3469a20795cSJonathan Cameron if (ret)
3479a20795cSJonathan Cameron return ret;
3489a20795cSJonathan Cameron
349c19ae6beSMike Looijmans ret = regmap_read(data->regmap,
35073314772SLI Qingwu BMI088_ACCEL_REG_ACC_RANGE, ®);
351c19ae6beSMike Looijmans if (ret)
352c19ae6beSMike Looijmans goto out_read_raw_pm_put;
353c19ae6beSMike Looijmans
35473314772SLI Qingwu reg = FIELD_GET(BMIO088_ACCEL_ACC_RANGE_MSK, reg);
35573314772SLI Qingwu *val = data->chip_info->scale_table[reg][0];
35673314772SLI Qingwu *val2 = data->chip_info->scale_table[reg][1];
35773314772SLI Qingwu ret = IIO_VAL_INT_PLUS_MICRO;
358c19ae6beSMike Looijmans
359c19ae6beSMike Looijmans goto out_read_raw_pm_put;
360c19ae6beSMike Looijmans default:
361c19ae6beSMike Looijmans return -EINVAL;
362c19ae6beSMike Looijmans }
363c19ae6beSMike Looijmans case IIO_CHAN_INFO_SAMP_FREQ:
3649a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev);
3659a20795cSJonathan Cameron if (ret)
3669a20795cSJonathan Cameron return ret;
3679a20795cSJonathan Cameron
368c19ae6beSMike Looijmans ret = bmi088_accel_get_sample_freq(data, val, val2);
369c19ae6beSMike Looijmans goto out_read_raw_pm_put;
370c19ae6beSMike Looijmans default:
371c19ae6beSMike Looijmans break;
372c19ae6beSMike Looijmans }
373c19ae6beSMike Looijmans
374c19ae6beSMike Looijmans return -EINVAL;
375c19ae6beSMike Looijmans
376c19ae6beSMike Looijmans out_read_raw_pm_put:
377c19ae6beSMike Looijmans pm_runtime_mark_last_busy(dev);
378c19ae6beSMike Looijmans pm_runtime_put_autosuspend(dev);
379c19ae6beSMike Looijmans
380c19ae6beSMike Looijmans return ret;
381c19ae6beSMike Looijmans }
382c19ae6beSMike Looijmans
bmi088_accel_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)383c19ae6beSMike Looijmans static int bmi088_accel_read_avail(struct iio_dev *indio_dev,
384c19ae6beSMike Looijmans struct iio_chan_spec const *chan,
385c19ae6beSMike Looijmans const int **vals, int *type, int *length,
386c19ae6beSMike Looijmans long mask)
387c19ae6beSMike Looijmans {
38848d07b3bSLI Qingwu struct bmi088_accel_data *data = iio_priv(indio_dev);
38967ac266dSLI Qingwu
390c19ae6beSMike Looijmans switch (mask) {
39148d07b3bSLI Qingwu case IIO_CHAN_INFO_SCALE:
39248d07b3bSLI Qingwu *vals = (const int *)data->chip_info->scale_table;
39348d07b3bSLI Qingwu *length = 8;
39448d07b3bSLI Qingwu *type = IIO_VAL_INT_PLUS_MICRO;
39548d07b3bSLI Qingwu return IIO_AVAIL_LIST;
396c19ae6beSMike Looijmans case IIO_CHAN_INFO_SAMP_FREQ:
397c19ae6beSMike Looijmans *type = IIO_VAL_INT_PLUS_MICRO;
398c19ae6beSMike Looijmans *vals = bmi088_sample_freqs;
399c19ae6beSMike Looijmans *length = ARRAY_SIZE(bmi088_sample_freqs);
400c19ae6beSMike Looijmans return IIO_AVAIL_LIST;
401c19ae6beSMike Looijmans default:
402c19ae6beSMike Looijmans return -EINVAL;
403c19ae6beSMike Looijmans }
404c19ae6beSMike Looijmans }
405c19ae6beSMike Looijmans
bmi088_accel_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)406c19ae6beSMike Looijmans static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
407c19ae6beSMike Looijmans struct iio_chan_spec const *chan,
408c19ae6beSMike Looijmans int val, int val2, long mask)
409c19ae6beSMike Looijmans {
410c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev);
411c19ae6beSMike Looijmans struct device *dev = regmap_get_device(data->regmap);
412c19ae6beSMike Looijmans int ret;
413c19ae6beSMike Looijmans
414c19ae6beSMike Looijmans switch (mask) {
41548d07b3bSLI Qingwu case IIO_CHAN_INFO_SCALE:
41648d07b3bSLI Qingwu ret = pm_runtime_resume_and_get(dev);
41748d07b3bSLI Qingwu if (ret)
41848d07b3bSLI Qingwu return ret;
41948d07b3bSLI Qingwu
42048d07b3bSLI Qingwu ret = bmi088_accel_set_scale(data, val, val2);
42148d07b3bSLI Qingwu pm_runtime_mark_last_busy(dev);
42248d07b3bSLI Qingwu pm_runtime_put_autosuspend(dev);
42348d07b3bSLI Qingwu return ret;
424c19ae6beSMike Looijmans case IIO_CHAN_INFO_SAMP_FREQ:
4259a20795cSJonathan Cameron ret = pm_runtime_resume_and_get(dev);
4269a20795cSJonathan Cameron if (ret)
4279a20795cSJonathan Cameron return ret;
4289a20795cSJonathan Cameron
429c19ae6beSMike Looijmans ret = bmi088_accel_set_sample_freq(data, val);
430c19ae6beSMike Looijmans pm_runtime_mark_last_busy(dev);
431c19ae6beSMike Looijmans pm_runtime_put_autosuspend(dev);
432c19ae6beSMike Looijmans return ret;
433c19ae6beSMike Looijmans default:
434c19ae6beSMike Looijmans return -EINVAL;
435c19ae6beSMike Looijmans }
436c19ae6beSMike Looijmans }
437c19ae6beSMike Looijmans
438c19ae6beSMike Looijmans #define BMI088_ACCEL_CHANNEL(_axis) { \
439c19ae6beSMike Looijmans .type = IIO_ACCEL, \
440c19ae6beSMike Looijmans .modified = 1, \
441c19ae6beSMike Looijmans .channel2 = IIO_MOD_##_axis, \
442c19ae6beSMike Looijmans .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
443c19ae6beSMike Looijmans .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
444c19ae6beSMike Looijmans BIT(IIO_CHAN_INFO_SAMP_FREQ), \
44548d07b3bSLI Qingwu .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
44648d07b3bSLI Qingwu BIT(IIO_CHAN_INFO_SCALE), \
447c19ae6beSMike Looijmans .scan_index = AXIS_##_axis, \
448c19ae6beSMike Looijmans }
449c19ae6beSMike Looijmans
450c19ae6beSMike Looijmans static const struct iio_chan_spec bmi088_accel_channels[] = {
451c19ae6beSMike Looijmans {
452c19ae6beSMike Looijmans .type = IIO_TEMP,
453c19ae6beSMike Looijmans .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
454c19ae6beSMike Looijmans BIT(IIO_CHAN_INFO_SCALE) |
455c19ae6beSMike Looijmans BIT(IIO_CHAN_INFO_OFFSET),
456c19ae6beSMike Looijmans .scan_index = -1,
457c19ae6beSMike Looijmans },
458c19ae6beSMike Looijmans BMI088_ACCEL_CHANNEL(X),
459c19ae6beSMike Looijmans BMI088_ACCEL_CHANNEL(Y),
460c19ae6beSMike Looijmans BMI088_ACCEL_CHANNEL(Z),
461c19ae6beSMike Looijmans IIO_CHAN_SOFT_TIMESTAMP(3),
462c19ae6beSMike Looijmans };
463c19ae6beSMike Looijmans
464c19ae6beSMike Looijmans static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = {
4657a61456cSLI Qingwu [BOSCH_BMI085] = {
4667a61456cSLI Qingwu .name = "bmi085-accel",
4677a61456cSLI Qingwu .chip_id = 0x1F,
4687a61456cSLI Qingwu .channels = bmi088_accel_channels,
4697a61456cSLI Qingwu .num_channels = ARRAY_SIZE(bmi088_accel_channels),
4707a61456cSLI Qingwu .scale_table = {{0, 598}, {0, 1196}, {0, 2393}, {0, 4785}},
4717a61456cSLI Qingwu },
47267ac266dSLI Qingwu [BOSCH_BMI088] = {
47367ac266dSLI Qingwu .name = "bmi088-accel",
474c19ae6beSMike Looijmans .chip_id = 0x1E,
475c19ae6beSMike Looijmans .channels = bmi088_accel_channels,
476c19ae6beSMike Looijmans .num_channels = ARRAY_SIZE(bmi088_accel_channels),
47773314772SLI Qingwu .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}},
478c19ae6beSMike Looijmans },
47957387d3cSLI Qingwu [BOSCH_BMI090L] = {
48057387d3cSLI Qingwu .name = "bmi090l-accel",
48157387d3cSLI Qingwu .chip_id = 0x1A,
48257387d3cSLI Qingwu .channels = bmi088_accel_channels,
48357387d3cSLI Qingwu .num_channels = ARRAY_SIZE(bmi088_accel_channels),
48457387d3cSLI Qingwu .scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}},
48557387d3cSLI Qingwu },
486c19ae6beSMike Looijmans };
487c19ae6beSMike Looijmans
488c19ae6beSMike Looijmans static const struct iio_info bmi088_accel_info = {
489c19ae6beSMike Looijmans .read_raw = bmi088_accel_read_raw,
490c19ae6beSMike Looijmans .write_raw = bmi088_accel_write_raw,
491c19ae6beSMike Looijmans .read_avail = bmi088_accel_read_avail,
492c19ae6beSMike Looijmans };
493c19ae6beSMike Looijmans
494c19ae6beSMike Looijmans static const unsigned long bmi088_accel_scan_masks[] = {
495c19ae6beSMike Looijmans BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
496c19ae6beSMike Looijmans 0
497c19ae6beSMike Looijmans };
498c19ae6beSMike Looijmans
bmi088_accel_chip_init(struct bmi088_accel_data * data,enum bmi_device_type type)49967ac266dSLI Qingwu static int bmi088_accel_chip_init(struct bmi088_accel_data *data, enum bmi_device_type type)
500c19ae6beSMike Looijmans {
501c19ae6beSMike Looijmans struct device *dev = regmap_get_device(data->regmap);
502c19ae6beSMike Looijmans int ret, i;
503c19ae6beSMike Looijmans unsigned int val;
504c19ae6beSMike Looijmans
50567ac266dSLI Qingwu if (type >= BOSCH_UNKNOWN)
50667ac266dSLI Qingwu return -ENODEV;
50767ac266dSLI Qingwu
508c19ae6beSMike Looijmans /* Do a dummy read to enable SPI interface, won't harm I2C */
509c19ae6beSMike Looijmans regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
510c19ae6beSMike Looijmans
511c19ae6beSMike Looijmans /*
512c19ae6beSMike Looijmans * Reset chip to get it in a known good state. A delay of 1ms after
513c19ae6beSMike Looijmans * reset is required according to the data sheet
514c19ae6beSMike Looijmans */
515c19ae6beSMike Looijmans ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET,
516c19ae6beSMike Looijmans BMI088_ACCEL_RESET_VAL);
517c19ae6beSMike Looijmans if (ret)
518c19ae6beSMike Looijmans return ret;
519c19ae6beSMike Looijmans
520c19ae6beSMike Looijmans usleep_range(1000, 2000);
521c19ae6beSMike Looijmans
522c19ae6beSMike Looijmans /* Do a dummy read again after a reset to enable the SPI interface */
523c19ae6beSMike Looijmans regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
524c19ae6beSMike Looijmans
525c19ae6beSMike Looijmans /* Read chip ID */
526c19ae6beSMike Looijmans ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val);
527c19ae6beSMike Looijmans if (ret) {
528c19ae6beSMike Looijmans dev_err(dev, "Error: Reading chip id\n");
529c19ae6beSMike Looijmans return ret;
530c19ae6beSMike Looijmans }
531c19ae6beSMike Looijmans
532c19ae6beSMike Looijmans /* Validate chip ID */
53367ac266dSLI Qingwu for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++)
53467ac266dSLI Qingwu if (bmi088_accel_chip_info_tbl[i].chip_id == val)
535c19ae6beSMike Looijmans break;
53667ac266dSLI Qingwu
53767ac266dSLI Qingwu if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl))
53867ac266dSLI Qingwu data->chip_info = &bmi088_accel_chip_info_tbl[type];
53967ac266dSLI Qingwu else
54067ac266dSLI Qingwu data->chip_info = &bmi088_accel_chip_info_tbl[i];
54167ac266dSLI Qingwu
54267ac266dSLI Qingwu if (i != type)
54367ac266dSLI Qingwu dev_warn(dev, "unexpected chip id 0x%X\n", val);
544c19ae6beSMike Looijmans
545c19ae6beSMike Looijmans return 0;
546c19ae6beSMike Looijmans }
547c19ae6beSMike Looijmans
bmi088_accel_core_probe(struct device * dev,struct regmap * regmap,int irq,enum bmi_device_type type)548c19ae6beSMike Looijmans int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
54967ac266dSLI Qingwu int irq, enum bmi_device_type type)
550c19ae6beSMike Looijmans {
551c19ae6beSMike Looijmans struct bmi088_accel_data *data;
552c19ae6beSMike Looijmans struct iio_dev *indio_dev;
553c19ae6beSMike Looijmans int ret;
554c19ae6beSMike Looijmans
555c19ae6beSMike Looijmans indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
556c19ae6beSMike Looijmans if (!indio_dev)
557c19ae6beSMike Looijmans return -ENOMEM;
558c19ae6beSMike Looijmans
559c19ae6beSMike Looijmans data = iio_priv(indio_dev);
560c19ae6beSMike Looijmans dev_set_drvdata(dev, indio_dev);
561c19ae6beSMike Looijmans
562c19ae6beSMike Looijmans data->regmap = regmap;
563c19ae6beSMike Looijmans
56467ac266dSLI Qingwu ret = bmi088_accel_chip_init(data, type);
565c19ae6beSMike Looijmans if (ret)
566c19ae6beSMike Looijmans return ret;
567c19ae6beSMike Looijmans
568c19ae6beSMike Looijmans indio_dev->channels = data->chip_info->channels;
569c19ae6beSMike Looijmans indio_dev->num_channels = data->chip_info->num_channels;
57067ac266dSLI Qingwu indio_dev->name = data->chip_info->name;
571c19ae6beSMike Looijmans indio_dev->available_scan_masks = bmi088_accel_scan_masks;
572c19ae6beSMike Looijmans indio_dev->modes = INDIO_DIRECT_MODE;
573c19ae6beSMike Looijmans indio_dev->info = &bmi088_accel_info;
574c19ae6beSMike Looijmans
575c19ae6beSMike Looijmans /* Enable runtime PM */
576c19ae6beSMike Looijmans pm_runtime_get_noresume(dev);
577c19ae6beSMike Looijmans pm_runtime_set_suspended(dev);
578c19ae6beSMike Looijmans pm_runtime_enable(dev);
579c19ae6beSMike Looijmans /* We need ~6ms to startup, so set the delay to 6 seconds */
580c19ae6beSMike Looijmans pm_runtime_set_autosuspend_delay(dev, 6000);
581c19ae6beSMike Looijmans pm_runtime_use_autosuspend(dev);
582c19ae6beSMike Looijmans pm_runtime_put(dev);
583c19ae6beSMike Looijmans
584c19ae6beSMike Looijmans ret = iio_device_register(indio_dev);
585c19ae6beSMike Looijmans if (ret)
586c19ae6beSMike Looijmans dev_err(dev, "Unable to register iio device\n");
587c19ae6beSMike Looijmans
588c19ae6beSMike Looijmans return ret;
589c19ae6beSMike Looijmans }
5903bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088);
591c19ae6beSMike Looijmans
592c19ae6beSMike Looijmans
bmi088_accel_core_remove(struct device * dev)593bcf9d61aSUwe Kleine-König void bmi088_accel_core_remove(struct device *dev)
594c19ae6beSMike Looijmans {
595c19ae6beSMike Looijmans struct iio_dev *indio_dev = dev_get_drvdata(dev);
596c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev);
597c19ae6beSMike Looijmans
598c19ae6beSMike Looijmans iio_device_unregister(indio_dev);
599c19ae6beSMike Looijmans
600c19ae6beSMike Looijmans pm_runtime_disable(dev);
601c19ae6beSMike Looijmans pm_runtime_set_suspended(dev);
602c19ae6beSMike Looijmans bmi088_accel_power_down(data);
603c19ae6beSMike Looijmans }
6043bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088);
605c19ae6beSMike Looijmans
bmi088_accel_runtime_suspend(struct device * dev)60666991b10SJonathan Cameron static int bmi088_accel_runtime_suspend(struct device *dev)
607c19ae6beSMike Looijmans {
608c19ae6beSMike Looijmans struct iio_dev *indio_dev = dev_get_drvdata(dev);
609c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev);
610c19ae6beSMike Looijmans
611c19ae6beSMike Looijmans return bmi088_accel_power_down(data);
612c19ae6beSMike Looijmans }
613c19ae6beSMike Looijmans
bmi088_accel_runtime_resume(struct device * dev)61466991b10SJonathan Cameron static int bmi088_accel_runtime_resume(struct device *dev)
615c19ae6beSMike Looijmans {
616c19ae6beSMike Looijmans struct iio_dev *indio_dev = dev_get_drvdata(dev);
617c19ae6beSMike Looijmans struct bmi088_accel_data *data = iio_priv(indio_dev);
618c19ae6beSMike Looijmans
619c19ae6beSMike Looijmans return bmi088_accel_power_up(data);
620c19ae6beSMike Looijmans }
621c19ae6beSMike Looijmans
62266991b10SJonathan Cameron EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS(bmi088_accel_pm_ops,
62366991b10SJonathan Cameron bmi088_accel_runtime_suspend,
62466991b10SJonathan Cameron bmi088_accel_runtime_resume, NULL,
62566991b10SJonathan Cameron IIO_BMI088);
626c19ae6beSMike Looijmans
627c19ae6beSMike Looijmans MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>");
628c19ae6beSMike Looijmans MODULE_LICENSE("GPL v2");
629c19ae6beSMike Looijmans MODULE_DESCRIPTION("BMI088 accelerometer driver (core)");
630