From patchwork Fri Apr 27 12:53:39 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mario Six X-Patchwork-Id: 905732 X-Patchwork-Delegate: sjg@chromium.org Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=gdsys.cc Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 40XYyd2lzMz9s1j for ; Fri, 27 Apr 2018 23:03:20 +1000 (AEST) Received: by lists.denx.de (Postfix, from userid 105) id B145AC22174; Fri, 27 Apr 2018 12:57:41 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=KHOP_BIG_TO_CC, SPF_HELO_PASS autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id 5C467C220D4; Fri, 27 Apr 2018 12:55:32 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 0FBCDC22053; Fri, 27 Apr 2018 12:54:07 +0000 (UTC) Received: from smtprelay08.ispgateway.de (smtprelay08.ispgateway.de [134.119.228.108]) by lists.denx.de (Postfix) with ESMTPS id CFD81C2210A for ; Fri, 27 Apr 2018 12:54:03 +0000 (UTC) Received: from [80.151.34.241] (helo=bob3.testumgebung.local) by smtprelay08.ispgateway.de with esmtpa (Exim 4.90_1) (envelope-from ) id 1fC2sz-0007S3-9x; Fri, 27 Apr 2018 14:54:01 +0200 From: Mario Six To: Wolfgang Denk , Bin Meng , Tuomas Tynkkynen , Simon Glass , Andy Shevchenko , Marek Vasut , Wilson Ding , Stefan Roese , Hua Jing , Victor Gu , Hannes Schmelzer , Bernhard Messerklinger , Masahiro Yamada , Heinrich Schuchardt , Heiko Schocher , U-Boot Mailing List Date: Fri, 27 Apr 2018 14:53:39 +0200 Message-Id: <20180427125339.1308-3-mario.six@gdsys.cc> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20180427125339.1308-1-mario.six@gdsys.cc> References: <20180427125339.1308-1-mario.six@gdsys.cc> X-Df-Sender: bWFyaW8uc2l4QGdkc3lzLmNj Subject: [U-Boot] [PATCH v2 3/3] pci: Add MPC83xx PCIe driver X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Add a PCIe driver for the MPC83xx architecture. Signed-off-by: Mario Six --- v1 -> v2: No changes --- 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 #include #include @@ -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 c20a0cc060..dcea7ffdb3 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -69,6 +69,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 40ebc06f6d..ae7ef1d811 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -34,4 +34,5 @@ obj-$(CONFIG_PCI_AARDVARK) += pci-aardvark.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@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +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), +};