aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2020-05-22 10:28:38 -0400
committerTom Rini <trini@konsulko.com>2020-05-22 10:28:38 -0400
commitf53c2dc162d0c62debd0ebb88383e3b6fee95c10 (patch)
treea2705fe5047277b8df937fbad7895b7cc7a2bd6e /drivers
parenta9f793a30dc091290dddae1d8d3798d25e39c710 (diff)
parent33863f744d513f5c16a254870e7b3cef8580bbc9 (diff)
downloadu-boot-f53c2dc162d0c62debd0ebb88383e3b6fee95c10.zip
u-boot-f53c2dc162d0c62debd0ebb88383e3b6fee95c10.tar.gz
u-boot-f53c2dc162d0c62debd0ebb88383e3b6fee95c10.tar.bz2
Merge tag 'u-boot-rockchip-20200522' of https://gitlab.denx.de/u-boot/custodians/u-boot-rockchip
- Fix rk3288 chromebook veyron support; - Add pcie driver support for rk3399; - other fixes for rk3399 boards
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/rockchip/clk_rk3399.c154
-rw-r--r--drivers/pci/Kconfig8
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/pcie_rockchip.c491
-rw-r--r--drivers/pci/pcie_rockchip.h142
-rw-r--r--drivers/pci/pcie_rockchip_phy.c205
6 files changed, 1001 insertions, 0 deletions
diff --git a/drivers/clk/rockchip/clk_rk3399.c b/drivers/clk/rockchip/clk_rk3399.c
index e009f1c..6a78837 100644
--- a/drivers/clk/rockchip/clk_rk3399.c
+++ b/drivers/clk/rockchip/clk_rk3399.c
@@ -1074,12 +1074,166 @@ static int __maybe_unused rk3399_clk_set_parent(struct clk *clk,
return -ENOENT;
}
+static int rk3399_clk_enable(struct clk *clk)
+{
+ struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case SCLK_MAC:
+ rk_clrreg(&priv->cru->clkgate_con[5], BIT(5));
+ break;
+ case SCLK_MAC_RX:
+ rk_clrreg(&priv->cru->clkgate_con[5], BIT(8));
+ break;
+ case SCLK_MAC_TX:
+ rk_clrreg(&priv->cru->clkgate_con[5], BIT(9));
+ break;
+ case SCLK_MACREF:
+ rk_clrreg(&priv->cru->clkgate_con[5], BIT(7));
+ break;
+ case SCLK_MACREF_OUT:
+ rk_clrreg(&priv->cru->clkgate_con[5], BIT(6));
+ break;
+ case ACLK_GMAC:
+ rk_clrreg(&priv->cru->clkgate_con[32], BIT(0));
+ break;
+ case PCLK_GMAC:
+ rk_clrreg(&priv->cru->clkgate_con[32], BIT(2));
+ break;
+ case SCLK_USB3OTG0_REF:
+ rk_clrreg(&priv->cru->clkgate_con[12], BIT(1));
+ break;
+ case SCLK_USB3OTG1_REF:
+ rk_clrreg(&priv->cru->clkgate_con[12], BIT(2));
+ break;
+ case SCLK_USB3OTG0_SUSPEND:
+ rk_clrreg(&priv->cru->clkgate_con[12], BIT(3));
+ break;
+ case SCLK_USB3OTG1_SUSPEND:
+ rk_clrreg(&priv->cru->clkgate_con[12], BIT(4));
+ break;
+ case ACLK_USB3OTG0:
+ rk_clrreg(&priv->cru->clkgate_con[30], BIT(1));
+ break;
+ case ACLK_USB3OTG1:
+ rk_clrreg(&priv->cru->clkgate_con[30], BIT(2));
+ break;
+ case ACLK_USB3_RKSOC_AXI_PERF:
+ rk_clrreg(&priv->cru->clkgate_con[30], BIT(3));
+ break;
+ case ACLK_USB3:
+ rk_clrreg(&priv->cru->clkgate_con[12], BIT(0));
+ break;
+ case ACLK_USB3_GRF:
+ rk_clrreg(&priv->cru->clkgate_con[30], BIT(4));
+ break;
+ case HCLK_HOST0:
+ rk_clrreg(&priv->cru->clksel_con[20], BIT(5));
+ break;
+ case HCLK_HOST0_ARB:
+ rk_clrreg(&priv->cru->clksel_con[20], BIT(6));
+ break;
+ case HCLK_HOST1:
+ rk_clrreg(&priv->cru->clksel_con[20], BIT(7));
+ break;
+ case HCLK_HOST1_ARB:
+ rk_clrreg(&priv->cru->clksel_con[20], BIT(8));
+ break;
+ case SCLK_PCIEPHY_REF:
+ rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
+ break;
+ default:
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static int rk3399_clk_disable(struct clk *clk)
+{
+ struct rk3399_clk_priv *priv = dev_get_priv(clk->dev);
+
+ switch (clk->id) {
+ case SCLK_MAC:
+ rk_setreg(&priv->cru->clkgate_con[5], BIT(5));
+ break;
+ case SCLK_MAC_RX:
+ rk_setreg(&priv->cru->clkgate_con[5], BIT(8));
+ break;
+ case SCLK_MAC_TX:
+ rk_setreg(&priv->cru->clkgate_con[5], BIT(9));
+ break;
+ case SCLK_MACREF:
+ rk_setreg(&priv->cru->clkgate_con[5], BIT(7));
+ break;
+ case SCLK_MACREF_OUT:
+ rk_setreg(&priv->cru->clkgate_con[5], BIT(6));
+ break;
+ case ACLK_GMAC:
+ rk_setreg(&priv->cru->clkgate_con[32], BIT(0));
+ break;
+ case PCLK_GMAC:
+ rk_setreg(&priv->cru->clkgate_con[32], BIT(2));
+ break;
+ case SCLK_USB3OTG0_REF:
+ rk_setreg(&priv->cru->clkgate_con[12], BIT(1));
+ break;
+ case SCLK_USB3OTG1_REF:
+ rk_setreg(&priv->cru->clkgate_con[12], BIT(2));
+ break;
+ case SCLK_USB3OTG0_SUSPEND:
+ rk_setreg(&priv->cru->clkgate_con[12], BIT(3));
+ break;
+ case SCLK_USB3OTG1_SUSPEND:
+ rk_setreg(&priv->cru->clkgate_con[12], BIT(4));
+ break;
+ case ACLK_USB3OTG0:
+ rk_setreg(&priv->cru->clkgate_con[30], BIT(1));
+ break;
+ case ACLK_USB3OTG1:
+ rk_setreg(&priv->cru->clkgate_con[30], BIT(2));
+ break;
+ case ACLK_USB3_RKSOC_AXI_PERF:
+ rk_setreg(&priv->cru->clkgate_con[30], BIT(3));
+ break;
+ case ACLK_USB3:
+ rk_setreg(&priv->cru->clkgate_con[12], BIT(0));
+ break;
+ case ACLK_USB3_GRF:
+ rk_setreg(&priv->cru->clkgate_con[30], BIT(4));
+ break;
+ case HCLK_HOST0:
+ rk_setreg(&priv->cru->clksel_con[20], BIT(5));
+ break;
+ case HCLK_HOST0_ARB:
+ rk_setreg(&priv->cru->clksel_con[20], BIT(6));
+ break;
+ case HCLK_HOST1:
+ rk_setreg(&priv->cru->clksel_con[20], BIT(7));
+ break;
+ case HCLK_HOST1_ARB:
+ rk_setreg(&priv->cru->clksel_con[20], BIT(8));
+ break;
+ case SCLK_PCIEPHY_REF:
+ rk_clrreg(&priv->cru->clksel_con[18], BIT(10));
+ break;
+ default:
+ debug("%s: unsupported clk %ld\n", __func__, clk->id);
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
static struct clk_ops rk3399_clk_ops = {
.get_rate = rk3399_clk_get_rate,
.set_rate = rk3399_clk_set_rate,
#if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA)
.set_parent = rk3399_clk_set_parent,
#endif
+ .enable = rk3399_clk_enable,
+ .disable = rk3399_clk_disable,
};
#ifdef CONFIG_SPL_BUILD
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 6fc2688..6d8c22a 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -197,4 +197,12 @@ config PCIE_MEDIATEK
Say Y here if you want to enable Gen2 PCIe controller,
which could be found on MT7623 SoC family.
+config PCIE_ROCKCHIP
+ bool "Enable Rockchip PCIe driver"
+ select DM_PCI
+ default y if ROCKCHIP_RK3399
+ help
+ Say Y here if you want to enable PCIe controller support on
+ Rockchip SoCs.
+
endif
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index c051ecc..955351c 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -43,3 +43,4 @@ obj-$(CONFIG_PCI_PHYTIUM) += pcie_phytium.o
obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o
obj-$(CONFIG_PCI_KEYSTONE) += pcie_dw_ti.o
obj-$(CONFIG_PCIE_MEDIATEK) += pcie_mediatek.o
+obj-$(CONFIG_PCIE_ROCKCHIP) += pcie_rockchip.o pcie_rockchip_phy.o
diff --git a/drivers/pci/pcie_rockchip.c b/drivers/pci/pcie_rockchip.c
new file mode 100644
index 0000000..82a8396
--- /dev/null
+++ b/drivers/pci/pcie_rockchip.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip AXI PCIe host controller driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ * Copyright (c) 2020 Amarula Solutions(India)
+ * Copyright (c) 2020 Jagan Teki <jagan@amarulasolutions.com>
+ * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
+ * Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
+ *
+ * Bits taken from Linux Rockchip PCIe host controller.
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <pci.h>
+#include <power-domain.h>
+#include <power/regulator.h>
+#include <reset.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm-generic/gpio.h>
+#include <asm/arch-rockchip/clock.h>
+#include <linux/iopoll.h>
+
+#include "pcie_rockchip.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int rockchip_pcie_off_conf(pci_dev_t bdf, uint offset)
+{
+ unsigned int bus = PCI_BUS(bdf);
+ unsigned int dev = PCI_DEV(bdf);
+ unsigned int func = PCI_FUNC(bdf);
+
+ return (bus << 20) | (dev << 15) | (func << 12) | (offset & ~0x3);
+}
+
+static int rockchip_pcie_rd_conf(const struct udevice *udev, pci_dev_t bdf,
+ uint offset, ulong *valuep,
+ enum pci_size_t size)
+{
+ struct rockchip_pcie *priv = dev_get_priv(udev);
+ unsigned int bus = PCI_BUS(bdf);
+ unsigned int dev = PCI_DEV(bdf);
+ int where = rockchip_pcie_off_conf(bdf, offset);
+ ulong value;
+
+ if (bus == priv->first_busno && dev == 0) {
+ value = readl(priv->apb_base + PCIE_RC_NORMAL_BASE + where);
+ *valuep = pci_conv_32_to_size(value, offset, size);
+ return 0;
+ }
+
+ if ((bus == priv->first_busno + 1) && dev == 0) {
+ value = readl(priv->axi_base + where);
+ *valuep = pci_conv_32_to_size(value, offset, size);
+ return 0;
+ }
+
+ *valuep = pci_get_ff(size);
+
+ return 0;
+}
+
+static int rockchip_pcie_wr_conf(struct udevice *udev, pci_dev_t bdf,
+ uint offset, ulong value,
+ enum pci_size_t size)
+{
+ struct rockchip_pcie *priv = dev_get_priv(udev);
+ unsigned int bus = PCI_BUS(bdf);
+ unsigned int dev = PCI_DEV(bdf);
+ int where = rockchip_pcie_off_conf(bdf, offset);
+ ulong old;
+
+ if (bus == priv->first_busno && dev == 0) {
+ old = readl(priv->apb_base + PCIE_RC_NORMAL_BASE + where);
+ value = pci_conv_size_to_32(old, value, offset, size);
+ writel(value, priv->apb_base + PCIE_RC_NORMAL_BASE + where);
+ return 0;
+ }
+
+ if ((bus == priv->first_busno + 1) && dev == 0) {
+ old = readl(priv->axi_base + where);
+ value = pci_conv_size_to_32(old, value, offset, size);
+ writel(value, priv->axi_base + where);
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rockchip_pcie_atr_init(struct rockchip_pcie *priv)
+{
+ struct udevice *ctlr = pci_get_controller(priv->dev);
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+ u64 addr, size, offset;
+ u32 type;
+ int i, region;
+
+ /* Use region 0 to map PCI configuration space. */
+ writel(25 - 1, priv->apb_base + PCIE_ATR_OB_ADDR0(0));
+ writel(0, priv->apb_base + PCIE_ATR_OB_ADDR1(0));
+ writel(PCIE_ATR_HDR_CFG_TYPE0 | PCIE_ATR_HDR_RID,
+ priv->apb_base + PCIE_ATR_OB_DESC0(0));
+ writel(0, priv->apb_base + PCIE_ATR_OB_DESC1(0));
+
+ for (i = 0; i < hose->region_count; i++) {
+ if (hose->regions[i].flags == PCI_REGION_SYS_MEMORY)
+ continue;
+
+ if (hose->regions[i].flags == PCI_REGION_IO)
+ type = PCIE_ATR_HDR_IO;
+ else
+ type = PCIE_ATR_HDR_MEM;
+
+ /* Only support identity mappings. */
+ if (hose->regions[i].bus_start !=
+ hose->regions[i].phys_start)
+ return -EINVAL;
+
+ /* Only support mappings aligned on a region boundary. */
+ addr = hose->regions[i].bus_start;
+ if (addr & (PCIE_ATR_OB_REGION_SIZE - 1))
+ return -EINVAL;
+
+ /* Mappings should lie between AXI and APB regions. */
+ size = hose->regions[i].size;
+ if (addr < (u64)priv->axi_base + PCIE_ATR_OB_REGION0_SIZE)
+ return -EINVAL;
+ if (addr + size > (u64)priv->apb_base)
+ return -EINVAL;
+
+ offset = addr - (u64)priv->axi_base - PCIE_ATR_OB_REGION0_SIZE;
+ region = 1 + (offset / PCIE_ATR_OB_REGION_SIZE);
+ while (size > 0) {
+ writel(32 - 1,
+ priv->apb_base + PCIE_ATR_OB_ADDR0(region));
+ writel(0, priv->apb_base + PCIE_ATR_OB_ADDR1(region));
+ writel(type | PCIE_ATR_HDR_RID,
+ priv->apb_base + PCIE_ATR_OB_DESC0(region));
+ writel(0, priv->apb_base + PCIE_ATR_OB_DESC1(region));
+
+ addr += PCIE_ATR_OB_REGION_SIZE;
+ size -= PCIE_ATR_OB_REGION_SIZE;
+ region++;
+ }
+ }
+
+ /* Passthrough inbound translations unmodified. */
+ writel(32 - 1, priv->apb_base + PCIE_ATR_IB_ADDR0(2));
+ writel(0, priv->apb_base + PCIE_ATR_IB_ADDR1(2));
+
+ return 0;
+}
+
+static int rockchip_pcie_init_port(struct udevice *dev)
+{
+ struct rockchip_pcie *priv = dev_get_priv(dev);
+ struct rockchip_pcie_phy *phy = pcie_get_phy(priv);
+ struct rockchip_pcie_phy_ops *ops = phy_get_ops(phy);
+ u32 cr, val, status;
+ int ret;
+
+ if (dm_gpio_is_valid(&priv->ep_gpio))
+ dm_gpio_set_value(&priv->ep_gpio, 0);
+
+ ret = reset_assert(&priv->aclk_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert aclk reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_assert(&priv->pclk_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert pclk reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_assert(&priv->pm_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert pm reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = ops->init(phy);
+ if (ret) {
+ dev_err(dev, "failed to init phy (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ ret = reset_assert(&priv->core_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert core reset (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ ret = reset_assert(&priv->mgmt_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert mgmt reset (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ ret = reset_assert(&priv->mgmt_sticky_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert mgmt-sticky reset (ret=%d)\n",
+ ret);
+ goto err_exit_phy;
+ }
+
+ ret = reset_assert(&priv->pipe_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert pipe reset (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ udelay(10);
+
+ ret = reset_deassert(&priv->pm_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert pm reset (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ ret = reset_deassert(&priv->aclk_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert aclk reset (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ ret = reset_deassert(&priv->pclk_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert pclk reset (ret=%d)\n", ret);
+ goto err_exit_phy;
+ }
+
+ /* Select GEN1 for now */
+ cr = PCIE_CLIENT_GEN_SEL_1;
+ /* Set Root complex mode */
+ cr |= PCIE_CLIENT_CONF_ENABLE | PCIE_CLIENT_MODE_RC;
+ writel(cr, priv->apb_base + PCIE_CLIENT_CONFIG);
+
+ ret = ops->power_on(phy);
+ if (ret) {
+ dev_err(dev, "failed to power on phy (ret=%d)\n", ret);
+ goto err_power_off_phy;
+ }
+
+ ret = reset_deassert(&priv->mgmt_sticky_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert mgmt-sticky reset (ret=%d)\n",
+ ret);
+ goto err_power_off_phy;
+ }
+
+ ret = reset_deassert(&priv->core_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert core reset (ret=%d)\n", ret);
+ goto err_power_off_phy;
+ }
+
+ ret = reset_deassert(&priv->mgmt_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert mgmt reset (ret=%d)\n", ret);
+ goto err_power_off_phy;
+ }
+
+ ret = reset_deassert(&priv->pipe_rst);
+ if (ret) {
+ dev_err(dev, "failed to deassert pipe reset (ret=%d)\n", ret);
+ goto err_power_off_phy;
+ }
+
+ /* Enable Gen1 training */
+ writel(PCIE_CLIENT_LINK_TRAIN_ENABLE,
+ priv->apb_base + PCIE_CLIENT_CONFIG);
+
+ if (dm_gpio_is_valid(&priv->ep_gpio))
+ dm_gpio_set_value(&priv->ep_gpio, 1);
+
+ ret = readl_poll_sleep_timeout
+ (priv->apb_base + PCIE_CLIENT_BASIC_STATUS1,
+ status, PCIE_LINK_UP(status), 20, 500 * 1000);
+ if (ret) {
+ dev_err(dev, "PCIe link training gen1 timeout!\n");
+ goto err_power_off_phy;
+ }
+
+ /* Initialize Root Complex registers. */
+ writel(PCIE_LM_VENDOR_ROCKCHIP, priv->apb_base + PCIE_LM_VENDOR_ID);
+ writel(PCI_CLASS_BRIDGE_PCI << 16,
+ priv->apb_base + PCIE_RC_BASE + PCI_CLASS_REVISION);
+ writel(PCIE_LM_RCBARPIE | PCIE_LM_RCBARPIS,
+ priv->apb_base + PCIE_LM_RCBAR);
+
+ if (dev_read_bool(dev, "aspm-no-l0s")) {
+ val = readl(priv->apb_base + PCIE_RC_PCIE_LCAP);
+ val &= ~PCIE_RC_PCIE_LCAP_APMS_L0S;
+ writel(val, priv->apb_base + PCIE_RC_PCIE_LCAP);
+ }
+
+ /* Configure Address Translation. */
+ ret = rockchip_pcie_atr_init(priv);
+ if (ret) {
+ dev_err(dev, "PCIE-%d: ATR init failed\n", dev->seq);
+ goto err_power_off_phy;
+ }
+
+ return 0;
+
+err_power_off_phy:
+ ops->power_off(phy);
+err_exit_phy:
+ ops->exit(phy);
+ return ret;
+}
+
+static int rockchip_pcie_set_vpcie(struct udevice *dev)
+{
+ struct rockchip_pcie *priv = dev_get_priv(dev);
+ int ret;
+
+ if (!IS_ERR(priv->vpcie3v3)) {
+ ret = regulator_set_enable(priv->vpcie3v3, true);
+ if (ret) {
+ dev_err(dev, "failed to enable vpcie3v3 (ret=%d)\n",
+ ret);
+ return ret;
+ }
+ }
+
+ ret = regulator_set_enable(priv->vpcie1v8, true);
+ if (ret) {
+ dev_err(dev, "failed to enable vpcie1v8 (ret=%d)\n", ret);
+ goto err_disable_3v3;
+ }
+
+ ret = regulator_set_enable(priv->vpcie0v9, true);
+ if (ret) {
+ dev_err(dev, "failed to enable vpcie0v9 (ret=%d)\n", ret);
+ goto err_disable_1v8;
+ }
+
+ return 0;
+
+err_disable_1v8:
+ regulator_set_enable(priv->vpcie1v8, false);
+err_disable_3v3:
+ if (!IS_ERR(priv->vpcie3v3))
+ regulator_set_enable(priv->vpcie3v3, false);
+ return ret;
+}
+
+static int rockchip_pcie_parse_dt(struct udevice *dev)
+{
+ struct rockchip_pcie *priv = dev_get_priv(dev);
+ int ret;
+
+ priv->axi_base = dev_read_addr_name(dev, "axi-base");
+ if (!priv->axi_base)
+ return -ENODEV;
+
+ priv->apb_base = dev_read_addr_name(dev, "apb-base");
+ if (!priv->axi_base)
+ return -ENODEV;
+
+ ret = gpio_request_by_name(dev, "ep-gpios", 0,
+ &priv->ep_gpio, GPIOD_IS_OUT);
+ if (ret) {
+ dev_err(dev, "failed to find ep-gpios property\n");
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "core", &priv->core_rst);
+ if (ret) {
+ dev_err(dev, "failed to get core reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "mgmt", &priv->mgmt_rst);
+ if (ret) {
+ dev_err(dev, "failed to get mgmt reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "mgmt-sticky", &priv->mgmt_sticky_rst);
+ if (ret) {
+ dev_err(dev, "failed to get mgmt-sticky reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "pipe", &priv->pipe_rst);
+ if (ret) {
+ dev_err(dev, "failed to get pipe reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "pm", &priv->pm_rst);
+ if (ret) {
+ dev_err(dev, "failed to get pm reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "pclk", &priv->pclk_rst);
+ if (ret) {
+ dev_err(dev, "failed to get pclk reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = reset_get_by_name(dev, "aclk", &priv->aclk_rst);
+ if (ret) {
+ dev_err(dev, "failed to get aclk reset (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vpcie3v3-supply",
+ &priv->vpcie3v3);
+ if (ret && ret != -ENOENT) {
+ dev_err(dev, "failed to get vpcie3v3 supply (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vpcie1v8-supply",
+ &priv->vpcie1v8);
+ if (ret) {
+ dev_err(dev, "failed to get vpcie1v8 supply (ret=%d)\n", ret);
+ return ret;
+ }
+
+ ret = device_get_supply_regulator(dev, "vpcie0v9-supply",
+ &priv->vpcie0v9);
+ if (ret) {
+ dev_err(dev, "failed to get vpcie0v9 supply (ret=%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_pcie_probe(struct udevice *dev)
+{
+ struct rockchip_pcie *priv = dev_get_priv(dev);
+ struct udevice *ctlr = pci_get_controller(dev);
+ struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+ int ret;
+
+ priv->first_busno = dev->seq;
+ priv->dev = dev;
+
+ ret = rockchip_pcie_parse_dt(dev);
+ if (ret)
+ return ret;
+
+ ret = rockchip_pcie_phy_get(dev);
+ if (ret)
+ return ret;
+
+ ret = rockchip_pcie_set_vpcie(dev);
+ if (ret)
+ return ret;
+
+ ret = rockchip_pcie_init_port(dev);
+ if (ret)
+ return ret;
+
+ dev_info(dev, "PCIE-%d: Link up (Bus%d)\n",
+ dev->seq, hose->first_busno);
+
+ return 0;
+}
+
+static const struct dm_pci_ops rockchip_pcie_ops = {
+ .read_config = rockchip_pcie_rd_conf,
+ .write_config = rockchip_pcie_wr_conf,
+};
+
+static const struct udevice_id rockchip_pcie_ids[] = {
+ { .compatible = "rockchip,rk3399-pcie" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_pcie) = {
+ .name = "rockchip_pcie",
+ .id = UCLASS_PCI,
+ .of_match = rockchip_pcie_ids,
+ .ops = &rockchip_pcie_ops,
+ .probe = rockchip_pcie_probe,
+ .priv_auto_alloc_size = sizeof(struct rockchip_pcie),
+};
diff --git a/drivers/pci/pcie_rockchip.h b/drivers/pci/pcie_rockchip.h
new file mode 100644
index 0000000..c3a0a28
--- /dev/null
+++ b/drivers/pci/pcie_rockchip.h
@@ -0,0 +1,142 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Rockchip PCIe Headers
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ * Copyright (c) 2020 Amarula Solutions(India)
+ * Copyright (c) 2020 Jagan Teki <jagan@amarulasolutions.com>
+ * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
+ *
+ */
+
+#define HIWORD_UPDATE(mask, val) (((mask) << 16) | (val))
+#define HIWORD_UPDATE_BIT(val) HIWORD_UPDATE(val, val)
+
+#define ENCODE_LANES(x) ((((x) >> 1) & 3) << 4)
+#define PCIE_CLIENT_BASE 0x0
+#define PCIE_CLIENT_CONFIG (PCIE_CLIENT_BASE + 0x00)
+#define PCIE_CLIENT_CONF_ENABLE HIWORD_UPDATE_BIT(0x0001)
+#define PCIE_CLIENT_LINK_TRAIN_ENABLE HIWORD_UPDATE_BIT(0x0002)
+#define PCIE_CLIENT_MODE_RC HIWORD_UPDATE_BIT(0x0040)
+#define PCIE_CLIENT_GEN_SEL_1 HIWORD_UPDATE(0x0080, 0)
+#define PCIE_CLIENT_BASIC_STATUS1 0x0048
+#define PCIE_CLIENT_LINK_STATUS_UP GENMASK(21, 20)
+#define PCIE_CLIENT_LINK_STATUS_MASK GENMASK(21, 20)
+#define PCIE_LINK_UP(x) \
+ (((x) & PCIE_CLIENT_LINK_STATUS_MASK) == PCIE_CLIENT_LINK_STATUS_UP)
+#define PCIE_RC_NORMAL_BASE 0x800000
+#define PCIE_LM_BASE 0x900000
+#define PCIE_LM_VENDOR_ID (PCIE_LM_BASE + 0x44)
+#define PCIE_LM_VENDOR_ROCKCHIP 0x1d87
+#define PCIE_LM_RCBAR (PCIE_LM_BASE + 0x300)
+#define PCIE_LM_RCBARPIE BIT(19)
+#define PCIE_LM_RCBARPIS BIT(20)
+#define PCIE_RC_BASE 0xa00000
+#define PCIE_RC_CONFIG_DCR (PCIE_RC_BASE + 0x0c4)
+#define PCIE_RC_CONFIG_DCR_CSPL_SHIFT 18
+#define PCIE_RC_CONFIG_DCR_CPLS_SHIFT 26
+#define PCIE_RC_PCIE_LCAP (PCIE_RC_BASE + 0x0cc)
+#define PCIE_RC_PCIE_LCAP_APMS_L0S BIT(10)
+#define PCIE_ATR_BASE 0xc00000
+#define PCIE_ATR_OB_ADDR0(i) (PCIE_ATR_BASE + 0x000 + (i) * 0x20)
+#define PCIE_ATR_OB_ADDR1(i) (PCIE_ATR_BASE + 0x004 + (i) * 0x20)
+#define PCIE_ATR_OB_DESC0(i) (PCIE_ATR_BASE + 0x008 + (i) * 0x20)
+#define PCIE_ATR_OB_DESC1(i) (PCIE_ATR_BASE + 0x00c + (i) * 0x20)
+#define PCIE_ATR_IB_ADDR0(i) (PCIE_ATR_BASE + 0x800 + (i) * 0x8)
+#define PCIE_ATR_IB_ADDR1(i) (PCIE_ATR_BASE + 0x804 + (i) * 0x8)
+#define PCIE_ATR_HDR_MEM 0x2
+#define PCIE_ATR_HDR_IO 0x6
+#define PCIE_ATR_HDR_CFG_TYPE0 0xa
+#define PCIE_ATR_HDR_CFG_TYPE1 0xb
+#define PCIE_ATR_HDR_RID BIT(23)
+
+#define PCIE_ATR_OB_REGION0_SIZE (32 * 1024 * 1024)
+#define PCIE_ATR_OB_REGION_SIZE (1 * 1024 * 1024)
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE_MASK(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define PHY_CFG_DATA_SHIFT 7
+#define PHY_CFG_ADDR_SHIFT 1
+#define PHY_CFG_DATA_MASK 0xf
+#define PHY_CFG_ADDR_MASK 0x3f
+#define PHY_CFG_RD_MASK 0x3ff
+#define PHY_CFG_WR_ENABLE 1
+#define PHY_CFG_WR_DISABLE 1
+#define PHY_CFG_WR_SHIFT 0
+#define PHY_CFG_WR_MASK 1
+#define PHY_CFG_PLL_LOCK 0x10
+#define PHY_CFG_CLK_TEST 0x10
+#define PHY_CFG_CLK_SCC 0x12
+#define PHY_CFG_SEPE_RATE BIT(3)
+#define PHY_CFG_PLL_100M BIT(3)
+#define PHY_PLL_LOCKED BIT(9)
+#define PHY_PLL_OUTPUT BIT(10)
+#define PHY_LANE_IDLE_OFF 0x1
+#define PHY_LANE_IDLE_MASK 0x1
+#define PHY_LANE_IDLE_A_SHIFT 3
+#define PHY_LANE_IDLE_B_SHIFT 4
+#define PHY_LANE_IDLE_C_SHIFT 5
+#define PHY_LANE_IDLE_D_SHIFT 6
+
+#define PCIE_PHY_CONF 0xe220
+#define PCIE_PHY_STATUS 0xe2a4
+#define PCIE_PHY_LANEOFF 0xe214
+
+struct rockchip_pcie_phy {
+ void *reg_base;
+ struct clk refclk;
+ struct reset_ctl phy_rst;
+ struct rockchip_pcie_phy_ops *ops;
+};
+
+struct rockchip_pcie_phy_ops {
+ int (*init)(struct rockchip_pcie_phy *phy);
+ int (*exit)(struct rockchip_pcie_phy *phy);
+ int (*power_on)(struct rockchip_pcie_phy *phy);
+ int (*power_off)(struct rockchip_pcie_phy *phy);
+};
+
+struct rockchip_pcie {
+ fdt_addr_t axi_base;
+ fdt_addr_t apb_base;
+ int first_busno;
+ struct udevice *dev;
+ struct rockchip_pcie_phy rk_phy;
+ struct rockchip_pcie_phy *phy;
+
+ /* resets */
+ struct reset_ctl core_rst;
+ struct reset_ctl mgmt_rst;
+ struct reset_ctl mgmt_sticky_rst;
+ struct reset_ctl pipe_rst;
+ struct reset_ctl pm_rst;
+ struct reset_ctl pclk_rst;
+ struct reset_ctl aclk_rst;
+
+ /* gpio */
+ struct gpio_desc ep_gpio;
+
+ /* vpcie regulators */
+ struct udevice *vpcie12v;
+ struct udevice *vpcie3v3;
+ struct udevice *vpcie1v8;
+ struct udevice *vpcie0v9;
+};
+
+int rockchip_pcie_phy_get(struct udevice *dev);
+
+inline struct rockchip_pcie_phy *pcie_get_phy(struct rockchip_pcie *pcie)
+{
+ return pcie->phy;
+}
+
+inline
+struct rockchip_pcie_phy_ops *phy_get_ops(struct rockchip_pcie_phy *phy)
+{
+ return (struct rockchip_pcie_phy_ops *)phy->ops;
+}
diff --git a/drivers/pci/pcie_rockchip_phy.c b/drivers/pci/pcie_rockchip_phy.c
new file mode 100644
index 0000000..47f5d6c
--- /dev/null
+++ b/drivers/pci/pcie_rockchip_phy.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Rockchip PCIe PHY driver
+ *
+ * Copyright (c) 2016 Rockchip, Inc.
+ * Copyright (c) 2020 Amarula Solutions(India)
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <reset.h>
+#include <syscon.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <linux/iopoll.h>
+#include <asm/arch-rockchip/clock.h>
+
+#include "pcie_rockchip.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static void phy_wr_cfg(struct rockchip_pcie_phy *phy, u32 addr, u32 data)
+{
+ u32 reg;
+
+ reg = HIWORD_UPDATE_MASK(data, PHY_CFG_DATA_MASK, PHY_CFG_DATA_SHIFT);
+ reg |= HIWORD_UPDATE_MASK(addr, PHY_CFG_ADDR_MASK, PHY_CFG_ADDR_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+ udelay(1);
+
+ reg = HIWORD_UPDATE_MASK(PHY_CFG_WR_ENABLE,
+ PHY_CFG_WR_MASK,
+ PHY_CFG_WR_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+ udelay(1);
+
+ reg = HIWORD_UPDATE_MASK(PHY_CFG_WR_DISABLE,
+ PHY_CFG_WR_MASK,
+ PHY_CFG_WR_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_CONF);
+}
+
+static int rockchip_pcie_phy_power_on(struct rockchip_pcie_phy *phy)
+{
+ int ret = 0;
+ u32 reg, status;
+
+ ret = reset_deassert(&phy->phy_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert phy reset\n");
+ return ret;
+ }
+
+ reg = HIWORD_UPDATE_MASK(PHY_CFG_PLL_LOCK,
+ PHY_CFG_ADDR_MASK,
+ PHY_CFG_ADDR_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+ reg = HIWORD_UPDATE_MASK(!PHY_LANE_IDLE_OFF,
+ PHY_LANE_IDLE_MASK,
+ PHY_LANE_IDLE_A_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_LANEOFF);
+
+ ret = -EINVAL;
+ ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
+ status,
+ status & PHY_PLL_LOCKED,
+ 20 * 1000,
+ 50);
+ if (ret) {
+ dev_err(&phy->dev, "pll lock timeout!\n");
+ goto err_pll_lock;
+ }
+
+ phy_wr_cfg(phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
+ phy_wr_cfg(phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
+
+ ret = -ETIMEDOUT;
+ ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
+ status,
+ !(status & PHY_PLL_OUTPUT),
+ 20 * 1000,
+ 50);
+ if (ret) {
+ dev_err(&phy->dev, "pll output enable timeout!\n");
+ goto err_pll_lock;
+ }
+
+ reg = HIWORD_UPDATE_MASK(PHY_CFG_PLL_LOCK,
+ PHY_CFG_ADDR_MASK,
+ PHY_CFG_ADDR_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_CONF);
+
+ ret = -EINVAL;
+ ret = readl_poll_sleep_timeout(phy->reg_base + PCIE_PHY_STATUS,
+ status,
+ status & PHY_PLL_LOCKED,
+ 20 * 1000,
+ 50);
+ if (ret) {
+ dev_err(&phy->dev, "pll relock timeout!\n");
+ goto err_pll_lock;
+ }
+
+ return 0;
+
+err_pll_lock:
+ reset_assert(&phy->phy_rst);
+ return ret;
+}
+
+static int rockchip_pcie_phy_power_off(struct rockchip_pcie_phy *phy)
+{
+ int ret;
+ u32 reg;
+
+ reg = HIWORD_UPDATE_MASK(PHY_LANE_IDLE_OFF,
+ PHY_LANE_IDLE_MASK,
+ PHY_LANE_IDLE_A_SHIFT);
+ writel(reg, phy->reg_base + PCIE_PHY_LANEOFF);
+
+ ret = reset_assert(&phy->phy_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert phy reset\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int rockchip_pcie_phy_init(struct rockchip_pcie_phy *phy)
+{
+ int ret;
+
+ ret = clk_enable(&phy->refclk);
+ if (ret) {
+ dev_err(dev, "failed to enable refclk clock\n");
+ return ret;
+ }
+
+ ret = reset_assert(&phy->phy_rst);
+ if (ret) {
+ dev_err(dev, "failed to assert phy reset\n");
+ goto err_reset;
+ }
+
+ return 0;
+
+err_reset:
+ clk_disable(&phy->refclk);
+ return ret;
+}
+
+static int rockchip_pcie_phy_exit(struct rockchip_pcie_phy *phy)
+{
+ clk_disable(&phy->refclk);
+
+ return 0;
+}
+
+static struct rockchip_pcie_phy_ops pcie_phy_ops = {
+ .init = rockchip_pcie_phy_init,
+ .power_on = rockchip_pcie_phy_power_on,
+ .power_off = rockchip_pcie_phy_power_off,
+ .exit = rockchip_pcie_phy_exit,
+};
+
+int rockchip_pcie_phy_get(struct udevice *dev)
+{
+ struct rockchip_pcie *priv = dev_get_priv(dev);
+ struct rockchip_pcie_phy *phy_priv = &priv->rk_phy;
+ ofnode phy_node;
+ u32 phandle;
+ int ret;
+
+ phandle = dev_read_u32_default(dev, "phys", 0);
+ phy_node = ofnode_get_by_phandle(phandle);
+ if (!ofnode_valid(phy_node)) {
+ dev_err(dev, "failed to found pcie-phy\n");
+ return -ENODEV;
+ }
+
+ phy_priv->reg_base = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+
+ ret = clk_get_by_index_nodev(phy_node, 0, &phy_priv->refclk);
+ if (ret) {
+ dev_err(dev, "failed to get refclk clock phandle\n");
+ return ret;
+ }
+
+ ret = reset_get_by_index_nodev(phy_node, 0, &phy_priv->phy_rst);
+ if (ret) {
+ dev_err(dev, "failed to get phy reset phandle\n");
+ return ret;
+ }
+
+ phy_priv->ops = &pcie_phy_ops;
+ priv->phy = phy_priv;
+
+ return 0;
+}