Loading Documentation/devicetree/bindings/usb/qcom,msm-phy.txt +3 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,9 @@ Optional properties: microvolts or a value corresponding to voltage corner. - qcom,link-training-reset: This property indicates to start link training timer to reset the elastic buffer based on rx equalization value. - extcon : phandle to external connector devices which provide type-C based "USB-HOST" cable events. This phandle is used for notifying number of lanes used in case of USB+DP concurrent mode to driver. Example: ssphy0: ssphy@f9b38000 { Loading drivers/usb/phy/phy-msm-ssusb-qmp.c +89 −8 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/regulator/consumer.h> #include <linux/usb/phy.h> #include <linux/clk.h> #include <linux/extcon.h> #include <linux/reset.h> #include <linux/hrtimer.h> Loading Loading @@ -137,6 +138,8 @@ struct msm_ssphy_qmp { struct reset_control *phy_reset; struct reset_control *phy_phy_reset; struct reset_control *global_phy_reset; struct extcon_dev *extcon_dp; struct notifier_block dp_nb; bool power_enabled; bool clk_enabled; bool cable_connected; Loading Loading @@ -375,9 +378,11 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy) switch (phy->phy.type) { case USB_PHY_TYPE_USB3_AND_DP: /* override hardware control for reset of qmp phy */ if (!(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) writel_relaxed(SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET, phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); /* update port select */ if (val > 0) { Loading @@ -387,11 +392,13 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy) phy->phy_reg[USB3_DP_COM_TYPEC_CTRL]); } if (!(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) { msm_ssphy_qmp_setmode(phy, USB3_DP_COMBO_MODE); /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); /* bring both USB and DP PHYs PCS block out of reset */ writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); } break; case USB_PHY_TYPE_USB3_OR_DP: if (val > 0) { Loading Loading @@ -479,7 +486,8 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy) } /* perform software reset of PHY common logic */ if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP) if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP && !(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_DP_COM_SW_RESET]); Loading Loading @@ -517,6 +525,25 @@ static int msm_ssphy_qmp_dp_combo_reset(struct usb_phy *uphy) phy); int ret = 0; if (phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE) { dev_dbg(uphy->dev, "Resetting USB part of QMP phy\n"); /* Assert USB3 PHY CSR reset */ ret = reset_control_assert(phy->phy_reset); if (ret) { dev_err(uphy->dev, "phy_reset assert failed\n"); goto exit; } /* Deassert USB3 PHY CSR reset */ ret = reset_control_deassert(phy->phy_reset); if (ret) { dev_err(uphy->dev, "phy_reset deassert failed\n"); goto exit; } return 0; } dev_dbg(uphy->dev, "Global reset of QMP DP combo phy\n"); /* Assert global PHY reset */ ret = reset_control_assert(phy->global_phy_reset); Loading Loading @@ -800,6 +827,56 @@ static int msm_ssphy_qmp_powerup(struct usb_phy *uphy, bool powerup) return 0; } static int msm_ssphy_qmp_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { return 0; } static int msm_ssphy_qmp_dp_notifier(struct notifier_block *nb, unsigned long dp_lane, void *ptr) { struct msm_ssphy_qmp *phy = container_of(nb, struct msm_ssphy_qmp, dp_nb); if (dp_lane == 2 || dp_lane == 4) phy->phy.flags |= PHY_USB_DP_CONCURRENT_MODE; else phy->phy.flags &= ~PHY_USB_DP_CONCURRENT_MODE; return 0; } static int msm_ssphy_qmp_extcon_register(struct msm_ssphy_qmp *phy, struct device *dev) { struct device_node *node = dev->of_node; struct extcon_dev *edev; int ret = 0; if (!of_property_read_bool(node, "extcon")) return 0; edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(edev)) { dev_err(dev, "failed to get phandle for msm_ssphy_qmp\n"); return PTR_ERR(edev); } phy->extcon_dp = edev; phy->phy.vbus_nb.notifier_call = msm_ssphy_qmp_vbus_notifier; phy->dp_nb.notifier_call = msm_ssphy_qmp_dp_notifier; ret = extcon_register_blocking_notifier(edev, EXTCON_DISP_DP, &phy->dp_nb); if (ret < 0) { dev_err(dev, "failed to register blocking notifier\n"); return ret; } return 0; } static int msm_ssphy_qmp_get_clks(struct msm_ssphy_qmp *phy, struct device *dev) { int ret = 0; Loading Loading @@ -1112,6 +1189,10 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) else phy->phy.reset = msm_ssphy_qmp_reset; ret = msm_ssphy_qmp_extcon_register(phy, dev); if (ret) goto err; ret = usb_add_phy_dev(&phy->phy); err: Loading include/linux/usb/phy.h +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #define PHY_LANE_B BIT(7) #define PHY_HSFS_MODE BIT(8) #define PHY_LS_MODE BIT(9) #define PHY_USB_DP_CONCURRENT_MODE BIT(10) enum usb_phy_interface { USBPHY_INTERFACE_MODE_UNKNOWN, Loading Loading
Documentation/devicetree/bindings/usb/qcom,msm-phy.txt +3 −0 Original line number Diff line number Diff line Loading @@ -115,6 +115,9 @@ Optional properties: microvolts or a value corresponding to voltage corner. - qcom,link-training-reset: This property indicates to start link training timer to reset the elastic buffer based on rx equalization value. - extcon : phandle to external connector devices which provide type-C based "USB-HOST" cable events. This phandle is used for notifying number of lanes used in case of USB+DP concurrent mode to driver. Example: ssphy0: ssphy@f9b38000 { Loading
drivers/usb/phy/phy-msm-ssusb-qmp.c +89 −8 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ #include <linux/regulator/consumer.h> #include <linux/usb/phy.h> #include <linux/clk.h> #include <linux/extcon.h> #include <linux/reset.h> #include <linux/hrtimer.h> Loading Loading @@ -137,6 +138,8 @@ struct msm_ssphy_qmp { struct reset_control *phy_reset; struct reset_control *phy_phy_reset; struct reset_control *global_phy_reset; struct extcon_dev *extcon_dp; struct notifier_block dp_nb; bool power_enabled; bool clk_enabled; bool cable_connected; Loading Loading @@ -375,9 +378,11 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy) switch (phy->phy.type) { case USB_PHY_TYPE_USB3_AND_DP: /* override hardware control for reset of qmp phy */ if (!(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) writel_relaxed(SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET, phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); /* update port select */ if (val > 0) { Loading @@ -387,11 +392,13 @@ static void usb_qmp_update_portselect_phymode(struct msm_ssphy_qmp *phy) phy->phy_reg[USB3_DP_COM_TYPEC_CTRL]); } if (!(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) { msm_ssphy_qmp_setmode(phy, USB3_DP_COMBO_MODE); /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); /* bring both USB and DP PHYs PCS block out of reset */ writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_DP_COM_RESET_OVRD_CTRL]); } break; case USB_PHY_TYPE_USB3_OR_DP: if (val > 0) { Loading Loading @@ -479,7 +486,8 @@ static int msm_ssphy_qmp_init(struct usb_phy *uphy) } /* perform software reset of PHY common logic */ if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP) if (phy->phy.type == USB_PHY_TYPE_USB3_AND_DP && !(phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE)) writel_relaxed(0x00, phy->base + phy->phy_reg[USB3_DP_COM_SW_RESET]); Loading Loading @@ -517,6 +525,25 @@ static int msm_ssphy_qmp_dp_combo_reset(struct usb_phy *uphy) phy); int ret = 0; if (phy->phy.flags & PHY_USB_DP_CONCURRENT_MODE) { dev_dbg(uphy->dev, "Resetting USB part of QMP phy\n"); /* Assert USB3 PHY CSR reset */ ret = reset_control_assert(phy->phy_reset); if (ret) { dev_err(uphy->dev, "phy_reset assert failed\n"); goto exit; } /* Deassert USB3 PHY CSR reset */ ret = reset_control_deassert(phy->phy_reset); if (ret) { dev_err(uphy->dev, "phy_reset deassert failed\n"); goto exit; } return 0; } dev_dbg(uphy->dev, "Global reset of QMP DP combo phy\n"); /* Assert global PHY reset */ ret = reset_control_assert(phy->global_phy_reset); Loading Loading @@ -800,6 +827,56 @@ static int msm_ssphy_qmp_powerup(struct usb_phy *uphy, bool powerup) return 0; } static int msm_ssphy_qmp_vbus_notifier(struct notifier_block *nb, unsigned long event, void *ptr) { return 0; } static int msm_ssphy_qmp_dp_notifier(struct notifier_block *nb, unsigned long dp_lane, void *ptr) { struct msm_ssphy_qmp *phy = container_of(nb, struct msm_ssphy_qmp, dp_nb); if (dp_lane == 2 || dp_lane == 4) phy->phy.flags |= PHY_USB_DP_CONCURRENT_MODE; else phy->phy.flags &= ~PHY_USB_DP_CONCURRENT_MODE; return 0; } static int msm_ssphy_qmp_extcon_register(struct msm_ssphy_qmp *phy, struct device *dev) { struct device_node *node = dev->of_node; struct extcon_dev *edev; int ret = 0; if (!of_property_read_bool(node, "extcon")) return 0; edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(edev)) { dev_err(dev, "failed to get phandle for msm_ssphy_qmp\n"); return PTR_ERR(edev); } phy->extcon_dp = edev; phy->phy.vbus_nb.notifier_call = msm_ssphy_qmp_vbus_notifier; phy->dp_nb.notifier_call = msm_ssphy_qmp_dp_notifier; ret = extcon_register_blocking_notifier(edev, EXTCON_DISP_DP, &phy->dp_nb); if (ret < 0) { dev_err(dev, "failed to register blocking notifier\n"); return ret; } return 0; } static int msm_ssphy_qmp_get_clks(struct msm_ssphy_qmp *phy, struct device *dev) { int ret = 0; Loading Loading @@ -1112,6 +1189,10 @@ static int msm_ssphy_qmp_probe(struct platform_device *pdev) else phy->phy.reset = msm_ssphy_qmp_reset; ret = msm_ssphy_qmp_extcon_register(phy, dev); if (ret) goto err; ret = usb_add_phy_dev(&phy->phy); err: Loading
include/linux/usb/phy.h +1 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ #define PHY_LANE_B BIT(7) #define PHY_HSFS_MODE BIT(8) #define PHY_LS_MODE BIT(9) #define PHY_USB_DP_CONCURRENT_MODE BIT(10) enum usb_phy_interface { USBPHY_INTERFACE_MODE_UNKNOWN, Loading