123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655 |
- // 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/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) < bus->seq)
- return -EINVAL;
- if (PCI_BUS(bdf) > bus->seq && (!fsl_pcie_link_up(pcie) || pcie->mode))
- return -EINVAL;
- if (PCI_BUS(bdf) == bus->seq && (PCI_DEV(bdf) > 0 || PCI_FUNC(bdf) > 0))
- return -EINVAL;
- if (PCI_BUS(bdf) == (bus->seq + 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(bus->seq, 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(bus->seq, 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(bus->seq, 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(bus->seq, 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 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
- clrbits_be32(®s->dbi_ro_wr_en, 0x01);
- #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)
- {
- ccsr_fsl_pci_t *regs = pcie->regs;
- u32 classcode_reg;
- u32 val;
- if (pcie->block_rev >= PEX_IP_BLK_REV_3_0) {
- classcode_reg = PCI_CLASS_REVISION;
- setbits_be32(®s->dbi_ro_wr_en, 0x01);
- } 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)
- clrbits_be32(®s->dbi_ro_wr_en, 0x01);
- 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_ofdata_to_platdata(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,
- .ofdata_to_platdata = fsl_pcie_ofdata_to_platdata,
- .probe = fsl_pcie_probe,
- .priv_auto_alloc_size = sizeof(struct fsl_pcie),
- };
|