aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorTiberiu Breana <tiberiu.a.breana@intel.com>2016-05-05 11:48:55 -0400
committerJonathan Cameron <jic23@kernel.org>2016-05-14 13:43:45 -0400
commitbf2a5600a3ebc9dc5f085b47791009e25ade0157 (patch)
treeb17a7190e1f8bf4e90969c3d5e2c7a5a425c1a50 /drivers/iio
parent2763ac94f3e4d3711863729f4c11500d245f68cc (diff)
iio: accel: Add support for Bosch BMA220
This commit adds basic support for the Bosch Sensortec BMA220 digital triaxial acceleration sensor. The device datasheet can be found here: http://www.mouser.com/pdfdocs/BSTBMA220DS00308.PDF Includes: - raw readings - ACPI detection - power management Signed-off-by: Tiberiu Breana <tiberiu.a.breana@intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig10
-rw-r--r--drivers/iio/accel/Makefile1
-rw-r--r--drivers/iio/accel/bma220_spi.c277
3 files changed, 288 insertions, 0 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 1df6361a57fe..31325870a9ef 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -17,6 +17,16 @@ config BMA180
17 To compile this driver as a module, choose M here: the 17 To compile this driver as a module, choose M here: the
18 module will be called bma180. 18 module will be called bma180.
19 19
20config BMA220
21 tristate "Bosch BMA220 3-Axis Accelerometer Driver"
22 depends on SPI
23 help
24 Say yes here to add support for the Bosch BMA220 triaxial
25 acceleration sensor.
26
27 To compile this driver as a module, choose M here: the
28 module will be called bma220_spi.
29
20config BMC150_ACCEL 30config BMC150_ACCEL
21 tristate "Bosch BMC150 Accelerometer Driver" 31 tristate "Bosch BMC150 Accelerometer Driver"
22 select IIO_BUFFER 32 select IIO_BUFFER
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index ba1165f54ec7..6cedbecca2ee 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -4,6 +4,7 @@
4 4
5# When adding new entries keep the list in alphabetical order 5# When adding new entries keep the list in alphabetical order
6obj-$(CONFIG_BMA180) += bma180.o 6obj-$(CONFIG_BMA180) += bma180.o
7obj-$(CONFIG_BMA220) += bma220_spi.o
7obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o 8obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
8obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o 9obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
9obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o 10obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c
new file mode 100644
index 000000000000..7343575e84ba
--- /dev/null
+++ b/drivers/iio/accel/bma220_spi.c
@@ -0,0 +1,277 @@
1/**
2 * BMA220 Digital triaxial acceleration sensor driver
3 *
4 * Copyright (c) 2016, Intel Corporation.
5 *
6 * This file is subject to the terms and conditions of version 2 of
7 * the GNU General Public License. See the file COPYING in the main
8 * directory of this archive for more details.
9 */
10
11#include <linux/acpi.h>
12#include <linux/kernel.h>
13#include <linux/module.h>
14#include <linux/iio/iio.h>
15#include <linux/iio/sysfs.h>
16#include <linux/spi/spi.h>
17
18#define BMA220_REG_ID 0x00
19#define BMA220_REG_ACCEL_X 0x02
20#define BMA220_REG_ACCEL_Y 0x03
21#define BMA220_REG_ACCEL_Z 0x04
22#define BMA220_REG_RANGE 0x11
23#define BMA220_REG_SUSPEND 0x18
24
25#define BMA220_CHIP_ID 0xDD
26#define BMA220_READ_MASK 0x80
27#define BMA220_RANGE_MASK 0x03
28#define BMA220_DATA_SHIFT 2
29#define BMA220_SUSPEND_SLEEP 0xFF
30#define BMA220_SUSPEND_WAKE 0x00
31
32#define BMA220_DEVICE_NAME "bma220"
33#define BMA220_SCALE_AVAILABLE "0.623 1.248 2.491 4.983"
34
35#define BMA220_ACCEL_CHANNEL(index, reg, axis) { \
36 .type = IIO_ACCEL, \
37 .address = reg, \
38 .modified = 1, \
39 .channel2 = IIO_MOD_##axis, \
40 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
41 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
42}
43
44static IIO_CONST_ATTR(in_accel_scale_available, BMA220_SCALE_AVAILABLE);
45
46static struct attribute *bma220_attributes[] = {
47 &iio_const_attr_in_accel_scale_available.dev_attr.attr,
48 NULL,
49};
50
51static const struct attribute_group bma220_attribute_group = {
52 .attrs = bma220_attributes,
53};
54
55static const int bma220_scale_table[][4] = {
56 {0, 623000}, {1, 248000}, {2, 491000}, {4, 983000}
57};
58
59struct bma220_data {
60 struct spi_device *spi_device;
61 struct mutex lock;
62 u8 tx_buf[2] ____cacheline_aligned;
63};
64
65static const struct iio_chan_spec bma220_channels[] = {
66 BMA220_ACCEL_CHANNEL(0, BMA220_REG_ACCEL_X, X),
67 BMA220_ACCEL_CHANNEL(1, BMA220_REG_ACCEL_Y, Y),
68 BMA220_ACCEL_CHANNEL(2, BMA220_REG_ACCEL_Z, Z),
69};
70
71static inline int bma220_read_reg(struct spi_device *spi, u8 reg)
72{
73 return spi_w8r8(spi, reg | BMA220_READ_MASK);
74}
75
76static int bma220_read_raw(struct iio_dev *indio_dev,
77 struct iio_chan_spec const *chan,
78 int *val, int *val2, long mask)
79{
80 int ret;
81 u8 range_idx;
82 struct bma220_data *data = iio_priv(indio_dev);
83
84 switch (mask) {
85 case IIO_CHAN_INFO_RAW:
86 ret = bma220_read_reg(data->spi_device, chan->address);
87 if (ret < 0)
88 return -EINVAL;
89 *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5);
90 return IIO_VAL_INT;
91 case IIO_CHAN_INFO_SCALE:
92 ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE);
93 if (ret < 0)
94 return ret;
95 range_idx = ret & BMA220_RANGE_MASK;
96 *val = bma220_scale_table[range_idx][0];
97 *val2 = bma220_scale_table[range_idx][1];
98 return IIO_VAL_INT_PLUS_MICRO;
99 }
100
101 return -EINVAL;
102}
103
104static int bma220_write_raw(struct iio_dev *indio_dev,
105 struct iio_chan_spec const *chan,
106 int val, int val2, long mask)
107{
108 int i;
109 int ret;
110 int index = -1;
111 struct bma220_data *data = iio_priv(indio_dev);
112
113 switch (mask) {
114 case IIO_CHAN_INFO_SCALE:
115 for (i = 0; i < ARRAY_SIZE(bma220_scale_table); i++)
116 if (val == bma220_scale_table[i][0] &&
117 val2 == bma220_scale_table[i][1]) {
118 index = i;
119 break;
120 }
121 if (index < 0)
122 return -EINVAL;
123
124 mutex_lock(&data->lock);
125 data->tx_buf[0] = BMA220_REG_RANGE;
126 data->tx_buf[1] = index;
127 ret = spi_write(data->spi_device, data->tx_buf,
128 sizeof(data->tx_buf));
129 if (ret < 0)
130 dev_err(&data->spi_device->dev,
131 "failed to set measurement range\n");
132 mutex_unlock(&data->lock);
133
134 return 0;
135 }
136
137 return -EINVAL;
138}
139
140static const struct iio_info bma220_info = {
141 .driver_module = THIS_MODULE,
142 .read_raw = bma220_read_raw,
143 .write_raw = bma220_write_raw,
144 .attrs = &bma220_attribute_group,
145};
146
147static int bma220_init(struct spi_device *spi)
148{
149 int ret;
150
151 ret = bma220_read_reg(spi, BMA220_REG_ID);
152 if (ret != BMA220_CHIP_ID)
153 return -ENODEV;
154
155 /* Make sure the chip is powered on */
156 ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
157 if (ret < 0)
158 return ret;
159 else if (ret == BMA220_SUSPEND_WAKE)
160 return bma220_read_reg(spi, BMA220_REG_SUSPEND);
161
162 return 0;
163}
164
165static int bma220_deinit(struct spi_device *spi)
166{
167 int ret;
168
169 /* Make sure the chip is powered off */
170 ret = bma220_read_reg(spi, BMA220_REG_SUSPEND);
171 if (ret < 0)
172 return ret;
173 else if (ret == BMA220_SUSPEND_SLEEP)
174 return bma220_read_reg(spi, BMA220_REG_SUSPEND);
175
176 return 0;
177}
178
179static int bma220_probe(struct spi_device *spi)
180{
181 int ret;
182 struct iio_dev *indio_dev;
183 struct bma220_data *data;
184
185 indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
186 if (!indio_dev) {
187 dev_err(&spi->dev, "iio allocation failed!\n");
188 return -ENOMEM;
189 }
190
191 data = iio_priv(indio_dev);
192 data->spi_device = spi;
193 spi_set_drvdata(spi, indio_dev);
194 mutex_init(&data->lock);
195
196 indio_dev->dev.parent = &spi->dev;
197 indio_dev->info = &bma220_info;
198 indio_dev->name = BMA220_DEVICE_NAME;
199 indio_dev->modes = INDIO_DIRECT_MODE;
200 indio_dev->channels = bma220_channels;
201 indio_dev->num_channels = ARRAY_SIZE(bma220_channels);
202
203 ret = bma220_init(data->spi_device);
204 if (ret < 0)
205 return ret;
206
207 ret = iio_device_register(indio_dev);
208 if (ret < 0) {
209 dev_err(&spi->dev, "iio_device_register failed\n");
210 return bma220_deinit(spi);
211 }
212
213 return ret;
214}
215
216static int bma220_remove(struct spi_device *spi)
217{
218 struct iio_dev *indio_dev = spi_get_drvdata(spi);
219
220 iio_device_unregister(indio_dev);
221
222 return bma220_deinit(spi);
223}
224
225#ifdef CONFIG_PM_SLEEP
226static int bma220_suspend(struct device *dev)
227{
228 struct bma220_data *data =
229 iio_priv(spi_get_drvdata(to_spi_device(dev)));
230
231 /* The chip can be suspended/woken up by a simple register read. */
232 return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND);
233}
234
235static int bma220_resume(struct device *dev)
236{
237 struct bma220_data *data =
238 iio_priv(spi_get_drvdata(to_spi_device(dev)));
239
240 return bma220_read_reg(data->spi_device, BMA220_REG_SUSPEND);
241}
242
243static SIMPLE_DEV_PM_OPS(bma220_pm_ops, bma220_suspend, bma220_resume);
244
245#define BMA220_PM_OPS (&bma220_pm_ops)
246#else
247#define BMA220_PM_OPS NULL
248#endif
249
250static const struct spi_device_id bma220_spi_id[] = {
251 {"bma220", 0},
252 {}
253};
254
255static const struct acpi_device_id bma220_acpi_id[] = {
256 {"BMA0220", 0},
257 {}
258};
259
260MODULE_DEVICE_TABLE(spi, bma220_spi_id);
261
262static struct spi_driver bma220_driver = {
263 .driver = {
264 .name = "bma220_spi",
265 .pm = BMA220_PM_OPS,
266 .acpi_match_table = ACPI_PTR(bma220_acpi_id),
267 },
268 .probe = bma220_probe,
269 .remove = bma220_remove,
270 .id_table = bma220_spi_id,
271};
272
273module_spi_driver(bma220_driver);
274
275MODULE_AUTHOR("Tiberiu Breana <tiberiu.a.breana@intel.com>");
276MODULE_DESCRIPTION("BMA220 acceleration sensor driver");
277MODULE_LICENSE("GPL v2");