All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] regmap: support regmap over SMBus
@ 2014-04-17  9:40 ` Boris BREZILLON
  0 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-17  9:40 UTC (permalink / raw)
  To: Mark Brown, Lars-Peter Clausen
  Cc: Greg Kroah-Hartman, Maxime Ripard, Shuge, kevin, Chen-Yu Tsai,
	Hans de Goede, Carlo Caione, linux-arm-kernel, linux-kernel, dev,
	Boris BREZILLON

Hello,

This patch series adds support for remap over SMBus in case your i2c
adapter does not support regular I2C transfers.

SMBus is a subset of the I2C protocol, often used to access registers
on external devices.

I2C adapters are able to access SMBus devices thanks to the SMBus
emulation layer. In the other hand SMBus adapters may not provide
regular I2C transfers, and thus you may not be able to expose a regmap
if your device is connected to such kind of adapter.
Hence why we need this regmap over SMBus implementation.

Best Regards,

Boris

Changes since v1:
 - add reg_read/reg_write callbacks to the regmap_bus struct

Boris BREZILLON (2):
  regmap: add reg_read/reg_write callbacks to regmap_bus struct
  regmap: i2c: fallback to SMBus if the adapter does not support
    standard I2C

 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 drivers/base/regmap/regmap.c     |  26 ++++++++++
 include/linux/regmap.h           |   6 +++
 3 files changed, 134 insertions(+), 2 deletions(-)

-- 
1.8.3.2


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 0/2] regmap: support regmap over SMBus
@ 2014-04-17  9:40 ` Boris BREZILLON
  0 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-17  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

Hello,

This patch series adds support for remap over SMBus in case your i2c
adapter does not support regular I2C transfers.

SMBus is a subset of the I2C protocol, often used to access registers
on external devices.

I2C adapters are able to access SMBus devices thanks to the SMBus
emulation layer. In the other hand SMBus adapters may not provide
regular I2C transfers, and thus you may not be able to expose a regmap
if your device is connected to such kind of adapter.
Hence why we need this regmap over SMBus implementation.

Best Regards,

Boris

Changes since v1:
 - add reg_read/reg_write callbacks to the regmap_bus struct

Boris BREZILLON (2):
  regmap: add reg_read/reg_write callbacks to regmap_bus struct
  regmap: i2c: fallback to SMBus if the adapter does not support
    standard I2C

 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 drivers/base/regmap/regmap.c     |  26 ++++++++++
 include/linux/regmap.h           |   6 +++
 3 files changed, 134 insertions(+), 2 deletions(-)

-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 1/2] regmap: add reg_read/reg_write callbacks to regmap_bus struct
  2014-04-17  9:40 ` Boris BREZILLON
@ 2014-04-17  9:40   ` Boris BREZILLON
  -1 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-17  9:40 UTC (permalink / raw)
  To: Mark Brown, Lars-Peter Clausen
  Cc: Greg Kroah-Hartman, Maxime Ripard, Shuge, kevin, Chen-Yu Tsai,
	Hans de Goede, Carlo Caione, linux-arm-kernel, linux-kernel, dev,
	Boris BREZILLON

Some busses do not support sending/receiving multiple registers in one go.
Such kind of busses just unpack the registers that have been previously
packed by the regmap core or pack registers that will be later unpacked by
the core code.

Add reg_write and reg_read callbacks in order to optimize access through
this kind of busses.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/base/regmap/regmap.c | 26 ++++++++++++++++++++++++++
 include/linux/regmap.h       |  6 ++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index d0a0724..c2a9933 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -35,10 +35,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
 			       unsigned int mask, unsigned int val,
 			       bool *change);
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val);
 static int _regmap_bus_read(void *context, unsigned int reg,
 			    unsigned int *val);
 static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 				       unsigned int val);
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val);
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 unsigned int val);
 
@@ -495,6 +499,12 @@ struct regmap *regmap_init(struct device *dev,
 
 		map->defer_caching = false;
 		goto skip_format_initialization;
+	} else if (!bus->read || !bus->write) {
+		map->reg_read = _regmap_bus_reg_read;
+		map->reg_write = _regmap_bus_reg_write;
+
+		map->defer_caching = false;
+		goto skip_format_initialization;
 	} else {
 		map->reg_read  = _regmap_bus_read;
 	}
@@ -1283,6 +1293,14 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 	return ret;
 }
 
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_write(map->bus_context, reg, val);
+}
+
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 unsigned int val)
 {
@@ -1924,6 +1942,14 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 	return ret;
 }
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_read(map->bus_context, reg, val);
+}
+
 static int _regmap_bus_read(void *context, unsigned int reg,
 			    unsigned int *val)
 {
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 34ef2c7..0c23ed5 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -276,6 +276,10 @@ typedef int (*regmap_hw_async_write)(void *context,
 typedef int (*regmap_hw_read)(void *context,
 			      const void *reg_buf, size_t reg_size,
 			      void *val_buf, size_t val_size);
+typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
+				  unsigned int *val);
+typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
+				   unsigned int val);
 typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
 typedef void (*regmap_hw_free_context)(void *context);
 
@@ -309,7 +313,9 @@ struct regmap_bus {
 	regmap_hw_write write;
 	regmap_hw_gather_write gather_write;
 	regmap_hw_async_write async_write;
+	regmap_hw_reg_write reg_write;
 	regmap_hw_read read;
+	regmap_hw_reg_read reg_read;
 	regmap_hw_free_context free_context;
 	regmap_hw_async_alloc async_alloc;
 	u8 read_flag_mask;
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 1/2] regmap: add reg_read/reg_write callbacks to regmap_bus struct
@ 2014-04-17  9:40   ` Boris BREZILLON
  0 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-17  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

Some busses do not support sending/receiving multiple registers in one go.
Such kind of busses just unpack the registers that have been previously
packed by the regmap core or pack registers that will be later unpacked by
the core code.

Add reg_write and reg_read callbacks in order to optimize access through
this kind of busses.

Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com>
---
 drivers/base/regmap/regmap.c | 26 ++++++++++++++++++++++++++
 include/linux/regmap.h       |  6 ++++++
 2 files changed, 32 insertions(+)

diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c
index d0a0724..c2a9933 100644
--- a/drivers/base/regmap/regmap.c
+++ b/drivers/base/regmap/regmap.c
@@ -35,10 +35,14 @@ static int _regmap_update_bits(struct regmap *map, unsigned int reg,
 			       unsigned int mask, unsigned int val,
 			       bool *change);
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val);
 static int _regmap_bus_read(void *context, unsigned int reg,
 			    unsigned int *val);
 static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 				       unsigned int val);
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val);
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 unsigned int val);
 
@@ -495,6 +499,12 @@ struct regmap *regmap_init(struct device *dev,
 
 		map->defer_caching = false;
 		goto skip_format_initialization;
+	} else if (!bus->read || !bus->write) {
+		map->reg_read = _regmap_bus_reg_read;
+		map->reg_write = _regmap_bus_reg_write;
+
+		map->defer_caching = false;
+		goto skip_format_initialization;
 	} else {
 		map->reg_read  = _regmap_bus_read;
 	}
@@ -1283,6 +1293,14 @@ static int _regmap_bus_formatted_write(void *context, unsigned int reg,
 	return ret;
 }
 
+static int _regmap_bus_reg_write(void *context, unsigned int reg,
+				 unsigned int val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_write(map->bus_context, reg, val);
+}
+
 static int _regmap_bus_raw_write(void *context, unsigned int reg,
 				 unsigned int val)
 {
@@ -1924,6 +1942,14 @@ static int _regmap_raw_read(struct regmap *map, unsigned int reg, void *val,
 	return ret;
 }
 
+static int _regmap_bus_reg_read(void *context, unsigned int reg,
+				unsigned int *val)
+{
+	struct regmap *map = context;
+
+	return map->bus->reg_read(map->bus_context, reg, val);
+}
+
 static int _regmap_bus_read(void *context, unsigned int reg,
 			    unsigned int *val)
 {
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 34ef2c7..0c23ed5 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -276,6 +276,10 @@ typedef int (*regmap_hw_async_write)(void *context,
 typedef int (*regmap_hw_read)(void *context,
 			      const void *reg_buf, size_t reg_size,
 			      void *val_buf, size_t val_size);
+typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg,
+				  unsigned int *val);
+typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg,
+				   unsigned int val);
 typedef struct regmap_async *(*regmap_hw_async_alloc)(void);
 typedef void (*regmap_hw_free_context)(void *context);
 
@@ -309,7 +313,9 @@ struct regmap_bus {
 	regmap_hw_write write;
 	regmap_hw_gather_write gather_write;
 	regmap_hw_async_write async_write;
+	regmap_hw_reg_write reg_write;
 	regmap_hw_read read;
+	regmap_hw_reg_read reg_read;
 	regmap_hw_free_context free_context;
 	regmap_hw_async_alloc async_alloc;
 	u8 read_flag_mask;
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-17  9:40 ` Boris BREZILLON
@ 2014-04-17  9:40   ` Boris BREZILLON
  -1 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-17  9:40 UTC (permalink / raw)
  To: Mark Brown, Lars-Peter Clausen
  Cc: Greg Kroah-Hartman, Maxime Ripard, Shuge, kevin, Chen-Yu Tsai,
	Hans de Goede, Carlo Caione, linux-arm-kernel, linux-kernel, dev,
	Boris BREZILLON

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>
---
 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd1895..d82830b 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -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 > 0xffff)
+		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 > 0xffff)
+		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;
@@ -97,6 +170,23 @@ static struct regmap_bus regmap_i2c = {
 	.read = regmap_i2c_read,
 };
 
+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 &&
+		 i2c_check_functionality(i2c->adapter,
+					 I2C_FUNC_SMBUS_WORD_DATA))
+		return &regmap_smbus_word;
+	else if (config->val_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
  *
@@ -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_PTR(PTR_ERR(bus));
+
+	return regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(regmap_init_i2c);
 
@@ -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_PTR(PTR_ERR(bus));
+
+	return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
 
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-17  9:40   ` Boris BREZILLON
  0 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-17  9:40 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd1895..d82830b 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -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 > 0xffff)
+		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 > 0xffff)
+		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;
@@ -97,6 +170,23 @@ static struct regmap_bus regmap_i2c = {
 	.read = regmap_i2c_read,
 };
 
+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 &&
+		 i2c_check_functionality(i2c->adapter,
+					 I2C_FUNC_SMBUS_WORD_DATA))
+		return &regmap_smbus_word;
+	else if (config->val_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
  *
@@ -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_PTR(PTR_ERR(bus));
+
+	return regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(regmap_init_i2c);
 
@@ -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_PTR(PTR_ERR(bus));
+
+	return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
 
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 1/2] regmap: add reg_read/reg_write callbacks to regmap_bus struct
  2014-04-17  9:40   ` Boris BREZILLON
@ 2014-04-18 15:08     ` Mark Brown
  -1 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-04-18 15:08 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Lars-Peter Clausen, Greg Kroah-Hartman, Maxime Ripard, Shuge,
	kevin, Chen-Yu Tsai, Hans de Goede, Carlo Caione,
	linux-arm-kernel, linux-kernel, dev

[-- Attachment #1: Type: text/plain, Size: 328 bytes --]

On Thu, Apr 17, 2014 at 11:40:11AM +0200, Boris BREZILLON wrote:
> Some busses do not support sending/receiving multiple registers in one go.
> Such kind of busses just unpack the registers that have been previously
> packed by the regmap core or pack registers that will be later unpacked by
> the core code.

Applied, thanks.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 1/2] regmap: add reg_read/reg_write callbacks to regmap_bus struct
@ 2014-04-18 15:08     ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-04-18 15:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 17, 2014 at 11:40:11AM +0200, Boris BREZILLON wrote:
> Some busses do not support sending/receiving multiple registers in one go.
> Such kind of busses just unpack the registers that have been previously
> packed by the regmap core or pack registers that will be later unpacked by
> the core code.

Applied, thanks.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140418/86311eb4/attachment.sig>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-17  9:40   ` Boris BREZILLON
@ 2014-04-18 15:10     ` Mark Brown
  -1 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-04-18 15:10 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Lars-Peter Clausen, Greg Kroah-Hartman, Maxime Ripard, Shuge,
	kevin, Chen-Yu Tsai, Hans de Goede, Carlo Caione,
	linux-arm-kernel, linux-kernel, dev

[-- Attachment #1: Type: text/plain, Size: 310 bytes --]

On Thu, Apr 17, 2014 at 11:40:12AM +0200, Boris BREZILLON wrote:

> +const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
> +					    const struct regmap_config *config)

Non-exported functions should be static.  Otherwise this looks good
modulo the register size issue that Guenter identified.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-18 15:10     ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-04-18 15:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 17, 2014 at 11:40:12AM +0200, Boris BREZILLON wrote:

> +const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c,
> +					    const struct regmap_config *config)

Non-exported functions should be static.  Otherwise this looks good
modulo the register size issue that Guenter identified.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140418/196e6243/attachment.sig>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-17  9:40   ` Boris BREZILLON
@ 2014-04-18 17:08     ` Lars-Peter Clausen
  -1 siblings, 0 replies; 20+ messages in thread
From: Lars-Peter Clausen @ 2014-04-18 17:08 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Mark Brown, Greg Kroah-Hartman, Maxime Ripard, Shuge, kevin,
	Chen-Yu Tsai, Hans de Goede, Carlo Caione, linux-arm-kernel,
	linux-kernel, dev

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

The fallback code should also check that reg_bits is 8.

> +
> +	return ERR_PTR(-ENOTSUPP);
> +}
> +


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-18 17:08     ` Lars-Peter Clausen
  0 siblings, 0 replies; 20+ messages in thread
From: Lars-Peter Clausen @ 2014-04-18 17:08 UTC (permalink / raw)
  To: linux-arm-kernel

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

The fallback code should also check that reg_bits is 8.

> +
> +	return ERR_PTR(-ENOTSUPP);
> +}
> +

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v3] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-17  9:40 ` Boris BREZILLON
@ 2014-04-18 20:11   ` Boris BREZILLON
  -1 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-18 20:11 UTC (permalink / raw)
  To: Mark Brown
  Cc: Lars-Peter Clausen, Guenter Roeck, Greg Kroah-Hartman,
	Maxime Ripard, Shuge, kevin, Chen-Yu Tsai, Hans de Goede,
	Carlo Caione, linux-arm-kernel, linux-kernel, dev,
	Boris BREZILLON

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>
---
Changes since v2:
- fix regmap_get_i2c_bus scope (declare as static)
- fix reg width check in reg_read/write functions
- add reg_bits check in regmap_get_i2c_bus function
- 

 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd1895..0a43211 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -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;
@@ -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
  *
@@ -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_PTR(PTR_ERR(bus));
+
+	return regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(regmap_init_i2c);
 
@@ -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_PTR(PTR_ERR(bus));
+
+	return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
 
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v3] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-18 20:11   ` Boris BREZILLON
  0 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-18 20:11 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
Changes since v2:
- fix regmap_get_i2c_bus scope (declare as static)
- fix reg width check in reg_read/write functions
- add reg_bits check in regmap_get_i2c_bus function
- 

 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd1895..0a43211 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -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;
@@ -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
  *
@@ -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_PTR(PTR_ERR(bus));
+
+	return regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(regmap_init_i2c);
 
@@ -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_PTR(PTR_ERR(bus));
+
+	return devm_regmap_init(&i2c->dev, bus, &i2c->dev, config);
 }
 EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);
 
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v3] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-18 20:11   ` Boris BREZILLON
@ 2014-04-19  1:57     ` Guenter Roeck
  -1 siblings, 0 replies; 20+ messages in thread
From: Guenter Roeck @ 2014-04-19  1:57 UTC (permalink / raw)
  To: Boris BREZILLON, Mark Brown
  Cc: Lars-Peter Clausen, Greg Kroah-Hartman, Maxime Ripard, Shuge,
	kevin, Chen-Yu Tsai, Hans de Goede, Carlo Caione,
	linux-arm-kernel, linux-kernel, dev

On 04/18/2014 01:11 PM, Boris BREZILLON wrote:
> 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>
> ---

[ ... ]

>   /**
>    * regmap_init_i2c(): Initialise register map
>    *
> @@ -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_PTR(PTR_ERR(bus));
> +
This seems clumsy. You should be able to use ERR_CAST() instead.
Also see Documentation/coccinelle.txt and scripts/coccinelle/api/err_cast.cocci.

> +	return regmap_init(&i2c->dev, bus, &i2c->dev, config);
>   }
>   EXPORT_SYMBOL_GPL(regmap_init_i2c);
>
> @@ -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_PTR(PTR_ERR(bus));
> +
Same here.

Guenter


^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v3] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-19  1:57     ` Guenter Roeck
  0 siblings, 0 replies; 20+ messages in thread
From: Guenter Roeck @ 2014-04-19  1:57 UTC (permalink / raw)
  To: linux-arm-kernel

On 04/18/2014 01:11 PM, Boris BREZILLON wrote:
> 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>
> ---

[ ... ]

>   /**
>    * regmap_init_i2c(): Initialise register map
>    *
> @@ -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_PTR(PTR_ERR(bus));
> +
This seems clumsy. You should be able to use ERR_CAST() instead.
Also see Documentation/coccinelle.txt and scripts/coccinelle/api/err_cast.cocci.

> +	return regmap_init(&i2c->dev, bus, &i2c->dev, config);
>   }
>   EXPORT_SYMBOL_GPL(regmap_init_i2c);
>
> @@ -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_PTR(PTR_ERR(bus));
> +
Same here.

Guenter

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v4] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-17  9:40 ` Boris BREZILLON
@ 2014-04-21 20:56   ` Boris BREZILLON
  -1 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-21 20:56 UTC (permalink / raw)
  To: Mark Brown
  Cc: Lars-Peter Clausen, Guenter Roeck, Greg Kroah-Hartman,
	Maxime Ripard, Shuge, kevin, Chen-Yu Tsai, Hans de Goede,
	Carlo Caione, linux-arm-kernel, linux-kernel, dev,
	Boris BREZILLON

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>
---
Changes since v3:
- make use of ERR_CAST instead of ERR_PTR + PTR_ERR

Changes since v2:
- fix regmap_get_i2c_bus scope (declare as static)
- fix reg width check in reg_read/write functions
- add reg_bits check in regmap_get_i2c_bus function


 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd1895..ca193d1 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -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;
@@ -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
  *
@@ -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);
 
@@ -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);
 
-- 
1.8.3.2


^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH v4] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-21 20:56   ` Boris BREZILLON
  0 siblings, 0 replies; 20+ messages in thread
From: Boris BREZILLON @ 2014-04-21 20:56 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
Changes since v3:
- make use of ERR_CAST instead of ERR_PTR + PTR_ERR

Changes since v2:
- fix regmap_get_i2c_bus scope (declare as static)
- fix reg width check in reg_read/write functions
- add reg_bits check in regmap_get_i2c_bus function


 drivers/base/regmap/regmap-i2c.c | 104 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 102 insertions(+), 2 deletions(-)

diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c
index ebd1895..ca193d1 100644
--- a/drivers/base/regmap/regmap-i2c.c
+++ b/drivers/base/regmap/regmap-i2c.c
@@ -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;
@@ -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
  *
@@ -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);
 
@@ -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);
 
-- 
1.8.3.2

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH v4] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
  2014-04-21 20:56   ` Boris BREZILLON
@ 2014-04-22 11:57     ` Mark Brown
  -1 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-04-22 11:57 UTC (permalink / raw)
  To: Boris BREZILLON
  Cc: Lars-Peter Clausen, Guenter Roeck, Greg Kroah-Hartman,
	Maxime Ripard, Shuge, kevin, Chen-Yu Tsai, Hans de Goede,
	Carlo Caione, linux-arm-kernel, linux-kernel, dev

[-- Attachment #1: Type: text/plain, Size: 192 bytes --]

On Mon, Apr 21, 2014 at 10:56:59PM +0200, Boris BREZILLON wrote:
> Some I2C adapters are only compatible with the SMBus protocol and do not
> support standard I2C transfers.

Applied, thanks.

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH v4] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C
@ 2014-04-22 11:57     ` Mark Brown
  0 siblings, 0 replies; 20+ messages in thread
From: Mark Brown @ 2014-04-22 11:57 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Apr 21, 2014 at 10:56:59PM +0200, Boris BREZILLON wrote:
> Some I2C adapters are only compatible with the SMBus protocol and do not
> support standard I2C transfers.

Applied, thanks.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20140422/05d8275c/attachment.sig>

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2014-04-22 11:58 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-17  9:40 [PATCH v2 0/2] regmap: support regmap over SMBus Boris BREZILLON
2014-04-17  9:40 ` Boris BREZILLON
2014-04-17  9:40 ` [PATCH v2 1/2] regmap: add reg_read/reg_write callbacks to regmap_bus struct Boris BREZILLON
2014-04-17  9:40   ` Boris BREZILLON
2014-04-18 15:08   ` Mark Brown
2014-04-18 15:08     ` Mark Brown
2014-04-17  9:40 ` [PATCH v2 2/2] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C Boris BREZILLON
2014-04-17  9:40   ` Boris BREZILLON
2014-04-18 15:10   ` Mark Brown
2014-04-18 15:10     ` Mark Brown
2014-04-18 17:08   ` Lars-Peter Clausen
2014-04-18 17:08     ` Lars-Peter Clausen
2014-04-18 20:11 ` [PATCH v3] " Boris BREZILLON
2014-04-18 20:11   ` Boris BREZILLON
2014-04-19  1:57   ` Guenter Roeck
2014-04-19  1:57     ` Guenter Roeck
2014-04-21 20:56 ` [PATCH v4] " Boris BREZILLON
2014-04-21 20:56   ` Boris BREZILLON
2014-04-22 11:57   ` Mark Brown
2014-04-22 11:57     ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.