18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/**
38c2ecf20Sopenharmony_ci * mCube MC3230 3-Axis Accelerometer
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/i2c.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
138c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define MC3230_REG_XOUT			0x00
168c2ecf20Sopenharmony_ci#define MC3230_REG_YOUT			0x01
178c2ecf20Sopenharmony_ci#define MC3230_REG_ZOUT			0x02
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define MC3230_REG_MODE			0x07
208c2ecf20Sopenharmony_ci#define MC3230_MODE_OPCON_MASK		0x03
218c2ecf20Sopenharmony_ci#define MC3230_MODE_OPCON_WAKE		0x01
228c2ecf20Sopenharmony_ci#define MC3230_MODE_OPCON_STANDBY	0x03
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define MC3230_REG_CHIP_ID		0x18
258c2ecf20Sopenharmony_ci#define MC3230_CHIP_ID			0x01
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define MC3230_REG_PRODUCT_CODE		0x3b
288c2ecf20Sopenharmony_ci#define MC3230_PRODUCT_CODE		0x19
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * The accelerometer has one measurement range:
328c2ecf20Sopenharmony_ci *
338c2ecf20Sopenharmony_ci * -1.5g - +1.5g (8-bit, signed)
348c2ecf20Sopenharmony_ci *
358c2ecf20Sopenharmony_ci * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1)	= 0.115411765
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic const int mc3230_nscale = 115411765;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci#define MC3230_CHANNEL(reg, axis) {	\
418c2ecf20Sopenharmony_ci	.type = IIO_ACCEL,	\
428c2ecf20Sopenharmony_ci	.address = reg,	\
438c2ecf20Sopenharmony_ci	.modified = 1,	\
448c2ecf20Sopenharmony_ci	.channel2 = IIO_MOD_##axis,	\
458c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
468c2ecf20Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic const struct iio_chan_spec mc3230_channels[] = {
508c2ecf20Sopenharmony_ci	MC3230_CHANNEL(MC3230_REG_XOUT, X),
518c2ecf20Sopenharmony_ci	MC3230_CHANNEL(MC3230_REG_YOUT, Y),
528c2ecf20Sopenharmony_ci	MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct mc3230_data {
568c2ecf20Sopenharmony_ci	struct i2c_client *client;
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic int mc3230_set_opcon(struct mc3230_data *data, int opcon)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	int ret;
628c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
658c2ecf20Sopenharmony_ci	if (ret < 0) {
668c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
678c2ecf20Sopenharmony_ci		return ret;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	ret &= ~MC3230_MODE_OPCON_MASK;
718c2ecf20Sopenharmony_ci	ret |= opcon;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
748c2ecf20Sopenharmony_ci	if (ret < 0) {
758c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
768c2ecf20Sopenharmony_ci		return ret;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int mc3230_read_raw(struct iio_dev *indio_dev,
838c2ecf20Sopenharmony_ci				struct iio_chan_spec const *chan,
848c2ecf20Sopenharmony_ci				int *val, int *val2, long mask)
858c2ecf20Sopenharmony_ci{
868c2ecf20Sopenharmony_ci	struct mc3230_data *data = iio_priv(indio_dev);
878c2ecf20Sopenharmony_ci	int ret;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	switch (mask) {
908c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
918c2ecf20Sopenharmony_ci		ret = i2c_smbus_read_byte_data(data->client, chan->address);
928c2ecf20Sopenharmony_ci		if (ret < 0)
938c2ecf20Sopenharmony_ci			return ret;
948c2ecf20Sopenharmony_ci		*val = sign_extend32(ret, 7);
958c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
968c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
978c2ecf20Sopenharmony_ci		*val = 0;
988c2ecf20Sopenharmony_ci		*val2 = mc3230_nscale;
998c2ecf20Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
1008c2ecf20Sopenharmony_ci	default:
1018c2ecf20Sopenharmony_ci		return -EINVAL;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistatic const struct iio_info mc3230_info = {
1068c2ecf20Sopenharmony_ci	.read_raw	= mc3230_read_raw,
1078c2ecf20Sopenharmony_ci};
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic int mc3230_probe(struct i2c_client *client,
1108c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
1118c2ecf20Sopenharmony_ci{
1128c2ecf20Sopenharmony_ci	int ret;
1138c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
1148c2ecf20Sopenharmony_ci	struct mc3230_data *data;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* First check chip-id and product-id */
1178c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
1188c2ecf20Sopenharmony_ci	if (ret != MC3230_CHIP_ID)
1198c2ecf20Sopenharmony_ci		return (ret < 0) ? ret : -ENODEV;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
1228c2ecf20Sopenharmony_ci	if (ret != MC3230_PRODUCT_CODE)
1238c2ecf20Sopenharmony_ci		return (ret < 0) ? ret : -ENODEV;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
1268c2ecf20Sopenharmony_ci	if (!indio_dev) {
1278c2ecf20Sopenharmony_ci		dev_err(&client->dev, "iio allocation failed!\n");
1288c2ecf20Sopenharmony_ci		return -ENOMEM;
1298c2ecf20Sopenharmony_ci	}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	data = iio_priv(indio_dev);
1328c2ecf20Sopenharmony_ci	data->client = client;
1338c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	indio_dev->info = &mc3230_info;
1368c2ecf20Sopenharmony_ci	indio_dev->name = "mc3230";
1378c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
1388c2ecf20Sopenharmony_ci	indio_dev->channels = mc3230_channels;
1398c2ecf20Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
1428c2ecf20Sopenharmony_ci	if (ret < 0)
1438c2ecf20Sopenharmony_ci		return ret;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	ret = iio_device_register(indio_dev);
1468c2ecf20Sopenharmony_ci	if (ret < 0) {
1478c2ecf20Sopenharmony_ci		dev_err(&client->dev, "device_register failed\n");
1488c2ecf20Sopenharmony_ci		mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	return ret;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int mc3230_remove(struct i2c_client *client)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	iio_device_unregister(indio_dev);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
1618c2ecf20Sopenharmony_ci}
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
1648c2ecf20Sopenharmony_cistatic int mc3230_suspend(struct device *dev)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct mc3230_data *data;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
1718c2ecf20Sopenharmony_ci}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int mc3230_resume(struct device *dev)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	struct mc3230_data *data;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci#endif
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic const struct i2c_device_id mc3230_i2c_id[] = {
1868c2ecf20Sopenharmony_ci	{"mc3230", 0},
1878c2ecf20Sopenharmony_ci	{}
1888c2ecf20Sopenharmony_ci};
1898c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic struct i2c_driver mc3230_driver = {
1928c2ecf20Sopenharmony_ci	.driver = {
1938c2ecf20Sopenharmony_ci		.name = "mc3230",
1948c2ecf20Sopenharmony_ci		.pm = &mc3230_pm_ops,
1958c2ecf20Sopenharmony_ci	},
1968c2ecf20Sopenharmony_ci	.probe		= mc3230_probe,
1978c2ecf20Sopenharmony_ci	.remove		= mc3230_remove,
1988c2ecf20Sopenharmony_ci	.id_table	= mc3230_i2c_id,
1998c2ecf20Sopenharmony_ci};
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cimodule_i2c_driver(mc3230_driver);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
2048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
2058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
206