pci: rmobile: Add RCar Gen2 PCIe controller driver
authorMarek Vasut <marek.vasut+renesas@gmail.com>
Thu, 18 Jan 2018 13:35:35 +0000 (14:35 +0100)
committerMarek Vasut <marex@denx.de>
Sat, 27 Jan 2018 19:38:53 +0000 (20:38 +0100)
Add driver for the Renesas RCar PCIe controller present on Gen2 SoCs.
The PCIe on Gen2 is used both to connect external PCIe peripherals as
well as access the on-SoC USB EHCI controller.

Signed-off-by: Marek Vasut <marek.vasut+renesas@gmail.com>
drivers/pci/Kconfig
drivers/pci/Makefile
drivers/pci/pci-rcar-gen2.c [new file with mode: 0644]

index 58f128d8a62c5a1e18101d01fc5ea3bb2ed241fe..da6421f35ccdf448b93fd188063cf17b2341253d 100644 (file)
@@ -50,6 +50,15 @@ config PCIE_DW_MVEBU
          Armada-8K SoCs. The PCIe controller on Armada-8K is based on
          DesignWare hardware.
 
+config PCI_RCAR_GEN2
+       bool "Renesas RCar Gen2 PCIe driver"
+       depends on DM_PCI
+       depends on RCAR_32
+       help
+         Say Y here if you want to enable PCIe controller support on
+         Renesas RCar Gen2 SoCs. The PCIe controller on RCar Gen2 is
+         also used to access EHCI USB controller on the SoC.
+
 config PCI_SANDBOX
        bool "Sandbox PCI support"
        depends on SANDBOX && DM_PCI
index 5410897e403b09f0aea836711bc3dcc16a671e7e..8fbab462a4382cb11819bd14858ffa2bf598b49f 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_PCI_MSC01) += pci_msc01.o
 obj-$(CONFIG_PCIE_IMX) += pcie_imx.o
 obj-$(CONFIG_FTPCI100) += pci_ftpci100.o
 obj-$(CONFIG_PCI_MVEBU) += pci_mvebu.o
+obj-$(CONFIG_PCI_RCAR_GEN2) += pci-rcar-gen2.o
 obj-$(CONFIG_SH4_PCI) += pci_sh4.o
 obj-$(CONFIG_SH7751_PCI) +=pci_sh7751.o
 obj-$(CONFIG_SH7780_PCI) +=pci_sh7780.o
diff --git a/drivers/pci/pci-rcar-gen2.c b/drivers/pci/pci-rcar-gen2.c
new file mode 100644 (file)
index 0000000..8293a6d
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Renesas RCar Gen2 PCIEC driver
+ *
+ * Copyright (C) 2018 Marek Vasut <marek.vasut@gmail.com>
+ *
+ * SPDX-License-Identifier:    GPL-2.0
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <pci.h>
+
+/* AHB-PCI Bridge PCI communication registers */
+#define RCAR_AHBPCI_PCICOM_OFFSET      0x800
+
+#define RCAR_PCIAHB_WIN1_CTR_REG       (RCAR_AHBPCI_PCICOM_OFFSET + 0x00)
+#define RCAR_PCIAHB_WIN2_CTR_REG       (RCAR_AHBPCI_PCICOM_OFFSET + 0x04)
+#define RCAR_PCIAHB_PREFETCH0          0x0
+#define RCAR_PCIAHB_PREFETCH4          0x1
+#define RCAR_PCIAHB_PREFETCH8          0x2
+#define RCAR_PCIAHB_PREFETCH16         0x3
+
+#define RCAR_AHBPCI_WIN1_CTR_REG       (RCAR_AHBPCI_PCICOM_OFFSET + 0x10)
+#define RCAR_AHBPCI_WIN2_CTR_REG       (RCAR_AHBPCI_PCICOM_OFFSET + 0x14)
+#define RCAR_AHBPCI_WIN_CTR_MEM                (3 << 1)
+#define RCAR_AHBPCI_WIN_CTR_CFG                (5 << 1)
+#define RCAR_AHBPCI_WIN1_HOST          BIT(30)
+#define RCAR_AHBPCI_WIN1_DEVICE                BIT(31)
+
+#define RCAR_PCI_INT_ENABLE_REG                (RCAR_AHBPCI_PCICOM_OFFSET + 0x20)
+#define RCAR_PCI_INT_STATUS_REG                (RCAR_AHBPCI_PCICOM_OFFSET + 0x24)
+#define RCAR_PCI_INT_SIGTABORT         BIT(0)
+#define RCAR_PCI_INT_SIGRETABORT       BIT(1)
+#define RCAR_PCI_INT_REMABORT          BIT(2)
+#define RCAR_PCI_INT_PERR              BIT(3)
+#define RCAR_PCI_INT_SIGSERR           BIT(4)
+#define RCAR_PCI_INT_RESERR            BIT(5)
+#define RCAR_PCI_INT_WIN1ERR           BIT(12)
+#define RCAR_PCI_INT_WIN2ERR           BIT(13)
+#define RCAR_PCI_INT_A                 BIT(16)
+#define RCAR_PCI_INT_B                 BIT(17)
+#define RCAR_PCI_INT_PME               BIT(19)
+#define RCAR_PCI_INT_ALLERRORS (RCAR_PCI_INT_SIGTABORT         | \
+                               RCAR_PCI_INT_SIGRETABORT        | \
+                               RCAR_PCI_INT_SIGRETABORT        | \
+                               RCAR_PCI_INT_REMABORT           | \
+                               RCAR_PCI_INT_PERR               | \
+                               RCAR_PCI_INT_SIGSERR            | \
+                               RCAR_PCI_INT_RESERR             | \
+                               RCAR_PCI_INT_WIN1ERR            | \
+                               RCAR_PCI_INT_WIN2ERR)
+
+#define RCAR_AHB_BUS_CTR_REG           (RCAR_AHBPCI_PCICOM_OFFSET + 0x30)
+#define RCAR_AHB_BUS_MMODE_HTRANS      BIT(0)
+#define RCAR_AHB_BUS_MMODE_BYTE_BURST  BIT(1)
+#define RCAR_AHB_BUS_MMODE_WR_INCR     BIT(2)
+#define RCAR_AHB_BUS_MMODE_HBUS_REQ    BIT(7)
+#define RCAR_AHB_BUS_SMODE_READYCTR    BIT(17)
+#define RCAR_AHB_BUS_MODE              (RCAR_AHB_BUS_MMODE_HTRANS |    \
+                                       RCAR_AHB_BUS_MMODE_BYTE_BURST | \
+                                       RCAR_AHB_BUS_MMODE_WR_INCR |    \
+                                       RCAR_AHB_BUS_MMODE_HBUS_REQ |   \
+                                       RCAR_AHB_BUS_SMODE_READYCTR)
+
+#define RCAR_USBCTR_REG                        (RCAR_AHBPCI_PCICOM_OFFSET + 0x34)
+#define RCAR_USBCTR_USBH_RST           BIT(0)
+#define RCAR_USBCTR_PCICLK_MASK                BIT(1)
+#define RCAR_USBCTR_PLL_RST            BIT(2)
+#define RCAR_USBCTR_DIRPD              BIT(8)
+#define RCAR_USBCTR_PCIAHB_WIN2_EN     BIT(9)
+#define RCAR_USBCTR_PCIAHB_WIN1_256M   (0 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_512M   (1 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_1G     (2 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_2G     (3 << 10)
+#define RCAR_USBCTR_PCIAHB_WIN1_MASK   (3 << 10)
+
+#define RCAR_PCI_ARBITER_CTR_REG       (RCAR_AHBPCI_PCICOM_OFFSET + 0x40)
+#define RCAR_PCI_ARBITER_PCIREQ0       BIT(0)
+#define RCAR_PCI_ARBITER_PCIREQ1       BIT(1)
+#define RCAR_PCI_ARBITER_PCIBP_MODE    BIT(12)
+
+#define RCAR_PCI_UNIT_REV_REG          (RCAR_AHBPCI_PCICOM_OFFSET + 0x48)
+
+struct rcar_gen2_pci_priv {
+       fdt_addr_t              cfg_base;
+       fdt_addr_t              mem_base;
+};
+
+static int rcar_gen2_pci_addr_valid(pci_dev_t d, uint offset)
+{
+       u32 slot;
+
+       if (PCI_FUNC(d))
+               return -EINVAL;
+
+       /* Only one EHCI/OHCI device built-in */
+       slot = PCI_DEV(d);
+       if (slot > 2)
+               return -EINVAL;
+
+       /* bridge logic only has registers to 0x40 */
+       if (slot == 0x0 && offset >= 0x40)
+               return -EINVAL;
+
+       return 0;
+}
+
+static u32 get_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset)
+{
+       struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
+
+       return priv->cfg_base + (PCI_DEV(bdf) >> 1) * 0x100 + (offset & ~3);
+}
+
+static u32 setup_bus_address(struct udevice *dev, pci_dev_t bdf, u32 offset)
+{
+       struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
+       u32 reg;
+
+       reg = PCI_DEV(bdf) ? RCAR_AHBPCI_WIN1_DEVICE : RCAR_AHBPCI_WIN1_HOST;
+       reg |= RCAR_AHBPCI_WIN_CTR_CFG;
+       writel(reg, priv->cfg_base + RCAR_AHBPCI_WIN1_CTR_REG);
+
+       return get_bus_address(dev, bdf, offset);
+}
+
+static int rcar_gen2_pci_read_config(struct udevice *dev, pci_dev_t bdf,
+                                    uint offset, ulong *value,
+                                    enum pci_size_t size)
+{
+       u32 addr, reg;
+       int ret;
+
+       ret = rcar_gen2_pci_addr_valid(bdf, offset);
+       if (ret) {
+               *value = pci_get_ff(size);
+               return 0;
+       }
+
+       addr = get_bus_address(dev, bdf, offset);
+       reg = readl(addr);
+       *value = pci_conv_32_to_size(reg, offset, size);
+
+       return 0;
+}
+
+static int rcar_gen2_pci_write_config(struct udevice *dev, pci_dev_t bdf,
+                                     uint offset, ulong value,
+                                     enum pci_size_t size)
+{
+       u32 addr, reg, old;
+       int ret;
+
+       ret = rcar_gen2_pci_addr_valid(bdf, offset);
+       if (ret)
+               return ret;
+
+       addr = get_bus_address(dev, bdf, offset);
+
+       old = readl(addr);
+       reg = pci_conv_size_to_32(old, value, offset, size);
+       writel(reg, addr);
+
+       return 0;
+}
+
+static int rcar_gen2_pci_probe(struct udevice *dev)
+{
+       struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
+       struct clk pci_clk;
+       u32 devad;
+       int ret;
+
+       ret = clk_get_by_index(dev, 0, &pci_clk);
+       if (ret)
+               return ret;
+
+       ret = clk_enable(&pci_clk);
+       if (ret)
+               return ret;
+
+       /* Clock & Reset & Direct Power Down */
+       clrsetbits_le32(priv->cfg_base + RCAR_USBCTR_REG,
+                       RCAR_USBCTR_DIRPD | RCAR_USBCTR_PCICLK_MASK |
+                       RCAR_USBCTR_USBH_RST,
+                       RCAR_USBCTR_PCIAHB_WIN1_1G);
+       clrbits_le32(priv->cfg_base + RCAR_USBCTR_REG, RCAR_USBCTR_PLL_RST);
+
+       /* AHB-PCI Bridge Communication Registers */
+       writel(RCAR_AHB_BUS_MODE, priv->cfg_base + RCAR_AHB_BUS_CTR_REG);
+       writel((CONFIG_SYS_SDRAM_BASE & 0xf0000000) | RCAR_PCIAHB_PREFETCH16,
+              priv->cfg_base + RCAR_PCIAHB_WIN1_CTR_REG);
+       writel(0xf0000000 | RCAR_PCIAHB_PREFETCH16,
+              priv->cfg_base + RCAR_PCIAHB_WIN2_CTR_REG);
+       writel(priv->mem_base | RCAR_AHBPCI_WIN_CTR_MEM,
+              priv->cfg_base + RCAR_AHBPCI_WIN2_CTR_REG);
+       setbits_le32(priv->cfg_base + RCAR_PCI_ARBITER_CTR_REG,
+                    RCAR_PCI_ARBITER_PCIREQ0 | RCAR_PCI_ARBITER_PCIREQ1 |
+                    RCAR_PCI_ARBITER_PCIBP_MODE);
+
+       /* PCI Configuration Registers for AHBPCI */
+       devad = setup_bus_address(dev, PCI_BDF(0, 0, 0), 0);
+       writel(priv->cfg_base + 0x800, devad + PCI_BASE_ADDRESS_0);
+       writel(CONFIG_SYS_SDRAM_BASE & 0xf0000000, devad + PCI_BASE_ADDRESS_1);
+       writel(0xf0000000, devad + PCI_BASE_ADDRESS_2);
+       writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+              PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
+              devad + PCI_COMMAND);
+
+       /* PCI Configuration Registers for OHCI */
+       devad = setup_bus_address(dev, PCI_BDF(0, 1, 0), 0);
+       writel(priv->mem_base + 0x0, devad + PCI_BASE_ADDRESS_0);
+       writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+              PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
+              devad + PCI_COMMAND);
+
+       /* PCI Configuration Registers for EHCI */
+       devad = setup_bus_address(dev, PCI_BDF(0, 2, 0), 0);
+       writel(priv->mem_base + 0x1000, devad + PCI_BASE_ADDRESS_0);
+       writel(PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+              PCI_COMMAND_PARITY | PCI_COMMAND_SERR,
+              devad + PCI_COMMAND);
+
+       /* Enable PCI interrupt */
+       setbits_le32(priv->cfg_base + RCAR_PCI_INT_ENABLE_REG,
+                    RCAR_PCI_INT_A | RCAR_PCI_INT_B | RCAR_PCI_INT_PME);
+
+       return 0;
+}
+
+static int rcar_gen2_pci_ofdata_to_platdata(struct udevice *dev)
+{
+       struct rcar_gen2_pci_priv *priv = dev_get_priv(dev);
+
+       priv->cfg_base = devfdt_get_addr_index(dev, 0);
+       priv->mem_base = devfdt_get_addr_index(dev, 1);
+       if (!priv->cfg_base || !priv->mem_base)
+               return -EINVAL;
+
+       return 0;
+}
+
+static const struct dm_pci_ops rcar_gen2_pci_ops = {
+       .read_config    = rcar_gen2_pci_read_config,
+       .write_config   = rcar_gen2_pci_write_config,
+};
+
+static const struct udevice_id rcar_gen2_pci_ids[] = {
+       { .compatible = "renesas,pci-rcar-gen2" },
+       { }
+};
+
+U_BOOT_DRIVER(rcar_gen2_pci) = {
+       .name                   = "rcar_gen2_pci",
+       .id                     = UCLASS_PCI,
+       .of_match               = rcar_gen2_pci_ids,
+       .ops                    = &rcar_gen2_pci_ops,
+       .probe                  = rcar_gen2_pci_probe,
+       .ofdata_to_platdata     = rcar_gen2_pci_ofdata_to_platdata,
+       .priv_auto_alloc_size   = sizeof(struct rcar_gen2_pci_priv),
+};