aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy/qualcomm
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/phy/qualcomm')
-rw-r--r--drivers/phy/qualcomm/Kconfig9
-rw-r--r--drivers/phy/qualcomm/Makefile1
-rw-r--r--drivers/phy/qualcomm/phy-qcom-apq8064-sata.c10
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c10
-rw-r--r--drivers/phy/qualcomm/phy-qcom-pcie2.c331
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.c225
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qmp.h12
-rw-r--r--drivers/phy/qualcomm/phy-qcom-qusb2.c13
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-i.h16
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c36
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h11
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c36
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h11
-rw-r--r--drivers/phy/qualcomm/phy-qcom-ufs.c70
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hs.c5
-rw-r--r--drivers/phy/qualcomm/phy-qcom-usb-hsic.c5
16 files changed, 581 insertions, 220 deletions
diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig
index 32f7d34eb784..e46824da29f6 100644
--- a/drivers/phy/qualcomm/Kconfig
+++ b/drivers/phy/qualcomm/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Phy drivers for Qualcomm and Atheros platforms
#
@@ -24,6 +25,14 @@ config PHY_QCOM_IPQ806X_SATA
depends on OF
select GENERIC_PHY
+config PHY_QCOM_PCIE2
+ tristate "Qualcomm PCIe Gen2 PHY Driver"
+ depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST)
+ select GENERIC_PHY
+ help
+ Enable this to support the Qualcomm PCIe PHY, used with the Synopsys
+ based PCIe controller.
+
config PHY_QCOM_QMP
tristate "Qualcomm QMP PHY Driver"
depends on OF && COMMON_CLK && (ARCH_QCOM || COMPILE_TEST)
diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile
index c56efd3af205..283251d6a5d9 100644
--- a/drivers/phy/qualcomm/Makefile
+++ b/drivers/phy/qualcomm/Makefile
@@ -2,6 +2,7 @@
obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o
obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
+obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o
obj-$(CONFIG_PHY_QCOM_QMP) += phy-qcom-qmp.o
obj-$(CONFIG_PHY_QCOM_QUSB2) += phy-qcom-qusb2.o
obj-$(CONFIG_PHY_QCOM_UFS) += phy-qcom-ufs.o
diff --git a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
index 69ce2afac015..42bc5150dd92 100644
--- a/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
+++ b/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/io.h>
diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
index 0ad127cc9298..41a69f56b346 100644
--- a/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
+++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/io.h>
diff --git a/drivers/phy/qualcomm/phy-qcom-pcie2.c b/drivers/phy/qualcomm/phy-qcom-pcie2.c
new file mode 100644
index 000000000000..9dba3594e6d9
--- /dev/null
+++ b/drivers/phy/qualcomm/phy-qcom-pcie2.c
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2019, Linaro Ltd.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/phy/phy.h>
+
+#define PCIE20_PARF_PHY_STTS 0x3c
+#define PCIE2_PHY_RESET_CTRL 0x44
+#define PCIE20_PARF_PHY_REFCLK_CTRL2 0xa0
+#define PCIE20_PARF_PHY_REFCLK_CTRL3 0xa4
+#define PCIE20_PARF_PCS_SWING_CTRL1 0x88
+#define PCIE20_PARF_PCS_SWING_CTRL2 0x8c
+#define PCIE20_PARF_PCS_DEEMPH1 0x74
+#define PCIE20_PARF_PCS_DEEMPH2 0x78
+#define PCIE20_PARF_PCS_DEEMPH3 0x7c
+#define PCIE20_PARF_CONFIGBITS 0x84
+#define PCIE20_PARF_PHY_CTRL3 0x94
+#define PCIE20_PARF_PCS_CTRL 0x80
+
+#define TX_AMP_VAL 120
+#define PHY_RX0_EQ_GEN1_VAL 0
+#define PHY_RX0_EQ_GEN2_VAL 4
+#define TX_DEEMPH_GEN1_VAL 24
+#define TX_DEEMPH_GEN2_3_5DB_VAL 26
+#define TX_DEEMPH_GEN2_6DB_VAL 36
+#define PHY_TX0_TERM_OFFST_VAL 0
+
+struct qcom_phy {
+ struct device *dev;
+ void __iomem *base;
+
+ struct regulator_bulk_data vregs[2];
+
+ struct reset_control *phy_reset;
+ struct reset_control *pipe_reset;
+ struct clk *pipe_clk;
+};
+
+static int qcom_pcie2_phy_init(struct phy *phy)
+{
+ struct qcom_phy *qphy = phy_get_drvdata(phy);
+ int ret;
+
+ ret = reset_control_deassert(qphy->phy_reset);
+ if (ret) {
+ dev_err(qphy->dev, "cannot deassert pipe reset\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+ if (ret)
+ reset_control_assert(qphy->phy_reset);
+
+ return ret;
+}
+
+static int qcom_pcie2_phy_power_on(struct phy *phy)
+{
+ struct qcom_phy *qphy = phy_get_drvdata(phy);
+ int ret;
+ u32 val;
+
+ /* Program REF_CLK source */
+ val = readl(qphy->base + PCIE20_PARF_PHY_REFCLK_CTRL2);
+ val &= ~BIT(1);
+ writel(val, qphy->base + PCIE20_PARF_PHY_REFCLK_CTRL2);
+
+ usleep_range(1000, 2000);
+
+ /* Don't use PAD for refclock */
+ val = readl(qphy->base + PCIE20_PARF_PHY_REFCLK_CTRL2);
+ val &= ~BIT(0);
+ writel(val, qphy->base + PCIE20_PARF_PHY_REFCLK_CTRL2);
+
+ /* Program SSP ENABLE */
+ val = readl(qphy->base + PCIE20_PARF_PHY_REFCLK_CTRL3);
+ val |= BIT(0);
+ writel(val, qphy->base + PCIE20_PARF_PHY_REFCLK_CTRL3);
+
+ usleep_range(1000, 2000);
+
+ /* Assert Phy SW Reset */
+ val = readl(qphy->base + PCIE2_PHY_RESET_CTRL);
+ val |= BIT(0);
+ writel(val, qphy->base + PCIE2_PHY_RESET_CTRL);
+
+ /* Program Tx Amplitude */
+ val = readl(qphy->base + PCIE20_PARF_PCS_SWING_CTRL1);
+ val &= ~0x7f;
+ val |= TX_AMP_VAL;
+ writel(val, qphy->base + PCIE20_PARF_PCS_SWING_CTRL1);
+
+ val = readl(qphy->base + PCIE20_PARF_PCS_SWING_CTRL2);
+ val &= ~0x7f;
+ val |= TX_AMP_VAL;
+ writel(val, qphy->base + PCIE20_PARF_PCS_SWING_CTRL2);
+
+ /* Program De-Emphasis */
+ val = readl(qphy->base + PCIE20_PARF_PCS_DEEMPH1);
+ val &= ~0x3f;
+ val |= TX_DEEMPH_GEN2_6DB_VAL;
+ writel(val, qphy->base + PCIE20_PARF_PCS_DEEMPH1);
+
+ val = readl(qphy->base + PCIE20_PARF_PCS_DEEMPH2);
+ val &= ~0x3f;
+ val |= TX_DEEMPH_GEN2_3_5DB_VAL;
+ writel(val, qphy->base + PCIE20_PARF_PCS_DEEMPH2);
+
+ val = readl(qphy->base + PCIE20_PARF_PCS_DEEMPH3);
+ val &= ~0x3f;
+ val |= TX_DEEMPH_GEN1_VAL;
+ writel(val, qphy->base + PCIE20_PARF_PCS_DEEMPH3);
+
+ /* Program Rx_Eq */
+ val = readl(qphy->base + PCIE20_PARF_CONFIGBITS);
+ val &= ~0x7;
+ val |= PHY_RX0_EQ_GEN2_VAL;
+ writel(val, qphy->base + PCIE20_PARF_CONFIGBITS);
+
+ /* Program Tx0_term_offset */
+ val = readl(qphy->base + PCIE20_PARF_PHY_CTRL3);
+ val &= ~0x1f;
+ val |= PHY_TX0_TERM_OFFST_VAL;
+ writel(val, qphy->base + PCIE20_PARF_PHY_CTRL3);
+
+ /* disable Tx2Rx Loopback */
+ val = readl(qphy->base + PCIE20_PARF_PCS_CTRL);
+ val &= ~BIT(1);
+ writel(val, qphy->base + PCIE20_PARF_PCS_CTRL);
+
+ /* De-assert Phy SW Reset */
+ val = readl(qphy->base + PCIE2_PHY_RESET_CTRL);
+ val &= ~BIT(0);
+ writel(val, qphy->base + PCIE2_PHY_RESET_CTRL);
+
+ usleep_range(1000, 2000);
+
+ ret = reset_control_deassert(qphy->pipe_reset);
+ if (ret) {
+ dev_err(qphy->dev, "cannot deassert pipe reset\n");
+ goto out;
+ }
+
+ clk_set_rate(qphy->pipe_clk, 250000000);
+
+ ret = clk_prepare_enable(qphy->pipe_clk);
+ if (ret) {
+ dev_err(qphy->dev, "failed to enable pipe clock\n");
+ goto out;
+ }
+
+ ret = readl_poll_timeout(qphy->base + PCIE20_PARF_PHY_STTS, val,
+ !(val & BIT(0)), 1000, 10);
+ if (ret)
+ dev_err(qphy->dev, "phy initialization failed\n");
+
+out:
+ return ret;
+}
+
+static int qcom_pcie2_phy_power_off(struct phy *phy)
+{
+ struct qcom_phy *qphy = phy_get_drvdata(phy);
+ u32 val;
+
+ val = readl(qphy->base + PCIE2_PHY_RESET_CTRL);
+ val |= BIT(0);
+ writel(val, qphy->base + PCIE2_PHY_RESET_CTRL);
+
+ clk_disable_unprepare(qphy->pipe_clk);
+ reset_control_assert(qphy->pipe_reset);
+
+ return 0;
+}
+
+static int qcom_pcie2_phy_exit(struct phy *phy)
+{
+ struct qcom_phy *qphy = phy_get_drvdata(phy);
+
+ regulator_bulk_disable(ARRAY_SIZE(qphy->vregs), qphy->vregs);
+ reset_control_assert(qphy->phy_reset);
+
+ return 0;
+}
+
+static const struct phy_ops qcom_pcie2_ops = {
+ .init = qcom_pcie2_phy_init,
+ .power_on = qcom_pcie2_phy_power_on,
+ .power_off = qcom_pcie2_phy_power_off,
+ .exit = qcom_pcie2_phy_exit,
+ .owner = THIS_MODULE,
+};
+
+/*
+ * Register a fixed rate pipe clock.
+ *
+ * The <s>_pipe_clksrc generated by PHY goes to the GCC that gate
+ * controls it. The <s>_pipe_clk coming out of the GCC is requested
+ * by the PHY driver for its operations.
+ * We register the <s>_pipe_clksrc here. The gcc driver takes care
+ * of assigning this <s>_pipe_clksrc as parent to <s>_pipe_clk.
+ * Below picture shows this relationship.
+ *
+ * +---------------+
+ * | PHY block |<<---------------------------------------+
+ * | | |
+ * | +-------+ | +-----+ |
+ * I/P---^-->| PLL |---^--->pipe_clksrc--->| GCC |--->pipe_clk---+
+ * clk | +-------+ | +-----+
+ * +---------------+
+ */
+static int phy_pipe_clksrc_register(struct qcom_phy *qphy)
+{
+ struct device_node *np = qphy->dev->of_node;
+ struct clk_fixed_rate *fixed;
+ struct clk_init_data init = { };
+ int ret;
+
+ ret = of_property_read_string(np, "clock-output-names", &init.name);
+ if (ret) {
+ dev_err(qphy->dev, "%s: No clock-output-names\n", np->name);
+ return ret;
+ }
+
+ fixed = devm_kzalloc(qphy->dev, sizeof(*fixed), GFP_KERNEL);
+ if (!fixed)
+ return -ENOMEM;
+
+ init.ops = &clk_fixed_rate_ops;
+
+ /* controllers using QMP phys use 250MHz pipe clock interface */
+ fixed->fixed_rate = 250000000;
+ fixed->hw.init = &init;
+
+ return devm_clk_hw_register(qphy->dev, &fixed->hw);
+}
+
+static int qcom_pcie2_phy_probe(struct platform_device *pdev)
+{
+ struct phy_provider *phy_provider;
+ struct qcom_phy *qphy;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct phy *phy;
+ int ret;
+
+ qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
+ if (!qphy)
+ return -ENOMEM;
+
+ qphy->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ qphy->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(qphy->base))
+ return PTR_ERR(qphy->base);
+
+ ret = phy_pipe_clksrc_register(qphy);
+ if (ret) {
+ dev_err(dev, "failed to register pipe_clk\n");
+ return ret;
+ }
+
+ qphy->vregs[0].supply = "vdda-vp";
+ qphy->vregs[1].supply = "vdda-vph";
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(qphy->vregs), qphy->vregs);
+ if (ret < 0)
+ return ret;
+
+ qphy->pipe_clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(qphy->pipe_clk)) {
+ dev_err(dev, "failed to acquire pipe clock\n");
+ return PTR_ERR(qphy->pipe_clk);
+ }
+
+ qphy->phy_reset = devm_reset_control_get_exclusive(dev, "phy");
+ if (IS_ERR(qphy->phy_reset)) {
+ dev_err(dev, "failed to acquire phy reset\n");
+ return PTR_ERR(qphy->phy_reset);
+ }
+
+ qphy->pipe_reset = devm_reset_control_get_exclusive(dev, "pipe");
+ if (IS_ERR(qphy->pipe_reset)) {
+ dev_err(dev, "failed to acquire pipe reset\n");
+ return PTR_ERR(qphy->pipe_reset);
+ }
+
+ phy = devm_phy_create(dev, dev->of_node, &qcom_pcie2_ops);
+ if (IS_ERR(phy)) {
+ dev_err(dev, "failed to create phy\n");
+ return PTR_ERR(phy);
+ }
+
+ phy_set_drvdata(phy, qphy);
+
+ phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+ if (IS_ERR(phy_provider))
+ dev_err(dev, "failed to register phy provider\n");
+
+ return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id qcom_pcie2_phy_match_table[] = {
+ { .compatible = "qcom,pcie2-phy" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, qcom_pcie2_phy_match_table);
+
+static struct platform_driver qcom_pcie2_phy_driver = {
+ .probe = qcom_pcie2_phy_probe,
+ .driver = {
+ .name = "phy-qcom-pcie2",
+ .of_match_table = qcom_pcie2_phy_match_table,
+ },
+};
+
+module_platform_driver(qcom_pcie2_phy_driver);
+
+MODULE_DESCRIPTION("Qualcomm PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c
index 08d6f6f7f039..34ff6434da8f 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp.c
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.c
@@ -242,6 +242,88 @@ static const struct qmp_phy_init_tbl msm8996_pcie_pcs_tbl[] = {
QMP_PHY_INIT_CFG(QPHY_TXDEEMPH_M3P5DB_V0, 0x0e),
};
+static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x0f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0x55),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x08),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x34),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x07),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e),
+ QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15),
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_tx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+ QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_rx_tbl[] = {
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x1c),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x00),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71),
+ QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40),
+};
+
+static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = {
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x99),
+ QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
+};
+
static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = {
QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
@@ -897,6 +979,7 @@ struct qmp_phy {
* @init_count: phy common block initialization count
* @phy_initialized: indicate if PHY has been initialized
* @mode: current PHY mode
+ * @ufs_reset: optional UFS PHY reset handle
*/
struct qcom_qmp {
struct device *dev;
@@ -914,6 +997,8 @@ struct qcom_qmp {
int init_count;
bool phy_initialized;
enum phy_mode mode;
+
+ struct reset_control *ufs_reset;
};
static inline void qphy_setbits(void __iomem *base, u32 offset, u32 val)
@@ -989,6 +1074,7 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
.start_ctrl = PCS_START | PLL_READY_GATE_EN,
.pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .mask_pcs_ready = PHYSTATUS,
.mask_com_pcs_ready = PCS_READY,
.has_phy_com_ctrl = true,
@@ -1146,6 +1232,31 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = {
.no_pcs_sw_reset = true,
};
+static const struct qmp_phy_cfg msm8998_pciephy_cfg = {
+ .type = PHY_TYPE_PCIE,
+ .nlanes = 1,
+
+ .serdes_tbl = msm8998_pcie_serdes_tbl,
+ .serdes_tbl_num = ARRAY_SIZE(msm8998_pcie_serdes_tbl),
+ .tx_tbl = msm8998_pcie_tx_tbl,
+ .tx_tbl_num = ARRAY_SIZE(msm8998_pcie_tx_tbl),
+ .rx_tbl = msm8998_pcie_rx_tbl,
+ .rx_tbl_num = ARRAY_SIZE(msm8998_pcie_rx_tbl),
+ .pcs_tbl = msm8998_pcie_pcs_tbl,
+ .pcs_tbl_num = ARRAY_SIZE(msm8998_pcie_pcs_tbl),
+ .clk_list = msm8996_phy_clk_l,
+ .num_clks = ARRAY_SIZE(msm8996_phy_clk_l),
+ .reset_list = ipq8074_pciephy_reset_l,
+ .num_resets = ARRAY_SIZE(ipq8074_pciephy_reset_l),
+ .vreg_list = qmp_phy_vreg_l,
+ .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l),
+ .regs = pciephy_regs_layout,
+
+ .start_ctrl = SERDES_START | PCS_START,
+ .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL,
+ .mask_pcs_ready = PHYSTATUS,
+};
+
static const struct qmp_phy_cfg msm8998_usb3phy_cfg = {
.type = PHY_TYPE_USB3,
.nlanes = 1,
@@ -1314,6 +1425,7 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
return 0;
}
+ reset_control_assert(qmp->ufs_reset);
if (cfg->has_phy_com_ctrl) {
qphy_setbits(serdes, cfg->regs[QPHY_COM_START_CONTROL],
SERDES_START | PCS_START);
@@ -1335,8 +1447,7 @@ static int qcom_qmp_phy_com_exit(struct qcom_qmp *qmp)
return 0;
}
-/* PHY Initialization */
-static int qcom_qmp_phy_init(struct phy *phy)
+static int qcom_qmp_phy_enable(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
@@ -1351,6 +1462,33 @@ static int qcom_qmp_phy_init(struct phy *phy)
dev_vdbg(qmp->dev, "Initializing QMP phy\n");
+ if (cfg->no_pcs_sw_reset) {
+ /*
+ * Get UFS reset, which is delayed until now to avoid a
+ * circular dependency where UFS needs its PHY, but the PHY
+ * needs this UFS reset.
+ */
+ if (!qmp->ufs_reset) {
+ qmp->ufs_reset =
+ devm_reset_control_get_exclusive(qmp->dev,
+ "ufsphy");
+
+ if (IS_ERR(qmp->ufs_reset)) {
+ ret = PTR_ERR(qmp->ufs_reset);
+ dev_err(qmp->dev,
+ "failed to get UFS reset: %d\n",
+ ret);
+
+ qmp->ufs_reset = NULL;
+ return ret;
+ }
+ }
+
+ ret = reset_control_assert(qmp->ufs_reset);
+ if (ret)
+ goto err_lane_rst;
+ }
+
ret = qcom_qmp_phy_com_init(qphy);
if (ret)
return ret;
@@ -1383,14 +1521,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
cfg->rx_tbl, cfg->rx_tbl_num);
qcom_qmp_phy_configure(pcs, cfg->regs, cfg->pcs_tbl, cfg->pcs_tbl_num);
-
- /*
- * UFS PHY requires the deassert of software reset before serdes start.
- * For UFS PHYs that do not have software reset control bits, defer
- * starting serdes until the power on callback.
- */
- if ((cfg->type == PHY_TYPE_UFS) && cfg->no_pcs_sw_reset)
- goto out;
+ ret = reset_control_deassert(qmp->ufs_reset);
+ if (ret)
+ goto err_lane_rst;
/*
* Pull out PHY from POWER DOWN state.
@@ -1403,7 +1536,9 @@ static int qcom_qmp_phy_init(struct phy *phy)
usleep_range(cfg->pwrdn_delay_min, cfg->pwrdn_delay_max);
/* Pull PHY out of reset state */
- qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+ if (!cfg->no_pcs_sw_reset)
+ qphy_clrbits(pcs, cfg->regs[QPHY_SW_RESET], SW_RESET);
+
if (cfg->has_phy_dp_com_ctrl)
qphy_clrbits(dp_com, QPHY_V3_DP_COM_SW_RESET, SW_RESET);
@@ -1413,18 +1548,17 @@ static int qcom_qmp_phy_init(struct phy *phy)
status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
mask = cfg->mask_pcs_ready;
- ret = readl_poll_timeout(status, val, !(val & mask), 1,
+ ret = readl_poll_timeout(status, val, val & mask, 10,
PHY_INIT_COMPLETE_TIMEOUT);
if (ret) {
dev_err(qmp->dev, "phy initialization timed-out\n");
goto err_pcs_ready;
}
qmp->phy_initialized = true;
-
-out:
- return ret;
+ return 0;
err_pcs_ready:
+ reset_control_assert(qmp->ufs_reset);
clk_disable_unprepare(qphy->pipe_clk);
err_clk_enable:
if (cfg->has_lane_rst)
@@ -1435,7 +1569,7 @@ err_lane_rst:
return ret;
}
-static int qcom_qmp_phy_exit(struct phy *phy)
+static int qcom_qmp_phy_disable(struct phy *phy)
{
struct qmp_phy *qphy = phy_get_drvdata(phy);
struct qcom_qmp *qmp = qphy->qmp;
@@ -1463,44 +1597,6 @@ static int qcom_qmp_phy_exit(struct phy *phy)
return 0;
}
-static int qcom_qmp_phy_poweron(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;
- void __iomem *pcs = qphy->pcs;
- void __iomem *status;
- unsigned int mask, val;
- int ret = 0;
-
- if (cfg->type != PHY_TYPE_UFS)
- return 0;
-
- /*
- * For UFS PHY that has not software reset control, serdes start
- * should only happen when UFS driver explicitly calls phy_power_on
- * after it deasserts software reset.
- */
- if (cfg->no_pcs_sw_reset && !qmp->phy_initialized &&
- (qmp->init_count != 0)) {
- /* start SerDes and Phy-Coding-Sublayer */
- qphy_setbits(pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
-
- status = pcs + cfg->regs[QPHY_PCS_READY_STATUS];
- mask = cfg->mask_pcs_ready;
-
- ret = readl_poll_timeout(status, val, !(val & mask), 1,
- PHY_INIT_COMPLETE_TIMEOUT);
- if (ret) {
- dev_err(qmp->dev, "phy initialization timed-out\n");
- return ret;
- }
- qmp->phy_initialized = true;
- }
-
- return ret;
-}
-
static int qcom_qmp_phy_set_mode(struct phy *phy,
enum phy_mode mode, int submode)
{
@@ -1750,9 +1846,15 @@ 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,
- .power_on = qcom_qmp_phy_poweron,
+ .init = qcom_qmp_phy_enable,
+ .exit = qcom_qmp_phy_disable,
+ .set_mode = qcom_qmp_phy_set_mode,
+ .owner = THIS_MODULE,
+};
+
+static const struct phy_ops qcom_qmp_ufs_ops = {
+ .power_on = qcom_qmp_phy_enable,
+ .power_off = qcom_qmp_phy_disable,
.set_mode = qcom_qmp_phy_set_mode,
.owner = THIS_MODULE,
};
@@ -1763,6 +1865,7 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
struct qcom_qmp *qmp = dev_get_drvdata(dev);
struct phy *generic_phy;
struct qmp_phy *qphy;
+ const struct phy_ops *ops = &qcom_qmp_phy_gen_ops;
char prop_name[MAX_PROP_NAME];
int ret;
@@ -1849,7 +1952,10 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
}
}
- generic_phy = devm_phy_create(dev, np, &qcom_qmp_phy_gen_ops);
+ if (qmp->cfg->type == PHY_TYPE_UFS)
+ ops = &qcom_qmp_ufs_ops;
+
+ generic_phy = devm_phy_create(dev, np, ops);
if (IS_ERR(generic_phy)) {
ret = PTR_ERR(generic_phy);
dev_err(dev, "failed to create qphy %d\n", ret);
@@ -1873,6 +1979,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
.compatible = "qcom,msm8996-qmp-usb3-phy",
.data = &msm8996_usb3phy_cfg,
}, {
+ .compatible = "qcom,msm8998-qmp-pcie-phy",
+ .data = &msm8998_pciephy_cfg,
+ }, {
.compatible = "qcom,msm8998-qmp-ufs-phy",
.data = &sdm845_ufsphy_cfg,
}, {
diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h
index a1b6cdee9a08..335ea5d7ef40 100644
--- a/drivers/phy/qualcomm/phy-qcom-qmp.h
+++ b/drivers/phy/qualcomm/phy-qcom-qmp.h
@@ -241,6 +241,7 @@
#define QSERDES_V3_RX_RX_BAND 0x110
#define QSERDES_V3_RX_RX_INTERFACE_MODE 0x11c
#define QSERDES_V3_RX_RX_MODE_00 0x164
+#define QSERDES_V3_RX_RX_MODE_01 0x168
/* Only for QMP V3 PHY - PCS registers */
#define QPHY_V3_PCS_POWER_DOWN_CONTROL 0x004
@@ -280,6 +281,7 @@
#define QPHY_V3_PCS_TSYNC_RSYNC_TIME 0x08c
#define QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK 0x0a0
#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK 0x0a4
+#define QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME 0x0a8
#define QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK 0x0b0
#define QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME 0x0b8
#define QPHY_V3_PCS_RXEQTRAINING_RUN_TIME 0x0bc
@@ -292,13 +294,23 @@
#define QPHY_V3_PCS_RX_MIN_HIBERN8_TIME 0x138
#define QPHY_V3_PCS_RX_SIGDET_CTRL1 0x13c
#define QPHY_V3_PCS_RX_SIGDET_CTRL2 0x140
+#define QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1a8
+#define QPHY_V3_PCS_OSC_DTCT_ACTIONS 0x1ac
+#define QPHY_V3_PCS_SIGDET_CNTRL 0x1b0
#define QPHY_V3_PCS_TX_MID_TERM_CTRL1 0x1bc
#define QPHY_V3_PCS_MULTI_LANE_CTRL1 0x1c4
#define QPHY_V3_PCS_RX_SIGDET_LVL 0x1d8
+#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB 0x1dc
+#define QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB 0x1e0
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG1 0x20c
#define QPHY_V3_PCS_REFGEN_REQ_CONFIG2 0x210
/* Only for QMP V3 PHY - PCS_MISC registers */
#define QPHY_V3_PCS_MISC_CLAMP_ENABLE 0x0c
+#define QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2 0x2c
+#define QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1 0x44
+#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2 0x54
+#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4 0x5c
+#define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5 0x60
#endif
diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c
index 8fd7ce139772..bf94a52d3087 100644
--- a/drivers/phy/qualcomm/phy-qcom-qusb2.c
+++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c
@@ -564,7 +564,7 @@ static int __maybe_unused qusb2_phy_runtime_resume(struct device *dev)
}
if (!qphy->has_se_clk_scheme) {
- clk_prepare_enable(qphy->ref_clk);
+ ret = clk_prepare_enable(qphy->ref_clk);
if (ret) {
dev_err(dev, "failed to enable ref clk, %d\n", ret);
goto disable_ahb_clk;
@@ -822,14 +822,9 @@ static int qusb2_phy_probe(struct platform_device *pdev)
return ret;
}
- qphy->iface_clk = devm_clk_get(dev, "iface");
- if (IS_ERR(qphy->iface_clk)) {
- ret = PTR_ERR(qphy->iface_clk);
- if (ret == -EPROBE_DEFER)
- return ret;
- qphy->iface_clk = NULL;
- dev_dbg(dev, "failed to get iface clk, %d\n", ret);
- }
+ qphy->iface_clk = devm_clk_get_optional(dev, "iface");
+ if (IS_ERR(qphy->iface_clk))
+ return PTR_ERR(qphy->iface_clk);
qphy->phy_reset = devm_reset_control_get_by_index(&pdev->dev, 0);
if (IS_ERR(qphy->phy_reset)) {
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-i.h b/drivers/phy/qualcomm/phy-qcom-ufs-i.h
index f798fb64de94..9bf973a0d8c3 100644
--- a/drivers/phy/qualcomm/phy-qcom-ufs-i.h
+++ b/drivers/phy/qualcomm/phy-qcom-ufs-i.h
@@ -1,15 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef UFS_QCOM_PHY_I_H_
@@ -19,6 +10,7 @@
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/io.h>
@@ -96,11 +88,10 @@ struct ufs_qcom_phy {
char name[UFS_QCOM_PHY_NAME_LEN];
struct ufs_qcom_phy_calibration *cached_regs;
int cached_regs_table_size;
- bool is_powered_on;
- bool is_started;
struct ufs_qcom_phy_specific_ops *phy_spec_ops;
enum phy_mode mode;
+ struct reset_control *ufs_reset;
};
/**
@@ -115,6 +106,7 @@ struct ufs_qcom_phy {
* and writes to QSERDES_RX_SIGDET_CNTRL attribute
*/
struct ufs_qcom_phy_specific_ops {
+ int (*calibrate)(struct ufs_qcom_phy *ufs_qcom_phy, bool is_rate_B);
void (*start_serdes)(struct ufs_qcom_phy *phy);
int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
index 1e0d4f2046a4..54b355bfc24c 100644
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
+++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include "phy-qcom-ufs-qmp-14nm.h"
@@ -42,28 +33,6 @@ void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
}
-static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
-{
- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
- bool is_rate_B = false;
- int ret;
-
- if (phy_common->mode == PHY_MODE_UFS_HS_B)
- is_rate_B = true;
-
- ret = ufs_qcom_phy_qmp_14nm_phy_calibrate(phy_common, is_rate_B);
- if (!ret)
- /* phy calibrated, but yet to be started */
- phy_common->is_started = false;
-
- return ret;
-}
-
-static int ufs_qcom_phy_qmp_14nm_exit(struct phy *generic_phy)
-{
- return 0;
-}
-
static
int ufs_qcom_phy_qmp_14nm_set_mode(struct phy *generic_phy,
enum phy_mode mode, int submode)
@@ -124,8 +93,6 @@ static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
}
static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
- .init = ufs_qcom_phy_qmp_14nm_init,
- .exit = ufs_qcom_phy_qmp_14nm_exit,
.power_on = ufs_qcom_phy_power_on,
.power_off = ufs_qcom_phy_power_off,
.set_mode = ufs_qcom_phy_qmp_14nm_set_mode,
@@ -133,6 +100,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
};
static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
+ .calibrate = ufs_qcom_phy_qmp_14nm_phy_calibrate,
.start_serdes = ufs_qcom_phy_qmp_14nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h
index 3aefdbacbcd0..ceca654b9338 100644
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h
+++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-14nm.h
@@ -1,15 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef UFS_QCOM_PHY_QMP_14NM_H_
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
index aef40f7a41d4..3e9d8b71e995 100644
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
+++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include "phy-qcom-ufs-qmp-20nm.h"
@@ -61,28 +52,6 @@ void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
}
-static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
-{
- struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
- bool is_rate_B = false;
- int ret;
-
- if (phy_common->mode == PHY_MODE_UFS_HS_B)
- is_rate_B = true;
-
- ret = ufs_qcom_phy_qmp_20nm_phy_calibrate(phy_common, is_rate_B);
- if (!ret)
- /* phy calibrated, but yet to be started */
- phy_common->is_started = false;
-
- return ret;
-}
-
-static int ufs_qcom_phy_qmp_20nm_exit(struct phy *generic_phy)
-{
- return 0;
-}
-
static
int ufs_qcom_phy_qmp_20nm_set_mode(struct phy *generic_phy,
enum phy_mode mode, int submode)
@@ -182,8 +151,6 @@ static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
}
static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
- .init = ufs_qcom_phy_qmp_20nm_init,
- .exit = ufs_qcom_phy_qmp_20nm_exit,
.power_on = ufs_qcom_phy_power_on,
.power_off = ufs_qcom_phy_power_off,
.set_mode = ufs_qcom_phy_qmp_20nm_set_mode,
@@ -191,6 +158,7 @@ static const struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
};
static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
+ .calibrate = ufs_qcom_phy_qmp_20nm_phy_calibrate,
.start_serdes = ufs_qcom_phy_qmp_20nm_start_serdes,
.is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
.set_tx_lane_enable = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h
index 4f3076bb3d71..8ce79f524eed 100644
--- a/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h
+++ b/drivers/phy/qualcomm/phy-qcom-ufs-qmp-20nm.h
@@ -1,15 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef UFS_QCOM_PHY_QMP_20NM_H_
diff --git a/drivers/phy/qualcomm/phy-qcom-ufs.c b/drivers/phy/qualcomm/phy-qcom-ufs.c
index f2979ccad00a..763c8d396af1 100644
--- a/drivers/phy/qualcomm/phy-qcom-ufs.c
+++ b/drivers/phy/qualcomm/phy-qcom-ufs.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include "phy-qcom-ufs-i.h"
@@ -147,6 +138,21 @@ out:
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
+static int ufs_qcom_phy_get_reset(struct ufs_qcom_phy *phy_common)
+{
+ struct reset_control *reset;
+
+ if (phy_common->ufs_reset)
+ return 0;
+
+ reset = devm_reset_control_get_exclusive_by_index(phy_common->dev, 0);
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ phy_common->ufs_reset = reset;
+ return 0;
+}
+
static int __ufs_qcom_phy_clk_get(struct device *dev,
const char *name, struct clk **clk_out, bool err_print)
{
@@ -459,7 +465,7 @@ out:
}
/* Turn OFF M-PHY RMMI interface clocks */
-void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
+static void ufs_qcom_phy_disable_iface_clk(struct ufs_qcom_phy *phy)
{
if (phy->is_iface_clk_enabled) {
clk_disable_unprepare(phy->tx_iface_clk);
@@ -528,23 +534,38 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
{
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
struct device *dev = phy_common->dev;
+ bool is_rate_B = false;
int err;
- if (phy_common->is_powered_on)
- return 0;
+ err = ufs_qcom_phy_get_reset(phy_common);
+ if (err)
+ return err;
- if (!phy_common->is_started) {
- err = ufs_qcom_phy_start_serdes(phy_common);
- if (err)
- return err;
+ err = reset_control_assert(phy_common->ufs_reset);
+ if (err)
+ return err;
- err = ufs_qcom_phy_is_pcs_ready(phy_common);
- if (err)
- return err;
+ if (phy_common->mode == PHY_MODE_UFS_HS_B)
+ is_rate_B = true;
- phy_common->is_started = true;
+ err = phy_common->phy_spec_ops->calibrate(phy_common, is_rate_B);
+ if (err)
+ return err;
+
+ err = reset_control_deassert(phy_common->ufs_reset);
+ if (err) {
+ dev_err(dev, "Failed to assert UFS PHY reset");
+ return err;
}
+ err = ufs_qcom_phy_start_serdes(phy_common);
+ if (err)
+ return err;
+
+ err = ufs_qcom_phy_is_pcs_ready(phy_common);
+ if (err)
+ return err;
+
err = ufs_qcom_phy_enable_vreg(dev, &phy_common->vdda_phy);
if (err) {
dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
@@ -587,7 +608,6 @@ int ufs_qcom_phy_power_on(struct phy *generic_phy)
}
}
- phy_common->is_powered_on = true;
goto out;
out_disable_ref_clk:
@@ -607,9 +627,6 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
{
struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
- if (!phy_common->is_powered_on)
- return 0;
-
phy_common->phy_spec_ops->power_control(phy_common, false);
if (phy_common->vddp_ref_clk.reg)
@@ -620,8 +637,7 @@ int ufs_qcom_phy_power_off(struct phy *generic_phy)
ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_pll);
ufs_qcom_phy_disable_vreg(phy_common->dev, &phy_common->vdda_phy);
- phy_common->is_powered_on = false;
-
+ reset_control_assert(phy_common->ufs_reset);
return 0;
}
EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs.c b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
index 04934f8dac91..b163b3a1558d 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hs.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hs.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (C) 2016 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/ulpi/driver.h>
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c
index c110563a73cb..04d18d52f700 100644
--- a/drivers/phy/qualcomm/phy-qcom-usb-hsic.c
+++ b/drivers/phy/qualcomm/phy-qcom-usb-hsic.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (C) 2016 Linaro Ltd
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/ulpi/driver.h>