18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/**
38c2ecf20Sopenharmony_ci * IIO driver for the 3-axis accelerometer Domintech ARD10.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
68c2ecf20Sopenharmony_ci * Copyright (c) 2012 Domintech Technology Co., Ltd
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/module.h>
108c2ecf20Sopenharmony_ci#include <linux/i2c.h>
118c2ecf20Sopenharmony_ci#include <linux/iio/iio.h>
128c2ecf20Sopenharmony_ci#include <linux/iio/sysfs.h>
138c2ecf20Sopenharmony_ci#include <linux/byteorder/generic.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define DMARD10_REG_ACTR			0x00
168c2ecf20Sopenharmony_ci#define DMARD10_REG_AFEM			0x0c
178c2ecf20Sopenharmony_ci#define DMARD10_REG_STADR			0x12
188c2ecf20Sopenharmony_ci#define DMARD10_REG_STAINT			0x1c
198c2ecf20Sopenharmony_ci#define DMARD10_REG_MISC2			0x1f
208c2ecf20Sopenharmony_ci#define DMARD10_REG_PD				0x21
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define DMARD10_MODE_OFF			0x00
238c2ecf20Sopenharmony_ci#define DMARD10_MODE_STANDBY			0x02
248c2ecf20Sopenharmony_ci#define DMARD10_MODE_ACTIVE			0x06
258c2ecf20Sopenharmony_ci#define DMARD10_MODE_READ_OTP			0x12
268c2ecf20Sopenharmony_ci#define DMARD10_MODE_RESET_DATA_PATH		0x82
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */
298c2ecf20Sopenharmony_ci#define DMARD10_VALUE_AFEM_AFEN_NORMAL		0x8f
308c2ecf20Sopenharmony_ci/* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */
318c2ecf20Sopenharmony_ci#define DMARD10_VALUE_CKSEL_ODR_100_204		0x74
328c2ecf20Sopenharmony_ci/* INTC[6:5]=b'00 */
338c2ecf20Sopenharmony_ci#define DMARD10_VALUE_INTC			0x00
348c2ecf20Sopenharmony_ci/* TAP1/TAP2 Average 2 */
358c2ecf20Sopenharmony_ci#define DMARD10_VALUE_TAPNS_AVE_2		0x11
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci#define DMARD10_VALUE_STADR			0x55
388c2ecf20Sopenharmony_ci#define DMARD10_VALUE_STAINT			0xaa
398c2ecf20Sopenharmony_ci#define DMARD10_VALUE_MISC2_OSCA_EN		0x08
408c2ecf20Sopenharmony_ci#define DMARD10_VALUE_PD_RST			0x52
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* Offsets into the buffer read in dmard10_read_raw() */
438c2ecf20Sopenharmony_ci#define DMARD10_X_OFFSET			1
448c2ecf20Sopenharmony_ci#define DMARD10_Y_OFFSET			2
458c2ecf20Sopenharmony_ci#define DMARD10_Z_OFFSET			3
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci/*
488c2ecf20Sopenharmony_ci * a value of + or -128 corresponds to + or - 1G
498c2ecf20Sopenharmony_ci * scale = 9.81 / 128 = 0.076640625
508c2ecf20Sopenharmony_ci */
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic const int dmard10_nscale = 76640625;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci#define DMARD10_CHANNEL(reg, axis) {	\
558c2ecf20Sopenharmony_ci	.type = IIO_ACCEL,	\
568c2ecf20Sopenharmony_ci	.address = reg,	\
578c2ecf20Sopenharmony_ci	.modified = 1,	\
588c2ecf20Sopenharmony_ci	.channel2 = IIO_MOD_##axis,	\
598c2ecf20Sopenharmony_ci	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
608c2ecf20Sopenharmony_ci	.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),	\
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic const struct iio_chan_spec dmard10_channels[] = {
648c2ecf20Sopenharmony_ci	DMARD10_CHANNEL(DMARD10_X_OFFSET, X),
658c2ecf20Sopenharmony_ci	DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y),
668c2ecf20Sopenharmony_ci	DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z),
678c2ecf20Sopenharmony_ci};
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_cistruct dmard10_data {
708c2ecf20Sopenharmony_ci	struct i2c_client *client;
718c2ecf20Sopenharmony_ci};
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* Init sequence taken from the android driver */
748c2ecf20Sopenharmony_cistatic int dmard10_reset(struct i2c_client *client)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned char buffer[7];
778c2ecf20Sopenharmony_ci	int ret;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* 1. Powerdown reset */
808c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD,
818c2ecf20Sopenharmony_ci						DMARD10_VALUE_PD_RST);
828c2ecf20Sopenharmony_ci	if (ret < 0)
838c2ecf20Sopenharmony_ci		return ret;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	/*
868c2ecf20Sopenharmony_ci	 * 2. ACTR => Standby mode => Download OTP to parameter reg =>
878c2ecf20Sopenharmony_ci	 *    Standby mode => Reset data path => Standby mode
888c2ecf20Sopenharmony_ci	 */
898c2ecf20Sopenharmony_ci	buffer[0] = DMARD10_REG_ACTR;
908c2ecf20Sopenharmony_ci	buffer[1] = DMARD10_MODE_STANDBY;
918c2ecf20Sopenharmony_ci	buffer[2] = DMARD10_MODE_READ_OTP;
928c2ecf20Sopenharmony_ci	buffer[3] = DMARD10_MODE_STANDBY;
938c2ecf20Sopenharmony_ci	buffer[4] = DMARD10_MODE_RESET_DATA_PATH;
948c2ecf20Sopenharmony_ci	buffer[5] = DMARD10_MODE_STANDBY;
958c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, buffer, 6);
968c2ecf20Sopenharmony_ci	if (ret < 0)
978c2ecf20Sopenharmony_ci		return ret;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */
1008c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2,
1018c2ecf20Sopenharmony_ci						DMARD10_VALUE_MISC2_OSCA_EN);
1028c2ecf20Sopenharmony_ci	if (ret < 0)
1038c2ecf20Sopenharmony_ci		return ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* 4. AFEN = 1 (AFE will powerdown after ADC) */
1068c2ecf20Sopenharmony_ci	buffer[0] = DMARD10_REG_AFEM;
1078c2ecf20Sopenharmony_ci	buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL;
1088c2ecf20Sopenharmony_ci	buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204;
1098c2ecf20Sopenharmony_ci	buffer[3] = DMARD10_VALUE_INTC;
1108c2ecf20Sopenharmony_ci	buffer[4] = DMARD10_VALUE_TAPNS_AVE_2;
1118c2ecf20Sopenharmony_ci	buffer[5] = 0x00; /* DLYC, no delay timing */
1128c2ecf20Sopenharmony_ci	buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */
1138c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, buffer, 7);
1148c2ecf20Sopenharmony_ci	if (ret < 0)
1158c2ecf20Sopenharmony_ci		return ret;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	/* 5. Activation mode */
1188c2ecf20Sopenharmony_ci	ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR,
1198c2ecf20Sopenharmony_ci						DMARD10_MODE_ACTIVE);
1208c2ecf20Sopenharmony_ci	if (ret < 0)
1218c2ecf20Sopenharmony_ci		return ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci/* Shutdown sequence taken from the android driver */
1278c2ecf20Sopenharmony_cistatic int dmard10_shutdown(struct i2c_client *client)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	unsigned char buffer[3];
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	buffer[0] = DMARD10_REG_ACTR;
1328c2ecf20Sopenharmony_ci	buffer[1] = DMARD10_MODE_STANDBY;
1338c2ecf20Sopenharmony_ci	buffer[2] = DMARD10_MODE_OFF;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return i2c_master_send(client, buffer, 3);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int dmard10_read_raw(struct iio_dev *indio_dev,
1398c2ecf20Sopenharmony_ci				struct iio_chan_spec const *chan,
1408c2ecf20Sopenharmony_ci				int *val, int *val2, long mask)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct dmard10_data *data = iio_priv(indio_dev);
1438c2ecf20Sopenharmony_ci	__le16 buf[4];
1448c2ecf20Sopenharmony_ci	int ret;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	switch (mask) {
1478c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_RAW:
1488c2ecf20Sopenharmony_ci		/*
1498c2ecf20Sopenharmony_ci		 * Read 8 bytes starting at the REG_STADR register, trying to
1508c2ecf20Sopenharmony_ci		 * read the individual X, Y, Z registers will always read 0.
1518c2ecf20Sopenharmony_ci		 */
1528c2ecf20Sopenharmony_ci		ret = i2c_smbus_read_i2c_block_data(data->client,
1538c2ecf20Sopenharmony_ci						    DMARD10_REG_STADR,
1548c2ecf20Sopenharmony_ci						    sizeof(buf), (u8 *)buf);
1558c2ecf20Sopenharmony_ci		if (ret < 0)
1568c2ecf20Sopenharmony_ci			return ret;
1578c2ecf20Sopenharmony_ci		ret = le16_to_cpu(buf[chan->address]);
1588c2ecf20Sopenharmony_ci		*val = sign_extend32(ret, 12);
1598c2ecf20Sopenharmony_ci		return IIO_VAL_INT;
1608c2ecf20Sopenharmony_ci	case IIO_CHAN_INFO_SCALE:
1618c2ecf20Sopenharmony_ci		*val = 0;
1628c2ecf20Sopenharmony_ci		*val2 = dmard10_nscale;
1638c2ecf20Sopenharmony_ci		return IIO_VAL_INT_PLUS_NANO;
1648c2ecf20Sopenharmony_ci	default:
1658c2ecf20Sopenharmony_ci		return -EINVAL;
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic const struct iio_info dmard10_info = {
1708c2ecf20Sopenharmony_ci	.read_raw	= dmard10_read_raw,
1718c2ecf20Sopenharmony_ci};
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_cistatic int dmard10_probe(struct i2c_client *client,
1748c2ecf20Sopenharmony_ci			const struct i2c_device_id *id)
1758c2ecf20Sopenharmony_ci{
1768c2ecf20Sopenharmony_ci	int ret;
1778c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev;
1788c2ecf20Sopenharmony_ci	struct dmard10_data *data;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	/* These 2 registers have special POR reset values used for id */
1818c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
1828c2ecf20Sopenharmony_ci	if (ret != DMARD10_VALUE_STADR)
1838c2ecf20Sopenharmony_ci		return (ret < 0) ? ret : -ENODEV;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
1868c2ecf20Sopenharmony_ci	if (ret != DMARD10_VALUE_STAINT)
1878c2ecf20Sopenharmony_ci		return (ret < 0) ? ret : -ENODEV;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
1908c2ecf20Sopenharmony_ci	if (!indio_dev) {
1918c2ecf20Sopenharmony_ci		dev_err(&client->dev, "iio allocation failed!\n");
1928c2ecf20Sopenharmony_ci		return -ENOMEM;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	data = iio_priv(indio_dev);
1968c2ecf20Sopenharmony_ci	data->client = client;
1978c2ecf20Sopenharmony_ci	i2c_set_clientdata(client, indio_dev);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	indio_dev->info = &dmard10_info;
2008c2ecf20Sopenharmony_ci	indio_dev->name = "dmard10";
2018c2ecf20Sopenharmony_ci	indio_dev->modes = INDIO_DIRECT_MODE;
2028c2ecf20Sopenharmony_ci	indio_dev->channels = dmard10_channels;
2038c2ecf20Sopenharmony_ci	indio_dev->num_channels = ARRAY_SIZE(dmard10_channels);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	ret = dmard10_reset(client);
2068c2ecf20Sopenharmony_ci	if (ret < 0)
2078c2ecf20Sopenharmony_ci		return ret;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	ret = iio_device_register(indio_dev);
2108c2ecf20Sopenharmony_ci	if (ret < 0) {
2118c2ecf20Sopenharmony_ci		dev_err(&client->dev, "device_register failed\n");
2128c2ecf20Sopenharmony_ci		dmard10_shutdown(client);
2138c2ecf20Sopenharmony_ci	}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	return ret;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic int dmard10_remove(struct i2c_client *client)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct iio_dev *indio_dev = i2c_get_clientdata(client);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	iio_device_unregister(indio_dev);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	return dmard10_shutdown(client);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
2288c2ecf20Sopenharmony_cistatic int dmard10_suspend(struct device *dev)
2298c2ecf20Sopenharmony_ci{
2308c2ecf20Sopenharmony_ci	return dmard10_shutdown(to_i2c_client(dev));
2318c2ecf20Sopenharmony_ci}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_cistatic int dmard10_resume(struct device *dev)
2348c2ecf20Sopenharmony_ci{
2358c2ecf20Sopenharmony_ci	return dmard10_reset(to_i2c_client(dev));
2368c2ecf20Sopenharmony_ci}
2378c2ecf20Sopenharmony_ci#endif
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cistatic const struct i2c_device_id dmard10_i2c_id[] = {
2428c2ecf20Sopenharmony_ci	{"dmard10", 0},
2438c2ecf20Sopenharmony_ci	{}
2448c2ecf20Sopenharmony_ci};
2458c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct i2c_driver dmard10_driver = {
2488c2ecf20Sopenharmony_ci	.driver = {
2498c2ecf20Sopenharmony_ci		.name = "dmard10",
2508c2ecf20Sopenharmony_ci		.pm = &dmard10_pm_ops,
2518c2ecf20Sopenharmony_ci	},
2528c2ecf20Sopenharmony_ci	.probe		= dmard10_probe,
2538c2ecf20Sopenharmony_ci	.remove		= dmard10_remove,
2548c2ecf20Sopenharmony_ci	.id_table	= dmard10_i2c_id,
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cimodule_i2c_driver(dmard10_driver);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
2608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver");
2618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
262