mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-19 10:14:23 +08:00
86effbe0d1
The commit message says that we are allowed to read and write up to 32
bytes but the code only allows us to write 31 bytes. In other words,
the ">=" should be changed to ">". But this is already checked in
regmap_raw_read()/write() so we can just remove the if statemetents.
Fixes: 29332534e2
("regmap-i2c: Add smbus i2c block support")
Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
312 lines
7.2 KiB
C
312 lines
7.2 KiB
C
/*
|
|
* Register map access API - I2C support
|
|
*
|
|
* Copyright 2011 Wolfson Microelectronics plc
|
|
*
|
|
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*/
|
|
|
|
#include <linux/regmap.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/module.h>
|
|
|
|
#include "internal.h"
|
|
|
|
static int regmap_smbus_byte_reg_read(void *context, unsigned int reg,
|
|
unsigned int *val)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
int ret;
|
|
|
|
if (reg > 0xff)
|
|
return -EINVAL;
|
|
|
|
ret = i2c_smbus_read_byte_data(i2c, reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*val = ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,
|
|
unsigned int val)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
|
|
if (val > 0xff || reg > 0xff)
|
|
return -EINVAL;
|
|
|
|
return i2c_smbus_write_byte_data(i2c, reg, val);
|
|
}
|
|
|
|
static struct regmap_bus regmap_smbus_byte = {
|
|
.reg_write = regmap_smbus_byte_reg_write,
|
|
.reg_read = regmap_smbus_byte_reg_read,
|
|
};
|
|
|
|
static int regmap_smbus_word_reg_read(void *context, unsigned int reg,
|
|
unsigned int *val)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
int ret;
|
|
|
|
if (reg > 0xff)
|
|
return -EINVAL;
|
|
|
|
ret = i2c_smbus_read_word_data(i2c, reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*val = ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int regmap_smbus_word_reg_write(void *context, unsigned int reg,
|
|
unsigned int val)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
|
|
if (val > 0xffff || reg > 0xff)
|
|
return -EINVAL;
|
|
|
|
return i2c_smbus_write_word_data(i2c, reg, val);
|
|
}
|
|
|
|
static struct regmap_bus regmap_smbus_word = {
|
|
.reg_write = regmap_smbus_word_reg_write,
|
|
.reg_read = regmap_smbus_word_reg_read,
|
|
};
|
|
|
|
static int regmap_smbus_word_read_swapped(void *context, unsigned int reg,
|
|
unsigned int *val)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
int ret;
|
|
|
|
if (reg > 0xff)
|
|
return -EINVAL;
|
|
|
|
ret = i2c_smbus_read_word_swapped(i2c, reg);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
*val = ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int regmap_smbus_word_write_swapped(void *context, unsigned int reg,
|
|
unsigned int val)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
|
|
if (val > 0xffff || reg > 0xff)
|
|
return -EINVAL;
|
|
|
|
return i2c_smbus_write_word_swapped(i2c, reg, val);
|
|
}
|
|
|
|
static struct regmap_bus regmap_smbus_word_swapped = {
|
|
.reg_write = regmap_smbus_word_write_swapped,
|
|
.reg_read = regmap_smbus_word_read_swapped,
|
|
};
|
|
|
|
static int regmap_i2c_write(void *context, const void *data, size_t count)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
int ret;
|
|
|
|
ret = i2c_master_send(i2c, data, count);
|
|
if (ret == count)
|
|
return 0;
|
|
else if (ret < 0)
|
|
return ret;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
static int regmap_i2c_gather_write(void *context,
|
|
const void *reg, size_t reg_size,
|
|
const void *val, size_t val_size)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
struct i2c_msg xfer[2];
|
|
int ret;
|
|
|
|
/* If the I2C controller can't do a gather tell the core, it
|
|
* will substitute in a linear write for us.
|
|
*/
|
|
if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_NOSTART))
|
|
return -ENOTSUPP;
|
|
|
|
xfer[0].addr = i2c->addr;
|
|
xfer[0].flags = 0;
|
|
xfer[0].len = reg_size;
|
|
xfer[0].buf = (void *)reg;
|
|
|
|
xfer[1].addr = i2c->addr;
|
|
xfer[1].flags = I2C_M_NOSTART;
|
|
xfer[1].len = val_size;
|
|
xfer[1].buf = (void *)val;
|
|
|
|
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
|
if (ret == 2)
|
|
return 0;
|
|
if (ret < 0)
|
|
return ret;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
static int regmap_i2c_read(void *context,
|
|
const void *reg, size_t reg_size,
|
|
void *val, size_t val_size)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
struct i2c_msg xfer[2];
|
|
int ret;
|
|
|
|
xfer[0].addr = i2c->addr;
|
|
xfer[0].flags = 0;
|
|
xfer[0].len = reg_size;
|
|
xfer[0].buf = (void *)reg;
|
|
|
|
xfer[1].addr = i2c->addr;
|
|
xfer[1].flags = I2C_M_RD;
|
|
xfer[1].len = val_size;
|
|
xfer[1].buf = val;
|
|
|
|
ret = i2c_transfer(i2c->adapter, xfer, 2);
|
|
if (ret == 2)
|
|
return 0;
|
|
else if (ret < 0)
|
|
return ret;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
static struct regmap_bus regmap_i2c = {
|
|
.write = regmap_i2c_write,
|
|
.gather_write = regmap_i2c_gather_write,
|
|
.read = regmap_i2c_read,
|
|
.reg_format_endian_default = REGMAP_ENDIAN_BIG,
|
|
.val_format_endian_default = REGMAP_ENDIAN_BIG,
|
|
};
|
|
|
|
static int regmap_i2c_smbus_i2c_write(void *context, const void *data,
|
|
size_t count)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
|
|
if (count < 1)
|
|
return -EINVAL;
|
|
|
|
--count;
|
|
return i2c_smbus_write_i2c_block_data(i2c, ((u8 *)data)[0], count,
|
|
((u8 *)data + 1));
|
|
}
|
|
|
|
static int regmap_i2c_smbus_i2c_read(void *context, const void *reg,
|
|
size_t reg_size, void *val,
|
|
size_t val_size)
|
|
{
|
|
struct device *dev = context;
|
|
struct i2c_client *i2c = to_i2c_client(dev);
|
|
int ret;
|
|
|
|
if (reg_size != 1 || val_size < 1)
|
|
return -EINVAL;
|
|
|
|
ret = i2c_smbus_read_i2c_block_data(i2c, ((u8 *)reg)[0], val_size, val);
|
|
if (ret == val_size)
|
|
return 0;
|
|
else if (ret < 0)
|
|
return ret;
|
|
else
|
|
return -EIO;
|
|
}
|
|
|
|
static struct regmap_bus regmap_i2c_smbus_i2c_block = {
|
|
.write = regmap_i2c_smbus_i2c_write,
|
|
.read = regmap_i2c_smbus_i2c_read,
|
|
.max_raw_read = I2C_SMBUS_BLOCK_MAX,
|
|
.max_raw_write = I2C_SMBUS_BLOCK_MAX,
|
|
};
|
|
|
|
static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
|
|
const struct regmap_config *config)
|
|
{
|
|
if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
|
|
return ®map_i2c;
|
|
else if (config->val_bits == 8 && config->reg_bits == 8 &&
|
|
i2c_check_functionality(i2c->adapter,
|
|
I2C_FUNC_SMBUS_I2C_BLOCK))
|
|
return ®map_i2c_smbus_i2c_block;
|
|
else if (config->val_bits == 16 && config->reg_bits == 8 &&
|
|
i2c_check_functionality(i2c->adapter,
|
|
I2C_FUNC_SMBUS_WORD_DATA))
|
|
switch (regmap_get_val_endian(&i2c->dev, NULL, config)) {
|
|
case REGMAP_ENDIAN_LITTLE:
|
|
return ®map_smbus_word;
|
|
case REGMAP_ENDIAN_BIG:
|
|
return ®map_smbus_word_swapped;
|
|
default: /* everything else is not supported */
|
|
break;
|
|
}
|
|
else if (config->val_bits == 8 && config->reg_bits == 8 &&
|
|
i2c_check_functionality(i2c->adapter,
|
|
I2C_FUNC_SMBUS_BYTE_DATA))
|
|
return ®map_smbus_byte;
|
|
|
|
return ERR_PTR(-ENOTSUPP);
|
|
}
|
|
|
|
struct regmap *__regmap_init_i2c(struct i2c_client *i2c,
|
|
const struct regmap_config *config,
|
|
struct lock_class_key *lock_key,
|
|
const char *lock_name)
|
|
{
|
|
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
|
|
|
|
if (IS_ERR(bus))
|
|
return ERR_CAST(bus);
|
|
|
|
return __regmap_init(&i2c->dev, bus, &i2c->dev, config,
|
|
lock_key, lock_name);
|
|
}
|
|
EXPORT_SYMBOL_GPL(__regmap_init_i2c);
|
|
|
|
struct regmap *__devm_regmap_init_i2c(struct i2c_client *i2c,
|
|
const struct regmap_config *config,
|
|
struct lock_class_key *lock_key,
|
|
const char *lock_name)
|
|
{
|
|
const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
|
|
|
|
if (IS_ERR(bus))
|
|
return ERR_CAST(bus);
|
|
|
|
return __devm_regmap_init(&i2c->dev, bus, &i2c->dev, config,
|
|
lock_key, lock_name);
|
|
}
|
|
EXPORT_SYMBOL_GPL(__devm_regmap_init_i2c);
|
|
|
|
MODULE_LICENSE("GPL");
|