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