All of lore.kernel.org
 help / color / mirror / Atom feed
From: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
To: Kishon Vijay Abraham I <kishon@ti.com>
Cc: Rob Herring <robh+dt@kernel.org>,
	Mark Rutland <mark.rutland@arm.com>,
	devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-arm-msm@vger.kernel.org,
	Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
Subject: [PATCH 2/2] phy: msm8996-pcie-phy: Add support to msm8996 pcie phy
Date: Wed,  7 Sep 2016 11:55:33 +0100	[thread overview]
Message-ID: <1473245733-17260-3-git-send-email-srinivas.kandagatla@linaro.org> (raw)
In-Reply-To: <1473245733-17260-1-git-send-email-srinivas.kandagatla@linaro.org>

This patch adds support to msm8996 pcie phy which supports 3 ports,
Port A, Port B and Port C.

Each port is independent and connected to a pcie host controller, there is
also a common block which is shared across all the 3 ports.

Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
---
 drivers/phy/Kconfig                 |   7 +
 drivers/phy/Makefile                |   1 +
 drivers/phy/phy-qcom-msm8996-pcie.c | 492 ++++++++++++++++++++++++++++++++++++
 3 files changed, 500 insertions(+)
 create mode 100644 drivers/phy/phy-qcom-msm8996-pcie.c

diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19bff3a..8ad621c 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -344,6 +344,13 @@ config PHY_QCOM_APQ8064_SATA
 	depends on OF
 	select GENERIC_PHY
 
+config PHY_QCOM_MSM8996_PCIE
+	tristate "Qualcomm MSM8996 PCIE SerDes/PHY driver"
+	depends on ARCH_QCOM
+	depends on HAS_IOMEM
+	depends on OF
+	select GENERIC_PHY
+
 config PHY_QCOM_IPQ806X_SATA
 	tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
 	depends on ARCH_QCOM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 90ae198..273b9c5 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -38,6 +38,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2)	+= phy-exynos5250-usb2.o
 phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
+obj-$(CONFIG_PHY_QCOM_MSM8996_PCIE)	+= phy-qcom-msm8996-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)		+= phy-rockchip-dp.o
diff --git a/drivers/phy/phy-qcom-msm8996-pcie.c b/drivers/phy/phy-qcom-msm8996-pcie.c
new file mode 100644
index 0000000..f7d0c73
--- /dev/null
+++ b/drivers/phy/phy-qcom-msm8996-pcie.c
@@ -0,0 +1,492 @@
+/*
+ * 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>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/phy/phy.h>
+
+#define QSERDES_COM_BG_TIMER			0x00c
+#define QSERDES_COM_SSC_EN_CENTER		0x010
+#define QSERDES_COM_SSC_ADJ_PER1		0x014
+#define QSERDES_COM_SSC_ADJ_PER2		0x018
+#define QSERDES_COM_SSC_PER1			0x01c
+#define QSERDES_COM_SSC_PER2			0x020
+#define QSERDES_COM_SSC_STEP_SIZE1		0x024
+#define QSERDES_COM_SSC_STEP_SIZE2		0x028
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN		0x034
+#define QSERDES_COM_CLK_ENABLE1			0x038
+#define QSERDES_COM_SYS_CLK_CTRL		0x03c
+#define QSERDES_COM_SYSCLK_BUF_ENABLE		0x040
+#define QSERDES_COM_PLL_IVCO			0x048
+#define QSERDES_COM_LOCK_CMP1_MODE0		0x04c
+#define QSERDES_COM_LOCK_CMP2_MODE0		0x050
+#define QSERDES_COM_LOCK_CMP3_MODE0		0x054
+#define QSERDES_COM_BG_TRIM			0x070
+#define QSERDES_COM_CLK_EP_DIV			0x074
+#define QSERDES_COM_CP_CTRL_MODE0		0x078
+#define QSERDES_COM_PLL_RCTRL_MODE0		0x084
+#define QSERDES_COM_PLL_CCTRL_MODE0		0x090
+#define QSERDES_COM_SYSCLK_EN_SEL		0x0ac
+#define QSERDES_COM_RESETSM_CNTRL		0x0b4
+#define QSERDES_COM_RESTRIM_CTRL		0x0bc
+#define QSERDES_COM_RESCODE_DIV_NUM		0x0c4
+#define QSERDES_COM_LOCK_CMP_EN			0x0c8
+#define QSERDES_COM_DEC_START_MODE0		0x0d0
+#define QSERDES_COM_DIV_FRAC_START1_MODE0	0x0dc
+#define QSERDES_COM_DIV_FRAC_START2_MODE0	0x0e0
+#define QSERDES_COM_DIV_FRAC_START3_MODE0	0x0e4
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0	0x108
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0	0x10c
+#define QSERDES_COM_VCO_TUNE_CTRL		0x124
+#define QSERDES_COM_VCO_TUNE_MAP		0x128
+#define QSERDES_COM_VCO_TUNE1_MODE0		0x12c
+#define QSERDES_COM_VCO_TUNE2_MODE0		0x130
+#define QSERDES_COM_VCO_TUNE_TIMER1		0x144
+#define QSERDES_COM_VCO_TUNE_TIMER2		0x148
+#define QSERDES_COM_BG_CTRL			0x170
+#define QSERDES_COM_CLK_SELECT			0x174
+#define QSERDES_COM_HSCLK_SEL			0x178
+#define QSERDES_COM_CORECLK_DIV			0x184
+#define QSERDES_COM_CORE_CLK_EN			0x18c
+#define QSERDES_COM_C_READY_STATUS		0x190
+#define QSERDES_COM_CMN_CONFIG			0x194
+#define QSERDES_COM_SVS_MODE_CLK_SEL		0x19c
+
+#define PCIE_N_SW_RESET(n)			(PCS_PORT(n) + 0x00)
+#define PCIE_N_POWER_DOWN_CONTROL(n)		(PCS_PORT(n) + 0x04)
+#define PCIE_N_START_CONTROL(n)			(PCS_PORT(n) + 0x08)
+#define PCIE_N_TXDEEMPH_M6DB_V0(n)		(PCS_PORT(n) + 0x24)
+#define PCIE_N_TXDEEMPH_M3P5DB_V0(n)		(PCS_PORT(n) + 0x28)
+#define PCIE_N_ENDPOINT_REFCLK_DRIVE(n)		(PCS_PORT(n) + 0x54)
+#define PCIE_N_RX_IDLE_DTCT_CNTRL(n)		(PCS_PORT(n) + 0x58)
+#define PCIE_N_POWER_STATE_CONFIG1(n)		(PCS_PORT(n) + 0x60)
+#define PCIE_N_POWER_STATE_CONFIG4(n)		(PCS_PORT(n) + 0x6c)
+#define PCIE_N_PWRUP_RESET_DLY_TIME_AUXCLK(n)	(PCS_PORT(n) + 0xa0)
+#define PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK(n)	(PCS_PORT(n) + 0xa4)
+#define PCIE_N_PLL_LOCK_CHK_DLY_TIME(n)		(PCS_PORT(n) + 0xa8)
+#define PCIE_N_PCS_STATUS(n)			(PCS_PORT(n) + 0x174)
+#define PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK_MSB(n)	(PCS_PORT(n) + 0x1a8)
+#define PCIE_N_OSC_DTCT_ACTIONS(n)		(PCS_PORT(n) + 0x1ac)
+#define PCIE_N_SIGDET_CNTRL(n)			(PCS_PORT(n) + 0x1b0)
+#define PCIE_N_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB(n)	(PCS_PORT(n) + 0x1dc)
+#define PCIE_N_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB(n)	(PCS_PORT(n) + 0x1e0)
+
+#define PCIE_COM_SW_RESET		0x400
+#define PCIE_COM_POWER_DOWN_CONTROL	0x404
+#define PCIE_COM_START_CONTROL		0x408
+#define PCIE_COM_PCS_READY_STATUS	0x448
+
+#define PCIE_LANE_TX_BASE 0x1000
+#define PCIE_LANE_RX_BASE 0x1200
+#define PCIE_LANE_PCS_BASE 0x1400
+
+#define TX(n) (PCIE_LANE_TX_BASE + n * 0x1000)
+#define RX(n) (PCIE_LANE_RX_BASE + n * 0x1000)
+#define PCS_PORT(n) (PCIE_LANE_PCS_BASE + n * 0x1000)
+
+#define QSERDES_TX_N_RES_CODE_LANE_OFFSET(n)	(TX(n) + 0x4c)
+#define QSERDES_TX_N_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN(n) (TX(n) + 0x68)
+#define QSERDES_TX_N_LANE_MODE(n)		(TX(n) + 0x94)
+#define QSERDES_TX_N_RCV_DETECT_LVL_2(n)	(TX(n) + 0xac)
+
+#define QSERDES_RX_N_UCDR_SO_GAIN_HALF(n)	(RX(n) + 0x010)
+#define QSERDES_RX_N_UCDR_SO_GAIN(n)		(RX(n) + 0x01c)
+#define QSERDES_RX_N_UCDR_SO_SATURATION_AND_ENABLE(n) (RX(n) + 0x048)
+#define QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL2(n)	(RX(n) + 0x0d8)
+#define QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL3(n)	(RX(n) + 0x0dc)
+#define QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL4(n)	(RX(n) + 0x0e0)
+#define QSERDES_RX_N_SIGDET_ENABLES(n)		(RX(n) + 0x110)
+#define QSERDES_RX_N_SIGDET_DEGLITCH_CNTRL(n)	(RX(n) + 0x11c)
+#define QSERDES_RX_N_SIGDET_LVL(n)		(RX(n) + 0x118)
+#define QSERDES_RX_N_RX_BAND(n)			(RX(n) + 0x120)
+
+#define REFCLK_STABILIZATION_DELAY_US_MIN	1000
+#define REFCLK_STABILIZATION_DELAY_US_MAX	1005
+#define PHY_READY_TIMEOUT_COUNT			10
+#define POWER_DOWN_DELAY_US_MIN			10
+#define POWER_DOWN_DELAY_US_MAX			11
+
+struct phy_msm8996_priv;
+
+struct phy_msm8996_desc {
+	struct phy	*phy;
+	unsigned int	index;
+	struct	reset_control *phy_rstc;
+	struct phy_msm8996_priv *priv;
+};
+
+struct phy_msm8996_priv {
+	void __iomem *base;
+	struct clk *cfg_clk;
+	struct clk *aux_clk;
+	struct clk *ref_clk;
+	struct clk *ref_clk_src;
+	struct	reset_control *phy_rstc, *phycom_rstc;
+	struct device *dev;
+	unsigned int	nphys;
+	int		init_count;
+	struct mutex	phy_mutex;
+	struct phy_msm8996_desc	**phys;
+};
+
+static struct phy *phy_msm8996_pcie_phy_xlate(struct device *dev,
+					     struct of_phandle_args *args)
+{
+	struct phy_msm8996_priv *priv = dev_get_drvdata(dev);
+	int i;
+
+	if (WARN_ON(args->args[0] >= priv->nphys))
+		return ERR_PTR(-ENODEV);
+
+	for (i = 0; i < priv->nphys; i++) {
+		if (priv->phys[i]->index == args->args[0])
+			break;
+	}
+
+	if (i == priv->nphys)
+		return ERR_PTR(-ENODEV);
+
+	return priv->phys[i]->phy;
+}
+
+static int pcie_phy_is_ready(struct phy *phy)
+{
+	struct phy_msm8996_desc *phydesc = phy_get_drvdata(phy);
+	struct phy_msm8996_priv *priv = phydesc->priv;
+	void __iomem *base = priv->base;
+	int retries = 0;
+
+	do {
+		if ((readl_relaxed(base + PCIE_COM_PCS_READY_STATUS) & 0x1))
+			return 0;
+		retries++;
+		usleep_range(REFCLK_STABILIZATION_DELAY_US_MIN,
+					 REFCLK_STABILIZATION_DELAY_US_MAX);
+	} while (retries < PHY_READY_TIMEOUT_COUNT);
+
+	dev_err(priv->dev, "PHY Failed to come up\n");
+
+	return -EBUSY;
+}
+
+static int qcom_msm8996_phy_common_power_off(struct phy *phy)
+{
+	struct phy_msm8996_desc *phydesc = phy_get_drvdata(phy);
+	struct phy_msm8996_priv *priv = phydesc->priv;
+	void __iomem *base = priv->base;
+
+	mutex_lock(&priv->phy_mutex);
+	if (--priv->init_count) {
+		mutex_unlock(&priv->phy_mutex);
+		return 0;
+	}
+
+	writel_relaxed(0x01, base + PCIE_COM_SW_RESET);
+	writel_relaxed(0x0, base + PCIE_COM_POWER_DOWN_CONTROL);
+
+	reset_control_assert(priv->phy_rstc);
+	reset_control_assert(priv->phycom_rstc);
+	clk_disable_unprepare(priv->cfg_clk);
+	clk_disable_unprepare(priv->aux_clk);
+	clk_disable_unprepare(priv->ref_clk);
+	clk_disable_unprepare(priv->ref_clk_src);
+
+	mutex_unlock(&priv->phy_mutex);
+
+	return 0;
+}
+
+static int qcom_msm8996_phy_common_power_on(struct phy *phy)
+{
+	struct phy_msm8996_desc *phydesc = phy_get_drvdata(phy);
+	struct phy_msm8996_priv *priv = phydesc->priv;
+	void __iomem *base = priv->base;
+	int ret;
+
+	mutex_lock(&priv->phy_mutex);
+	if (priv->init_count++) {
+		mutex_unlock(&priv->phy_mutex);
+		return 0;
+	}
+
+	clk_prepare_enable(priv->cfg_clk);
+	clk_prepare_enable(priv->aux_clk);
+	clk_prepare_enable(priv->ref_clk);
+	clk_prepare_enable(priv->ref_clk_src);
+
+	reset_control_deassert(priv->phy_rstc);
+	reset_control_deassert(priv->phycom_rstc);
+
+	writel_relaxed(0x01, base + PCIE_COM_POWER_DOWN_CONTROL);
+	writel_relaxed(0x1c, base + QSERDES_COM_BIAS_EN_CLKBUFLR_EN);
+	writel_relaxed(0x10, base + QSERDES_COM_CLK_ENABLE1);
+	writel_relaxed(0x33, base + QSERDES_COM_CLK_SELECT);
+	writel_relaxed(0x06, base + QSERDES_COM_CMN_CONFIG);
+	writel_relaxed(0x42, base + QSERDES_COM_LOCK_CMP_EN);
+	writel_relaxed(0x00, base + QSERDES_COM_VCO_TUNE_MAP);
+	writel_relaxed(0xff, base + QSERDES_COM_VCO_TUNE_TIMER1);
+	writel_relaxed(0x1f, base + QSERDES_COM_VCO_TUNE_TIMER2);
+	writel_relaxed(0x01, base + QSERDES_COM_HSCLK_SEL);
+	writel_relaxed(0x01, base + QSERDES_COM_SVS_MODE_CLK_SEL);
+	writel_relaxed(0x00, base + QSERDES_COM_CORE_CLK_EN);
+	writel_relaxed(0x0a, base + QSERDES_COM_CORECLK_DIV);
+	writel_relaxed(0x09, base + QSERDES_COM_BG_TIMER);
+	writel_relaxed(0x82, base + QSERDES_COM_DEC_START_MODE0);
+	writel_relaxed(0x03, base + QSERDES_COM_DIV_FRAC_START3_MODE0);
+	writel_relaxed(0x55, base + QSERDES_COM_DIV_FRAC_START2_MODE0);
+	writel_relaxed(0x55, base + QSERDES_COM_DIV_FRAC_START1_MODE0);
+	writel_relaxed(0x00, base + QSERDES_COM_LOCK_CMP3_MODE0);
+	writel_relaxed(0x1a, base + QSERDES_COM_LOCK_CMP2_MODE0);
+	writel_relaxed(0x0a, base + QSERDES_COM_LOCK_CMP1_MODE0);
+	writel_relaxed(0x33, base + QSERDES_COM_CLK_SELECT);
+	writel_relaxed(0x02, base + QSERDES_COM_SYS_CLK_CTRL);
+	writel_relaxed(0x1f, base + QSERDES_COM_SYSCLK_BUF_ENABLE);
+	writel_relaxed(0x04, base + QSERDES_COM_SYSCLK_EN_SEL);
+	writel_relaxed(0x0b, base + QSERDES_COM_CP_CTRL_MODE0);
+	writel_relaxed(0x16, base + QSERDES_COM_PLL_RCTRL_MODE0);
+	writel_relaxed(0x28, base + QSERDES_COM_PLL_CCTRL_MODE0);
+	writel_relaxed(0x00, base + QSERDES_COM_INTEGLOOP_GAIN1_MODE0);
+	writel_relaxed(0x80, base + QSERDES_COM_INTEGLOOP_GAIN0_MODE0);
+	writel_relaxed(0x01, base + QSERDES_COM_SSC_EN_CENTER);
+	writel_relaxed(0x31, base + QSERDES_COM_SSC_PER1);
+	writel_relaxed(0x01, base + QSERDES_COM_SSC_PER2);
+	writel_relaxed(0x02, base + QSERDES_COM_SSC_ADJ_PER1);
+	writel_relaxed(0x00, base + QSERDES_COM_SSC_ADJ_PER2);
+	writel_relaxed(0x2f, base + QSERDES_COM_SSC_STEP_SIZE1);
+	writel_relaxed(0x19, base + QSERDES_COM_SSC_STEP_SIZE2);
+	writel_relaxed(0x15, base + QSERDES_COM_RESCODE_DIV_NUM);
+	writel_relaxed(0x0f, base + QSERDES_COM_BG_TRIM);
+	writel_relaxed(0x0f, base + QSERDES_COM_PLL_IVCO);
+	writel_relaxed(0x19, base + QSERDES_COM_CLK_EP_DIV);
+	writel_relaxed(0x10, base + QSERDES_COM_CLK_ENABLE1);
+	writel_relaxed(0x00, base + QSERDES_COM_HSCLK_SEL);
+	writel_relaxed(0x40, base + QSERDES_COM_RESCODE_DIV_NUM);
+	writel_relaxed(0x00, base + PCIE_COM_SW_RESET);
+	writel_relaxed(0x03, base + PCIE_COM_START_CONTROL);
+
+	ret = pcie_phy_is_ready(phy);
+
+	mutex_unlock(&priv->phy_mutex);
+
+	return ret;
+}
+
+static int qcom_msm8996_pcie_phy_power_on(struct phy *phy)
+{
+	struct phy_msm8996_desc *phydesc = phy_get_drvdata(phy);
+	struct phy_msm8996_priv *priv = phydesc->priv;
+	void __iomem *base = priv->base;
+	int id = phydesc->index;
+	int err;
+
+	err = qcom_msm8996_phy_common_power_on(phy);
+	if (err) {
+		dev_err(priv->dev, "PCIE phy power on failed\n");
+		return err;
+	}
+
+	reset_control_deassert(phydesc->phy_rstc);
+
+	writel_relaxed(0x45, base +
+		       QSERDES_TX_N_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN(id));
+	writel_relaxed(0x06, base + QSERDES_TX_N_LANE_MODE(id));
+	writel_relaxed(0x1c, base + QSERDES_RX_N_SIGDET_ENABLES(id));
+	writel_relaxed(0x17, base + QSERDES_RX_N_SIGDET_LVL(id));
+	writel_relaxed(0x01, base + QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL2(id));
+	writel_relaxed(0x00, base + QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL3(id));
+	writel_relaxed(0xdb, base + QSERDES_RX_N_RX_EQU_ADAPTOR_CNTRL4(id));
+	writel_relaxed(0x18, base + QSERDES_RX_N_RX_BAND(id));
+	writel_relaxed(0x04, base + QSERDES_RX_N_UCDR_SO_GAIN(id));
+	writel_relaxed(0x04, base + QSERDES_RX_N_UCDR_SO_GAIN_HALF(id));
+	writel_relaxed(0x4c, base + PCIE_N_RX_IDLE_DTCT_CNTRL(id));
+	writel_relaxed(0x00, base + PCIE_N_PWRUP_RESET_DLY_TIME_AUXCLK(id));
+	writel_relaxed(0x01, base + PCIE_N_LP_WAKEUP_DLY_TIME_AUXCLK(id));
+	writel_relaxed(0x05, base + PCIE_N_PLL_LOCK_CHK_DLY_TIME(id));
+	writel_relaxed(0x4b, base +
+		       QSERDES_RX_N_UCDR_SO_SATURATION_AND_ENABLE(id));
+	writel_relaxed(0x14, base + QSERDES_RX_N_SIGDET_DEGLITCH_CNTRL(id));
+	writel_relaxed(0x05, base + PCIE_N_ENDPOINT_REFCLK_DRIVE(id));
+	writel_relaxed(0x02, base + PCIE_N_POWER_DOWN_CONTROL(id));
+	writel_relaxed(0x00, base + PCIE_N_POWER_STATE_CONFIG4(id));
+	writel_relaxed(0xa3, base + PCIE_N_POWER_STATE_CONFIG1(id));
+	writel_relaxed(0x19, base + QSERDES_RX_N_SIGDET_LVL(id));
+	writel_relaxed(0x0e, base + PCIE_N_TXDEEMPH_M3P5DB_V0(id));
+	writel_relaxed(0x03, base + PCIE_N_POWER_DOWN_CONTROL(id));
+
+	usleep_range(POWER_DOWN_DELAY_US_MIN, POWER_DOWN_DELAY_US_MAX);
+
+	writel_relaxed(0x00, base + PCIE_N_SW_RESET(id));
+	writel_relaxed(0x0a, base + PCIE_N_START_CONTROL(id));
+
+	return 0;
+}
+
+static int qcom_msm8996_pcie_phy_power_off(struct phy *phy)
+{
+	struct phy_msm8996_desc *phydesc = phy_get_drvdata(phy);
+	struct phy_msm8996_priv *priv = phydesc->priv;
+	void __iomem *base = priv->base;
+	int id = phydesc->index;
+	int err;
+
+	writel_relaxed(0x01, base + PCIE_N_SW_RESET(id));
+	writel_relaxed(0x0, base + PCIE_N_POWER_DOWN_CONTROL(id));
+
+	err = qcom_msm8996_phy_common_power_off(phy);
+	if (err < 0)
+		return err;
+
+	reset_control_assert(phydesc->phy_rstc);
+
+	return err;
+}
+
+static const struct phy_ops qcom_msm8996_pcie_phy_ops = {
+	.power_on	= qcom_msm8996_pcie_phy_power_on,
+	.power_off	= qcom_msm8996_pcie_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+
+static int qcom_msm8996_pcie_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *child;
+	struct phy *phy;
+	struct phy_provider *phy_provider;
+	struct phy_msm8996_priv *priv;
+	struct resource *res;
+	int ret;
+	u32 phy_id;
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res)
+		return -EINVAL;
+
+	priv->base = devm_ioremap(dev, res->start, resource_size(res));
+	if (!priv->base)
+		return -ENOMEM;
+
+	priv->cfg_clk = devm_clk_get(dev, "cfg");
+	if (IS_ERR(priv->cfg_clk))
+		return PTR_ERR(priv->cfg_clk);
+
+	priv->aux_clk = devm_clk_get(dev, "aux");
+	if (IS_ERR(priv->aux_clk))
+		return PTR_ERR(priv->aux_clk);
+
+	priv->ref_clk = devm_clk_get(dev, "ref_clk");
+	if (IS_ERR(priv->ref_clk))
+		return PTR_ERR(priv->ref_clk);
+
+	priv->ref_clk_src = devm_clk_get(dev, "ref_clk_src");
+	if (IS_ERR(priv->ref_clk_src))
+		return PTR_ERR(priv->ref_clk_src);
+
+	priv->nphys = of_get_child_count(dev->of_node);
+	if (priv->nphys == 0)
+		return -ENODEV;
+
+	priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys),
+				  GFP_KERNEL);
+	if (!priv->phys)
+		return -ENOMEM;
+
+	priv->phy_rstc = reset_control_get(dev, "phy");
+	if (IS_ERR(priv->phy_rstc))
+		return PTR_ERR(priv->phy_rstc);
+
+	priv->phycom_rstc = reset_control_get(dev, "common");
+	if (IS_ERR(priv->phycom_rstc))
+		return PTR_ERR(priv->phycom_rstc);
+
+	mutex_init(&priv->phy_mutex);
+
+	dev_set_drvdata(dev, priv);
+
+	for_each_available_child_of_node(dev->of_node, child) {
+		struct phy_msm8996_desc *phy_desc;
+
+		if (of_property_read_u32(child, "reg", &phy_id)) {
+			dev_err(dev, "missing reg property in node %s\n",
+				child->name);
+			ret = -EINVAL;
+			goto put_child;
+		}
+
+		phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+		if (!phy_desc) {
+			ret = -ENOMEM;
+			goto put_child;
+		}
+
+		phy = devm_phy_create(dev, NULL, &qcom_msm8996_pcie_phy_ops);
+		if (IS_ERR(phy)) {
+			dev_err(dev, "failed to create PHY %d\n", phy_id);
+			ret = PTR_ERR(phy);
+			goto put_child;
+		}
+
+		phy_desc->phy_rstc = of_reset_control_get(child, "phy");
+		if (IS_ERR(phy_desc->phy_rstc)) {
+			ret = PTR_ERR(phy_desc->phy_rstc);
+			goto put_child;
+		}
+
+		phy_desc->phy = phy;
+		phy_desc->priv = priv;
+		phy_desc->index = phy_id;
+		phy_set_drvdata(phy, phy_desc);
+		priv->phys[phy_id] = phy_desc;
+	}
+
+	phy_provider =
+		devm_of_phy_provider_register(dev, phy_msm8996_pcie_phy_xlate);
+
+	return PTR_ERR_OR_ZERO(phy_provider);
+
+put_child:
+	of_node_put(child);
+	return ret;
+}
+
+static const struct of_device_id qcom_msm8996_pcie_phy_of_match[] = {
+	{ .compatible = "qcom,msm8996-pcie-phy" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, qcom_msm8996_pcie_phy_of_match);
+
+static struct platform_driver qcom_msm8996_pcie_phy_driver = {
+	.probe	= qcom_msm8996_pcie_phy_probe,
+	.driver = {
+		.name	= "qcom-msm8996-pcie-phy",
+		.of_match_table	= qcom_msm8996_pcie_phy_of_match,
+	}
+};
+module_platform_driver(qcom_msm8996_pcie_phy_driver);
+
+MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@st.com>");
+MODULE_DESCRIPTION("QCOM MSM899 PCIE PHY driver");
+MODULE_LICENSE("GPL v2");
-- 
2.7.4

  parent reply	other threads:[~2016-09-07 10:55 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-09-07 10:55 [PATCH 0/2] phy: Add support msm8996/apq8096 pcie phy Srinivas Kandagatla
2016-09-07 10:55 ` [PATCH 1/2] dt-bindings: msm8996-pcie-phy: add support for msm8996 " Srinivas Kandagatla
     [not found]   ` <1473245733-17260-2-git-send-email-srinivas.kandagatla-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2016-09-12 22:51     ` Stephen Boyd
2016-09-12 22:51       ` Stephen Boyd
2016-09-16 16:51       ` Srinivas Kandagatla
2016-09-16 13:52     ` Rob Herring
2016-09-16 13:52       ` Rob Herring
2016-09-16 16:45       ` Srinivas Kandagatla
2016-09-07 10:55 ` Srinivas Kandagatla [this message]
2016-09-13 16:06   ` [PATCH 2/2] phy: msm8996-pcie-phy: Add support to " Archit Taneja
     [not found]     ` <57D82403.9080004-sgV2jX0FEOL9JmXXK+q4OQ@public.gmane.org>
2016-09-16 16:48       ` Srinivas Kandagatla
2016-09-16 16:48         ` Srinivas Kandagatla
2016-09-16 14:14   ` Stanimir Varbanov
2016-09-16 16:49     ` Srinivas Kandagatla

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=1473245733-17260-3-git-send-email-srinivas.kandagatla@linaro.org \
    --to=srinivas.kandagatla@linaro.org \
    --cc=devicetree@vger.kernel.org \
    --cc=kishon@ti.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mark.rutland@arm.com \
    --cc=robh+dt@kernel.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.