From: Hou Zhiqiang <zhiqiang....@nxp.com> Add PCIe driver for the NXP LX series SoCs. This PCIe controller is based on the Mobiveil IP, which is compatible with the PCI Express™ Base Specification, Revision 4.0.
Signed-off-by: Hou Zhiqiang <zhiqiang....@nxp.com> Signed-off-by: Bao Xiaowei <xiaowei....@nxp.com> --- drivers/pci/Kconfig | 8 + drivers/pci/Makefile | 1 + drivers/pci/pcie_lx.c | 612 ++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pcie_lx.h | 258 ++++++++++++++++++ 4 files changed, 879 insertions(+) create mode 100644 drivers/pci/pcie_lx.c create mode 100644 drivers/pci/pcie_lx.h diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index f59803dbd6..f0db158ccf 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -69,6 +69,14 @@ 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_LX + bool "LX PCIe support" + depends on DM_PCI + help + Support PCIe on NXP LX series SoCs, which may have one or several + PCIe controllers. The PCIe may works in RC or EP mode according to + RCW[HOST_AGT_PEX] setting. + config PCI_SANDBOX bool "Sandbox PCI support" depends on SANDBOX && DM_PCI diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 4923641895..bb125c7adc 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -32,5 +32,6 @@ 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_LX) += pcie_lx.o obj-$(CONFIG_PCI_XILINX) += pcie_xilinx.o obj-$(CONFIG_PCIE_INTEL_FPGA) += pcie_intel_fpga.o diff --git a/drivers/pci/pcie_lx.c b/drivers/pci/pcie_lx.c new file mode 100644 index 0000000000..1ba1721e38 --- /dev/null +++ b/drivers/pci/pcie_lx.c @@ -0,0 +1,612 @@ +// SPDX-License-Identifier: GPL-2.0+ OR X11 +/* + * Copyright 2018 NXP + * + * PCIe driver for NXP LX SoCs + * Author: Hou Zhiqiang <minder....@gmail.com> + */ + +#include <common.h> +#include <asm/arch/fsl_serdes.h> +#include <pci.h> +#include <asm/io.h> +#include <errno.h> +#include <malloc.h> +#include <dm.h> +#include "pcie_lx.h" + +DECLARE_GLOBAL_DATA_PTR; + +LIST_HEAD(lx_pcie_list); + +static u64 bar_size[4] = { + PCIE_BAR0_SIZE, + PCIE_BAR1_SIZE, + PCIE_BAR2_SIZE, + PCIE_BAR4_SIZE +}; + +static int lx_pcie_ltssm(struct lx_pcie *pcie) +{ + u32 state; + + state = pf_ctrl_readl(pcie, PCIE_LTSSM_STA) & LTSSM_STATE_MASK; + + return state; +} + +static int lx_pcie_link_up(struct lx_pcie *pcie) +{ + int ltssm; + + ltssm = lx_pcie_ltssm(pcie); + if (ltssm != LTSSM_PCIE_L0) + return 0; + + return 1; +} + +static void lx_pcie_ep_enable_cfg(struct lx_pcie *pcie) +{ + ccsr_writel(pcie, GPEX_CFG_READY, PCIE_CONFIG_READY); +} + +static void lx_pcie_cfg_set_target(struct lx_pcie *pcie, u32 target) +{ + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_L(0), target); + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_H(0), 0); +} + +static int lx_pcie_outbound_win_set(struct lx_pcie *pcie, int idx, int type, + u64 phys, u64 bus_addr, pci_size_t size) +{ + u32 val; + u32 size_h, size_l; + + if (idx > PAB_WINS_NUM) + return -EINVAL; + + size_h = upper_32_bits(~(size - 1)); + size_l = lower_32_bits(~(size - 1)); + + val = ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(idx)); + val &= ~((AXI_AMAP_CTRL_TYPE_MASK << AXI_AMAP_CTRL_TYPE_SHIFT) | + (AXI_AMAP_CTRL_SIZE_MASK << AXI_AMAP_CTRL_SIZE_SHIFT) | + AXI_AMAP_CTRL_EN); + val |= ((type & AXI_AMAP_CTRL_TYPE_MASK) << AXI_AMAP_CTRL_TYPE_SHIFT) | + ((size_l >> AXI_AMAP_CTRL_SIZE_SHIFT) << + AXI_AMAP_CTRL_SIZE_SHIFT) | AXI_AMAP_CTRL_EN; + + ccsr_writel(pcie, PAB_AXI_AMAP_CTRL(idx), val); + + ccsr_writel(pcie, PAB_AXI_AMAP_AXI_WIN(idx), lower_32_bits(phys)); + ccsr_writel(pcie, PAB_EXT_AXI_AMAP_AXI_WIN(idx), upper_32_bits(phys)); + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_L(idx), lower_32_bits(bus_addr)); + ccsr_writel(pcie, PAB_AXI_AMAP_PEX_WIN_H(idx), upper_32_bits(bus_addr)); + ccsr_writel(pcie, PAB_EXT_AXI_AMAP_SIZE(idx), size_h); + + return 0; +} + +static int lx_pcie_inbound_win_set_rc(struct lx_pcie *pcie, int idx, int type, + u64 phys, u64 bus_addr, pci_size_t size) +{ + u32 val; + pci_size_t win_size = ~(size - 1); + + val = ccsr_readl(pcie, PAB_PEX_AMAP_CTRL(idx)); + + val &= ~(PEX_AMAP_CTRL_TYPE_MASK << PEX_AMAP_CTRL_TYPE_SHIFT); + val &= ~(PEX_AMAP_CTRL_EN_MASK << PEX_AMAP_CTRL_EN_SHIFT); + val = (val | (type << PEX_AMAP_CTRL_TYPE_SHIFT)); + val = (val | (1 << PEX_AMAP_CTRL_EN_SHIFT)); + + ccsr_writel(pcie, PAB_PEX_AMAP_CTRL(idx), + val | lower_32_bits(win_size)); + + ccsr_writel(pcie, PAB_EXT_PEX_AMAP_SIZE(idx), upper_32_bits(win_size)); + ccsr_writel(pcie, PAB_PEX_AMAP_AXI_WIN(idx), lower_32_bits(phys)); + ccsr_writel(pcie, PAB_EXT_PEX_AMAP_AXI_WIN(idx), upper_32_bits(phys)); + ccsr_writel(pcie, PAB_PEX_AMAP_PEX_WIN_L(idx), lower_32_bits(bus_addr)); + ccsr_writel(pcie, PAB_PEX_AMAP_PEX_WIN_H(idx), upper_32_bits(bus_addr)); + + return 0; +} + +static void lx_pcie_dump_wins(struct lx_pcie *pcie, int wins) +{ + int i; + + for (i = 0; i < wins; i++) { + debug("APIO Win%d:\n", i); + debug("\tLOWER PHYS: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_AXI_WIN(i))); + debug("\tUPPER PHYS: 0x%08x\n", + ccsr_readl(pcie, PAB_EXT_AXI_AMAP_AXI_WIN(i))); + debug("\tLOWER BUS: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_L(i))); + debug("\tUPPER BUS: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_PEX_WIN_H(i))); + debug("\tSIZE: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(i)) & + (AXI_AMAP_CTRL_SIZE_MASK << AXI_AMAP_CTRL_SIZE_SHIFT)); + debug("\tEXT_SIZE: 0x%08x\n", + ccsr_readl(pcie, PAB_EXT_AXI_AMAP_SIZE(i))); + debug("\tPARAM: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(i))); + debug("\tCTRL: 0x%08x\n", + ccsr_readl(pcie, PAB_AXI_AMAP_CTRL(i))); + } +} + +static void lx_pcie_setup_wins(struct lx_pcie *pcie) +{ + struct pci_region *io, *mem, *pref; + int idx = 1; + + /* INBOUND WIN */ + lx_pcie_inbound_win_set_rc(pcie, 0, IB_TYPE_MEM_F, 0, 0, SIZE_1T); + + /* OUTBOUND WIN 0: CFG */ + lx_pcie_outbound_win_set(pcie, 0, + PAB_AXI_TYPE_CFG, + pcie->cfg_res.start, + 0, + fdt_resource_size(&pcie->cfg_res)); + + pci_get_regions(pcie->bus, &io, &mem, &pref); + + if (io) + /* OUTBOUND WIN: IO */ + lx_pcie_outbound_win_set(pcie, idx++, + PAB_AXI_TYPE_IO, + io->phys_start, + io->bus_start, + io->size); + + if (mem) + /* OUTBOUND WIN: MEM */ + lx_pcie_outbound_win_set(pcie, idx++, + PAB_AXI_TYPE_MEM, + mem->phys_start, + mem->bus_start, + mem->size); + + if (pref) + /* OUTBOUND WIN: perf MEM */ + lx_pcie_outbound_win_set(pcie, idx++, + PAB_AXI_TYPE_MEM, + pref->phys_start, + pref->bus_start, + pref->size); + + lx_pcie_dump_wins(pcie, idx); +} + +/* Return 0 if the address is valid, -errno if not valid */ +static int lx_pcie_addr_valid(struct lx_pcie *pcie, pci_dev_t bdf) +{ + struct udevice *bus = pcie->bus; + + if (pcie->mode == PCI_HEADER_TYPE_NORMAL) + return -ENODEV; + + if (!pcie->enabled) + return -ENXIO; + + if (PCI_BUS(bdf) < bus->seq) + return -EINVAL; + + if ((PCI_BUS(bdf) > bus->seq) && (!lx_pcie_link_up(pcie))) + return -EINVAL; + + if (PCI_BUS(bdf) <= (bus->seq + 1) && (PCI_DEV(bdf) > 0)) + return -EINVAL; + + return 0; +} + +void *lx_pcie_conf_address(struct lx_pcie *pcie, pci_dev_t bdf, + int offset) +{ + struct udevice *bus = pcie->bus; + u32 target; + + if (PCI_BUS(bdf) == bus->seq) { + if (offset < INDIRECT_ADDR_BNDRY) { + ccsr_set_page(pcie, 0); + return pcie->ccsr + offset; + } + + ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset)); + return pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset); + } + + target = PAB_TARGET_BUS(PCI_BUS(bdf) - bus->seq) | + PAB_TARGET_DEV(PCI_DEV(bdf)) | + PAB_TARGET_FUNC(PCI_FUNC(bdf)); + + lx_pcie_cfg_set_target(pcie, target); + + return pcie->cfg + offset; +} + +static int lx_pcie_read_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct lx_pcie *pcie = dev_get_priv(bus); + void *address; + int ret = 0; + + if (lx_pcie_addr_valid(pcie, bdf)) { + *valuep = pci_get_ff(size); + return 0; + } + + address = lx_pcie_conf_address(pcie, bdf, offset); + + switch (size) { + case PCI_SIZE_8: + *valuep = readb(address); + break; + case PCI_SIZE_16: + *valuep = readw(address); + break; + case PCI_SIZE_32: + *valuep = readl(address); + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int lx_pcie_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct lx_pcie *pcie = dev_get_priv(bus); + void *address; + + if (lx_pcie_addr_valid(pcie, bdf)) + return 0; + + address = lx_pcie_conf_address(pcie, bdf, offset); + + switch (size) { + case PCI_SIZE_8: + writeb(value, address); + return 0; + case PCI_SIZE_16: + writew(value, address); + return 0; + case PCI_SIZE_32: + writel(value, address); + return 0; + default: + return -EINVAL; + } +} + +static void lx_pcie_setup_ctrl(struct lx_pcie *pcie) +{ + u32 val; + + /* Fix class code */ + val = ccsr_readl(pcie, GPEX_CLASSCODE); + val &= ~(GPEX_CLASSCODE_MASK << GPEX_CLASSCODE_SHIFT); + val |= PCI_CLASS_BRIDGE_PCI << GPEX_CLASSCODE_SHIFT; + ccsr_writel(pcie, GPEX_CLASSCODE, val); + + /* Enable APIO and Memory/IO/CFG Wins */ + val = ccsr_readl(pcie, PAB_AXI_PIO_CTRL(0)); + val |= APIO_EN | MEM_WIN_EN | IO_WIN_EN | CFG_WIN_EN; + ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val); + + lx_pcie_setup_wins(pcie); + + pcie->stream_id_cur = 0; +} + +static void lx_pcie_ep_inbound_win_set(struct lx_pcie *pcie, int func, + int bar, u64 phys) +{ + ccsr_writel(pcie, PAB_EXT_PEX_BAR_AMAP(func, bar), upper_32_bits(phys)); + ccsr_writel(pcie, PAB_PEX_BAR_AMAP(func, bar), lower_32_bits(phys) | 1); +} + +static void lx_pcie_ep_setup_wins(struct lx_pcie *pcie, int pf) +{ + u64 phys; + int bar, bar_num; + u32 val; + + if (pcie->sriov_enabled) { + bar_num = 8; + if (pf == 1) + phys = CONFIG_SYS_PCI_EP_MEMORY_BASE + + PCIE_BAR_SIZE * 4; + else + phys = CONFIG_SYS_PCI_EP_MEMORY_BASE; + } else { + bar_num = 4; + phys = CONFIG_SYS_PCI_EP_MEMORY_BASE; + } + + for (bar = 0; bar < bar_num; bar++) { + if (bar == 1 || bar == 5) + ccsr_writel(pcie, PAB_PEX_BAR_AMAP(pf, bar), 1); + else + if (bar < 4) { + lx_pcie_ep_inbound_win_set(pcie, pf, bar, phys); + phys += PCIE_BAR_SIZE; + } + } + + /* WIN 0 : OUTBOUND : map MEM */ + if (pf == 1) { + lx_pcie_outbound_win_set(pcie, 1, PAB_AXI_TYPE_MEM, + pcie->cfg_res.start + + CONFIG_SYS_PCI_MEMORY_SIZE, + 0x00000000, + CONFIG_SYS_PCI_MEMORY_SIZE); + val = ccsr_readl(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(1)); + ccsr_writel(pcie, PAB_AXI_AMAP_PCI_HDR_PARAM(1), val | 1); + } else if (pf == 0) { + lx_pcie_outbound_win_set(pcie, 0, PAB_AXI_TYPE_MEM, + pcie->cfg_res.start, 0x00000000, + CONFIG_SYS_PCI_MEMORY_SIZE); + } +} + +static void lx_pcie_ep_enable_bar(struct lx_pcie *pcie, int bar) +{ + u32 val; + + val = ccsr_readl(pcie, GPEX_BAR_ENABLE); + val |= 1 << bar; + ccsr_writel(pcie, GPEX_BAR_ENABLE, val); +} + +static void lx_pcie_ep_disable_bar(struct lx_pcie *pcie, int bar) +{ + u32 val; + + val = ccsr_readl(pcie, GPEX_BAR_ENABLE); + val &= ~(1 << bar); + ccsr_writel(pcie, GPEX_BAR_ENABLE, val); +} + +static void lx_pcie_ep_set_bar_size(struct lx_pcie *pcie, int bar, u64 size) +{ + u32 mask_l = lower_32_bits(~(size - 1)); + u32 mask_h = upper_32_bits(~(size - 1)); + + ccsr_writel(pcie, GPEX_BAR_SELECT, bar); + ccsr_writel(pcie, GPEX_BAR_SIZE_LDW, mask_l); + ccsr_writel(pcie, GPEX_BAR_SIZE_UDW, mask_h); +} + +static void lx_pcie_ep_setup_bar_vf(struct lx_pcie *pcie, + int pf, int bar, u64 size) +{ + if (pf == LX_PF1) + bar += PF1_VF_BAR_OFFSET; + else + bar += PF0_VF_BAR_OFFSET; + + if (size == 0) + lx_pcie_ep_disable_bar(pcie, bar); + else + lx_pcie_ep_enable_bar(pcie, bar); + + lx_pcie_ep_set_bar_size(pcie, bar, size); +} + +static void lx_pcie_ep_setup_bar_pf(struct lx_pcie *pcie, + int pf, int bar, u64 size) +{ + if (pf == LX_PF1) + bar += PF1_BAR_OFFSET; + + if (size == 0) + lx_pcie_ep_disable_bar(pcie, bar); + else + lx_pcie_ep_enable_bar(pcie, bar); + + lx_pcie_ep_set_bar_size(pcie, bar, size); +} + +static void lx_pcie_ep_setup_bars(struct lx_pcie *pcie, int pf) +{ + int bar; + + for (bar = 0; bar < BAR_NUM; bar++) + lx_pcie_ep_setup_bar_pf(pcie, pf, bar, bar_size[bar]); + if (pcie->sriov_enabled) { + for (bar = 0; bar < BAR_NUM; bar++) + lx_pcie_ep_setup_bar_vf(pcie, pf, bar, bar_size[bar]); + } +} + +static void lx_pcie_set_sriov(struct lx_pcie *pcie, int func) +{ + unsigned int val; + + val = ccsr_readl(pcie, GPEX_SRIOV_INIT_VFS_TOTAL_VF(func)); + val &= ~(TTL_VF_MASK << TTL_VF_SHIFT); + val |= PCIE_VF_NUM << TTL_VF_SHIFT; + val &= ~(INI_VF_MASK << INI_VF_SHIFT); + val |= PCIE_VF_NUM << INI_VF_SHIFT; + ccsr_writel(pcie, GPEX_SRIOV_INIT_VFS_TOTAL_VF(func), val); + + if (func == 1) { + val = ccsr_readl(pcie, PCIE_SRIOV_VF_OFFSET_STRIDE); + val += PCIE_VF_NUM - 1; + ccsr_writel(pcie, GPEX_SRIOV_VF_OFFSET_STRIDE(func), val); + } +} + +static void lx_pcie_setup_ep(struct lx_pcie *pcie) +{ + u32 pf; + u32 sriov; + u32 val; + + /* Enable APIO and Memory Win */ + val = ccsr_readl(pcie, PAB_AXI_PIO_CTRL(0)); + val |= APIO_EN | MEM_WIN_EN; + ccsr_writel(pcie, PAB_AXI_PIO_CTRL(0), val); + + sriov = ccsr_readl(pcie, PCIE_SRIOV_CAPABILITY); + if (PCI_EXT_CAP_ID(sriov) == PCI_EXT_CAP_ID_SRIOV) { + pcie->sriov_enabled = 1; + + for (pf = 0; pf < PCIE_PF_NUM; pf++) { + lx_pcie_ep_setup_bars(pcie, pf); + lx_pcie_ep_setup_wins(pcie, pf); + lx_pcie_set_sriov(pcie, pf); + } + } else { + pcie->sriov_enabled = 0; + + lx_pcie_ep_setup_bars(pcie, 0); + lx_pcie_ep_setup_wins(pcie, 0); + } + + lx_pcie_ep_enable_cfg(pcie); + lx_pcie_dump_wins(pcie, 2); +} + +static int lx_pcie_probe(struct udevice *dev) +{ + struct lx_pcie *pcie = dev_get_priv(dev); + const void *fdt = gd->fdt_blob; + int node = dev_of_offset(dev); + u32 link_ctrl_sta; + u32 val; + int ret; + + pcie->bus = dev; + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "ccsr", &pcie->ccsr_res); + if (ret) { + printf("lx-pcie: resource \"ccsr\" not found\n"); + return ret; + } + + pcie->idx = (pcie->ccsr_res.start - PCIE_SYS_BASE_ADDR) / + PCIE_CCSR_SIZE; + + list_add(&pcie->list, &lx_pcie_list); + + pcie->enabled = is_serdes_configured(PCIE_SRDS_PRTCL(pcie->idx)); + if (!pcie->enabled) { + printf("PCIe%d: %s disabled\n", pcie->idx, dev->name); + return 0; + } + + pcie->ccsr = map_physmem(pcie->ccsr_res.start, + fdt_resource_size(&pcie->ccsr_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "config", &pcie->cfg_res); + if (ret) { + printf("%s: resource \"config\" not found\n", dev->name); + return ret; + } + + pcie->cfg = map_physmem(pcie->cfg_res.start, + fdt_resource_size(&pcie->cfg_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "lut", &pcie->lut_res); + if (ret) { + printf("lx-pcie: resource \"lut\" not found\n"); + return ret; + } + + pcie->lut = map_physmem(pcie->lut_res.start, + fdt_resource_size(&pcie->lut_res), + MAP_NOCACHE); + + ret = fdt_get_named_resource(fdt, node, "reg", "reg-names", + "pf_ctrl", &pcie->pf_ctrl_res); + if (ret) { + printf("lx-pcie: resource \"pf_ctrl\" not found\n"); + return ret; + } + + pcie->pf_ctrl = map_physmem(pcie->pf_ctrl_res.start, + fdt_resource_size(&pcie->pf_ctrl_res), + MAP_NOCACHE); + + pcie->big_endian = fdtdec_get_bool(fdt, node, "big-endian"); + + debug("%s ccsr:%lx, cfg:0x%lx, big-endian:%d\n", + dev->name, (unsigned long)pcie->ccsr, (unsigned long)pcie->cfg, + pcie->big_endian); + + pcie->mode = readb(pcie->ccsr + PCI_HEADER_TYPE) & 0x7f; + + if (pcie->mode == PCI_HEADER_TYPE_NORMAL) { + printf("PCIe%u: %s %s", pcie->idx, dev->name, "Endpoint"); + lx_pcie_setup_ep(pcie); + } else { + printf("PCIe%u: %s %s", pcie->idx, dev->name, "Root Complex"); + lx_pcie_setup_ctrl(pcie); + } + + /* Enable Amba & PEX PIO */ + val = ccsr_readl(pcie, PAB_CTRL); + val |= PAB_CTRL_APIO_EN | PAB_CTRL_PPIO_EN; + ccsr_writel(pcie, PAB_CTRL, val); + + val = ccsr_readl(pcie, PAB_PEX_PIO_CTRL(0)); + val |= PPIO_EN; + ccsr_writel(pcie, PAB_PEX_PIO_CTRL(0), val); + + if (!lx_pcie_link_up(pcie)) { + /* Let the user know there's no PCIe link */ + printf(": no link\n"); + return 0; + } + + /* Print the negotiated PCIe link width */ + link_ctrl_sta = ccsr_readl(pcie, PCIE_LINK_CTRL_STA); + printf(": x%d gen%d\n", + (link_ctrl_sta >> PCIE_LINK_WIDTH_SHIFT & PCIE_LINK_WIDTH_MASK), + (link_ctrl_sta >> PCIE_LINK_SPEED_SHIFT) & PCIE_LINK_SPEED_MASK); + + return 0; +} + +static const struct dm_pci_ops lx_pcie_ops = { + .read_config = lx_pcie_read_config, + .write_config = lx_pcie_write_config, +}; + +static const struct udevice_id lx_pcie_ids[] = { + { .compatible = "fsl,lx2160a-pcie" }, + { } +}; + +U_BOOT_DRIVER(pcie_lx) = { + .name = "pcie_lx", + .id = UCLASS_PCI, + .of_match = lx_pcie_ids, + .ops = &lx_pcie_ops, + .probe = lx_pcie_probe, + .priv_auto_alloc_size = sizeof(struct lx_pcie), +}; + +/* No any fixup so far */ +void ft_pci_setup(void *blob, bd_t *bd) +{ +} diff --git a/drivers/pci/pcie_lx.h b/drivers/pci/pcie_lx.h new file mode 100644 index 0000000000..4deb52ffff --- /dev/null +++ b/drivers/pci/pcie_lx.h @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2018 NXP + * + * PCIe driver for NXP LX SoCs + * Author: Hou Zhiqiang <minder....@gmail.com> + */ + +#ifndef _PCIE_LX_H_ +#define _PCIE_LX_H_ +#include <pci.h> +#include <dm.h> + +#ifndef CONFIG_SYS_PCI_MEMORY_SIZE +#define CONFIG_SYS_PCI_MEMORY_SIZE (4 * 1024 * 1024 * 1024ULL) +#endif + +#ifndef CONFIG_SYS_PCI_EP_MEMORY_BASE +#define CONFIG_SYS_PCI_EP_MEMORY_BASE CONFIG_SYS_LOAD_ADDR +#endif + +#define PCIE_PF_NUM 2 +#define PCIE_VF_NUM 32 +#define PCIE_SRIOV_CAPABILITY 0x2a0 +#define PCI_EXT_CAP_ID_SRIOV 0x10 +#define PCIE_SRIOV_VF_OFFSET_STRIDE 0x2b4 + +#define LX_PF1 1 +#define BAR_NUM 4 +#define PF1_BAR_OFFSET 4 +#define PF0_VF_BAR_OFFSET 8 +#define PF1_VF_BAR_OFFSET 12 +#define PCIE_BAR_SIZE (8 * 1024) /* 8K */ +#define PCIE_BAR0_SIZE PCIE_BAR_SIZE +#define PCIE_BAR1_SIZE PCIE_BAR_SIZE +#define PCIE_BAR2_SIZE PCIE_BAR_SIZE +#define PCIE_BAR4_SIZE PCIE_BAR_SIZE +#define SIZE_1T (1024 * 1024 * 1024 * 1024ULL) +#define SIZE_1M (1024 * 1024) + +/* LUT registers */ +#define PCIE_LUT_UDR(n) (0x800 + (n) * 8) +#define PCIE_LUT_LDR(n) (0x804 + (n) * 8) +#define PCIE_LUT_ENABLE BIT(31) +#define PCIE_LUT_ENTRY_COUNT 32 + +/* GPEX CSR */ +#define GPEX_CLASSCODE 0x474 +#define GPEX_CLASSCODE_SHIFT 16 +#define GPEX_CLASSCODE_MASK 0xffff + +#define GPEX_CFG_READY 0x4b0 +#define PCIE_CONFIG_READY BIT(0) +#define GPEX_BAR_ENABLE 0x4d4 +#define GPEX_BAR_SIZE_LDW 0x4d8 +#define GPEX_BAR_SIZE_UDW 0x4dC +#define GPEX_BAR_SELECT 0x4e0 +#define GPEX_SRIOV_INIT_VFS_TOTAL_VF(func) (0x644 + (func) * 4) +#define TTL_VF_MASK 0xffff +#define TTL_VF_SHIFT 16 +#define INI_VF_MASK 0xffff +#define INI_VF_SHIFT 0 +#define GPEX_SRIOV_VF_OFFSET_STRIDE(func) (0x704 + (func) * 4) + +/* PAB CSR */ +#define PAB_CTRL 0x808 +#define PAB_CTRL_APIO_EN BIT(0) +#define PAB_CTRL_PPIO_EN BIT(1) +#define PAB_CTRL_MAX_BRST_LEN_SHIFT 4 +#define PAB_CTRL_MAX_BRST_LEN_MASK 0x3 +#define PAB_CTRL_PAGE_SEL_SHIFT 13 +#define PAB_CTRL_PAGE_SEL_MASK 0x3f +#define PAB_CTRL_FUNC_SEL_SHIFT 19 +#define PAB_CTRL_FUNC_SEL_MASK 0x1ff + +#define PAB_RST_CTRL 0x820 +#define PAB_BR_STAT 0x80c +/* AXI PIO Engines */ +#define PAB_AXI_PIO_CTRL(idx) (0x840 + 0x10 * (idx)) +#define APIO_EN BIT(0) +#define MEM_WIN_EN BIT(1) +#define IO_WIN_EN BIT(2) +#define CFG_WIN_EN BIT(3) +#define PAB_AXI_PIO_STAT(idx) (0x844 + 0x10 * (idx)) +#define PAB_AXI_PIO_SL_CMD_STAT(idx) (0x848 + 0x10 * (idx)) +#define PAB_AXI_PIO_SL_ADDR_STAT(idx) (0x84c + 0x10 * (idx)) +#define PAB_AXI_PIO_SL_EXT_ADDR_STAT(idx) (0xb8a0 + 0x4 * (idx)) + +/* PEX PIO Engines */ +#define PAB_PEX_PIO_CTRL(idx) (0x8c0 + 0x10 * (idx)) +#define PPIO_EN BIT(0) +#define PAB_PEX_PIO_STAT(idx) (0x8c4 + 0x10 * (idx)) +#define PAB_PEX_PIO_MT_STAT(idx) (0x8c8 + 0x10 * (idx)) + +#define INDIRECT_ADDR_BNDRY 0xc00 +#define PAGE_IDX_SHIFT 10 +#define PAGE_ADDR_MASK 0x3ff + +#define OFFSET_TO_PAGE_IDX(off) \ + (((off) >> PAGE_IDX_SHIFT) & PAB_CTRL_PAGE_SEL_MASK) + +#define OFFSET_TO_PAGE_ADDR(off) \ + (((off) & PAGE_ADDR_MASK) | INDIRECT_ADDR_BNDRY) + +/* APIO WINs */ +#define PAB_AXI_AMAP_CTRL(idx) (0xba0 + 0x10 * (idx)) +#define PAB_EXT_AXI_AMAP_SIZE(idx) (0xbaf0 + 0x4 * (idx)) +#define PAB_AXI_AMAP_AXI_WIN(idx) (0xba4 + 0x10 * (idx)) +#define PAB_EXT_AXI_AMAP_AXI_WIN(idx) (0x80a0 + 0x4 * (idx)) +#define PAB_AXI_AMAP_PEX_WIN_L(idx) (0xba8 + 0x10 * (idx)) +#define PAB_AXI_AMAP_PEX_WIN_H(idx) (0xbac + 0x10 * (idx)) +#define PAB_AXI_AMAP_PCI_HDR_PARAM(idx) (0x5ba0 + 0x4 * (idx)) + +#define AXI_AMAP_CTRL_EN BIT(0) +#define AXI_AMAP_CTRL_TYPE_SHIFT 1 +#define AXI_AMAP_CTRL_TYPE_MASK 0x3 +#define AXI_AMAP_CTRL_SIZE_SHIFT 10 +#define AXI_AMAP_CTRL_SIZE_MASK 0x3fffff + +#define PAB_TARGET_BUS(x) (((x) & 0xff) << 24) +#define PAB_TARGET_DEV(x) (((x) & 0x1f) << 19) +#define PAB_TARGET_FUNC(x) (((x) & 0x7) << 16) + +#define PAB_AXI_TYPE_CFG 0x00 +#define PAB_AXI_TYPE_IO 0x01 +#define PAB_AXI_TYPE_MEM 0x02 +#define PAB_AXI_TYPE_ATOM 0x03 + +#define PAB_WINS_NUM 256 + +/* PPIO WINs RC mode */ +#define PAB_PEX_AMAP_CTRL(idx) (0x4ba0 + 0x10 * (idx)) +#define PAB_EXT_PEX_AMAP_SIZE(idx) (0xbef0 + 0x04 * (idx)) +#define PAB_PEX_AMAP_AXI_WIN(idx) (0x4ba4 + 0x10 * (idx)) +#define PAB_EXT_PEX_AMAP_AXI_WIN(idx) (0xb4a0 + 0x04 * (idx)) +#define PAB_PEX_AMAP_PEX_WIN_L(idx) (0x4ba8 + 0x10 * (idx)) +#define PAB_PEX_AMAP_PEX_WIN_H(idx) (0x4bac + 0x10 * (idx)) + +#define IB_TYPE_MEM_F 0x2 +#define IB_TYPE_MEM_NF 0x3 + +#define PEX_AMAP_CTRL_TYPE_SHIFT 0x1 +#define PEX_AMAP_CTRL_EN_SHIFT 0x0 +#define PEX_AMAP_CTRL_TYPE_MASK 0x3 +#define PEX_AMAP_CTRL_EN_MASK 0x1 + +/* PPIO WINs EP mode */ +#define PAB_PEX_BAR_AMAP(func, bar) \ + (0x1ba0 + 0x20 * (func) + 4 * (bar)) +#define PAB_EXT_PEX_BAR_AMAP(func, bar) \ + (0x84a0 + 0x20 * (func) + 4 * (bar)) + +/* CCSR registers */ +#define PCIE_LINK_CTRL_STA 0x5c +#define PCIE_LINK_SPEED_SHIFT 16 +#define PCIE_LINK_SPEED_MASK 0x0f +#define PCIE_LINK_WIDTH_SHIFT 20 +#define PCIE_LINK_WIDTH_MASK 0x3f + +/* PF control registers */ +#define PCIE_LTSSM_STA 0x7fc +#define LTSSM_STATE_MASK 0x7f +#define LTSSM_PCIE_L0 0x2d /* L0 state */ + +#define PCIE_SRDS_PRTCL(idx) (PCIE1 + (idx)) +#define PCIE_SYS_BASE_ADDR 0x3400000 +#define PCIE_CCSR_SIZE 0x0100000 + +struct lx_pcie { + int idx; + struct list_head list; + struct udevice *bus; + struct fdt_resource ccsr_res; + struct fdt_resource cfg_res; + struct fdt_resource lut_res; + struct fdt_resource pf_ctrl_res; + void __iomem *ccsr; + void __iomem *cfg; + void __iomem *lut; + void __iomem *pf_ctrl; + bool big_endian; + bool enabled; + int next_lut_index; + struct pci_controller hose; + int stream_id_cur; + int mode; + int sriov_enabled; +}; + +extern struct list_head lx_pcie_list; + +static inline void lut_writel(struct lx_pcie *pcie, unsigned int value, + unsigned int offset) +{ + if (pcie->big_endian) + out_be32(pcie->lut + offset, value); + else + out_le32(pcie->lut + offset, value); +} + +static inline u32 lut_readl(struct lx_pcie *pcie, unsigned int offset) +{ + if (pcie->big_endian) + return in_be32(pcie->lut + offset); + else + return in_le32(pcie->lut + offset); +} + +static inline void ccsr_set_page(struct lx_pcie *pcie, u8 pg_idx) +{ + u32 val; + + val = in_le32(pcie->ccsr + PAB_CTRL); + val &= ~(PAB_CTRL_PAGE_SEL_MASK << PAB_CTRL_PAGE_SEL_SHIFT); + val |= (pg_idx & PAB_CTRL_PAGE_SEL_MASK) << PAB_CTRL_PAGE_SEL_SHIFT; + + out_le32(pcie->ccsr + PAB_CTRL, val); +} + +static inline unsigned int ccsr_readl(struct lx_pcie *pcie, u32 offset) +{ + if (offset < INDIRECT_ADDR_BNDRY) { + ccsr_set_page(pcie, 0); + return in_le32(pcie->ccsr + offset); + } + + ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset)); + return in_le32(pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset)); +} + +static inline void ccsr_writel(struct lx_pcie *pcie, u32 offset, u32 value) +{ + if (offset < INDIRECT_ADDR_BNDRY) { + ccsr_set_page(pcie, 0); + out_le32(pcie->ccsr + offset, value); + } else { + ccsr_set_page(pcie, OFFSET_TO_PAGE_IDX(offset)); + out_le32(pcie->ccsr + OFFSET_TO_PAGE_ADDR(offset), value); + } +} + +static inline unsigned int pf_ctrl_readl(struct lx_pcie *pcie, u32 offset) +{ + if (pcie->big_endian) + return in_be32(pcie->pf_ctrl + offset); + else + return in_le32(pcie->pf_ctrl + offset); +} + +static inline void pf_ctrl_writel(struct lx_pcie *pcie, u32 offset, u32 value) +{ + if (pcie->big_endian) + out_be32(pcie->pf_ctrl + offset, value); + else + out_le32(pcie->pf_ctrl + offset, value); +} + +#endif /* _PCIE_LX_H_ */ -- 2.17.1 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot