Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c69aa33e authored by Pratham Pratap's avatar Pratham Pratap
Browse files

usb: phy-msm-ssusb-qmp: Perform USB only reset in USB+DP mode



When USB3 controller stops responding to stop endpoint
command driver performs global phy reset to recover from
error state. During this USB host mode recovery is performed
and global phy reset is done but DP driver is not notified
of this reset which results in unclocked access of DP link
registers. Fix this by replacing phy global reset with USB
only reset to not affect DP PHY when USB is reset in DP+USB
concurrent mode.

Change-Id: I2acde04c99b84293644fdefdceee4bd00a964e7f
Signed-off-by: default avatarPratham Pratap <prathampratap@codeaurora.org>
parent 8f25c5b6
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -107,6 +107,9 @@ Optional properties:
   microvolts or a value corresponding to voltage corner.
 - "pcs_clamp_enable_reg" : Clamps the phy data inputs and enables USB3
   autonomous mode.
 - 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 {
+1 −0
Original line number Diff line number Diff line
@@ -83,4 +83,5 @@
&usb_qmp_dp_phy {
	vdd-supply = <&pm660l_l1>; /* 0.88v */
	core-supply = <&pm660_l1>; /* 1.2v */
	extcon = <&pm660_pdphy>;
};
+82 −1
Original line number Diff line number Diff line
@@ -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>

enum ldo_levels {
@@ -133,6 +134,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;
@@ -377,6 +380,17 @@ 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) {
			if (val > 0) {
				dev_dbg(phy->phy.dev,
					"USB QMP PHY: Update TYPEC CTRL(%d)\n",
					val);
				writel_relaxed(val, phy->base +
				 phy->phy_reg[USB3_PHY_PCS_MISC_TYPEC_CTRL]);
			}
			break;
		}

		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]);
@@ -485,7 +499,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]);

@@ -523,6 +538,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);
@@ -716,6 +750,49 @@ static int msm_ssphy_qmp_notify_disconnect(struct usb_phy *uphy,
	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->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;
@@ -1065,6 +1142,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:
+1 −0
Original line number Diff line number Diff line
@@ -22,6 +22,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,