[RFC PATCH 2/2] PCI: exynos: Add PCIe support for Samsung GH7 SoC
From: Jingoo Han
Date: Wed Apr 16 2014 - 00:43:41 EST
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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/