[PATCH 1/4] i2c: add bus recovery infrastructure
Sascha Hauer
s.hauer at pengutronix.de
Sun Jul 12 23:47:03 PDT 2015
On Wed, Jul 08, 2015 at 03:29:15PM +0200, Jan Luebbe wrote:
> This is based on the code introduced to the kernel in
> 5f9296ba21b3c395e53dd84e7ff9578f97f24295.
>
> Signed-off-by: Jan Luebbe <jluebbe at debian.org>
> ---
> drivers/i2c/i2c.c | 161 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/i2c/i2c.h | 42 ++++++++++++++
> 2 files changed, 203 insertions(+)
>
> diff --git a/drivers/i2c/i2c.c b/drivers/i2c/i2c.c
> index ebc7e23..e350ac5 100644
> --- a/drivers/i2c/i2c.c
> +++ b/drivers/i2c/i2c.c
> @@ -23,6 +23,7 @@
> #include <xfuncs.h>
> #include <init.h>
> #include <of.h>
> +#include <gpio.h>
>
> #include <i2c/i2c.h>
>
> @@ -228,6 +229,133 @@ int i2c_write_reg(struct i2c_client *client, u32 addr, const u8 *buf, u16 count)
> }
> EXPORT_SYMBOL(i2c_write_reg);
>
> +/* i2c bus recovery routines */
> +static int get_scl_gpio_value(struct i2c_adapter *adap)
> +{
> + gpio_direction_input(adap->bus_recovery_info->scl_gpio);
> + return gpio_get_value(adap->bus_recovery_info->scl_gpio);
> +}
> +
> +static void set_scl_gpio_value(struct i2c_adapter *adap, int val)
> +{
> + if (val)
> + gpio_direction_input(adap->bus_recovery_info->scl_gpio);
> + else
> + gpio_direction_output(adap->bus_recovery_info->scl_gpio, 0);
> +}
> +
> +static int get_sda_gpio_value(struct i2c_adapter *adap)
> +{
> + return gpio_get_value(adap->bus_recovery_info->sda_gpio);
> +}
> +
> +static int i2c_get_gpios_for_recovery(struct i2c_adapter *adap)
> +{
> + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
> + struct device_d *dev = &adap->dev;
> + int ret = 0;
> +
> + ret = gpio_request_one(bri->scl_gpio, GPIOF_IN, "i2c-scl");
> + if (ret) {
> + dev_warn(dev, "Can't get SCL gpio: %d\n", bri->scl_gpio);
> + return ret;
> + }
> +
> + if (bri->get_sda) {
> + if (gpio_request_one(bri->sda_gpio, GPIOF_IN, "i2c-sda")) {
> + /* work without SDA polling */
> + dev_warn(dev, "Can't get SDA gpio: %d. Not using SDA polling\n",
> + bri->sda_gpio);
> + bri->get_sda = NULL;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static void i2c_put_gpios_for_recovery(struct i2c_adapter *adap)
> +{
> + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
> +
> + if (bri->get_sda)
> + gpio_free(bri->sda_gpio);
> +
> + gpio_free(bri->scl_gpio);
> +}
> +
> +/*
> + * We are generating clock pulses. ndelay() determines durating of clk pulses.
> + * We will generate clock with rate 100 KHz and so duration of both clock levels
> + * is: delay in ns = (10^6 / 100) / 2
> + */
> +#define RECOVERY_NDELAY 5000
> +#define RECOVERY_CLK_CNT 9
> +
> +static int i2c_generic_recovery(struct i2c_adapter *adap)
> +{
> + struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
> + int i = 0, val = 1, ret = 0;
> +
> + if (bri->prepare_recovery)
> + bri->prepare_recovery(adap);
> +
> + /*
> + * By this time SCL is high, as we need to give 9 falling-rising edges
> + */
> + while (i++ < RECOVERY_CLK_CNT * 2) {
> + if (val) {
> + /* Break if SDA is high */
> + if (bri->get_sda && bri->get_sda(adap))
> + break;
> + /* SCL shouldn't be low here */
> + if (!bri->get_scl(adap)) {
> + dev_err(&adap->dev,
> + "SCL is stuck low, exit recovery\n");
> + ret = -EBUSY;
> + break;
> + }
> + }
> +
> + val = !val;
> + bri->set_scl(adap, val);
> + ndelay(RECOVERY_NDELAY);
> + }
> +
> + if (bri->unprepare_recovery)
> + bri->unprepare_recovery(adap);
> +
> + return ret;
> +}
> +
> +int i2c_generic_scl_recovery(struct i2c_adapter *adap)
> +{
> + adap->bus_recovery_info->set_scl(adap, 1);
> + return i2c_generic_recovery(adap);
> +}
> +
> +int i2c_generic_gpio_recovery(struct i2c_adapter *adap)
> +{
> + int ret;
> +
> + ret = i2c_get_gpios_for_recovery(adap);
> + if (ret)
> + return ret;
> +
> + ret = i2c_generic_recovery(adap);
> + i2c_put_gpios_for_recovery(adap);
> +
> + return ret;
> +}
> +
> +int i2c_recover_bus(struct i2c_adapter *adap)
> +{
> + if (!adap->bus_recovery_info)
> + return -EOPNOTSUPP;
> +
> + dev_dbg(&adap->dev, "Trying i2c bus recovery\n");
> + return adap->bus_recovery_info->recover_bus(adap);
> +}
> +
> /**
> * i2c_new_device - instantiate one new I2C device
> *
> @@ -456,6 +584,39 @@ int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
>
> list_add_tail(&adapter->list, &adapter_list);
>
> + /* bus recovery specific initialization */
> + if (adapter->bus_recovery_info) {
> + struct i2c_bus_recovery_info *bri = adapter->bus_recovery_info;
> +
> + if (!bri->recover_bus) {
> + dev_err(&adapter->dev, "No recover_bus() found, not using recovery\n");
> + adapter->bus_recovery_info = NULL;
> + goto exit_recovery;
> + }
> +
> + /* Generic GPIO recovery */
> + if (bri->recover_bus == i2c_generic_gpio_recovery) {
This comparison to i2c_generic_gpio_recovery here forces the linker to
always compile in gpio recovery support, even when it's unused in the
compiled in drivers. I think we can do better here.
Also, could you put the bus recovery support into a separate function so
that we can call it wherever we need it? We might call it from other
places later aswell.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list