diff --git a/Documentation/devicetree/bindings/usb/msm-ssusb.txt b/Documentation/devicetree/bindings/usb/msm-ssusb.txt index fa6ee8969946..47fad8aa4a1a 100644 --- a/Documentation/devicetree/bindings/usb/msm-ssusb.txt +++ b/Documentation/devicetree/bindings/usb/msm-ssusb.txt @@ -19,6 +19,7 @@ Required properties : and reset lines used by this controller. - reset-names: reset signal name strings sorted in the same order as the resets property. +- qcom,core-clk-rate: clock frequency to be set for USB master clock. Optional properties : - reg: Additional registers @@ -54,7 +55,8 @@ Optional properties : - qcom,disable-dev-mode-pm: If present, it disables PM runtime functionality for device mode. - qcom,disable-host-mode-pm: If present, it disables XHCI PM runtime functionality when USB host mode is used. -- qcom,core-clk-rate: If present, indicates clock frequency to be set for USB master clock. +- qcom,core-clk-rate-hs: If present, indicates min core clock frequency required to support + hs speed. - extcon: phandles to external connector devices. First phandle should point to external connector, which provide "USB" cable events, the second should point to external connector device, which provide "USB-HOST" diff --git a/arch/arm/boot/dts/qcom/msm8998.dtsi b/arch/arm/boot/dts/qcom/msm8998.dtsi index 9490046133a3..c71392dad5de 100644 --- a/arch/arm/boot/dts/qcom/msm8998.dtsi +++ b/arch/arm/boot/dts/qcom/msm8998.dtsi @@ -1739,6 +1739,7 @@ "utmi_clk", "sleep_clk", "xo"; qcom,core-clk-rate = <120000000>; + qcom,core-clk-rate-hs = <60000000>; resets = <&clock_gcc USB_30_BCR>; reset-names = "core_reset"; diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index ecfee972ea56..1e252febc783 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -156,6 +156,7 @@ struct dwc3_msm { struct clk *xo_clk; struct clk *core_clk; long core_clk_rate; + long core_clk_rate_hs; struct clk *iface_clk; struct clk *sleep_clk; struct clk *utmi_clk; @@ -195,6 +196,7 @@ struct dwc3_msm { struct power_supply *usb_psy; struct work_struct vbus_draw_work; bool in_host_mode; + enum usb_device_speed max_rh_port_speed; unsigned int tx_fifo_size; bool vbus_active; bool suspend; @@ -342,6 +344,23 @@ static inline void dwc3_msm_write_readback(void *base, u32 offset, __func__, val, offset); } +static bool dwc3_msm_is_ss_rhport_connected(struct dwc3_msm *mdwc) +{ + int i, num_ports; + u32 reg; + + reg = dwc3_msm_read_reg(mdwc->base, USB3_HCSPARAMS1); + num_ports = HCS_MAX_PORTS(reg); + + for (i = 0; i < num_ports; i++) { + reg = dwc3_msm_read_reg(mdwc->base, USB3_PORTSC + i*0x10); + if ((reg & PORT_CONNECT) && DEV_SUPERSPEED(reg)) + return true; + } + + return false; +} + static bool dwc3_msm_is_host_superspeed(struct dwc3_msm *mdwc) { int i, num_ports; @@ -2128,6 +2147,7 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc) static int dwc3_msm_resume(struct dwc3_msm *mdwc) { int ret; + long core_clk_rate; struct dwc3 *dwc = platform_get_drvdata(mdwc->dwc3); dev_dbg(mdwc->dev, "%s: exiting lpm\n", __func__); @@ -2175,7 +2195,15 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) clk_prepare_enable(mdwc->iface_clk); if (mdwc->noc_aggr_clk) clk_prepare_enable(mdwc->noc_aggr_clk); - clk_set_rate(mdwc->core_clk, mdwc->core_clk_rate); + + core_clk_rate = mdwc->core_clk_rate; + if (mdwc->in_host_mode && mdwc->max_rh_port_speed == USB_SPEED_HIGH) { + core_clk_rate = mdwc->core_clk_rate_hs; + dev_dbg(mdwc->dev, "%s: set hs core clk rate %ld\n", __func__, + core_clk_rate); + } + + clk_set_rate(mdwc->core_clk, core_clk_rate); clk_prepare_enable(mdwc->core_clk); /* set Memory core: ON, Memory periphery: ON */ @@ -2496,6 +2524,11 @@ static int dwc3_msm_get_clk_gdsc(struct dwc3_msm *mdwc) if (ret) dev_err(mdwc->dev, "fail to set core_clk freq:%d\n", ret); + if (of_property_read_u32(mdwc->dev->of_node, "qcom,core-clk-rate-hs", + (u32 *)&mdwc->core_clk_rate_hs)) { + dev_dbg(mdwc->dev, "USB core-clk-rate-hs is not present\n"); + mdwc->core_clk_rate_hs = mdwc->core_clk_rate; + } mdwc->core_reset = devm_reset_control_get(mdwc->dev, "core_reset"); if (IS_ERR(mdwc->core_reset)) { @@ -3179,10 +3212,26 @@ static int dwc3_msm_host_notifier(struct notifier_block *nb, if (udev->parent && !udev->parent->parent && udev->dev.parent->parent == &dwc->xhci->dev) { if (event == USB_DEVICE_ADD && udev->actconfig) { + if (!dwc3_msm_is_ss_rhport_connected(mdwc)) { + /* + * Core clock rate can be reduced only if root + * hub SS port is not enabled/connected. + */ + clk_set_rate(mdwc->core_clk, + mdwc->core_clk_rate_hs); + dev_dbg(mdwc->dev, + "set hs core clk rate %ld\n", + mdwc->core_clk_rate_hs); + mdwc->max_rh_port_speed = USB_SPEED_HIGH; + } else { + mdwc->max_rh_port_speed = USB_SPEED_SUPER; + } + if (udev->speed >= USB_SPEED_SUPER) max_power = udev->actconfig->desc.bMaxPower * 8; else max_power = udev->actconfig->desc.bMaxPower * 2; + dev_dbg(mdwc->dev, "%s configured bMaxPower:%d (mA)\n", dev_name(&udev->dev), max_power); @@ -3194,6 +3243,7 @@ static int dwc3_msm_host_notifier(struct notifier_block *nb, pval.intval = 0; power_supply_set_property(mdwc->usb_psy, POWER_SUPPLY_PROP_BOOST_CURRENT, &pval); + mdwc->max_rh_port_speed = USB_SPEED_UNKNOWN; } }