[RFC PATCH 2/2] PCI: exynos: Add PCIe support for Samsung GH7 SoC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Samsung GH7 has four PCIe controllers which can be used as root
complex for PCIe interface.

Signed-off-by: Jingoo Han <jg1.han@xxxxxxxxxxx>
---
 drivers/pci/host/Kconfig      |    2 +-
 drivers/pci/host/pci-exynos.c |  135 ++++++++++++++++++++++++++++++++++++++---
 2 files changed, 126 insertions(+), 11 deletions(-)

diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index a6f67ec..3be047c 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -11,7 +11,7 @@ config PCIE_DW
 
 config PCI_EXYNOS
 	bool "Samsung Exynos PCIe controller"
-	depends on SOC_EXYNOS5440
+	depends on SOC_EXYNOS5440 || ARCH_GH7
 	select PCIEPORTBUS
 	select PCIE_DW
 
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 3de6bfb..6e845ca 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -57,6 +57,8 @@ struct exynos_pcie {
 #define PCIE_NONSTICKY_RESET		0x024
 #define PCIE_APP_INIT_RESET		0x028
 #define PCIE_APP_LTSSM_ENABLE		0x02c
+#define PCIE_SYS_AUX_PWR_DET		0x038
+#define PCIE_SYS_AUX_PWR_ENABLE		(0x1 << 0)
 #define PCIE_ELBI_RDLH_LINKUP		0x064
 #define PCIE_ELBI_LTSSM_ENABLE		0x1
 #define PCIE_ELBI_SLV_AWMISC		0x11c
@@ -72,6 +74,23 @@ struct exynos_pcie {
 #define PCIE_PHY_TRSVREG_RESET		0x020
 #define PCIE_PHY_TRSV_RESET		0x024
 
+/* PCIe PHY glue registers */
+#define PCIE_PHY_GLUE_REG0		0x000
+#define PCIE_PHY_GLUE_GLOBAL_RESET	(0x1 << 0)
+#define PCIE_PHY_GLUE_COMMON_RESET	(0x1 << 1)
+#define PCIE_PHY_GLUE_MAC_RESET		(0x1 << 11)
+#define PCIE_PHY_GLUE_REG2		0x008
+#define PCIE_PHY_GLUE_CLK100M_DS_MAX	(0x7 << 0)
+#define PCIE_PHY_GLUE_CLK100M_RFCLK	(0x1 << 3)
+#define PCIE_PHY_GLUE_CLK100M_ENABLE	(0x1 << 4)
+#define PCIE_PHY_GLUE_PLL_BUF_ENABLE	(0x1 << 8)
+#define PCIE_PHY_GLUE_PLL_DIV_ENABLE	(0x1 << 9)
+#define PCIE_PHY_GLUE_REFCLK_IN(x)	(((x) & 0xf) << 10)
+#define PCIE_PHY_GLUE_REG3		0x00c
+#define PCIE_PHY_GLUE_REF_RATE_100MHZ	(0x2 << 0)
+#define PCIE_PHY_GLUE_REG4		0x010
+#define PCIE_PHY_GLUE_MODE_PCIE		(0x0 << 0)
+
 /* PCIe PHY registers */
 #define PCIE_PHY_IMPEDANCE		0x004
 #define PCIE_PHY_PLL_DIV_0		0x008
@@ -164,11 +183,45 @@ static void exynos_pcie_sideband_dbi_r_mode(struct pcie_port *pp, bool on)
 	}
 }
 
+static void exynos_pcie_set_phy_mode(struct pcie_port *pp)
+{
+	u32 val;
+	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+	if (!of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie"))
+		return;
+
+	/* configure phy reference clock setting */
+	val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG2);
+	val |= PCIE_PHY_GLUE_CLK100M_ENABLE | PCIE_PHY_GLUE_CLK100M_RFCLK |
+		PCIE_PHY_GLUE_CLK100M_DS_MAX;
+	exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG2);
+
+	val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG2);
+	val |= PCIE_PHY_GLUE_PLL_DIV_ENABLE | PCIE_PHY_GLUE_PLL_BUF_ENABLE;
+	exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG2);
+
+	val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG2);
+	val |= PCIE_PHY_GLUE_REFCLK_IN(6);
+	exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG2);
+
+	/* set phy reference clock  */
+	exynos_blk_writel(exynos_pcie, PCIE_PHY_GLUE_REF_RATE_100MHZ,
+			  PCIE_PHY_GLUE_REG3);
+
+	/* set phy mode to pcie mode */
+	exynos_blk_writel(exynos_pcie, PCIE_PHY_GLUE_MODE_PCIE,
+			  PCIE_PHY_GLUE_REG4);
+}
+
 static void exynos_pcie_assert_core_reset(struct pcie_port *pp)
 {
 	u32 val;
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie"))
+		return;
+
 	val = exynos_elb_readl(exynos_pcie, PCIE_CORE_RESET);
 	val &= ~PCIE_CORE_RESET_ENABLE;
 	exynos_elb_writel(exynos_pcie, val, PCIE_CORE_RESET);
@@ -190,27 +243,48 @@ static void exynos_pcie_deassert_core_reset(struct pcie_port *pp)
 	exynos_elb_writel(exynos_pcie, 1, PCIE_NONSTICKY_RESET);
 	exynos_elb_writel(exynos_pcie, 1, PCIE_APP_INIT_RESET);
 	exynos_elb_writel(exynos_pcie, 0, PCIE_APP_INIT_RESET);
-	exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET);
+
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie")) {
+		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG0);
+		val |= PCIE_PHY_GLUE_MAC_RESET;
+		exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG0);
+	} else {
+		exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_MAC_RESET);
+	}
 }
 
 static void exynos_pcie_assert_phy_reset(struct pcie_port *pp)
 {
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie"))
+		return;
+
 	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_MAC_RESET);
 	exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_GLOBAL_RESET);
 }
 
 static void exynos_pcie_deassert_phy_reset(struct pcie_port *pp)
 {
+	u32 val;
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
-	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET);
-	exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
-	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
-	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_CMN_REG);
-	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSVREG_RESET);
-	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET);
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie")) {
+		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG0);
+		val |= PCIE_PHY_GLUE_GLOBAL_RESET;
+		exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG0);
+		exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
+		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG0);
+		val |= PCIE_PHY_GLUE_COMMON_RESET;
+		exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG0);
+	} else {
+		exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_GLOBAL_RESET);
+		exynos_elb_writel(exynos_pcie, 1, PCIE_PWR_RESET);
+		exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
+		exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_CMN_REG);
+		exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSVREG_RESET);
+		exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_TRSV_RESET);
+	}
 }
 
 static void exynos_pcie_power_on_phy(struct pcie_port *pp)
@@ -269,6 +343,9 @@ static void exynos_pcie_init_phy(struct pcie_port *pp)
 {
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie"))
+		return;
+
 	/* DCC feedback control off */
 	exynos_phy_writel(exynos_pcie, 0x29, PCIE_PHY_DCC_FEEDBACK);
 
@@ -305,6 +382,26 @@ static void exynos_pcie_init_phy(struct pcie_port *pp)
 	exynos_phy_writel(exynos_pcie, 0xa0, PCIE_PHY_TRSV3_LVCC);
 }
 
+static void exynos_pcie_pulse_common_reset(struct pcie_port *pp)
+{
+	u32 val;
+	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
+
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie")) {
+		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG0);
+		val &= ~PCIE_PHY_GLUE_COMMON_RESET;
+		exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG0);
+		udelay(500);
+		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_GLUE_REG0);
+		val |= PCIE_PHY_GLUE_COMMON_RESET;
+		exynos_blk_writel(exynos_pcie, val, PCIE_PHY_GLUE_REG0);
+	} else {
+		exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET);
+		udelay(500);
+		exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
+	}
+}
+
 static void exynos_pcie_assert_reset(struct pcie_port *pp)
 {
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
@@ -326,6 +423,9 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
 		return 0;
 	}
 
+	/* set phy mode */
+	exynos_pcie_set_phy_mode(pp);
+
 	/* assert reset signals */
 	exynos_pcie_assert_core_reset(pp);
 	exynos_pcie_assert_phy_reset(pp);
@@ -340,9 +440,7 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
 	exynos_pcie_init_phy(pp);
 
 	/* pulse for common reset */
-	exynos_blk_writel(exynos_pcie, 1, PCIE_PHY_COMMON_RESET);
-	udelay(500);
-	exynos_blk_writel(exynos_pcie, 0, PCIE_PHY_COMMON_RESET);
+	exynos_pcie_pulse_common_reset(pp);
 
 	/* de-assert core reset */
 	exynos_pcie_deassert_core_reset(pp);
@@ -357,6 +455,12 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
 	exynos_elb_writel(exynos_pcie, PCIE_ELBI_LTSSM_ENABLE,
 			  PCIE_APP_LTSSM_ENABLE);
 
+	if (of_device_is_compatible(pp->dev->of_node, "samsung,gh7-pcie")) {
+		/* supply auxiliary power */
+		exynos_elb_writel(exynos_pcie, PCIE_SYS_AUX_PWR_ENABLE,
+				  PCIE_SYS_AUX_PWR_DET);
+	}
+
 	/* check if the link is up or not */
 	while (!dw_pcie_link_up(pp)) {
 		mdelay(100);
@@ -564,6 +668,7 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
 	struct resource *elbi_base;
 	struct resource *phy_base;
 	struct resource *block_base;
+	 struct resource *dbi_base;
 	int ret;
 
 	exynos_pcie = devm_kzalloc(&pdev->dev, sizeof(*exynos_pcie),
@@ -619,6 +724,15 @@ static int __init exynos_pcie_probe(struct platform_device *pdev)
 		goto fail_bus_clk;
 	}
 
+	if (of_device_is_compatible(pdev->dev.of_node, "samsung,gh7-pcie")) {
+		dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 3);
+		pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
+		if (IS_ERR(pp->dbi_base)) {
+			ret = PTR_ERR(pp->dbi_base);
+			goto fail_bus_clk;
+		}
+	}
+
 	ret = add_pcie_port(pp, pdev);
 	if (ret < 0)
 		goto fail_bus_clk;
@@ -645,6 +759,7 @@ static int __exit exynos_pcie_remove(struct platform_device *pdev)
 
 static const struct of_device_id exynos_pcie_of_match[] = {
 	{ .compatible = "samsung,exynos5440-pcie", },
+	{ .compatible = "samsung,gh7-pcie", },
 	{},
 };
 MODULE_DEVICE_TABLE(of, exynos_pcie_of_match);
-- 
1.7.10.4


--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux