diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt index a7bc32ef3797..f13e9d09ae48 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt @@ -21,8 +21,8 @@ Optional properties: usb-phy output 480m and xin24m. Refer to clk/clock-bindings.txt for generic clock consumer properties. - - rockchip,u2phy-tuning; when set, tuning u2phy to improve usb2 SI. - - otg-vbus-gpios : assign the gpio to control vbus of otg port. + - rockchip,u2phy-tuning: when set, tuning u2phy to improve usb2 SI. + - vbus-supply: regulator phandle for vbus power source. Required nodes : a sub-node is required for each port the phy provides. The sub-node name is used to identify host or otg port, diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 078d535585cc..1aa2f6904a56 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -206,9 +206,9 @@ struct rockchip_usb2phy_cfg { * flase - use bvalid to get vbus status * @vbus_attached: otg device vbus status. * @vbus_always_on: otg vbus is always powered on. + * @vbus_enabled: vbus regulator status. * @bypass_uart_en: usb bypass uart enable, passed from DT. * @bvalid_irq: IRQ number assigned for vbus valid rise detection. - * @vbus_drv_gpio: gpio description for vbus control. * @ls_irq: IRQ number assigned for linestate detection. * @id_irq: IRQ number assigned for id fall or rise detection. * @mutex: for register updating in sm_work. @@ -216,6 +216,7 @@ struct rockchip_usb2phy_cfg { * @bypass_uart_work: usb bypass uart work. * @otg_sm_work: OTG state machine work. * @sm_work: HOST state machine work. + * @vbus: vbus regulator supply on few rockchip boards. * @phy_cfg: port register configuration, assigned by driver data. * @event_nb: hold event notification callback. * @wakelock: wake lock struct to prevent system suspend @@ -231,6 +232,7 @@ struct rockchip_usb2phy_port { bool utmi_avalid; bool vbus_attached; bool vbus_always_on; + bool vbus_enabled; bool bypass_uart_en; int bvalid_irq; int ls_irq; @@ -240,7 +242,7 @@ struct rockchip_usb2phy_port { struct delayed_work chg_work; struct delayed_work otg_sm_work; struct delayed_work sm_work; - struct gpio_desc *vbus_drv_gpio; + struct regulator *vbus; const struct rockchip_usb2phy_port_cfg *port_cfg; struct notifier_block event_nb; struct wake_lock wakelock; @@ -735,6 +737,29 @@ static int rockchip_usb2phy_exit(struct phy *phy) return 0; } +static int rockchip_set_vbus_power(struct rockchip_usb2phy_port *rport, + bool en) +{ + int ret = 0; + + if (!rport->vbus) + return 0; + + if (en && !rport->vbus_enabled) { + ret = regulator_enable(rport->vbus); + if (ret) + dev_err(&rport->phy->dev, + "Failed to enable VBUS supply\n"); + } else if (!en && rport->vbus_enabled) { + ret = regulator_disable(rport->vbus); + } + + if (ret == 0) + rport->vbus_enabled = en; + + return ret; +} + static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode) { struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy); @@ -754,12 +779,20 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode) * fallthrough */ case PHY_MODE_USB_DEVICE: - gpiod_set_value_cansleep(rport->vbus_drv_gpio, 0); + /* Disable VBUS supply */ + rockchip_set_vbus_power(rport, false); extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, false); vbus_det_en = true; break; case PHY_MODE_USB_HOST: - gpiod_set_value_cansleep(rport->vbus_drv_gpio, 1); + /* Enable VBUS supply */ + ret = rockchip_set_vbus_power(rport, true); + if (ret) { + dev_err(&rport->phy->dev, + "Failed to set host mode\n"); + return ret; + } + extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, true); /* fallthrough */ case PHY_MODE_INVALID: @@ -1427,8 +1460,8 @@ static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) extcon_sync(rphy->edev, EXTCON_USB_HOST); extcon_sync(rphy->edev, EXTCON_USB_VBUS_EN); - gpiod_set_value_cansleep(rport->vbus_drv_gpio, - cable_vbus_state ? 1 : 0); + + rockchip_set_vbus_power(rport, cable_vbus_state); mutex_unlock(&rport->mutex); @@ -1497,6 +1530,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_OTG]; rport->state = OTG_STATE_UNDEFINED; rport->vbus_attached = false; + rport->vbus_enabled = false; rport->perip_connected = false; mutex_init(&rport->mutex); @@ -1524,13 +1558,15 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, return ret; } - rport->vbus_drv_gpio = devm_gpiod_get_optional(rphy->dev, "otg-vbus", - GPIOD_OUT_LOW); - if (!rport->vbus_drv_gpio) { - dev_warn(rphy->dev, "vbus_drv is not assigned\n"); - } else if (IS_ERR(rport->vbus_drv_gpio)) { - dev_err(rphy->dev, "failed to get vbus_drv\n"); - return PTR_ERR(rport->vbus_drv_gpio); + /* Get Vbus regulators */ + rport->vbus = devm_regulator_get_optional(&rport->phy->dev, "vbus"); + if (IS_ERR(rport->vbus)) { + ret = PTR_ERR(rport->vbus); + if (ret == -EPROBE_DEFER) + return ret; + + dev_warn(&rport->phy->dev, "Failed to get VBUS supply regulator\n"); + rport->vbus = NULL; } rport->mode = of_usb_get_dr_mode_by_phy(child_np, -1); @@ -1539,8 +1575,12 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, if (rphy->edev_self) { extcon_set_state(rphy->edev, EXTCON_USB, false); extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); + /* Enable VBUS supply */ extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, true); - gpiod_set_value_cansleep(rport->vbus_drv_gpio, 1); + ret = rockchip_set_vbus_power(rport, true); + if (ret) + return ret; + } goto out; } @@ -1590,11 +1630,10 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, extcon_set_state(rphy->edev, EXTCON_USB, false); extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, true); - gpiod_set_value_cansleep(rport->vbus_drv_gpio, 1); - } else { - extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); - extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, false); - gpiod_set_value_cansleep(rport->vbus_drv_gpio, 0); + /* Enable VBUS supply */ + ret = rockchip_set_vbus_power(rport, true); + if (ret) + return ret; } } @@ -1960,17 +1999,21 @@ static int rockchip_usb2phy_pm_resume(struct device *dev) } if (!property_enabled(rphy, - &rport->port_cfg->utmi_iddig)) { + &rport->port_cfg->utmi_iddig) && + !extcon_get_cable_state_(rphy->edev, + EXTCON_USB_HOST)) { dev_dbg(&rport->phy->dev, "port power on when resume\n"); extcon_set_state_sync(rphy->edev, EXTCON_USB_HOST, true); + /* Enable VBUS supply */ extcon_set_state_sync(rphy->edev, EXTCON_USB_VBUS_EN, true); - gpiod_set_value_cansleep(rport->vbus_drv_gpio, - 1); + ret = rockchip_set_vbus_power(rport, true); + if (ret) + return ret; } } }