From 25e44a6ed98b67003a43850b811129fc8b706335 Mon Sep 17 00:00:00 2001 From: Wang Jie Date: Sun, 28 Nov 2021 19:53:45 +0800 Subject: [PATCH] power: supply: bq25700: register otg vbus regulator For Type-C PD driver using the tcpm framework and charger ic (bq25700) output otg vbus solution. Because the dwc3 driver cannot know whether the Type-C PD device sends DR_swap and PR_swap messages, there are some problems with the charger ic vbus control: (1) rk3588s as sink, Type-C device sends DR_swap Message, u2phy driver will send enable otg vbus notification to charger ic driver; (2) After the Type-C device sends PR_swap Message to realize the Sink->Source or Source->Sink switch, the charge ic driver cannot dynamically enable or disable the otg vbus; Based on the above problems, an otg vbus regulator is registered in the charge ic driver for use by the fusb302 (Type-C PD controller chip) driver, the otg vbus control is transferred to the tcpm framework. In some cases (for example, the hardware does not have a PD chip), in order to be compatible with switching from a lower version kernel (kenrel-4.4/4.19) to a higher version kernel(kernel-5.10), dts will not be modified. the software registration of the otg vbus regulator fails, the vbus extcon mechanism will be registered. Signed-off-by: Wang Jie Change-Id: I721abcb214795c0024e200b10ec3ab1d4a9b790a --- drivers/power/supply/bq25700_charger.c | 129 +++++++++++++++++++------ 1 file changed, 102 insertions(+), 27 deletions(-) diff --git a/drivers/power/supply/bq25700_charger.c b/drivers/power/supply/bq25700_charger.c index db737f6d5bb8..24e0beb9597f 100644 --- a/drivers/power/supply/bq25700_charger.c +++ b/drivers/power/supply/bq25700_charger.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -194,6 +195,7 @@ struct bq25700_device { struct gpio_desc *typec1_discharge_io; struct gpio_desc *otg_mode_en_io; + struct regulator_dev *otg_vbus_reg; struct regmap *regmap; struct regmap_field *rmap_fields[F_MAX_FIELDS]; int chip_id; @@ -1583,24 +1585,25 @@ static int bq25700_charger_evt_notifier1(struct notifier_block *nb, return NOTIFY_DONE; } +static void bq25700_set_otg_vbus(struct bq25700_device *charger, bool enable) +{ + DBG("OTG %s\n", enable ? "enable" : "disable"); + + if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) + gpiod_direction_output(charger->otg_mode_en_io, enable); + bq25700_field_write(charger, EN_OTG, enable); +} + static void bq25700_host_evt_worker(struct work_struct *work) { struct bq25700_device *charger = container_of(work, struct bq25700_device, host_work.work); struct extcon_dev *edev = charger->cable_edev; - /* Determine charger type */ - if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) { - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) - gpiod_direction_output(charger->otg_mode_en_io, 1); - bq25700_field_write(charger, EN_OTG, 1); - DBG("OTG enable\n"); - } else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) { - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) - gpiod_direction_output(charger->otg_mode_en_io, 0); - bq25700_field_write(charger, EN_OTG, 0); - DBG("OTG disable\n"); - } + if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) + bq25700_set_otg_vbus(charger, true); + else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) + bq25700_set_otg_vbus(charger, false); } static void bq25700_host_evt_worker1(struct work_struct *work) @@ -1609,18 +1612,10 @@ static void bq25700_host_evt_worker1(struct work_struct *work) container_of(work, struct bq25700_device, host_work1.work); struct extcon_dev *edev = charger->cable_edev_1; - /* Determine charger type */ - if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) { - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) - gpiod_direction_output(charger->otg_mode_en_io, 1); - bq25700_field_write(charger, EN_OTG, 1); - DBG("OTG enable\n"); - } else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) { - if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) - gpiod_direction_output(charger->otg_mode_en_io, 0); - bq25700_field_write(charger, EN_OTG, 0); - DBG("OTG disable\n"); - } + if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) > 0) + bq25700_set_otg_vbus(charger, true); + else if (extcon_get_state(edev, EXTCON_USB_VBUS_EN) == 0) + bq25700_set_otg_vbus(charger, false); } static int bq25700_host_evt_notifier(struct notifier_block *nb, @@ -1943,6 +1938,77 @@ static int bq25700_register_host_nb(struct bq25700_device *charger) return 0; } +static int bq25700_otg_vbus_enable(struct regulator_dev *dev) +{ + struct bq25700_device *charger = rdev_get_drvdata(dev); + + bq25700_set_otg_vbus(charger, true); + + return 0; +} + +static int bq25700_otg_vbus_disable(struct regulator_dev *dev) +{ + struct bq25700_device *charger = rdev_get_drvdata(dev); + + bq25700_set_otg_vbus(charger, false); + + return 0; +} + +static int bq25700_otg_vbus_is_enabled(struct regulator_dev *dev) +{ + struct bq25700_device *charger = rdev_get_drvdata(dev); + u8 val; + int gpio_status = 1; + + val = bq25700_field_read(charger, EN_OTG); + if (!IS_ERR_OR_NULL(charger->otg_mode_en_io)) + gpio_status = gpiod_get_value(charger->otg_mode_en_io); + + return val && gpio_status ? 1 : 0; +} + +static const struct regulator_ops bq25700_otg_vbus_ops = { + .enable = bq25700_otg_vbus_enable, + .disable = bq25700_otg_vbus_disable, + .is_enabled = bq25700_otg_vbus_is_enabled, +}; + +static const struct regulator_desc bq25700_otg_vbus_desc = { + .name = "otg-vbus", + .of_match = "otg-vbus", + .regulators_node = of_match_ptr("regulators"), + .owner = THIS_MODULE, + .ops = &bq25700_otg_vbus_ops, + .type = REGULATOR_VOLTAGE, + .fixed_uV = 5000000, + .n_voltages = 1, +}; + +static int bq25700_register_otg_vbus_regulator(struct bq25700_device *charger) +{ + struct device_node *np; + struct regulator_config config = { }; + + np = of_get_child_by_name(charger->dev->of_node, "regulators"); + if (!np) { + dev_warn(charger->dev, "cannot find regulators node\n"); + return -ENXIO; + } + + config.dev = charger->dev; + config.driver_data = charger; + + charger->otg_vbus_reg = devm_regulator_register(charger->dev, + &bq25700_otg_vbus_desc, + &config); + if (IS_ERR(charger->otg_vbus_reg)) + return PTR_ERR(charger->otg_vbus_reg); + + return 0; +} + static long bq25700_init_usb(struct bq25700_device *charger) { struct extcon_dev *edev, *edev1; @@ -1976,18 +2042,27 @@ static long bq25700_init_usb(struct bq25700_device *charger) if (!charger->pd_charge_only) bq25700_register_cg_nb(charger); - bq25700_register_host_nb(charger); + + if (bq25700_register_otg_vbus_regulator(charger) < 0) { + dev_warn(charger->dev, + "Cannot register otg vbus regulator\n"); + charger->otg_vbus_reg = NULL; + bq25700_register_host_nb(charger); + } + bq25700_register_discnt_nb(charger); bq25700_register_pd_nb(charger); if (charger->cable_edev) { - schedule_delayed_work(&charger->host_work, 0); + if (!charger->otg_vbus_reg) + schedule_delayed_work(&charger->host_work, 0); schedule_delayed_work(&charger->pd_work, 0); if (!charger->pd_charge_only) schedule_delayed_work(&charger->usb_work, 0); } if (charger->cable_edev_1) { - schedule_delayed_work(&charger->host_work1, 0); + if (!charger->otg_vbus_reg) + schedule_delayed_work(&charger->host_work1, 0); schedule_delayed_work(&charger->pd_work1, 0); if (!charger->pd_charge_only) schedule_delayed_work(&charger->usb_work1, 0);