Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

iio: accel: add Freescale MMA7455L/MMA7456L 3-axis accelerometer driver

Add support for Freescale MMA7455L/MMA7456L 3-axis in 10-bit mode for
I2C and SPI bus. This rather simple driver that currently doesn't
support all the hardware features of MMA7455L/MMA7456L.

Tested on Embedded Artist's LPC4357 Dev Kit with MMA7455L on I2C bus.

Data sheets for the two devices can be found here:
http://cache.freescale.com/files/sensors/doc/data_sheet/MMA7455L.pdf
http://cache.freescale.com/files/sensors/doc/data_sheet/MMA7456L.pdf

Signed-off-by: Joachim Eastwood <manabian@gmail.com>
Signed-off-by: Jonathan Cameron <jic23@kernel.org>

authored by

Joachim Eastwood and committed by
Jonathan Cameron
a84ef0d1 18fb1ab0

+472
+29
drivers/iio/accel/Kconfig
··· 107 107 To compile this driver as a module, choose M here: the module will 108 108 be called kxcjk-1013. 109 109 110 + config MMA7455 111 + tristate 112 + select IIO_BUFFER 113 + select IIO_TRIGGERED_BUFFER 114 + 115 + config MMA7455_I2C 116 + tristate "Freescale MMA7455L/MMA7456L Accelerometer I2C Driver" 117 + depends on I2C 118 + select MMA7455 119 + select REGMAP_I2C 120 + help 121 + Say yes here to build support for the Freescale MMA7455L and 122 + MMA7456L 3-axis accelerometer. 123 + 124 + To compile this driver as a module, choose M here: the module 125 + will be called mma7455_i2c. 126 + 127 + config MMA7455_SPI 128 + tristate "Freescale MMA7455L/MMA7456L Accelerometer SPI Driver" 129 + depends on SPI_MASTER 130 + select MMA7455 131 + select REGMAP_SPI 132 + help 133 + Say yes here to build support for the Freescale MMA7455L and 134 + MMA7456L 3-axis accelerometer. 135 + 136 + To compile this driver as a module, choose M here: the module 137 + will be called mma7455_spi. 138 + 110 139 config MMA8452 111 140 tristate "Freescale MMA8452Q and similar Accelerometers Driver" 112 141 depends on I2C
+5
drivers/iio/accel/Makefile
··· 10 10 obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o 11 11 obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o 12 12 obj-$(CONFIG_KXSD9) += kxsd9.o 13 + 14 + obj-$(CONFIG_MMA7455) += mma7455_core.o 15 + obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o 16 + obj-$(CONFIG_MMA7455_SPI) += mma7455_spi.o 17 + 13 18 obj-$(CONFIG_MMA8452) += mma8452.o 14 19 15 20 obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
+19
drivers/iio/accel/mma7455.h
··· 1 + /* 2 + * IIO accel driver for Freescale MMA7455L 3-axis 10-bit accelerometer 3 + * Copyright 2015 Joachim Eastwood <manabian@gmail.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 as 7 + * published by the Free Software Foundation. 8 + */ 9 + 10 + #ifndef __MMA7455_H 11 + #define __MMA7455_H 12 + 13 + extern const struct regmap_config mma7455_core_regmap; 14 + 15 + int mma7455_core_probe(struct device *dev, struct regmap *regmap, 16 + const char *name); 17 + int mma7455_core_remove(struct device *dev); 18 + 19 + #endif
+311
drivers/iio/accel/mma7455_core.c
··· 1 + /* 2 + * IIO accel core driver for Freescale MMA7455L 3-axis 10-bit accelerometer 3 + * Copyright 2015 Joachim Eastwood <manabian@gmail.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 as 7 + * published by the Free Software Foundation. 8 + * 9 + * UNSUPPORTED hardware features: 10 + * - 8-bit mode with different scales 11 + * - INT1/INT2 interrupts 12 + * - Offset calibration 13 + * - Events 14 + */ 15 + 16 + #include <linux/delay.h> 17 + #include <linux/iio/iio.h> 18 + #include <linux/iio/sysfs.h> 19 + #include <linux/iio/buffer.h> 20 + #include <linux/iio/trigger.h> 21 + #include <linux/iio/trigger_consumer.h> 22 + #include <linux/iio/triggered_buffer.h> 23 + #include <linux/module.h> 24 + #include <linux/regmap.h> 25 + 26 + #include "mma7455.h" 27 + 28 + #define MMA7455_REG_XOUTL 0x00 29 + #define MMA7455_REG_XOUTH 0x01 30 + #define MMA7455_REG_YOUTL 0x02 31 + #define MMA7455_REG_YOUTH 0x03 32 + #define MMA7455_REG_ZOUTL 0x04 33 + #define MMA7455_REG_ZOUTH 0x05 34 + #define MMA7455_REG_STATUS 0x09 35 + #define MMA7455_STATUS_DRDY BIT(0) 36 + #define MMA7455_REG_WHOAMI 0x0f 37 + #define MMA7455_WHOAMI_ID 0x55 38 + #define MMA7455_REG_MCTL 0x16 39 + #define MMA7455_MCTL_MODE_STANDBY 0x00 40 + #define MMA7455_MCTL_MODE_MEASURE 0x01 41 + #define MMA7455_REG_CTL1 0x18 42 + #define MMA7455_CTL1_DFBW_MASK BIT(7) 43 + #define MMA7455_CTL1_DFBW_125HZ BIT(7) 44 + #define MMA7455_CTL1_DFBW_62_5HZ 0 45 + #define MMA7455_REG_TW 0x1e 46 + 47 + /* 48 + * When MMA7455 is used in 10-bit it has a fullscale of -8g 49 + * corresponding to raw value -512. The userspace interface 50 + * uses m/s^2 and we declare micro units. 51 + * So scale factor is given by: 52 + * g * 8 * 1e6 / 512 = 153228.90625, with g = 9.80665 53 + */ 54 + #define MMA7455_10BIT_SCALE 153229 55 + 56 + struct mma7455_data { 57 + struct regmap *regmap; 58 + struct device *dev; 59 + }; 60 + 61 + static int mma7455_drdy(struct mma7455_data *mma7455) 62 + { 63 + unsigned int reg; 64 + int tries = 3; 65 + int ret; 66 + 67 + while (tries-- > 0) { 68 + ret = regmap_read(mma7455->regmap, MMA7455_REG_STATUS, &reg); 69 + if (ret) 70 + return ret; 71 + 72 + if (reg & MMA7455_STATUS_DRDY) 73 + return 0; 74 + 75 + msleep(20); 76 + } 77 + 78 + dev_warn(mma7455->dev, "data not ready\n"); 79 + 80 + return -EIO; 81 + } 82 + 83 + static irqreturn_t mma7455_trigger_handler(int irq, void *p) 84 + { 85 + struct iio_poll_func *pf = p; 86 + struct iio_dev *indio_dev = pf->indio_dev; 87 + struct mma7455_data *mma7455 = iio_priv(indio_dev); 88 + u8 buf[16]; /* 3 x 16-bit channels + padding + ts */ 89 + int ret; 90 + 91 + ret = mma7455_drdy(mma7455); 92 + if (ret) 93 + goto done; 94 + 95 + ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL, buf, 96 + sizeof(__le16) * 3); 97 + if (ret) 98 + goto done; 99 + 100 + iio_push_to_buffers_with_timestamp(indio_dev, buf, iio_get_time_ns()); 101 + 102 + done: 103 + iio_trigger_notify_done(indio_dev->trig); 104 + 105 + return IRQ_HANDLED; 106 + } 107 + 108 + static int mma7455_read_raw(struct iio_dev *indio_dev, 109 + struct iio_chan_spec const *chan, 110 + int *val, int *val2, long mask) 111 + { 112 + struct mma7455_data *mma7455 = iio_priv(indio_dev); 113 + unsigned int reg; 114 + __le16 data; 115 + int ret; 116 + 117 + switch (mask) { 118 + case IIO_CHAN_INFO_RAW: 119 + if (iio_buffer_enabled(indio_dev)) 120 + return -EBUSY; 121 + 122 + ret = mma7455_drdy(mma7455); 123 + if (ret) 124 + return ret; 125 + 126 + ret = regmap_bulk_read(mma7455->regmap, chan->address, &data, 127 + sizeof(data)); 128 + if (ret) 129 + return ret; 130 + 131 + *val = sign_extend32(le16_to_cpu(data), 9); 132 + 133 + return IIO_VAL_INT; 134 + 135 + case IIO_CHAN_INFO_SCALE: 136 + *val = 0; 137 + *val2 = MMA7455_10BIT_SCALE; 138 + 139 + return IIO_VAL_INT_PLUS_MICRO; 140 + 141 + case IIO_CHAN_INFO_SAMP_FREQ: 142 + ret = regmap_read(mma7455->regmap, MMA7455_REG_CTL1, &reg); 143 + if (ret) 144 + return ret; 145 + 146 + if (reg & MMA7455_CTL1_DFBW_MASK) 147 + *val = 250; 148 + else 149 + *val = 125; 150 + 151 + return IIO_VAL_INT; 152 + } 153 + 154 + return -EINVAL; 155 + } 156 + 157 + static int mma7455_write_raw(struct iio_dev *indio_dev, 158 + struct iio_chan_spec const *chan, 159 + int val, int val2, long mask) 160 + { 161 + struct mma7455_data *mma7455 = iio_priv(indio_dev); 162 + int i; 163 + 164 + switch (mask) { 165 + case IIO_CHAN_INFO_SAMP_FREQ: 166 + if (val == 250 && val2 == 0) 167 + i = MMA7455_CTL1_DFBW_125HZ; 168 + else if (val == 125 && val2 == 0) 169 + i = MMA7455_CTL1_DFBW_62_5HZ; 170 + else 171 + return -EINVAL; 172 + 173 + return regmap_update_bits(mma7455->regmap, MMA7455_REG_CTL1, 174 + MMA7455_CTL1_DFBW_MASK, i); 175 + 176 + case IIO_CHAN_INFO_SCALE: 177 + /* In 10-bit mode there is only one scale available */ 178 + if (val == 0 && val2 == MMA7455_10BIT_SCALE) 179 + return 0; 180 + break; 181 + } 182 + 183 + return -EINVAL; 184 + } 185 + 186 + static IIO_CONST_ATTR(sampling_frequency_available, "125 250"); 187 + 188 + static struct attribute *mma7455_attributes[] = { 189 + &iio_const_attr_sampling_frequency_available.dev_attr.attr, 190 + NULL 191 + }; 192 + 193 + static const struct attribute_group mma7455_group = { 194 + .attrs = mma7455_attributes, 195 + }; 196 + 197 + static const struct iio_info mma7455_info = { 198 + .attrs = &mma7455_group, 199 + .read_raw = mma7455_read_raw, 200 + .write_raw = mma7455_write_raw, 201 + .driver_module = THIS_MODULE, 202 + }; 203 + 204 + #define MMA7455_CHANNEL(axis, idx) { \ 205 + .type = IIO_ACCEL, \ 206 + .modified = 1, \ 207 + .address = MMA7455_REG_##axis##OUTL,\ 208 + .channel2 = IIO_MOD_##axis, \ 209 + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 210 + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ 211 + BIT(IIO_CHAN_INFO_SCALE), \ 212 + .scan_index = idx, \ 213 + .scan_type = { \ 214 + .sign = 's', \ 215 + .realbits = 10, \ 216 + .storagebits = 16, \ 217 + .endianness = IIO_LE, \ 218 + }, \ 219 + } 220 + 221 + static const struct iio_chan_spec mma7455_channels[] = { 222 + MMA7455_CHANNEL(X, 0), 223 + MMA7455_CHANNEL(Y, 1), 224 + MMA7455_CHANNEL(Z, 2), 225 + IIO_CHAN_SOFT_TIMESTAMP(3), 226 + }; 227 + 228 + static const unsigned long mma7455_scan_masks[] = {0x7, 0}; 229 + 230 + const struct regmap_config mma7455_core_regmap = { 231 + .reg_bits = 8, 232 + .val_bits = 8, 233 + .max_register = MMA7455_REG_TW, 234 + }; 235 + EXPORT_SYMBOL_GPL(mma7455_core_regmap); 236 + 237 + int mma7455_core_probe(struct device *dev, struct regmap *regmap, 238 + const char *name) 239 + { 240 + struct mma7455_data *mma7455; 241 + struct iio_dev *indio_dev; 242 + unsigned int reg; 243 + int ret; 244 + 245 + ret = regmap_read(regmap, MMA7455_REG_WHOAMI, &reg); 246 + if (ret) { 247 + dev_err(dev, "unable to read reg\n"); 248 + return ret; 249 + } 250 + 251 + if (reg != MMA7455_WHOAMI_ID) { 252 + dev_err(dev, "device id mismatch\n"); 253 + return -ENODEV; 254 + } 255 + 256 + indio_dev = devm_iio_device_alloc(dev, sizeof(*mma7455)); 257 + if (!indio_dev) 258 + return -ENOMEM; 259 + 260 + dev_set_drvdata(dev, indio_dev); 261 + mma7455 = iio_priv(indio_dev); 262 + mma7455->regmap = regmap; 263 + mma7455->dev = dev; 264 + 265 + indio_dev->info = &mma7455_info; 266 + indio_dev->name = name; 267 + indio_dev->dev.parent = dev; 268 + indio_dev->modes = INDIO_DIRECT_MODE; 269 + indio_dev->channels = mma7455_channels; 270 + indio_dev->num_channels = ARRAY_SIZE(mma7455_channels); 271 + indio_dev->available_scan_masks = mma7455_scan_masks; 272 + 273 + regmap_write(mma7455->regmap, MMA7455_REG_MCTL, 274 + MMA7455_MCTL_MODE_MEASURE); 275 + 276 + ret = iio_triggered_buffer_setup(indio_dev, NULL, 277 + mma7455_trigger_handler, NULL); 278 + if (ret) { 279 + dev_err(dev, "unable to setup triggered buffer\n"); 280 + return ret; 281 + } 282 + 283 + ret = iio_device_register(indio_dev); 284 + if (ret) { 285 + dev_err(dev, "unable to register device\n"); 286 + iio_triggered_buffer_cleanup(indio_dev); 287 + return ret; 288 + } 289 + 290 + return 0; 291 + } 292 + EXPORT_SYMBOL_GPL(mma7455_core_probe); 293 + 294 + int mma7455_core_remove(struct device *dev) 295 + { 296 + struct iio_dev *indio_dev = dev_get_drvdata(dev); 297 + struct mma7455_data *mma7455 = iio_priv(indio_dev); 298 + 299 + iio_device_unregister(indio_dev); 300 + iio_triggered_buffer_cleanup(indio_dev); 301 + 302 + regmap_write(mma7455->regmap, MMA7455_REG_MCTL, 303 + MMA7455_MCTL_MODE_STANDBY); 304 + 305 + return 0; 306 + } 307 + EXPORT_SYMBOL_GPL(mma7455_core_remove); 308 + 309 + MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); 310 + MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver"); 311 + MODULE_LICENSE("GPL v2");
+56
drivers/iio/accel/mma7455_i2c.c
··· 1 + /* 2 + * IIO accel I2C driver for Freescale MMA7455L 3-axis 10-bit accelerometer 3 + * Copyright 2015 Joachim Eastwood <manabian@gmail.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 as 7 + * published by the Free Software Foundation. 8 + */ 9 + 10 + #include <linux/i2c.h> 11 + #include <linux/module.h> 12 + #include <linux/regmap.h> 13 + 14 + #include "mma7455.h" 15 + 16 + static int mma7455_i2c_probe(struct i2c_client *i2c, 17 + const struct i2c_device_id *id) 18 + { 19 + struct regmap *regmap; 20 + const char *name = NULL; 21 + 22 + regmap = devm_regmap_init_i2c(i2c, &mma7455_core_regmap); 23 + if (IS_ERR(regmap)) 24 + return PTR_ERR(regmap); 25 + 26 + if (id) 27 + name = id->name; 28 + 29 + return mma7455_core_probe(&i2c->dev, regmap, name); 30 + } 31 + 32 + static int mma7455_i2c_remove(struct i2c_client *i2c) 33 + { 34 + return mma7455_core_remove(&i2c->dev); 35 + } 36 + 37 + static const struct i2c_device_id mma7455_i2c_ids[] = { 38 + { "mma7455", 0 }, 39 + { "mma7456", 0 }, 40 + { } 41 + }; 42 + MODULE_DEVICE_TABLE(i2c, mma7455_i2c_ids); 43 + 44 + static struct i2c_driver mma7455_i2c_driver = { 45 + .probe = mma7455_i2c_probe, 46 + .remove = mma7455_i2c_remove, 47 + .id_table = mma7455_i2c_ids, 48 + .driver = { 49 + .name = "mma7455-i2c", 50 + }, 51 + }; 52 + module_i2c_driver(mma7455_i2c_driver); 53 + 54 + MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); 55 + MODULE_DESCRIPTION("Freescale MMA7455L I2C accelerometer driver"); 56 + MODULE_LICENSE("GPL v2");
+52
drivers/iio/accel/mma7455_spi.c
··· 1 + /* 2 + * IIO accel SPI driver for Freescale MMA7455L 3-axis 10-bit accelerometer 3 + * Copyright 2015 Joachim Eastwood <manabian@gmail.com> 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 as 7 + * published by the Free Software Foundation. 8 + */ 9 + 10 + #include <linux/module.h> 11 + #include <linux/regmap.h> 12 + #include <linux/spi/spi.h> 13 + 14 + #include "mma7455.h" 15 + 16 + static int mma7455_spi_probe(struct spi_device *spi) 17 + { 18 + const struct spi_device_id *id = spi_get_device_id(spi); 19 + struct regmap *regmap; 20 + 21 + regmap = devm_regmap_init_spi(spi, &mma7455_core_regmap); 22 + if (IS_ERR(regmap)) 23 + return PTR_ERR(regmap); 24 + 25 + return mma7455_core_probe(&spi->dev, regmap, id->name); 26 + } 27 + 28 + static int mma7455_spi_remove(struct spi_device *spi) 29 + { 30 + return mma7455_core_remove(&spi->dev); 31 + } 32 + 33 + static const struct spi_device_id mma7455_spi_ids[] = { 34 + { "mma7455", 0 }, 35 + { "mma7456", 0 }, 36 + { } 37 + }; 38 + MODULE_DEVICE_TABLE(spi, mma7455_spi_ids); 39 + 40 + static struct spi_driver mma7455_spi_driver = { 41 + .probe = mma7455_spi_probe, 42 + .remove = mma7455_spi_remove, 43 + .id_table = mma7455_spi_ids, 44 + .driver = { 45 + .name = "mma7455-spi", 46 + }, 47 + }; 48 + module_spi_driver(mma7455_spi_driver); 49 + 50 + MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>"); 51 + MODULE_DESCRIPTION("Freescale MMA7455L SPI accelerometer driver"); 52 + MODULE_LICENSE("GPL v2");