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 <[email protected]>
> ---
>  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 |

_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to