[PATCH 5/8] pci: Add Rockchip PCIe controller driver
Jagan Teki
jagan at amarulasolutions.com
Sat Apr 25 21:36:56 CEST 2020
On Sun, Apr 26, 2020 at 12:23 AM Mark Kettenis <mark.kettenis at xs4all.nl> wrote:
>
> > From: Jagan Teki <jagan at amarulasolutions.com>
> > Date: Sat, 25 Apr 2020 16:33:51 +0530
> >
> > Add Rockchip PCIe controller driver for rk3399 platform.
> >
> > Driver support Gen1 by operating as a Root complex.
> >
> > Thanks to Patrick for initial work.
>
> Tried to get this to work on my firefly-rk3399 which made me notice
> some shortcomings:
>
> 1. The vpcie1v8 and vpcie0v9 supplies are optional, just like the
> vpcie3v3 supply.
>
> 2. The vpcie3v3 regulator check doesn't quite work.
You mean the regulator check?
>
> See below for suggestions on how to fix this.
>
> Sadly the NVME SSD doesn't seem to be happy and shows up as only 1023 MB.
> But that is probably not caused by this diff.
>
> > Signed-off-by: Patrick Wildt <patrick at blueri.se>
> > Signed-off-by: Jagan Teki <jagan at amarulasolutions.com>
> > ---
> > drivers/pci/Kconfig | 8 +
> > drivers/pci/Makefile | 1 +
> > drivers/pci/pcie_rockchip.c | 460 ++++++++++++++++++++++++++++++++++++
> > drivers/pci/pcie_rockchip.h | 79 +++++++
> > 4 files changed, 548 insertions(+)
> > create mode 100644 drivers/pci/pcie_rockchip.c
> > create mode 100644 drivers/pci/pcie_rockchip.h
> >
> > diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
> > index 437cd9a055..3dba84103b 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 c051ecc9f3..493e9354dd 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
> > diff --git a/drivers/pci/pcie_rockchip.c b/drivers/pci/pcie_rockchip.c
> > new file mode 100644
> > index 0000000000..adc64aedf5
> > --- /dev/null
> > +++ b/drivers/pci/pcie_rockchip.c
> > @@ -0,0 +1,460 @@
> > +// 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 at amarulasolutions.com>
> > + * Copyright (c) 2019 Patrick Wildt <patrick at blueri.se>
> > + * Copyright (c) 2018 Mark Kettenis <kettenis at 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_rd_conf(const struct udevice *bus, pci_dev_t bdf,
> > + uint offset, ulong *valuep,
> > + enum pci_size_t size)
> > +{
> > + struct rockchip_pcie *priv = dev_get_priv(bus);
> > + ulong value;
> > + u32 off;
> > +
> > + off = (PCI_BUS(bdf) << 20) | (PCI_DEV(bdf) << 15) |
> > + (PCI_FUNC(bdf) << 12) | (offset & ~0x3);
> > +
> > + if ((PCI_BUS(bdf) == priv->first_busno) && (PCI_DEV(bdf) == 0)) {
> > + value = readl(priv->apb_base + PCIE_RC_NORMAL_BASE + off);
> > + *valuep = pci_conv_32_to_size(value, offset, size);
> > + return 0;
> > + }
> > +
> > + if ((PCI_BUS(bdf) == priv->first_busno + 1) && (PCI_DEV(bdf) == 0)) {
> > + value = readl(priv->axi_base + off);
> > + *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 *bus, pci_dev_t bdf,
> > + uint offset, ulong value,
> > + enum pci_size_t size)
> > +{
> > + struct rockchip_pcie *priv = dev_get_priv(bus);
> > + ulong old;
> > + u32 off;
> > +
> > + off = (PCI_BUS(bdf) << 20) | (PCI_DEV(bdf) << 15) |
> > + (PCI_FUNC(bdf) << 12) | (offset & ~0x3);
> > +
> > + if ((PCI_BUS(bdf) == priv->first_busno) && (PCI_DEV(bdf) == 0)) {
> > + old = readl(priv->apb_base + PCIE_RC_NORMAL_BASE + off);
> > + value = pci_conv_size_to_32(old, value, offset, size);
> > + writel(value, priv->apb_base + PCIE_RC_NORMAL_BASE + off);
> > + return 0;
> > + }
> > +
> > + if ((PCI_BUS(bdf) == priv->first_busno + 1) && (PCI_DEV(bdf) == 0)) {
> > + old = readl(priv->axi_base + off);
> > + value = pci_conv_size_to_32(old, value, offset, size);
> > + writel(value, priv->axi_base + off);
> > + 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);
> > + 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 = reset_assert(&priv->core_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to assert core reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_assert(&priv->mgmt_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to assert mgmt reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_assert(&priv->mgmt_sticky_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to assert mgmt-sticky reset (ret=%d)\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_assert(&priv->pipe_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to assert pipe reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + udelay(10);
> > +
> > + ret = reset_deassert(&priv->pm_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert pm reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_deassert(&priv->aclk_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert aclk reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_deassert(&priv->pclk_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert pclk reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + /* 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 = reset_deassert(&priv->mgmt_sticky_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert mgmt-sticky reset (ret=%d)\n",
> > + ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_deassert(&priv->core_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert core reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_deassert(&priv->mgmt_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert mgmt reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + ret = reset_deassert(&priv->pipe_rst);
> > + if (ret) {
> > + dev_err(dev, "failed to deassert pipe reset (ret=%d)\n", ret);
> > + return ret;
> > + }
> > +
> > + /* 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");
> > + return ret;
> > + }
> > +
> > + /* 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);
> > + return ret;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int rockchip_pcie_set_vpcie(struct udevice *dev)
> > +{
> > + struct rockchip_pcie *priv = dev_get_priv(dev);
> > + int ret;
> > +
> > + if (!IS_ERR(priv->vpcie3v3)) {
>
> I think this should be:
>
> if (priv->vpcie3v3) {
I didn't find any issue with the board I have optional of this, but
will check it.
>
> > + ret = regulator_set_enable(priv->vpcie3v3, true);
> > + if (ret) {
> > + dev_err(dev, "failed to enable vpcie3v3 (ret=%d)\n",
> > + ret);
> > + return ret;
> > + }
> > + }
> > +
>
> And to make this regulator optional, it needs an
>
> if (priv->vpcie1v8) {
I can see from v5.7-rc1, 12v, 3v3 are optional and rest not If I'm not wrong.
Jagan.
More information about the U-Boot
mailing list