From f964e1bae5e22fc16a89fafa24dd5b89d1717ca4 Mon Sep 17 00:00:00 2001 From: William Wu Date: Sat, 24 Feb 2018 16:29:32 +0800 Subject: [PATCH] phy: rockchip-inno-usb2: use fixed-regulator for vbus power This patch uses a fixed-regulator instead of GPIO pin for usb vbus power. It doesn't fix any issue, but it makes more sense to convert the GPIO code into a fixed-regulator. Change-Id: I7196a9cd592dbb3fab3ef8b9e99babc613a42869 Signed-off-by: William Wu Signed-off-by: Frank Wang --- .../bindings/phy/phy-rockchip-inno-usb2.txt | 3 +- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 104 ++++++++++++++++-- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt index 64f69ca1439b..cce92964c53b 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt @@ -25,7 +25,8 @@ Optional properties: register files". When set driver will request its phandle as one companion-grf for some special SoCs (e.g RV1108). - - rockchip,u2phy-tuning; when set, tuning u2phy to improve usb2 SI. + - 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 513ba9ec5535..d7263ae54b20 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -205,6 +205,7 @@ 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. * @ls_irq: IRQ number assigned for linestate detection. @@ -216,6 +217,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. * @state: define OTG enumeration states before device reset. @@ -229,6 +231,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; @@ -239,6 +242,7 @@ struct rockchip_usb2phy_port { struct delayed_work chg_work; struct delayed_work otg_sm_work; struct delayed_work sm_work; + struct regulator *vbus; const struct rockchip_usb2phy_port_cfg *port_cfg; struct notifier_block event_nb; enum usb_otg_state state; @@ -745,6 +749,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); @@ -764,9 +791,21 @@ static int rockchip_usb2phy_set_mode(struct phy *phy, enum phy_mode mode) * fallthrough */ case PHY_MODE_USB_DEVICE: + /* 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: + /* 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: vbus_det_en = false; @@ -1414,6 +1453,7 @@ static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) { struct rockchip_usb2phy_port *rport = data; struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent); + bool cable_vbus_state = false; if (!property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st) && !property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) @@ -1425,18 +1465,22 @@ static irqreturn_t rockchip_usb2phy_id_irq(int irq, void *data) if (property_enabled(rphy->grf, &rport->port_cfg->idfall_det_st)) { property_enable(rphy->grf, &rport->port_cfg->idfall_det_clr, true); - extcon_set_state(rphy->edev, EXTCON_USB_HOST, true); - extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, true); + cable_vbus_state = true; } else if (property_enabled(rphy->grf, &rport->port_cfg->idrise_det_st)) { property_enable(rphy->grf, &rport->port_cfg->idrise_det_clr, true); - extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); - extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, false); + cable_vbus_state = false; } + + extcon_set_state(rphy->edev, EXTCON_USB_HOST, cable_vbus_state); + extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, cable_vbus_state); + extcon_sync(rphy->edev, EXTCON_USB_HOST); extcon_sync(rphy->edev, EXTCON_USB_VBUS_EN); + rockchip_set_vbus_power(rport, cable_vbus_state); + mutex_unlock(&rport->mutex); return IRQ_HANDLED; @@ -1506,6 +1550,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); @@ -1518,14 +1563,35 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, rport->utmi_avalid = of_property_read_bool(child_np, "rockchip,utmi-avalid"); + /* 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); if (rport->mode == USB_DR_MODE_HOST || - rport->mode == USB_DR_MODE_UNKNOWN || - rport->vbus_always_on) { - ret = 0; + rport->mode == USB_DR_MODE_UNKNOWN) { + 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); + ret = rockchip_set_vbus_power(rport, true); + if (ret) + return ret; + } goto out; } + if (rport->vbus_always_on) + goto out; + INIT_DELAYED_WORK(&rport->bypass_uart_work, rockchip_usb_bypass_uart_work); INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); @@ -1605,9 +1671,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); - } else { - extcon_set_state(rphy->edev, EXTCON_USB_HOST, false); - extcon_set_state(rphy->edev, EXTCON_USB_VBUS_EN, false); + /* Enable VBUS supply */ + ret = rockchip_set_vbus_power(rport, true); + if (ret) + return ret; } } @@ -1961,6 +2028,23 @@ static int rockchip_usb2phy_pm_resume(struct device *dev) "failed to enable id irq\n"); return ret; } + + if (!property_enabled(rphy->grf, + &rport->port_cfg->utmi_iddig) && + !extcon_get_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); + ret = rockchip_set_vbus_power(rport, true); + if (ret) + return ret; + } } }