diff options
Diffstat (limited to 'roms/u-boot/drivers/pci/pcie_fsl.c')
-rw-r--r-- | roms/u-boot/drivers/pci/pcie_fsl.c | 668 |
1 files changed, 668 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/pci/pcie_fsl.c b/roms/u-boot/drivers/pci/pcie_fsl.c new file mode 100644 index 000000000..3c2a2a476 --- /dev/null +++ b/roms/u-boot/drivers/pci/pcie_fsl.c @@ -0,0 +1,668 @@ +// SPDX-License-Identifier: GPL-2.0+ OR X11 +/* + * Copyright 2019 NXP + * + * PCIe DM U-Boot driver for Freescale PowerPC SoCs + * Author: Hou Zhiqiang <Zhiqiang.Hou@nxp.com> + */ + +#include <common.h> +#include <dm.h> +#include <malloc.h> +#include <mapmem.h> +#include <pci.h> +#include <asm/fsl_pci.h> +#include <asm/fsl_serdes.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/delay.h> +#include "pcie_fsl.h" +#include <dm/device_compat.h> + +LIST_HEAD(fsl_pcie_list); + +static int fsl_pcie_link_up(struct fsl_pcie *pcie); + +static int fsl_pcie_addr_valid(struct fsl_pcie *pcie, pci_dev_t bdf) +{ + struct udevice *bus = pcie->bus; + + if (!pcie->enabled) + return -ENXIO; + + if (PCI_BUS(bdf) < dev_seq(bus)) + return -EINVAL; + + if (PCI_BUS(bdf) > dev_seq(bus) && (!fsl_pcie_link_up(pcie) || pcie->mode)) + return -EINVAL; + + if (PCI_BUS(bdf) == dev_seq(bus) && (PCI_DEV(bdf) > 0 || PCI_FUNC(bdf) > 0)) + return -EINVAL; + + if (PCI_BUS(bdf) == (dev_seq(bus) + 1) && (PCI_DEV(bdf) > 0)) + return -EINVAL; + + return 0; +} + +static int fsl_pcie_read_config(const struct udevice *bus, pci_dev_t bdf, + uint offset, ulong *valuep, + enum pci_size_t size) +{ + struct fsl_pcie *pcie = dev_get_priv(bus); + ccsr_fsl_pci_t *regs = pcie->regs; + u32 val; + + if (fsl_pcie_addr_valid(pcie, bdf)) { + *valuep = pci_get_ff(size); + return 0; + } + + bdf = bdf - PCI_BDF(dev_seq(bus), 0, 0); + val = bdf | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000; + out_be32(®s->cfg_addr, val); + + sync(); + + switch (size) { + case PCI_SIZE_8: + *valuep = in_8((u8 *)®s->cfg_data + (offset & 3)); + break; + case PCI_SIZE_16: + *valuep = in_le16((u16 *)((u8 *)®s->cfg_data + + (offset & 2))); + break; + case PCI_SIZE_32: + *valuep = in_le32(®s->cfg_data); + break; + } + + return 0; +} + +static int fsl_pcie_write_config(struct udevice *bus, pci_dev_t bdf, + uint offset, ulong value, + enum pci_size_t size) +{ + struct fsl_pcie *pcie = dev_get_priv(bus); + ccsr_fsl_pci_t *regs = pcie->regs; + u32 val; + u8 val_8; + u16 val_16; + u32 val_32; + + if (fsl_pcie_addr_valid(pcie, bdf)) + return 0; + + bdf = bdf - PCI_BDF(dev_seq(bus), 0, 0); + val = bdf | (offset & 0xfc) | ((offset & 0xf00) << 16) | 0x80000000; + out_be32(®s->cfg_addr, val); + + sync(); + + switch (size) { + case PCI_SIZE_8: + val_8 = value; + out_8((u8 *)®s->cfg_data + (offset & 3), val_8); + break; + case PCI_SIZE_16: + val_16 = value; + out_le16((u16 *)((u8 *)®s->cfg_data + (offset & 2)), val_16); + break; + case PCI_SIZE_32: + val_32 = value; + out_le32(®s->cfg_data, val_32); + break; + } + + return 0; +} + +static int fsl_pcie_hose_read_config(struct fsl_pcie *pcie, uint offset, + ulong *valuep, enum pci_size_t size) +{ + int ret; + struct udevice *bus = pcie->bus; + + ret = fsl_pcie_read_config(bus, PCI_BDF(dev_seq(bus), 0, 0), + offset, valuep, size); + + return ret; +} + +static int fsl_pcie_hose_write_config(struct fsl_pcie *pcie, uint offset, + ulong value, enum pci_size_t size) +{ + struct udevice *bus = pcie->bus; + + return fsl_pcie_write_config(bus, PCI_BDF(dev_seq(bus), 0, 0), + offset, value, size); +} + +static int fsl_pcie_hose_read_config_byte(struct fsl_pcie *pcie, uint offset, + u8 *valuep) +{ + ulong val; + int ret; + + ret = fsl_pcie_hose_read_config(pcie, offset, &val, PCI_SIZE_8); + *valuep = val; + + return ret; +} + +static int fsl_pcie_hose_read_config_word(struct fsl_pcie *pcie, uint offset, + u16 *valuep) +{ + ulong val; + int ret; + + ret = fsl_pcie_hose_read_config(pcie, offset, &val, PCI_SIZE_16); + *valuep = val; + + return ret; +} + +static int fsl_pcie_hose_read_config_dword(struct fsl_pcie *pcie, uint offset, + u32 *valuep) +{ + ulong val; + int ret; + + ret = fsl_pcie_hose_read_config(pcie, offset, &val, PCI_SIZE_32); + *valuep = val; + + return ret; +} + +static int fsl_pcie_hose_write_config_byte(struct fsl_pcie *pcie, uint offset, + u8 value) +{ + return fsl_pcie_hose_write_config(pcie, offset, value, PCI_SIZE_8); +} + +static int fsl_pcie_hose_write_config_word(struct fsl_pcie *pcie, uint offset, + u16 value) +{ + return fsl_pcie_hose_write_config(pcie, offset, value, PCI_SIZE_16); +} + +static int fsl_pcie_hose_write_config_dword(struct fsl_pcie *pcie, uint offset, + u32 value) +{ + return fsl_pcie_hose_write_config(pcie, offset, value, PCI_SIZE_32); +} + +static int fsl_pcie_link_up(struct fsl_pcie *pcie) +{ + ccsr_fsl_pci_t *regs = pcie->regs; + u16 ltssm; + + if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) { + ltssm = (in_be32(®s->pex_csr0) + & PEX_CSR0_LTSSM_MASK) >> PEX_CSR0_LTSSM_SHIFT; + return ltssm == LTSSM_L0_REV3; + } + + fsl_pcie_hose_read_config_word(pcie, PCI_LTSSM, <ssm); + + return ltssm == LTSSM_L0; +} + +static bool fsl_pcie_is_agent(struct fsl_pcie *pcie) +{ + u8 header_type; + + fsl_pcie_hose_read_config_byte(pcie, PCI_HEADER_TYPE, &header_type); + + return (header_type & 0x7f) == PCI_HEADER_TYPE_NORMAL; +} + +static int fsl_pcie_setup_law(struct fsl_pcie *pcie) +{ + struct pci_region *io, *mem, *pref; + + pci_get_regions(pcie->bus, &io, &mem, &pref); + + if (mem) + set_next_law(mem->phys_start, + law_size_bits(mem->size), + pcie->law_trgt_if); + + if (io) + set_next_law(io->phys_start, + law_size_bits(io->size), + pcie->law_trgt_if); + + return 0; +} + +static void fsl_pcie_config_ready(struct fsl_pcie *pcie) +{ + ccsr_fsl_pci_t *regs = pcie->regs; + + if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) { + setbits_be32(®s->config, FSL_PCIE_V3_CFG_RDY); + return; + } + + fsl_pcie_hose_write_config_byte(pcie, FSL_PCIE_CFG_RDY, 0x1); +} + +static int fsl_pcie_setup_outbound_win(struct fsl_pcie *pcie, int idx, + int type, u64 phys, u64 bus_addr, + pci_size_t size) +{ + ccsr_fsl_pci_t *regs = pcie->regs; + pot_t *po = ®s->pot[idx]; + u32 war, sz; + + if (idx < 0) + return -EINVAL; + + out_be32(&po->powbar, phys >> 12); + out_be32(&po->potar, bus_addr >> 12); +#ifdef CONFIG_SYS_PCI_64BIT + out_be32(&po->potear, bus_addr >> 44); +#else + out_be32(&po->potear, 0); +#endif + + sz = (__ilog2_u64((u64)size) - 1); + war = POWAR_EN | sz; + + if (type == PCI_REGION_IO) + war |= POWAR_IO_READ | POWAR_IO_WRITE; + else + war |= POWAR_MEM_READ | POWAR_MEM_WRITE; + + out_be32(&po->powar, war); + + return 0; +} + +static int fsl_pcie_setup_inbound_win(struct fsl_pcie *pcie, int idx, + bool pf, u64 phys, u64 bus_addr, + pci_size_t size) +{ + ccsr_fsl_pci_t *regs = pcie->regs; + pit_t *pi = ®s->pit[idx]; + u32 sz = (__ilog2_u64(size) - 1); + u32 flag = PIWAR_LOCAL; + + if (idx < 0) + return -EINVAL; + + out_be32(&pi->pitar, phys >> 12); + out_be32(&pi->piwbar, bus_addr >> 12); + +#ifdef CONFIG_SYS_PCI_64BIT + out_be32(&pi->piwbear, bus_addr >> 44); +#else + out_be32(&pi->piwbear, 0); +#endif + +#ifdef CONFIG_SYS_FSL_ERRATUM_A005434 + flag = 0; +#endif + + flag |= PIWAR_EN | PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; + if (pf) + flag |= PIWAR_PF; + out_be32(&pi->piwar, flag | sz); + + return 0; +} + +static int fsl_pcie_setup_outbound_wins(struct fsl_pcie *pcie) +{ + struct pci_region *io, *mem, *pref; + int idx = 1; /* skip 0 */ + + pci_get_regions(pcie->bus, &io, &mem, &pref); + + if (io) + /* ATU : OUTBOUND : IO */ + fsl_pcie_setup_outbound_win(pcie, idx++, + PCI_REGION_IO, + io->phys_start, + io->bus_start, + io->size); + + if (mem) + /* ATU : OUTBOUND : MEM */ + fsl_pcie_setup_outbound_win(pcie, idx++, + PCI_REGION_MEM, + mem->phys_start, + mem->bus_start, + mem->size); + return 0; +} + +static int fsl_pcie_setup_inbound_wins(struct fsl_pcie *pcie) +{ + phys_addr_t phys_start = CONFIG_SYS_PCI_MEMORY_PHYS; + pci_addr_t bus_start = CONFIG_SYS_PCI_MEMORY_BUS; + u64 sz = min((u64)gd->ram_size, (1ull << 32)); + pci_size_t pci_sz; + int idx; + + if (pcie->block_rev >= PEX_IP_BLK_REV_2_2) + idx = 2; + else + idx = 3; + + pci_sz = 1ull << __ilog2_u64(sz); + + dev_dbg(pcie->bus, "R0 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)sz); + + /* if we aren't an exact power of two match, pci_sz is smaller + * round it up to the next power of two. We report the actual + * size to pci region tracking. + */ + if (pci_sz != sz) + sz = 2ull << __ilog2_u64(sz); + + fsl_pcie_setup_inbound_win(pcie, idx--, true, + CONFIG_SYS_PCI_MEMORY_PHYS, + CONFIG_SYS_PCI_MEMORY_BUS, sz); +#if defined(CONFIG_PHYS_64BIT) && defined(CONFIG_SYS_PCI_64BIT) + /* + * On 64-bit capable systems, set up a mapping for all of DRAM + * in high pci address space. + */ + pci_sz = 1ull << __ilog2_u64(gd->ram_size); + /* round up to the next largest power of two */ + if (gd->ram_size > pci_sz) + pci_sz = 1ull << (__ilog2_u64(gd->ram_size) + 1); + + dev_dbg(pcie->bus, "R64 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)CONFIG_SYS_PCI64_MEMORY_BUS, + (u64)CONFIG_SYS_PCI_MEMORY_PHYS, (u64)pci_sz); + + fsl_pcie_setup_inbound_win(pcie, idx--, true, + CONFIG_SYS_PCI_MEMORY_PHYS, + CONFIG_SYS_PCI64_MEMORY_BUS, pci_sz); +#endif + + return 0; +} + +static int fsl_pcie_init_atmu(struct fsl_pcie *pcie) +{ + fsl_pcie_setup_outbound_wins(pcie); + fsl_pcie_setup_inbound_wins(pcie); + + return 0; +} + +static void fsl_pcie_dbi_read_only_reg_write_enable(struct fsl_pcie *pcie, + bool enable) +{ + u32 val; + + fsl_pcie_hose_read_config_dword(pcie, DBI_RO_WR_EN, &val); + if (enable) + val |= 1; + else + val &= ~1; + fsl_pcie_hose_write_config_dword(pcie, DBI_RO_WR_EN, val); +} + +static int fsl_pcie_init_port(struct fsl_pcie *pcie) +{ + ccsr_fsl_pci_t *regs = pcie->regs; + u32 val_32; + u16 val_16; + + fsl_pcie_init_atmu(pcie); + +#ifdef CONFIG_FSL_PCIE_DISABLE_ASPM + val_32 = 0; + fsl_pcie_hose_read_config_dword(pcie, PCI_LCR, &val_32); + val_32 &= ~0x03; + fsl_pcie_hose_write_config_dword(pcie, PCI_LCR, val_32); + udelay(1); +#endif + +#ifdef CONFIG_FSL_PCIE_RESET + u16 ltssm; + int i; + + if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) { + /* assert PCIe reset */ + setbits_be32(®s->pdb_stat, 0x08000000); + (void)in_be32(®s->pdb_stat); + udelay(1000); + /* clear PCIe reset */ + clrbits_be32(®s->pdb_stat, 0x08000000); + asm("sync;isync"); + for (i = 0; i < 100 && !fsl_pcie_link_up(pcie); i++) + udelay(1000); + } else { + fsl_pcie_hose_read_config_word(pcie, PCI_LTSSM, <ssm); + if (ltssm == 1) { + /* assert PCIe reset */ + setbits_be32(®s->pdb_stat, 0x08000000); + (void)in_be32(®s->pdb_stat); + udelay(100); + /* clear PCIe reset */ + clrbits_be32(®s->pdb_stat, 0x08000000); + asm("sync;isync"); + for (i = 0; i < 100 && + !fsl_pcie_link_up(pcie); i++) + udelay(1000); + } + } +#endif + +#ifdef CONFIG_SYS_P4080_ERRATUM_PCIE_A003 + if (!fsl_pcie_link_up(pcie)) { + serdes_corenet_t *srds_regs; + + srds_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR; + val_32 = in_be32(&srds_regs->srdspccr0); + + if ((val_32 >> 28) == 3) { + int i; + + out_be32(&srds_regs->srdspccr0, 2 << 28); + setbits_be32(®s->pdb_stat, 0x08000000); + in_be32(®s->pdb_stat); + udelay(100); + clrbits_be32(®s->pdb_stat, 0x08000000); + asm("sync;isync"); + for (i = 0; i < 100 && !fsl_pcie_link_up(pcie); i++) + udelay(1000); + } + } +#endif + + /* + * The Read-Only Write Enable bit defaults to 1 instead of 0. + * Set to 0 to protect the read-only registers. + */ +#ifdef CONFIG_SYS_FSL_ERRATUM_A007815 + fsl_pcie_dbi_read_only_reg_write_enable(pcie, false); +#endif + + /* + * Enable All Error Interrupts except + * - Master abort (pci) + * - Master PERR (pci) + * - ICCA (PCIe) + */ + out_be32(®s->peer, ~0x20140); + + /* set URR, FER, NFER (but not CER) */ + fsl_pcie_hose_read_config_dword(pcie, PCI_DCR, &val_32); + val_32 |= 0xf000e; + fsl_pcie_hose_write_config_dword(pcie, PCI_DCR, val_32); + + /* Clear all error indications */ + out_be32(®s->pme_msg_det, 0xffffffff); + out_be32(®s->pme_msg_int_en, 0xffffffff); + out_be32(®s->pedr, 0xffffffff); + + fsl_pcie_hose_read_config_word(pcie, PCI_DSR, &val_16); + if (val_16) + fsl_pcie_hose_write_config_word(pcie, PCI_DSR, 0xffff); + + fsl_pcie_hose_read_config_word(pcie, PCI_SEC_STATUS, &val_16); + if (val_16) + fsl_pcie_hose_write_config_word(pcie, PCI_SEC_STATUS, 0xffff); + + return 0; +} + +static int fsl_pcie_fixup_classcode(struct fsl_pcie *pcie) +{ + u32 classcode_reg; + u32 val; + + if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) { + classcode_reg = PCI_CLASS_REVISION; + fsl_pcie_dbi_read_only_reg_write_enable(pcie, true); + } else { + classcode_reg = CSR_CLASSCODE; + } + + fsl_pcie_hose_read_config_dword(pcie, classcode_reg, &val); + val &= 0xff; + val |= PCI_CLASS_BRIDGE_PCI << 16; + fsl_pcie_hose_write_config_dword(pcie, classcode_reg, val); + + if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) + fsl_pcie_dbi_read_only_reg_write_enable(pcie, false); + + return 0; +} + +static int fsl_pcie_init_rc(struct fsl_pcie *pcie) +{ + return fsl_pcie_fixup_classcode(pcie); +} + +static int fsl_pcie_init_ep(struct fsl_pcie *pcie) +{ + fsl_pcie_config_ready(pcie); + + return 0; +} + +static int fsl_pcie_probe(struct udevice *dev) +{ + struct fsl_pcie *pcie = dev_get_priv(dev); + ccsr_fsl_pci_t *regs = pcie->regs; + u16 val_16; + + pcie->bus = dev; + pcie->block_rev = in_be32(®s->block_rev1); + + list_add(&pcie->list, &fsl_pcie_list); + pcie->enabled = is_serdes_configured(PCIE1 + pcie->idx); + if (!pcie->enabled) { + printf("PCIe%d: %s disabled\n", pcie->idx, dev->name); + return 0; + } + + fsl_pcie_setup_law(pcie); + + pcie->mode = fsl_pcie_is_agent(pcie); + + fsl_pcie_init_port(pcie); + + printf("PCIe%d: %s ", pcie->idx, dev->name); + + if (pcie->mode) { + printf("Endpoint"); + fsl_pcie_init_ep(pcie); + } else { + printf("Root Complex"); + fsl_pcie_init_rc(pcie); + } + + if (!fsl_pcie_link_up(pcie)) { + printf(": %s\n", pcie->mode ? "undetermined link" : "no link"); + return 0; + } + + fsl_pcie_hose_read_config_word(pcie, PCI_LSR, &val_16); + printf(": x%d gen%d\n", (val_16 & 0x3f0) >> 4, (val_16 & 0xf)); + + return 0; +} + +static int fsl_pcie_of_to_plat(struct udevice *dev) +{ + struct fsl_pcie *pcie = dev_get_priv(dev); + struct fsl_pcie_data *info; + int ret; + + pcie->regs = dev_remap_addr(dev); + if (!pcie->regs) { + pr_err("\"reg\" resource not found\n"); + return -EINVAL; + } + + ret = dev_read_u32(dev, "law_trgt_if", &pcie->law_trgt_if); + if (ret < 0) { + pr_err("\"law_trgt_if\" not found\n"); + return ret; + } + + info = (struct fsl_pcie_data *)dev_get_driver_data(dev); + pcie->info = info; + pcie->idx = abs((u32)(dev_read_addr(dev) & info->block_offset_mask) - + info->block_offset) / info->stride; + + return 0; +} + +static const struct dm_pci_ops fsl_pcie_ops = { + .read_config = fsl_pcie_read_config, + .write_config = fsl_pcie_write_config, +}; + +static struct fsl_pcie_data p1_p2_data = { + .block_offset = 0xa000, + .block_offset_mask = 0xffff, + .stride = 0x1000, +}; + +static struct fsl_pcie_data p2041_data = { + .block_offset = 0x200000, + .block_offset_mask = 0x3fffff, + .stride = 0x1000, +}; + +static struct fsl_pcie_data t2080_data = { + .block_offset = 0x240000, + .block_offset_mask = 0x3fffff, + .stride = 0x10000, +}; + +static const struct udevice_id fsl_pcie_ids[] = { + { .compatible = "fsl,pcie-mpc8548", .data = (ulong)&p1_p2_data }, + { .compatible = "fsl,pcie-p1_p2", .data = (ulong)&p1_p2_data }, + { .compatible = "fsl,pcie-p2041", .data = (ulong)&p2041_data }, + { .compatible = "fsl,pcie-p3041", .data = (ulong)&p2041_data }, + { .compatible = "fsl,pcie-p4080", .data = (ulong)&p2041_data }, + { .compatible = "fsl,pcie-p5040", .data = (ulong)&p2041_data }, + { .compatible = "fsl,pcie-t102x", .data = (ulong)&t2080_data }, + { .compatible = "fsl,pcie-t104x", .data = (ulong)&t2080_data }, + { .compatible = "fsl,pcie-t2080", .data = (ulong)&t2080_data }, + { .compatible = "fsl,pcie-t4240", .data = (ulong)&t2080_data }, + { } +}; + +U_BOOT_DRIVER(fsl_pcie) = { + .name = "fsl_pcie", + .id = UCLASS_PCI, + .of_match = fsl_pcie_ids, + .ops = &fsl_pcie_ops, + .of_to_plat = fsl_pcie_of_to_plat, + .probe = fsl_pcie_probe, + .priv_auto = sizeof(struct fsl_pcie), +}; |