|
@@ -11,9 +11,19 @@
|
|
#include <malloc.h>
|
|
#include <malloc.h>
|
|
#include <dm/device-internal.h>
|
|
#include <dm/device-internal.h>
|
|
#include <dm/lists.h>
|
|
#include <dm/lists.h>
|
|
|
|
+#include <dm/pinctrl.h>
|
|
|
|
+#ifdef CONFIG_DM_GPIO
|
|
|
|
+#include <asm/gpio.h>
|
|
|
|
+#endif
|
|
|
|
|
|
#define I2C_MAX_OFFSET_LEN 4
|
|
#define I2C_MAX_OFFSET_LEN 4
|
|
|
|
|
|
|
|
+enum {
|
|
|
|
+ PIN_SDA = 0,
|
|
|
|
+ PIN_SCL,
|
|
|
|
+ PIN_COUNT,
|
|
|
|
+};
|
|
|
|
+
|
|
/* Useful debugging function */
|
|
/* Useful debugging function */
|
|
void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs)
|
|
void i2c_dump_msgs(struct i2c_msg *msg, int nmsgs)
|
|
{
|
|
{
|
|
@@ -445,20 +455,110 @@ int i2c_get_chip_offset_len(struct udevice *dev)
|
|
return chip->offset_len;
|
|
return chip->offset_len;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef CONFIG_DM_GPIO
|
|
|
|
+static void i2c_gpio_set_pin(struct gpio_desc *pin, int bit)
|
|
|
|
+{
|
|
|
|
+ if (bit)
|
|
|
|
+ dm_gpio_set_dir_flags(pin, GPIOD_IS_IN);
|
|
|
|
+ else
|
|
|
|
+ dm_gpio_set_dir_flags(pin, GPIOD_IS_OUT |
|
|
|
|
+ GPIOD_ACTIVE_LOW |
|
|
|
|
+ GPIOD_IS_OUT_ACTIVE);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int i2c_gpio_get_pin(struct gpio_desc *pin)
|
|
|
|
+{
|
|
|
|
+ return dm_gpio_get_value(pin);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int i2c_deblock_gpio_loop(struct gpio_desc *sda_pin,
|
|
|
|
+ struct gpio_desc *scl_pin)
|
|
|
|
+{
|
|
|
|
+ int counter = 9;
|
|
|
|
+ int ret = 0;
|
|
|
|
+
|
|
|
|
+ i2c_gpio_set_pin(sda_pin, 1);
|
|
|
|
+ i2c_gpio_set_pin(scl_pin, 1);
|
|
|
|
+ udelay(5);
|
|
|
|
+
|
|
|
|
+ /* Toggle SCL until slave release SDA */
|
|
|
|
+ while (counter-- >= 0) {
|
|
|
|
+ i2c_gpio_set_pin(scl_pin, 1);
|
|
|
|
+ udelay(5);
|
|
|
|
+ i2c_gpio_set_pin(scl_pin, 0);
|
|
|
|
+ udelay(5);
|
|
|
|
+ if (i2c_gpio_get_pin(sda_pin))
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Then, send I2C stop */
|
|
|
|
+ i2c_gpio_set_pin(sda_pin, 0);
|
|
|
|
+ udelay(5);
|
|
|
|
+
|
|
|
|
+ i2c_gpio_set_pin(scl_pin, 1);
|
|
|
|
+ udelay(5);
|
|
|
|
+
|
|
|
|
+ i2c_gpio_set_pin(sda_pin, 1);
|
|
|
|
+ udelay(5);
|
|
|
|
+
|
|
|
|
+ if (!i2c_gpio_get_pin(sda_pin) || !i2c_gpio_get_pin(scl_pin))
|
|
|
|
+ ret = -EREMOTEIO;
|
|
|
|
+
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int i2c_deblock_gpio(struct udevice *bus)
|
|
|
|
+{
|
|
|
|
+ struct gpio_desc gpios[PIN_COUNT];
|
|
|
|
+ int ret, ret0;
|
|
|
|
+
|
|
|
|
+ ret = gpio_request_list_by_name(bus, "gpios", gpios,
|
|
|
|
+ ARRAY_SIZE(gpios), GPIOD_IS_IN);
|
|
|
|
+ if (ret != ARRAY_SIZE(gpios)) {
|
|
|
|
+ debug("%s: I2C Node '%s' has no 'gpios' property %s\n",
|
|
|
|
+ __func__, dev_read_name(bus), bus->name);
|
|
|
|
+ if (ret >= 0) {
|
|
|
|
+ gpio_free_list(bus, gpios, ret);
|
|
|
|
+ ret = -ENOENT;
|
|
|
|
+ }
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = pinctrl_select_state(bus, "gpio");
|
|
|
|
+ if (ret) {
|
|
|
|
+ debug("%s: I2C Node '%s' has no 'gpio' pinctrl state. %s\n",
|
|
|
|
+ __func__, dev_read_name(bus), bus->name);
|
|
|
|
+ goto out_no_pinctrl;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret0 = i2c_deblock_gpio_loop(&gpios[PIN_SDA], &gpios[PIN_SCL]);
|
|
|
|
+
|
|
|
|
+ ret = pinctrl_select_state(bus, "default");
|
|
|
|
+ if (ret) {
|
|
|
|
+ debug("%s: I2C Node '%s' has no 'default' pinctrl state. %s\n",
|
|
|
|
+ __func__, dev_read_name(bus), bus->name);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = !ret ? ret0 : ret;
|
|
|
|
+
|
|
|
|
+out_no_pinctrl:
|
|
|
|
+ gpio_free_list(bus, gpios, ARRAY_SIZE(gpios));
|
|
|
|
+out:
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+#else
|
|
|
|
+static int i2c_deblock_gpio(struct udevice *bus)
|
|
|
|
+{
|
|
|
|
+ return -ENOSYS;
|
|
|
|
+}
|
|
|
|
+#endif // CONFIG_DM_GPIO
|
|
|
|
+
|
|
int i2c_deblock(struct udevice *bus)
|
|
int i2c_deblock(struct udevice *bus)
|
|
{
|
|
{
|
|
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
|
struct dm_i2c_ops *ops = i2c_get_ops(bus);
|
|
|
|
|
|
- /*
|
|
|
|
- * We could implement a software deblocking here if we could get
|
|
|
|
- * access to the GPIOs used by I2C, and switch them to GPIO mode
|
|
|
|
- * and then back to I2C. This is somewhat beyond our powers in
|
|
|
|
- * driver model at present, so for now just fail.
|
|
|
|
- *
|
|
|
|
- * See https://patchwork.ozlabs.org/patch/399040/
|
|
|
|
- */
|
|
|
|
if (!ops->deblock)
|
|
if (!ops->deblock)
|
|
- return -ENOSYS;
|
|
|
|
|
|
+ return i2c_deblock_gpio(bus);
|
|
|
|
|
|
return ops->deblock(bus);
|
|
return ops->deblock(bus);
|
|
}
|
|
}
|