xref: /openbmc/linux/drivers/iio/accel/bma220_spi.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b9d453a5SGwendal Grignou /*
3bf2a5600STiberiu Breana  * BMA220 Digital triaxial acceleration sensor driver
4bf2a5600STiberiu Breana  *
5f530f882SAndy Shevchenko  * Copyright (c) 2016,2020 Intel Corporation.
6bf2a5600STiberiu Breana  */
7bf2a5600STiberiu Breana 
82b09b41dSAndy Shevchenko #include <linux/bits.h>
9bf2a5600STiberiu Breana #include <linux/kernel.h>
10846afc1dSAndy Shevchenko #include <linux/mod_devicetable.h>
11bf2a5600STiberiu Breana #include <linux/module.h>
12df9f7d4cSAndy Shevchenko #include <linux/spi/spi.h>
13df9f7d4cSAndy Shevchenko 
14194dc4c7STiberiu Breana #include <linux/iio/buffer.h>
15bf2a5600STiberiu Breana #include <linux/iio/iio.h>
16bf2a5600STiberiu Breana #include <linux/iio/sysfs.h>
17194dc4c7STiberiu Breana #include <linux/iio/trigger_consumer.h>
18194dc4c7STiberiu Breana #include <linux/iio/triggered_buffer.h>
19bf2a5600STiberiu Breana 
20bf2a5600STiberiu Breana #define BMA220_REG_ID				0x00
21bf2a5600STiberiu Breana #define BMA220_REG_ACCEL_X			0x02
22bf2a5600STiberiu Breana #define BMA220_REG_ACCEL_Y			0x03
23bf2a5600STiberiu Breana #define BMA220_REG_ACCEL_Z			0x04
24bf2a5600STiberiu Breana #define BMA220_REG_RANGE			0x11
25bf2a5600STiberiu Breana #define BMA220_REG_SUSPEND			0x18
26bf2a5600STiberiu Breana 
27bf2a5600STiberiu Breana #define BMA220_CHIP_ID				0xDD
282b09b41dSAndy Shevchenko #define BMA220_READ_MASK			BIT(7)
292b09b41dSAndy Shevchenko #define BMA220_RANGE_MASK			GENMASK(1, 0)
30bf2a5600STiberiu Breana #define BMA220_SUSPEND_SLEEP			0xFF
31bf2a5600STiberiu Breana #define BMA220_SUSPEND_WAKE			0x00
32bf2a5600STiberiu Breana 
33bf2a5600STiberiu Breana #define BMA220_DEVICE_NAME			"bma220"
34bf2a5600STiberiu Breana 
35bf2a5600STiberiu Breana #define BMA220_ACCEL_CHANNEL(index, reg, axis) {			\
36bf2a5600STiberiu Breana 	.type = IIO_ACCEL,						\
37bf2a5600STiberiu Breana 	.address = reg,							\
38bf2a5600STiberiu Breana 	.modified = 1,							\
39bf2a5600STiberiu Breana 	.channel2 = IIO_MOD_##axis,					\
40bf2a5600STiberiu Breana 	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),			\
41bf2a5600STiberiu Breana 	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),		\
42194dc4c7STiberiu Breana 	.scan_index = index,						\
43194dc4c7STiberiu Breana 	.scan_type = {							\
44194dc4c7STiberiu Breana 		.sign = 's',						\
45194dc4c7STiberiu Breana 		.realbits = 6,						\
46194dc4c7STiberiu Breana 		.storagebits = 8,					\
47f905772eSGwendal Grignou 		.shift = 2,						\
48194dc4c7STiberiu Breana 		.endianness = IIO_CPU,					\
49194dc4c7STiberiu Breana 	},								\
50bf2a5600STiberiu Breana }
51bf2a5600STiberiu Breana 
52194dc4c7STiberiu Breana enum bma220_axis {
53194dc4c7STiberiu Breana 	AXIS_X,
54194dc4c7STiberiu Breana 	AXIS_Y,
55194dc4c7STiberiu Breana 	AXIS_Z,
56194dc4c7STiberiu Breana };
57194dc4c7STiberiu Breana 
58938d1b38SAndy Shevchenko static const int bma220_scale_table[][2] = {
59938d1b38SAndy Shevchenko 	{0, 623000}, {1, 248000}, {2, 491000}, {4, 983000},
60bf2a5600STiberiu Breana };
61bf2a5600STiberiu Breana 
62bf2a5600STiberiu Breana struct bma220_data {
63bf2a5600STiberiu Breana 	struct spi_device *spi_device;
64bf2a5600STiberiu Breana 	struct mutex lock;
65151dbf00SJonathan Cameron 	struct {
66151dbf00SJonathan Cameron 		s8 chans[3];
67151dbf00SJonathan Cameron 		/* Ensure timestamp is naturally aligned. */
68151dbf00SJonathan Cameron 		s64 timestamp __aligned(8);
69151dbf00SJonathan Cameron 	} scan;
7038e71240SJonathan Cameron 	u8 tx_buf[2] __aligned(IIO_DMA_MINALIGN);
71bf2a5600STiberiu Breana };
72bf2a5600STiberiu Breana 
73bf2a5600STiberiu Breana static const struct iio_chan_spec bma220_channels[] = {
74bf2a5600STiberiu Breana 	BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X),
75bf2a5600STiberiu Breana 	BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y),
76bf2a5600STiberiu Breana 	BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z),
77194dc4c7STiberiu Breana 	IIO_CHAN_SOFT_TIMESTAMP(3),
78bf2a5600STiberiu Breana };
79bf2a5600STiberiu Breana 
bma220_read_reg(struct spi_device * spi,u8 reg)80bf2a5600STiberiu Breana static inline int bma220_read_reg(struct spi_device *spi, u8 reg)
81bf2a5600STiberiu Breana {
82bf2a5600STiberiu Breana 	return spi_w8r8(spi, reg | BMA220_READ_MASK);
83bf2a5600STiberiu Breana }
84bf2a5600STiberiu Breana 
85194dc4c7STiberiu Breana static const unsigned long bma220_accel_scan_masks[] = {
86194dc4c7STiberiu Breana 	BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z),
87194dc4c7STiberiu Breana 	0
88194dc4c7STiberiu Breana };
89194dc4c7STiberiu Breana 
bma220_trigger_handler(int irq,void * p)90194dc4c7STiberiu Breana static irqreturn_t bma220_trigger_handler(int irq, void *p)
91194dc4c7STiberiu Breana {
92194dc4c7STiberiu Breana 	int ret;
93194dc4c7STiberiu Breana 	struct iio_poll_func *pf = p;
94194dc4c7STiberiu Breana 	struct iio_dev *indio_dev = pf->indio_dev;
95194dc4c7STiberiu Breana 	struct bma220_data *data = iio_priv(indio_dev);
96194dc4c7STiberiu Breana 	struct spi_device *spi = data->spi_device;
97194dc4c7STiberiu Breana 
98194dc4c7STiberiu Breana 	mutex_lock(&data->lock);
99194dc4c7STiberiu Breana 	data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK;
100151dbf00SJonathan Cameron 	ret = spi_write_then_read(spi, data->tx_buf, 1, &data->scan.chans,
101194dc4c7STiberiu Breana 				  ARRAY_SIZE(bma220_channels) - 1);
102194dc4c7STiberiu Breana 	if (ret < 0)
103194dc4c7STiberiu Breana 		goto err;
104194dc4c7STiberiu Breana 
105151dbf00SJonathan Cameron 	iio_push_to_buffers_with_timestamp(indio_dev, &data->scan,
106194dc4c7STiberiu Breana 					   pf->timestamp);
107194dc4c7STiberiu Breana err:
108194dc4c7STiberiu Breana 	mutex_unlock(&data->lock);
109194dc4c7STiberiu Breana 	iio_trigger_notify_done(indio_dev->trig);
110194dc4c7STiberiu Breana 
111194dc4c7STiberiu Breana 	return IRQ_HANDLED;
112194dc4c7STiberiu Breana }
113194dc4c7STiberiu Breana 
bma220_read_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int * val,int * val2,long mask)114bf2a5600STiberiu Breana static int bma220_read_raw(struct iio_dev *indio_dev,
115bf2a5600STiberiu Breana 			   struct iio_chan_spec const *chan,
116bf2a5600STiberiu Breana 			   int *val, int *val2, long mask)
117bf2a5600STiberiu Breana {
118bf2a5600STiberiu Breana 	int ret;
119bf2a5600STiberiu Breana 	u8 range_idx;
120bf2a5600STiberiu Breana 	struct bma220_data *data = iio_priv(indio_dev);
121bf2a5600STiberiu Breana 
122bf2a5600STiberiu Breana 	switch (mask) {
123bf2a5600STiberiu Breana 	case IIO_CHAN_INFO_RAW:
124bf2a5600STiberiu Breana 		ret = bma220_read_reg(data->spi_device, chan->address);
125bf2a5600STiberiu Breana 		if (ret < 0)
126bf2a5600STiberiu Breana 			return -EINVAL;
127f905772eSGwendal Grignou 		*val = sign_extend32(ret >> chan->scan_type.shift,
128f905772eSGwendal Grignou 				     chan->scan_type.realbits - 1);
129bf2a5600STiberiu Breana 		return IIO_VAL_INT;
130bf2a5600STiberiu Breana 	case IIO_CHAN_INFO_SCALE:
131bf2a5600STiberiu Breana 		ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE);
132bf2a5600STiberiu Breana 		if (ret < 0)
133bf2a5600STiberiu Breana 			return ret;
134bf2a5600STiberiu Breana 		range_idx = ret & BMA220_RANGE_MASK;
135bf2a5600STiberiu Breana 		*val = bma220_scale_table[range_idx][0];
136bf2a5600STiberiu Breana 		*val2 = bma220_scale_table[range_idx][1];
137bf2a5600STiberiu Breana 		return IIO_VAL_INT_PLUS_MICRO;
138bf2a5600STiberiu Breana 	}
139bf2a5600STiberiu Breana 
140bf2a5600STiberiu Breana 	return -EINVAL;
141bf2a5600STiberiu Breana }
142bf2a5600STiberiu Breana 
bma220_write_raw(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,int val,int val2,long mask)143bf2a5600STiberiu Breana static int bma220_write_raw(struct iio_dev *indio_dev,
144bf2a5600STiberiu Breana 			    struct iio_chan_spec const *chan,
145bf2a5600STiberiu Breana 			    int val, int val2, long mask)
146bf2a5600STiberiu Breana {
147bf2a5600STiberiu Breana 	int i;
148bf2a5600STiberiu Breana 	int ret;
149bf2a5600STiberiu Breana 	int index = -1;
150bf2a5600STiberiu Breana 	struct bma220_data *data = iio_priv(indio_dev);
151bf2a5600STiberiu Breana 
152bf2a5600STiberiu Breana 	switch (mask) {
153bf2a5600STiberiu Breana 	case IIO_CHAN_INFO_SCALE:
154bf2a5600STiberiu Breana 		for (i = 0; i < ARRAY_SIZE(bma220_scale_table); i++)
155bf2a5600STiberiu Breana 			if (val == bma220_scale_table[i][0] &&
156bf2a5600STiberiu Breana 			    val2 == bma220_scale_table[i][1]) {
157bf2a5600STiberiu Breana 				index = i;
158bf2a5600STiberiu Breana 				break;
159bf2a5600STiberiu Breana 			}
160bf2a5600STiberiu Breana 		if (index < 0)
161bf2a5600STiberiu Breana 			return -EINVAL;
162bf2a5600STiberiu Breana 
163bf2a5600STiberiu Breana 		mutex_lock(&data->lock);
164bf2a5600STiberiu Breana 		data->tx_buf[0] = BMA220_REG_RANGE;
165bf2a5600STiberiu Breana 		data->tx_buf[1] = index;
166bf2a5600STiberiu Breana 		ret = spi_write(data->spi_device, data->tx_buf,
167bf2a5600STiberiu Breana 				sizeof(data->tx_buf));
168bf2a5600STiberiu Breana 		if (ret < 0)
169bf2a5600STiberiu Breana 			dev_err(&data->spi_device->dev,
170bf2a5600STiberiu Breana 				"failed to set measurement range\n");
171bf2a5600STiberiu Breana 		mutex_unlock(&data->lock);
172bf2a5600STiberiu Breana 
173bf2a5600STiberiu Breana 		return 0;
174bf2a5600STiberiu Breana 	}
175bf2a5600STiberiu Breana 
176bf2a5600STiberiu Breana 	return -EINVAL;
177bf2a5600STiberiu Breana }
178bf2a5600STiberiu Breana 
bma220_read_avail(struct iio_dev * indio_dev,struct iio_chan_spec const * chan,const int ** vals,int * type,int * length,long mask)179938d1b38SAndy Shevchenko static int bma220_read_avail(struct iio_dev *indio_dev,
180938d1b38SAndy Shevchenko 			     struct iio_chan_spec const *chan,
181938d1b38SAndy Shevchenko 			     const int **vals, int *type, int *length,
182938d1b38SAndy Shevchenko 			     long mask)
183938d1b38SAndy Shevchenko {
184938d1b38SAndy Shevchenko 	switch (mask) {
185938d1b38SAndy Shevchenko 	case IIO_CHAN_INFO_SCALE:
186938d1b38SAndy Shevchenko 		*vals = (int *)bma220_scale_table;
187938d1b38SAndy Shevchenko 		*type = IIO_VAL_INT_PLUS_MICRO;
188938d1b38SAndy Shevchenko 		*length = ARRAY_SIZE(bma220_scale_table) * 2;
189938d1b38SAndy Shevchenko 		return IIO_AVAIL_LIST;
190938d1b38SAndy Shevchenko 	default:
191938d1b38SAndy Shevchenko 		return -EINVAL;
192938d1b38SAndy Shevchenko 	}
193938d1b38SAndy Shevchenko }
194938d1b38SAndy Shevchenko 
195bf2a5600STiberiu Breana static const struct iio_info bma220_info = {
196bf2a5600STiberiu Breana 	.read_raw		= bma220_read_raw,
197bf2a5600STiberiu Breana 	.write_raw		= bma220_write_raw,
198938d1b38SAndy Shevchenko 	.read_avail		= bma220_read_avail,
199bf2a5600STiberiu Breana };
200bf2a5600STiberiu Breana 
bma220_init(struct spi_device * spi)201bf2a5600STiberiu Breana static int bma220_init(struct spi_device *spi)
202bf2a5600STiberiu Breana {
203bf2a5600STiberiu Breana 	int ret;
204bf2a5600STiberiu Breana 
205bf2a5600STiberiu Breana 	ret = bma220_read_reg(spi, BMA220_REG_ID);
206bf2a5600STiberiu Breana 	if (ret != BMA220_CHIP_ID)
207bf2a5600STiberiu Breana 		return -ENODEV;
208bf2a5600STiberiu Breana 
209bf2a5600STiberiu Breana 	/* Make sure the chip is powered on */
210bf2a5600STiberiu Breana 	ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
2115265b267SAndy Shevchenko 	if (ret == BMA220_SUSPEND_WAKE)
2125265b267SAndy Shevchenko 		ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
213bf2a5600STiberiu Breana 	if (ret < 0)
214bf2a5600STiberiu Breana 		return ret;
2155265b267SAndy Shevchenko 	if (ret == BMA220_SUSPEND_WAKE)
2165265b267SAndy Shevchenko 		return -EBUSY;
217bf2a5600STiberiu Breana 
218bf2a5600STiberiu Breana 	return 0;
219bf2a5600STiberiu Breana }
220bf2a5600STiberiu Breana 
bma220_power(struct spi_device * spi,bool up)2213ce868bbSAlexandru Ardelean static int bma220_power(struct spi_device *spi, bool up)
2223ce868bbSAlexandru Ardelean {
2233ce868bbSAlexandru Ardelean 	int i, ret;
2243ce868bbSAlexandru Ardelean 
2253ce868bbSAlexandru Ardelean 	/**
2263ce868bbSAlexandru Ardelean 	 * The chip can be suspended/woken up by a simple register read.
2273ce868bbSAlexandru Ardelean 	 * So, we need up to 2 register reads of the suspend register
2283ce868bbSAlexandru Ardelean 	 * to make sure that the device is in the desired state.
2293ce868bbSAlexandru Ardelean 	 */
2303ce868bbSAlexandru Ardelean 	for (i = 0; i < 2; i++) {
2313ce868bbSAlexandru Ardelean 		ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
2323ce868bbSAlexandru Ardelean 		if (ret < 0)
2333ce868bbSAlexandru Ardelean 			return ret;
2343ce868bbSAlexandru Ardelean 
2353ce868bbSAlexandru Ardelean 		if (up && ret == BMA220_SUSPEND_SLEEP)
2363ce868bbSAlexandru Ardelean 			return 0;
2373ce868bbSAlexandru Ardelean 
2383ce868bbSAlexandru Ardelean 		if (!up && ret == BMA220_SUSPEND_WAKE)
2393ce868bbSAlexandru Ardelean 			return 0;
2403ce868bbSAlexandru Ardelean 	}
2413ce868bbSAlexandru Ardelean 
2423ce868bbSAlexandru Ardelean 	return -EBUSY;
2433ce868bbSAlexandru Ardelean }
2443ce868bbSAlexandru Ardelean 
bma220_deinit(void * spi)245c336b611SAlexandru Ardelean static void bma220_deinit(void *spi)
246bf2a5600STiberiu Breana {
2473ce868bbSAlexandru Ardelean 	bma220_power(spi, false);
248bf2a5600STiberiu Breana }
249bf2a5600STiberiu Breana 
bma220_probe(struct spi_device * spi)250bf2a5600STiberiu Breana static int bma220_probe(struct spi_device *spi)
251bf2a5600STiberiu Breana {
252bf2a5600STiberiu Breana 	int ret;
253bf2a5600STiberiu Breana 	struct iio_dev *indio_dev;
254bf2a5600STiberiu Breana 	struct bma220_data *data;
255bf2a5600STiberiu Breana 
256bf2a5600STiberiu Breana 	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
257bf2a5600STiberiu Breana 	if (!indio_dev) {
258bf2a5600STiberiu Breana 		dev_err(&spi->dev, "iio allocation failed!\n");
259bf2a5600STiberiu Breana 		return -ENOMEM;
260bf2a5600STiberiu Breana 	}
261bf2a5600STiberiu Breana 
262bf2a5600STiberiu Breana 	data = iio_priv(indio_dev);
263bf2a5600STiberiu Breana 	data->spi_device = spi;
264bf2a5600STiberiu Breana 	mutex_init(&data->lock);
265bf2a5600STiberiu Breana 
266bf2a5600STiberiu Breana 	indio_dev->info = &bma220_info;
267bf2a5600STiberiu Breana 	indio_dev->name = BMA220_DEVICE_NAME;
268bf2a5600STiberiu Breana 	indio_dev->modes = INDIO_DIRECT_MODE;
269bf2a5600STiberiu Breana 	indio_dev->channels = bma220_channels;
270bf2a5600STiberiu Breana 	indio_dev->num_channels = ARRAY_SIZE(bma220_channels);
271194dc4c7STiberiu Breana 	indio_dev->available_scan_masks = bma220_accel_scan_masks;
272bf2a5600STiberiu Breana 
273bf2a5600STiberiu Breana 	ret = bma220_init(data->spi_device);
2745265b267SAndy Shevchenko 	if (ret)
275bf2a5600STiberiu Breana 		return ret;
276bf2a5600STiberiu Breana 
277c336b611SAlexandru Ardelean 	ret = devm_add_action_or_reset(&spi->dev, bma220_deinit, spi);
278c336b611SAlexandru Ardelean 	if (ret)
279c336b611SAlexandru Ardelean 		return ret;
280c336b611SAlexandru Ardelean 
281c336b611SAlexandru Ardelean 	ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
282c336b611SAlexandru Ardelean 					      iio_pollfunc_store_time,
283194dc4c7STiberiu Breana 					      bma220_trigger_handler, NULL);
284194dc4c7STiberiu Breana 	if (ret < 0) {
285194dc4c7STiberiu Breana 		dev_err(&spi->dev, "iio triggered buffer setup failed\n");
286c336b611SAlexandru Ardelean 		return ret;
287194dc4c7STiberiu Breana 	}
288194dc4c7STiberiu Breana 
289c336b611SAlexandru Ardelean 	return devm_iio_device_register(&spi->dev, indio_dev);
290bf2a5600STiberiu Breana }
291bf2a5600STiberiu Breana 
bma220_suspend(struct device * dev)292*93a73f6aSJonathan Cameron static int bma220_suspend(struct device *dev)
293bf2a5600STiberiu Breana {
2943ce868bbSAlexandru Ardelean 	struct spi_device *spi = to_spi_device(dev);
295bf2a5600STiberiu Breana 
2963ce868bbSAlexandru Ardelean 	return bma220_power(spi, false);
297bf2a5600STiberiu Breana }
298bf2a5600STiberiu Breana 
bma220_resume(struct device * dev)299*93a73f6aSJonathan Cameron static int bma220_resume(struct device *dev)
300bf2a5600STiberiu Breana {
3013ce868bbSAlexandru Ardelean 	struct spi_device *spi = to_spi_device(dev);
302bf2a5600STiberiu Breana 
3033ce868bbSAlexandru Ardelean 	return bma220_power(spi, true);
304bf2a5600STiberiu Breana }
305*93a73f6aSJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume);
306bf2a5600STiberiu Breana 
307bf2a5600STiberiu Breana static const struct spi_device_id bma220_spi_id[] = {
308bf2a5600STiberiu Breana 	{"bma220", 0},
309bf2a5600STiberiu Breana 	{}
310bf2a5600STiberiu Breana };
311bf2a5600STiberiu Breana 
312bf2a5600STiberiu Breana static const struct acpi_device_id bma220_acpi_id[] = {
313bf2a5600STiberiu Breana 	{"BMA0220", 0},
314bf2a5600STiberiu Breana 	{}
315bf2a5600STiberiu Breana };
316bf2a5600STiberiu Breana MODULE_DEVICE_TABLE(spi, bma220_spi_id);
317bf2a5600STiberiu Breana 
318bf2a5600STiberiu Breana static struct spi_driver bma220_driver = {
319bf2a5600STiberiu Breana 	.driver = {
320bf2a5600STiberiu Breana 		.name = "bma220_spi",
321*93a73f6aSJonathan Cameron 		.pm = pm_sleep_ptr(&bma220_pm_ops),
322846afc1dSAndy Shevchenko 		.acpi_match_table = bma220_acpi_id,
323bf2a5600STiberiu Breana 	},
324bf2a5600STiberiu Breana 	.probe =            bma220_probe,
325bf2a5600STiberiu Breana 	.id_table =         bma220_spi_id,
326bf2a5600STiberiu Breana };
327bf2a5600STiberiu Breana module_spi_driver(bma220_driver);
328bf2a5600STiberiu Breana 
329bf2a5600STiberiu Breana MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
330bf2a5600STiberiu Breana MODULE_DESCRIPTION("BMA220 acceleration sensor driver");
331bf2a5600STiberiu Breana MODULE_LICENSE("GPL v2");
332