162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface)
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Enter bugs at http://blackfin.uclinux.org/
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/input.h>	/* BUS_I2C */
1162306a36Sopenharmony_ci#include <linux/i2c.h>
1262306a36Sopenharmony_ci#include <linux/module.h>
1362306a36Sopenharmony_ci#include <linux/of.h>
1462306a36Sopenharmony_ci#include <linux/types.h>
1562306a36Sopenharmony_ci#include <linux/pm.h>
1662306a36Sopenharmony_ci#include "adxl34x.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_cistatic int adxl34x_smbus_read(struct device *dev, unsigned char reg)
1962306a36Sopenharmony_ci{
2062306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	return i2c_smbus_read_byte_data(client, reg);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic int adxl34x_smbus_write(struct device *dev,
2662306a36Sopenharmony_ci			       unsigned char reg, unsigned char val)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	return i2c_smbus_write_byte_data(client, reg, val);
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic int adxl34x_smbus_read_block(struct device *dev,
3462306a36Sopenharmony_ci				    unsigned char reg, int count,
3562306a36Sopenharmony_ci				    void *buf)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return i2c_smbus_read_i2c_block_data(client, reg, count, buf);
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic int adxl34x_i2c_read_block(struct device *dev,
4362306a36Sopenharmony_ci				  unsigned char reg, int count,
4462306a36Sopenharmony_ci				  void *buf)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	struct i2c_client *client = to_i2c_client(dev);
4762306a36Sopenharmony_ci	int ret;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	ret = i2c_master_send(client, &reg, 1);
5062306a36Sopenharmony_ci	if (ret < 0)
5162306a36Sopenharmony_ci		return ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ret = i2c_master_recv(client, buf, count);
5462306a36Sopenharmony_ci	if (ret < 0)
5562306a36Sopenharmony_ci		return ret;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (ret != count)
5862306a36Sopenharmony_ci		return -EIO;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return 0;
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic const struct adxl34x_bus_ops adxl34x_smbus_bops = {
6462306a36Sopenharmony_ci	.bustype	= BUS_I2C,
6562306a36Sopenharmony_ci	.write		= adxl34x_smbus_write,
6662306a36Sopenharmony_ci	.read		= adxl34x_smbus_read,
6762306a36Sopenharmony_ci	.read_block	= adxl34x_smbus_read_block,
6862306a36Sopenharmony_ci};
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_cistatic const struct adxl34x_bus_ops adxl34x_i2c_bops = {
7162306a36Sopenharmony_ci	.bustype	= BUS_I2C,
7262306a36Sopenharmony_ci	.write		= adxl34x_smbus_write,
7362306a36Sopenharmony_ci	.read		= adxl34x_smbus_read,
7462306a36Sopenharmony_ci	.read_block	= adxl34x_i2c_read_block,
7562306a36Sopenharmony_ci};
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_cistatic int adxl34x_i2c_probe(struct i2c_client *client)
7862306a36Sopenharmony_ci{
7962306a36Sopenharmony_ci	struct adxl34x *ac;
8062306a36Sopenharmony_ci	int error;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	error = i2c_check_functionality(client->adapter,
8362306a36Sopenharmony_ci			I2C_FUNC_SMBUS_BYTE_DATA);
8462306a36Sopenharmony_ci	if (!error) {
8562306a36Sopenharmony_ci		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
8662306a36Sopenharmony_ci		return -EIO;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ac = adxl34x_probe(&client->dev, client->irq, false,
9062306a36Sopenharmony_ci			   i2c_check_functionality(client->adapter,
9162306a36Sopenharmony_ci						   I2C_FUNC_SMBUS_READ_I2C_BLOCK) ?
9262306a36Sopenharmony_ci				&adxl34x_smbus_bops : &adxl34x_i2c_bops);
9362306a36Sopenharmony_ci	if (IS_ERR(ac))
9462306a36Sopenharmony_ci		return PTR_ERR(ac);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	i2c_set_clientdata(client, ac);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return 0;
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_cistatic void adxl34x_i2c_remove(struct i2c_client *client)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	struct adxl34x *ac = i2c_get_clientdata(client);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	adxl34x_remove(ac);
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic const struct i2c_device_id adxl34x_id[] = {
10962306a36Sopenharmony_ci	{ "adxl34x", 0 },
11062306a36Sopenharmony_ci	{ }
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, adxl34x_id);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_cistatic const struct of_device_id adxl34x_of_id[] = {
11662306a36Sopenharmony_ci	/*
11762306a36Sopenharmony_ci	 * The ADXL346 is backward-compatible with the ADXL345. Differences are
11862306a36Sopenharmony_ci	 * handled by runtime detection of the device model, there's thus no
11962306a36Sopenharmony_ci	 * need for listing the "adi,adxl346" compatible value explicitly.
12062306a36Sopenharmony_ci	 */
12162306a36Sopenharmony_ci	{ .compatible = "adi,adxl345", },
12262306a36Sopenharmony_ci	/*
12362306a36Sopenharmony_ci	 * Deprecated, DT nodes should use one or more of the device-specific
12462306a36Sopenharmony_ci	 * compatible values "adi,adxl345" and "adi,adxl346".
12562306a36Sopenharmony_ci	 */
12662306a36Sopenharmony_ci	{ .compatible = "adi,adxl34x", },
12762306a36Sopenharmony_ci	{ }
12862306a36Sopenharmony_ci};
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, adxl34x_of_id);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_cistatic struct i2c_driver adxl34x_driver = {
13362306a36Sopenharmony_ci	.driver = {
13462306a36Sopenharmony_ci		.name = "adxl34x",
13562306a36Sopenharmony_ci		.pm = pm_sleep_ptr(&adxl34x_pm),
13662306a36Sopenharmony_ci		.of_match_table = adxl34x_of_id,
13762306a36Sopenharmony_ci	},
13862306a36Sopenharmony_ci	.probe    = adxl34x_i2c_probe,
13962306a36Sopenharmony_ci	.remove   = adxl34x_i2c_remove,
14062306a36Sopenharmony_ci	.id_table = adxl34x_id,
14162306a36Sopenharmony_ci};
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_cimodule_i2c_driver(adxl34x_driver);
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ciMODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
14662306a36Sopenharmony_ciMODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver");
14762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
148