Hello Grygorii, On 01/12/14 16:34, Grygorii Strashko wrote: > Having a board where the I2C bus locks up occasionally made it clear > that the bus recovery in the i2c-davinci driver will only work on > some boards, because on regular boards, this will only toggle GPIO > lines that aren't muxed to the actual pins. > > The I2C controller on SoCs like da850 (and da830), Keystone 2 has the > built-in capability to bit-bang its lines by using the ICPFUNC registers > of the i2c controller. > Implement the suggested procedure by toggling SCL and checking SDA using > the ICPFUNC registers of the I2C controller when present. Allow platforms > to indicate the presence of the ICPFUNC registers with a has_pfunc platform > data flag and add optional DT property "ti,has-pfunc" to indicate > the same in DT. > > CC: Sekhar Nori <nsekhar@xxxxxx> > CC: Kevin Hilman <khilman@xxxxxxxxxxxxxxxxxxx> > CC: Santosh Shilimkar <ssantosh@xxxxxxxxxx> > CC: Murali Karicheri <m-karicheri2@xxxxxx> > CC: Mike Looijmans <info@xxxxxxxxxxxxxxxx> > CC: <devicetree@xxxxxxxxxxxxxxx> > Reviewed-by: Uwe Kleine-König <u.kleine-koenig@xxxxxxxxxxxxxx> > Signed-off-by: Ben Gardiner <bengardiner@xxxxxxxxxxxxxx> > Signed-off-by: Mike Looijmans <milo-software@xxxxxxxxxxxxxxxxxxxxx> > [grygorii.strashko@xxxxxx: combined patches from Ben Gardiner and > Mike Looijmans and reimplemented ICPFUNC bus recovery using I2C > bus recovery infrastructure] > Signed-off-by: Grygorii Strashko <grygorii.strashko@xxxxxx> We have tested it on a custom Keystone2-based board, recovery seems to work when SDA is held low externally. Acked-by: Alexander Sverdlin <alexander.sverdlin@xxxxxxxxx> Tested-by: Michael Lawnick <michael.lawnick@xxxxxxxxx> > --- > .../devicetree/bindings/i2c/i2c-davinci.txt | 3 + > drivers/i2c/busses/i2c-davinci.c | 102 ++++++++++++++++++++- > include/linux/platform_data/i2c-davinci.h | 1 + > 3 files changed, 105 insertions(+), 1 deletion(-) > > diff --git a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt > index 2dc935b..a4e1cbc 100644 > --- a/Documentation/devicetree/bindings/i2c/i2c-davinci.txt > +++ b/Documentation/devicetree/bindings/i2c/i2c-davinci.txt > @@ -10,6 +10,9 @@ Required properties: > Recommended properties : > - interrupts : standard interrupt property. > - clock-frequency : desired I2C bus clock frequency in Hz. > +- ti,has-pfunc: boolean; if defined, it indicates that SoC supports PFUNC > + registers. PFUNC registers allow to switch I2C pins to function as > + GPIOs, so they can by toggled manually. > > Example (enbw_cmc board): > i2c@1c22000 { > diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c > index 00aed63..a1bb587 100644 > --- a/drivers/i2c/busses/i2c-davinci.c > +++ b/drivers/i2c/busses/i2c-davinci.c > @@ -64,6 +64,12 @@ > #define DAVINCI_I2C_IVR_REG 0x28 > #define DAVINCI_I2C_EMDR_REG 0x2c > #define DAVINCI_I2C_PSC_REG 0x30 > +#define DAVINCI_I2C_FUNC_REG 0x48 > +#define DAVINCI_I2C_DIR_REG 0x4c > +#define DAVINCI_I2C_DIN_REG 0x50 > +#define DAVINCI_I2C_DOUT_REG 0x54 > +#define DAVINCI_I2C_DSET_REG 0x58 > +#define DAVINCI_I2C_DCLR_REG 0x5c > > #define DAVINCI_I2C_IVR_AAS 0x07 > #define DAVINCI_I2C_IVR_SCD 0x06 > @@ -97,6 +103,29 @@ > #define DAVINCI_I2C_IMR_NACK BIT(1) > #define DAVINCI_I2C_IMR_AL BIT(0) > > +/* set SDA and SCL as GPIO */ > +#define DAVINCI_I2C_FUNC_PFUNC0 BIT(0) > + > +/* set SCL as output when used as GPIO*/ > +#define DAVINCI_I2C_DIR_PDIR0 BIT(0) > +/* set SDA as output when used as GPIO*/ > +#define DAVINCI_I2C_DIR_PDIR1 BIT(1) > + > +/* read SCL GPIO level */ > +#define DAVINCI_I2C_DIN_PDIN0 BIT(0) > +/* read SDA GPIO level */ > +#define DAVINCI_I2C_DIN_PDIN1 BIT(1) > + > +/*set the SCL GPIO high */ > +#define DAVINCI_I2C_DSET_PDSET0 BIT(0) > +/*set the SDA GPIO high */ > +#define DAVINCI_I2C_DSET_PDSET1 BIT(1) > + > +/* set the SCL GPIO low */ > +#define DAVINCI_I2C_DCLR_PDCLR0 BIT(0) > +/* set the SDA GPIO low */ > +#define DAVINCI_I2C_DCLR_PDCLR1 BIT(1) > + > struct davinci_i2c_dev { > struct device *dev; > void __iomem *base; > @@ -257,6 +286,71 @@ static struct i2c_bus_recovery_info davinci_i2c_gpio_recovery_info = { > .unprepare_recovery = davinci_i2c_unprepare_recovery, > }; > > +static void davinci_i2c_set_scl(struct i2c_adapter *adap, int val) > +{ > + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); > + > + if (val) > + davinci_i2c_write_reg(dev, DAVINCI_I2C_DSET_REG, > + DAVINCI_I2C_DSET_PDSET0); > + else > + davinci_i2c_write_reg(dev, DAVINCI_I2C_DCLR_REG, > + DAVINCI_I2C_DCLR_PDCLR0); > +} > + > +static int davinci_i2c_get_scl(struct i2c_adapter *adap) > +{ > + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); > + int val; > + > + /* read the state of SCL */ > + val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); > + return val & DAVINCI_I2C_DIN_PDIN0; > +} > + > +static int davinci_i2c_get_sda(struct i2c_adapter *adap) > +{ > + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); > + int val; > + > + /* read the state of SDA */ > + val = davinci_i2c_read_reg(dev, DAVINCI_I2C_DIN_REG); > + return val & DAVINCI_I2C_DIN_PDIN1; > +} > + > +static void davinci_i2c_scl_prepare_recovery(struct i2c_adapter *adap) > +{ > + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); > + > + davinci_i2c_prepare_recovery(adap); > + > + /* SCL output, SDA input */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_DIR_REG, DAVINCI_I2C_DIR_PDIR0); > + > + /* change to GPIO mode */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, > + DAVINCI_I2C_FUNC_PFUNC0); > +} > + > +static void davinci_i2c_scl_unprepare_recovery(struct i2c_adapter *adap) > +{ > + struct davinci_i2c_dev *dev = i2c_get_adapdata(adap); > + > + /* change back to I2C mode */ > + davinci_i2c_write_reg(dev, DAVINCI_I2C_FUNC_REG, 0); > + > + davinci_i2c_unprepare_recovery(adap); > +} > + > +static struct i2c_bus_recovery_info davinci_i2c_scl_recovery_info = { > + .recover_bus = i2c_generic_scl_recovery, > + .set_scl = davinci_i2c_set_scl, > + .get_scl = davinci_i2c_get_scl, > + .get_sda = davinci_i2c_get_sda, > + .prepare_recovery = davinci_i2c_scl_prepare_recovery, > + .unprepare_recovery = davinci_i2c_scl_unprepare_recovery, > +}; > + > /* > * Waiting for bus not busy > */ > @@ -669,6 +763,10 @@ static int davinci_i2c_probe(struct platform_device *pdev) > if (!of_property_read_u32(pdev->dev.of_node, "clock-frequency", > &prop)) > dev->pdata->bus_freq = prop / 1000; > + > + dev->pdata->has_pfunc = > + of_property_read_bool(pdev->dev.of_node, > + "ti,has-pfunc"); > } else if (!dev->pdata) { > dev->pdata = &davinci_i2c_platform_data_default; > } > @@ -710,7 +808,9 @@ static int davinci_i2c_probe(struct platform_device *pdev) > adap->timeout = DAVINCI_I2C_TIMEOUT; > adap->dev.of_node = pdev->dev.of_node; > > - if (dev->pdata->scl_pin) { > + if (dev->pdata->has_pfunc) > + adap->bus_recovery_info = &davinci_i2c_scl_recovery_info; > + else if (dev->pdata->scl_pin) { > adap->bus_recovery_info = &davinci_i2c_gpio_recovery_info; > adap->bus_recovery_info->scl_gpio = dev->pdata->scl_pin; > adap->bus_recovery_info->sda_gpio = dev->pdata->sda_pin; > diff --git a/include/linux/platform_data/i2c-davinci.h b/include/linux/platform_data/i2c-davinci.h > index 2312d19..89fd347 100644 > --- a/include/linux/platform_data/i2c-davinci.h > +++ b/include/linux/platform_data/i2c-davinci.h > @@ -18,6 +18,7 @@ struct davinci_i2c_platform_data { > unsigned int bus_delay; /* post-transaction delay (usec) */ > unsigned int sda_pin; /* GPIO pin ID to use for SDA */ > unsigned int scl_pin; /* GPIO pin ID to use for SCL */ > + bool has_pfunc; /*chip has a ICPFUNC register */ > }; > > /* for board setup code */ -- Best regards, Alexander Sverdlin. -- To unsubscribe from this list: send the line "unsubscribe linux-i2c" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html