Add a PCIe driver for the MPC83xx architecture.

Signed-off-by: Mario Six <mario....@gdsys.cc>
---
 arch/powerpc/cpu/mpc83xx/pcie.c |   4 +
 drivers/pci/Kconfig             |  16 ++
 drivers/pci/Makefile            |   1 +
 drivers/pci/pcie_mpc83xx.c      | 442 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 463 insertions(+)
 create mode 100644 drivers/pci/pcie_mpc83xx.c

diff --git a/arch/powerpc/cpu/mpc83xx/pcie.c b/arch/powerpc/cpu/mpc83xx/pcie.c
index 28c25e5feb..ed9c3d5d9d 100644
--- a/arch/powerpc/cpu/mpc83xx/pcie.c
+++ b/arch/powerpc/cpu/mpc83xx/pcie.c
@@ -8,6 +8,8 @@
  * SPDX-License-Identifier:    GPL-2.0+
  */
 
+#ifndef CONFIG_PCIE_MPC83XX
+
 #include <common.h>
 #include <pci.h>
 #include <mpc83xx.h>
@@ -333,3 +335,5 @@ void mpc83xx_pcie_init(int num_buses, struct pci_region 
**reg)
        for (i = 0; i < num_buses; i++)
                mpc83xx_pcie_init_bus(i, reg[i]);
 }
+
+#endif /* !CONFIG_PCIE_MPC83XX */
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index da6421f35c..8385fa5c9b 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -59,6 +59,22 @@ config PCI_RCAR_GEN2
          Renesas RCar Gen2 SoCs. The PCIe controller on RCar Gen2 is
          also used to access EHCI USB controller on the SoC.
 
+config PCIE_MPC83XX
+       bool "MPC83xx PCIe driver support"
+       depends on DM_PCI
+       help
+         Say Y here if you want to enable support for the PCIe controller on
+         MPC83xx SoCs.
+
+if PCIE_MPC83XX
+
+config 83XX_GENERIC_PCIE_REGISTER_HOSES
+       bool "Register generic hoses"
+       help
+         Register generic PCIe hoses when probing the PCIe device.
+
+endif
+
 config PCI_SANDBOX
        bool "Sandbox PCI support"
        depends on SANDBOX && DM_PCI
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 8fbab462a4..c6452e6f42 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -33,4 +33,5 @@ obj-$(CONFIG_PCI_TEGRA) += pci_tegra.o
 obj-$(CONFIG_PCIE_DW_MVEBU) += pcie_dw_mvebu.o
 obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape.o
 obj-$(CONFIG_PCIE_LAYERSCAPE) += pcie_layerscape_fixup.o
+obj-$(CONFIG_PCIE_MPC83XX) += pcie_mpc83xx.o
 obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o
diff --git a/drivers/pci/pcie_mpc83xx.c b/drivers/pci/pcie_mpc83xx.c
new file mode 100644
index 0000000000..19abf225b0
--- /dev/null
+++ b/drivers/pci/pcie_mpc83xx.c
@@ -0,0 +1,442 @@
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario....@gdsys.cc
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <pci.h>
+#include <clk.h>
+#include <mapmem.h>
+#include <asm/io.h>
+#include <asm-generic/gpio.h>
+
+struct pex_inbound_window {
+       u32 ar;
+       u32 tar;
+       u32 barl;
+       u32 barh;
+};
+
+struct pex_outbound_window {
+       u32 ar;
+       u32 bar;
+       u32 tarl;
+       u32 tarh;
+};
+
+struct pex_csb_bridge {
+       u32 pex_csb_ver;
+       u32 pex_csb_cab;
+       u32 pex_csb_ctrl;
+       u8 res0[8];
+       u32 pex_dms_dstmr;
+       u8 res1[4];
+       u32 pex_cbs_stat;
+       u8 res2[0x20];
+       u32 pex_csb_obctrl;
+       u32 pex_csb_obstat;
+       u8 res3[0x98];
+       u32 pex_csb_ibctrl;
+       u32 pex_csb_ibstat;
+       u8 res4[0xb8];
+       u32 pex_wdma_ctrl;
+       u32 pex_wdma_addr;
+       u32 pex_wdma_stat;
+       u8 res5[0x94];
+       u32 pex_rdma_ctrl;
+       u32 pex_rdma_addr;
+       u32 pex_rdma_stat;
+       u8 res6[0xd4];
+       u32 pex_ombcr;
+       u32 pex_ombdr;
+       u8 res7[0x38];
+       u32 pex_imbcr;
+       u32 pex_imbdr;
+       u8 res8[0x38];
+       u32 pex_int_enb;
+       u32 pex_int_stat;
+       u32 pex_int_apio_vec1;
+       u32 pex_int_apio_vec2;
+       u8 res9[0x10];
+       u32 pex_int_ppio_vec1;
+       u32 pex_int_ppio_vec2;
+       u32 pex_int_wdma_vec1;
+       u32 pex_int_wdma_vec2;
+       u32 pex_int_rdma_vec1;
+       u32 pex_int_rdma_vec2;
+       u32 pex_int_misc_vec;
+       u8 res10[4];
+       u32 pex_int_axi_pio_enb;
+       u32 pex_int_axi_wdma_enb;
+       u32 pex_int_axi_rdma_enb;
+       u32 pex_int_axi_misc_enb;
+       u32 pex_int_axi_pio_stat;
+       u32 pex_int_axi_wdma_stat;
+       u32 pex_int_axi_rdma_stat;
+       u32 pex_int_axi_misc_stat;
+       u8 res11[0xa0];
+       struct pex_outbound_window pex_outbound_win[4];
+       u8 res12[0x100];
+       u32 pex_epiwtar0;
+       u32 pex_epiwtar1;
+       u32 pex_epiwtar2;
+       u32 pex_epiwtar3;
+       u8 res13[0x70];
+       struct pex_inbound_window pex_inbound_win[4];
+};
+
+struct pcie_mpc83xx_regs {
+       u8 pex_cfg_header[0x404];
+       u32 pex_ltssm_stat;
+       u8 res0[0x30];
+       u32 pex_ack_replay_timeout;
+       u8 res1[4];
+       u32 pex_gclk_ratio;
+       u8 res2[0xc];
+       u32 pex_pm_timer;
+       u32 pex_pme_timeout;
+       u8 res3[4];
+       u32 pex_aspm_req_timer;
+       u8 res4[0x18];
+       u32 pex_ssvid_update;
+       u8 res5[0x34];
+       u32 pex_cfg_ready;
+       u8 res6[0x24];
+       u32 pex_bar_sizel;
+       u8 res7[4];
+       u32 pex_bar_sel;
+       u8 res8[0x20];
+       u32 pex_bar_pf;
+       u8 res9[0x88];
+       u32 pex_pme_to_ack_tor;
+       u8 res10[0xc];
+       u32 pex_ss_intr_mask;
+       u8 res11[0x25c];
+       struct pex_csb_bridge bridge;
+       u8 res12[0x160];
+};
+
+struct pcie_mpc83xx {
+       struct pcie_mpc83xx_regs *regs;
+       struct pci_region region[2];
+       struct {
+               u32 base;
+               u32 size;
+       } cfg_space;
+};
+
+#if CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES
+
+static int pcie_mpc83xx_remap_cfg(struct udevice *bus, pci_dev_t bdf)
+{
+       struct udevice *ctlr = pci_get_controller(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+       struct pcie_mpc83xx *priv = dev_get_priv(bus);
+       struct pcie_mpc83xx_regs *pex = priv->regs;
+       struct pex_outbound_window *out_win = &pex->bridge.pex_outbound_win[0];
+
+       int busn = PCI_BUS(bdf) - hose->first_busno;
+       u8 devfn = PCI_DEV(bdf) << 3 | PCI_FUNC(bdf);
+       u32 dev_base = busn << 24 | devfn << 16;
+
+       if (hose->indirect_type == INDIRECT_TYPE_NO_PCIE_LINK)
+               return 0;
+       /*
+        * Workaround for the HW bug: for Type 0 configure transactions the
+        * PCI-E controller does not check the device number bits and just
+        * assumes that the device number bits are 0.
+        */
+       if (devfn & 0xf8)
+               return -1;
+
+       out_le32(&out_win->tarl, dev_base);
+       return 0;
+}
+
+static void mpc83xx_pcie_register_hose(struct udevice *bus, struct pci_region 
*reg,
+                                      u8 link)
+{
+       struct udevice *ctlr = pci_get_controller(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+
+       /*
+        * There are no spare BATs to remap all PCI-E windows for U-Boot, so
+        * disable translations. In general, this is not great solution, and
+        * that's why we don't register PCI-E hoses by default.
+        */
+       disable_addr_trans();
+
+       if (!link)
+               hose->indirect_type = INDIRECT_TYPE_NO_PCIE_LINK;
+}
+#else
+
+static void mpc83xx_pcie_register_hose(struct udevice *bus, struct pci_region 
*reg,
+                                      u8 link) {}
+
+#endif /* CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES */
+
+static int pcie_mpc83xx_read_config(struct udevice *bus, pci_dev_t bdf,
+                                   uint offset, ulong *valuep,
+                                   enum pci_size_t size)
+{
+#ifdef CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES
+       struct udevice *ctlr = pci_get_controller(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+
+       int ret;
+
+       ret = pcie_mpc83xx_remap_cfg(bus, bdf);
+       if (ret) {
+               /* TODO: Old code sets the value to -1 here... */
+               return ret;
+       }
+
+       switch (size) {
+       case PCI_SIZE_8:
+               *valuep = in_8((u8 *)(hose->cfg_addr + offset));
+               break;
+       case PCI_SIZE_16:
+               *valuep = in_le16((u16 *)(hose->cfg_addr + offset));
+               break;
+       case PCI_SIZE_32:
+               *valuep = in_le32((u32 *)(hose->cfg_addr + offset));
+               break;
+       }
+#endif
+       return 0;
+}
+
+static int pcie_mpc83xx_write_config(struct udevice *bus, pci_dev_t bdf,
+                                    uint offset, ulong value,
+                                    enum pci_size_t size)
+{
+#ifdef CONFIG_83XX_GENERIC_PCIE_REGISTER_HOSES
+       struct udevice *ctlr = pci_get_controller(bus);
+       struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+
+       int ret;
+
+       ret = pcie_mpc83xx_remap_cfg(bus, bdf);
+       if (ret)
+               return ret;
+
+       switch (size) {
+       case PCI_SIZE_8:
+               out_8((u8 *)(hose->cfg_addr + offset), value);
+               break;
+       case PCI_SIZE_16:
+               out_le16((u16 *)(hose->cfg_addr + offset), value);
+               break;
+       case PCI_SIZE_32:
+               out_le32((u32 *)(hose->cfg_addr + offset), value);
+               break;
+       }
+#endif
+       return 0;
+}
+
+void configure_pcie_law(pci_addr_t addr)
+{
+       immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
+       sysconf83xx_t *sysconf = &immr->sysconf;
+       law83xx_t *pcie_law = sysconf->pcielaw;
+
+       /* Deassert the resets in the control register */
+       out_be32(&sysconf->pecr1, 0xE0008000);
+       udelay(2000);
+
+       /* Configure PCI Express Local Access Windows */
+       out_be32(&pcie_law[0].bar, addr & LAWBAR_BAR);
+       out_be32(&pcie_law[0].ar, LBLAWAR_EN | LBLAWAR_512MB);
+}
+
+static int pcie_mpc83xx_probe(struct udevice *bus)
+{
+       //struct udevice *ctlr = pci_get_controller(bus);
+       //struct pci_controller *hose = dev_get_uclass_priv(ctlr);
+       struct pcie_mpc83xx *priv = dev_get_priv(bus);
+       struct pcie_mpc83xx_regs *pex;
+       struct pex_outbound_window *out_win;
+       struct pex_inbound_window *in_win;
+       u8 *hose_cfg_base;
+       unsigned int ram_sz;
+       unsigned int barl;
+       unsigned int tar;
+       u16 ltssm;
+       bool have_link;
+       int i;
+       struct clk clock;
+       struct pci_region *mem;
+       struct pci_region *io;
+       struct pci_region *pref;
+
+       pci_get_regions(bus, &io, &mem, &pref);
+       configure_pcie_law(mem->bus_start);
+
+       priv->regs = map_sysmem(dev_read_addr(bus),
+                               sizeof(struct pcie_mpc83xx));
+       pex = priv->regs;
+
+       /*
+        * Release PCI RST Output signal.
+        * Power on to RST high must be at least 100 ms as per PCI spec.
+        * On warm boots only 1 ms is required, but we play it safe.
+        */
+       mdelay(100);
+
+       /* Enable pex csb bridge inbound & outbound transactions */
+       setbits_le32(&pex->bridge.pex_csb_ctrl, PEX_CSB_CTRL_OBPIOE |
+                                               PEX_CSB_CTRL_IBPIOE);
+
+       /* Enable bridge outbound */
+       out_le32(&pex->bridge.pex_csb_obctrl, PEX_CSB_OBCTRL_PIOE |
+                                             PEX_CSB_OBCTRL_MEMWE |
+                                             PEX_CSB_OBCTRL_IOWE |
+                                             PEX_CSB_OBCTRL_CFGWE);
+
+       out_win = &pex->bridge.pex_outbound_win[0];
+       out_le32(&out_win->ar, PEX_OWAR_EN | PEX_OWAR_TYPE_CFG |
+                       priv->cfg_space.size);
+       out_le32(&out_win->bar, priv->cfg_space.base);
+       out_le32(&out_win->tarl, 0);
+       out_le32(&out_win->tarh, 0);
+
+       for (i = 0; i < 2; i++) {
+               u32 ar;
+
+               if (priv->region[i].size == 0)
+                       break;
+
+               out_win = &pex->bridge.pex_outbound_win[i + 1];
+               out_le32(&out_win->bar, priv->region[i].phys_start);
+               out_le32(&out_win->tarl, priv->region[i].bus_start);
+               out_le32(&out_win->tarh, 0);
+               ar = PEX_OWAR_EN | (priv->region[i].size & PEX_OWAR_SIZE);
+               if (priv->region[i].flags & PCI_REGION_IO)
+                       ar |= PEX_OWAR_TYPE_IO;
+               else
+                       ar |= PEX_OWAR_TYPE_MEM;
+               out_le32(&out_win->ar, ar);
+       }
+
+       out_le32(&pex->bridge.pex_csb_ibctrl, PEX_CSB_IBCTRL_PIOE);
+
+       ram_sz = gd->ram_size;
+       barl = 0;
+       tar = 0;
+       i = 0;
+       while (ram_sz > 0) {
+               in_win = &pex->bridge.pex_inbound_win[i];
+               out_le32(&in_win->barl, barl);
+               out_le32(&in_win->barh, 0x0);
+               out_le32(&in_win->tar, tar);
+               if (ram_sz >= 0x10000000) {
+                       /* The maximum windows size is 256M */
+                       out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV |
+                               PEX_IWAR_TYPE_PF | 0x0FFFF000);
+                       barl += 0x10000000;
+                       tar += 0x10000000;
+                       ram_sz -= 0x10000000;
+               } else {
+                       /* The UM is not clear here.
+                        * So, round up to even Mb boundary
+                        */
+
+                       ram_sz = ram_sz >> (20 +
+                                       ((ram_sz & 0xFFFFF) ? 1 : 0));
+                       if (!(ram_sz % 2))
+                               ram_sz -= 1;
+                       out_le32(&in_win->ar, PEX_IWAR_EN | PEX_IWAR_NSOV |
+                               PEX_IWAR_TYPE_PF | (ram_sz << 20) | 0xFF000);
+                       ram_sz = 0;
+               }
+               i++;
+       }
+
+       in_win = &pex->bridge.pex_inbound_win[i];
+       out_le32(&in_win->barl, CONFIG_SYS_IMMR);
+       out_le32(&in_win->barh, 0);
+       out_le32(&in_win->tar, CONFIG_SYS_IMMR);
+       out_le32(&in_win->ar, PEX_IWAR_EN |
+               PEX_IWAR_TYPE_NO_PF | PEX_IWAR_SIZE_1M);
+
+       /* Enable the host virtual INTX interrupts */
+       out_le32(&pex->bridge.pex_int_axi_misc_enb,
+                in_le32(&pex->bridge.pex_int_axi_misc_enb) | 0x1E0);
+
+       /* Hose configure header is memory-mapped */
+       hose_cfg_base = (u8 *)pex;
+
+       /* Configure the PCIE controller core clock ratio */
+       clk_get_by_index(bus, 0, &clock);
+       out_le32((u32 *)(hose_cfg_base + PEX_GCLK_RATIO),
+                ((clk_get_rate(&clock) / 1000000) * 16) / 333);
+       mdelay(1000);
+
+       /* Do Type 1 bridge configuration */
+       out_8(hose_cfg_base + PCI_PRIMARY_BUS, 0);
+       out_8(hose_cfg_base + PCI_SECONDARY_BUS, 1);
+       out_8(hose_cfg_base + PCI_SUBORDINATE_BUS, 255);
+
+       /*
+        * Write to Command register
+        */
+       setbits_le16((u16 *)(hose_cfg_base + PCI_COMMAND), PCI_COMMAND_MASTER |
+                                                          PCI_COMMAND_MEMORY |
+                                                          PCI_COMMAND_IO |
+                                                          PCI_COMMAND_SERR |
+                                                          PCI_COMMAND_PARITY);
+       /*
+        * Clear non-reserved bits in status register.
+        */
+       out_le16((u16 *)(hose_cfg_base + PCI_STATUS), 0xffff);
+       out_8(hose_cfg_base + PCI_LATENCY_TIMER, 0x80);
+       out_8(hose_cfg_base + PCI_CACHE_LINE_SIZE, 0x08);
+
+       printf("PCIE(%s): ", bus->name);
+
+#define PCI_LTSSM      0x404 /* PCIe Link Training, Status State Machine */
+#define PCI_LTSSM_L0   0x16 /* L0 state */
+       ltssm = in_le16((u16 *)(hose_cfg_base + PCI_LTSSM));
+       have_link = (ltssm >= PCI_LTSSM_L0);
+       if (have_link)
+               printf("link\n");
+       else
+               printf("No link\n");
+
+       mpc83xx_pcie_register_hose(bus, priv->region, have_link);
+
+       return 0;
+}
+
+static int pcie_mpc83xx_ofdata_to_platdata(struct udevice *bus)
+{
+       /* TODO: Read region data from DT */
+       /* TODO: Read cfg_space data from DT */
+       return 0;
+}
+
+static const struct dm_pci_ops pcie_mpc83xx_ops = {
+       .read_config    = pcie_mpc83xx_read_config,
+       .write_config   = pcie_mpc83xx_write_config,
+};
+
+static const struct udevice_id pcie_mpc83xx_ids[] = {
+       { .compatible = "fsl,mpc8308-pcie" },
+       { .compatible = "fsl,mpc8314-pcie" },
+       { }
+};
+
+U_BOOT_DRIVER(pcie_mpc83xx) = {
+       .name                   = "pcie_mpc83xx",
+       .id                     = UCLASS_PCI,
+       .of_match               = pcie_mpc83xx_ids,
+       .ops                    = &pcie_mpc83xx_ops,
+       .ofdata_to_platdata     = pcie_mpc83xx_ofdata_to_platdata,
+       .probe                  = pcie_mpc83xx_probe,
+       .priv_auto_alloc_size   = sizeof(struct pcie_mpc83xx),
+};
-- 
2.16.1

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to