mfd: fusb302: add gpio control vbus discharge
authorBin Yang <yangbin@rock-chips.com>
Wed, 4 Jan 2017 00:55:26 +0000 (08:55 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Fri, 6 Jan 2017 06:37:44 +0000 (14:37 +0800)
When type-c vbus output power down or disconnect charging input, the
type-c vbus voltage discharge to 0V take a long time, during this time
residual voltage would affect charge circuit in some platforms(e.g.
rk3399-tve1205g board). So we need add fast discharge circuit, then use
a gpio control it.

Change-Id: I4c8eebc0cf10c6c38c7db9d9d5c71c4f6769e7fc
Signed-off-by: Bin Yang <yangbin@rock-chips.com>
drivers/mfd/fusb302.c
drivers/mfd/fusb302.h

index c7bc44266d54d3f586ffca738c36f9b9493bf436..6f07904718e48721ef4e3ef9d19b0ddd70b5c9d4 100644 (file)
@@ -300,6 +300,9 @@ static void fusb_timer_start(struct hrtimer *timer, int ms)
 static void platform_set_vbus_lvl_enable(struct fusb30x_chip *chip, int vbus_5v,
                                         int vbus_other)
 {
+       bool gpio_vbus_value = 0;
+
+       gpio_vbus_value = gpiod_get_value(chip->gpio_vbus_5v);
        if (chip->gpio_vbus_5v) {
                gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_5v);
                /* Only set state here, don't sync notifier to PMIC */
@@ -312,6 +315,12 @@ static void platform_set_vbus_lvl_enable(struct fusb30x_chip *chip, int vbus_5v,
 
        if (chip->gpio_vbus_other)
                gpiod_set_raw_value(chip->gpio_vbus_5v, vbus_other);
+
+       if (chip->gpio_discharge && !vbus_5v && gpio_vbus_value) {
+               gpiod_set_value(chip->gpio_discharge, 1);
+               msleep(20);
+               gpiod_set_value(chip->gpio_discharge, 0);
+       }
 }
 
 static void set_state(struct fusb30x_chip *chip, enum connection_state state)
@@ -786,7 +795,11 @@ static void set_state_unattached(struct fusb30x_chip *chip)
        memset(&chip->notify, 0, sizeof(struct notify_info));
        platform_fusb_notify(chip);
 
+       if (chip->gpio_discharge)
+               gpiod_set_value(chip->gpio_discharge, 1);
        msleep(100);
+       if (chip->gpio_discharge)
+               gpiod_set_value(chip->gpio_discharge, 0);
 }
 
 static int tcpm_check_vbus(struct fusb30x_chip *chip)
@@ -2278,6 +2291,14 @@ static int fusb_initialize_gpio(struct fusb30x_chip *chip)
        else
                gpiod_set_raw_value(chip->gpio_vbus_other, 0);
 
+       chip->gpio_discharge = devm_gpiod_get_optional(chip->dev, "discharge",
+                                                      GPIOD_OUT_LOW);
+       if (IS_ERR(chip->gpio_discharge)) {
+               dev_warn(chip->dev,
+                        "Could not get named GPIO for discharge!\n");
+               chip->gpio_discharge = NULL;
+       }
+
        return 0;
 }
 
@@ -2503,6 +2524,19 @@ static int fusb30x_remove(struct i2c_client *client)
        return 0;
 }
 
+static void fusb30x_shutdown(struct i2c_client *client)
+{
+       struct fusb30x_chip *chip = i2c_get_clientdata(client);
+
+       if (chip->gpio_vbus_5v)
+               gpiod_set_value(chip->gpio_vbus_5v, 0);
+       if (chip->gpio_discharge) {
+               gpiod_set_value(chip->gpio_discharge, 1);
+               msleep(100);
+               gpiod_set_value(chip->gpio_discharge, 0);
+       }
+}
+
 static const struct of_device_id fusb30x_dt_match[] = {
        { .compatible = FUSB30X_I2C_DEVICETREE_NAME },
        {},
@@ -2522,6 +2556,7 @@ static struct i2c_driver fusb30x_driver = {
        },
        .probe = fusb30x_probe,
        .remove = fusb30x_remove,
+       .shutdown = fusb30x_shutdown,
        .id_table = fusb30x_i2c_device_id,
 };
 
index db564f4d0c2cf12bcc1a66ee89df517c103320fc..8d98e94120a9c0fcc96e4e0306203bdf2c22cf62 100644 (file)
@@ -378,6 +378,7 @@ struct fusb30x_chip {
        struct gpio_desc *gpio_vbus_5v;
        struct gpio_desc *gpio_vbus_other;
        struct gpio_desc *gpio_int;
+       struct gpio_desc *gpio_discharge;
        int timer_state;
        int timer_mux;
        int port_num;