314 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * IIO accel core driver for Freescale MMA7455L 3-axis 10-bit accelerometer4 * Copyright 2015 Joachim Eastwood <manabian@gmail.com>5 *6 * UNSUPPORTED hardware features:7 * - 8-bit mode with different scales8 * - INT1/INT2 interrupts9 * - Offset calibration10 * - Events11 */12 13#include <linux/delay.h>14#include <linux/iio/iio.h>15#include <linux/iio/sysfs.h>16#include <linux/iio/buffer.h>17#include <linux/iio/trigger.h>18#include <linux/iio/trigger_consumer.h>19#include <linux/iio/triggered_buffer.h>20#include <linux/module.h>21#include <linux/regmap.h>22 23#include "mma7455.h"24 25#define MMA7455_REG_XOUTL 0x0026#define MMA7455_REG_XOUTH 0x0127#define MMA7455_REG_YOUTL 0x0228#define MMA7455_REG_YOUTH 0x0329#define MMA7455_REG_ZOUTL 0x0430#define MMA7455_REG_ZOUTH 0x0531#define MMA7455_REG_STATUS 0x0932#define MMA7455_STATUS_DRDY BIT(0)33#define MMA7455_REG_WHOAMI 0x0f34#define MMA7455_WHOAMI_ID 0x5535#define MMA7455_REG_MCTL 0x1636#define MMA7455_MCTL_MODE_STANDBY 0x0037#define MMA7455_MCTL_MODE_MEASURE 0x0138#define MMA7455_REG_CTL1 0x1839#define MMA7455_CTL1_DFBW_MASK BIT(7)40#define MMA7455_CTL1_DFBW_125HZ BIT(7)41#define MMA7455_CTL1_DFBW_62_5HZ 042#define MMA7455_REG_TW 0x1e43 44/*45 * When MMA7455 is used in 10-bit it has a fullscale of -8g46 * corresponding to raw value -512. The userspace interface47 * uses m/s^2 and we declare micro units.48 * So scale factor is given by:49 * g * 8 * 1e6 / 512 = 153228.90625, with g = 9.8066550 */51#define MMA7455_10BIT_SCALE 15322952 53struct mma7455_data {54 struct regmap *regmap;55 /*56 * Used to reorganize data. Will ensure correct alignment of57 * the timestamp if present58 */59 struct {60 __le16 channels[3];61 s64 ts __aligned(8);62 } scan;63};64 65static int mma7455_drdy(struct mma7455_data *mma7455)66{67 struct device *dev = regmap_get_device(mma7455->regmap);68 unsigned int reg;69 int tries = 3;70 int ret;71 72 while (tries-- > 0) {73 ret = regmap_read(mma7455->regmap, MMA7455_REG_STATUS, ®);74 if (ret)75 return ret;76 77 if (reg & MMA7455_STATUS_DRDY)78 return 0;79 80 msleep(20);81 }82 83 dev_warn(dev, "data not ready\n");84 85 return -EIO;86}87 88static irqreturn_t mma7455_trigger_handler(int irq, void *p)89{90 struct iio_poll_func *pf = p;91 struct iio_dev *indio_dev = pf->indio_dev;92 struct mma7455_data *mma7455 = iio_priv(indio_dev);93 int ret;94 95 ret = mma7455_drdy(mma7455);96 if (ret)97 goto done;98 99 ret = regmap_bulk_read(mma7455->regmap, MMA7455_REG_XOUTL,100 mma7455->scan.channels,101 sizeof(mma7455->scan.channels));102 if (ret)103 goto done;104 105 iio_push_to_buffers_with_timestamp(indio_dev, &mma7455->scan,106 iio_get_time_ns(indio_dev));107 108done:109 iio_trigger_notify_done(indio_dev->trig);110 111 return IRQ_HANDLED;112}113 114static int mma7455_read_raw(struct iio_dev *indio_dev,115 struct iio_chan_spec const *chan,116 int *val, int *val2, long mask)117{118 struct mma7455_data *mma7455 = iio_priv(indio_dev);119 unsigned int reg;120 __le16 data;121 int ret;122 123 switch (mask) {124 case IIO_CHAN_INFO_RAW:125 if (iio_buffer_enabled(indio_dev))126 return -EBUSY;127 128 ret = mma7455_drdy(mma7455);129 if (ret)130 return ret;131 132 ret = regmap_bulk_read(mma7455->regmap, chan->address, &data,133 sizeof(data));134 if (ret)135 return ret;136 137 *val = sign_extend32(le16_to_cpu(data),138 chan->scan_type.realbits - 1);139 140 return IIO_VAL_INT;141 142 case IIO_CHAN_INFO_SCALE:143 *val = 0;144 *val2 = MMA7455_10BIT_SCALE;145 146 return IIO_VAL_INT_PLUS_MICRO;147 148 case IIO_CHAN_INFO_SAMP_FREQ:149 ret = regmap_read(mma7455->regmap, MMA7455_REG_CTL1, ®);150 if (ret)151 return ret;152 153 if (reg & MMA7455_CTL1_DFBW_MASK)154 *val = 250;155 else156 *val = 125;157 158 return IIO_VAL_INT;159 }160 161 return -EINVAL;162}163 164static int mma7455_write_raw(struct iio_dev *indio_dev,165 struct iio_chan_spec const *chan,166 int val, int val2, long mask)167{168 struct mma7455_data *mma7455 = iio_priv(indio_dev);169 int i;170 171 switch (mask) {172 case IIO_CHAN_INFO_SAMP_FREQ:173 if (val == 250 && val2 == 0)174 i = MMA7455_CTL1_DFBW_125HZ;175 else if (val == 125 && val2 == 0)176 i = MMA7455_CTL1_DFBW_62_5HZ;177 else178 return -EINVAL;179 180 return regmap_update_bits(mma7455->regmap, MMA7455_REG_CTL1,181 MMA7455_CTL1_DFBW_MASK, i);182 183 case IIO_CHAN_INFO_SCALE:184 /* In 10-bit mode there is only one scale available */185 if (val == 0 && val2 == MMA7455_10BIT_SCALE)186 return 0;187 break;188 }189 190 return -EINVAL;191}192 193static IIO_CONST_ATTR(sampling_frequency_available, "125 250");194 195static struct attribute *mma7455_attributes[] = {196 &iio_const_attr_sampling_frequency_available.dev_attr.attr,197 NULL198};199 200static const struct attribute_group mma7455_group = {201 .attrs = mma7455_attributes,202};203 204static const struct iio_info mma7455_info = {205 .attrs = &mma7455_group,206 .read_raw = mma7455_read_raw,207 .write_raw = mma7455_write_raw,208};209 210#define MMA7455_CHANNEL(axis, idx) { \211 .type = IIO_ACCEL, \212 .modified = 1, \213 .address = MMA7455_REG_##axis##OUTL,\214 .channel2 = IIO_MOD_##axis, \215 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \216 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \217 BIT(IIO_CHAN_INFO_SCALE), \218 .scan_index = idx, \219 .scan_type = { \220 .sign = 's', \221 .realbits = 10, \222 .storagebits = 16, \223 .endianness = IIO_LE, \224 }, \225}226 227static const struct iio_chan_spec mma7455_channels[] = {228 MMA7455_CHANNEL(X, 0),229 MMA7455_CHANNEL(Y, 1),230 MMA7455_CHANNEL(Z, 2),231 IIO_CHAN_SOFT_TIMESTAMP(3),232};233 234static const unsigned long mma7455_scan_masks[] = {0x7, 0};235 236const struct regmap_config mma7455_core_regmap = {237 .reg_bits = 8,238 .val_bits = 8,239 .max_register = MMA7455_REG_TW,240};241EXPORT_SYMBOL_NS_GPL(mma7455_core_regmap, IIO_MMA7455);242 243int mma7455_core_probe(struct device *dev, struct regmap *regmap,244 const char *name)245{246 struct mma7455_data *mma7455;247 struct iio_dev *indio_dev;248 unsigned int reg;249 int ret;250 251 ret = regmap_read(regmap, MMA7455_REG_WHOAMI, ®);252 if (ret) {253 dev_err(dev, "unable to read reg\n");254 return ret;255 }256 257 if (reg != MMA7455_WHOAMI_ID) {258 dev_err(dev, "device id mismatch\n");259 return -ENODEV;260 }261 262 indio_dev = devm_iio_device_alloc(dev, sizeof(*mma7455));263 if (!indio_dev)264 return -ENOMEM;265 266 dev_set_drvdata(dev, indio_dev);267 mma7455 = iio_priv(indio_dev);268 mma7455->regmap = regmap;269 270 indio_dev->info = &mma7455_info;271 indio_dev->name = name;272 indio_dev->modes = INDIO_DIRECT_MODE;273 indio_dev->channels = mma7455_channels;274 indio_dev->num_channels = ARRAY_SIZE(mma7455_channels);275 indio_dev->available_scan_masks = mma7455_scan_masks;276 277 regmap_write(mma7455->regmap, MMA7455_REG_MCTL,278 MMA7455_MCTL_MODE_MEASURE);279 280 ret = iio_triggered_buffer_setup(indio_dev, NULL,281 mma7455_trigger_handler, NULL);282 if (ret) {283 dev_err(dev, "unable to setup triggered buffer\n");284 return ret;285 }286 287 ret = iio_device_register(indio_dev);288 if (ret) {289 dev_err(dev, "unable to register device\n");290 iio_triggered_buffer_cleanup(indio_dev);291 return ret;292 }293 294 return 0;295}296EXPORT_SYMBOL_NS_GPL(mma7455_core_probe, IIO_MMA7455);297 298void mma7455_core_remove(struct device *dev)299{300 struct iio_dev *indio_dev = dev_get_drvdata(dev);301 struct mma7455_data *mma7455 = iio_priv(indio_dev);302 303 iio_device_unregister(indio_dev);304 iio_triggered_buffer_cleanup(indio_dev);305 306 regmap_write(mma7455->regmap, MMA7455_REG_MCTL,307 MMA7455_MCTL_MODE_STANDBY);308}309EXPORT_SYMBOL_NS_GPL(mma7455_core_remove, IIO_MMA7455);310 311MODULE_AUTHOR("Joachim Eastwood <manabian@gmail.com>");312MODULE_DESCRIPTION("Freescale MMA7455L core accelerometer driver");313MODULE_LICENSE("GPL v2");314