From patchwork Wed Apr 16 08:16:10 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Boris BREZILLON X-Patchwork-Id: 3998951 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 9141C9F2BA for ; Wed, 16 Apr 2014 08:18:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id B1E3E2021A for ; Wed, 16 Apr 2014 08:18:45 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id CCB4320212 for ; Wed, 16 Apr 2014 08:18:44 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WaL1Q-0001rg-Rz; Wed, 16 Apr 2014 08:16:44 +0000 Received: from top.free-electrons.com ([176.31.233.9] helo=mail.free-electrons.com) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1WaL1N-0001ga-Ki for linux-arm-kernel@lists.infradead.org; Wed, 16 Apr 2014 08:16:42 +0000 Received: by mail.free-electrons.com (Postfix, from userid 106) id B3DE47D9; Wed, 16 Apr 2014 10:16:15 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Spam-Level: X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00,RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 Received: from localhost.localdomain (col31-4-88-188-83-94.fbx.proxad.net [88.188.83.94]) by mail.free-electrons.com (Postfix) with ESMTPSA id E22217A3; Wed, 16 Apr 2014 10:16:14 +0200 (CEST) From: Boris BREZILLON To: Mark Brown , Lars-Peter Clausen Subject: [PATCH] regmap: i2c: fallback to SMBus if the adapter does not support standard I2C Date: Wed, 16 Apr 2014 10:16:10 +0200 Message-Id: <1397636170-664-1-git-send-email-boris.brezillon@free-electrons.com> X-Mailer: git-send-email 1.8.3.2 In-Reply-To: <20140415223849.GJ12304@sirena.org.uk> References: <20140415223849.GJ12304@sirena.org.uk> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140416_011641_980037_226D422E X-CRM114-Status: GOOD ( 17.10 ) X-Spam-Score: 0.3 (/) Cc: Boris BREZILLON , dev@linux-sunxi.org, Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Hans de Goede , Chen-Yu Tsai , Shuge , Maxime Ripard , Carlo Caione , kevin@allwinnertech.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP 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 --- Hello Mark, Lars, Would this patch be acceptable ? I'm still not convinced that we can decide which kind of protocol should be used based only on the adapter functionalities and the val_bytes field. But doing this as a first step should work in my case (my adapter only supports SMBus byte transfers :-)). Note that this patch has not been tested yet. I'll test it and resend a proper version, if you agree on the implementation. Best Regards, Boris drivers/base/regmap/regmap-i2c.c | 101 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 99 insertions(+), 2 deletions(-) diff --git a/drivers/base/regmap/regmap-i2c.c b/drivers/base/regmap/regmap-i2c.c index ebd1895..80051c5 100644 --- a/drivers/base/regmap/regmap-i2c.c +++ b/drivers/base/regmap/regmap-i2c.c @@ -14,6 +14,69 @@ #include #include + +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 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 int regmap_i2c_write(void *context, const void *data, size_t count) { struct device *dev = context; @@ -97,6 +160,28 @@ static struct regmap_bus regmap_i2c = { .read = regmap_i2c_read, }; +const struct regmap_bus *regmap_get_i2c_bus(struct i2c_client *i2c, + struct regmap_config *config) +{ + if (i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C)) { + return ®map_i2c; + } else if (config->val_bits == 16 && + i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_WORD_DATA)) { + config->reg_read = regmap_smbus_word_reg_read; + config->reg_write = regmap_smbus_word_reg_write; + return NULL; + } else if (config->val_bits == 8 && + i2c_check_functionality(i2c->adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + config->reg_read = regmap_smbus_byte_reg_read; + config->reg_write = regmap_smbus_byte_reg_write; + return NULL; + } + + return ERR_PTR(-ENOTSUPP); +} + /** * regmap_init_i2c(): Initialise register map * @@ -109,7 +194,13 @@ 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, ®map_i2c, &i2c->dev, config); + struct regmap_config upd_conf = *config; + const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, &upd_conf); + + if (IS_ERR(bus)) + return ERR_PTR(PTR_ERR(bus)); + + return regmap_init(&i2c->dev, bus, &i2c->dev, &upd_conf); } EXPORT_SYMBOL_GPL(regmap_init_i2c); @@ -126,7 +217,13 @@ 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, ®map_i2c, &i2c->dev, config); + struct regmap_config upd_conf = *config; + const struct regmap_bus *bus = regmap_get_i2c_bus(i2c, &upd_conf); + + if (IS_ERR(bus)) + return ERR_PTR(PTR_ERR(bus)); + + return devm_regmap_init(&i2c->dev, bus, &i2c->dev, &upd_conf); } EXPORT_SYMBOL_GPL(devm_regmap_init_i2c);