aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorBoris BREZILLON <boris.brezillon@free-electrons.com>2014-04-21 16:56:59 -0400
committerMark Brown <broonie@linaro.org>2014-04-22 07:57:28 -0400
commitb42261078a91db8a8307db42cad41a619077d1df (patch)
treefa1d655bc813477914a4bca5fd0ca1d6833d2256 /drivers/base
parent3ac170376f2c5123414e0267aa0f9cf218965e24 (diff)
regmap: i2c: fallback to SMBus if the adapter does not support standard 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>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/regmap/regmap-i2c.c104
1 files changed, 102 insertions, 2 deletions
diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd189529760..ca193d1ef47c 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -14,6 +14,79 @@
14#include <linux/i2c.h> 14#include <linux/i2c.h>
15#include <linux/module.h> 15#include <linux/module.h>
16 16
17
18static int regmap_smbus_byte_reg_read(void *context, unsigned int reg,
19 unsigned int *val)
20{
21 struct device *dev = context;
22 struct i2c_client *i2c = to_i2c_client(dev);
23 int ret;
24
25 if (reg > 0xff)
26 return -EINVAL;
27
28 ret = i2c_smbus_read_byte_data(i2c, reg);
29 if (ret < 0)
30 return ret;
31
32 *val = ret;
33
34 return 0;
35}
36
37static int regmap_smbus_byte_reg_write(void *context, unsigned int reg,
38 unsigned int val)
39{
40 struct device *dev = context;
41 struct i2c_client *i2c = to_i2c_client(dev);
42
43 if (val > 0xff || reg > 0xff)
44 return -EINVAL;
45
46 return i2c_smbus_write_byte_data(i2c, reg, val);
47}
48
49static struct regmap_bus regmap_smbus_byte = {
50 .reg_write = regmap_smbus_byte_reg_write,
51 .reg_read = regmap_smbus_byte_reg_read,
52};
53
54static int regmap_smbus_word_reg_read(void *context, unsigned int reg,
55 unsigned int *val)
56{
57 struct device *dev = context;
58 struct i2c_client *i2c = to_i2c_client(dev);
59 int ret;
60
61 if (reg > 0xff)
62 return -EINVAL;
63
64 ret = i2c_smbus_read_word_data(i2c, reg);
65 if (ret < 0)
66 return ret;
67
68 *val = ret;
69
70 return 0;
71}
72
73static int regmap_smbus_word_reg_write(void *context, unsigned int reg,
74 unsigned int val)
75{
76 struct device *dev = context;
77 struct i2c_client *i2c = to_i2c_client(dev);
78
79 if (val > 0xffff || reg > 0xff)
80 return -EINVAL;
81
82 return i2c_smbus_write_word_data(i2c, reg, val);
83}
84
85static struct regmap_bus regmap_smbus_word = {
86 .reg_write = regmap_smbus_word_reg_write,
87 .reg_read = regmap_smbus_word_reg_read,
88};
89
17static int regmap_i2c_write(void *context, const void *data, size_t count) 90static int regmap_i2c_write(void *context, const void *data, size_t count)
18{ 91{
19 struct device *dev = context; 92 struct device *dev = context;
@@ -97,6 +170,23 @@ static struct regmap_bus regmap_i2c = {
97 .read = regmap_i2c_read, 170 .read = regmap_i2c_read,
98}; 171};
99 172
173static const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
174 const struct regmap_config *config)
175{
176 if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C))
177 return &regmap_i2c;
178 else if (config->val_bits == 16 && config->reg_bits == 8 &&
179 i2c_check_functionality(i2c->adapter,
180 I2C_FUNC_SMBUS_WORD_DATA))
181 return &regmap_smbus_word;
182 else if (config->val_bits == 8 && config->reg_bits == 8 &&
183 i2c_check_functionality(i2c->adapter,
184 I2C_FUNC_SMBUS_BYTE_DATA))
185 return &regmap_smbus_byte;
186
187 return ERR_PTR(-ENOTSUPP);
188}
189
100/** 190/**
101 * regmap_init_i2c(): Initialise register map 191 * regmap_init_i2c(): Initialise register map
102 * 192 *
@@ -109,7 +199,12 @@ static struct regmap_bus regmap_i2c = {
109struct regmap *regmap_init_i2c(struct i2c_client *i2c, 199struct regmap *regmap_init_i2c(struct i2c_client *i2c,
110 const struct regmap_config *config) 200 const struct regmap_config *config)
111{ 201{
112 return regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config); 202 const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
203
204 if (IS_ERR(bus))
205 return ERR_CAST(bus);
206
207 return regmap_init(&i2c->dev, bus, &i2c->dev, config);
113} 208}
114EXPORT_SYMBOL_GPL(regmap_init_i2c); 209EXPORT_SYMBOL_GPL(regmap_init_i2c);
115 210
@@ -126,7 +221,12 @@ EXPORT_SYMBOL_GPL(regmap_init_i2c);
126struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c, 221struct regmap *devm_regmap_init_i2c(struct i2c_client *i2c,
127 const struct regmap_config *config) 222 const struct regmap_config *config)
128{ 223{
129 return devm_regmap_init(&i2c->dev, &regmap_i2c, &i2c->dev, config); 224 const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, config);
225
226 if (IS_ERR(bus))
227 return ERR_CAST(bus);
228
229 return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config);
130} 230}
131EXPORT_SYMBOL_GPL(devm_regmap_init_i2c); 231EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
132 232