summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/i2c/busses/i2c-gpio.c102
1 files changed, 39 insertions, 63 deletions
diff --git a/drivers/i2c/busses/i2c-gpio.c b/drivers/i2c/busses/i2c-gpio.c
index b4664037eded..97b9c29e9429 100644
--- a/drivers/i2c/busses/i2c-gpio.c
+++ b/drivers/i2c/busses/i2c-gpio.c
@@ -25,23 +25,6 @@ struct i2c_gpio_private_data {
struct i2c_gpio_platform_data pdata;
};
-/* Toggle SDA by changing the direction of the pin */
-static void i2c_gpio_setsda_dir(void *data, int state)
-{
- struct i2c_gpio_private_data *priv = data;
-
- /*
- * This is a way of saying "do not drive
- * me actively high" which means emulating open drain.
- * The right way to do this is for gpiolib to
- * handle this, by the function below.
- */
- if (state)
- gpiod_direction_input(priv->sda);
- else
- gpiod_direction_output(priv->sda, 0);
-}
-
/*
* Toggle SDA by changing the output value of the pin. This is only
* valid for pins configured as open drain (i.e. setting the value
@@ -54,17 +37,6 @@ static void i2c_gpio_setsda_val(void *data, int state)
gpiod_set_value(priv->sda, state);
}
-/* Toggle SCL by changing the direction of the pin. */
-static void i2c_gpio_setscl_dir(void *data, int state)
-{
- struct i2c_gpio_private_data *priv = data;
-
- if (state)
- gpiod_direction_input(priv->scl);
- else
- gpiod_direction_output(priv->scl, 0);
-}
-
/*
* Toggle SCL by changing the output value of the pin. This is used
* for pins that are configured as open drain and for output-only
@@ -116,30 +88,13 @@ static int i2c_gpio_probe(struct platform_device *pdev)
struct i2c_gpio_platform_data *pdata;
struct i2c_algo_bit_data *bit_data;
struct i2c_adapter *adap;
+ enum gpiod_flags gflags;
int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
- /* First get the GPIO pins; if it fails, we'll defer the probe. */
- priv->sda = devm_gpiod_get_index(&pdev->dev, NULL, 0, GPIOD_OUT_HIGH);
- if (IS_ERR(priv->sda)) {
- ret = PTR_ERR(priv->sda);
- /* FIXME: hack in the old code, is this really necessary? */
- if (ret == -EINVAL)
- ret = -EPROBE_DEFER;
- return ret;
- }
- priv->scl = devm_gpiod_get_index(&pdev->dev, NULL, 1, GPIOD_OUT_LOW);
- if (IS_ERR(priv->scl)) {
- ret = PTR_ERR(priv->scl);
- /* FIXME: hack in the old code, is this really necessary? */
- if (ret == -EINVAL)
- ret = -EPROBE_DEFER;
- return ret;
- }
-
adap = &priv->adap;
bit_data = &priv->bit_data;
pdata = &priv->pdata;
@@ -157,27 +112,48 @@ static int i2c_gpio_probe(struct platform_device *pdev)
}
/*
- * FIXME: this is a hack emulating the open drain emulation
- * that gpiolib can already do for us. Make all clients properly
- * flag their lines as open drain and get rid of this property
- * and the special callback.
+ * First get the GPIO pins; if it fails, we'll defer the probe.
+ * If the SDA line is marked from platform data or device tree as
+ * "open drain" it means something outside of our control is making
+ * this line being handled as open drain, and we should just handle
+ * it as any other output. Else we enforce open drain as this is
+ * required for an I2C bus.
*/
- if (pdata->sda_is_open_drain) {
- gpiod_direction_output(priv->sda, 1);
- bit_data->setsda = i2c_gpio_setsda_val;
- } else {
- gpiod_direction_input(priv->sda);
- bit_data->setsda = i2c_gpio_setsda_dir;
+ if (pdata->sda_is_open_drain)
+ gflags = GPIOD_OUT_HIGH;
+ else
+ gflags = GPIOD_OUT_HIGH_OPEN_DRAIN;
+ priv->sda = devm_gpiod_get_index(&pdev->dev, NULL, 0, gflags);
+ if (IS_ERR(priv->sda)) {
+ ret = PTR_ERR(priv->sda);
+ /* FIXME: hack in the old code, is this really necessary? */
+ if (ret == -EINVAL)
+ ret = -EPROBE_DEFER;
+ return ret;
}
-
- if (pdata->scl_is_open_drain || pdata->scl_is_output_only) {
- gpiod_direction_output(priv->scl, 1);
- bit_data->setscl = i2c_gpio_setscl_val;
- } else {
- gpiod_direction_input(priv->scl);
- bit_data->setscl = i2c_gpio_setscl_dir;
+ /*
+ * If the SCL line is marked from platform data or device tree as
+ * "open drain" it means something outside of our control is making
+ * this line being handled as open drain, and we should just handle
+ * it as any other output. Else we enforce open drain as this is
+ * required for an I2C bus.
+ */
+ if (pdata->scl_is_open_drain)
+ gflags = GPIOD_OUT_LOW;
+ else
+ gflags = GPIOD_OUT_LOW_OPEN_DRAIN;
+ priv->scl = devm_gpiod_get_index(&pdev->dev, NULL, 1, gflags);
+ if (IS_ERR(priv->scl)) {
+ ret = PTR_ERR(priv->scl);
+ /* FIXME: hack in the old code, is this really necessary? */
+ if (ret == -EINVAL)
+ ret = -EPROBE_DEFER;
+ return ret;
}
+ bit_data->setsda = i2c_gpio_setsda_val;
+ bit_data->setscl = i2c_gpio_setscl_val;
+
if (!pdata->scl_is_output_only)
bit_data->getscl = i2c_gpio_getscl;
bit_data->getsda = i2c_gpio_getsda;