Skip to content

Commit

Permalink
regmap: i2c: fallback to SMBus if the adapter does not support standa…
Browse files Browse the repository at this point in the history
…rd I2C

Some I2C adapters are only compatible with the SMBus protocol and do not
support standard I2C transfers.

Fallback to SMBus transfers if we encounter such kind of adapters.
The transfer type is chosen according to the val_bits field in the regmap
config.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
Signed-off-by: Mark Brown <broonie@linaro.org>
  • Loading branch information
Boris BREZILLON authored and Mark Brown committed Apr 22, 2014
1 parent 3ac1703 commit b422610
Showing 1 changed file with 102 additions and 2 deletions.
104 changes: 102 additions & 2 deletions drivers/base/regmap/regmap-i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,79 @@
#include <linux/i2c.h>
#include <linux/module.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_i2c_write(void *context, const void *data, size_t count)
{
struct device *dev = context;
Expand Down Expand Up @@ -97,6 +170,23 @@ static struct regmap_bus regmap_i2c = {
.read = regmap_i2c_read,
};

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 &regmap_i2c;
else if (config->val_bits == 16 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_WORD_DATA))
return &regmap_smbus_word;
else if (config->val_bits == 8 && config->reg_bits == 8 &&
i2c_check_functionality(i2c->adapter,
I2C_FUNC_SMBUS_BYTE_DATA))
return &regmap_smbus_byte;

return ERR_PTR(-ENOTSUPP);
}

/**
* regmap_init_i2c(): Initialise register map
*
Expand All @@ -109,7 +199,12 @@ static struct regmap_bus regmap_i2c = {
struct regmap *regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config)
{
return regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
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);
}
EXPORT_SYMBOL_GPL(regmap_init_i2c);

Expand All @@ -126,7 +221,12 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
const struct regmap_config *config)
{
return devm_regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config);
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);
}
EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);

Expand Down

0 comments on commit b422610

Please sign in to comment.