[U-Boot] [PATCH 3/3] pci: Add MPC83xx PCIe driver
Mario Six
mario.six at gdsys.cc
Wed Mar 28 12:40:12 UTC 2018
Add a PCIe driver for the MPC83xx architecture.
Signed-off-by: Mario Six <mario.six at 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.six at 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
More information about the U-Boot
mailing list