187 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * IIO driver for the MiraMEMS DA217 and DA280 3-axis accelerometer and4 * IIO driver for the MiraMEMS DA226 2-axis accelerometer5 *6 * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>7 */8 9#include <linux/module.h>10#include <linux/i2c.h>11#include <linux/acpi.h>12#include <linux/iio/iio.h>13#include <linux/iio/sysfs.h>14#include <linux/byteorder/generic.h>15 16#define DA280_REG_CHIP_ID 0x0117#define DA280_REG_ACC_X_LSB 0x0218#define DA280_REG_ACC_Y_LSB 0x0419#define DA280_REG_ACC_Z_LSB 0x0620#define DA280_REG_MODE_BW 0x1121 22#define DA280_CHIP_ID 0x1323#define DA280_MODE_ENABLE 0x1e24#define DA280_MODE_DISABLE 0x9e25 26/*27 * a value of + or -4096 corresponds to + or - 1G28 * scale = 9.81 / 4096 = 0.00239501929 */30 31static const int da280_nscale = 2395019;32 33#define DA280_CHANNEL(reg, axis) { \34 .type = IIO_ACCEL, \35 .address = reg, \36 .modified = 1, \37 .channel2 = IIO_MOD_##axis, \38 .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \39 .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \40}41 42static const struct iio_chan_spec da280_channels[] = {43 DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),44 DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),45 DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),46};47 48struct da280_match_data {49 const char *name;50 int num_channels;51};52 53struct da280_data {54 struct i2c_client *client;55};56 57static int da280_enable(struct i2c_client *client, bool enable)58{59 u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;60 61 return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);62}63 64static int da280_read_raw(struct iio_dev *indio_dev,65 struct iio_chan_spec const *chan,66 int *val, int *val2, long mask)67{68 struct da280_data *data = iio_priv(indio_dev);69 int ret;70 71 switch (mask) {72 case IIO_CHAN_INFO_RAW:73 ret = i2c_smbus_read_word_data(data->client, chan->address);74 if (ret < 0)75 return ret;76 /*77 * Values are 14 bits, stored as 16 bits with the 278 * least significant bits always 0.79 */80 *val = (short)ret >> 2;81 return IIO_VAL_INT;82 case IIO_CHAN_INFO_SCALE:83 *val = 0;84 *val2 = da280_nscale;85 return IIO_VAL_INT_PLUS_NANO;86 default:87 return -EINVAL;88 }89}90 91static const struct iio_info da280_info = {92 .read_raw = da280_read_raw,93};94 95static void da280_disable(void *client)96{97 da280_enable(client, false);98}99 100static int da280_probe(struct i2c_client *client)101{102 const struct da280_match_data *match_data;103 struct iio_dev *indio_dev;104 struct da280_data *data;105 int ret;106 107 ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);108 if (ret != DA280_CHIP_ID)109 return (ret < 0) ? ret : -ENODEV;110 111 match_data = i2c_get_match_data(client);112 if (!match_data) {113 dev_err(&client->dev, "Error match-data not set\n");114 return -EINVAL;115 }116 117 indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));118 if (!indio_dev)119 return -ENOMEM;120 121 data = iio_priv(indio_dev);122 data->client = client;123 124 indio_dev->info = &da280_info;125 indio_dev->modes = INDIO_DIRECT_MODE;126 indio_dev->channels = da280_channels;127 indio_dev->num_channels = match_data->num_channels;128 indio_dev->name = match_data->name;129 130 ret = da280_enable(client, true);131 if (ret < 0)132 return ret;133 134 ret = devm_add_action_or_reset(&client->dev, da280_disable, client);135 if (ret)136 return ret;137 138 return devm_iio_device_register(&client->dev, indio_dev);139}140 141static int da280_suspend(struct device *dev)142{143 return da280_enable(to_i2c_client(dev), false);144}145 146static int da280_resume(struct device *dev)147{148 return da280_enable(to_i2c_client(dev), true);149}150 151static DEFINE_SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);152 153static const struct da280_match_data da217_match_data = { "da217", 3 };154static const struct da280_match_data da226_match_data = { "da226", 2 };155static const struct da280_match_data da280_match_data = { "da280", 3 };156 157static const struct acpi_device_id da280_acpi_match[] = {158 { "NSA2513", (kernel_ulong_t)&da217_match_data },159 { "MIRAACC", (kernel_ulong_t)&da280_match_data },160 {}161};162MODULE_DEVICE_TABLE(acpi, da280_acpi_match);163 164static const struct i2c_device_id da280_i2c_id[] = {165 { "da217", (kernel_ulong_t)&da217_match_data },166 { "da226", (kernel_ulong_t)&da226_match_data },167 { "da280", (kernel_ulong_t)&da280_match_data },168 {}169};170MODULE_DEVICE_TABLE(i2c, da280_i2c_id);171 172static struct i2c_driver da280_driver = {173 .driver = {174 .name = "da280",175 .acpi_match_table = da280_acpi_match,176 .pm = pm_sleep_ptr(&da280_pm_ops),177 },178 .probe = da280_probe,179 .id_table = da280_i2c_id,180};181 182module_i2c_driver(da280_driver);183 184MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");185MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");186MODULE_LICENSE("GPL v2");187