All of lore.kernel.org
 help / color / mirror / Atom feed
From: Manu Gautam <mgautam@codeaurora.org>
To: Kishon Vijay Abraham I <kishon@ti.com>
Cc: linux-arm-msm@vger.kernel.org, linux-usb@vger.kernel.org,
	Manu Gautam <mgautam@codeaurora.org>,
	Vivek Gautam <vivek.gautam@codeaurora.org>,
	Heiko Stuebner <heiko@sntech.de>,
	Jaehoon Chung <jh80.chung@samsung.com>,
	"open list:GENERIC PHY FRAMEWORK" <linux-kernel@vger.kernel.org>
Subject: [PATCH v3 15/16] phy: qcom-qusb2: Add support for runtime PM
Date: Tue, 21 Nov 2017 14:53:25 +0530	[thread overview]
Message-ID: <1511256206-1587-16-git-send-email-mgautam@codeaurora.org> (raw)
In-Reply-To: <1511256206-1587-1-git-send-email-mgautam@codeaurora.org>

Disable clocks and enable DP/DM wakeup interrupts when
suspending PHY.
Core driver should notify speed to PHY driver to enable
appropriate DP/DM wakeup interrupts polarity in suspend state.

Signed-off-by: Manu Gautam <mgautam@codeaurora.org>
---
 drivers/phy/qualcomm/phy-qcom-qusb2.c | 181 ++++++++++++++++++++++++++++++++++
 1 file changed, 181 insertions(+)

diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index bda1f4c..84cce0e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -56,6 +56,18 @@
 
 #define PHY_CLK_SCHEME_SEL		BIT(0)
 
+/* QUSB2PHY_INTR_CTRL register bits */
+#define DMSE_INTR_HIGH_SEL			BIT(4)
+#define DPSE_INTR_HIGH_SEL			BIT(3)
+#define CHG_DET_INTR_EN				BIT(2)
+#define DMSE_INTR_EN				BIT(1)
+#define DPSE_INTR_EN				BIT(0)
+
+/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */
+#define CORE_PLL_EN_FROM_RESET			BIT(4)
+#define CORE_RESET				BIT(5)
+#define CORE_RESET_MUX				BIT(6)
+
 #define	QUSB2PHY_PLL_ANALOG_CONTROLS_TWO	0x04
 #define	QUSB2PHY_PLL_CLOCK_INVERTERS		0x18c
 #define	QUSB2PHY_PLL_CMODE			0x2c
@@ -93,6 +105,7 @@ struct qusb2_phy_init_tbl {
 
 /* set of registers with offsets different per-PHY */
 enum qusb2phy_reg_layout {
+	QUSB2PHY_PLL_CORE_INPUT_OVERRIDE,
 	QUSB2PHY_PLL_STATUS,
 	QUSB2PHY_PORT_TUNE1,
 	QUSB2PHY_PORT_TUNE2,
@@ -112,8 +125,10 @@ enum qusb2phy_reg_layout {
 	[QUSB2PHY_PORT_TUNE3]		= 0x88,
 	[QUSB2PHY_PORT_TUNE4]		= 0x8c,
 	[QUSB2PHY_PORT_TUNE5]		= 0x90,
+	[QUSB2PHY_PORT_TEST1]		= 0xb8,
 	[QUSB2PHY_PORT_TEST2]		= 0x9c,
 	[QUSB2PHY_PORT_POWERDOWN]	= 0xb4,
+	[QUSB2PHY_INTR_CTRL]		= 0xbc,
 };
 
 static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
@@ -133,14 +148,17 @@ enum qusb2phy_reg_layout {
 };
 
 static const unsigned int qusb2_v2_regs_layout[] = {
+	[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
 	[QUSB2PHY_PLL_STATUS]		= 0x1a0,
 	[QUSB2PHY_PORT_TUNE1]		= 0x240,
 	[QUSB2PHY_PORT_TUNE2]		= 0x244,
 	[QUSB2PHY_PORT_TUNE3]		= 0x248,
 	[QUSB2PHY_PORT_TUNE4]		= 0x24c,
 	[QUSB2PHY_PORT_TUNE5]		= 0x250,
+	[QUSB2PHY_PORT_TEST1]		= 0x254,
 	[QUSB2PHY_PORT_TEST2]		= 0x258,
 	[QUSB2PHY_PORT_POWERDOWN]	= 0x210,
+	[QUSB2PHY_INTR_CTRL]		= 0x230,
 };
 
 static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
@@ -175,9 +193,13 @@ struct qusb2_phy_cfg {
 	const unsigned int *regs;
 	unsigned int mask_core_ready;
 	unsigned int disable_ctrl;
+	unsigned int autoresume_en;
 
 	/* true if PHY has PLL_TEST register to select clk_scheme */
 	bool has_pll_test;
+
+	/* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */
+	bool has_pll_override;
 };
 
 static const struct qusb2_phy_cfg msm8996_phy_cfg = {
@@ -188,6 +210,7 @@ struct qusb2_phy_cfg {
 	.has_pll_test	= true,
 	.disable_ctrl	= (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
 	.mask_core_ready = PLL_LOCKED,
+	.autoresume_en	 = BIT(3),
 };
 
 static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
@@ -198,6 +221,8 @@ struct qusb2_phy_cfg {
 	.disable_ctrl	= (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
 			   POWER_DOWN),
 	.mask_core_ready = CORE_READY_STATUS,
+	.has_pll_override = true,
+	.autoresume_en	  = BIT(0),
 };
 
 static const char * const qusb2_phy_vreg_names[] = {
@@ -223,6 +248,8 @@ struct qusb2_phy_cfg {
  *
  * @cfg: phy config data
  * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
+ * @phy_initialized: indicate if PHY has been initialized
+ * @speed: current PHY speed
  */
 struct qusb2_phy {
 	struct phy *phy;
@@ -239,6 +266,8 @@ struct qusb2_phy {
 
 	const struct qusb2_phy_cfg *cfg;
 	bool has_se_clk_scheme;
+	bool phy_initialized;
+	enum phy_speed speed;
 };
 
 static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
@@ -307,6 +336,137 @@ 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_notify_speed(struct phy *phy, enum phy_speed speed)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	qphy->speed = speed;
+
+	return 0;
+}
+
+static enum phy_speed qusb2_phy_get_speed(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	return qphy->speed;
+}
+
+static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
+	u32 intr_mask;
+
+	dev_vdbg(dev, "Suspending QUSB2 Phy, speed:%d\n", qphy->speed);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	/*
+	 * Enable DP/DM interrupts to detect line state changes based on current
+	 * speed. In other words, enable the triggers _opposite_ of what the
+	 * current D+/D- levels are e.g. if currently D+ high, D- low
+	 * (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high
+	 */
+	intr_mask = DPSE_INTR_EN | DMSE_INTR_EN;
+	switch (qphy->speed) {
+	case PHY_SPEED_USB_HS:
+	case PHY_SPEED_USB_FS:
+		intr_mask |= DMSE_INTR_HIGH_SEL;
+		break;
+	case PHY_SPEED_USB_LS:
+		intr_mask |= DPSE_INTR_HIGH_SEL;
+		break;
+	default:
+		/* No device connected, enable both DP/DM high interrupt */
+		intr_mask |= DMSE_INTR_HIGH_SEL;
+		intr_mask |= DPSE_INTR_HIGH_SEL;
+		break;
+	}
+
+	writel(intr_mask, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
+
+	/* hold core PLL into reset */
+	if (cfg->has_pll_override) {
+		qusb2_setbits(qphy->base,
+			      cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+			      CORE_PLL_EN_FROM_RESET | CORE_RESET |
+			      CORE_RESET_MUX);
+	}
+
+	/* enable phy auto-resume only if device is connected on bus */
+	if (qphy->speed != PHY_SPEED_UNKNOWN) {
+		qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+			      cfg->autoresume_en);
+		/* Autoresume bit has to be toggled in order to enable it */
+		qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+			      cfg->autoresume_en);
+	}
+
+	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);
+
+	return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
+	int ret;
+
+	dev_vdbg(dev, "Resuming QUSB2 phy, speed:%d\n", qphy->speed);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	ret = clk_prepare_enable(qphy->iface_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable iface_clk, %d\n", ret);
+		return ret;
+	}
+
+	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;
+		}
+	}
+
+	writel(0x0, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
+
+	/* bring core PLL out of reset */
+	if (cfg->has_pll_override) {
+		qusb2_clrbits(qphy->base,
+			      cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+			      CORE_RESET | CORE_RESET_MUX);
+	}
+
+	return 0;
+
+disable_ahb_clk:
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+	clk_disable_unprepare(qphy->iface_clk);
+
+	return ret;
+}
+
 static int qusb2_phy_init(struct phy *phy)
 {
 	struct qusb2_phy *qphy = phy_get_drvdata(phy);
@@ -431,6 +591,7 @@ static int qusb2_phy_init(struct phy *phy)
 		ret = -EBUSY;
 		goto disable_ref_clk;
 	}
+	qphy->phy_initialized = true;
 
 	return 0;
 
@@ -467,12 +628,16 @@ static int qusb2_phy_exit(struct phy *phy)
 
 	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
 
+	qphy->phy_initialized = false;
+
 	return 0;
 }
 
 static const struct phy_ops qusb2_phy_gen_ops = {
 	.init		= qusb2_phy_init,
 	.exit		= qusb2_phy_exit,
+	.notify_speed	= qusb2_phy_notify_speed,
+	.get_speed	= qusb2_phy_get_speed,
 	.owner		= THIS_MODULE,
 };
 
@@ -488,6 +653,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;
@@ -565,11 +735,19 @@ 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);
+	/*
+	 * Prevent runtime pm from being ON by default. Users can enable
+	 * it using power/control in sysfs.
+	 */
+	pm_runtime_forbid(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;
@@ -580,6 +758,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);
 }
@@ -588,6 +768,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,
 	},
 };
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

WARNING: multiple messages have this Message-ID (diff)
From: Manu Gautam <mgautam@codeaurora.org>
To: Kishon Vijay Abraham I <kishon@ti.com>
Cc: linux-arm-msm@vger.kernel.org, linux-usb@vger.kernel.org,
	Manu Gautam <mgautam@codeaurora.org>,
	Vivek Gautam <vivek.gautam@codeaurora.org>,
	Heiko Stuebner <heiko@sntech.de>,
	Jaehoon Chung <jh80.chung@samsung.com>,
	linux-kernel@vger.kernel.org (open list:GENERIC PHY FRAMEWORK)
Subject: [PATCH v3 15/16] phy: qcom-qusb2: Add support for runtime PM
Date: Tue, 21 Nov 2017 14:53:25 +0530	[thread overview]
Message-ID: <1511256206-1587-16-git-send-email-mgautam@codeaurora.org> (raw)
In-Reply-To: <1511256206-1587-1-git-send-email-mgautam@codeaurora.org>

Disable clocks and enable DP/DM wakeup interrupts when
suspending PHY.
Core driver should notify speed to PHY driver to enable
appropriate DP/DM wakeup interrupts polarity in suspend state.

Signed-off-by: Manu Gautam <mgautam@codeaurora.org>
---
 drivers/phy/qualcomm/phy-qcom-qusb2.c | 181 ++++++++++++++++++++++++++++++++++
 1 file changed, 181 insertions(+)

diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index bda1f4c..84cce0e 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -56,6 +56,18 @@
 
 #define PHY_CLK_SCHEME_SEL		BIT(0)
 
+/* QUSB2PHY_INTR_CTRL register bits */
+#define DMSE_INTR_HIGH_SEL			BIT(4)
+#define DPSE_INTR_HIGH_SEL			BIT(3)
+#define CHG_DET_INTR_EN				BIT(2)
+#define DMSE_INTR_EN				BIT(1)
+#define DPSE_INTR_EN				BIT(0)
+
+/* QUSB2PHY_PLL_CORE_INPUT_OVERRIDE register bits */
+#define CORE_PLL_EN_FROM_RESET			BIT(4)
+#define CORE_RESET				BIT(5)
+#define CORE_RESET_MUX				BIT(6)
+
 #define	QUSB2PHY_PLL_ANALOG_CONTROLS_TWO	0x04
 #define	QUSB2PHY_PLL_CLOCK_INVERTERS		0x18c
 #define	QUSB2PHY_PLL_CMODE			0x2c
@@ -93,6 +105,7 @@ struct qusb2_phy_init_tbl {
 
 /* set of registers with offsets different per-PHY */
 enum qusb2phy_reg_layout {
+	QUSB2PHY_PLL_CORE_INPUT_OVERRIDE,
 	QUSB2PHY_PLL_STATUS,
 	QUSB2PHY_PORT_TUNE1,
 	QUSB2PHY_PORT_TUNE2,
@@ -112,8 +125,10 @@ enum qusb2phy_reg_layout {
 	[QUSB2PHY_PORT_TUNE3]		= 0x88,
 	[QUSB2PHY_PORT_TUNE4]		= 0x8c,
 	[QUSB2PHY_PORT_TUNE5]		= 0x90,
+	[QUSB2PHY_PORT_TEST1]		= 0xb8,
 	[QUSB2PHY_PORT_TEST2]		= 0x9c,
 	[QUSB2PHY_PORT_POWERDOWN]	= 0xb4,
+	[QUSB2PHY_INTR_CTRL]		= 0xbc,
 };
 
 static const struct qusb2_phy_init_tbl msm8996_init_tbl[] = {
@@ -133,14 +148,17 @@ enum qusb2phy_reg_layout {
 };
 
 static const unsigned int qusb2_v2_regs_layout[] = {
+	[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
 	[QUSB2PHY_PLL_STATUS]		= 0x1a0,
 	[QUSB2PHY_PORT_TUNE1]		= 0x240,
 	[QUSB2PHY_PORT_TUNE2]		= 0x244,
 	[QUSB2PHY_PORT_TUNE3]		= 0x248,
 	[QUSB2PHY_PORT_TUNE4]		= 0x24c,
 	[QUSB2PHY_PORT_TUNE5]		= 0x250,
+	[QUSB2PHY_PORT_TEST1]		= 0x254,
 	[QUSB2PHY_PORT_TEST2]		= 0x258,
 	[QUSB2PHY_PORT_POWERDOWN]	= 0x210,
+	[QUSB2PHY_INTR_CTRL]		= 0x230,
 };
 
 static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
@@ -175,9 +193,13 @@ struct qusb2_phy_cfg {
 	const unsigned int *regs;
 	unsigned int mask_core_ready;
 	unsigned int disable_ctrl;
+	unsigned int autoresume_en;
 
 	/* true if PHY has PLL_TEST register to select clk_scheme */
 	bool has_pll_test;
+
+	/* true if PHY has PLL_CORE_INPUT_OVERRIDE register to reset PLL */
+	bool has_pll_override;
 };
 
 static const struct qusb2_phy_cfg msm8996_phy_cfg = {
@@ -188,6 +210,7 @@ struct qusb2_phy_cfg {
 	.has_pll_test	= true,
 	.disable_ctrl	= (CLAMP_N_EN | FREEZIO_N | POWER_DOWN),
 	.mask_core_ready = PLL_LOCKED,
+	.autoresume_en	 = BIT(3),
 };
 
 static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
@@ -198,6 +221,8 @@ struct qusb2_phy_cfg {
 	.disable_ctrl	= (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
 			   POWER_DOWN),
 	.mask_core_ready = CORE_READY_STATUS,
+	.has_pll_override = true,
+	.autoresume_en	  = BIT(0),
 };
 
 static const char * const qusb2_phy_vreg_names[] = {
@@ -223,6 +248,8 @@ struct qusb2_phy_cfg {
  *
  * @cfg: phy config data
  * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
+ * @phy_initialized: indicate if PHY has been initialized
+ * @speed: current PHY speed
  */
 struct qusb2_phy {
 	struct phy *phy;
@@ -239,6 +266,8 @@ struct qusb2_phy {
 
 	const struct qusb2_phy_cfg *cfg;
 	bool has_se_clk_scheme;
+	bool phy_initialized;
+	enum phy_speed speed;
 };
 
 static inline void qusb2_setbits(void __iomem *base, u32 offset, u32 val)
@@ -307,6 +336,137 @@ 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_notify_speed(struct phy *phy, enum phy_speed speed)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	qphy->speed = speed;
+
+	return 0;
+}
+
+static enum phy_speed qusb2_phy_get_speed(struct phy *phy)
+{
+	struct qusb2_phy *qphy = phy_get_drvdata(phy);
+
+	return qphy->speed;
+}
+
+static int __maybe_unused qusb2_phy_runtime_suspend(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
+	u32 intr_mask;
+
+	dev_vdbg(dev, "Suspending QUSB2 Phy, speed:%d\n", qphy->speed);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	/*
+	 * Enable DP/DM interrupts to detect line state changes based on current
+	 * speed. In other words, enable the triggers _opposite_ of what the
+	 * current D+/D- levels are e.g. if currently D+ high, D- low
+	 * (HS 'J'/Suspend), configure the mask to trigger on D+ low OR D- high
+	 */
+	intr_mask = DPSE_INTR_EN | DMSE_INTR_EN;
+	switch (qphy->speed) {
+	case PHY_SPEED_USB_HS:
+	case PHY_SPEED_USB_FS:
+		intr_mask |= DMSE_INTR_HIGH_SEL;
+		break;
+	case PHY_SPEED_USB_LS:
+		intr_mask |= DPSE_INTR_HIGH_SEL;
+		break;
+	default:
+		/* No device connected, enable both DP/DM high interrupt */
+		intr_mask |= DMSE_INTR_HIGH_SEL;
+		intr_mask |= DPSE_INTR_HIGH_SEL;
+		break;
+	}
+
+	writel(intr_mask, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
+
+	/* hold core PLL into reset */
+	if (cfg->has_pll_override) {
+		qusb2_setbits(qphy->base,
+			      cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+			      CORE_PLL_EN_FROM_RESET | CORE_RESET |
+			      CORE_RESET_MUX);
+	}
+
+	/* enable phy auto-resume only if device is connected on bus */
+	if (qphy->speed != PHY_SPEED_UNKNOWN) {
+		qusb2_setbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+			      cfg->autoresume_en);
+		/* Autoresume bit has to be toggled in order to enable it */
+		qusb2_clrbits(qphy->base, cfg->regs[QUSB2PHY_PORT_TEST1],
+			      cfg->autoresume_en);
+	}
+
+	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);
+
+	return 0;
+}
+
+static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
+{
+	struct qusb2_phy *qphy = dev_get_drvdata(dev);
+	const struct qusb2_phy_cfg *cfg = qphy->cfg;
+	int ret;
+
+	dev_vdbg(dev, "Resuming QUSB2 phy, speed:%d\n", qphy->speed);
+
+	if (!qphy->phy_initialized) {
+		dev_vdbg(dev, "PHY not initialized, bailing out\n");
+		return 0;
+	}
+
+	ret = clk_prepare_enable(qphy->iface_clk);
+	if (ret) {
+		dev_err(dev, "failed to enable iface_clk, %d\n", ret);
+		return ret;
+	}
+
+	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;
+		}
+	}
+
+	writel(0x0, qphy->base + cfg->regs[QUSB2PHY_INTR_CTRL]);
+
+	/* bring core PLL out of reset */
+	if (cfg->has_pll_override) {
+		qusb2_clrbits(qphy->base,
+			      cfg->regs[QUSB2PHY_PLL_CORE_INPUT_OVERRIDE],
+			      CORE_RESET | CORE_RESET_MUX);
+	}
+
+	return 0;
+
+disable_ahb_clk:
+	clk_disable_unprepare(qphy->cfg_ahb_clk);
+disable_iface_clk:
+	clk_disable_unprepare(qphy->iface_clk);
+
+	return ret;
+}
+
 static int qusb2_phy_init(struct phy *phy)
 {
 	struct qusb2_phy *qphy = phy_get_drvdata(phy);
@@ -431,6 +591,7 @@ static int qusb2_phy_init(struct phy *phy)
 		ret = -EBUSY;
 		goto disable_ref_clk;
 	}
+	qphy->phy_initialized = true;
 
 	return 0;
 
@@ -467,12 +628,16 @@ static int qusb2_phy_exit(struct phy *phy)
 
 	regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
 
+	qphy->phy_initialized = false;
+
 	return 0;
 }
 
 static const struct phy_ops qusb2_phy_gen_ops = {
 	.init		= qusb2_phy_init,
 	.exit		= qusb2_phy_exit,
+	.notify_speed	= qusb2_phy_notify_speed,
+	.get_speed	= qusb2_phy_get_speed,
 	.owner		= THIS_MODULE,
 };
 
@@ -488,6 +653,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;
@@ -565,11 +735,19 @@ 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);
+	/*
+	 * Prevent runtime pm from being ON by default. Users can enable
+	 * it using power/control in sysfs.
+	 */
+	pm_runtime_forbid(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;
@@ -580,6 +758,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);
 }
@@ -588,6 +768,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,
 	},
 };
-- 
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

  parent reply	other threads:[~2017-11-21  9:23 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-11-21  9:23 [PATCH v3 00/16] Support for Qualcomm QUSBv2 and QMPv3 USB PHYs Manu Gautam
2017-11-21  9:23 ` [PATCH v3 01/16] phy: qcom-qmp: Fix phy pipe clock gating Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 02/16] phy: qcom-qmp: Adapt to clk_bulk_* APIs Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-12-20  2:37   ` Vivek Gautam
     [not found]     ` <CAFp+6iEgiv0KJo+ikR_jyakc2dGjetiArJSvC4d5JFO0ZnO7-Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2017-12-20  5:03       ` Manu Gautam
2017-12-20  5:03         ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 03/16] phy: qcom-qmp: Power-on PHY before initialization Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-22 18:03   ` Stephen Boyd
2017-11-23  3:59     ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 04/16] phy: qcom-qusb2: " Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 05/16] phy: qcom-qmp: Fix PHY block reset sequence Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 06/16] phy: qcom-qmp: Move SERDES/PCS START after PHY reset Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 07/16] phy: qcom-qusb2: Add support for different register layouts Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-12-05 10:23   ` Vivek Gautam
     [not found]     ` <3c0dca03-bf9e-8747-8c31-546172f88d6f-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2017-12-12  8:51       ` Manu Gautam
2017-12-12  8:51         ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 08/16] dt-bindings: phy-qcom-qusb2: Update binding for QUSB2 V2 version Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 09/16] phy: qcom-qusb2: Add support " Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 10/16] phy: qcom-qmp: Move register offsets to header file Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-22 17:26   ` Stephen Boyd
2017-11-23  4:02     ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 11/16] phy: qcom-qmp: Add register offsets for QMP V3 PHY Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 12/16] dt-bindings: phy-qcom-qmp: Update bindings for QMP V3 USB PHY Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 13/16] phy: qcom-qmp: Add support for QMP V3 USB3 PHY Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-11-21  9:23 ` [PATCH v3 14/16] phy: Add notify_speed callback Manu Gautam
2017-11-21  9:23   ` Manu Gautam
2017-12-12 11:43   ` Kishon Vijay Abraham I
2017-12-12 11:43     ` Kishon Vijay Abraham I
2017-12-12 15:24     ` Manu Gautam
2017-12-20  5:49       ` Kishon Vijay Abraham I
2017-12-20  5:49         ` Kishon Vijay Abraham I
2017-12-20  6:29         ` Manu Gautam
2017-12-20  7:17           ` Kishon Vijay Abraham I
2017-12-20  7:17             ` Kishon Vijay Abraham I
2017-12-20  8:41             ` Manu Gautam
2017-12-20  8:41               ` [v3,14/16] " Manu Gautam
2017-12-28 11:04               ` [PATCH v3 14/16] " Kishon Vijay Abraham I
2017-12-28 11:04                 ` Kishon Vijay Abraham I
2017-12-28 11:04                 ` [v3,14/16] " Kishon Vijay Abraham I
2017-12-29  4:24                 ` [PATCH v3 14/16] " Manu Gautam
2017-12-29  4:24                   ` [v3,14/16] " Manu Gautam
2017-12-29  6:28                   ` [PATCH v3 14/16] " Kishon Vijay Abraham I
2017-12-29  6:28                     ` Kishon Vijay Abraham I
2017-12-29  6:28                     ` [v3,14/16] " Kishon Vijay Abraham I
2018-01-02  5:42                     ` [PATCH v3 14/16] " Manu Gautam
2018-01-02  5:42                       ` [v3,14/16] " Manu Gautam
2017-11-21  9:23 ` Manu Gautam [this message]
2017-11-21  9:23   ` [PATCH v3 15/16] phy: qcom-qusb2: Add support for runtime PM Manu Gautam
2017-11-21  9:23 ` [PATCH v3 16/16] phy: qcom-qmp: " Manu Gautam
2017-11-21  9:23   ` Manu Gautam

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1511256206-1587-16-git-send-email-mgautam@codeaurora.org \
    --to=mgautam@codeaurora.org \
    --cc=heiko@sntech.de \
    --cc=jh80.chung@samsung.com \
    --cc=kishon@ti.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=vivek.gautam@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.