136edc939SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2b9d453a5SGwendal Grignou /* 332133be6SConstantin Musca * Freescale MMA7660FC 3-Axis Accelerometer 432133be6SConstantin Musca * 532133be6SConstantin Musca * Copyright (c) 2016, Intel Corporation. 632133be6SConstantin Musca * 732133be6SConstantin Musca * IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c. 832133be6SConstantin Musca */ 932133be6SConstantin Musca 1032133be6SConstantin Musca #include <linux/i2c.h> 11072c7d34SAndy Shevchenko #include <linux/mod_devicetable.h> 1232133be6SConstantin Musca #include <linux/module.h> 1332133be6SConstantin Musca #include <linux/iio/iio.h> 1432133be6SConstantin Musca #include <linux/iio/sysfs.h> 1532133be6SConstantin Musca 1632133be6SConstantin Musca #define MMA7660_DRIVER_NAME "mma7660" 1732133be6SConstantin Musca 1832133be6SConstantin Musca #define MMA7660_REG_XOUT 0x00 1932133be6SConstantin Musca #define MMA7660_REG_YOUT 0x01 2032133be6SConstantin Musca #define MMA7660_REG_ZOUT 0x02 2132133be6SConstantin Musca #define MMA7660_REG_OUT_BIT_ALERT BIT(6) 2232133be6SConstantin Musca 2332133be6SConstantin Musca #define MMA7660_REG_MODE 0x07 2432133be6SConstantin Musca #define MMA7660_REG_MODE_BIT_MODE BIT(0) 2532133be6SConstantin Musca #define MMA7660_REG_MODE_BIT_TON BIT(2) 2632133be6SConstantin Musca 2732133be6SConstantin Musca #define MMA7660_I2C_READ_RETRIES 5 2832133be6SConstantin Musca 2932133be6SConstantin Musca /* 3032133be6SConstantin Musca * The accelerometer has one measurement range: 3132133be6SConstantin Musca * 3232133be6SConstantin Musca * -1.5g - +1.5g (6-bit, signed) 3332133be6SConstantin Musca * 3432133be6SConstantin Musca * scale = (1.5 + 1.5) * 9.81 / (2^6 - 1) = 0.467142857 3532133be6SConstantin Musca */ 3632133be6SConstantin Musca 3732133be6SConstantin Musca #define MMA7660_SCALE_AVAIL "0.467142857" 3832133be6SConstantin Musca 391b14adcaSWei Yongjun static const int mma7660_nscale = 467142857; 4032133be6SConstantin Musca 4132133be6SConstantin Musca enum mma7660_mode { 4232133be6SConstantin Musca MMA7660_MODE_STANDBY, 4332133be6SConstantin Musca MMA7660_MODE_ACTIVE 4432133be6SConstantin Musca }; 4532133be6SConstantin Musca 4632133be6SConstantin Musca struct mma7660_data { 4732133be6SConstantin Musca struct i2c_client *client; 4832133be6SConstantin Musca struct mutex lock; 4932133be6SConstantin Musca enum mma7660_mode mode; 50*f4bed1ceSVal Packett struct iio_mount_matrix orientation; 51*f4bed1ceSVal Packett }; 52*f4bed1ceSVal Packett 53*f4bed1ceSVal Packett static const struct iio_mount_matrix * 54*f4bed1ceSVal Packett mma7660_get_mount_matrix(const struct iio_dev *indio_dev, 55*f4bed1ceSVal Packett const struct iio_chan_spec *chan) 56*f4bed1ceSVal Packett { 57*f4bed1ceSVal Packett struct mma7660_data *data = iio_priv(indio_dev); 58*f4bed1ceSVal Packett 59*f4bed1ceSVal Packett return &data->orientation; 60*f4bed1ceSVal Packett } 61*f4bed1ceSVal Packett 62*f4bed1ceSVal Packett static const struct iio_chan_spec_ext_info mma7660_ext_info[] = { 63*f4bed1ceSVal Packett IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, mma7660_get_mount_matrix), 64*f4bed1ceSVal Packett { } 6532133be6SConstantin Musca }; 6632133be6SConstantin Musca 6732133be6SConstantin Musca static IIO_CONST_ATTR(in_accel_scale_available, MMA7660_SCALE_AVAIL); 6832133be6SConstantin Musca 6932133be6SConstantin Musca static struct attribute *mma7660_attributes[] = { 7032133be6SConstantin Musca &iio_const_attr_in_accel_scale_available.dev_attr.attr, 7132133be6SConstantin Musca NULL, 7232133be6SConstantin Musca }; 7332133be6SConstantin Musca 7432133be6SConstantin Musca static const struct attribute_group mma7660_attribute_group = { 7532133be6SConstantin Musca .attrs = mma7660_attributes 7632133be6SConstantin Musca }; 7732133be6SConstantin Musca 78*f4bed1ceSVal Packett #define MMA7660_CHANNEL(reg, axis) { \ 79*f4bed1ceSVal Packett .type = IIO_ACCEL, \ 80*f4bed1ceSVal Packett .address = reg, \ 81*f4bed1ceSVal Packett .modified = 1, \ 82*f4bed1ceSVal Packett .channel2 = IIO_MOD_##axis, \ 83*f4bed1ceSVal Packett .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ 84*f4bed1ceSVal Packett .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ 85*f4bed1ceSVal Packett .ext_info = mma7660_ext_info, \ 86*f4bed1ceSVal Packett } 87*f4bed1ceSVal Packett 88*f4bed1ceSVal Packett static const struct iio_chan_spec mma7660_channels[] = { 89*f4bed1ceSVal Packett MMA7660_CHANNEL(MMA7660_REG_XOUT, X), 90*f4bed1ceSVal Packett MMA7660_CHANNEL(MMA7660_REG_YOUT, Y), 91*f4bed1ceSVal Packett MMA7660_CHANNEL(MMA7660_REG_ZOUT, Z), 92*f4bed1ceSVal Packett }; 93*f4bed1ceSVal Packett 9432133be6SConstantin Musca static int mma7660_set_mode(struct mma7660_data *data, 9532133be6SConstantin Musca enum mma7660_mode mode) 9632133be6SConstantin Musca { 9732133be6SConstantin Musca int ret; 9832133be6SConstantin Musca struct i2c_client *client = data->client; 9932133be6SConstantin Musca 10032133be6SConstantin Musca if (mode == data->mode) 10132133be6SConstantin Musca return 0; 10232133be6SConstantin Musca 10332133be6SConstantin Musca ret = i2c_smbus_read_byte_data(client, MMA7660_REG_MODE); 10432133be6SConstantin Musca if (ret < 0) { 10532133be6SConstantin Musca dev_err(&client->dev, "failed to read sensor mode\n"); 10632133be6SConstantin Musca return ret; 10732133be6SConstantin Musca } 10832133be6SConstantin Musca 10932133be6SConstantin Musca if (mode == MMA7660_MODE_ACTIVE) { 11032133be6SConstantin Musca ret &= ~MMA7660_REG_MODE_BIT_TON; 11132133be6SConstantin Musca ret |= MMA7660_REG_MODE_BIT_MODE; 11232133be6SConstantin Musca } else { 11332133be6SConstantin Musca ret &= ~MMA7660_REG_MODE_BIT_TON; 11432133be6SConstantin Musca ret &= ~MMA7660_REG_MODE_BIT_MODE; 11532133be6SConstantin Musca } 11632133be6SConstantin Musca 11732133be6SConstantin Musca ret = i2c_smbus_write_byte_data(client, MMA7660_REG_MODE, ret); 11832133be6SConstantin Musca if (ret < 0) { 11932133be6SConstantin Musca dev_err(&client->dev, "failed to change sensor mode\n"); 12032133be6SConstantin Musca return ret; 12132133be6SConstantin Musca } 12232133be6SConstantin Musca 12332133be6SConstantin Musca data->mode = mode; 12432133be6SConstantin Musca 12532133be6SConstantin Musca return ret; 12632133be6SConstantin Musca } 12732133be6SConstantin Musca 12832133be6SConstantin Musca static int mma7660_read_accel(struct mma7660_data *data, u8 address) 12932133be6SConstantin Musca { 13032133be6SConstantin Musca int ret, retries = MMA7660_I2C_READ_RETRIES; 13132133be6SConstantin Musca struct i2c_client *client = data->client; 13232133be6SConstantin Musca 13332133be6SConstantin Musca /* 13432133be6SConstantin Musca * Read data. If the Alert bit is set, the register was read at 13532133be6SConstantin Musca * the same time as the device was attempting to update the content. 13632133be6SConstantin Musca * The solution is to read the register again. Do this only 13732133be6SConstantin Musca * MMA7660_I2C_READ_RETRIES times to avoid spending too much time 13832133be6SConstantin Musca * in the kernel. 13932133be6SConstantin Musca */ 14032133be6SConstantin Musca do { 14132133be6SConstantin Musca ret = i2c_smbus_read_byte_data(client, address); 14232133be6SConstantin Musca if (ret < 0) { 14332133be6SConstantin Musca dev_err(&client->dev, "register read failed\n"); 14432133be6SConstantin Musca return ret; 14532133be6SConstantin Musca } 14632133be6SConstantin Musca } while (retries-- > 0 && ret & MMA7660_REG_OUT_BIT_ALERT); 14732133be6SConstantin Musca 14832133be6SConstantin Musca if (ret & MMA7660_REG_OUT_BIT_ALERT) { 14932133be6SConstantin Musca dev_err(&client->dev, "all register read retries failed\n"); 15032133be6SConstantin Musca return -ETIMEDOUT; 15132133be6SConstantin Musca } 15232133be6SConstantin Musca 15332133be6SConstantin Musca return ret; 15432133be6SConstantin Musca } 15532133be6SConstantin Musca 15632133be6SConstantin Musca static int mma7660_read_raw(struct iio_dev *indio_dev, 15732133be6SConstantin Musca struct iio_chan_spec const *chan, 15832133be6SConstantin Musca int *val, int *val2, long mask) 15932133be6SConstantin Musca { 16032133be6SConstantin Musca struct mma7660_data *data = iio_priv(indio_dev); 16132133be6SConstantin Musca int ret; 16232133be6SConstantin Musca 16332133be6SConstantin Musca switch (mask) { 16432133be6SConstantin Musca case IIO_CHAN_INFO_RAW: 16532133be6SConstantin Musca mutex_lock(&data->lock); 16632133be6SConstantin Musca ret = mma7660_read_accel(data, chan->address); 16732133be6SConstantin Musca mutex_unlock(&data->lock); 16832133be6SConstantin Musca if (ret < 0) 16932133be6SConstantin Musca return ret; 17032133be6SConstantin Musca *val = sign_extend32(ret, 5); 17132133be6SConstantin Musca return IIO_VAL_INT; 17232133be6SConstantin Musca case IIO_CHAN_INFO_SCALE: 17332133be6SConstantin Musca *val = 0; 17432133be6SConstantin Musca *val2 = mma7660_nscale; 17532133be6SConstantin Musca return IIO_VAL_INT_PLUS_NANO; 17632133be6SConstantin Musca default: 17732133be6SConstantin Musca return -EINVAL; 17832133be6SConstantin Musca } 17932133be6SConstantin Musca 18032133be6SConstantin Musca return -EINVAL; 18132133be6SConstantin Musca } 18232133be6SConstantin Musca 18332133be6SConstantin Musca static const struct iio_info mma7660_info = { 18432133be6SConstantin Musca .read_raw = mma7660_read_raw, 18532133be6SConstantin Musca .attrs = &mma7660_attribute_group, 18632133be6SConstantin Musca }; 18732133be6SConstantin Musca 18876403ea6SUwe Kleine-König static int mma7660_probe(struct i2c_client *client) 18932133be6SConstantin Musca { 19032133be6SConstantin Musca int ret; 19132133be6SConstantin Musca struct iio_dev *indio_dev; 19232133be6SConstantin Musca struct mma7660_data *data; 19332133be6SConstantin Musca 19432133be6SConstantin Musca indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); 19532133be6SConstantin Musca if (!indio_dev) { 19632133be6SConstantin Musca dev_err(&client->dev, "iio allocation failed!\n"); 19732133be6SConstantin Musca return -ENOMEM; 19832133be6SConstantin Musca } 19932133be6SConstantin Musca 20032133be6SConstantin Musca data = iio_priv(indio_dev); 20132133be6SConstantin Musca data->client = client; 20232133be6SConstantin Musca i2c_set_clientdata(client, indio_dev); 20332133be6SConstantin Musca mutex_init(&data->lock); 20432133be6SConstantin Musca data->mode = MMA7660_MODE_STANDBY; 20532133be6SConstantin Musca 206*f4bed1ceSVal Packett ret = iio_read_mount_matrix(&client->dev, &data->orientation); 207*f4bed1ceSVal Packett if (ret) 208*f4bed1ceSVal Packett return ret; 209*f4bed1ceSVal Packett 21032133be6SConstantin Musca indio_dev->info = &mma7660_info; 21132133be6SConstantin Musca indio_dev->name = MMA7660_DRIVER_NAME; 21232133be6SConstantin Musca indio_dev->modes = INDIO_DIRECT_MODE; 21332133be6SConstantin Musca indio_dev->channels = mma7660_channels; 21432133be6SConstantin Musca indio_dev->num_channels = ARRAY_SIZE(mma7660_channels); 21532133be6SConstantin Musca 21632133be6SConstantin Musca ret = mma7660_set_mode(data, MMA7660_MODE_ACTIVE); 21732133be6SConstantin Musca if (ret < 0) 21832133be6SConstantin Musca return ret; 21932133be6SConstantin Musca 22032133be6SConstantin Musca ret = iio_device_register(indio_dev); 22132133be6SConstantin Musca if (ret < 0) { 22232133be6SConstantin Musca dev_err(&client->dev, "device_register failed\n"); 22332133be6SConstantin Musca mma7660_set_mode(data, MMA7660_MODE_STANDBY); 22432133be6SConstantin Musca } 22532133be6SConstantin Musca 22632133be6SConstantin Musca return ret; 22732133be6SConstantin Musca } 22832133be6SConstantin Musca 229ed5c2f5fSUwe Kleine-König static void mma7660_remove(struct i2c_client *client) 23032133be6SConstantin Musca { 23132133be6SConstantin Musca struct iio_dev *indio_dev = i2c_get_clientdata(client); 232e12653ebSUwe Kleine-König int ret; 23332133be6SConstantin Musca 23432133be6SConstantin Musca iio_device_unregister(indio_dev); 23532133be6SConstantin Musca 236e12653ebSUwe Kleine-König ret = mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); 237e12653ebSUwe Kleine-König if (ret) 238e12653ebSUwe Kleine-König dev_warn(&client->dev, "Failed to put device in stand-by mode (%pe), ignoring\n", 239e12653ebSUwe Kleine-König ERR_PTR(ret)); 24032133be6SConstantin Musca } 24132133be6SConstantin Musca 24232133be6SConstantin Musca static int mma7660_suspend(struct device *dev) 24332133be6SConstantin Musca { 24432133be6SConstantin Musca struct mma7660_data *data; 24532133be6SConstantin Musca 24632133be6SConstantin Musca data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 24732133be6SConstantin Musca 24832133be6SConstantin Musca return mma7660_set_mode(data, MMA7660_MODE_STANDBY); 24932133be6SConstantin Musca } 25032133be6SConstantin Musca 25132133be6SConstantin Musca static int mma7660_resume(struct device *dev) 25232133be6SConstantin Musca { 25332133be6SConstantin Musca struct mma7660_data *data; 25432133be6SConstantin Musca 25532133be6SConstantin Musca data = iio_priv(i2c_get_clientdata(to_i2c_client(dev))); 25632133be6SConstantin Musca 25732133be6SConstantin Musca return mma7660_set_mode(data, MMA7660_MODE_ACTIVE); 25832133be6SConstantin Musca } 25932133be6SConstantin Musca 260812c5f31SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(mma7660_pm_ops, mma7660_suspend, 261812c5f31SJonathan Cameron mma7660_resume); 26232133be6SConstantin Musca 26332133be6SConstantin Musca static const struct i2c_device_id mma7660_i2c_id[] = { 2644391affaSUwe Kleine-König { "mma7660" }, 26532133be6SConstantin Musca {} 26632133be6SConstantin Musca }; 26756203188SHans de Goede MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id); 26832133be6SConstantin Musca 269f1c088a7SJavier Martinez Canillas static const struct of_device_id mma7660_of_match[] = { 270f1c088a7SJavier Martinez Canillas { .compatible = "fsl,mma7660" }, 271f1c088a7SJavier Martinez Canillas { } 272f1c088a7SJavier Martinez Canillas }; 273f1c088a7SJavier Martinez Canillas MODULE_DEVICE_TABLE(of, mma7660_of_match); 274f1c088a7SJavier Martinez Canillas 275072c7d34SAndy Shevchenko static const struct acpi_device_id mma7660_acpi_id[] = { 27632133be6SConstantin Musca {"MMA7660", 0}, 27732133be6SConstantin Musca {} 27832133be6SConstantin Musca }; 27932133be6SConstantin Musca 28032133be6SConstantin Musca MODULE_DEVICE_TABLE(acpi, mma7660_acpi_id); 28132133be6SConstantin Musca 28232133be6SConstantin Musca static struct i2c_driver mma7660_driver = { 28332133be6SConstantin Musca .driver = { 28432133be6SConstantin Musca .name = "mma7660", 285812c5f31SJonathan Cameron .pm = pm_sleep_ptr(&mma7660_pm_ops), 286f1c088a7SJavier Martinez Canillas .of_match_table = mma7660_of_match, 287072c7d34SAndy Shevchenko .acpi_match_table = mma7660_acpi_id, 28832133be6SConstantin Musca }, 2897cf15f42SUwe Kleine-König .probe = mma7660_probe, 29032133be6SConstantin Musca .remove = mma7660_remove, 29132133be6SConstantin Musca .id_table = mma7660_i2c_id, 29232133be6SConstantin Musca }; 29332133be6SConstantin Musca 29432133be6SConstantin Musca module_i2c_driver(mma7660_driver); 29532133be6SConstantin Musca 29632133be6SConstantin Musca MODULE_AUTHOR("Constantin Musca <constantin.musca@intel.com>"); 29732133be6SConstantin Musca MODULE_DESCRIPTION("Freescale MMA7660FC 3-Axis Accelerometer driver"); 29832133be6SConstantin Musca MODULE_LICENSE("GPL v2"); 299