Skip to content

Commit

Permalink
Revert "i2c: pxa: move to generic GPIO recovery"
Browse files Browse the repository at this point in the history
commit 7b211c7 upstream.

This reverts commit 0b01392.

Conversion of PXA to generic I2C recovery, makes the I2C bus completely
lock up if recovery pinctrl is present in the DT and I2C recovery is
enabled.

So, until the generic I2C recovery can also work with PXA lets revert
to have working I2C and I2C recovery again.

Signed-off-by: Robert Marko <robert.marko@sartura.hr>
Cc: stable@vger.kernel.org # 5.11+
Acked-by: Andi Shyti <andi.shyti@kernel.org>
Acked-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
robimarko authored and gregkh committed Nov 28, 2023
1 parent db205ac commit 695a358
Showing 1 changed file with 68 additions and 8 deletions.
76 changes: 68 additions & 8 deletions drivers/i2c/busses/i2c-pxa.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,9 @@ struct pxa_i2c {
u32 hs_mask;

struct i2c_bus_recovery_info recovery;
struct pinctrl *pinctrl;
struct pinctrl_state *pinctrl_default;
struct pinctrl_state *pinctrl_recovery;
};

#define _IBMR(i2c) ((i2c)->reg_ibmr)
Expand Down Expand Up @@ -1300,12 +1303,13 @@ static void i2c_pxa_prepare_recovery(struct i2c_adapter *adap)
*/
gpiod_set_value(i2c->recovery.scl_gpiod, ibmr & IBMR_SCLS);
gpiod_set_value(i2c->recovery.sda_gpiod, ibmr & IBMR_SDAS);

WARN_ON(pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_recovery));
}

static void i2c_pxa_unprepare_recovery(struct i2c_adapter *adap)
{
struct pxa_i2c *i2c = adap->algo_data;
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
u32 isr;

/*
Expand All @@ -1319,7 +1323,7 @@ static void i2c_pxa_unprepare_recovery(struct i2c_adapter *adap)
i2c_pxa_do_reset(i2c);
}

WARN_ON(pinctrl_select_state(bri->pinctrl, bri->pins_default));
WARN_ON(pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_default));

dev_dbg(&i2c->adap.dev, "recovery: IBMR 0x%08x ISR 0x%08x\n",
readl(_IBMR(i2c)), readl(_ISR(i2c)));
Expand All @@ -1341,20 +1345,76 @@ static int i2c_pxa_init_recovery(struct pxa_i2c *i2c)
if (IS_ENABLED(CONFIG_I2C_PXA_SLAVE))
return 0;

bri->pinctrl = devm_pinctrl_get(dev);
if (PTR_ERR(bri->pinctrl) == -ENODEV) {
bri->pinctrl = NULL;
i2c->pinctrl = devm_pinctrl_get(dev);
if (PTR_ERR(i2c->pinctrl) == -ENODEV)
i2c->pinctrl = NULL;
if (IS_ERR(i2c->pinctrl))
return PTR_ERR(i2c->pinctrl);

if (!i2c->pinctrl)
return 0;

i2c->pinctrl_default = pinctrl_lookup_state(i2c->pinctrl,
PINCTRL_STATE_DEFAULT);
i2c->pinctrl_recovery = pinctrl_lookup_state(i2c->pinctrl, "recovery");

if (IS_ERR(i2c->pinctrl_default) || IS_ERR(i2c->pinctrl_recovery)) {
dev_info(dev, "missing pinmux recovery information: %ld %ld\n",
PTR_ERR(i2c->pinctrl_default),
PTR_ERR(i2c->pinctrl_recovery));
return 0;
}

/*
* Claiming GPIOs can influence the pinmux state, and may glitch the
* I2C bus. Do this carefully.
*/
bri->scl_gpiod = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN);
if (bri->scl_gpiod == ERR_PTR(-EPROBE_DEFER))
return -EPROBE_DEFER;
if (IS_ERR(bri->scl_gpiod)) {
dev_info(dev, "missing scl gpio recovery information: %pe\n",
bri->scl_gpiod);
return 0;
}

/*
* We have SCL. Pull SCL low and wait a bit so that SDA glitches
* have no effect.
*/
gpiod_direction_output(bri->scl_gpiod, 0);
udelay(10);
bri->sda_gpiod = devm_gpiod_get(dev, "sda", GPIOD_OUT_HIGH_OPEN_DRAIN);

/* Wait a bit in case of a SDA glitch, and then release SCL. */
udelay(10);
gpiod_direction_output(bri->scl_gpiod, 1);

if (bri->sda_gpiod == ERR_PTR(-EPROBE_DEFER))
return -EPROBE_DEFER;

if (IS_ERR(bri->sda_gpiod)) {
dev_info(dev, "missing sda gpio recovery information: %pe\n",
bri->sda_gpiod);
return 0;
}
if (IS_ERR(bri->pinctrl))
return PTR_ERR(bri->pinctrl);

bri->prepare_recovery = i2c_pxa_prepare_recovery;
bri->unprepare_recovery = i2c_pxa_unprepare_recovery;
bri->recover_bus = i2c_generic_scl_recovery;

i2c->adap.bus_recovery_info = bri;

return 0;
/*
* Claiming GPIOs can change the pinmux state, which confuses the
* pinctrl since pinctrl's idea of the current setting is unaffected
* by the pinmux change caused by claiming the GPIO. Work around that
* by switching pinctrl to the GPIO state here. We do it this way to
* avoid glitching the I2C bus.
*/
pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_recovery);

return pinctrl_select_state(i2c->pinctrl, i2c->pinctrl_default);
}

static int i2c_pxa_probe(struct platform_device *dev)
Expand Down

0 comments on commit 695a358

Please sign in to comment.