xref: /linux/drivers/iio/accel/bmi088-accel-core.c (revision 57387d3c09c2a56fd6f53f2b14662a9ad6a93fed)
1c19ae6beSMike Looijmans // SPDX-License-Identifier: GPL-2.0
2c19ae6beSMike Looijmans /*
3c19ae6beSMike Looijmans  * 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
4c19ae6beSMike Looijmans  *  - BMI088
5c19ae6beSMike Looijmans  *
6c19ae6beSMike Looijmans  * Copyright (c) 2018-2021, Topic Embedded Products
7c19ae6beSMike Looijmans  */
8c19ae6beSMike Looijmans 
973314772SLI Qingwu #include <linux/bitfield.h>
10c19ae6beSMike Looijmans #include <linux/delay.h>
11c19ae6beSMike Looijmans #include <linux/iio/iio.h>
12c19ae6beSMike Looijmans #include <linux/iio/sysfs.h>
13c19ae6beSMike Looijmans #include <linux/interrupt.h>
14c19ae6beSMike Looijmans #include <linux/module.h>
15c19ae6beSMike Looijmans #include <linux/pm.h>
16c19ae6beSMike Looijmans #include <linux/pm_runtime.h>
17c19ae6beSMike Looijmans #include <linux/regmap.h>
18c19ae6beSMike Looijmans #include <linux/slab.h>
19c19ae6beSMike Looijmans #include <asm/unaligned.h>
20c19ae6beSMike Looijmans 
21c19ae6beSMike Looijmans #include "bmi088-accel.h"
22c19ae6beSMike Looijmans 
23c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_CHIP_ID			0x00
24c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ERROR				0x02
25c19ae6beSMike Looijmans 
26c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT_STATUS			0x1D
27c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_STATUS_BIT_DRDY		BIT(7)
28c19ae6beSMike Looijmans 
29c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_RESET				0x7E
30c19ae6beSMike Looijmans #define BMI088_ACCEL_RESET_VAL				0xB6
31c19ae6beSMike Looijmans 
32c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_PWR_CTRL			0x7D
33c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_PWR_CONF			0x7C
34c19ae6beSMike Looijmans 
35c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT_MAP_DATA			0x58
36c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY		BIT(2)
37c19ae6beSMike Looijmans #define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM		BIT(5)
38c19ae6beSMike Looijmans 
39c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT1_IO_CONF			0x53
40c19ae6beSMike Looijmans #define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT	BIT(3)
41c19ae6beSMike Looijmans #define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL		BIT(1)
42c19ae6beSMike Looijmans 
43c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_INT2_IO_CONF			0x54
44c19ae6beSMike Looijmans #define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT	BIT(3)
45c19ae6beSMike Looijmans #define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL		BIT(1)
46c19ae6beSMike Looijmans 
47c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ACC_CONF			0x40
48c19ae6beSMike Looijmans #define BMI088_ACCEL_MODE_ODR_MASK			0x0f
49c19ae6beSMike Looijmans 
50c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_ACC_RANGE			0x41
51c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_3G				0x00
52c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_6G				0x01
53c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_12G				0x02
54c19ae6beSMike Looijmans #define BMI088_ACCEL_RANGE_24G				0x03
55c19ae6beSMike Looijmans 
56c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_TEMP				0x22
57c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_TEMP_SHIFT			5
58c19ae6beSMike Looijmans #define BMI088_ACCEL_TEMP_UNIT				125
59c19ae6beSMike Looijmans #define BMI088_ACCEL_TEMP_OFFSET			23000
60c19ae6beSMike Looijmans 
61c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_XOUT_L				0x12
62c19ae6beSMike Looijmans #define BMI088_ACCEL_AXIS_TO_REG(axis) \
63c19ae6beSMike Looijmans 	(BMI088_ACCEL_REG_XOUT_L + (axis * 2))
64c19ae6beSMike Looijmans 
65c19ae6beSMike Looijmans #define BMI088_ACCEL_MAX_STARTUP_TIME_US		1000
66c19ae6beSMike Looijmans #define BMI088_AUTO_SUSPEND_DELAY_MS			2000
67c19ae6beSMike Looijmans 
68c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_STATUS			0x0E
69c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_CONFIG0			0x48
70c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_CONFIG1			0x49
71c19ae6beSMike Looijmans #define BMI088_ACCEL_REG_FIFO_DATA			0x3F
72c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_LENGTH			100
73c19ae6beSMike Looijmans 
74c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_MODE_FIFO			0x40
75c19ae6beSMike Looijmans #define BMI088_ACCEL_FIFO_MODE_STREAM			0x80
76c19ae6beSMike Looijmans 
7773314772SLI Qingwu #define BMIO088_ACCEL_ACC_RANGE_MSK			GENMASK(1, 0)
7873314772SLI Qingwu 
79c19ae6beSMike Looijmans enum bmi088_accel_axis {
80c19ae6beSMike Looijmans 	AXIS_X,
81c19ae6beSMike Looijmans 	AXIS_Y,
82c19ae6beSMike Looijmans 	AXIS_Z,
83c19ae6beSMike Looijmans };
84c19ae6beSMike Looijmans 
85c19ae6beSMike Looijmans static const int bmi088_sample_freqs[] = {
86c19ae6beSMike Looijmans 	12, 500000,
87c19ae6beSMike Looijmans 	25, 0,
88c19ae6beSMike Looijmans 	50, 0,
89c19ae6beSMike Looijmans 	100, 0,
90c19ae6beSMike Looijmans 	200, 0,
91c19ae6beSMike Looijmans 	400, 0,
92c19ae6beSMike Looijmans 	800, 0,
93c19ae6beSMike Looijmans 	1600, 0,
94c19ae6beSMike Looijmans };
95c19ae6beSMike Looijmans 
96c19ae6beSMike Looijmans /* Available OSR (over sampling rate) sets the 3dB cut-off frequency */
97c19ae6beSMike Looijmans enum bmi088_osr_modes {
98c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_OSR_NORMAL = 0xA,
99c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_OSR_2 = 0x9,
100c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_OSR_4 = 0x8,
101c19ae6beSMike Looijmans };
102c19ae6beSMike Looijmans 
103c19ae6beSMike Looijmans /* Available ODR (output data rates) in Hz */
104c19ae6beSMike Looijmans enum bmi088_odr_modes {
105c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_12_5 = 0x5,
106c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_25 = 0x6,
107c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_50 = 0x7,
108c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_100 = 0x8,
109c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_200 = 0x9,
110c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_400 = 0xa,
111c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_800 = 0xb,
112c19ae6beSMike Looijmans 	BMI088_ACCEL_MODE_ODR_1600 = 0xc,
113c19ae6beSMike Looijmans };
114c19ae6beSMike Looijmans 
115c19ae6beSMike Looijmans struct bmi088_scale_info {
116c19ae6beSMike Looijmans 	int scale;
117c19ae6beSMike Looijmans 	u8 reg_range;
118c19ae6beSMike Looijmans };
119c19ae6beSMike Looijmans 
120c19ae6beSMike Looijmans struct bmi088_accel_chip_info {
121c19ae6beSMike Looijmans 	const char *name;
122c19ae6beSMike Looijmans 	u8 chip_id;
123c19ae6beSMike Looijmans 	const struct iio_chan_spec *channels;
124c19ae6beSMike Looijmans 	int num_channels;
12573314772SLI Qingwu 	const int scale_table[4][2];
126c19ae6beSMike Looijmans };
127c19ae6beSMike Looijmans 
128c19ae6beSMike Looijmans struct bmi088_accel_data {
129c19ae6beSMike Looijmans 	struct regmap *regmap;
130c19ae6beSMike Looijmans 	const struct bmi088_accel_chip_info *chip_info;
131c19ae6beSMike Looijmans 	u8 buffer[2] ____cacheline_aligned; /* shared DMA safe buffer */
132c19ae6beSMike Looijmans };
133c19ae6beSMike Looijmans 
134c19ae6beSMike Looijmans static const struct regmap_range bmi088_volatile_ranges[] = {
135c19ae6beSMike Looijmans 	/* All registers below 0x40 are volatile, except the CHIP ID. */
136c19ae6beSMike Looijmans 	regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f),
137c19ae6beSMike Looijmans 	/* Mark the RESET as volatile too, it is self-clearing */
138c19ae6beSMike Looijmans 	regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET),
139c19ae6beSMike Looijmans };
140c19ae6beSMike Looijmans 
141c19ae6beSMike Looijmans static const struct regmap_access_table bmi088_volatile_table = {
142c19ae6beSMike Looijmans 	.yes_ranges	= bmi088_volatile_ranges,
143c19ae6beSMike Looijmans 	.n_yes_ranges	= ARRAY_SIZE(bmi088_volatile_ranges),
144c19ae6beSMike Looijmans };
145c19ae6beSMike Looijmans 
146c19ae6beSMike Looijmans const struct regmap_config bmi088_regmap_conf = {
147c19ae6beSMike Looijmans 	.reg_bits = 8,
148c19ae6beSMike Looijmans 	.val_bits = 8,
149c19ae6beSMike Looijmans 	.max_register = 0x7E,
150c19ae6beSMike Looijmans 	.volatile_table = &bmi088_volatile_table,
151c19ae6beSMike Looijmans 	.cache_type = REGCACHE_RBTREE,
152c19ae6beSMike Looijmans };
1533bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_regmap_conf, IIO_BMI088);
154c19ae6beSMike Looijmans 
155c19ae6beSMike Looijmans static int bmi088_accel_power_up(struct bmi088_accel_data *data)
156c19ae6beSMike Looijmans {
157c19ae6beSMike Looijmans 	int ret;
158c19ae6beSMike Looijmans 
159c19ae6beSMike Looijmans 	/* Enable accelerometer and temperature sensor */
160c19ae6beSMike Looijmans 	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4);
161c19ae6beSMike Looijmans 	if (ret)
162c19ae6beSMike Looijmans 		return ret;
163c19ae6beSMike Looijmans 
164c19ae6beSMike Looijmans 	/* Datasheet recommends to wait at least 5ms before communication */
165c19ae6beSMike Looijmans 	usleep_range(5000, 6000);
166c19ae6beSMike Looijmans 
167c19ae6beSMike Looijmans 	/* Disable suspend mode */
168c19ae6beSMike Looijmans 	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0);
169c19ae6beSMike Looijmans 	if (ret)
170c19ae6beSMike Looijmans 		return ret;
171c19ae6beSMike Looijmans 
172c19ae6beSMike Looijmans 	/* Recommended at least 1ms before further communication */
173c19ae6beSMike Looijmans 	usleep_range(1000, 1200);
174c19ae6beSMike Looijmans 
175c19ae6beSMike Looijmans 	return 0;
176c19ae6beSMike Looijmans }
177c19ae6beSMike Looijmans 
178c19ae6beSMike Looijmans static int bmi088_accel_power_down(struct bmi088_accel_data *data)
179c19ae6beSMike Looijmans {
180c19ae6beSMike Looijmans 	int ret;
181c19ae6beSMike Looijmans 
182c19ae6beSMike Looijmans 	/* Enable suspend mode */
183c19ae6beSMike Looijmans 	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3);
184c19ae6beSMike Looijmans 	if (ret)
185c19ae6beSMike Looijmans 		return ret;
186c19ae6beSMike Looijmans 
187c19ae6beSMike Looijmans 	/* Recommended at least 1ms before further communication */
188c19ae6beSMike Looijmans 	usleep_range(1000, 1200);
189c19ae6beSMike Looijmans 
190c19ae6beSMike Looijmans 	/* Disable accelerometer and temperature sensor */
191c19ae6beSMike Looijmans 	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0);
192c19ae6beSMike Looijmans 	if (ret)
193c19ae6beSMike Looijmans 		return ret;
194c19ae6beSMike Looijmans 
195c19ae6beSMike Looijmans 	/* Datasheet recommends to wait at least 5ms before communication */
196c19ae6beSMike Looijmans 	usleep_range(5000, 6000);
197c19ae6beSMike Looijmans 
198c19ae6beSMike Looijmans 	return 0;
199c19ae6beSMike Looijmans }
200c19ae6beSMike Looijmans 
201c19ae6beSMike Looijmans static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data,
202c19ae6beSMike Looijmans 					int *val, int *val2)
203c19ae6beSMike Looijmans {
204c19ae6beSMike Looijmans 	unsigned int value;
205c19ae6beSMike Looijmans 	int ret;
206c19ae6beSMike Looijmans 
207c19ae6beSMike Looijmans 	ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF,
208c19ae6beSMike Looijmans 			  &value);
209c19ae6beSMike Looijmans 	if (ret)
210c19ae6beSMike Looijmans 		return ret;
211c19ae6beSMike Looijmans 
212c19ae6beSMike Looijmans 	value &= BMI088_ACCEL_MODE_ODR_MASK;
213c19ae6beSMike Looijmans 	value -= BMI088_ACCEL_MODE_ODR_12_5;
214c19ae6beSMike Looijmans 	value <<= 1;
215c19ae6beSMike Looijmans 
216c19ae6beSMike Looijmans 	if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1)
217c19ae6beSMike Looijmans 		return -EINVAL;
218c19ae6beSMike Looijmans 
219c19ae6beSMike Looijmans 	*val = bmi088_sample_freqs[value];
220c19ae6beSMike Looijmans 	*val2 = bmi088_sample_freqs[value + 1];
221c19ae6beSMike Looijmans 
222c19ae6beSMike Looijmans 	return IIO_VAL_INT_PLUS_MICRO;
223c19ae6beSMike Looijmans }
224c19ae6beSMike Looijmans 
225c19ae6beSMike Looijmans static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val)
226c19ae6beSMike Looijmans {
227c19ae6beSMike Looijmans 	unsigned int regval;
228c19ae6beSMike Looijmans 	int index = 0;
229c19ae6beSMike Looijmans 
230c19ae6beSMike Looijmans 	while (index < ARRAY_SIZE(bmi088_sample_freqs) &&
231c19ae6beSMike Looijmans 	       bmi088_sample_freqs[index] != val)
232c19ae6beSMike Looijmans 		index += 2;
233c19ae6beSMike Looijmans 
234c19ae6beSMike Looijmans 	if (index >= ARRAY_SIZE(bmi088_sample_freqs))
235c19ae6beSMike Looijmans 		return -EINVAL;
236c19ae6beSMike Looijmans 
237c19ae6beSMike Looijmans 	regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5;
238c19ae6beSMike Looijmans 
239c19ae6beSMike Looijmans 	return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF,
240c19ae6beSMike Looijmans 				  BMI088_ACCEL_MODE_ODR_MASK, regval);
241c19ae6beSMike Looijmans }
242c19ae6beSMike Looijmans 
24348d07b3bSLI Qingwu static int bmi088_accel_set_scale(struct bmi088_accel_data *data, int val, int val2)
24448d07b3bSLI Qingwu {
24548d07b3bSLI Qingwu 	unsigned int i;
24648d07b3bSLI Qingwu 
24748d07b3bSLI Qingwu 	for (i = 0; i < 4; i++)
24848d07b3bSLI Qingwu 		if (val  == data->chip_info->scale_table[i][0] &&
24948d07b3bSLI Qingwu 		    val2 == data->chip_info->scale_table[i][1])
25048d07b3bSLI Qingwu 			break;
25148d07b3bSLI Qingwu 
25248d07b3bSLI Qingwu 	if (i == 4)
25348d07b3bSLI Qingwu 		return -EINVAL;
25448d07b3bSLI Qingwu 
25548d07b3bSLI Qingwu 	return regmap_write(data->regmap, BMI088_ACCEL_REG_ACC_RANGE, i);
25648d07b3bSLI Qingwu }
25748d07b3bSLI Qingwu 
258c19ae6beSMike Looijmans static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val)
259c19ae6beSMike Looijmans {
260c19ae6beSMike Looijmans 	int ret;
261c19ae6beSMike Looijmans 	s16 temp;
262c19ae6beSMike Looijmans 
263c19ae6beSMike Looijmans 	ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP,
264c19ae6beSMike Looijmans 			       &data->buffer, sizeof(__be16));
265c19ae6beSMike Looijmans 	if (ret)
266c19ae6beSMike Looijmans 		return ret;
267c19ae6beSMike Looijmans 
268c19ae6beSMike Looijmans 	/* data->buffer is cacheline aligned */
269c19ae6beSMike Looijmans 	temp = be16_to_cpu(*(__be16 *)data->buffer);
270c19ae6beSMike Looijmans 
271c19ae6beSMike Looijmans 	*val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT;
272c19ae6beSMike Looijmans 
273c19ae6beSMike Looijmans 	return IIO_VAL_INT;
274c19ae6beSMike Looijmans }
275c19ae6beSMike Looijmans 
276c19ae6beSMike Looijmans static int bmi088_accel_get_axis(struct bmi088_accel_data *data,
277c19ae6beSMike Looijmans 				 struct iio_chan_spec const *chan,
278c19ae6beSMike Looijmans 				 int *val)
279c19ae6beSMike Looijmans {
280c19ae6beSMike Looijmans 	int ret;
281c19ae6beSMike Looijmans 	s16 raw_val;
282c19ae6beSMike Looijmans 
283c19ae6beSMike Looijmans 	ret = regmap_bulk_read(data->regmap,
284c19ae6beSMike Looijmans 			       BMI088_ACCEL_AXIS_TO_REG(chan->scan_index),
285c19ae6beSMike Looijmans 			       data->buffer, sizeof(__le16));
286c19ae6beSMike Looijmans 	if (ret)
287c19ae6beSMike Looijmans 		return ret;
288c19ae6beSMike Looijmans 
289c19ae6beSMike Looijmans 	raw_val = le16_to_cpu(*(__le16 *)data->buffer);
290c19ae6beSMike Looijmans 	*val = raw_val;
291c19ae6beSMike Looijmans 
292c19ae6beSMike Looijmans 	return IIO_VAL_INT;
293c19ae6beSMike Looijmans }
294c19ae6beSMike Looijmans 
295c19ae6beSMike Looijmans static int bmi088_accel_read_raw(struct iio_dev *indio_dev,
296c19ae6beSMike Looijmans 				 struct iio_chan_spec const *chan,
297c19ae6beSMike Looijmans 				 int *val, int *val2, long mask)
298c19ae6beSMike Looijmans {
299c19ae6beSMike Looijmans 	struct bmi088_accel_data *data = iio_priv(indio_dev);
300c19ae6beSMike Looijmans 	struct device *dev = regmap_get_device(data->regmap);
301c19ae6beSMike Looijmans 	int ret;
30273314772SLI Qingwu 	int reg;
303c19ae6beSMike Looijmans 
304c19ae6beSMike Looijmans 	switch (mask) {
305c19ae6beSMike Looijmans 	case IIO_CHAN_INFO_RAW:
306c19ae6beSMike Looijmans 		switch (chan->type) {
307c19ae6beSMike Looijmans 		case IIO_TEMP:
3089a20795cSJonathan Cameron 			ret = pm_runtime_resume_and_get(dev);
3099a20795cSJonathan Cameron 			if (ret)
3109a20795cSJonathan Cameron 				return ret;
3119a20795cSJonathan Cameron 
312c19ae6beSMike Looijmans 			ret = bmi088_accel_get_temp(data, val);
313c19ae6beSMike Looijmans 			goto out_read_raw_pm_put;
314c19ae6beSMike Looijmans 		case IIO_ACCEL:
3159a20795cSJonathan Cameron 			ret = pm_runtime_resume_and_get(dev);
3169a20795cSJonathan Cameron 			if (ret)
3179a20795cSJonathan Cameron 				return ret;
3189a20795cSJonathan Cameron 
319c19ae6beSMike Looijmans 			ret = iio_device_claim_direct_mode(indio_dev);
320c19ae6beSMike Looijmans 			if (ret)
321c19ae6beSMike Looijmans 				goto out_read_raw_pm_put;
322c19ae6beSMike Looijmans 
323c19ae6beSMike Looijmans 			ret = bmi088_accel_get_axis(data, chan, val);
324c19ae6beSMike Looijmans 			iio_device_release_direct_mode(indio_dev);
325c19ae6beSMike Looijmans 			if (!ret)
326c19ae6beSMike Looijmans 				ret = IIO_VAL_INT;
327c19ae6beSMike Looijmans 
328c19ae6beSMike Looijmans 			goto out_read_raw_pm_put;
329c19ae6beSMike Looijmans 		default:
330c19ae6beSMike Looijmans 			return -EINVAL;
331c19ae6beSMike Looijmans 		}
332c19ae6beSMike Looijmans 	case IIO_CHAN_INFO_OFFSET:
333c19ae6beSMike Looijmans 		switch (chan->type) {
334c19ae6beSMike Looijmans 		case IIO_TEMP:
335c19ae6beSMike Looijmans 			/* Offset applies before scale */
336c19ae6beSMike Looijmans 			*val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT;
337c19ae6beSMike Looijmans 			return IIO_VAL_INT;
338c19ae6beSMike Looijmans 		default:
339c19ae6beSMike Looijmans 			return -EINVAL;
340c19ae6beSMike Looijmans 		}
341c19ae6beSMike Looijmans 	case IIO_CHAN_INFO_SCALE:
342c19ae6beSMike Looijmans 		switch (chan->type) {
343c19ae6beSMike Looijmans 		case IIO_TEMP:
344c19ae6beSMike Looijmans 			/* 0.125 degrees per LSB */
345c19ae6beSMike Looijmans 			*val = BMI088_ACCEL_TEMP_UNIT;
346c19ae6beSMike Looijmans 			return IIO_VAL_INT;
347c19ae6beSMike Looijmans 		case IIO_ACCEL:
3489a20795cSJonathan Cameron 			ret = pm_runtime_resume_and_get(dev);
3499a20795cSJonathan Cameron 			if (ret)
3509a20795cSJonathan Cameron 				return ret;
3519a20795cSJonathan Cameron 
352c19ae6beSMike Looijmans 			ret = regmap_read(data->regmap,
35373314772SLI Qingwu 					  BMI088_ACCEL_REG_ACC_RANGE, &reg);
354c19ae6beSMike Looijmans 			if (ret)
355c19ae6beSMike Looijmans 				goto out_read_raw_pm_put;
356c19ae6beSMike Looijmans 
35773314772SLI Qingwu 			reg = FIELD_GET(BMIO088_ACCEL_ACC_RANGE_MSK, reg);
35873314772SLI Qingwu 			*val  = data->chip_info->scale_table[reg][0];
35973314772SLI Qingwu 			*val2 = data->chip_info->scale_table[reg][1];
36073314772SLI Qingwu 			ret = IIO_VAL_INT_PLUS_MICRO;
361c19ae6beSMike Looijmans 
362c19ae6beSMike Looijmans 			goto out_read_raw_pm_put;
363c19ae6beSMike Looijmans 		default:
364c19ae6beSMike Looijmans 			return -EINVAL;
365c19ae6beSMike Looijmans 		}
366c19ae6beSMike Looijmans 	case IIO_CHAN_INFO_SAMP_FREQ:
3679a20795cSJonathan Cameron 		ret = pm_runtime_resume_and_get(dev);
3689a20795cSJonathan Cameron 		if (ret)
3699a20795cSJonathan Cameron 			return ret;
3709a20795cSJonathan Cameron 
371c19ae6beSMike Looijmans 		ret = bmi088_accel_get_sample_freq(data, val, val2);
372c19ae6beSMike Looijmans 		goto out_read_raw_pm_put;
373c19ae6beSMike Looijmans 	default:
374c19ae6beSMike Looijmans 		break;
375c19ae6beSMike Looijmans 	}
376c19ae6beSMike Looijmans 
377c19ae6beSMike Looijmans 	return -EINVAL;
378c19ae6beSMike Looijmans 
379c19ae6beSMike Looijmans out_read_raw_pm_put:
380c19ae6beSMike Looijmans 	pm_runtime_mark_last_busy(dev);
381c19ae6beSMike Looijmans 	pm_runtime_put_autosuspend(dev);
382c19ae6beSMike Looijmans 
383c19ae6beSMike Looijmans 	return ret;
384c19ae6beSMike Looijmans }
385c19ae6beSMike Looijmans 
386c19ae6beSMike Looijmans static int bmi088_accel_read_avail(struct iio_dev *indio_dev,
387c19ae6beSMike Looijmans 			     struct iio_chan_spec const *chan,
388c19ae6beSMike Looijmans 			     const int **vals, int *type, int *length,
389c19ae6beSMike Looijmans 			     long mask)
390c19ae6beSMike Looijmans {
39148d07b3bSLI Qingwu 	struct bmi088_accel_data *data = iio_priv(indio_dev);
39267ac266dSLI Qingwu 
393c19ae6beSMike Looijmans 	switch (mask) {
39448d07b3bSLI Qingwu 	case IIO_CHAN_INFO_SCALE:
39548d07b3bSLI Qingwu 		*vals = (const int *)data->chip_info->scale_table;
39648d07b3bSLI Qingwu 		*length = 8;
39748d07b3bSLI Qingwu 		*type = IIO_VAL_INT_PLUS_MICRO;
39848d07b3bSLI Qingwu 		return IIO_AVAIL_LIST;
399c19ae6beSMike Looijmans 	case IIO_CHAN_INFO_SAMP_FREQ:
400c19ae6beSMike Looijmans 		*type = IIO_VAL_INT_PLUS_MICRO;
401c19ae6beSMike Looijmans 		*vals = bmi088_sample_freqs;
402c19ae6beSMike Looijmans 		*length = ARRAY_SIZE(bmi088_sample_freqs);
403c19ae6beSMike Looijmans 		return IIO_AVAIL_LIST;
404c19ae6beSMike Looijmans 	default:
405c19ae6beSMike Looijmans 		return -EINVAL;
406c19ae6beSMike Looijmans 	}
407c19ae6beSMike Looijmans }
408c19ae6beSMike Looijmans 
409c19ae6beSMike Looijmans static int bmi088_accel_write_raw(struct iio_dev *indio_dev,
410c19ae6beSMike Looijmans 				  struct iio_chan_spec const *chan,
411c19ae6beSMike Looijmans 				  int val, int val2, long mask)
412c19ae6beSMike Looijmans {
413c19ae6beSMike Looijmans 	struct bmi088_accel_data *data = iio_priv(indio_dev);
414c19ae6beSMike Looijmans 	struct device *dev = regmap_get_device(data->regmap);
415c19ae6beSMike Looijmans 	int ret;
416c19ae6beSMike Looijmans 
417c19ae6beSMike Looijmans 	switch (mask) {
41848d07b3bSLI Qingwu 	case IIO_CHAN_INFO_SCALE:
41948d07b3bSLI Qingwu 		ret = pm_runtime_resume_and_get(dev);
42048d07b3bSLI Qingwu 		if (ret)
42148d07b3bSLI Qingwu 			return ret;
42248d07b3bSLI Qingwu 
42348d07b3bSLI Qingwu 		ret = bmi088_accel_set_scale(data, val, val2);
42448d07b3bSLI Qingwu 		pm_runtime_mark_last_busy(dev);
42548d07b3bSLI Qingwu 		pm_runtime_put_autosuspend(dev);
42648d07b3bSLI Qingwu 		return ret;
427c19ae6beSMike Looijmans 	case IIO_CHAN_INFO_SAMP_FREQ:
4289a20795cSJonathan Cameron 		ret = pm_runtime_resume_and_get(dev);
4299a20795cSJonathan Cameron 		if (ret)
4309a20795cSJonathan Cameron 			return ret;
4319a20795cSJonathan Cameron 
432c19ae6beSMike Looijmans 		ret = bmi088_accel_set_sample_freq(data, val);
433c19ae6beSMike Looijmans 		pm_runtime_mark_last_busy(dev);
434c19ae6beSMike Looijmans 		pm_runtime_put_autosuspend(dev);
435c19ae6beSMike Looijmans 		return ret;
436c19ae6beSMike Looijmans 	default:
437c19ae6beSMike Looijmans 		return -EINVAL;
438c19ae6beSMike Looijmans 	}
439c19ae6beSMike Looijmans }
440c19ae6beSMike Looijmans 
441c19ae6beSMike Looijmans #define BMI088_ACCEL_CHANNEL(_axis) { \
442c19ae6beSMike Looijmans 	.type = IIO_ACCEL, \
443c19ae6beSMike Looijmans 	.modified = 1, \
444c19ae6beSMike Looijmans 	.channel2 = IIO_MOD_##_axis, \
445c19ae6beSMike Looijmans 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
446c19ae6beSMike Looijmans 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
447c19ae6beSMike Looijmans 				BIT(IIO_CHAN_INFO_SAMP_FREQ), \
44848d07b3bSLI Qingwu 	.info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
44948d07b3bSLI Qingwu 				BIT(IIO_CHAN_INFO_SCALE), \
450c19ae6beSMike Looijmans 	.scan_index = AXIS_##_axis, \
451c19ae6beSMike Looijmans }
452c19ae6beSMike Looijmans 
453c19ae6beSMike Looijmans static const struct iio_chan_spec bmi088_accel_channels[] = {
454c19ae6beSMike Looijmans 	{
455c19ae6beSMike Looijmans 		.type = IIO_TEMP,
456c19ae6beSMike Looijmans 		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
457c19ae6beSMike Looijmans 				      BIT(IIO_CHAN_INFO_SCALE) |
458c19ae6beSMike Looijmans 				      BIT(IIO_CHAN_INFO_OFFSET),
459c19ae6beSMike Looijmans 		.scan_index = -1,
460c19ae6beSMike Looijmans 	},
461c19ae6beSMike Looijmans 	BMI088_ACCEL_CHANNEL(X),
462c19ae6beSMike Looijmans 	BMI088_ACCEL_CHANNEL(Y),
463c19ae6beSMike Looijmans 	BMI088_ACCEL_CHANNEL(Z),
464c19ae6beSMike Looijmans 	IIO_CHAN_SOFT_TIMESTAMP(3),
465c19ae6beSMike Looijmans };
466c19ae6beSMike Looijmans 
467c19ae6beSMike Looijmans static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = {
4687a61456cSLI Qingwu 	[BOSCH_BMI085] = {
4697a61456cSLI Qingwu 		.name = "bmi085-accel",
4707a61456cSLI Qingwu 		.chip_id = 0x1F,
4717a61456cSLI Qingwu 		.channels = bmi088_accel_channels,
4727a61456cSLI Qingwu 		.num_channels = ARRAY_SIZE(bmi088_accel_channels),
4737a61456cSLI Qingwu 		.scale_table = {{0, 598}, {0, 1196}, {0, 2393}, {0, 4785}},
4747a61456cSLI Qingwu 	},
47567ac266dSLI Qingwu 	[BOSCH_BMI088] = {
47667ac266dSLI Qingwu 		.name = "bmi088-accel",
477c19ae6beSMike Looijmans 		.chip_id = 0x1E,
478c19ae6beSMike Looijmans 		.channels = bmi088_accel_channels,
479c19ae6beSMike Looijmans 		.num_channels = ARRAY_SIZE(bmi088_accel_channels),
48073314772SLI Qingwu 		.scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}},
481c19ae6beSMike Looijmans 	},
482*57387d3cSLI Qingwu 	[BOSCH_BMI090L] = {
483*57387d3cSLI Qingwu 		.name = "bmi090l-accel",
484*57387d3cSLI Qingwu 		.chip_id = 0x1A,
485*57387d3cSLI Qingwu 		.channels = bmi088_accel_channels,
486*57387d3cSLI Qingwu 		.num_channels = ARRAY_SIZE(bmi088_accel_channels),
487*57387d3cSLI Qingwu 		.scale_table = {{0, 897}, {0, 1794}, {0, 3589}, {0, 7178}},
488*57387d3cSLI Qingwu 	},
489c19ae6beSMike Looijmans };
490c19ae6beSMike Looijmans 
491c19ae6beSMike Looijmans static const struct iio_info bmi088_accel_info = {
492c19ae6beSMike Looijmans 	.read_raw	= bmi088_accel_read_raw,
493c19ae6beSMike Looijmans 	.write_raw	= bmi088_accel_write_raw,
494c19ae6beSMike Looijmans 	.read_avail	= bmi088_accel_read_avail,
495c19ae6beSMike Looijmans };
496c19ae6beSMike Looijmans 
497c19ae6beSMike Looijmans static const unsigned long bmi088_accel_scan_masks[] = {
498c19ae6beSMike Looijmans 	BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
499c19ae6beSMike Looijmans 	0
500c19ae6beSMike Looijmans };
501c19ae6beSMike Looijmans 
50267ac266dSLI Qingwu static int bmi088_accel_chip_init(struct bmi088_accel_data *data, enum bmi_device_type type)
503c19ae6beSMike Looijmans {
504c19ae6beSMike Looijmans 	struct device *dev = regmap_get_device(data->regmap);
505c19ae6beSMike Looijmans 	int ret, i;
506c19ae6beSMike Looijmans 	unsigned int val;
507c19ae6beSMike Looijmans 
50867ac266dSLI Qingwu 	if (type >= BOSCH_UNKNOWN)
50967ac266dSLI Qingwu 		return -ENODEV;
51067ac266dSLI Qingwu 
511c19ae6beSMike Looijmans 	/* Do a dummy read to enable SPI interface, won't harm I2C */
512c19ae6beSMike Looijmans 	regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
513c19ae6beSMike Looijmans 
514c19ae6beSMike Looijmans 	/*
515c19ae6beSMike Looijmans 	 * Reset chip to get it in a known good state. A delay of 1ms after
516c19ae6beSMike Looijmans 	 * reset is required according to the data sheet
517c19ae6beSMike Looijmans 	 */
518c19ae6beSMike Looijmans 	ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET,
519c19ae6beSMike Looijmans 			   BMI088_ACCEL_RESET_VAL);
520c19ae6beSMike Looijmans 	if (ret)
521c19ae6beSMike Looijmans 		return ret;
522c19ae6beSMike Looijmans 
523c19ae6beSMike Looijmans 	usleep_range(1000, 2000);
524c19ae6beSMike Looijmans 
525c19ae6beSMike Looijmans 	/* Do a dummy read again after a reset to enable the SPI interface */
526c19ae6beSMike Looijmans 	regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val);
527c19ae6beSMike Looijmans 
528c19ae6beSMike Looijmans 	/* Read chip ID */
529c19ae6beSMike Looijmans 	ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val);
530c19ae6beSMike Looijmans 	if (ret) {
531c19ae6beSMike Looijmans 		dev_err(dev, "Error: Reading chip id\n");
532c19ae6beSMike Looijmans 		return ret;
533c19ae6beSMike Looijmans 	}
534c19ae6beSMike Looijmans 
535c19ae6beSMike Looijmans 	/* Validate chip ID */
53667ac266dSLI Qingwu 	for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++)
53767ac266dSLI Qingwu 		if (bmi088_accel_chip_info_tbl[i].chip_id == val)
538c19ae6beSMike Looijmans 			break;
53967ac266dSLI Qingwu 
54067ac266dSLI Qingwu 	if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl))
54167ac266dSLI Qingwu 		data->chip_info = &bmi088_accel_chip_info_tbl[type];
54267ac266dSLI Qingwu 	else
54367ac266dSLI Qingwu 		data->chip_info = &bmi088_accel_chip_info_tbl[i];
54467ac266dSLI Qingwu 
54567ac266dSLI Qingwu 	if (i != type)
54667ac266dSLI Qingwu 		dev_warn(dev, "unexpected chip id 0x%X\n", val);
547c19ae6beSMike Looijmans 
548c19ae6beSMike Looijmans 	return 0;
549c19ae6beSMike Looijmans }
550c19ae6beSMike Looijmans 
551c19ae6beSMike Looijmans int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap,
55267ac266dSLI Qingwu 	int irq, enum bmi_device_type type)
553c19ae6beSMike Looijmans {
554c19ae6beSMike Looijmans 	struct bmi088_accel_data *data;
555c19ae6beSMike Looijmans 	struct iio_dev *indio_dev;
556c19ae6beSMike Looijmans 	int ret;
557c19ae6beSMike Looijmans 
558c19ae6beSMike Looijmans 	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
559c19ae6beSMike Looijmans 	if (!indio_dev)
560c19ae6beSMike Looijmans 		return -ENOMEM;
561c19ae6beSMike Looijmans 
562c19ae6beSMike Looijmans 	data = iio_priv(indio_dev);
563c19ae6beSMike Looijmans 	dev_set_drvdata(dev, indio_dev);
564c19ae6beSMike Looijmans 
565c19ae6beSMike Looijmans 	data->regmap = regmap;
566c19ae6beSMike Looijmans 
56767ac266dSLI Qingwu 	ret = bmi088_accel_chip_init(data, type);
568c19ae6beSMike Looijmans 	if (ret)
569c19ae6beSMike Looijmans 		return ret;
570c19ae6beSMike Looijmans 
571c19ae6beSMike Looijmans 	indio_dev->channels = data->chip_info->channels;
572c19ae6beSMike Looijmans 	indio_dev->num_channels = data->chip_info->num_channels;
57367ac266dSLI Qingwu 	indio_dev->name = data->chip_info->name;
574c19ae6beSMike Looijmans 	indio_dev->available_scan_masks = bmi088_accel_scan_masks;
575c19ae6beSMike Looijmans 	indio_dev->modes = INDIO_DIRECT_MODE;
576c19ae6beSMike Looijmans 	indio_dev->info = &bmi088_accel_info;
577c19ae6beSMike Looijmans 
578c19ae6beSMike Looijmans 	/* Enable runtime PM */
579c19ae6beSMike Looijmans 	pm_runtime_get_noresume(dev);
580c19ae6beSMike Looijmans 	pm_runtime_set_suspended(dev);
581c19ae6beSMike Looijmans 	pm_runtime_enable(dev);
582c19ae6beSMike Looijmans 	/* We need ~6ms to startup, so set the delay to 6 seconds */
583c19ae6beSMike Looijmans 	pm_runtime_set_autosuspend_delay(dev, 6000);
584c19ae6beSMike Looijmans 	pm_runtime_use_autosuspend(dev);
585c19ae6beSMike Looijmans 	pm_runtime_put(dev);
586c19ae6beSMike Looijmans 
587c19ae6beSMike Looijmans 	ret = iio_device_register(indio_dev);
588c19ae6beSMike Looijmans 	if (ret)
589c19ae6beSMike Looijmans 		dev_err(dev, "Unable to register iio device\n");
590c19ae6beSMike Looijmans 
591c19ae6beSMike Looijmans 	return ret;
592c19ae6beSMike Looijmans }
5933bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_probe, IIO_BMI088);
594c19ae6beSMike Looijmans 
595c19ae6beSMike Looijmans 
596bcf9d61aSUwe Kleine-König void bmi088_accel_core_remove(struct device *dev)
597c19ae6beSMike Looijmans {
598c19ae6beSMike Looijmans 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
599c19ae6beSMike Looijmans 	struct bmi088_accel_data *data = iio_priv(indio_dev);
600c19ae6beSMike Looijmans 
601c19ae6beSMike Looijmans 	iio_device_unregister(indio_dev);
602c19ae6beSMike Looijmans 
603c19ae6beSMike Looijmans 	pm_runtime_disable(dev);
604c19ae6beSMike Looijmans 	pm_runtime_set_suspended(dev);
605c19ae6beSMike Looijmans 	bmi088_accel_power_down(data);
606c19ae6beSMike Looijmans }
6073bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088);
608c19ae6beSMike Looijmans 
609c19ae6beSMike Looijmans static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev)
610c19ae6beSMike Looijmans {
611c19ae6beSMike Looijmans 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
612c19ae6beSMike Looijmans 	struct bmi088_accel_data *data = iio_priv(indio_dev);
613c19ae6beSMike Looijmans 
614c19ae6beSMike Looijmans 	return bmi088_accel_power_down(data);
615c19ae6beSMike Looijmans }
616c19ae6beSMike Looijmans 
617c19ae6beSMike Looijmans static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev)
618c19ae6beSMike Looijmans {
619c19ae6beSMike Looijmans 	struct iio_dev *indio_dev = dev_get_drvdata(dev);
620c19ae6beSMike Looijmans 	struct bmi088_accel_data *data = iio_priv(indio_dev);
621c19ae6beSMike Looijmans 
622c19ae6beSMike Looijmans 	return bmi088_accel_power_up(data);
623c19ae6beSMike Looijmans }
624c19ae6beSMike Looijmans 
625c19ae6beSMike Looijmans const struct dev_pm_ops bmi088_accel_pm_ops = {
626c19ae6beSMike Looijmans 	SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
627c19ae6beSMike Looijmans 				pm_runtime_force_resume)
628c19ae6beSMike Looijmans 	SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend,
629c19ae6beSMike Looijmans 			   bmi088_accel_runtime_resume, NULL)
630c19ae6beSMike Looijmans };
6313bd072d1SJonathan Cameron EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088);
632c19ae6beSMike Looijmans 
633c19ae6beSMike Looijmans MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>");
634c19ae6beSMike Looijmans MODULE_LICENSE("GPL v2");
635c19ae6beSMike Looijmans MODULE_DESCRIPTION("BMI088 accelerometer driver (core)");
636