Skip to contentSkip to conversation

phy: qualcomm: Add suspend/resume support for USB PHYs

PATCH Superseded Manu Gautam 2 participants 14 messages Jul 21, 2017 – Aug 9, 2017

Subsystems (4)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover phy: qualcomm: Add suspend/resume support for USB PHYs

These patches add support for runtime PM in Qualcomm QUSB2 and QMP USB PHY drivers.
It also requires notifying usb3 PHY of USB mode (device/host) so that driver can decide whther to power down PHY or not. This can later be extended to add support in PHY drivers to enable wakeup events detection on dp/dm or SS lines (for LFPS or rx-terminations) only for bus suspend case.

Also addresses a requirement where USB PHYs must be powered before initialization.

Manu Gautam (5):
phy: qcom-qmp: Power-on PHY before initialization
phy: qcom-qusb2: Power-on PHY before initialization
phy: qcom-qusb2: Add support for runtime PM
phy: qcom-qmp: Add support for runtime PM
usb: dwc3: core: Notify USB3 PHY as well for DRD modes

Vivek Gautam (1):
phy: qcom-qmp: Fix phy pipe clock gating

 drivers/phy/qualcomm/phy-qcom-qmp.c   | 196 +++++++++++++++++++++++++++-------
 drivers/phy/qualcomm/phy-qcom-qusb2.c | 141 +++++++++++++++++++++---
 drivers/usb/dwc3/core.c               |  17 ++-
 3 files changed, 295 insertions(+), 59 deletions(-)
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
#1 lore raw

PATCH 1/6 phy: qcom-qmp: Fix phy pipe clock gating

From: Vivek Gautam <[email protected]>

Pipe clock comes out of the phy and is available as long as the phy is turned on. Clock controller fails to gate this clock after the phy is turned off and generates a warning.

/ # [ 33.048561] gcc_usb3_phy_pipe_clk status stuck at 'on'

[   33.048585] ------------[ cut here ]------------
[   33.052621] WARNING: CPU: 1 PID: 18 at ../drivers/clk/qcom/clk-branch.c:97 clk_branch_wait+0xf0/0x108
[   33.057384] Modules linked in:
[   33.066497] CPU: 1 PID: 18 Comm: kworker/1:0 Tainted: G        W       4.12.0-rc7-00024-gfe926e34c36d-dirty #96
[   33.069451] Hardware name: Qualcomm Technologies, Inc. DB820c (DT)
...
[   33.278565] [<ffff00000849b27c>] clk_branch_wait+0xf0/0x108
[   33.286375] [<ffff00000849b2f4>] clk_branch2_disable+0x28/0x34
[   33.291761] [<ffff0000084868dc>] clk_core_disable+0x5c/0x88
[   33.297660] [<ffff000008487d68>] clk_core_disable_lock+0x20/0x34
[   33.303129] [<ffff000008487d98>] clk_disable+0x1c/0x24
[   33.309384] [<ffff0000083ccd78>] qcom_qmp_phy_poweroff+0x20/0x48
[   33.314328] [<ffff0000083c53f4>] phy_power_off+0x80/0xdc
[   33.320492] [<ffff00000875c950>] dwc3_core_exit+0x94/0xa0
[   33.325784] [<ffff00000875c9ac>] dwc3_suspend_common+0x50/0x60
[   33.331080] [<ffff00000875ca04>] dwc3_runtime_suspend+0x48/0x6c
[   33.336810] [<ffff0000085b82f4>] pm_generic_runtime_suspend+0x28/0x38
[   33.342627] [<ffff0000085bace0>] __rpm_callback+0x150/0x254
[   33.349222] [<ffff0000085bae08>] rpm_callback+0x24/0x78
[   33.354604] [<ffff0000085b9fd8>] rpm_suspend+0xe0/0x4e4
[   33.359813] [<ffff0000085bb784>] pm_runtime_work+0xdc/0xf0
[   33.365028] [<ffff0000080d7b30>] process_one_work+0x12c/0x28c
[   33.370576] [<ffff0000080d7ce8>] worker_thread+0x58/0x3b8
[   33.376393] [<ffff0000080dd4a8>] kthread+0x100/0x12c
[   33.381776] [<ffff0000080836c0>] ret_from_fork+0x10/0x50

Fix this by enabling pipe clock at the end of phy_init(), and disabling it as the first thing in phy_exit().

Fixes e78f3d15e115 ("phy: qcom-qmp: new qmp phy driver for qcom-chipsets")
drivers/phy/qualcomm/phy-qcom-qmp.c +12 -14
index 78ca628..a230c7b 100644
@@ -610,19 +610,10 @@ static int qcom_qmp_phy_poweron(struct phy *phy)
 
 	/* turn on regulator supplies */
 	ret = regulator_bulk_enable(num, qmp->vregs);
-	if (ret) {
+	if (ret)
 		dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
-		return ret;
-	}
-
-	ret = clk_prepare_enable(qphy->pipe_clk);
-	if (ret) {
-		dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret);
-		regulator_bulk_disable(num, qmp->vregs);
-		return ret;
-	}
 
-	return 0;
+	return ret;
 }
 
 static int qcom_qmp_phy_poweroff(struct phy *phy)
@@ -630,8 +621,6 @@ static int qcom_qmp_phy_poweroff(struct phy *phy)
 	struct qmp_phy *qphy = phy_get_drvdata(phy);
 	struct qcom_qmp *qmp = qphy->qmp;
 
-	clk_disable_unprepare(qphy->pipe_clk);
-
 	regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs);
 
 	return 0;
@@ -797,7 +786,14 @@ static int qcom_qmp_phy_init(struct phy *phy)
 		goto err_pcs_ready;
 	}
 
-	return ret;
+	/* phy is initialized; we can turn on the pipe clock now */
+	ret = clk_prepare_enable(qphy->pipe_clk);
+	if (ret) {
+		dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret);
+		goto err_pcs_ready;
+	}
+
+	return 0;
 
 err_pcs_ready:
 	if (cfg->has_lane_rst)
@@ -818,6 +814,8 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 	const struct qmp_phy_cfg *cfg = qmp->cfg;
 	int i = cfg->num_clks;
 
+	clk_disable_unprepare(qphy->pipe_clk);
+
 	/* PHY reset */
 	qphy_setbits(qphy->pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
 
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
#2 lore raw

Re: [PATCH v1 1/6] phy: qcom-qmp: Fix phy pipe clock gating

Quoted 33 lines · Manu Gautam

From: Vivek Gautam <[email protected]>

Pipe clock comes out of the phy and is available as long as the phy is turned on. Clock controller fails to gate this clock after the phy is turned off and generates a warning.

/ # [ 33.048561] gcc_usb3_phy_pipe_clk status stuck at 'on'

[   33.048585] ------------[ cut here ]------------
[   33.052621] WARNING: CPU: 1 PID: 18 at ../drivers/clk/qcom/clk-branch.c:97 clk_branch_wait+0xf0/0x108
[   33.057384] Modules linked in:
[   33.066497] CPU: 1 PID: 18 Comm: kworker/1:0 Tainted: G        W       4.12.0-rc7-00024-gfe926e34c36d-dirty #96
[   33.069451] Hardware name: Qualcomm Technologies, Inc. DB820c (DT)
...
[   33.278565] [<ffff00000849b27c>] clk_branch_wait+0xf0/0x108
[   33.286375] [<ffff00000849b2f4>] clk_branch2_disable+0x28/0x34
[   33.291761] [<ffff0000084868dc>] clk_core_disable+0x5c/0x88
[   33.297660] [<ffff000008487d68>] clk_core_disable_lock+0x20/0x34
[   33.303129] [<ffff000008487d98>] clk_disable+0x1c/0x24
[   33.309384] [<ffff0000083ccd78>] qcom_qmp_phy_poweroff+0x20/0x48
[   33.314328] [<ffff0000083c53f4>] phy_power_off+0x80/0xdc
[   33.320492] [<ffff00000875c950>] dwc3_core_exit+0x94/0xa0
[   33.325784] [<ffff00000875c9ac>] dwc3_suspend_common+0x50/0x60
[   33.331080] [<ffff00000875ca04>] dwc3_runtime_suspend+0x48/0x6c
[   33.336810] [<ffff0000085b82f4>] pm_generic_runtime_suspend+0x28/0x38
[   33.342627] [<ffff0000085bace0>] __rpm_callback+0x150/0x254
[   33.349222] [<ffff0000085bae08>] rpm_callback+0x24/0x78
[   33.354604] [<ffff0000085b9fd8>] rpm_suspend+0xe0/0x4e4
[   33.359813] [<ffff0000085bb784>] pm_runtime_work+0xdc/0xf0
[   33.365028] [<ffff0000080d7b30>] process_one_work+0x12c/0x28c
[   33.370576] [<ffff0000080d7ce8>] worker_thread+0x58/0x3b8
[   33.376393] [<ffff0000080dd4a8>] kthread+0x100/0x12c
[   33.381776] [<ffff0000080836c0>] ret_from_fork+0x10/0x50
Quoted 6 lines

Fix this by enabling pipe clock at the end of phy_init(), and disabling it as the first thing in phy_exit().

Fixes e78f3d15e115 ("phy: qcom-qmp: new qmp phy driver for qcom-chipsets")

Missing your signoff? Also, the fixes tag should be right before signoff without a newline between.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
#3 lore raw

Re: [PATCH v1 1/6] phy: qcom-qmp: Fix phy pipe clock gating

Quoted 34 lines · Stephen Boyd
Quoted 33 lines · Manu Gautam

From: Vivek Gautam <[email protected]>

Pipe clock comes out of the phy and is available as long as the phy is turned on. Clock controller fails to gate this clock after the phy is turned off and generates a warning.

/ # [ 33.048561] gcc_usb3_phy_pipe_clk status stuck at 'on'

[   33.048585] ------------[ cut here ]------------
[   33.052621] WARNING: CPU: 1 PID: 18 at ../drivers/clk/qcom/clk-branch.c:97 clk_branch_wait+0xf0/0x108
[   33.057384] Modules linked in:
[   33.066497] CPU: 1 PID: 18 Comm: kworker/1:0 Tainted: G        W       4.12.0-rc7-00024-gfe926e34c36d-dirty #96
[   33.069451] Hardware name: Qualcomm Technologies, Inc. DB820c (DT)
...
[   33.278565] [<ffff00000849b27c>] clk_branch_wait+0xf0/0x108
[   33.286375] [<ffff00000849b2f4>] clk_branch2_disable+0x28/0x34
[   33.291761] [<ffff0000084868dc>] clk_core_disable+0x5c/0x88
[   33.297660] [<ffff000008487d68>] clk_core_disable_lock+0x20/0x34
[   33.303129] [<ffff000008487d98>] clk_disable+0x1c/0x24
[   33.309384] [<ffff0000083ccd78>] qcom_qmp_phy_poweroff+0x20/0x48
[   33.314328] [<ffff0000083c53f4>] phy_power_off+0x80/0xdc
[   33.320492] [<ffff00000875c950>] dwc3_core_exit+0x94/0xa0
[   33.325784] [<ffff00000875c9ac>] dwc3_suspend_common+0x50/0x60
[   33.331080] [<ffff00000875ca04>] dwc3_runtime_suspend+0x48/0x6c
[   33.336810] [<ffff0000085b82f4>] pm_generic_runtime_suspend+0x28/0x38
[   33.342627] [<ffff0000085bace0>] __rpm_callback+0x150/0x254
[   33.349222] [<ffff0000085bae08>] rpm_callback+0x24/0x78
[   33.354604] [<ffff0000085b9fd8>] rpm_suspend+0xe0/0x4e4
[   33.359813] [<ffff0000085bb784>] pm_runtime_work+0xdc/0xf0
[   33.365028] [<ffff0000080d7b30>] process_one_work+0x12c/0x28c
[   33.370576] [<ffff0000080d7ce8>] worker_thread+0x58/0x3b8
[   33.376393] [<ffff0000080dd4a8>] kthread+0x100/0x12c
[   33.381776] [<ffff0000080836c0>] ret_from_fork+0x10/0x50
Quoted 9 lines
Quoted 6 lines

Fix this by enabling pipe clock at the end of phy_init(), and disabling it as the first thing in phy_exit().

Fixes e78f3d15e115 ("phy: qcom-qmp: new qmp phy driver for qcom-chipsets")

Missing your signoff? Also, the fixes tag should be right before signoff without a newline between.

Will fix it in next version.

#4 lore raw

PATCH 2/6 phy: qcom-qmp: Power-on PHY before initialization

In reply to Manu Gautam

PHY must be powered on before turning ON clocks and attempting to initialize it. Driver is exposing separate init and power_on routines for this. Apparently USB dwc3 core driver performs power-on after init. Also, poweron and init for QMP PHY need to be executed together always, hence remove poweron callback from phy_ops and explicitly perform this from init, similar changes needed for poweroff.

drivers/phy/qualcomm/phy-qcom-qmp.c +63 -24
index a230c7b..aefb853 100644
@@ -479,6 +479,8 @@ struct qcom_qmp {
 
 	struct mutex phy_mutex;
 	int init_count;
+	bool power_enabled;
+	bool clk_enabled;
 };
 
 static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@@ -599,29 +601,69 @@ static void qcom_qmp_phy_configure(void __iomem *base,
 	}
 }
 
-static int qcom_qmp_phy_poweron(struct phy *phy)
+static int qcom_qmp_phy_poweron(struct qcom_qmp *qmp)
 {
-	struct qmp_phy *qphy = phy_get_drvdata(phy);
-	struct qcom_qmp *qmp = qphy->qmp;
 	int num = qmp->cfg->num_vregs;
 	int ret;
 
-	dev_vdbg(&phy->dev, "Powering on QMP phy\n");
+	dev_vdbg(qmp->dev, "Powering on QMP phy\n");
+
+	if (qmp->power_enabled)
+		return 0;
 
 	/* turn on regulator supplies */
 	ret = regulator_bulk_enable(num, qmp->vregs);
 	if (ret)
 		dev_err(qmp->dev, "failed to enable regulators, err=%d\n", ret);
+	else
+		qmp->power_enabled = true;
 
 	return ret;
 }
 
-static int qcom_qmp_phy_poweroff(struct phy *phy)
+static int qcom_qmp_phy_poweroff(struct qcom_qmp *qmp)
 {
-	struct qmp_phy *qphy = phy_get_drvdata(phy);
-	struct qcom_qmp *qmp = qphy->qmp;
+	if (!qmp->power_enabled)
+		return 0;
 
 	regulator_bulk_disable(qmp->cfg->num_vregs, qmp->vregs);
+	qmp->power_enabled = false;
+
+	return 0;
+}
+
+static int qcom_qmp_phy_enable_clocks(struct qcom_qmp *qmp)
+{
+	int ret = 0, i;
+
+	if (qmp->clk_enabled)
+		return 0;
+
+	for (i = 0; i < qmp->cfg->num_clks; i++) {
+		ret = clk_prepare_enable(qmp->clks[i]);
+		if (ret) {
+			dev_err(qmp->dev, "failed to enable %s clk, err=%d\n",
+				qmp->cfg->clk_list[i], ret);
+			while (--i >= 0)
+				clk_disable_unprepare(qmp->clks[i]);
+		}
+	}
+	qmp->clk_enabled = true;
+
+	return ret;
+}
+
+static int qcom_qmp_phy_disable_clocks(struct qcom_qmp *qmp)
+{
+	int i = qmp->cfg->num_clks;
+
+	if (!qmp->clk_enabled)
+		return 0;
+
+	while (--i >= 0)
+		clk_disable_unprepare(qmp->clks[i]);
+
+	qmp->clk_enabled = false;
 
 	return 0;
 }
@@ -729,19 +771,17 @@ static int qcom_qmp_phy_init(struct phy *phy)
 	void __iomem *pcs = qphy->pcs;
 	void __iomem *status;
 	unsigned int mask, val;
-	int ret, i;
+	int ret;
 
 	dev_vdbg(qmp->dev, "Initializing QMP phy\n");
 
-	for (i = 0; i < qmp->cfg->num_clks; i++) {
-		ret = clk_prepare_enable(qmp->clks[i]);
-		if (ret) {
-			dev_err(qmp->dev, "failed to enable %s clk, err=%d\n",
-				qmp->cfg->clk_list[i], ret);
-			while (--i >= 0)
-				clk_disable_unprepare(qmp->clks[i]);
-		}
-	}
+	ret = qcom_qmp_phy_poweron(qmp);
+	if (ret)
+		return ret;
+
+	ret = qcom_qmp_phy_enable_clocks(qmp);
+	if (ret)
+		goto err_clk_enable;
 
 	ret = qcom_qmp_phy_com_init(qmp);
 	if (ret)
@@ -801,8 +841,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
 err_lane_rst:
 	qcom_qmp_phy_com_exit(qmp);
 err_com_init:
-	while (--i >= 0)
-		clk_disable_unprepare(qmp->clks[i]);
+	qcom_qmp_phy_disable_clocks(qmp);
+err_clk_enable:
+	qcom_qmp_phy_poweroff(qmp);
 
 	return ret;
 }
@@ -812,7 +853,6 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 	struct qmp_phy *qphy = phy_get_drvdata(phy);
 	struct qcom_qmp *qmp = qphy->qmp;
 	const struct qmp_phy_cfg *cfg = qmp->cfg;
-	int i = cfg->num_clks;
 
 	clk_disable_unprepare(qphy->pipe_clk);
 
@@ -830,8 +870,9 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 
 	qcom_qmp_phy_com_exit(qmp);
 
-	while (--i >= 0)
-		clk_disable_unprepare(qmp->clks[i]);
+	qcom_qmp_phy_disable_clocks(qmp);
+
+	qcom_qmp_phy_poweroff(qmp);
 
 	return 0;
 }
@@ -958,8 +999,6 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id)
 static const struct phy_ops qcom_qmp_phy_gen_ops = {
 	.init		= qcom_qmp_phy_init,
 	.exit		= qcom_qmp_phy_exit,
-	.power_on	= qcom_qmp_phy_poweron,
-	.power_off	= qcom_qmp_phy_poweroff,
 	.owner		= THIS_MODULE,
 };
 
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
#5 lore raw

Re: [PATCH v1 2/6] phy: qcom-qmp: Power-on PHY before initialization

Quoted 18 lines · Manu Gautam

}

drivers/phy/qualcomm/phy-qcom-qmp.c +1 -9
@@ -729,19 +771,17 @@ static int qcom_qmp_phy_init(struct phy *phy)
 	void __iomem *pcs = qphy->pcs;
 	void __iomem *status;
 	unsigned int mask, val;
-	int ret, i;
+	int ret;
 
 	dev_vdbg(qmp->dev, "Initializing QMP phy\n");
 
-	for (i = 0; i < qmp->cfg->num_clks; i++) {
-		ret = clk_prepare_enable(qmp->clks[i]);
-		if (ret) {
-			dev_err(qmp->dev, "failed to enable %s clk, err=%d\n",
-				qmp->cfg->clk_list[i], ret);
-			while (--i >= 0)
-				clk_disable_unprepare(qmp->clks[i]);
-		}

drivers/phy/qualcomm/phy-qcom-qmp.c static int qcom_qmp_phy_init(struct phy *phy) · line 786

Quoted 10 lines
drivers/phy/qualcomm/phy-qcom-qmp.c +7 -1
-	}
+	ret = qcom_qmp_phy_poweron(qmp);
+	if (ret)
+		return ret;
+
+	ret = qcom_qmp_phy_enable_clocks(qmp);
+	if (ret)
+		goto err_clk_enable;
 
 	ret = qcom_qmp_phy_com_init(qmp);

In next version I will move poweron and clock_enable to com_init as PCI QMP PHY might use multiple PHY instances (for each lane) each of them will call phy_init/exit.
qcom_qmp_phy_com_init executed only once, hence better suited for turning ON clocks/regulators.

Quoted 22 lines
drivers/phy/qualcomm/phy-qcom-qmp.c +3 -3
 	if (ret)
@@ -801,8 +841,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
 err_lane_rst:
 	qcom_qmp_phy_com_exit(qmp);
 err_com_init:
-	while (--i >= 0)
-		clk_disable_unprepare(qmp->clks[i]);
+	qcom_qmp_phy_disable_clocks(qmp);
+err_clk_enable:
+	qcom_qmp_phy_poweroff(qmp);
 
 	return ret;
 }
@@ -812,7 +853,6 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 	struct qmp_phy *qphy = phy_get_drvdata(phy);
 	struct qcom_qmp *qmp = qphy->qmp;
 	const struct qmp_phy_cfg *cfg = qmp->cfg;
-	int i = cfg->num_clks;
 
 	clk_disable_unprepare(qphy->pipe_clk);
 

drivers/phy/qualcomm/phy-qcom-qmp.c static int qcom_qmp_phy_exit(struct phy *phy) · line 875

Quoted 8 lines
drivers/phy/qualcomm/phy-qcom-qmp.c +3 -2
@@ -830,8 +870,9 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 
 	qcom_qmp_phy_com_exit(qmp);
 
-	while (--i >= 0)
-		clk_disable_unprepare(qmp->clks[i]);
+	qcom_qmp_phy_disable_clocks(qmp);
+
+	qcom_qmp_phy_poweroff(qmp);

On similar lines will be moving these to qcom_qmp_phy_com_exit().

Quoted 12 lines
drivers/phy/qualcomm/phy-qcom-qmp.c +0 -2
 	return 0;
 }
@@ -958,8 +999,6 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id)
 static const struct phy_ops qcom_qmp_phy_gen_ops = {
 	.init		= qcom_qmp_phy_init,
 	.exit		= qcom_qmp_phy_exit,
-	.power_on	= qcom_qmp_phy_poweron,
-	.power_off	= qcom_qmp_phy_poweroff,
 	.owner		= THIS_MODULE,
 };
 
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project
#6 lore raw

PATCH 3/6 phy: qcom-qusb2: Power-on PHY before initialization

In reply to Manu Gautam

PHY must be powered on before turning ON clocks and attempting to initialize it. Driver is exposing separate init and power_on routines for this. Apparently USB dwc3 core driver performs power-on after init. Also, poweron and init for QUSB2 PHY need to be executed together always, hence remove poweron callback from phy_ops and explicitly perform this from init, similar changes needed for poweroff.

drivers/phy/qualcomm/phy-qcom-qusb2.c +15 -10
index 6c57524..fa60a99 100644
@@ -195,13 +195,13 @@ static void qusb2_phy_set_tune2_param(struct qusb2_phy *qphy)
 	qusb2_setbits(qphy->base, QUSB2PHY_PORT_TUNE2, val[0] << 0x4);
 }
 
-static int qusb2_phy_poweron(struct phy *phy)
+static int qusb2_phy_poweron(struct qusb2_phy *qphy)
 {
-	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+	struct device *dev = &qphy->phy->dev;
 	int num = ARRAY_SIZE(qphy->vregs);
 	int ret;
 
-	dev_vdbg(&phy->dev, "%s(): Powering-on QUSB2 phy\n", __func__);
+	dev_vdbg(dev, "%s(): Powering-on QUSB2 phy\n", __func__);
 
 	/* turn on regulator supplies */
 	ret = regulator_bulk_enable(num, qphy->vregs);
@@ -210,7 +210,7 @@ static int qusb2_phy_poweron(struct phy *phy)
 
 	ret = clk_prepare_enable(qphy->iface_clk);
 	if (ret) {
-		dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
+		dev_err(dev, "failed to enable iface_clk, %d\n", ret);
 		regulator_bulk_disable(num, qphy->vregs);
 		return ret;
 	}
@@ -218,10 +218,8 @@ static int qusb2_phy_poweron(struct phy *phy)
 	return 0;
 }
 
-static int qusb2_phy_poweroff(struct phy *phy)
+static int qusb2_phy_poweroff(struct qusb2_phy *qphy)
 {
-	struct qusb2_phy *qphy = phy_get_drvdata(phy);
-
 	clk_disable_unprepare(qphy->iface_clk);
 
 	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
@@ -238,11 +236,15 @@ static int qusb2_phy_init(struct phy *phy)
 
 	dev_vdbg(&phy->dev, "%s(): Initializing QUSB2 phy\n", __func__);
 
+	ret = qusb2_phy_poweron(qphy);
+	if (ret)
+		return ret;
+
 	/* enable ahb interface clock to program phy */
 	ret = clk_prepare_enable(qphy->cfg_ahb_clk);
 	if (ret) {
 		dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
-		return ret;
+		goto poweroff_phy;
 	}
 
 	/* Perform phy reset */
@@ -344,6 +346,9 @@ static int qusb2_phy_init(struct phy *phy)
 	reset_control_assert(qphy->phy_reset);
 disable_ahb_clk:
 	clk_disable_unprepare(qphy->cfg_ahb_clk);
+poweroff_phy:
+	qusb2_phy_poweroff(qphy);
+
 	return ret;
 }
 
@@ -362,14 +367,14 @@ static int qusb2_phy_exit(struct phy *phy)
 
 	clk_disable_unprepare(qphy->cfg_ahb_clk);
 
+	qusb2_phy_poweroff(qphy);
+
 	return 0;
 }
 
 static const struct phy_ops qusb2_phy_gen_ops = {
 	.init		= qusb2_phy_init,
 	.exit		= qusb2_phy_exit,
-	.power_on	= qusb2_phy_poweron,
-	.power_off	= qusb2_phy_poweroff,
 	.owner		= THIS_MODULE,
 };
 
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
#7 lore raw

PATCH 4/6 phy: qcom-qusb2: Add support for runtime PM

In reply to Manu Gautam

Driver can turn off clocks during runtime suspend. Also, runtime suspend is not as a result of host mode selective suspend then PHY can be powered off as well.

drivers/phy/qualcomm/phy-qcom-qusb2.c +116 -8
index fa60a99..b505681 100644
@@ -132,6 +132,9 @@ struct qusb2_phy {
 
 	const struct qusb2_phy_cfg *cfg;
 	bool has_se_clk_scheme;
+	bool phy_initialized;
+	bool powered_on;
+	enum phy_mode mode;
 };
 
 static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
@@ -203,30 +206,111 @@ static int qusb2_phy_poweron(struct qusb2_phy *qphy)
 
 	dev_vdbg(dev, "%s(): Powering-on QUSB2 phy\n", __func__);
 
+	if (qphy->powered_on)
+		return 0;
+
 	/* turn on regulator supplies */
 	ret = regulator_bulk_enable(num, qphy->vregs);
 	if (ret)
 		return ret;
 
-	ret = clk_prepare_enable(qphy->iface_clk);
-	if (ret) {
-		dev_err(dev, "failed to enable iface_clk, %d\n", ret);
-		regulator_bulk_disable(num, qphy->vregs);
-		return ret;
-	}
+	qphy->powered_on = true;
 
 	return 0;
 }
 
 static int qusb2_phy_poweroff(struct qusb2_phy *qphy)
 {
-	clk_disable_unprepare(qphy->iface_clk);
+	if (!qphy->powered_on)
+		return 0;
 
 	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
 
+	qphy->powered_on = false;
+
+	return 0;
+}
+
+static int qusb2_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	qphy->mode = mode;
+
 	return 0;
 }
 
+static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+
+	dev_vdbg(dev, "Suspending QUSB2 Phy, mode:%d\n", qphy->mode);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	if (!qphy->has_se_clk_scheme)
+		clk_disable_unprepare(qphy->ref_clk);
+
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+	clk_disable_unprepare(qphy->iface_clk);
+
+	if (qphy->mode == PHY_MODE_INVALID)
+		qusb2_phy_poweroff(qphy);
+
+	return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	int ret;
+
+	dev_vdbg(dev, "Resuming QUSB2 phy, mode:%d\n", qphy->mode);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	ret = qusb2_phy_poweron(qphy);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(qphy->iface_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable iface_clk, %d\n", ret);
+		goto poweroff_phy;
+	}
+
+	ret = clk_prepare_enable(qphy->cfg_ahb_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable cfg ahb clock, %d\n", ret);
+		goto disable_iface_clk;
+	}
+
+	if (!qphy->has_se_clk_scheme) {
+		clk_prepare_enable(qphy->ref_clk);
+		if (ret) {
+			dev_err(dev, "failed to enable ref clk, %d\n", ret);
+			goto disable_ahb_clk;
+		}
+	}
+
+	return 0;
+
+disable_ahb_clk:
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+	clk_disable_unprepare(qphy->iface_clk);
+poweroff_phy:
+	qusb2_phy_poweroff(qphy);
+
+	return ret;
+}
+
 static int qusb2_phy_init(struct phy *phy)
 {
 	struct qusb2_phy *qphy = phy_get_drvdata(phy);
@@ -240,11 +324,17 @@ static int qusb2_phy_init(struct phy *phy)
 	if (ret)
 		return ret;
 
+	ret = clk_prepare_enable(qphy->iface_clk);
+	if (ret) {
+		dev_err(&phy->dev, "failed to enable iface_clk, %d\n", ret);
+		goto poweroff_phy;
+	}
+
 	/* enable ahb interface clock to program phy */
 	ret = clk_prepare_enable(qphy->cfg_ahb_clk);
 	if (ret) {
 		dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
-		goto poweroff_phy;
+		goto disable_iface_clk;
 	}
 
 	/* Perform phy reset */
@@ -336,6 +426,7 @@ static int qusb2_phy_init(struct phy *phy)
 		ret = -EBUSY;
 		goto disable_ref_clk;
 	}
+	qphy->phy_initialized = true;
 
 	return 0;
 
@@ -346,6 +437,8 @@ static int qusb2_phy_init(struct phy *phy)
 	reset_control_assert(qphy->phy_reset);
 disable_ahb_clk:
 	clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+	clk_disable_unprepare(qphy->iface_clk);
 poweroff_phy:
 	qusb2_phy_poweroff(qphy);
 
@@ -366,15 +459,19 @@ static int qusb2_phy_exit(struct phy *phy)
 	reset_control_assert(qphy->phy_reset);
 
 	clk_disable_unprepare(qphy->cfg_ahb_clk);
+	clk_disable_unprepare(qphy->iface_clk);
 
 	qusb2_phy_poweroff(qphy);
 
+	qphy->phy_initialized = false;
+
 	return 0;
 }
 
 static const struct phy_ops qusb2_phy_gen_ops = {
 	.init		= qusb2_phy_init,
 	.exit		= qusb2_phy_exit,
+	.set_mode	= qusb2_phy_set_mode,
 	.owner		= THIS_MODULE,
 };
 
@@ -387,6 +484,11 @@ static int qusb2_phy_exit(struct phy *phy)
 };
 MODULE_DEVICE_TABLE(of, qusb2_phy_of_match_table);
 
+static const struct dev_pm_ops qusb2_phy_pm_ops = {
+	SET_RUNTIME_PM_OPS(qusb2_phy_runtime_suspend,
+			   qusb2_phy_runtime_resume, NULL)
+};
+
 static int qusb2_phy_probe(struct platform_device *pdev)
 {
 	struct device *dev = &pdev->dev;
@@ -464,11 +566,14 @@ static int qusb2_phy_probe(struct platform_device *pdev)
 		qphy->cell = NULL;
 		dev_dbg(dev, "failed to lookup tune2 hstx trim value\n");
 	}
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
 
 	generic_phy = devm_phy_create(dev, NULL, &qusb2_phy_gen_ops);
 	if (IS_ERR(generic_phy)) {
 		ret = PTR_ERR(generic_phy);
 		dev_err(dev, "failed to create phy, %d\n", ret);
+		pm_runtime_disable(dev);
 		return ret;
 	}
 	qphy->phy = generic_phy;
@@ -479,6 +584,8 @@ static int qusb2_phy_probe(struct platform_device *pdev)
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 	if (!IS_ERR(phy_provider))
 		dev_info(dev, "Registered Qcom-QUSB2 phy\n");
+	else
+		pm_runtime_disable(dev);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
@@ -487,6 +594,7 @@ static int qusb2_phy_probe(struct platform_device *pdev)
 	.probe		= qusb2_phy_probe,
 	.driver = {
 		.name	= "qcom-qusb2-phy",
+		.pm	= &qusb2_phy_pm_ops,
 		.of_match_table = qusb2_phy_of_match_table,
 	},
 };
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
#8 lore raw

Re: [PATCH v1 4/6] phy: qcom-qusb2: Add support for runtime PM

Quoted 11 lines · Manu Gautam

Driver can turn off clocks during runtime suspend. Also, runtime suspend is not as a result of host mode selective suspend then PHY can be powered off as well.

drivers/phy/qualcomm/phy-qcom-qusb2.c +0 -0
index fa60a99..b505681 100644

drivers/phy/qualcomm/phy-qcom-qusb2.c struct qusb2_phy { · line 136

Quoted 5 lines
drivers/phy/qualcomm/phy-qcom-qusb2.c +2 -0
@@ -132,6 +132,9 @@ struct qusb2_phy {
 
 	const struct qusb2_phy_cfg *cfg;
 	bool has_se_clk_scheme;
+	bool phy_initialized;
+	bool powered_on;

Is the powered_on flag here because the controller driver has unbalanced power on calls to the phy? Same comment applies for the phy_initialized flag. Both of these look like workarounds for some odd behavior in the controller driver.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
#9 lore raw

Re: [PATCH v1 4/6] phy: qcom-qusb2: Add support for runtime PM

Hi,

Quoted 13 lines · Stephen Boyd
Quoted 12 lines · Manu Gautam

Driver can turn off clocks during runtime suspend. Also, runtime suspend is not as a result of host mode selective suspend then PHY can be powered off as well.

drivers/phy/qualcomm/phy-qcom-qusb2.c +0 -0
index fa60a99..b505681 100644
@@ -132,6 +132,9 @@ struct qusb2_phy {
 
Quoted 8 lines

drivers/phy/qualcomm/phy-qcom-qusb2.c struct qusb2_phy { · line 136

Quoted 4 lines
drivers/phy/qualcomm/phy-qcom-qusb2.c +2 -0
 	const struct qusb2_phy_cfg *cfg;
 	bool has_se_clk_scheme;
+	bool phy_initialized;
+	bool powered_on;

Is the powered_on flag here because the controller driver has unbalanced power on calls to the phy? Same comment applies for the phy_initialized flag. Both of these look like workarounds for some odd behavior in the controller driver.

phy_initialized flag is to make runtime_suspend/resume no-ops until PHY gets initialized.
powered_on flag is not related to any issue issue in core (as of now). I just added that to bail out early from phy power_on/off which is now called from phy_init/exit and runtime_suspend/resume as well.

#10 lore raw

PATCH 5/6 phy: qcom-qmp: Add support for runtime PM

In reply to Manu Gautam

Driver can turn off clocks during runtime suspend. Also, runtime suspend is not as a result of host mode selective suspend then PHY can be powered off as well.

drivers/phy/qualcomm/phy-qcom-qmp.c +87 -0
index aefb853..8394e24 100644
@@ -481,6 +481,8 @@ struct qcom_qmp {
 	int init_count;
 	bool power_enabled;
 	bool clk_enabled;
+	bool phy_initialized;
+	enum phy_mode mode;
 };
 
 static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@@ -832,6 +834,7 @@ static int qcom_qmp_phy_init(struct phy *phy)
 		dev_err(qmp->dev, "pipe_clk enable failed, err=%d\n", ret);
 		goto err_pcs_ready;
 	}
+	qmp->phy_initialized = true;
 
 	return 0;
 
@@ -874,6 +877,75 @@ static int qcom_qmp_phy_exit(struct phy *phy)
 
 	qcom_qmp_phy_poweroff(qmp);
 
+	qmp->phy_initialized = false;
+
+	return 0;
+}
+
+static int qcom_qmp_phy_set_mode(struct phy *phy, enum phy_mode mode)
+{
+	struct qmp_phy *qphy = phy_get_drvdata(phy);
+	struct qcom_qmp *qmp = qphy->qmp;
+
+	qmp->mode = mode;
+
+	return 0;
+}
+
+static int __maybe_unused qcom_qmp_phy_runtime_suspend(struct device *dev)
+{
+	struct qcom_qmp *qmp = dev_get_drvdata(dev);
+	struct qmp_phy *qphy = qmp->phys[0];
+	const struct qmp_phy_cfg *cfg = qmp->cfg;
+	void __iomem *pcs = qphy->pcs;
+
+	dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode);
+
+	if (!qmp->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	/* Power down PHY if not in session */
+	if (qmp->mode == PHY_MODE_INVALID)
+		qphy_clrbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
+
+	clk_disable_unprepare(qphy->pipe_clk);
+	qcom_qmp_phy_disable_clocks(qmp);
+
+	if (qmp->mode == PHY_MODE_INVALID)
+		qcom_qmp_phy_poweroff(qmp);
+
+	return 0;
+}
+
+static int __maybe_unused qcom_qmp_phy_runtime_resume(struct device *dev)
+{
+	struct qcom_qmp *qmp = dev_get_drvdata(dev);
+	struct qmp_phy *qphy = qmp->phys[0];
+	const struct qmp_phy_cfg *cfg = qmp->cfg;
+	void __iomem *pcs = qphy->pcs;
+	int ret = 0;
+
+	dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode);
+
+	if (!qmp->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	qcom_qmp_phy_poweron(qmp);
+
+	qcom_qmp_phy_enable_clocks(qmp);
+
+	ret = clk_prepare_enable(qphy->pipe_clk);
+	if (ret) {
+		dev_err(dev, "pipe_clk enable failed, err=%d\n", ret);
+		return ret;
+	}
+
+	qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
+
 	return 0;
 }
 
@@ -999,6 +1071,7 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, int id)
 static const struct phy_ops qcom_qmp_phy_gen_ops = {
 	.init		= qcom_qmp_phy_init,
 	.exit		= qcom_qmp_phy_exit,
+	.set_mode	= qcom_qmp_phy_set_mode,
 	.owner		= THIS_MODULE,
 };
 
@@ -1091,6 +1164,11 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
 };
 MODULE_DEVICE_TABLE(of, qcom_qmp_phy_of_match_table);
 
+static const struct dev_pm_ops qcom_qmp_phy_pm_ops = {
+	SET_RUNTIME_PM_OPS(qcom_qmp_phy_runtime_suspend,
+			   qcom_qmp_phy_runtime_resume, NULL)
+};
+
 static int qcom_qmp_phy_probe(struct platform_device *pdev)
 {
 	struct qcom_qmp *qmp;
@@ -1118,6 +1196,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 	qmp->serdes = base;
 
 	mutex_init(&qmp->phy_mutex);
+	qmp->mode = PHY_MODE_INVALID;
 
 	/* Get the specific init parameters of QMP phy */
 	qmp->cfg = of_device_get_match_data(dev);
@@ -1146,12 +1225,16 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	id = 0;
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
 	for_each_available_child_of_node(dev->of_node, child) {
 		/* Create per-lane phy */
 		ret = qcom_qmp_phy_create(dev, child, id);
 		if (ret) {
 			dev_err(dev, "failed to create lane%d phy, %d\n",
 				id, ret);
+			pm_runtime_disable(dev);
 			return ret;
 		}
 
@@ -1163,6 +1246,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 		if (ret) {
 			dev_err(qmp->dev,
 				"failed to register pipe clock source\n");
+			pm_runtime_disable(dev);
 			return ret;
 		}
 		id++;
@@ -1171,6 +1255,8 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 	if (!IS_ERR(phy_provider))
 		dev_info(dev, "Registered Qcom-QMP phy\n");
+	else
+		pm_runtime_disable(dev);
 
 	return PTR_ERR_OR_ZERO(phy_provider);
 }
@@ -1179,6 +1265,7 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev)
 	.probe		= qcom_qmp_phy_probe,
 	.driver = {
 		.name	= "qcom-qmp-phy",
+		.pm	= &qcom_qmp_phy_pm_ops,
 		.of_match_table = qcom_qmp_phy_of_match_table,
 	},
 };
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
#11 lore raw

PATCH 6/6 usb: dwc3: core: Notify USB3 PHY as well for DRD modes

In reply to Manu Gautam

Driver currently notifies only USB3 PHY for mode change. Extend this to USB3 PHY so that driver based on the mode can release system resources - clocks, regulators etc. and can even turn off PHY during runtime suspend.

drivers/usb/dwc3/core.c +8 -9
index 326b302..1f6c51e 100644
@@ -156,9 +156,8 @@ static void __dwc3_set_mode(struct work_struct *work)
 		} else {
 			if (dwc->usb2_phy)
 				otg_set_vbus(dwc->usb2_phy->otg, true);
-			if (dwc->usb2_generic_phy)
-				phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
-
+			phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+			phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
 		}
 		break;
 	case DWC3_GCTL_PRTCAP_DEVICE:
@@ -166,8 +165,8 @@ static void __dwc3_set_mode(struct work_struct *work)
 
 		if (dwc->usb2_phy)
 			otg_set_vbus(dwc->usb2_phy->otg, false);
-		if (dwc->usb2_generic_phy)
-			phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+		phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+		phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
 
 		ret = dwc3_gadget_init(dwc);
 		if (ret)
@@ -931,8 +930,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 
 		if (dwc->usb2_phy)
 			otg_set_vbus(dwc->usb2_phy->otg, false);
-		if (dwc->usb2_generic_phy)
-			phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+		phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_DEVICE);
+		phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_DEVICE);
 
 		ret = dwc3_gadget_init(dwc);
 		if (ret) {
@@ -946,8 +945,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc)
 
 		if (dwc->usb2_phy)
 			otg_set_vbus(dwc->usb2_phy->otg, true);
-		if (dwc->usb2_generic_phy)
-			phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+		phy_set_mode(dwc->usb2_generic_phy, PHY_MODE_USB_HOST);
+		phy_set_mode(dwc->usb3_generic_phy, PHY_MODE_USB_HOST);
 
 		ret = dwc3_host_init(dwc);
 		if (ret) {
Signature · 1 line
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,\na Linux Foundation Collaborative Project
#12 lore raw

Re: [PATCH v1 6/6] usb: dwc3: core: Notify USB3 PHY as well for DRD modes

Quoted 6 lines · Manu Gautam

Driver currently notifies only USB3 PHY for mode change. Extend this to USB3 PHY so that driver based on the mode can release system resources - clocks, regulators etc. and can even turn off PHY during runtime suspend.

This patch is confusing because you say "notify for usb3 phy", but then combine a change to remove the NULL pointer check on phy_set_mode() for the usb2 phy. Please describe everything that's happening in a patch in the commit text.

Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
#13 lore raw

Re: [PATCH v1 6/6] usb: dwc3: core: Notify USB3 PHY as well for DRD modes

Quoted 11 lines · Stephen Boyd
Quoted 6 lines · Manu Gautam

Driver currently notifies only USB3 PHY for mode change. Extend this to USB3 PHY so that driver based on the mode can release system resources - clocks, regulators etc. and can even turn off PHY during runtime suspend.

This patch is confusing because you say "notify for usb3 phy", but then combine a change to remove the NULL pointer check on phy_set_mode() for the usb2 phy. Please describe everything that's happening in a patch in the commit text.

Sure, will add a comment for that.
It was done to align with other phy_ops usage in the driver. Also, these wrapper functions already have a NULL pointer check.

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html