299 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Freescale MMA7660FC 3-Axis Accelerometer4 *5 * Copyright (c) 2016, Intel Corporation.6 *7 * IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c.8 */9 10#include <linux/i2c.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/iio/iio.h>14#include <linux/iio/sysfs.h>15 16#define MMA7660_DRIVER_NAME "mma7660"17 18#define MMA7660_REG_XOUT 0x0019#define MMA7660_REG_YOUT 0x0120#define MMA7660_REG_ZOUT 0x0221#define MMA7660_REG_OUT_BIT_ALERT BIT(6)22 23#define MMA7660_REG_MODE 0x0724#define MMA7660_REG_MODE_BIT_MODE BIT(0)25#define MMA7660_REG_MODE_BIT_TON BIT(2)26 27#define MMA7660_I2C_READ_RETRIES 528 29/*30 * The accelerometer has one measurement range:31 *32 * -1.5g - +1.5g (6-bit, signed)33 *34 * scale = (1.5 + 1.5) * 9.81 / (2^6 - 1) = 0.46714285735 */36 37#define MMA7660_SCALE_AVAIL "0.467142857"38 39static const int mma7660_nscale = 467142857;40 41enum mma7660_mode {42 MMA7660_MODE_STANDBY,43 MMA7660_MODE_ACTIVE44};45 46struct mma7660_data {47 struct i2c_client *client;48 struct mutex lock;49 enum mma7660_mode mode;50 struct iio_mount_matrix orientation;51};52 53static const struct iio_mount_matrix *54mma7660_get_mount_matrix(const struct iio_dev *indio_dev,55 const struct iio_chan_spec *chan)56{57 struct mma7660_data *data = iio_priv(indio_dev);58 59 return &data->orientation;60}61 62static const struct iio_chan_spec_ext_info mma7660_ext_info[] = {63 IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, mma7660_get_mount_matrix),64 { }65};66 67static IIO_CONST_ATTR(in_accel_scale_available, MMA7660_SCALE_AVAIL);68 69static struct attribute *mma7660_attributes[] = {70 &iio_const_attr_in_accel_scale_available.dev_attr.attr,71 NULL,72};73 74static const struct attribute_group mma7660_attribute_group = {75 .attrs = mma7660_attributes76};77 78#define MMA7660_CHANNEL(reg, axis) { \79 .type = IIO_ACCEL, \80 .address = reg, \81 .modified = 1, \82 .channel2 = IIO_MOD_##axis, \83 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \84 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \85 .ext_info = mma7660_ext_info, \86}87 88static const struct iio_chan_spec mma7660_channels[] = {89 MMA7660_CHANNEL(MMA7660_REG_XOUT, X),90 MMA7660_CHANNEL(MMA7660_REG_YOUT, Y),91 MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z),92};93 94static int mma7660_set_mode(struct mma7660_data *data,95 enum mma7660_mode mode)96{97 int ret;98 struct i2c_client *client = data->client;99 100 if (mode == data->mode)101 return 0;102 103 ret = i2c_smbus_read_byte_data(client, MMA7660_REG_MODE);104 if (ret < 0) {105 dev_err(&client->dev, "failed to read sensor mode\n");106 return ret;107 }108 109 if (mode == MMA7660_MODE_ACTIVE) {110 ret &= ~MMA7660_REG_MODE_BIT_TON;111 ret |= MMA7660_REG_MODE_BIT_MODE;112 } else {113 ret &= ~MMA7660_REG_MODE_BIT_TON;114 ret &= ~MMA7660_REG_MODE_BIT_MODE;115 }116 117 ret = i2c_smbus_write_byte_data(client, MMA7660_REG_MODE, ret);118 if (ret < 0) {119 dev_err(&client->dev, "failed to change sensor mode\n");120 return ret;121 }122 123 data->mode = mode;124 125 return ret;126}127 128static int mma7660_read_accel(struct mma7660_data *data, u8 address)129{130 int ret, retries = MMA7660_I2C_READ_RETRIES;131 struct i2c_client *client = data->client;132 133 /*134 * Read data. If the Alert bit is set, the register was read at135 * the same time as the device was attempting to update the content.136 * The solution is to read the register again. Do this only137 * MMA7660_I2C_READ_RETRIES times to avoid spending too much time138 * in the kernel.139 */140 do {141 ret = i2c_smbus_read_byte_data(client, address);142 if (ret < 0) {143 dev_err(&client->dev, "register read failed\n");144 return ret;145 }146 } while (retries-- > 0 && ret & MMA7660_REG_OUT_BIT_ALERT);147 148 if (ret & MMA7660_REG_OUT_BIT_ALERT) {149 dev_err(&client->dev, "all register read retries failed\n");150 return -ETIMEDOUT;151 }152 153 return ret;154}155 156static int mma7660_read_raw(struct iio_dev *indio_dev,157 struct iio_chan_spec const *chan,158 int *val, int *val2, long mask)159{160 struct mma7660_data *data = iio_priv(indio_dev);161 int ret;162 163 switch (mask) {164 case IIO_CHAN_INFO_RAW:165 mutex_lock(&data->lock);166 ret = mma7660_read_accel(data, chan->address);167 mutex_unlock(&data->lock);168 if (ret < 0)169 return ret;170 *val = sign_extend32(ret, 5);171 return IIO_VAL_INT;172 case IIO_CHAN_INFO_SCALE:173 *val = 0;174 *val2 = mma7660_nscale;175 return IIO_VAL_INT_PLUS_NANO;176 default:177 return -EINVAL;178 }179 180 return -EINVAL;181}182 183static const struct iio_info mma7660_info = {184 .read_raw = mma7660_read_raw,185 .attrs = &mma7660_attribute_group,186};187 188static int mma7660_probe(struct i2c_client *client)189{190 int ret;191 struct iio_dev *indio_dev;192 struct mma7660_data *data;193 194 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));195 if (!indio_dev) {196 dev_err(&client->dev, "iio allocation failed!\n");197 return -ENOMEM;198 }199 200 data = iio_priv(indio_dev);201 data->client = client;202 i2c_set_clientdata(client, indio_dev);203 mutex_init(&data->lock);204 data->mode = MMA7660_MODE_STANDBY;205 206 ret = iio_read_mount_matrix(&client->dev, &data->orientation);207 if (ret)208 return ret;209 210 indio_dev->info = &mma7660_info;211 indio_dev->name = MMA7660_DRIVER_NAME;212 indio_dev->modes = INDIO_DIRECT_MODE;213 indio_dev->channels = mma7660_channels;214 indio_dev->num_channels = ARRAY_SIZE(mma7660_channels);215 216 ret = mma7660_set_mode(data, MMA7660_MODE_ACTIVE);217 if (ret < 0)218 return ret;219 220 ret = iio_device_register(indio_dev);221 if (ret < 0) {222 dev_err(&client->dev, "device_register failed\n");223 mma7660_set_mode(data, MMA7660_MODE_STANDBY);224 }225 226 return ret;227}228 229static void mma7660_remove(struct i2c_client *client)230{231 struct iio_dev *indio_dev = i2c_get_clientdata(client);232 int ret;233 234 iio_device_unregister(indio_dev);235 236 ret = mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY);237 if (ret)238 dev_warn(&client->dev, "Failed to put device in stand-by mode (%pe), ignoring\n",239 ERR_PTR(ret));240}241 242static int mma7660_suspend(struct device *dev)243{244 struct mma7660_data *data;245 246 data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));247 248 return mma7660_set_mode(data, MMA7660_MODE_STANDBY);249}250 251static int mma7660_resume(struct device *dev)252{253 struct mma7660_data *data;254 255 data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));256 257 return mma7660_set_mode(data, MMA7660_MODE_ACTIVE);258}259 260static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend,261 mma7660_resume);262 263static const struct i2c_device_id mma7660_i2c_id[] = {264 { "mma7660" },265 {}266};267MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);268 269static const struct of_device_id mma7660_of_match[] = {270 { .compatible = "fsl,mma7660" },271 { }272};273MODULE_DEVICE_TABLE(of, mma7660_of_match);274 275static const struct acpi_device_id mma7660_acpi_id[] = {276 {"MMA7660", 0},277 {}278};279 280MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id);281 282static struct i2c_driver mma7660_driver = {283 .driver = {284 .name = "mma7660",285 .pm = pm_sleep_ptr(&mma7660_pm_ops),286 .of_match_table = mma7660_of_match,287 .acpi_match_table = mma7660_acpi_id,288 },289 .probe = mma7660_probe,290 .remove = mma7660_remove,291 .id_table = mma7660_i2c_id,292};293 294module_i2c_driver(mma7660_driver);295 296MODULE_AUTHOR("Constantin Musca <constantin.musca@intel.com>");297MODULE_DESCRIPTION("Freescale MMA7660FC 3-Axis Accelerometer driver");298MODULE_LICENSE("GPL v2");299