diff options
| author | Guenter Roeck <linux@roeck-us.net> | 2015-02-03 13:01:19 -0500 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2015-02-04 15:48:13 -0500 |
| commit | 5892ded23c8e9e015b01347222ba4de8a80e7ce2 (patch) | |
| tree | 24a8457b9c79c08289add18d09c07ed6cb22c9ae /drivers/base/regmap/regmap-i2c.c | |
| parent | 3c174d29263b1954171cf857a9bf59dcbe527875 (diff) | |
regmap: Fix i2c word access when using SMBus access functions
SMBus access functions assume that 16-bit values are formatted as
little endian numbers. The direct i2c access functions in regmap,
however, assume that 16-bit values are formatted as big endian numbers.
As a result, the current code returns different values if an i2c chip's
16-bit registers are accessed through i2c access functions vs. SMBus
access functions.
Use regmap_smbus_read_word_swapped and regmap_smbus_write_word_swapped
for 16-bit SMBus accesses if a chip is configured as REGMAP_ENDIAN_BIG.
If the chip is configured as REGMAP_ENDIAN_LITTLE, keep using
regmap_smbus_write_word_data and regmap_smbus_read_word_data. Otherwise
reject registration if the controller does not support direct i2c accesses.
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/base/regmap/regmap-i2c.c')
| -rw-r--r-- | drivers/base/regmap/regmap-i2c.c | 46 |
1 files changed, 45 insertions, 1 deletions
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index 053150a7f9f2..4b76e33110a2 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c | |||
| @@ -14,6 +14,7 @@ | |||
| 14 | #include <linux/i2c.h> | 14 | #include <linux/i2c.h> |
| 15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
| 16 | 16 | ||
| 17 | #include "internal.h" | ||
| 17 | 18 | ||
| 18 | static int regmap_smbus_byte_reg_read(void *context, unsigned int reg, | 19 | static int regmap_smbus_byte_reg_read(void *context, unsigned int reg, |
| 19 | unsigned int *val) | 20 | unsigned int *val) |
| @@ -87,6 +88,42 @@ static struct regmap_bus regmap_smbus_word = { | |||
| 87 | .reg_read = regmap_smbus_word_reg_read, | 88 | .reg_read = regmap_smbus_word_reg_read, |
| 88 | }; | 89 | }; |
| 89 | 90 | ||
| 91 | static int regmap_smbus_word_read_swapped(void *context, unsigned int reg, | ||
| 92 | unsigned int *val) | ||
| 93 | { | ||
| 94 | struct device *dev = context; | ||
| 95 | struct i2c_client *i2c = to_i2c_client(dev); | ||
| 96 | int ret; | ||
| 97 | |||
| 98 | if (reg > 0xff) | ||
| 99 | return -EINVAL; | ||
| 100 | |||
| 101 | ret = i2c_smbus_read_word_swapped(i2c, reg); | ||
| 102 | if (ret < 0) | ||
| 103 | return ret; | ||
| 104 | |||
| 105 | *val = ret; | ||
| 106 | |||
| 107 | return 0; | ||
| 108 | } | ||
| 109 | |||
| 110 | static int regmap_smbus_word_write_swapped(void *context, unsigned int reg, | ||
| 111 | unsigned int val) | ||
| 112 | { | ||
| 113 | struct device *dev = context; | ||
| 114 | struct i2c_client *i2c = to_i2c_client(dev); | ||
| 115 | |||
| 116 | if (val > 0xffff || reg > 0xff) | ||
| 117 | return -EINVAL; | ||
| 118 | |||
| 119 | return i2c_smbus_write_word_swapped(i2c, reg, val); | ||
| 120 | } | ||
| 121 | |||
| 122 | static struct regmap_bus regmap_smbus_word_swapped = { | ||
| 123 | .reg_write = regmap_smbus_word_write_swapped, | ||
| 124 | .reg_read = regmap_smbus_word_read_swapped, | ||
| 125 | }; | ||
| 126 | |||
| 90 | static int regmap_i2c_write(void *context, const void *data, size_t count) | 127 | static int regmap_i2c_write(void *context, const void *data, size_t count) |
| 91 | { | 128 | { |
| 92 | struct device *dev = context; | 129 | struct device *dev = context; |
| @@ -180,7 +217,14 @@ static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, | |||
| 180 | else if (config->val_bits == 16 && config->reg_bits == 8 && | 217 | else if (config->val_bits == 16 && config->reg_bits == 8 && |
| 181 | i2c_check_functionality(i2c->adapter, | 218 | i2c_check_functionality(i2c->adapter, |
| 182 | I2C_FUNC_SMBUS_WORD_DATA)) | 219 | I2C_FUNC_SMBUS_WORD_DATA)) |
| 183 | return ®map_smbus_word; | 220 | switch (regmap_get_val_endian(&i2c->dev, NULL, config)) { |
| 221 | case REGMAP_ENDIAN_LITTLE: | ||
| 222 | return ®map_smbus_word; | ||
| 223 | case REGMAP_ENDIAN_BIG: | ||
| 224 | return ®map_smbus_word_swapped; | ||
| 225 | default: /* everything else is not supported */ | ||
| 226 | break; | ||
| 227 | } | ||
| 184 | else if (config->val_bits == 8 && config->reg_bits == 8 && | 228 | else if (config->val_bits == 8 && config->reg_bits == 8 && |
| 185 | i2c_check_functionality(i2c->adapter, | 229 | i2c_check_functionality(i2c->adapter, |
| 186 | I2C_FUNC_SMBUS_BYTE_DATA)) | 230 | I2C_FUNC_SMBUS_BYTE_DATA)) |
