Received: by 10.223.164.202 with SMTP id h10csp4904857wrb; Tue, 21 Nov 2017 01:26:44 -0800 (PST) X-Google-Smtp-Source: AGs4zMa/LtYq9BU+L2Upo5Vnv0e7gSPyvlJgn359vxG/rV7ReQoyGelmLXf2a+ozHAGqu5xGlLEG X-Received: by 10.98.79.152 with SMTP id f24mr14814800pfj.143.1511256404512; Tue, 21 Nov 2017 01:26:44 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1511256404; cv=none; d=google.com; s=arc-20160816; b=s+wC/VayP1N6609lGybw+MPAobKhHl4xIyV/aAq6z98fLw8nCDRy/GlbBgd2GEkc5o BIZLsbAe3+NMqkobhcVBpL9F2eybKDhgivQa+Pm69Kc2CCg7IJkVl3njxVpT0UHArCME qNR70U7UdJmweFieYXXYn2mPKVokGuDlGMf0BFK+Xg6S4gjE5RGruuLMEpVIy/lQt1Ud zWB9pEGyQ57BGhb+jjQLsr6RjOM2EUFmyqFGV74GrEFxr5QUileF2eDgnyOmAQWJpMmy XMaKAYPezor6n2Obsm5Cg7h7iiL5lZUshcwD9mlpycpO+VO8apltlXZ33K94NQjSkcQH T4rA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:references:in-reply-to:message-id:date :subject:cc:to:from:dmarc-filter:dkim-signature:dkim-signature :arc-authentication-results; bh=48J1nZExYSi+TeTDUxZ/77ILXA+ZcT84ka+dPE+sclU=; b=T3I+MdXb7ac4Xs1wqlQKhCy282lp3rL/zlCcBysLH5mOk95rPxCBdcaS4N/VV8zrzJ 8ohn/ngv38UMejpghIGFxTzM+OzGt8jx6QVM9kjZ5Q71SCZ3XhPk7Kl5BCmkrZjJoiu0 CGDjFxrcj4+hHLqIxvcqDcz/dkbuFVyL2nvofT5NFivMnR8kQDHpqRTcHK2/Gp4eIZed kA412ALPJXitm6+e0ucZN8V/lLSNEBGFKXqumCwcOrmGJ0e2KPK0osHAvsTY75PxsTg3 zx0A+f7jad7HfpIAWP1H0/P5M8AB1OaO55d8HQbTQ49uZJj5+ImnOcjNhaESJRmmzXfM j1Aw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=DvmK0Qp9; dkim=pass header.i=@codeaurora.org header.s=default header.b=jKGfuR00; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id m11si10544767pln.195.2017.11.21.01.26.33; Tue, 21 Nov 2017 01:26:44 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@codeaurora.org header.s=default header.b=DvmK0Qp9; dkim=pass header.i=@codeaurora.org header.s=default header.b=jKGfuR00; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752046AbdKUJZl (ORCPT + 72 others); Tue, 21 Nov 2017 04:25:41 -0500 Received: from smtp.codeaurora.org ([198.145.29.96]:42098 "EHLO smtp.codeaurora.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751232AbdKUJZg (ORCPT ); Tue, 21 Nov 2017 04:25:36 -0500 Received: by smtp.codeaurora.org (Postfix, from userid 1000) id 2EA93606F8; Tue, 21 Nov 2017 09:25:36 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1511256336; bh=4CnLVRjegbbMPYNs/cQYAwsJXdWbmqXKIYlvhvkTJM8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=DvmK0Qp9Z6Zdk0qcSnOLgsZ7yyUfWEW6E/wcB4rjwYGPEMG92X6NWoR7EPegioQVi ErXFHU4UxpTzgvJWVuqTqNxvkc8oWhZf0eL5cuhhzK6RTcfo0CgocPRetvYT5Gfsec RzgQKrBlTeoQ9Jvk74Hyhsyolp67XHB7gU7NZ0MU= X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on pdx-caf-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.8 required=2.0 tests=ALL_TRUSTED,BAYES_00, DKIM_SIGNED,T_DKIM_INVALID autolearn=no autolearn_force=no version=3.4.0 Received: from mgautam-linux.qualcomm.com (blr-c-bdr-fw-01_globalnat_allzones-outside.qualcomm.com [103.229.19.19]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: mgautam@smtp.codeaurora.org) by smtp.codeaurora.org (Postfix) with ESMTPSA id 77EFE6024A; Tue, 21 Nov 2017 09:25:32 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=codeaurora.org; s=default; t=1511256335; bh=4CnLVRjegbbMPYNs/cQYAwsJXdWbmqXKIYlvhvkTJM8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=jKGfuR00DGp3JBTOQwhN/jQasEnVyxvzGZ4GzC84MSUPkmFZ6EwoBQqOwjMqPkuMR 2xx/C1zA1HVl7SBsI/XR5cmBvz/S/pXc2omspmPfTmbAQ3xAACbifDS8LyRiM9lOC8 DmWhK9f6Zp6kJOE16iD79j8iZDYrVG7m7k+MTWjE= DMARC-Filter: OpenDMARC Filter v1.3.2 smtp.codeaurora.org 77EFE6024A Authentication-Results: pdx-caf-mail.web.codeaurora.org; dmarc=none (p=none dis=none) header.from=codeaurora.org Authentication-Results: pdx-caf-mail.web.codeaurora.org; spf=none smtp.mailfrom=mgautam@codeaurora.org From: Manu Gautam To: Kishon Vijay Abraham I Cc: linux-arm-msm@vger.kernel.org, linux-usb@vger.kernel.org, Manu Gautam , Vivek Gautam , Varadarajan Narayanan , Krzysztof Kozlowski , Wei Yongjun , Fengguang Wu , linux-kernel@vger.kernel.org (open list:GENERIC PHY FRAMEWORK) Subject: [PATCH v3 16/16] phy: qcom-qmp: Add support for runtime PM Date: Tue, 21 Nov 2017 14:53:26 +0530 Message-Id: <1511256206-1587-17-git-send-email-mgautam@codeaurora.org> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1511256206-1587-1-git-send-email-mgautam@codeaurora.org> References: <1511256206-1587-1-git-send-email-mgautam@codeaurora.org> Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Disable clocks and enable PHY autonomous mode to detect wakeup events when PHY is suspended. Core driver should notify speed to PHY driver to enable LFPS and/or RX_DET interrupts. Signed-off-by: Manu Gautam --- drivers/phy/qualcomm/phy-qcom-qmp.c | 185 +++++++++++++++++++++++++++++++++++- drivers/phy/qualcomm/phy-qcom-qmp.h | 3 + 2 files changed, 187 insertions(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index efd187b..9553741 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -61,6 +61,19 @@ #define USB3_MODE BIT(0) /* enables USB3 mode */ #define DP_MODE BIT(1) /* enables DP mode */ +/* QPHY_PCS_AUTONOMOUS_MODE_CTRL register bits */ +#define ARCVR_DTCT_EN BIT(0) +#define ALFPS_DTCT_EN BIT(1) +#define ARCVR_DTCT_EVENT_SEL BIT(4) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR register bits */ +#define IRQ_CLEAR BIT(0) + +/* QPHY_PCS_LFPS_RXTERM_IRQ_STATUS register bits */ +#define RCVR_DETECT BIT(0) + +/* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ +#define CLAMP_EN BIT(0) /* enables i/o clamp_n */ #define PHY_INIT_COMPLETE_TIMEOUT 1000 #define POWER_DOWN_DELAY_US_MIN 10 @@ -108,6 +121,9 @@ enum qphy_reg_layout { QPHY_SW_RESET, QPHY_START_CTRL, QPHY_PCS_READY_STATUS, + QPHY_PCS_AUTONOMOUS_MODE_CTRL, + QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR, + QPHY_PCS_LFPS_RXTERM_IRQ_STATUS, }; static const unsigned int pciephy_regs_layout[] = { @@ -135,12 +151,18 @@ enum qphy_reg_layout { [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, [QPHY_PCS_READY_STATUS] = 0x17c, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d4, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0d8, + [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178, }; static const unsigned int qmp_v3_usb3phy_regs_layout[] = { [QPHY_SW_RESET] = 0x00, [QPHY_START_CTRL] = 0x08, [QPHY_PCS_READY_STATUS] = 0x174, + [QPHY_PCS_AUTONOMOUS_MODE_CTRL] = 0x0d8, + [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = 0x0dc, + [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170, }; static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = { @@ -536,6 +558,7 @@ struct qmp_phy_cfg { * @tx: iomapped memory space for lane's tx * @rx: iomapped memory space for lane's rx * @pcs: iomapped memory space for lane's pcs + * @pcs_misc: iomapped memory space for lane's pcs_misc * @pipe_clk: pipe lock * @index: lane index * @qmp: QMP phy to which this lane belongs @@ -546,6 +569,7 @@ struct qmp_phy { void __iomem *tx; void __iomem *rx; void __iomem *pcs; + void __iomem *pcs_misc; struct clk *pipe_clk; unsigned int index; struct qcom_qmp *qmp; @@ -567,6 +591,8 @@ struct qmp_phy { * @phys: array of per-lane phy descriptors * @phy_mutex: mutex lock for PHY common block initialization * @init_count: phy common block initialization count + * @phy_initialized: indicate if PHY has been initialized + * @speed: current PHY speed */ struct qcom_qmp { struct device *dev; @@ -582,6 +608,8 @@ struct qcom_qmp { struct mutex phy_mutex; int init_count; + bool phy_initialized; + enum phy_speed speed; }; static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val) @@ -988,6 +1016,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; @@ -1022,6 +1051,135 @@ static int qcom_qmp_phy_exit(struct phy *phy) qcom_qmp_phy_com_exit(qmp); + qmp->phy_initialized = false; + + return 0; +} + +static int qcom_qmp_phy_notify_speed(struct phy *phy, enum phy_speed speed) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + + qmp->speed = speed; + + return 0; +} + +static enum phy_speed qcom_qmp_phy_get_speed(struct phy *phy) +{ + struct qmp_phy *qphy = phy_get_drvdata(phy); + struct qcom_qmp *qmp = qphy->qmp; + + return qmp->speed; +} + +static void qcom_qmp_phy_enable_autonomous_mode(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *pcs = qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + u32 intr_mask; + + if (qmp->speed == PHY_SPEED_USB_SS) + intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; + else + intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; + + /* Clear any pending interrupts status */ + qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + + qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ALFPS_DTCT_EN | ARCVR_DTCT_EVENT_SEL); + + /* Enable required PHY autonomous mode interrupts */ + qphy_setbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], intr_mask); + + /* Enable i/o clamp_n for autonomous mode */ + if (pcs_misc) + qphy_clrbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); +} + +static void qcom_qmp_phy_disable_autonomous_mode(struct qmp_phy *qphy) +{ + struct qcom_qmp *qmp = qphy->qmp; + const struct qmp_phy_cfg *cfg = qmp->cfg; + void __iomem *pcs = qphy->pcs; + void __iomem *pcs_misc = qphy->pcs_misc; + + /* Disable i/o clamp_n on resume for normal mode */ + if (pcs_misc) + qphy_setbits(pcs_misc, QPHY_V3_PCS_MISC_CLAMP_ENABLE, CLAMP_EN); + + qphy_clrbits(pcs, cfg->regs[QPHY_PCS_AUTONOMOUS_MODE_CTRL], + ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL | ALFPS_DTCT_EN); + + qphy_setbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); + /* Writing 1 followed by 0 clears the interrupt */ + qphy_clrbits(pcs, cfg->regs[QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR], IRQ_CLEAR); +} + +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; + + dev_vdbg(dev, "Suspending QMP phy, speed:%d\n", qmp->speed); + + /* Supported only for USB3 PHY */ + if (cfg->type != PHY_TYPE_USB3) + return 0; + + if (!qmp->phy_initialized) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + qcom_qmp_phy_enable_autonomous_mode(qphy); + + clk_disable_unprepare(qphy->pipe_clk); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + + 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; + int ret = 0; + + dev_vdbg(dev, "Resuming QMP phy, speed:%d\n", qmp->speed); + + /* Supported only for USB3 PHY */ + if (cfg->type != PHY_TYPE_USB3) + return 0; + + if (!qmp->phy_initialized) { + dev_vdbg(dev, "PHY not initialized, bailing out\n"); + return 0; + } + + ret = clk_bulk_prepare_enable(cfg->num_clks, qmp->clks); + if (ret) { + dev_err(qmp->dev, "failed to enable clks, err=%d\n", ret); + return ret; + } + + ret = clk_prepare_enable(qphy->pipe_clk); + if (ret) { + dev_err(dev, "pipe_clk enable failed, err=%d\n", ret); + clk_bulk_disable_unprepare(cfg->num_clks, qmp->clks); + return ret; + } + + qcom_qmp_phy_disable_autonomous_mode(qphy); + return 0; } @@ -1134,6 +1292,8 @@ static int phy_pipe_clk_register(struct qcom_qmp *qmp, struct device_node *np) static const struct phy_ops qcom_qmp_phy_gen_ops = { .init = qcom_qmp_phy_init, .exit = qcom_qmp_phy_exit, + .notify_speed = qcom_qmp_phy_notify_speed, + .get_speed = qcom_qmp_phy_get_speed, .owner = THIS_MODULE, }; @@ -1152,7 +1312,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) /* * Get memory resources for each phy lane: - * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2. + * Resources are indexed as: tx -> 0; rx -> 1; pcs -> 2; and + * pcs_misc (optional) -> 3. */ qphy->tx = of_iomap(np, 0); if (!qphy->tx) @@ -1166,6 +1327,10 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id) if (!qphy->pcs) return -ENOMEM; + qphy->pcs_misc = of_iomap(np, 3); + if (!qphy->pcs_misc) + dev_vdbg(dev, "PHY pcs_misc-reg not used\n"); + /* * Get PHY's Pipe clock, if any. USB3 and PCIe are PIPE3 * based phys, so they essentially have pipe clock. So, @@ -1232,6 +1397,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; @@ -1300,12 +1470,21 @@ static int qcom_qmp_phy_probe(struct platform_device *pdev) return -ENOMEM; id = 0; + 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); + 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; } @@ -1317,6 +1496,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++; @@ -1325,6 +1505,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); } @@ -1333,6 +1515,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, }, }; diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index f7d4c2a..264b5c4 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -283,4 +283,7 @@ #define QPHY_V3_PCS_FLL_MAN_CODE 0x0d4 #define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8 +/* Only for QMP V3 PHY - PCS_MISC registers */ +#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c + #endif -- The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project From 1591067518617783532@xxx Wed Jan 31 00:57:08 +0000 2018 X-GM-THRID: 1584597372349222467 X-Gmail-Labels: Inbox,Category Forums,Downloaded_2018-01