diff options
author | Kumar Gala <galak@kernel.crashing.org> | 2009-08-05 07:59:35 -0500 |
---|---|---|
committer | Kumar Gala <galak@kernel.crashing.org> | 2009-08-28 17:12:36 -0500 |
commit | ad19e7a5d2de337064ce7728d6504df9648f5d31 (patch) | |
tree | b9f8344338ac6397703851476bec6b43b55847f0 /drivers/pci/fsl_pci_init.c | |
parent | 8295b94400449586505ffe34ec024feb3d2c8fe4 (diff) | |
download | u-boot-imx-ad19e7a5d2de337064ce7728d6504df9648f5d31.zip u-boot-imx-ad19e7a5d2de337064ce7728d6504df9648f5d31.tar.gz u-boot-imx-ad19e7a5d2de337064ce7728d6504df9648f5d31.tar.bz2 |
pci/fsl_pci_init: Rework PCI ATMU setup to handle >4G of memory
The old PCI ATMU setup code would just mimic the PCI regions into the
ATMU registers. For simple memory maps in which all memory, MMIO, etc
space fit into 4G this works ok. However there are issues with we have
>4G of memory as we know can't access all of memory and we need to
ensure that PCICSRBAR (PEXCSRBAR on PCIe) isn't overlapping with
anything since we can't turn it off.
We first setup outbound windows based on what the board code setup
in the pci regions for MMIO and IO access. Next we place PCICSRBAR
below the MMIO window. After which we try to setup the inbound windows
to map as much of memory as possible.
On PCIe based controllers we are able to overmap the ATMU setup since
RX & TX links are separate but report the proper amount of inbound
address space to the region tracking to ensure there is no overlap.
On PCI based controllers we use as many inbound windows as available to
map as much of the memory as possible.
Additionally we changed all the CCSR register access to use proper IO
accessor functions. Also had to add CONFIG_SYS_CCSRBAR_PHYS to some
86xx platforms that didn't have it defined.
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
Diffstat (limited to 'drivers/pci/fsl_pci_init.c')
-rw-r--r-- | drivers/pci/fsl_pci_init.c | 225 |
1 files changed, 162 insertions, 63 deletions
diff --git a/drivers/pci/fsl_pci_init.c b/drivers/pci/fsl_pci_init.c index f9c0752..ee89aaa 100644 --- a/drivers/pci/fsl_pci_init.c +++ b/drivers/pci/fsl_pci_init.c @@ -35,6 +35,7 @@ DECLARE_GLOBAL_DATA_PTR; */ #include <pci.h> +#include <asm/io.h> #include <asm/fsl_pci.h> /* Freescale-specific PCI config registers */ @@ -60,35 +61,98 @@ void pciauto_config_init(struct pci_controller *hose); #define CONFIG_SYS_PCI64_MEMORY_BUS (64ull*1024*1024*1024) #endif -static int fsl_pci_setup_inbound_windows(struct pci_region *r) +/* Setup one inbound ATMU window. + * + * We let the caller decide what the window size should be + */ +static void set_inbound_window(volatile pit_t *pi, + struct pci_region *r, + u64 size) { - struct pci_region *rgn_base = r; - u64 sz = min((u64)gd->ram_size, (1ull << 32) - 1); + u32 sz = (__ilog2_u64(size) - 1); + u32 flag = PIWAR_EN | PIWAR_LOCAL | + PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; + + out_be32(&pi->pitar, r->phys_start >> 12); + out_be32(&pi->piwbar, r->bus_start >> 12); +#ifdef CONFIG_SYS_PCI_64BIT + out_be32(&pi->piwbear, r->bus_start >> 44); +#else + out_be32(&pi->piwbear, 0); +#endif + if (r->flags & PCI_REGION_PREFETCH) + flag |= PIWAR_PF; + out_be32(&pi->piwar, flag | sz); +} + +static int fsl_pci_setup_inbound_windows(struct pci_controller *hose, + u64 out_lo, u8 pcie_cap, + volatile pit_t *pi) +{ + struct pci_region *r = hose->regions + hose->region_count; + u64 sz = min((u64)gd->ram_size, (1ull << 32)); phys_addr_t phys_start = CONFIG_SYS_PCI_MEMORY_PHYS; pci_addr_t bus_start = CONFIG_SYS_PCI_MEMORY_BUS; - pci_size_t pci_sz = 1ull << __ilog2_u64(sz); + pci_size_t pci_sz; - debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", - (u64)bus_start, (u64)phys_start, (u64)pci_sz); - pci_set_region(r++, bus_start, phys_start, pci_sz, - PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | - PCI_REGION_PREFETCH); + /* we have no space available for inbound memory mapping */ + if (bus_start > out_lo) { + printf ("no space for inbound mapping of memory\n"); + return 0; + } - sz -= pci_sz; - bus_start += pci_sz; - phys_start += pci_sz; + /* limit size */ + if ((bus_start + sz) > out_lo) { + sz = out_lo - bus_start; + debug ("limiting size to %llx\n", sz); + } pci_sz = 1ull << __ilog2_u64(sz); - if (sz) { - debug ("R1 bus_start: %llx phys_start: %llx size: %llx\n", + /* + * we can overlap inbound/outbound windows on PCI-E since RX & TX + * links a separate + */ + if ((pcie_cap == PCI_CAP_ID_EXP) && (pci_sz < sz)) { + debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)sz); + pci_set_region(r, bus_start, phys_start, sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + + /* 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); + + set_inbound_window(pi--, r++, sz); + sz = 0; /* make sure we dont set the R2 window */ + } else { + debug ("R0 bus_start: %llx phys_start: %llx size: %llx\n", (u64)bus_start, (u64)phys_start, (u64)pci_sz); - pci_set_region(r++, bus_start, phys_start, pci_sz, + pci_set_region(r, bus_start, phys_start, pci_sz, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | PCI_REGION_PREFETCH); + set_inbound_window(pi--, r++, pci_sz); + sz -= pci_sz; bus_start += pci_sz; phys_start += pci_sz; + + pci_sz = 1ull << __ilog2_u64(sz); + if (sz) { + debug ("R1 bus_start: %llx phys_start: %llx size: %llx\n", + (u64)bus_start, (u64)phys_start, (u64)pci_sz); + pci_set_region(r, bus_start, phys_start, pci_sz, + PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | + PCI_REGION_PREFETCH); + set_inbound_window(pi--, r++, pci_sz); + sz -= pci_sz; + bus_start += pci_sz; + phys_start += pci_sz; + } } #if defined(CONFIG_PHYS_64BIT) && defined(CONFIG_SYS_PCI_64BIT) @@ -104,23 +168,25 @@ static int fsl_pci_setup_inbound_windows(struct pci_region *r) (u64)CONFIG_SYS_PCI64_MEMORY_BUS, (u64)CONFIG_SYS_PCI_MEMORY_PHYS, (u64)pci_sz); - pci_set_region(r++, + pci_set_region(r, CONFIG_SYS_PCI64_MEMORY_BUS, CONFIG_SYS_PCI_MEMORY_PHYS, pci_sz, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | PCI_REGION_PREFETCH); + set_inbound_window(pi--, r++, pci_sz); #else pci_sz = 1ull << __ilog2_u64(sz); if (sz) { debug ("R2 bus_start: %llx phys_start: %llx size: %llx\n", (u64)bus_start, (u64)phys_start, (u64)pci_sz); - pci_set_region(r++, bus_start, phys_start, pci_sz, + pci_set_region(r, bus_start, phys_start, pci_sz, PCI_REGION_MEM | PCI_REGION_SYS_MEMORY | PCI_REGION_PREFETCH); sz -= pci_sz; bus_start += pci_sz; phys_start += pci_sz; + set_inbound_window(pi--, r++, pci_sz); } #endif @@ -130,7 +196,9 @@ static int fsl_pci_setup_inbound_windows(struct pci_region *r) "inbound windows -- %lld remaining\n", sz); #endif - return r - rgn_base; + hose->region_count = r - hose->regions; + + return 1; } void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) @@ -146,7 +214,10 @@ void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) /* Initialize ATMU registers based on hose regions and flags */ volatile pot_t *po = &pci->pot[1]; /* skip 0 */ - volatile pit_t *pi = &pci->pit[0]; /* ranges from: 3 to 1 */ + volatile pit_t *pi = &pci->pit[2]; /* ranges from: 3 to 1 */ + + u64 out_hi = 0, out_lo = -1ULL; + u32 pcicsrbar, pcicsrbar_sz; #ifdef DEBUG int neg_link_w; @@ -154,60 +225,81 @@ void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) pci_setup_indirect(hose, cfg_addr, cfg_data); - /* inbound */ - reg += fsl_pci_setup_inbound_windows(reg); + /* Handle setup of outbound windows first */ + for (r = 0; r < hose->region_count; r++) { + unsigned long flags = hose->regions[r].flags; + u32 sz = (__ilog2_u64((u64)hose->regions[r].size) - 1); - hose->region_count = reg - hose->regions; + flags &= PCI_REGION_SYS_MEMORY|PCI_REGION_TYPE; + if (flags != PCI_REGION_SYS_MEMORY) { + u64 start = hose->regions[r].bus_start; + u64 end = start + hose->regions[r].size; - for (r=0; r<hose->region_count; r++) { - u32 sz = (__ilog2_u64((u64)hose->regions[r].size) - 1); - if (hose->regions[r].flags & PCI_REGION_SYS_MEMORY) { /* inbound */ - u32 flag = PIWAR_EN | PIWAR_LOCAL | - PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; - pi->pitar = (hose->regions[r].phys_start >> 12); - pi->piwbar = (hose->regions[r].bus_start >> 12); -#ifdef CONFIG_SYS_PCI_64BIT - pi->piwbear = (hose->regions[r].bus_start >> 44); -#else - pi->piwbear = 0; -#endif - if (hose->regions[r].flags & PCI_REGION_PREFETCH) - flag |= PIWAR_PF; - pi->piwar = flag | sz; - pi++; - inbound = hose->regions[r].size > 0; - } else { /* Outbound */ - po->powbar = (hose->regions[r].phys_start >> 12); - po->potar = (hose->regions[r].bus_start >> 12); + out_be32(&po->powbar, hose->regions[r].phys_start >> 12); + out_be32(&po->potar, start >> 12); #ifdef CONFIG_SYS_PCI_64BIT - po->potear = (hose->regions[r].bus_start >> 44); + out_be32(&po->potear, start >> 44); #else - po->potear = 0; + out_be32(&po->potear, 0); #endif - if (hose->regions[r].flags & PCI_REGION_IO) - po->powar = POWAR_EN | sz | - POWAR_IO_READ | POWAR_IO_WRITE; - else - po->powar = POWAR_EN | sz | - POWAR_MEM_READ | POWAR_MEM_WRITE; + if (hose->regions[r].flags & PCI_REGION_IO) { + out_be32(&po->powar, POWAR_EN | sz | + POWAR_IO_READ | POWAR_IO_WRITE); + } else { + out_be32(&po->powar, POWAR_EN | sz | + POWAR_MEM_READ | POWAR_MEM_WRITE); + out_lo = min(start, out_lo); + out_hi = max(end, out_hi); + } po++; } } + debug("Outbound memory range: %llx:%llx\n", out_lo, out_hi); + + /* setup PCSRBAR/PEXCSRBAR */ + pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, 0xffffffff); + pci_hose_read_config_dword (hose, dev, PCI_BASE_ADDRESS_0, &pcicsrbar_sz); + pcicsrbar_sz = ~pcicsrbar_sz + 1; + + if (out_hi < (0x100000000ull - pcicsrbar_sz) || + (out_lo > 0x100000000ull)) + pcicsrbar = 0x100000000ull - pcicsrbar_sz; + else + pcicsrbar = (out_lo - pcicsrbar_sz) & -pcicsrbar_sz; + pci_hose_write_config_dword(hose, dev, PCI_BASE_ADDRESS_0, pcicsrbar); + + out_lo = min(out_lo, (u64)pcicsrbar); + + debug("PCICSRBAR @ 0x%x\n", pcicsrbar); + + pci_set_region(reg++, pcicsrbar, CONFIG_SYS_CCSRBAR_PHYS, + pcicsrbar_sz, PCI_REGION_SYS_MEMORY); + hose->region_count++; /* see if we are a PCIe or PCI controller */ pci_hose_read_config_byte(hose, dev, FSL_PCIE_CAP_ID, &pcie_cap); + /* inbound */ + inbound = fsl_pci_setup_inbound_windows(hose, out_lo, pcie_cap, pi); + + for (r = 0; r < hose->region_count; r++) + debug("PCI reg:%d %016llx:%016llx %016llx %08x\n", r, + (u64)hose->regions[r].phys_start, + hose->regions[r].bus_start, + hose->regions[r].size, + hose->regions[r].flags); + pci_register_hose(hose); pciauto_config_init(hose); /* grab pci_{mem,prefetch,io} */ hose->current_busno = hose->first_busno; - pci->pedr = 0xffffffff; /* Clear any errors */ - pci->peer = ~0x20140; /* Enable All Error Interupts except + out_be32(&pci->pedr, 0xffffffff); /* Clear any errors */ + out_be32(&pci->peer, ~0x20140); /* Enable All Error Interupts except * - Master abort (pci) * - Master PERR (pci) * - ICCA (PCIe) */ - pci_hose_read_config_dword (hose, dev, PCI_DCR, &temp32); + pci_hose_read_config_dword(hose, dev, PCI_DCR, &temp32); temp32 |= 0xf000e; /* set URR, FER, NFER (but not CER) */ pci_hose_write_config_dword(hose, dev, PCI_DCR, temp32); @@ -218,14 +310,15 @@ void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) #ifdef CONFIG_FSL_PCIE_RESET if (ltssm == 1) { int i; - debug("....PCIe link error. " - "LTSSM=0x%02x.", ltssm); - pci->pdb_stat |= 0x08000000; /* assert PCIe reset */ - temp32 = pci->pdb_stat; + debug("....PCIe link error. " "LTSSM=0x%02x.", ltssm); + /* assert PCIe reset */ + setbits_be32(&pci->pdb_stat, 0x08000000); + (void) in_be32(&pci->pdb_stat); udelay(100); debug(" Asserting PCIe reset @%x = %x\n", - &pci->pdb_stat, pci->pdb_stat); - pci->pdb_stat &= ~0x08000000; /* clear reset */ + &pci->pdb_stat, in_be32(&pci->pdb_stat)); + /* clear PCIe reset */ + clrbits_be32(&pci->pdb_stat, 0x08000000); asm("sync;isync"); for (i=0; i<100 && ltssm < PCI_LTSSM_L0; i++) { pci_hose_read_config_word(hose, dev, PCI_LTSSM, @@ -235,6 +328,12 @@ void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) "LTSSM=0x%02x.\n", ltssm); } enabled = ltssm >= PCI_LTSSM_L0; + + /* we need to re-write the bar0 since a reset will + * clear it + */ + pci_hose_write_config_dword(hose, dev, + PCI_BASE_ADDRESS_0, pcicsrbar); } #endif @@ -245,8 +344,8 @@ void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) return; } - pci->pme_msg_det = 0xffffffff; - pci->pme_msg_int_en = 0xffffffff; + out_be32(&pci->pme_msg_det, 0xffffffff); + out_be32(&pci->pme_msg_int_en, 0xffffffff); #ifdef DEBUG pci_hose_read_config_word(hose, dev, PCI_LSR, &temp16); neg_link_w = (temp16 & 0x3f0 ) >> 4; @@ -299,8 +398,8 @@ void fsl_pci_init(struct pci_controller *hose, u32 cfg_addr, u32 cfg_data) /* Clear all error indications */ if (pcie_cap == PCI_CAP_ID_EXP) - pci->pme_msg_det = 0xffffffff; - pci->pedr = 0xffffffff; + out_be32(&pci->pme_msg_det, 0xffffffff); + out_be32(&pci->pedr, 0xffffffff); pci_hose_read_config_word (hose, dev, PCI_DSR, &temp16); if (temp16) { |