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