xref: /linux/drivers/iio/accel/bmi088-accel-core.c (revision 7ec462100ef9142344ddbf86f2c3008b97acddbe)
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, &reg);
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