| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * ADLX345/346 Three-Axis Digital Accelerometers (I2C Interface) |
| * |
| * Enter bugs at http://blackfin.uclinux.org/ |
| * |
| * Copyright (C) 2009 Michael Hennerich, Analog Devices Inc. |
| */ |
| |
| #include <linux/input.h> /* BUS_I2C */ |
| #include <linux/i2c.h> |
| #include <linux/module.h> |
| #include <linux/of.h> |
| #include <linux/types.h> |
| #include <linux/pm.h> |
| #include "adxl34x.h" |
| |
| static int adxl34x_smbus_read(struct device *dev, unsigned char reg) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| |
| return i2c_smbus_read_byte_data(client, reg); |
| } |
| |
| static int adxl34x_smbus_write(struct device *dev, |
| unsigned char reg, unsigned char val) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| |
| return i2c_smbus_write_byte_data(client, reg, val); |
| } |
| |
| static int adxl34x_smbus_read_block(struct device *dev, |
| unsigned char reg, int count, |
| void *buf) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| |
| return i2c_smbus_read_i2c_block_data(client, reg, count, buf); |
| } |
| |
| static int adxl34x_i2c_read_block(struct device *dev, |
| unsigned char reg, int count, |
| void *buf) |
| { |
| struct i2c_client *client = to_i2c_client(dev); |
| int ret; |
| |
| ret = i2c_master_send(client, ®, 1); |
| if (ret < 0) |
| return ret; |
| |
| ret = i2c_master_recv(client, buf, count); |
| if (ret < 0) |
| return ret; |
| |
| if (ret != count) |
| return -EIO; |
| |
| return 0; |
| } |
| |
| static const struct adxl34x_bus_ops adxl34x_smbus_bops = { |
| .bustype = BUS_I2C, |
| .write = adxl34x_smbus_write, |
| .read = adxl34x_smbus_read, |
| .read_block = adxl34x_smbus_read_block, |
| }; |
| |
| static const struct adxl34x_bus_ops adxl34x_i2c_bops = { |
| .bustype = BUS_I2C, |
| .write = adxl34x_smbus_write, |
| .read = adxl34x_smbus_read, |
| .read_block = adxl34x_i2c_read_block, |
| }; |
| |
| static int adxl34x_i2c_probe(struct i2c_client *client) |
| { |
| struct adxl34x *ac; |
| int error; |
| |
| error = i2c_check_functionality(client->adapter, |
| I2C_FUNC_SMBUS_BYTE_DATA); |
| if (!error) { |
| dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); |
| return -EIO; |
| } |
| |
| ac = adxl34x_probe(&client->dev, client->irq, false, |
| i2c_check_functionality(client->adapter, |
| I2C_FUNC_SMBUS_READ_I2C_BLOCK) ? |
| &adxl34x_smbus_bops : &adxl34x_i2c_bops); |
| if (IS_ERR(ac)) |
| return PTR_ERR(ac); |
| |
| i2c_set_clientdata(client, ac); |
| |
| return 0; |
| } |
| |
| static const struct i2c_device_id adxl34x_id[] = { |
| { "adxl34x" }, |
| { } |
| }; |
| |
| MODULE_DEVICE_TABLE(i2c, adxl34x_id); |
| |
| static const struct of_device_id adxl34x_of_id[] = { |
| /* |
| * The ADXL346 is backward-compatible with the ADXL345. Differences are |
| * handled by runtime detection of the device model, there's thus no |
| * need for listing the "adi,adxl346" compatible value explicitly. |
| */ |
| { .compatible = "adi,adxl345", }, |
| /* |
| * Deprecated, DT nodes should use one or more of the device-specific |
| * compatible values "adi,adxl345" and "adi,adxl346". |
| */ |
| { .compatible = "adi,adxl34x", }, |
| { } |
| }; |
| |
| MODULE_DEVICE_TABLE(of, adxl34x_of_id); |
| |
| static struct i2c_driver adxl34x_driver = { |
| .driver = { |
| .name = "adxl34x", |
| .dev_groups = adxl34x_groups, |
| .pm = pm_sleep_ptr(&adxl34x_pm), |
| .of_match_table = adxl34x_of_id, |
| }, |
| .probe = adxl34x_i2c_probe, |
| .id_table = adxl34x_id, |
| }; |
| |
| module_i2c_driver(adxl34x_driver); |
| |
| MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); |
| MODULE_DESCRIPTION("ADXL345/346 Three-Axis Digital Accelerometer I2C Bus Driver"); |
| MODULE_LICENSE("GPL"); |