aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorPeter Meerwald <pmeerw@pmeerw.net>2014-02-05 04:51:00 -0500
committerJonathan Cameron <jic23@kernel.org>2014-05-03 07:06:25 -0400
commitc7eeea93ac60aba3c037af8933a7ffc96ccd495c (patch)
tree0009ddacc7103bed21bb67fab7efef1ada7e8371 /drivers/iio
parent88cece46acb88c4032d34b8b52796e08a99c34d2 (diff)
iio: Add Freescale MMA8452Q 3-axis accelerometer driver
3-axis accelerometer sensor (2/4/8 g) with 12-bit resolution and I2C interface many extra features are unsupported (freefall detection, orientation change, autosleep) datasheet is here: http://cache.freescale.com/files/sensors/doc/data_sheet/MMA8452Q.pdf v2: (thanks to Jonathan Cameron) * use ARRAY_SIZE() Signed-off-by: Peter Meerwald <pmeerw@pmeerw.net> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig12
-rw-r--r--drivers/iio/accel/Makefile1
-rw-r--r--drivers/iio/accel/mma8452.c439
3 files changed, 452 insertions, 0 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index e23e50850655..1e120fa1e156 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -65,4 +65,16 @@ config KXSD9
65 Say yes here to build support for the Kionix KXSD9 accelerometer. 65 Say yes here to build support for the Kionix KXSD9 accelerometer.
66 Currently this only supports the device via an SPI interface. 66 Currently this only supports the device via an SPI interface.
67 67
68config MMA8452
69 tristate "Freescale MMA8452Q Accelerometer Driver"
70 depends on I2C
71 select IIO_BUFFER
72 select IIO_TRIGGERED_BUFFER
73 help
74 Say yes here to build support for the Freescale MMA8452Q 3-axis
75 accelerometer.
76
77 To compile this driver as a module, choose M here: the module
78 will be called mma8452.
79
68endmenu 80endmenu
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index c48d15f25616..dc0e379c2592 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -6,6 +6,7 @@
6obj-$(CONFIG_BMA180) += bma180.o 6obj-$(CONFIG_BMA180) += bma180.o
7obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o 7obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
8obj-$(CONFIG_KXSD9) += kxsd9.o 8obj-$(CONFIG_KXSD9) += kxsd9.o
9obj-$(CONFIG_MMA8452) += mma8452.o
9 10
10obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o 11obj-$(CONFIG_IIO_ST_ACCEL_3AXIS) += st_accel.o
11st_accel-y := st_accel_core.o 12st_accel-y := st_accel_core.o
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
new file mode 100644
index 000000000000..17aeea170566
--- /dev/null
+++ b/drivers/iio/accel/mma8452.c
@@ -0,0 +1,439 @@
1/*
2 * mma8452.c - Support for Freescale MMA8452Q 3-axis 12-bit accelerometer
3 *
4 * Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
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 * 7-bit I2C slave address 0x1c/0x1d (pin selectable)
11 *
12 * TODO: interrupt, thresholding, orientation / freefall events, autosleep
13 */
14
15#include <linux/module.h>
16#include <linux/i2c.h>
17#include <linux/iio/iio.h>
18#include <linux/iio/sysfs.h>
19#include <linux/iio/trigger_consumer.h>
20#include <linux/iio/buffer.h>
21#include <linux/iio/triggered_buffer.h>
22#include <linux/delay.h>
23
24#define MMA8452_STATUS 0x00
25#define MMA8452_OUT_X 0x01 /* MSB first, 12-bit */
26#define MMA8452_OUT_Y 0x03
27#define MMA8452_OUT_Z 0x05
28#define MMA8452_WHO_AM_I 0x0d
29#define MMA8452_DATA_CFG 0x0e
30#define MMA8452_OFF_X 0x2f
31#define MMA8452_OFF_Y 0x30
32#define MMA8452_OFF_Z 0x31
33#define MMA8452_CTRL_REG1 0x2a
34#define MMA8452_CTRL_REG2 0x2b
35
36#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
37
38#define MMA8452_CTRL_DR_MASK (BIT(5) | BIT(4) | BIT(3))
39#define MMA8452_CTRL_DR_SHIFT 3
40#define MMA8452_CTRL_DR_DEFAULT 0x4 /* 50 Hz sample frequency */
41#define MMA8452_CTRL_ACTIVE BIT(0)
42
43#define MMA8452_DATA_CFG_FS_MASK (BIT(1) | BIT(0))
44#define MMA8452_DATA_CFG_FS_2G 0
45#define MMA8452_DATA_CFG_FS_4G 1
46#define MMA8452_DATA_CFG_FS_8G 2
47
48#define MMA8452_DEVICE_ID 0x2a
49
50struct mma8452_data {
51 struct i2c_client *client;
52 struct mutex lock;
53 u8 ctrl_reg1;
54 u8 data_cfg;
55};
56
57static int mma8452_drdy(struct mma8452_data *data)
58{
59 int tries = 150;
60
61 while (tries-- > 0) {
62 int ret = i2c_smbus_read_byte_data(data->client,
63 MMA8452_STATUS);
64 if (ret < 0)
65 return ret;
66 if ((ret & MMA8452_STATUS_DRDY) == MMA8452_STATUS_DRDY)
67 return 0;
68 msleep(20);
69 }
70
71 dev_err(&data->client->dev, "data not ready\n");
72 return -EIO;
73}
74
75static int mma8452_read(struct mma8452_data *data, __be16 buf[3])
76{
77 int ret = mma8452_drdy(data);
78 if (ret < 0)
79 return ret;
80 return i2c_smbus_read_i2c_block_data(data->client,
81 MMA8452_OUT_X, 3 * sizeof(__be16), (u8 *) buf);
82}
83
84static ssize_t mma8452_show_int_plus_micros(char *buf,
85 const int (*vals)[2], int n)
86{
87 size_t len = 0;
88
89 while (n-- > 0)
90 len += scnprintf(buf + len, PAGE_SIZE - len,
91 "%d.%06d ", vals[n][0], vals[n][1]);
92
93 /* replace trailing space by newline */
94 buf[len - 1] = '\n';
95
96 return len;
97}
98
99static int mma8452_get_int_plus_micros_index(const int (*vals)[2], int n,
100 int val, int val2)
101{
102 while (n-- > 0)
103 if (val == vals[n][0] && val2 == vals[n][1])
104 return n;
105
106 return -EINVAL;
107}
108
109static const int mma8452_samp_freq[8][2] = {
110 {800, 0}, {400, 0}, {200, 0}, {100, 0}, {50, 0}, {12, 500000},
111 {6, 250000}, {1, 560000}
112};
113
114static const int mma8452_scales[3][2] = {
115 {0, 977}, {0, 1953}, {0, 3906}
116};
117
118static ssize_t mma8452_show_samp_freq_avail(struct device *dev,
119 struct device_attribute *attr, char *buf)
120{
121 return mma8452_show_int_plus_micros(buf, mma8452_samp_freq,
122 ARRAY_SIZE(mma8452_samp_freq));
123}
124
125static ssize_t mma8452_show_scale_avail(struct device *dev,
126 struct device_attribute *attr, char *buf)
127{
128 return mma8452_show_int_plus_micros(buf, mma8452_scales,
129 ARRAY_SIZE(mma8452_scales));
130}
131
132static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(mma8452_show_samp_freq_avail);
133static IIO_DEVICE_ATTR(in_accel_scale_available, S_IRUGO,
134 mma8452_show_scale_avail, NULL, 0);
135
136static int mma8452_get_samp_freq_index(struct mma8452_data *data,
137 int val, int val2)
138{
139 return mma8452_get_int_plus_micros_index(mma8452_samp_freq,
140 ARRAY_SIZE(mma8452_samp_freq), val, val2);
141}
142
143static int mma8452_get_scale_index(struct mma8452_data *data,
144 int val, int val2)
145{
146 return mma8452_get_int_plus_micros_index(mma8452_scales,
147 ARRAY_SIZE(mma8452_scales), val, val2);
148}
149
150static int mma8452_read_raw(struct iio_dev *indio_dev,
151 struct iio_chan_spec const *chan,
152 int *val, int *val2, long mask)
153{
154 struct mma8452_data *data = iio_priv(indio_dev);
155 __be16 buffer[3];
156 int i, ret;
157
158 switch (mask) {
159 case IIO_CHAN_INFO_RAW:
160 if (iio_buffer_enabled(indio_dev))
161 return -EBUSY;
162
163 mutex_lock(&data->lock);
164 ret = mma8452_read(data, buffer);
165 mutex_unlock(&data->lock);
166 if (ret < 0)
167 return ret;
168 *val = sign_extend32(
169 be16_to_cpu(buffer[chan->scan_index]) >> 4, 11);
170 return IIO_VAL_INT;
171 case IIO_CHAN_INFO_SCALE:
172 i = data->data_cfg & MMA8452_DATA_CFG_FS_MASK;
173 *val = mma8452_scales[i][0];
174 *val2 = mma8452_scales[i][1];
175 return IIO_VAL_INT_PLUS_MICRO;
176 case IIO_CHAN_INFO_SAMP_FREQ:
177 i = (data->ctrl_reg1 & MMA8452_CTRL_DR_MASK) >>
178 MMA8452_CTRL_DR_SHIFT;
179 *val = mma8452_samp_freq[i][0];
180 *val2 = mma8452_samp_freq[i][1];
181 return IIO_VAL_INT_PLUS_MICRO;
182 case IIO_CHAN_INFO_CALIBBIAS:
183 ret = i2c_smbus_read_byte_data(data->client, MMA8452_OFF_X +
184 chan->scan_index);
185 if (ret < 0)
186 return ret;
187 *val = sign_extend32(ret, 7);
188 return IIO_VAL_INT;
189 }
190 return -EINVAL;
191}
192
193static int mma8452_standby(struct mma8452_data *data)
194{
195 return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
196 data->ctrl_reg1 & ~MMA8452_CTRL_ACTIVE);
197}
198
199static int mma8452_active(struct mma8452_data *data)
200{
201 return i2c_smbus_write_byte_data(data->client, MMA8452_CTRL_REG1,
202 data->ctrl_reg1);
203}
204
205static int mma8452_change_config(struct mma8452_data *data, u8 reg, u8 val)
206{
207 int ret;
208
209 mutex_lock(&data->lock);
210
211 /* config can only be changed when in standby */
212 ret = mma8452_standby(data);
213 if (ret < 0)
214 goto fail;
215
216 ret = i2c_smbus_write_byte_data(data->client, reg, val);
217 if (ret < 0)
218 goto fail;
219
220 ret = mma8452_active(data);
221 if (ret < 0)
222 goto fail;
223
224 ret = 0;
225fail:
226 mutex_unlock(&data->lock);
227 return ret;
228}
229
230static int mma8452_write_raw(struct iio_dev *indio_dev,
231 struct iio_chan_spec const *chan,
232 int val, int val2, long mask)
233{
234 struct mma8452_data *data = iio_priv(indio_dev);
235 int i;
236
237 if (iio_buffer_enabled(indio_dev))
238 return -EBUSY;
239
240 switch (mask) {
241 case IIO_CHAN_INFO_SAMP_FREQ:
242 i = mma8452_get_samp_freq_index(data, val, val2);
243 if (i < 0)
244 return -EINVAL;
245
246 data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
247 data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
248 return mma8452_change_config(data, MMA8452_CTRL_REG1,
249 data->ctrl_reg1);
250 case IIO_CHAN_INFO_SCALE:
251 i = mma8452_get_scale_index(data, val, val2);
252 if (i < 0)
253 return -EINVAL;
254 data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
255 data->data_cfg |= i;
256 return mma8452_change_config(data, MMA8452_DATA_CFG,
257 data->data_cfg);
258 case IIO_CHAN_INFO_CALIBBIAS:
259 if (val < -128 || val > 127)
260 return -EINVAL;
261 return mma8452_change_config(data, MMA8452_OFF_X +
262 chan->scan_index, val);
263 default:
264 return -EINVAL;
265 }
266}
267
268static irqreturn_t mma8452_trigger_handler(int irq, void *p)
269{
270 struct iio_poll_func *pf = p;
271 struct iio_dev *indio_dev = pf->indio_dev;
272 struct mma8452_data *data = iio_priv(indio_dev);
273 u8 buffer[16]; /* 3 16-bit channels + padding + ts */
274 int ret;
275
276 ret = mma8452_read(data, (__be16 *) buffer);
277 if (ret < 0)
278 goto done;
279
280 iio_push_to_buffers_with_timestamp(indio_dev, buffer,
281 iio_get_time_ns());
282
283done:
284 iio_trigger_notify_done(indio_dev->trig);
285 return IRQ_HANDLED;
286}
287
288#define MMA8452_CHANNEL(axis, idx) { \
289 .type = IIO_ACCEL, \
290 .modified = 1, \
291 .channel2 = IIO_MOD_##axis, \
292 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
293 BIT(IIO_CHAN_INFO_CALIBBIAS), \
294 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
295 BIT(IIO_CHAN_INFO_SCALE), \
296 .scan_index = idx, \
297 .scan_type = { \
298 .sign = 's', \
299 .realbits = 12, \
300 .storagebits = 16, \
301 .shift = 4, \
302 .endianness = IIO_BE, \
303 }, \
304}
305
306static const struct iio_chan_spec mma8452_channels[] = {
307 MMA8452_CHANNEL(X, 0),
308 MMA8452_CHANNEL(Y, 1),
309 MMA8452_CHANNEL(Z, 2),
310 IIO_CHAN_SOFT_TIMESTAMP(3),
311};
312
313static struct attribute *mma8452_attributes[] = {
314 &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
315 &iio_dev_attr_in_accel_scale_available.dev_attr.attr,
316 NULL
317};
318
319static const struct attribute_group mma8452_group = {
320 .attrs = mma8452_attributes,
321};
322
323static const struct iio_info mma8452_info = {
324 .attrs = &mma8452_group,
325 .read_raw = &mma8452_read_raw,
326 .write_raw = &mma8452_write_raw,
327 .driver_module = THIS_MODULE,
328};
329
330static const unsigned long mma8452_scan_masks[] = {0x7, 0};
331
332static int mma8452_probe(struct i2c_client *client,
333 const struct i2c_device_id *id)
334{
335 struct mma8452_data *data;
336 struct iio_dev *indio_dev;
337 int ret;
338
339 ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
340 if (ret < 0)
341 return ret;
342 if (ret != MMA8452_DEVICE_ID)
343 return -ENODEV;
344
345 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
346 if (!indio_dev)
347 return -ENOMEM;
348
349 data = iio_priv(indio_dev);
350 data->client = client;
351 mutex_init(&data->lock);
352
353 i2c_set_clientdata(client, indio_dev);
354 indio_dev->info = &mma8452_info;
355 indio_dev->name = id->name;
356 indio_dev->dev.parent = &client->dev;
357 indio_dev->modes = INDIO_DIRECT_MODE;
358 indio_dev->channels = mma8452_channels;
359 indio_dev->num_channels = ARRAY_SIZE(mma8452_channels);
360 indio_dev->available_scan_masks = mma8452_scan_masks;
361
362 data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
363 (MMA8452_CTRL_DR_DEFAULT << MMA8452_CTRL_DR_SHIFT);
364 ret = i2c_smbus_write_byte_data(client, MMA8452_CTRL_REG1,
365 data->ctrl_reg1);
366 if (ret < 0)
367 return ret;
368
369 data->data_cfg = MMA8452_DATA_CFG_FS_2G;
370 ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
371 data->data_cfg);
372 if (ret < 0)
373 return ret;
374
375 ret = iio_triggered_buffer_setup(indio_dev, NULL,
376 mma8452_trigger_handler, NULL);
377 if (ret < 0)
378 return ret;
379
380 ret = iio_device_register(indio_dev);
381 if (ret < 0)
382 goto buffer_cleanup;
383 return 0;
384
385buffer_cleanup:
386 iio_triggered_buffer_cleanup(indio_dev);
387 return ret;
388}
389
390static int mma8452_remove(struct i2c_client *client)
391{
392 struct iio_dev *indio_dev = i2c_get_clientdata(client);
393
394 iio_device_unregister(indio_dev);
395 iio_triggered_buffer_cleanup(indio_dev);
396 mma8452_standby(iio_priv(indio_dev));
397
398 return 0;
399}
400
401#ifdef CONFIG_PM_SLEEP
402static int mma8452_suspend(struct device *dev)
403{
404 return mma8452_standby(iio_priv(i2c_get_clientdata(
405 to_i2c_client(dev))));
406}
407
408static int mma8452_resume(struct device *dev)
409{
410 return mma8452_active(iio_priv(i2c_get_clientdata(
411 to_i2c_client(dev))));
412}
413
414static SIMPLE_DEV_PM_OPS(mma8452_pm_ops, mma8452_suspend, mma8452_resume);
415#define MMA8452_PM_OPS (&mma8452_pm_ops)
416#else
417#define MMA8452_PM_OPS NULL
418#endif
419
420static const struct i2c_device_id mma8452_id[] = {
421 { "mma8452", 0 },
422 { }
423};
424MODULE_DEVICE_TABLE(i2c, mma8452_id);
425
426static struct i2c_driver mma8452_driver = {
427 .driver = {
428 .name = "mma8452",
429 .pm = MMA8452_PM_OPS,
430 },
431 .probe = mma8452_probe,
432 .remove = mma8452_remove,
433 .id_table = mma8452_id,
434};
435module_i2c_driver(mma8452_driver);
436
437MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
438MODULE_DESCRIPTION("Freescale MMA8452 accelerometer driver");
439MODULE_LICENSE("GPL");