blob: 1fa4530550f7f9105c58c5ecf254ee6df5488a47 [file] [log] [blame]
/*
* Copyright (C) 2016 Ambarella, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/of_i2c.h>
#include <linux/iio/iio.h>
#include "inv_mpu_iio.h"
int inv_mpu_i2cmst_read_data(struct inv_mpu6050_state *st,
u16 addr, u8 command, int size, union i2c_smbus_data *data)
{
unsigned int d, num;
void *buf;
int result;
result = inv_mpu6050_set_power_itg(st, true);
if (result)
return result;
switch (size) {
case I2C_SMBUS_BYTE_DATA:
num = 1;
buf = &data->byte;
break;
case I2C_SMBUS_WORD_DATA:
num = 2;
buf = &data->word;
break;
case I2C_SMBUS_I2C_BLOCK_DATA:
num = data->block[0];
buf = &data->block[1];
break;
default:
return -EINVAL;
}
d = INV_MPU6050_BIT_I2C_SLV0_EN | num;
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_CTRL, d);
if (result)
return result;
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_REG, command);
if (result)
return result;
d = INV_MPU6050_BIT_I2C_SLV0_RNW | addr;
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_ADDR, d);
if (result)
return result;
d = 1000 / st->chip_config.fifo_rate;
msleep(d + d / 2);
result = regmap_bulk_read(st->map, INV_MPU6050_REG_EXT_SENS_DATA, buf, num);
if (result)
return result;
result = inv_mpu6050_set_power_itg(st, false);
if (result)
return result;
return 0;
}
int inv_mpu_i2cmst_write_data(struct inv_mpu6050_state *st,
u16 addr, u8 command, int size, union i2c_smbus_data *data)
{
unsigned int d;
int result;
if (size != I2C_SMBUS_BYTE_DATA) {
dev_err(&st->adap->dev, "funky write size %d\n", size);
return -EINVAL;
}
result = inv_mpu6050_set_power_itg(st, true);
if (result)
return result;
d = INV_MPU6050_BIT_I2C_SLV0_EN | 1; /* only one byte allowed to write */
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_CTRL, d);
if (result)
return result;
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_REG, command);
if (result)
return result;
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_DO, data->byte);
if (result)
return result;
result = regmap_write(st->map, INV_MPU6050_REG_I2C_SLV0_ADDR, addr);
if (result)
return result;
d = 1000 / st->chip_config.fifo_rate;
msleep(d + d / 2);
result = inv_mpu6050_set_power_itg(st, false);
if (result)
return result;
return 0;
}
static int inv_mpu_i2cmst_xfer(struct i2c_adapter *adap, u16 addr,
unsigned short flags, char rw, u8 command,
int size, union i2c_smbus_data *data)
{
struct inv_mpu6050_state *st = i2c_get_adapdata(adap);
int ret = -EINVAL;
if (rw == I2C_SMBUS_WRITE)
ret = inv_mpu_i2cmst_write_data(st, addr, command, size, data);
else if (rw == I2C_SMBUS_READ)
ret = inv_mpu_i2cmst_read_data(st, addr, command, size, data);
return ret;
}
static u32 inv_mpu_i2cmst_func(struct i2c_adapter *adap)
{
return I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK;
}
static const struct i2c_algorithm inv_mpu_i2cmst_algo = {
.smbus_xfer = inv_mpu_i2cmst_xfer,
.functionality = inv_mpu_i2cmst_func,
};
int inv_mpu_i2cmst_probe(struct iio_dev *indio_dev)
{
struct inv_mpu6050_state *st = iio_priv(indio_dev);
struct i2c_adapter *adap;
int ret;
adap = devm_kzalloc(&indio_dev->dev, sizeof(*adap), GFP_KERNEL);
if (!adap)
return -ENOMEM;
adap->owner = THIS_MODULE;
adap->class = I2C_CLASS_HWMON;
strlcpy(adap->name, "INV MPU I2CMST adapter", sizeof(adap->name));
adap->algo = &inv_mpu_i2cmst_algo;
adap->dev.parent = &indio_dev->dev;
adap->dev.of_node = indio_dev->dev.of_node;
i2c_set_adapdata(adap, st);
st->adap = adap;
ret = i2c_add_adapter(adap);
if (ret < 0) {
dev_err(&indio_dev->dev, "failed to add I2C master: %d\n", ret);
return ret;
}
of_i2c_register_devices(adap);
return 0;
}
void inv_mpu_i2cmst_remove(struct iio_dev *indio_dev)
{
struct inv_mpu6050_state *st = iio_priv(indio_dev);
i2c_del_adapter(st->adap);
}
MODULE_AUTHOR("Cao Rongrong <rrcao@ambarella.com>");
MODULE_DESCRIPTION("Invensense MPU9250 I2C Master driver");
MODULE_LICENSE("GPL");