162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * mCube MC3230 3-Axis Accelerometer
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/i2c.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/iio/iio.h>
1362306a36Sopenharmony_ci#include <linux/iio/sysfs.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#define MC3230_REG_XOUT			0x00
1662306a36Sopenharmony_ci#define MC3230_REG_YOUT			0x01
1762306a36Sopenharmony_ci#define MC3230_REG_ZOUT			0x02
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define MC3230_REG_MODE			0x07
2062306a36Sopenharmony_ci#define MC3230_MODE_OPCON_MASK		0x03
2162306a36Sopenharmony_ci#define MC3230_MODE_OPCON_WAKE		0x01
2262306a36Sopenharmony_ci#define MC3230_MODE_OPCON_STANDBY	0x03
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define MC3230_REG_CHIP_ID		0x18
2562306a36Sopenharmony_ci#define MC3230_CHIP_ID			0x01
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define MC3230_REG_PRODUCT_CODE		0x3b
2862306a36Sopenharmony_ci#define MC3230_PRODUCT_CODE		0x19
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/*
3162306a36Sopenharmony_ci * The accelerometer has one measurement range:
3262306a36Sopenharmony_ci *
3362306a36Sopenharmony_ci * -1.5g - +1.5g (8-bit, signed)
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1)	= 0.115411765
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic const int mc3230_nscale = 115411765;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#define MC3230_CHANNEL(reg, axis) {	\
4162306a36Sopenharmony_ci	.type = IIO_ACCEL,	\
4262306a36Sopenharmony_ci	.address = reg,	\
4362306a36Sopenharmony_ci	.modified = 1,	\
4462306a36Sopenharmony_ci	.channel2 = IIO_MOD_##axis,	\
4562306a36Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
4662306a36Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const struct iio_chan_spec mc3230_channels[] = {
5062306a36Sopenharmony_ci	MC3230_CHANNEL(MC3230_REG_XOUT, X),
5162306a36Sopenharmony_ci	MC3230_CHANNEL(MC3230_REG_YOUT, Y),
5262306a36Sopenharmony_ci	MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistruct mc3230_data {
5662306a36Sopenharmony_ci	struct i2c_client *client;
5762306a36Sopenharmony_ci};
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int mc3230_set_opcon(struct mc3230_data *data, int opcon)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	int ret;
6262306a36Sopenharmony_ci	struct i2c_client *client = data->client;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
6562306a36Sopenharmony_ci	if (ret < 0) {
6662306a36Sopenharmony_ci		dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
6762306a36Sopenharmony_ci		return ret;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	ret &= ~MC3230_MODE_OPCON_MASK;
7162306a36Sopenharmony_ci	ret |= opcon;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
7462306a36Sopenharmony_ci	if (ret < 0) {
7562306a36Sopenharmony_ci		dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
7662306a36Sopenharmony_ci		return ret;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ci}
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cistatic int mc3230_read_raw(struct iio_dev *indio_dev,
8362306a36Sopenharmony_ci				struct iio_chan_spec const *chan,
8462306a36Sopenharmony_ci				int *val, int *val2, long mask)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct mc3230_data *data = iio_priv(indio_dev);
8762306a36Sopenharmony_ci	int ret;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	switch (mask) {
9062306a36Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
9162306a36Sopenharmony_ci		ret = i2c_smbus_read_byte_data(data->client, chan->address);
9262306a36Sopenharmony_ci		if (ret < 0)
9362306a36Sopenharmony_ci			return ret;
9462306a36Sopenharmony_ci		*val = sign_extend32(ret, 7);
9562306a36Sopenharmony_ci		return IIO_VAL_INT;
9662306a36Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
9762306a36Sopenharmony_ci		*val = 0;
9862306a36Sopenharmony_ci		*val2 = mc3230_nscale;
9962306a36Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
10062306a36Sopenharmony_ci	default:
10162306a36Sopenharmony_ci		return -EINVAL;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic const struct iio_info mc3230_info = {
10662306a36Sopenharmony_ci	.read_raw	= mc3230_read_raw,
10762306a36Sopenharmony_ci};
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int mc3230_probe(struct i2c_client *client)
11062306a36Sopenharmony_ci{
11162306a36Sopenharmony_ci	int ret;
11262306a36Sopenharmony_ci	struct iio_dev *indio_dev;
11362306a36Sopenharmony_ci	struct mc3230_data *data;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	/* First check chip-id and product-id */
11662306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
11762306a36Sopenharmony_ci	if (ret != MC3230_CHIP_ID)
11862306a36Sopenharmony_ci		return (ret < 0) ? ret : -ENODEV;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
12162306a36Sopenharmony_ci	if (ret != MC3230_PRODUCT_CODE)
12262306a36Sopenharmony_ci		return (ret < 0) ? ret : -ENODEV;
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
12562306a36Sopenharmony_ci	if (!indio_dev) {
12662306a36Sopenharmony_ci		dev_err(&client->dev, "iio allocation failed!\n");
12762306a36Sopenharmony_ci		return -ENOMEM;
12862306a36Sopenharmony_ci	}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	data = iio_priv(indio_dev);
13162306a36Sopenharmony_ci	data->client = client;
13262306a36Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	indio_dev->info = &mc3230_info;
13562306a36Sopenharmony_ci	indio_dev->name = "mc3230";
13662306a36Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
13762306a36Sopenharmony_ci	indio_dev->channels = mc3230_channels;
13862306a36Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
14162306a36Sopenharmony_ci	if (ret < 0)
14262306a36Sopenharmony_ci		return ret;
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci	ret = iio_device_register(indio_dev);
14562306a36Sopenharmony_ci	if (ret < 0) {
14662306a36Sopenharmony_ci		dev_err(&client->dev, "device_register failed\n");
14762306a36Sopenharmony_ci		mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	return ret;
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic void mc3230_remove(struct i2c_client *client)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	iio_device_unregister(indio_dev);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int mc3230_suspend(struct device *dev)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct mc3230_data *data;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
16962306a36Sopenharmony_ci}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic int mc3230_resume(struct device *dev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct mc3230_data *data;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic const struct i2c_device_id mc3230_i2c_id[] = {
18362306a36Sopenharmony_ci	{"mc3230", 0},
18462306a36Sopenharmony_ci	{}
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_cistatic struct i2c_driver mc3230_driver = {
18962306a36Sopenharmony_ci	.driver = {
19062306a36Sopenharmony_ci		.name = "mc3230",
19162306a36Sopenharmony_ci		.pm = pm_sleep_ptr(&mc3230_pm_ops),
19262306a36Sopenharmony_ci	},
19362306a36Sopenharmony_ci	.probe		= mc3230_probe,
19462306a36Sopenharmony_ci	.remove		= mc3230_remove,
19562306a36Sopenharmony_ci	.id_table	= mc3230_i2c_id,
19662306a36Sopenharmony_ci};
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_cimodule_i2c_driver(mc3230_driver);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
20162306a36Sopenharmony_ciMODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
20262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
203