162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * PCI / PCI-X / PCI-Express support for 4xx parts 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2007 Ben. Herrenschmidt <benh@kernel.crashing.org>, IBM Corp. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Most PCI Express code is coming from Stefan Roese implementation for 762306a36Sopenharmony_ci * arch/ppc in the Denx tree, slightly reworked by me. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright 2007 DENX Software Engineering, Stefan Roese <sr@denx.de> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Some of that comes itself from a previous implementation for 440SPE only 1262306a36Sopenharmony_ci * by Roland Dreier: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Copyright (c) 2005 Cisco Systems. All rights reserved. 1562306a36Sopenharmony_ci * Roland Dreier <rolandd@cisco.com> 1662306a36Sopenharmony_ci * 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#undef DEBUG 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/init.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/of_address.h> 2662306a36Sopenharmony_ci#include <linux/delay.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include <asm/io.h> 3062306a36Sopenharmony_ci#include <asm/pci-bridge.h> 3162306a36Sopenharmony_ci#include <asm/machdep.h> 3262306a36Sopenharmony_ci#include <asm/dcr.h> 3362306a36Sopenharmony_ci#include <asm/dcr-regs.h> 3462306a36Sopenharmony_ci#include <mm/mmu_decl.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "pci.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic int dma_offset_set; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define U64_TO_U32_LOW(val) ((u32)((val) & 0x00000000ffffffffULL)) 4162306a36Sopenharmony_ci#define U64_TO_U32_HIGH(val) ((u32)((val) >> 32)) 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci#define RES_TO_U32_LOW(val) \ 4462306a36Sopenharmony_ci ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_LOW(val) : (val)) 4562306a36Sopenharmony_ci#define RES_TO_U32_HIGH(val) \ 4662306a36Sopenharmony_ci ((sizeof(resource_size_t) > sizeof(u32)) ? U64_TO_U32_HIGH(val) : (0)) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_cistatic inline int ppc440spe_revA(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci /* Catch both 440SPe variants, with and without RAID6 support */ 5162306a36Sopenharmony_ci if ((mfspr(SPRN_PVR) & 0xffefffff) == 0x53421890) 5262306a36Sopenharmony_ci return 1; 5362306a36Sopenharmony_ci else 5462306a36Sopenharmony_ci return 0; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic void fixup_ppc4xx_pci_bridge(struct pci_dev *dev) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci struct pci_controller *hose; 6062306a36Sopenharmony_ci struct resource *r; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci if (dev->devfn != 0 || dev->bus->self != NULL) 6362306a36Sopenharmony_ci return; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci hose = pci_bus_to_host(dev->bus); 6662306a36Sopenharmony_ci if (hose == NULL) 6762306a36Sopenharmony_ci return; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (!of_device_is_compatible(hose->dn, "ibm,plb-pciex") && 7062306a36Sopenharmony_ci !of_device_is_compatible(hose->dn, "ibm,plb-pcix") && 7162306a36Sopenharmony_ci !of_device_is_compatible(hose->dn, "ibm,plb-pci")) 7262306a36Sopenharmony_ci return; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci if (of_device_is_compatible(hose->dn, "ibm,plb440epx-pci") || 7562306a36Sopenharmony_ci of_device_is_compatible(hose->dn, "ibm,plb440grx-pci")) { 7662306a36Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_BROKEN_MRM; 7762306a36Sopenharmony_ci } 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Hide the PCI host BARs from the kernel as their content doesn't 8062306a36Sopenharmony_ci * fit well in the resource management 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci pci_dev_for_each_resource(dev, r) { 8362306a36Sopenharmony_ci r->start = r->end = 0; 8462306a36Sopenharmony_ci r->flags = 0; 8562306a36Sopenharmony_ci } 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci printk(KERN_INFO "PCI: Hiding 4xx host bridge resources %s\n", 8862306a36Sopenharmony_ci pci_name(dev)); 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ciDECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, fixup_ppc4xx_pci_bridge); 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int __init ppc4xx_parse_dma_ranges(struct pci_controller *hose, 9362306a36Sopenharmony_ci void __iomem *reg, 9462306a36Sopenharmony_ci struct resource *res) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci u64 size; 9762306a36Sopenharmony_ci const u32 *ranges; 9862306a36Sopenharmony_ci int rlen; 9962306a36Sopenharmony_ci int pna = of_n_addr_cells(hose->dn); 10062306a36Sopenharmony_ci int np = pna + 5; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci /* Default */ 10362306a36Sopenharmony_ci res->start = 0; 10462306a36Sopenharmony_ci size = 0x80000000; 10562306a36Sopenharmony_ci res->end = size - 1; 10662306a36Sopenharmony_ci res->flags = IORESOURCE_MEM | IORESOURCE_PREFETCH; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Get dma-ranges property */ 10962306a36Sopenharmony_ci ranges = of_get_property(hose->dn, "dma-ranges", &rlen); 11062306a36Sopenharmony_ci if (ranges == NULL) 11162306a36Sopenharmony_ci goto out; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci /* Walk it */ 11462306a36Sopenharmony_ci while ((rlen -= np * 4) >= 0) { 11562306a36Sopenharmony_ci u32 pci_space = ranges[0]; 11662306a36Sopenharmony_ci u64 pci_addr = of_read_number(ranges + 1, 2); 11762306a36Sopenharmony_ci u64 cpu_addr = of_translate_dma_address(hose->dn, ranges + 3); 11862306a36Sopenharmony_ci size = of_read_number(ranges + pna + 3, 2); 11962306a36Sopenharmony_ci ranges += np; 12062306a36Sopenharmony_ci if (cpu_addr == OF_BAD_ADDR || size == 0) 12162306a36Sopenharmony_ci continue; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* We only care about memory */ 12462306a36Sopenharmony_ci if ((pci_space & 0x03000000) != 0x02000000) 12562306a36Sopenharmony_ci continue; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci /* We currently only support memory at 0, and pci_addr 12862306a36Sopenharmony_ci * within 32 bits space 12962306a36Sopenharmony_ci */ 13062306a36Sopenharmony_ci if (cpu_addr != 0 || pci_addr > 0xffffffff) { 13162306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Ignored unsupported dma range" 13262306a36Sopenharmony_ci " 0x%016llx...0x%016llx -> 0x%016llx\n", 13362306a36Sopenharmony_ci hose->dn, 13462306a36Sopenharmony_ci pci_addr, pci_addr + size - 1, cpu_addr); 13562306a36Sopenharmony_ci continue; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* Check if not prefetchable */ 13962306a36Sopenharmony_ci if (!(pci_space & 0x40000000)) 14062306a36Sopenharmony_ci res->flags &= ~IORESOURCE_PREFETCH; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci /* Use that */ 14462306a36Sopenharmony_ci res->start = pci_addr; 14562306a36Sopenharmony_ci /* Beware of 32 bits resources */ 14662306a36Sopenharmony_ci if (sizeof(resource_size_t) == sizeof(u32) && 14762306a36Sopenharmony_ci (pci_addr + size) > 0x100000000ull) 14862306a36Sopenharmony_ci res->end = 0xffffffff; 14962306a36Sopenharmony_ci else 15062306a36Sopenharmony_ci res->end = res->start + size - 1; 15162306a36Sopenharmony_ci break; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* We only support one global DMA offset */ 15562306a36Sopenharmony_ci if (dma_offset_set && pci_dram_offset != res->start) { 15662306a36Sopenharmony_ci printk(KERN_ERR "%pOF: dma-ranges(s) mismatch\n", hose->dn); 15762306a36Sopenharmony_ci return -ENXIO; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Check that we can fit all of memory as we don't support 16162306a36Sopenharmony_ci * DMA bounce buffers 16262306a36Sopenharmony_ci */ 16362306a36Sopenharmony_ci if (size < total_memory) { 16462306a36Sopenharmony_ci printk(KERN_ERR "%pOF: dma-ranges too small " 16562306a36Sopenharmony_ci "(size=%llx total_memory=%llx)\n", 16662306a36Sopenharmony_ci hose->dn, size, (u64)total_memory); 16762306a36Sopenharmony_ci return -ENXIO; 16862306a36Sopenharmony_ci } 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* Check we are a power of 2 size and that base is a multiple of size*/ 17162306a36Sopenharmony_ci if ((size & (size - 1)) != 0 || 17262306a36Sopenharmony_ci (res->start & (size - 1)) != 0) { 17362306a36Sopenharmony_ci printk(KERN_ERR "%pOF: dma-ranges unaligned\n", hose->dn); 17462306a36Sopenharmony_ci return -ENXIO; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Check that we are fully contained within 32 bits space if we are not 17862306a36Sopenharmony_ci * running on a 460sx or 476fpe which have 64 bit bus addresses. 17962306a36Sopenharmony_ci */ 18062306a36Sopenharmony_ci if (res->end > 0xffffffff && 18162306a36Sopenharmony_ci !(of_device_is_compatible(hose->dn, "ibm,plb-pciex-460sx") 18262306a36Sopenharmony_ci || of_device_is_compatible(hose->dn, "ibm,plb-pciex-476fpe"))) { 18362306a36Sopenharmony_ci printk(KERN_ERR "%pOF: dma-ranges outside of 32 bits space\n", 18462306a36Sopenharmony_ci hose->dn); 18562306a36Sopenharmony_ci return -ENXIO; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci out: 18862306a36Sopenharmony_ci dma_offset_set = 1; 18962306a36Sopenharmony_ci pci_dram_offset = res->start; 19062306a36Sopenharmony_ci hose->dma_window_base_cur = res->start; 19162306a36Sopenharmony_ci hose->dma_window_size = resource_size(res); 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci printk(KERN_INFO "4xx PCI DMA offset set to 0x%08lx\n", 19462306a36Sopenharmony_ci pci_dram_offset); 19562306a36Sopenharmony_ci printk(KERN_INFO "4xx PCI DMA window base to 0x%016llx\n", 19662306a36Sopenharmony_ci (unsigned long long)hose->dma_window_base_cur); 19762306a36Sopenharmony_ci printk(KERN_INFO "DMA window size 0x%016llx\n", 19862306a36Sopenharmony_ci (unsigned long long)hose->dma_window_size); 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci/* 20362306a36Sopenharmony_ci * 4xx PCI 2.x part 20462306a36Sopenharmony_ci */ 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int __init ppc4xx_setup_one_pci_PMM(struct pci_controller *hose, 20762306a36Sopenharmony_ci void __iomem *reg, 20862306a36Sopenharmony_ci u64 plb_addr, 20962306a36Sopenharmony_ci u64 pci_addr, 21062306a36Sopenharmony_ci u64 size, 21162306a36Sopenharmony_ci unsigned int flags, 21262306a36Sopenharmony_ci int index) 21362306a36Sopenharmony_ci{ 21462306a36Sopenharmony_ci u32 ma, pcila, pciha; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci /* Hack warning ! The "old" PCI 2.x cell only let us configure the low 21762306a36Sopenharmony_ci * 32-bit of incoming PLB addresses. The top 4 bits of the 36-bit 21862306a36Sopenharmony_ci * address are actually hard wired to a value that appears to depend 21962306a36Sopenharmony_ci * on the specific SoC. For example, it's 0 on 440EP and 1 on 440EPx. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * The trick here is we just crop those top bits and ignore them when 22262306a36Sopenharmony_ci * programming the chip. That means the device-tree has to be right 22362306a36Sopenharmony_ci * for the specific part used (we don't print a warning if it's wrong 22462306a36Sopenharmony_ci * but on the other hand, you'll crash quickly enough), but at least 22562306a36Sopenharmony_ci * this code should work whatever the hard coded value is 22662306a36Sopenharmony_ci */ 22762306a36Sopenharmony_ci plb_addr &= 0xffffffffull; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci /* Note: Due to the above hack, the test below doesn't actually test 23062306a36Sopenharmony_ci * if you address is above 4G, but it tests that address and 23162306a36Sopenharmony_ci * (address + size) are both contained in the same 4G 23262306a36Sopenharmony_ci */ 23362306a36Sopenharmony_ci if ((plb_addr + size) > 0xffffffffull || !is_power_of_2(size) || 23462306a36Sopenharmony_ci size < 0x1000 || (plb_addr & (size - 1)) != 0) { 23562306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Resource out of range\n", hose->dn); 23662306a36Sopenharmony_ci return -1; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci ma = (0xffffffffu << ilog2(size)) | 1; 23962306a36Sopenharmony_ci if (flags & IORESOURCE_PREFETCH) 24062306a36Sopenharmony_ci ma |= 2; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci pciha = RES_TO_U32_HIGH(pci_addr); 24362306a36Sopenharmony_ci pcila = RES_TO_U32_LOW(pci_addr); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci writel(plb_addr, reg + PCIL0_PMM0LA + (0x10 * index)); 24662306a36Sopenharmony_ci writel(pcila, reg + PCIL0_PMM0PCILA + (0x10 * index)); 24762306a36Sopenharmony_ci writel(pciha, reg + PCIL0_PMM0PCIHA + (0x10 * index)); 24862306a36Sopenharmony_ci writel(ma, reg + PCIL0_PMM0MA + (0x10 * index)); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci} 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_cistatic void __init ppc4xx_configure_pci_PMMs(struct pci_controller *hose, 25462306a36Sopenharmony_ci void __iomem *reg) 25562306a36Sopenharmony_ci{ 25662306a36Sopenharmony_ci int i, j, found_isa_hole = 0; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Setup outbound memory windows */ 25962306a36Sopenharmony_ci for (i = j = 0; i < 3; i++) { 26062306a36Sopenharmony_ci struct resource *res = &hose->mem_resources[i]; 26162306a36Sopenharmony_ci resource_size_t offset = hose->mem_offset[i]; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* we only care about memory windows */ 26462306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_MEM)) 26562306a36Sopenharmony_ci continue; 26662306a36Sopenharmony_ci if (j > 2) { 26762306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Too many ranges\n", hose->dn); 26862306a36Sopenharmony_ci break; 26962306a36Sopenharmony_ci } 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Configure the resource */ 27262306a36Sopenharmony_ci if (ppc4xx_setup_one_pci_PMM(hose, reg, 27362306a36Sopenharmony_ci res->start, 27462306a36Sopenharmony_ci res->start - offset, 27562306a36Sopenharmony_ci resource_size(res), 27662306a36Sopenharmony_ci res->flags, 27762306a36Sopenharmony_ci j) == 0) { 27862306a36Sopenharmony_ci j++; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* If the resource PCI address is 0 then we have our 28162306a36Sopenharmony_ci * ISA memory hole 28262306a36Sopenharmony_ci */ 28362306a36Sopenharmony_ci if (res->start == offset) 28462306a36Sopenharmony_ci found_isa_hole = 1; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci } 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Handle ISA memory hole if not already covered */ 28962306a36Sopenharmony_ci if (j <= 2 && !found_isa_hole && hose->isa_mem_size) 29062306a36Sopenharmony_ci if (ppc4xx_setup_one_pci_PMM(hose, reg, hose->isa_mem_phys, 0, 29162306a36Sopenharmony_ci hose->isa_mem_size, 0, j) == 0) 29262306a36Sopenharmony_ci printk(KERN_INFO "%pOF: Legacy ISA memory support enabled\n", 29362306a36Sopenharmony_ci hose->dn); 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic void __init ppc4xx_configure_pci_PTMs(struct pci_controller *hose, 29762306a36Sopenharmony_ci void __iomem *reg, 29862306a36Sopenharmony_ci const struct resource *res) 29962306a36Sopenharmony_ci{ 30062306a36Sopenharmony_ci resource_size_t size = resource_size(res); 30162306a36Sopenharmony_ci u32 sa; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Calculate window size */ 30462306a36Sopenharmony_ci sa = (0xffffffffu << ilog2(size)) | 1; 30562306a36Sopenharmony_ci sa |= 0x1; 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* RAM is always at 0 local for now */ 30862306a36Sopenharmony_ci writel(0, reg + PCIL0_PTM1LA); 30962306a36Sopenharmony_ci writel(sa, reg + PCIL0_PTM1MS); 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci /* Map on PCI side */ 31262306a36Sopenharmony_ci early_write_config_dword(hose, hose->first_busno, 0, 31362306a36Sopenharmony_ci PCI_BASE_ADDRESS_1, res->start); 31462306a36Sopenharmony_ci early_write_config_dword(hose, hose->first_busno, 0, 31562306a36Sopenharmony_ci PCI_BASE_ADDRESS_2, 0x00000000); 31662306a36Sopenharmony_ci early_write_config_word(hose, hose->first_busno, 0, 31762306a36Sopenharmony_ci PCI_COMMAND, 0x0006); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic void __init ppc4xx_probe_pci_bridge(struct device_node *np) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci /* NYI */ 32362306a36Sopenharmony_ci struct resource rsrc_cfg; 32462306a36Sopenharmony_ci struct resource rsrc_reg; 32562306a36Sopenharmony_ci struct resource dma_window; 32662306a36Sopenharmony_ci struct pci_controller *hose = NULL; 32762306a36Sopenharmony_ci void __iomem *reg = NULL; 32862306a36Sopenharmony_ci const int *bus_range; 32962306a36Sopenharmony_ci int primary = 0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* Check if device is enabled */ 33262306a36Sopenharmony_ci if (!of_device_is_available(np)) { 33362306a36Sopenharmony_ci printk(KERN_INFO "%pOF: Port disabled via device-tree\n", np); 33462306a36Sopenharmony_ci return; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci /* Fetch config space registers address */ 33862306a36Sopenharmony_ci if (of_address_to_resource(np, 0, &rsrc_cfg)) { 33962306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get PCI config register base !", 34062306a36Sopenharmony_ci np); 34162306a36Sopenharmony_ci return; 34262306a36Sopenharmony_ci } 34362306a36Sopenharmony_ci /* Fetch host bridge internal registers address */ 34462306a36Sopenharmony_ci if (of_address_to_resource(np, 3, &rsrc_reg)) { 34562306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get PCI internal register base !", 34662306a36Sopenharmony_ci np); 34762306a36Sopenharmony_ci return; 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Check if primary bridge */ 35162306a36Sopenharmony_ci if (of_property_read_bool(np, "primary")) 35262306a36Sopenharmony_ci primary = 1; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* Get bus range if any */ 35562306a36Sopenharmony_ci bus_range = of_get_property(np, "bus-range", NULL); 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Map registers */ 35862306a36Sopenharmony_ci reg = ioremap(rsrc_reg.start, resource_size(&rsrc_reg)); 35962306a36Sopenharmony_ci if (reg == NULL) { 36062306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map registers !", np); 36162306a36Sopenharmony_ci goto fail; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Allocate the host controller data structure */ 36562306a36Sopenharmony_ci hose = pcibios_alloc_controller(np); 36662306a36Sopenharmony_ci if (!hose) 36762306a36Sopenharmony_ci goto fail; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0x0; 37062306a36Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci /* Setup config space */ 37362306a36Sopenharmony_ci setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 0); 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci /* Disable all windows */ 37662306a36Sopenharmony_ci writel(0, reg + PCIL0_PMM0MA); 37762306a36Sopenharmony_ci writel(0, reg + PCIL0_PMM1MA); 37862306a36Sopenharmony_ci writel(0, reg + PCIL0_PMM2MA); 37962306a36Sopenharmony_ci writel(0, reg + PCIL0_PTM1MS); 38062306a36Sopenharmony_ci writel(0, reg + PCIL0_PTM2MS); 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Parse outbound mapping resources */ 38362306a36Sopenharmony_ci pci_process_bridge_OF_ranges(hose, np, primary); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Parse inbound mapping resources */ 38662306a36Sopenharmony_ci if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0) 38762306a36Sopenharmony_ci goto fail; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci /* Configure outbound ranges POMs */ 39062306a36Sopenharmony_ci ppc4xx_configure_pci_PMMs(hose, reg); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* Configure inbound ranges PIMs */ 39362306a36Sopenharmony_ci ppc4xx_configure_pci_PTMs(hose, reg, &dma_window); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci /* We don't need the registers anymore */ 39662306a36Sopenharmony_ci iounmap(reg); 39762306a36Sopenharmony_ci return; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci fail: 40062306a36Sopenharmony_ci if (hose) 40162306a36Sopenharmony_ci pcibios_free_controller(hose); 40262306a36Sopenharmony_ci if (reg) 40362306a36Sopenharmony_ci iounmap(reg); 40462306a36Sopenharmony_ci} 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci/* 40762306a36Sopenharmony_ci * 4xx PCI-X part 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic int __init ppc4xx_setup_one_pcix_POM(struct pci_controller *hose, 41162306a36Sopenharmony_ci void __iomem *reg, 41262306a36Sopenharmony_ci u64 plb_addr, 41362306a36Sopenharmony_ci u64 pci_addr, 41462306a36Sopenharmony_ci u64 size, 41562306a36Sopenharmony_ci unsigned int flags, 41662306a36Sopenharmony_ci int index) 41762306a36Sopenharmony_ci{ 41862306a36Sopenharmony_ci u32 lah, lal, pciah, pcial, sa; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci if (!is_power_of_2(size) || size < 0x1000 || 42162306a36Sopenharmony_ci (plb_addr & (size - 1)) != 0) { 42262306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Resource out of range\n", 42362306a36Sopenharmony_ci hose->dn); 42462306a36Sopenharmony_ci return -1; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* Calculate register values */ 42862306a36Sopenharmony_ci lah = RES_TO_U32_HIGH(plb_addr); 42962306a36Sopenharmony_ci lal = RES_TO_U32_LOW(plb_addr); 43062306a36Sopenharmony_ci pciah = RES_TO_U32_HIGH(pci_addr); 43162306a36Sopenharmony_ci pcial = RES_TO_U32_LOW(pci_addr); 43262306a36Sopenharmony_ci sa = (0xffffffffu << ilog2(size)) | 0x1; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci /* Program register values */ 43562306a36Sopenharmony_ci if (index == 0) { 43662306a36Sopenharmony_ci writel(lah, reg + PCIX0_POM0LAH); 43762306a36Sopenharmony_ci writel(lal, reg + PCIX0_POM0LAL); 43862306a36Sopenharmony_ci writel(pciah, reg + PCIX0_POM0PCIAH); 43962306a36Sopenharmony_ci writel(pcial, reg + PCIX0_POM0PCIAL); 44062306a36Sopenharmony_ci writel(sa, reg + PCIX0_POM0SA); 44162306a36Sopenharmony_ci } else { 44262306a36Sopenharmony_ci writel(lah, reg + PCIX0_POM1LAH); 44362306a36Sopenharmony_ci writel(lal, reg + PCIX0_POM1LAL); 44462306a36Sopenharmony_ci writel(pciah, reg + PCIX0_POM1PCIAH); 44562306a36Sopenharmony_ci writel(pcial, reg + PCIX0_POM1PCIAL); 44662306a36Sopenharmony_ci writel(sa, reg + PCIX0_POM1SA); 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return 0; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic void __init ppc4xx_configure_pcix_POMs(struct pci_controller *hose, 45362306a36Sopenharmony_ci void __iomem *reg) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci int i, j, found_isa_hole = 0; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Setup outbound memory windows */ 45862306a36Sopenharmony_ci for (i = j = 0; i < 3; i++) { 45962306a36Sopenharmony_ci struct resource *res = &hose->mem_resources[i]; 46062306a36Sopenharmony_ci resource_size_t offset = hose->mem_offset[i]; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* we only care about memory windows */ 46362306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_MEM)) 46462306a36Sopenharmony_ci continue; 46562306a36Sopenharmony_ci if (j > 1) { 46662306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Too many ranges\n", hose->dn); 46762306a36Sopenharmony_ci break; 46862306a36Sopenharmony_ci } 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci /* Configure the resource */ 47162306a36Sopenharmony_ci if (ppc4xx_setup_one_pcix_POM(hose, reg, 47262306a36Sopenharmony_ci res->start, 47362306a36Sopenharmony_ci res->start - offset, 47462306a36Sopenharmony_ci resource_size(res), 47562306a36Sopenharmony_ci res->flags, 47662306a36Sopenharmony_ci j) == 0) { 47762306a36Sopenharmony_ci j++; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci /* If the resource PCI address is 0 then we have our 48062306a36Sopenharmony_ci * ISA memory hole 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci if (res->start == offset) 48362306a36Sopenharmony_ci found_isa_hole = 1; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* Handle ISA memory hole if not already covered */ 48862306a36Sopenharmony_ci if (j <= 1 && !found_isa_hole && hose->isa_mem_size) 48962306a36Sopenharmony_ci if (ppc4xx_setup_one_pcix_POM(hose, reg, hose->isa_mem_phys, 0, 49062306a36Sopenharmony_ci hose->isa_mem_size, 0, j) == 0) 49162306a36Sopenharmony_ci printk(KERN_INFO "%pOF: Legacy ISA memory support enabled\n", 49262306a36Sopenharmony_ci hose->dn); 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_cistatic void __init ppc4xx_configure_pcix_PIMs(struct pci_controller *hose, 49662306a36Sopenharmony_ci void __iomem *reg, 49762306a36Sopenharmony_ci const struct resource *res, 49862306a36Sopenharmony_ci int big_pim, 49962306a36Sopenharmony_ci int enable_msi_hole) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci resource_size_t size = resource_size(res); 50262306a36Sopenharmony_ci u32 sa; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci /* RAM is always at 0 */ 50562306a36Sopenharmony_ci writel(0x00000000, reg + PCIX0_PIM0LAH); 50662306a36Sopenharmony_ci writel(0x00000000, reg + PCIX0_PIM0LAL); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci /* Calculate window size */ 50962306a36Sopenharmony_ci sa = (0xffffffffu << ilog2(size)) | 1; 51062306a36Sopenharmony_ci sa |= 0x1; 51162306a36Sopenharmony_ci if (res->flags & IORESOURCE_PREFETCH) 51262306a36Sopenharmony_ci sa |= 0x2; 51362306a36Sopenharmony_ci if (enable_msi_hole) 51462306a36Sopenharmony_ci sa |= 0x4; 51562306a36Sopenharmony_ci writel(sa, reg + PCIX0_PIM0SA); 51662306a36Sopenharmony_ci if (big_pim) 51762306a36Sopenharmony_ci writel(0xffffffff, reg + PCIX0_PIM0SAH); 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Map on PCI side */ 52062306a36Sopenharmony_ci writel(0x00000000, reg + PCIX0_BAR0H); 52162306a36Sopenharmony_ci writel(res->start, reg + PCIX0_BAR0L); 52262306a36Sopenharmony_ci writew(0x0006, reg + PCIX0_COMMAND); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic void __init ppc4xx_probe_pcix_bridge(struct device_node *np) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct resource rsrc_cfg; 52862306a36Sopenharmony_ci struct resource rsrc_reg; 52962306a36Sopenharmony_ci struct resource dma_window; 53062306a36Sopenharmony_ci struct pci_controller *hose = NULL; 53162306a36Sopenharmony_ci void __iomem *reg = NULL; 53262306a36Sopenharmony_ci const int *bus_range; 53362306a36Sopenharmony_ci int big_pim, msi, primary; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Fetch config space registers address */ 53662306a36Sopenharmony_ci if (of_address_to_resource(np, 0, &rsrc_cfg)) { 53762306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get PCI-X config register base !", 53862306a36Sopenharmony_ci np); 53962306a36Sopenharmony_ci return; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci /* Fetch host bridge internal registers address */ 54262306a36Sopenharmony_ci if (of_address_to_resource(np, 3, &rsrc_reg)) { 54362306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get PCI-X internal register base !", 54462306a36Sopenharmony_ci np); 54562306a36Sopenharmony_ci return; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci 54862306a36Sopenharmony_ci /* Check if it supports large PIMs (440GX) */ 54962306a36Sopenharmony_ci big_pim = of_property_read_bool(np, "large-inbound-windows"); 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci /* Check if we should enable MSIs inbound hole */ 55262306a36Sopenharmony_ci msi = of_property_read_bool(np, "enable-msi-hole"); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Check if primary bridge */ 55562306a36Sopenharmony_ci primary = of_property_read_bool(np, "primary"); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci /* Get bus range if any */ 55862306a36Sopenharmony_ci bus_range = of_get_property(np, "bus-range", NULL); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* Map registers */ 56162306a36Sopenharmony_ci reg = ioremap(rsrc_reg.start, resource_size(&rsrc_reg)); 56262306a36Sopenharmony_ci if (reg == NULL) { 56362306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map registers !", np); 56462306a36Sopenharmony_ci goto fail; 56562306a36Sopenharmony_ci } 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci /* Allocate the host controller data structure */ 56862306a36Sopenharmony_ci hose = pcibios_alloc_controller(np); 56962306a36Sopenharmony_ci if (!hose) 57062306a36Sopenharmony_ci goto fail; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0x0; 57362306a36Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci /* Setup config space */ 57662306a36Sopenharmony_ci setup_indirect_pci(hose, rsrc_cfg.start, rsrc_cfg.start + 0x4, 57762306a36Sopenharmony_ci PPC_INDIRECT_TYPE_SET_CFG_TYPE); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Disable all windows */ 58062306a36Sopenharmony_ci writel(0, reg + PCIX0_POM0SA); 58162306a36Sopenharmony_ci writel(0, reg + PCIX0_POM1SA); 58262306a36Sopenharmony_ci writel(0, reg + PCIX0_POM2SA); 58362306a36Sopenharmony_ci writel(0, reg + PCIX0_PIM0SA); 58462306a36Sopenharmony_ci writel(0, reg + PCIX0_PIM1SA); 58562306a36Sopenharmony_ci writel(0, reg + PCIX0_PIM2SA); 58662306a36Sopenharmony_ci if (big_pim) { 58762306a36Sopenharmony_ci writel(0, reg + PCIX0_PIM0SAH); 58862306a36Sopenharmony_ci writel(0, reg + PCIX0_PIM2SAH); 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci /* Parse outbound mapping resources */ 59262306a36Sopenharmony_ci pci_process_bridge_OF_ranges(hose, np, primary); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci /* Parse inbound mapping resources */ 59562306a36Sopenharmony_ci if (ppc4xx_parse_dma_ranges(hose, reg, &dma_window) != 0) 59662306a36Sopenharmony_ci goto fail; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci /* Configure outbound ranges POMs */ 59962306a36Sopenharmony_ci ppc4xx_configure_pcix_POMs(hose, reg); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci /* Configure inbound ranges PIMs */ 60262306a36Sopenharmony_ci ppc4xx_configure_pcix_PIMs(hose, reg, &dma_window, big_pim, msi); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci /* We don't need the registers anymore */ 60562306a36Sopenharmony_ci iounmap(reg); 60662306a36Sopenharmony_ci return; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci fail: 60962306a36Sopenharmony_ci if (hose) 61062306a36Sopenharmony_ci pcibios_free_controller(hose); 61162306a36Sopenharmony_ci if (reg) 61262306a36Sopenharmony_ci iounmap(reg); 61362306a36Sopenharmony_ci} 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci#ifdef CONFIG_PPC4xx_PCI_EXPRESS 61662306a36Sopenharmony_ci 61762306a36Sopenharmony_ci/* 61862306a36Sopenharmony_ci * 4xx PCI-Express part 61962306a36Sopenharmony_ci * 62062306a36Sopenharmony_ci * We support 3 parts currently based on the compatible property: 62162306a36Sopenharmony_ci * 62262306a36Sopenharmony_ci * ibm,plb-pciex-440spe 62362306a36Sopenharmony_ci * ibm,plb-pciex-405ex 62462306a36Sopenharmony_ci * ibm,plb-pciex-460ex 62562306a36Sopenharmony_ci * 62662306a36Sopenharmony_ci * Anything else will be rejected for now as they are all subtly 62762306a36Sopenharmony_ci * different unfortunately. 62862306a36Sopenharmony_ci * 62962306a36Sopenharmony_ci */ 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci#define MAX_PCIE_BUS_MAPPED 0x40 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistruct ppc4xx_pciex_port 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct pci_controller *hose; 63662306a36Sopenharmony_ci struct device_node *node; 63762306a36Sopenharmony_ci unsigned int index; 63862306a36Sopenharmony_ci int endpoint; 63962306a36Sopenharmony_ci int link; 64062306a36Sopenharmony_ci int has_ibpre; 64162306a36Sopenharmony_ci unsigned int sdr_base; 64262306a36Sopenharmony_ci dcr_host_t dcrs; 64362306a36Sopenharmony_ci struct resource cfg_space; 64462306a36Sopenharmony_ci struct resource utl_regs; 64562306a36Sopenharmony_ci void __iomem *utl_base; 64662306a36Sopenharmony_ci}; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_cistatic struct ppc4xx_pciex_port *ppc4xx_pciex_ports; 64962306a36Sopenharmony_cistatic unsigned int ppc4xx_pciex_port_count; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_cistruct ppc4xx_pciex_hwops 65262306a36Sopenharmony_ci{ 65362306a36Sopenharmony_ci bool want_sdr; 65462306a36Sopenharmony_ci int (*core_init)(struct device_node *np); 65562306a36Sopenharmony_ci int (*port_init_hw)(struct ppc4xx_pciex_port *port); 65662306a36Sopenharmony_ci int (*setup_utl)(struct ppc4xx_pciex_port *port); 65762306a36Sopenharmony_ci void (*check_link)(struct ppc4xx_pciex_port *port); 65862306a36Sopenharmony_ci}; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops *ppc4xx_pciex_hwops; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_cistatic int __init ppc4xx_pciex_wait_on_sdr(struct ppc4xx_pciex_port *port, 66362306a36Sopenharmony_ci unsigned int sdr_offset, 66462306a36Sopenharmony_ci unsigned int mask, 66562306a36Sopenharmony_ci unsigned int value, 66662306a36Sopenharmony_ci int timeout_ms) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci u32 val; 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci while(timeout_ms--) { 67162306a36Sopenharmony_ci val = mfdcri(SDR0, port->sdr_base + sdr_offset); 67262306a36Sopenharmony_ci if ((val & mask) == value) { 67362306a36Sopenharmony_ci pr_debug("PCIE%d: Wait on SDR %x success with tm %d (%08x)\n", 67462306a36Sopenharmony_ci port->index, sdr_offset, timeout_ms, val); 67562306a36Sopenharmony_ci return 0; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci msleep(1); 67862306a36Sopenharmony_ci } 67962306a36Sopenharmony_ci return -1; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int __init ppc4xx_pciex_port_reset_sdr(struct ppc4xx_pciex_port *port) 68362306a36Sopenharmony_ci{ 68462306a36Sopenharmony_ci /* Wait for reset to complete */ 68562306a36Sopenharmony_ci if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 1 << 20, 0, 10)) { 68662306a36Sopenharmony_ci printk(KERN_WARNING "PCIE%d: PGRST failed\n", 68762306a36Sopenharmony_ci port->index); 68862306a36Sopenharmony_ci return -1; 68962306a36Sopenharmony_ci } 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci} 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic void __init ppc4xx_pciex_check_link_sdr(struct ppc4xx_pciex_port *port) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: Checking link...\n", port->index); 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci /* Check for card presence detect if supported, if not, just wait for 69962306a36Sopenharmony_ci * link unconditionally. 70062306a36Sopenharmony_ci * 70162306a36Sopenharmony_ci * note that we don't fail if there is no link, we just filter out 70262306a36Sopenharmony_ci * config space accesses. That way, it will be easier to implement 70362306a36Sopenharmony_ci * hotplug later on. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ci if (!port->has_ibpre || 70662306a36Sopenharmony_ci !ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, 70762306a36Sopenharmony_ci 1 << 28, 1 << 28, 100)) { 70862306a36Sopenharmony_ci printk(KERN_INFO 70962306a36Sopenharmony_ci "PCIE%d: Device detected, waiting for link...\n", 71062306a36Sopenharmony_ci port->index); 71162306a36Sopenharmony_ci if (ppc4xx_pciex_wait_on_sdr(port, PESDRn_LOOP, 71262306a36Sopenharmony_ci 0x1000, 0x1000, 2000)) 71362306a36Sopenharmony_ci printk(KERN_WARNING 71462306a36Sopenharmony_ci "PCIE%d: Link up failed\n", port->index); 71562306a36Sopenharmony_ci else { 71662306a36Sopenharmony_ci printk(KERN_INFO 71762306a36Sopenharmony_ci "PCIE%d: link is up !\n", port->index); 71862306a36Sopenharmony_ci port->link = 1; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci } else 72162306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: No device detected.\n", port->index); 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci#ifdef CONFIG_44x 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci/* Check various reset bits of the 440SPe PCIe core */ 72762306a36Sopenharmony_cistatic int __init ppc440spe_pciex_check_reset(struct device_node *np) 72862306a36Sopenharmony_ci{ 72962306a36Sopenharmony_ci u32 valPE0, valPE1, valPE2; 73062306a36Sopenharmony_ci int err = 0; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* SDR0_PEGPLLLCT1 reset */ 73362306a36Sopenharmony_ci if (!(mfdcri(SDR0, PESDR0_PLLLCT1) & 0x01000000)) { 73462306a36Sopenharmony_ci /* 73562306a36Sopenharmony_ci * the PCIe core was probably already initialised 73662306a36Sopenharmony_ci * by firmware - let's re-reset RCSSET regs 73762306a36Sopenharmony_ci * 73862306a36Sopenharmony_ci * -- Shouldn't we also re-reset the whole thing ? -- BenH 73962306a36Sopenharmony_ci */ 74062306a36Sopenharmony_ci pr_debug("PCIE: SDR0_PLLLCT1 already reset.\n"); 74162306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_440SPE_RCSSET, 0x01010000); 74262306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_440SPE_RCSSET, 0x01010000); 74362306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_440SPE_RCSSET, 0x01010000); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci valPE0 = mfdcri(SDR0, PESDR0_440SPE_RCSSET); 74762306a36Sopenharmony_ci valPE1 = mfdcri(SDR0, PESDR1_440SPE_RCSSET); 74862306a36Sopenharmony_ci valPE2 = mfdcri(SDR0, PESDR2_440SPE_RCSSET); 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci /* SDR0_PExRCSSET rstgu */ 75162306a36Sopenharmony_ci if (!(valPE0 & 0x01000000) || 75262306a36Sopenharmony_ci !(valPE1 & 0x01000000) || 75362306a36Sopenharmony_ci !(valPE2 & 0x01000000)) { 75462306a36Sopenharmony_ci printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstgu error\n"); 75562306a36Sopenharmony_ci err = -1; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci /* SDR0_PExRCSSET rstdl */ 75962306a36Sopenharmony_ci if (!(valPE0 & 0x00010000) || 76062306a36Sopenharmony_ci !(valPE1 & 0x00010000) || 76162306a36Sopenharmony_ci !(valPE2 & 0x00010000)) { 76262306a36Sopenharmony_ci printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstdl error\n"); 76362306a36Sopenharmony_ci err = -1; 76462306a36Sopenharmony_ci } 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci /* SDR0_PExRCSSET rstpyn */ 76762306a36Sopenharmony_ci if ((valPE0 & 0x00001000) || 76862306a36Sopenharmony_ci (valPE1 & 0x00001000) || 76962306a36Sopenharmony_ci (valPE2 & 0x00001000)) { 77062306a36Sopenharmony_ci printk(KERN_INFO "PCIE: SDR0_PExRCSSET rstpyn error\n"); 77162306a36Sopenharmony_ci err = -1; 77262306a36Sopenharmony_ci } 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* SDR0_PExRCSSET hldplb */ 77562306a36Sopenharmony_ci if ((valPE0 & 0x10000000) || 77662306a36Sopenharmony_ci (valPE1 & 0x10000000) || 77762306a36Sopenharmony_ci (valPE2 & 0x10000000)) { 77862306a36Sopenharmony_ci printk(KERN_INFO "PCIE: SDR0_PExRCSSET hldplb error\n"); 77962306a36Sopenharmony_ci err = -1; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci /* SDR0_PExRCSSET rdy */ 78362306a36Sopenharmony_ci if ((valPE0 & 0x00100000) || 78462306a36Sopenharmony_ci (valPE1 & 0x00100000) || 78562306a36Sopenharmony_ci (valPE2 & 0x00100000)) { 78662306a36Sopenharmony_ci printk(KERN_INFO "PCIE: SDR0_PExRCSSET rdy error\n"); 78762306a36Sopenharmony_ci err = -1; 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* SDR0_PExRCSSET shutdown */ 79162306a36Sopenharmony_ci if ((valPE0 & 0x00000100) || 79262306a36Sopenharmony_ci (valPE1 & 0x00000100) || 79362306a36Sopenharmony_ci (valPE2 & 0x00000100)) { 79462306a36Sopenharmony_ci printk(KERN_INFO "PCIE: SDR0_PExRCSSET shutdown error\n"); 79562306a36Sopenharmony_ci err = -1; 79662306a36Sopenharmony_ci } 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci return err; 79962306a36Sopenharmony_ci} 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci/* Global PCIe core initializations for 440SPe core */ 80262306a36Sopenharmony_cistatic int __init ppc440spe_pciex_core_init(struct device_node *np) 80362306a36Sopenharmony_ci{ 80462306a36Sopenharmony_ci int time_out = 20; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci /* Set PLL clock receiver to LVPECL */ 80762306a36Sopenharmony_ci dcri_clrset(SDR0, PESDR0_PLLLCT1, 0, 1 << 28); 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci /* Shouldn't we do all the calibration stuff etc... here ? */ 81062306a36Sopenharmony_ci if (ppc440spe_pciex_check_reset(np)) 81162306a36Sopenharmony_ci return -ENXIO; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci if (!(mfdcri(SDR0, PESDR0_PLLLCT2) & 0x10000)) { 81462306a36Sopenharmony_ci printk(KERN_INFO "PCIE: PESDR_PLLCT2 resistance calibration " 81562306a36Sopenharmony_ci "failed (0x%08x)\n", 81662306a36Sopenharmony_ci mfdcri(SDR0, PESDR0_PLLLCT2)); 81762306a36Sopenharmony_ci return -1; 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* De-assert reset of PCIe PLL, wait for lock */ 82162306a36Sopenharmony_ci dcri_clrset(SDR0, PESDR0_PLLLCT1, 1 << 24, 0); 82262306a36Sopenharmony_ci udelay(3); 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci while (time_out) { 82562306a36Sopenharmony_ci if (!(mfdcri(SDR0, PESDR0_PLLLCT3) & 0x10000000)) { 82662306a36Sopenharmony_ci time_out--; 82762306a36Sopenharmony_ci udelay(1); 82862306a36Sopenharmony_ci } else 82962306a36Sopenharmony_ci break; 83062306a36Sopenharmony_ci } 83162306a36Sopenharmony_ci if (!time_out) { 83262306a36Sopenharmony_ci printk(KERN_INFO "PCIE: VCO output not locked\n"); 83362306a36Sopenharmony_ci return -1; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci pr_debug("PCIE initialization OK\n"); 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci return 3; 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int __init ppc440spe_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 84262306a36Sopenharmony_ci{ 84362306a36Sopenharmony_ci u32 val = 1 << 24; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (port->endpoint) 84662306a36Sopenharmony_ci val = PTYPE_LEGACY_ENDPOINT << 20; 84762306a36Sopenharmony_ci else 84862306a36Sopenharmony_ci val = PTYPE_ROOT_PORT << 20; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (port->index == 0) 85162306a36Sopenharmony_ci val |= LNKW_X8 << 12; 85262306a36Sopenharmony_ci else 85362306a36Sopenharmony_ci val |= LNKW_X4 << 12; 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); 85662306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x20222222); 85762306a36Sopenharmony_ci if (ppc440spe_revA()) 85862306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x11000000); 85962306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL0SET1, 0x35000000); 86062306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL1SET1, 0x35000000); 86162306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL2SET1, 0x35000000); 86262306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL3SET1, 0x35000000); 86362306a36Sopenharmony_ci if (port->index == 0) { 86462306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL4SET1, 86562306a36Sopenharmony_ci 0x35000000); 86662306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL5SET1, 86762306a36Sopenharmony_ci 0x35000000); 86862306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL6SET1, 86962306a36Sopenharmony_ci 0x35000000); 87062306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_440SPE_HSSL7SET1, 87162306a36Sopenharmony_ci 0x35000000); 87262306a36Sopenharmony_ci } 87362306a36Sopenharmony_ci dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 87462306a36Sopenharmony_ci (1 << 24) | (1 << 16), 1 << 12); 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci return ppc4xx_pciex_port_reset_sdr(port); 87762306a36Sopenharmony_ci} 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cistatic int __init ppc440speA_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 88062306a36Sopenharmony_ci{ 88162306a36Sopenharmony_ci return ppc440spe_pciex_init_port_hw(port); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic int __init ppc440speB_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci int rc = ppc440spe_pciex_init_port_hw(port); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci port->has_ibpre = 1; 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ci return rc; 89162306a36Sopenharmony_ci} 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_cistatic int ppc440speA_pciex_init_utl(struct ppc4xx_pciex_port *port) 89462306a36Sopenharmony_ci{ 89562306a36Sopenharmony_ci /* XXX Check what that value means... I hate magic */ 89662306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x68782800); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci /* 89962306a36Sopenharmony_ci * Set buffer allocations and then assert VRB and TXE. 90062306a36Sopenharmony_ci */ 90162306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000); 90262306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_INTR, 0x02000000); 90362306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_OPDBSZ, 0x10000000); 90462306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PBBSZ, 0x53000000); 90562306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_IPHBSZ, 0x08000000); 90662306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_IPDBSZ, 0x10000000); 90762306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_RCIRQEN, 0x00f00000); 90862306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return 0; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_cistatic int ppc440speB_pciex_init_utl(struct ppc4xx_pciex_port *port) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci /* Report CRS to the operating system */ 91662306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PBCTL, 0x08000000); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci return 0; 91962306a36Sopenharmony_ci} 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops ppc440speA_pcie_hwops __initdata = 92262306a36Sopenharmony_ci{ 92362306a36Sopenharmony_ci .want_sdr = true, 92462306a36Sopenharmony_ci .core_init = ppc440spe_pciex_core_init, 92562306a36Sopenharmony_ci .port_init_hw = ppc440speA_pciex_init_port_hw, 92662306a36Sopenharmony_ci .setup_utl = ppc440speA_pciex_init_utl, 92762306a36Sopenharmony_ci .check_link = ppc4xx_pciex_check_link_sdr, 92862306a36Sopenharmony_ci}; 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops ppc440speB_pcie_hwops __initdata = 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci .want_sdr = true, 93362306a36Sopenharmony_ci .core_init = ppc440spe_pciex_core_init, 93462306a36Sopenharmony_ci .port_init_hw = ppc440speB_pciex_init_port_hw, 93562306a36Sopenharmony_ci .setup_utl = ppc440speB_pciex_init_utl, 93662306a36Sopenharmony_ci .check_link = ppc4xx_pciex_check_link_sdr, 93762306a36Sopenharmony_ci}; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_cistatic int __init ppc460ex_pciex_core_init(struct device_node *np) 94062306a36Sopenharmony_ci{ 94162306a36Sopenharmony_ci /* Nothing to do, return 2 ports */ 94262306a36Sopenharmony_ci return 2; 94362306a36Sopenharmony_ci} 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic int __init ppc460ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci u32 val; 94862306a36Sopenharmony_ci u32 utlset1; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci if (port->endpoint) 95162306a36Sopenharmony_ci val = PTYPE_LEGACY_ENDPOINT << 20; 95262306a36Sopenharmony_ci else 95362306a36Sopenharmony_ci val = PTYPE_ROOT_PORT << 20; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (port->index == 0) { 95662306a36Sopenharmony_ci val |= LNKW_X1 << 12; 95762306a36Sopenharmony_ci utlset1 = 0x20000000; 95862306a36Sopenharmony_ci } else { 95962306a36Sopenharmony_ci val |= LNKW_X4 << 12; 96062306a36Sopenharmony_ci utlset1 = 0x20101101; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci 96362306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); 96462306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, utlset1); 96562306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01210000); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci switch (port->index) { 96862306a36Sopenharmony_ci case 0: 96962306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230); 97062306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); 97162306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST,0x10000000); 97462306a36Sopenharmony_ci break; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci case 1: 97762306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L0CDRCTL, 0x00003230); 97862306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L1CDRCTL, 0x00003230); 97962306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L2CDRCTL, 0x00003230); 98062306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L3CDRCTL, 0x00003230); 98162306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L0DRV, 0x00000130); 98262306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L1DRV, 0x00000130); 98362306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L2DRV, 0x00000130); 98462306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L3DRV, 0x00000130); 98562306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L0CLK, 0x00000006); 98662306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L1CLK, 0x00000006); 98762306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L2CLK, 0x00000006); 98862306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_L3CLK, 0x00000006); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460EX_PHY_CTL_RST,0x10000000); 99162306a36Sopenharmony_ci break; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 99562306a36Sopenharmony_ci mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 99662306a36Sopenharmony_ci (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN)); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci /* Poll for PHY reset */ 99962306a36Sopenharmony_ci /* XXX FIXME add timeout */ 100062306a36Sopenharmony_ci switch (port->index) { 100162306a36Sopenharmony_ci case 0: 100262306a36Sopenharmony_ci while (!(mfdcri(SDR0, PESDR0_460EX_RSTSTA) & 0x1)) 100362306a36Sopenharmony_ci udelay(10); 100462306a36Sopenharmony_ci break; 100562306a36Sopenharmony_ci case 1: 100662306a36Sopenharmony_ci while (!(mfdcri(SDR0, PESDR1_460EX_RSTSTA) & 0x1)) 100762306a36Sopenharmony_ci udelay(10); 100862306a36Sopenharmony_ci break; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 101262306a36Sopenharmony_ci (mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) & 101362306a36Sopenharmony_ci ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) | 101462306a36Sopenharmony_ci PESDRx_RCSSET_RSTPYN); 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci port->has_ibpre = 1; 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci return ppc4xx_pciex_port_reset_sdr(port); 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic int ppc460ex_pciex_init_utl(struct ppc4xx_pciex_port *port) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci /* 102662306a36Sopenharmony_ci * Set buffer allocations and then assert VRB and TXE. 102762306a36Sopenharmony_ci */ 102862306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PBCTL, 0x0800000c); 102962306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_OUTTR, 0x08000000); 103062306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_INTR, 0x02000000); 103162306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000); 103262306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PBBSZ, 0x00000000); 103362306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000); 103462306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000); 103562306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_RCIRQEN,0x00f00000); 103662306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci return 0; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops ppc460ex_pcie_hwops __initdata = 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci .want_sdr = true, 104462306a36Sopenharmony_ci .core_init = ppc460ex_pciex_core_init, 104562306a36Sopenharmony_ci .port_init_hw = ppc460ex_pciex_init_port_hw, 104662306a36Sopenharmony_ci .setup_utl = ppc460ex_pciex_init_utl, 104762306a36Sopenharmony_ci .check_link = ppc4xx_pciex_check_link_sdr, 104862306a36Sopenharmony_ci}; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_cistatic int __init apm821xx_pciex_core_init(struct device_node *np) 105162306a36Sopenharmony_ci{ 105262306a36Sopenharmony_ci /* Return the number of pcie port */ 105362306a36Sopenharmony_ci return 1; 105462306a36Sopenharmony_ci} 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cistatic int __init apm821xx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 105762306a36Sopenharmony_ci{ 105862306a36Sopenharmony_ci u32 val; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci /* 106162306a36Sopenharmony_ci * Do a software reset on PCIe ports. 106262306a36Sopenharmony_ci * This code is to fix the issue that pci drivers doesn't re-assign 106362306a36Sopenharmony_ci * bus number for PCIE devices after Uboot 106462306a36Sopenharmony_ci * scanned and configured all the buses (eg. PCIE NIC IntelPro/1000 106562306a36Sopenharmony_ci * PT quad port, SAS LSI 1064E) 106662306a36Sopenharmony_ci */ 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x0); 106962306a36Sopenharmony_ci mdelay(10); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci if (port->endpoint) 107262306a36Sopenharmony_ci val = PTYPE_LEGACY_ENDPOINT << 20; 107362306a36Sopenharmony_ci else 107462306a36Sopenharmony_ci val = PTYPE_ROOT_PORT << 20; 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci val |= LNKW_X1 << 12; 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, val); 107962306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000); 108062306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000); 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_L0CDRCTL, 0x00003230); 108362306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_L0DRV, 0x00000130); 108462306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_L0CLK, 0x00000006); 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x10000000); 108762306a36Sopenharmony_ci mdelay(50); 108862306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460EX_PHY_CTL_RST, 0x30000000); 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 109162306a36Sopenharmony_ci mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) | 109262306a36Sopenharmony_ci (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTPYN)); 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* Poll for PHY reset */ 109562306a36Sopenharmony_ci val = PESDR0_460EX_RSTSTA - port->sdr_base; 109662306a36Sopenharmony_ci if (ppc4xx_pciex_wait_on_sdr(port, val, 0x1, 1, 100)) { 109762306a36Sopenharmony_ci printk(KERN_WARNING "%s: PCIE: Can't reset PHY\n", __func__); 109862306a36Sopenharmony_ci return -EBUSY; 109962306a36Sopenharmony_ci } else { 110062306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 110162306a36Sopenharmony_ci (mfdcri(SDR0, port->sdr_base + PESDRn_RCSSET) & 110262306a36Sopenharmony_ci ~(PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL)) | 110362306a36Sopenharmony_ci PESDRx_RCSSET_RSTPYN); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci port->has_ibpre = 1; 110662306a36Sopenharmony_ci return 0; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops apm821xx_pcie_hwops __initdata = { 111162306a36Sopenharmony_ci .want_sdr = true, 111262306a36Sopenharmony_ci .core_init = apm821xx_pciex_core_init, 111362306a36Sopenharmony_ci .port_init_hw = apm821xx_pciex_init_port_hw, 111462306a36Sopenharmony_ci .setup_utl = ppc460ex_pciex_init_utl, 111562306a36Sopenharmony_ci .check_link = ppc4xx_pciex_check_link_sdr, 111662306a36Sopenharmony_ci}; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_cistatic int __init ppc460sx_pciex_core_init(struct device_node *np) 111962306a36Sopenharmony_ci{ 112062306a36Sopenharmony_ci /* HSS drive amplitude */ 112162306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL0DAMP, 0xB9843211); 112262306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL1DAMP, 0xB9843211); 112362306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL2DAMP, 0xB9843211); 112462306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL3DAMP, 0xB9843211); 112562306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL4DAMP, 0xB9843211); 112662306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL5DAMP, 0xB9843211); 112762306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL6DAMP, 0xB9843211); 112862306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL7DAMP, 0xB9843211); 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL0DAMP, 0xB9843211); 113162306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL1DAMP, 0xB9843211); 113262306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL2DAMP, 0xB9843211); 113362306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL3DAMP, 0xB9843211); 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL0DAMP, 0xB9843211); 113662306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL1DAMP, 0xB9843211); 113762306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL2DAMP, 0xB9843211); 113862306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL3DAMP, 0xB9843211); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci /* HSS TX pre-emphasis */ 114162306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL0COEFA, 0xDCB98987); 114262306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL1COEFA, 0xDCB98987); 114362306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL2COEFA, 0xDCB98987); 114462306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL3COEFA, 0xDCB98987); 114562306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL4COEFA, 0xDCB98987); 114662306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL5COEFA, 0xDCB98987); 114762306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL6COEFA, 0xDCB98987); 114862306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL7COEFA, 0xDCB98987); 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL0COEFA, 0xDCB98987); 115162306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL1COEFA, 0xDCB98987); 115262306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL2COEFA, 0xDCB98987); 115362306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL3COEFA, 0xDCB98987); 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL0COEFA, 0xDCB98987); 115662306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL1COEFA, 0xDCB98987); 115762306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL2COEFA, 0xDCB98987); 115862306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL3COEFA, 0xDCB98987); 115962306a36Sopenharmony_ci 116062306a36Sopenharmony_ci /* HSS TX calibration control */ 116162306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSL1CALDRV, 0x22222222); 116262306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSL1CALDRV, 0x22220000); 116362306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSL1CALDRV, 0x22220000); 116462306a36Sopenharmony_ci 116562306a36Sopenharmony_ci /* HSS TX slew control */ 116662306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSSLEW, 0xFFFFFFFF); 116762306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_HSSSLEW, 0xFFFF0000); 116862306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSSLEW, 0xFFFF0000); 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci /* Set HSS PRBS enabled */ 117162306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_HSSCTLSET, 0x00001130); 117262306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_HSSCTLSET, 0x00001130); 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci udelay(100); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci /* De-assert PLLRESET */ 117762306a36Sopenharmony_ci dcri_clrset(SDR0, PESDR0_PLLLCT2, 0x00000100, 0); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci /* Reset DL, UTL, GPL before configuration */ 118062306a36Sopenharmony_ci mtdcri(SDR0, PESDR0_460SX_RCSSET, 118162306a36Sopenharmony_ci PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU); 118262306a36Sopenharmony_ci mtdcri(SDR0, PESDR1_460SX_RCSSET, 118362306a36Sopenharmony_ci PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU); 118462306a36Sopenharmony_ci mtdcri(SDR0, PESDR2_460SX_RCSSET, 118562306a36Sopenharmony_ci PESDRx_RCSSET_RSTDL | PESDRx_RCSSET_RSTGU); 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci udelay(100); 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci /* 119062306a36Sopenharmony_ci * If bifurcation is not enabled, u-boot would have disabled the 119162306a36Sopenharmony_ci * third PCIe port 119262306a36Sopenharmony_ci */ 119362306a36Sopenharmony_ci if (((mfdcri(SDR0, PESDR1_460SX_HSSCTLSET) & 0x00000001) == 119462306a36Sopenharmony_ci 0x00000001)) { 119562306a36Sopenharmony_ci printk(KERN_INFO "PCI: PCIE bifurcation setup successfully.\n"); 119662306a36Sopenharmony_ci printk(KERN_INFO "PCI: Total 3 PCIE ports are present\n"); 119762306a36Sopenharmony_ci return 3; 119862306a36Sopenharmony_ci } 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci printk(KERN_INFO "PCI: Total 2 PCIE ports are present\n"); 120162306a36Sopenharmony_ci return 2; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistatic int __init ppc460sx_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci 120762306a36Sopenharmony_ci if (port->endpoint) 120862306a36Sopenharmony_ci dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2, 120962306a36Sopenharmony_ci 0x01000000, 0); 121062306a36Sopenharmony_ci else 121162306a36Sopenharmony_ci dcri_clrset(SDR0, port->sdr_base + PESDRn_UTLSET2, 121262306a36Sopenharmony_ci 0, 0x01000000); 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 121562306a36Sopenharmony_ci (PESDRx_RCSSET_RSTGU | PESDRx_RCSSET_RSTDL), 121662306a36Sopenharmony_ci PESDRx_RCSSET_RSTPYN); 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci port->has_ibpre = 1; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci return ppc4xx_pciex_port_reset_sdr(port); 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic int ppc460sx_pciex_init_utl(struct ppc4xx_pciex_port *port) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci /* Max 128 Bytes */ 122662306a36Sopenharmony_ci out_be32 (port->utl_base + PEUTL_PBBSZ, 0x00000000); 122762306a36Sopenharmony_ci /* Assert VRB and TXE - per datasheet turn off addr validation */ 122862306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PCTL, 0x80800000); 122962306a36Sopenharmony_ci return 0; 123062306a36Sopenharmony_ci} 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_cistatic void __init ppc460sx_pciex_check_link(struct ppc4xx_pciex_port *port) 123362306a36Sopenharmony_ci{ 123462306a36Sopenharmony_ci void __iomem *mbase; 123562306a36Sopenharmony_ci int attempt = 50; 123662306a36Sopenharmony_ci 123762306a36Sopenharmony_ci port->link = 0; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000); 124062306a36Sopenharmony_ci if (mbase == NULL) { 124162306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map internal config space !", 124262306a36Sopenharmony_ci port->node); 124362306a36Sopenharmony_ci return; 124462306a36Sopenharmony_ci } 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci while (attempt && (0 == (in_le32(mbase + PECFG_460SX_DLLSTA) 124762306a36Sopenharmony_ci & PECFG_460SX_DLLSTA_LINKUP))) { 124862306a36Sopenharmony_ci attempt--; 124962306a36Sopenharmony_ci mdelay(10); 125062306a36Sopenharmony_ci } 125162306a36Sopenharmony_ci if (attempt) 125262306a36Sopenharmony_ci port->link = 1; 125362306a36Sopenharmony_ci iounmap(mbase); 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops ppc460sx_pcie_hwops __initdata = { 125762306a36Sopenharmony_ci .want_sdr = true, 125862306a36Sopenharmony_ci .core_init = ppc460sx_pciex_core_init, 125962306a36Sopenharmony_ci .port_init_hw = ppc460sx_pciex_init_port_hw, 126062306a36Sopenharmony_ci .setup_utl = ppc460sx_pciex_init_utl, 126162306a36Sopenharmony_ci .check_link = ppc460sx_pciex_check_link, 126262306a36Sopenharmony_ci}; 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci#endif /* CONFIG_44x */ 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci#ifdef CONFIG_40x 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic int __init ppc405ex_pciex_core_init(struct device_node *np) 126962306a36Sopenharmony_ci{ 127062306a36Sopenharmony_ci /* Nothing to do, return 2 ports */ 127162306a36Sopenharmony_ci return 2; 127262306a36Sopenharmony_ci} 127362306a36Sopenharmony_ci 127462306a36Sopenharmony_cistatic void __init ppc405ex_pcie_phy_reset(struct ppc4xx_pciex_port *port) 127562306a36Sopenharmony_ci{ 127662306a36Sopenharmony_ci /* Assert the PE0_PHY reset */ 127762306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01010000); 127862306a36Sopenharmony_ci msleep(1); 127962306a36Sopenharmony_ci 128062306a36Sopenharmony_ci /* deassert the PE0_hotreset */ 128162306a36Sopenharmony_ci if (port->endpoint) 128262306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01111000); 128362306a36Sopenharmony_ci else 128462306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x01101000); 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci /* poll for phy !reset */ 128762306a36Sopenharmony_ci /* XXX FIXME add timeout */ 128862306a36Sopenharmony_ci while (!(mfdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSTA) & 0x00001000)) 128962306a36Sopenharmony_ci ; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci /* deassert the PE0_gpl_utl_reset */ 129262306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_RCSSET, 0x00101000); 129362306a36Sopenharmony_ci} 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_cistatic int __init ppc405ex_pciex_init_port_hw(struct ppc4xx_pciex_port *port) 129662306a36Sopenharmony_ci{ 129762306a36Sopenharmony_ci u32 val; 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_ci if (port->endpoint) 130062306a36Sopenharmony_ci val = PTYPE_LEGACY_ENDPOINT; 130162306a36Sopenharmony_ci else 130262306a36Sopenharmony_ci val = PTYPE_ROOT_PORT; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_DLPSET, 130562306a36Sopenharmony_ci 1 << 24 | val << 20 | LNKW_X1 << 12); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET1, 0x00000000); 130862306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_UTLSET2, 0x01010000); 130962306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET1, 0x720F0000); 131062306a36Sopenharmony_ci mtdcri(SDR0, port->sdr_base + PESDRn_405EX_PHYSET2, 0x70600003); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci /* 131362306a36Sopenharmony_ci * Only reset the PHY when no link is currently established. 131462306a36Sopenharmony_ci * This is for the Atheros PCIe board which has problems to establish 131562306a36Sopenharmony_ci * the link (again) after this PHY reset. All other currently tested 131662306a36Sopenharmony_ci * PCIe boards don't show this problem. 131762306a36Sopenharmony_ci * This has to be re-tested and fixed in a later release! 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_ci val = mfdcri(SDR0, port->sdr_base + PESDRn_LOOP); 132062306a36Sopenharmony_ci if (!(val & 0x00001000)) 132162306a36Sopenharmony_ci ppc405ex_pcie_phy_reset(port); 132262306a36Sopenharmony_ci 132362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFG, 0x10000000); /* guarded on */ 132462306a36Sopenharmony_ci 132562306a36Sopenharmony_ci port->has_ibpre = 1; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci return ppc4xx_pciex_port_reset_sdr(port); 132862306a36Sopenharmony_ci} 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_cistatic int ppc405ex_pciex_init_utl(struct ppc4xx_pciex_port *port) 133162306a36Sopenharmony_ci{ 133262306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_SPECIAL, 0x0); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci /* 133562306a36Sopenharmony_ci * Set buffer allocations and then assert VRB and TXE. 133662306a36Sopenharmony_ci */ 133762306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_OUTTR, 0x02000000); 133862306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_INTR, 0x02000000); 133962306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_OPDBSZ, 0x04000000); 134062306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PBBSZ, 0x21000000); 134162306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_IPHBSZ, 0x02000000); 134262306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_IPDBSZ, 0x04000000); 134362306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_RCIRQEN, 0x00f00000); 134462306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PCTL, 0x80800066); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_PBCTL, 0x08000000); 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci return 0; 134962306a36Sopenharmony_ci} 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops ppc405ex_pcie_hwops __initdata = 135262306a36Sopenharmony_ci{ 135362306a36Sopenharmony_ci .want_sdr = true, 135462306a36Sopenharmony_ci .core_init = ppc405ex_pciex_core_init, 135562306a36Sopenharmony_ci .port_init_hw = ppc405ex_pciex_init_port_hw, 135662306a36Sopenharmony_ci .setup_utl = ppc405ex_pciex_init_utl, 135762306a36Sopenharmony_ci .check_link = ppc4xx_pciex_check_link_sdr, 135862306a36Sopenharmony_ci}; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci#endif /* CONFIG_40x */ 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_ci#ifdef CONFIG_476FPE 136362306a36Sopenharmony_cistatic int __init ppc_476fpe_pciex_core_init(struct device_node *np) 136462306a36Sopenharmony_ci{ 136562306a36Sopenharmony_ci return 4; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic void __init ppc_476fpe_pciex_check_link(struct ppc4xx_pciex_port *port) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci u32 timeout_ms = 20; 137162306a36Sopenharmony_ci u32 val = 0, mask = (PECFG_TLDLP_LNKUP|PECFG_TLDLP_PRESENT); 137262306a36Sopenharmony_ci void __iomem *mbase = ioremap(port->cfg_space.start + 0x10000000, 137362306a36Sopenharmony_ci 0x1000); 137462306a36Sopenharmony_ci 137562306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: Checking link...\n", port->index); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci if (mbase == NULL) { 137862306a36Sopenharmony_ci printk(KERN_WARNING "PCIE%d: failed to get cfg space\n", 137962306a36Sopenharmony_ci port->index); 138062306a36Sopenharmony_ci return; 138162306a36Sopenharmony_ci } 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci while (timeout_ms--) { 138462306a36Sopenharmony_ci val = in_le32(mbase + PECFG_TLDLP); 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci if ((val & mask) == mask) 138762306a36Sopenharmony_ci break; 138862306a36Sopenharmony_ci msleep(10); 138962306a36Sopenharmony_ci } 139062306a36Sopenharmony_ci 139162306a36Sopenharmony_ci if (val & PECFG_TLDLP_PRESENT) { 139262306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: link is up !\n", port->index); 139362306a36Sopenharmony_ci port->link = 1; 139462306a36Sopenharmony_ci } else 139562306a36Sopenharmony_ci printk(KERN_WARNING "PCIE%d: Link up failed\n", port->index); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci iounmap(mbase); 139862306a36Sopenharmony_ci} 139962306a36Sopenharmony_ci 140062306a36Sopenharmony_cistatic struct ppc4xx_pciex_hwops ppc_476fpe_pcie_hwops __initdata = 140162306a36Sopenharmony_ci{ 140262306a36Sopenharmony_ci .core_init = ppc_476fpe_pciex_core_init, 140362306a36Sopenharmony_ci .check_link = ppc_476fpe_pciex_check_link, 140462306a36Sopenharmony_ci}; 140562306a36Sopenharmony_ci#endif /* CONFIG_476FPE */ 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci/* Check that the core has been initied and if not, do it */ 140862306a36Sopenharmony_cistatic int __init ppc4xx_pciex_check_core_init(struct device_node *np) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci static int core_init; 141162306a36Sopenharmony_ci int count = -ENODEV; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (core_init++) 141462306a36Sopenharmony_ci return 0; 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci#ifdef CONFIG_44x 141762306a36Sopenharmony_ci if (of_device_is_compatible(np, "ibm,plb-pciex-440spe")) { 141862306a36Sopenharmony_ci if (ppc440spe_revA()) 141962306a36Sopenharmony_ci ppc4xx_pciex_hwops = &ppc440speA_pcie_hwops; 142062306a36Sopenharmony_ci else 142162306a36Sopenharmony_ci ppc4xx_pciex_hwops = &ppc440speB_pcie_hwops; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci if (of_device_is_compatible(np, "ibm,plb-pciex-460ex")) 142462306a36Sopenharmony_ci ppc4xx_pciex_hwops = &ppc460ex_pcie_hwops; 142562306a36Sopenharmony_ci if (of_device_is_compatible(np, "ibm,plb-pciex-460sx")) 142662306a36Sopenharmony_ci ppc4xx_pciex_hwops = &ppc460sx_pcie_hwops; 142762306a36Sopenharmony_ci if (of_device_is_compatible(np, "ibm,plb-pciex-apm821xx")) 142862306a36Sopenharmony_ci ppc4xx_pciex_hwops = &apm821xx_pcie_hwops; 142962306a36Sopenharmony_ci#endif /* CONFIG_44x */ 143062306a36Sopenharmony_ci#ifdef CONFIG_40x 143162306a36Sopenharmony_ci if (of_device_is_compatible(np, "ibm,plb-pciex-405ex")) 143262306a36Sopenharmony_ci ppc4xx_pciex_hwops = &ppc405ex_pcie_hwops; 143362306a36Sopenharmony_ci#endif 143462306a36Sopenharmony_ci#ifdef CONFIG_476FPE 143562306a36Sopenharmony_ci if (of_device_is_compatible(np, "ibm,plb-pciex-476fpe") 143662306a36Sopenharmony_ci || of_device_is_compatible(np, "ibm,plb-pciex-476gtr")) 143762306a36Sopenharmony_ci ppc4xx_pciex_hwops = &ppc_476fpe_pcie_hwops; 143862306a36Sopenharmony_ci#endif 143962306a36Sopenharmony_ci if (ppc4xx_pciex_hwops == NULL) { 144062306a36Sopenharmony_ci printk(KERN_WARNING "PCIE: unknown host type %pOF\n", np); 144162306a36Sopenharmony_ci return -ENODEV; 144262306a36Sopenharmony_ci } 144362306a36Sopenharmony_ci 144462306a36Sopenharmony_ci count = ppc4xx_pciex_hwops->core_init(np); 144562306a36Sopenharmony_ci if (count > 0) { 144662306a36Sopenharmony_ci ppc4xx_pciex_ports = 144762306a36Sopenharmony_ci kcalloc(count, sizeof(struct ppc4xx_pciex_port), 144862306a36Sopenharmony_ci GFP_KERNEL); 144962306a36Sopenharmony_ci if (ppc4xx_pciex_ports) { 145062306a36Sopenharmony_ci ppc4xx_pciex_port_count = count; 145162306a36Sopenharmony_ci return 0; 145262306a36Sopenharmony_ci } 145362306a36Sopenharmony_ci printk(KERN_WARNING "PCIE: failed to allocate ports array\n"); 145462306a36Sopenharmony_ci return -ENOMEM; 145562306a36Sopenharmony_ci } 145662306a36Sopenharmony_ci return -ENODEV; 145762306a36Sopenharmony_ci} 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_cistatic void __init ppc4xx_pciex_port_init_mapping(struct ppc4xx_pciex_port *port) 146062306a36Sopenharmony_ci{ 146162306a36Sopenharmony_ci /* We map PCI Express configuration based on the reg property */ 146262306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFGBAH, 146362306a36Sopenharmony_ci RES_TO_U32_HIGH(port->cfg_space.start)); 146462306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFGBAL, 146562306a36Sopenharmony_ci RES_TO_U32_LOW(port->cfg_space.start)); 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_ci /* XXX FIXME: Use size from reg property. For now, map 512M */ 146862306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFGMSK, 0xe0000001); 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci /* We map UTL registers based on the reg property */ 147162306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_REGBAH, 147262306a36Sopenharmony_ci RES_TO_U32_HIGH(port->utl_regs.start)); 147362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_REGBAL, 147462306a36Sopenharmony_ci RES_TO_U32_LOW(port->utl_regs.start)); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci /* XXX FIXME: Use size from reg property */ 147762306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_REGMSK, 0x00007001); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci /* Disable all other outbound windows */ 148062306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 0); 148162306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, 0); 148262306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 0); 148362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_MSGMSK, 0); 148462306a36Sopenharmony_ci} 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_cistatic int __init ppc4xx_pciex_port_init(struct ppc4xx_pciex_port *port) 148762306a36Sopenharmony_ci{ 148862306a36Sopenharmony_ci int rc = 0; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci /* Init HW */ 149162306a36Sopenharmony_ci if (ppc4xx_pciex_hwops->port_init_hw) 149262306a36Sopenharmony_ci rc = ppc4xx_pciex_hwops->port_init_hw(port); 149362306a36Sopenharmony_ci if (rc != 0) 149462306a36Sopenharmony_ci return rc; 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_ci /* 149762306a36Sopenharmony_ci * Initialize mapping: disable all regions and configure 149862306a36Sopenharmony_ci * CFG and REG regions based on resources in the device tree 149962306a36Sopenharmony_ci */ 150062306a36Sopenharmony_ci ppc4xx_pciex_port_init_mapping(port); 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci if (ppc4xx_pciex_hwops->check_link) 150362306a36Sopenharmony_ci ppc4xx_pciex_hwops->check_link(port); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci /* 150662306a36Sopenharmony_ci * Map UTL 150762306a36Sopenharmony_ci */ 150862306a36Sopenharmony_ci port->utl_base = ioremap(port->utl_regs.start, 0x100); 150962306a36Sopenharmony_ci BUG_ON(port->utl_base == NULL); 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci /* 151262306a36Sopenharmony_ci * Setup UTL registers --BenH. 151362306a36Sopenharmony_ci */ 151462306a36Sopenharmony_ci if (ppc4xx_pciex_hwops->setup_utl) 151562306a36Sopenharmony_ci ppc4xx_pciex_hwops->setup_utl(port); 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci /* 151862306a36Sopenharmony_ci * Check for VC0 active or PLL Locked and assert RDY. 151962306a36Sopenharmony_ci */ 152062306a36Sopenharmony_ci if (port->sdr_base) { 152162306a36Sopenharmony_ci if (of_device_is_compatible(port->node, 152262306a36Sopenharmony_ci "ibm,plb-pciex-460sx")){ 152362306a36Sopenharmony_ci if (port->link && ppc4xx_pciex_wait_on_sdr(port, 152462306a36Sopenharmony_ci PESDRn_RCSSTS, 152562306a36Sopenharmony_ci 1 << 12, 1 << 12, 5000)) { 152662306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: PLL not locked\n", 152762306a36Sopenharmony_ci port->index); 152862306a36Sopenharmony_ci port->link = 0; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci } else if (port->link && 153162306a36Sopenharmony_ci ppc4xx_pciex_wait_on_sdr(port, PESDRn_RCSSTS, 153262306a36Sopenharmony_ci 1 << 16, 1 << 16, 5000)) { 153362306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: VC0 not active\n", 153462306a36Sopenharmony_ci port->index); 153562306a36Sopenharmony_ci port->link = 0; 153662306a36Sopenharmony_ci } 153762306a36Sopenharmony_ci 153862306a36Sopenharmony_ci dcri_clrset(SDR0, port->sdr_base + PESDRn_RCSSET, 0, 1 << 20); 153962306a36Sopenharmony_ci } 154062306a36Sopenharmony_ci 154162306a36Sopenharmony_ci msleep(100); 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci return 0; 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic int ppc4xx_pciex_validate_bdf(struct ppc4xx_pciex_port *port, 154762306a36Sopenharmony_ci struct pci_bus *bus, 154862306a36Sopenharmony_ci unsigned int devfn) 154962306a36Sopenharmony_ci{ 155062306a36Sopenharmony_ci static int message; 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci /* Endpoint can not generate upstream(remote) config cycles */ 155362306a36Sopenharmony_ci if (port->endpoint && bus->number != port->hose->first_busno) 155462306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci /* Check we are within the mapped range */ 155762306a36Sopenharmony_ci if (bus->number > port->hose->last_busno) { 155862306a36Sopenharmony_ci if (!message) { 155962306a36Sopenharmony_ci printk(KERN_WARNING "Warning! Probing bus %u" 156062306a36Sopenharmony_ci " out of range !\n", bus->number); 156162306a36Sopenharmony_ci message++; 156262306a36Sopenharmony_ci } 156362306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 156462306a36Sopenharmony_ci } 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* The root complex has only one device / function */ 156762306a36Sopenharmony_ci if (bus->number == port->hose->first_busno && devfn != 0) 156862306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci /* The other side of the RC has only one device as well */ 157162306a36Sopenharmony_ci if (bus->number == (port->hose->first_busno + 1) && 157262306a36Sopenharmony_ci PCI_SLOT(devfn) != 0) 157362306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 157462306a36Sopenharmony_ci 157562306a36Sopenharmony_ci /* Check if we have a link */ 157662306a36Sopenharmony_ci if ((bus->number != port->hose->first_busno) && !port->link) 157762306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci return 0; 158062306a36Sopenharmony_ci} 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_cistatic void __iomem *ppc4xx_pciex_get_config_base(struct ppc4xx_pciex_port *port, 158362306a36Sopenharmony_ci struct pci_bus *bus, 158462306a36Sopenharmony_ci unsigned int devfn) 158562306a36Sopenharmony_ci{ 158662306a36Sopenharmony_ci int relbus; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci /* Remove the casts when we finally remove the stupid volatile 158962306a36Sopenharmony_ci * in struct pci_controller 159062306a36Sopenharmony_ci */ 159162306a36Sopenharmony_ci if (bus->number == port->hose->first_busno) 159262306a36Sopenharmony_ci return (void __iomem *)port->hose->cfg_addr; 159362306a36Sopenharmony_ci 159462306a36Sopenharmony_ci relbus = bus->number - (port->hose->first_busno + 1); 159562306a36Sopenharmony_ci return (void __iomem *)port->hose->cfg_data + 159662306a36Sopenharmony_ci ((relbus << 20) | (devfn << 12)); 159762306a36Sopenharmony_ci} 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_cistatic int ppc4xx_pciex_read_config(struct pci_bus *bus, unsigned int devfn, 160062306a36Sopenharmony_ci int offset, int len, u32 *val) 160162306a36Sopenharmony_ci{ 160262306a36Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 160362306a36Sopenharmony_ci struct ppc4xx_pciex_port *port = 160462306a36Sopenharmony_ci &ppc4xx_pciex_ports[hose->indirect_type]; 160562306a36Sopenharmony_ci void __iomem *addr; 160662306a36Sopenharmony_ci u32 gpl_cfg; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci BUG_ON(hose != port->hose); 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0) 161162306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci addr = ppc4xx_pciex_get_config_base(port, bus, devfn); 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* 161662306a36Sopenharmony_ci * Reading from configuration space of non-existing device can 161762306a36Sopenharmony_ci * generate transaction errors. For the read duration we suppress 161862306a36Sopenharmony_ci * assertion of machine check exceptions to avoid those. 161962306a36Sopenharmony_ci */ 162062306a36Sopenharmony_ci gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG); 162162306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* Make sure no CRS is recorded */ 162462306a36Sopenharmony_ci out_be32(port->utl_base + PEUTL_RCSTA, 0x00040000); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci switch (len) { 162762306a36Sopenharmony_ci case 1: 162862306a36Sopenharmony_ci *val = in_8((u8 *)(addr + offset)); 162962306a36Sopenharmony_ci break; 163062306a36Sopenharmony_ci case 2: 163162306a36Sopenharmony_ci *val = in_le16((u16 *)(addr + offset)); 163262306a36Sopenharmony_ci break; 163362306a36Sopenharmony_ci default: 163462306a36Sopenharmony_ci *val = in_le32((u32 *)(addr + offset)); 163562306a36Sopenharmony_ci break; 163662306a36Sopenharmony_ci } 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_ci pr_debug("pcie-config-read: bus=%3d [%3d..%3d] devfn=0x%04x" 163962306a36Sopenharmony_ci " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n", 164062306a36Sopenharmony_ci bus->number, hose->first_busno, hose->last_busno, 164162306a36Sopenharmony_ci devfn, offset, len, addr + offset, *val); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci /* Check for CRS (440SPe rev B does that for us but heh ..) */ 164462306a36Sopenharmony_ci if (in_be32(port->utl_base + PEUTL_RCSTA) & 0x00040000) { 164562306a36Sopenharmony_ci pr_debug("Got CRS !\n"); 164662306a36Sopenharmony_ci if (len != 4 || offset != 0) 164762306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 164862306a36Sopenharmony_ci *val = 0xffff0001; 164962306a36Sopenharmony_ci } 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg); 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 165462306a36Sopenharmony_ci} 165562306a36Sopenharmony_ci 165662306a36Sopenharmony_cistatic int ppc4xx_pciex_write_config(struct pci_bus *bus, unsigned int devfn, 165762306a36Sopenharmony_ci int offset, int len, u32 val) 165862306a36Sopenharmony_ci{ 165962306a36Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 166062306a36Sopenharmony_ci struct ppc4xx_pciex_port *port = 166162306a36Sopenharmony_ci &ppc4xx_pciex_ports[hose->indirect_type]; 166262306a36Sopenharmony_ci void __iomem *addr; 166362306a36Sopenharmony_ci u32 gpl_cfg; 166462306a36Sopenharmony_ci 166562306a36Sopenharmony_ci if (ppc4xx_pciex_validate_bdf(port, bus, devfn) != 0) 166662306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci addr = ppc4xx_pciex_get_config_base(port, bus, devfn); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci /* 167162306a36Sopenharmony_ci * Reading from configuration space of non-existing device can 167262306a36Sopenharmony_ci * generate transaction errors. For the read duration we suppress 167362306a36Sopenharmony_ci * assertion of machine check exceptions to avoid those. 167462306a36Sopenharmony_ci */ 167562306a36Sopenharmony_ci gpl_cfg = dcr_read(port->dcrs, DCRO_PEGPL_CFG); 167662306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg | GPL_DMER_MASK_DISA); 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci pr_debug("pcie-config-write: bus=%3d [%3d..%3d] devfn=0x%04x" 167962306a36Sopenharmony_ci " offset=0x%04x len=%d, addr=0x%p val=0x%08x\n", 168062306a36Sopenharmony_ci bus->number, hose->first_busno, hose->last_busno, 168162306a36Sopenharmony_ci devfn, offset, len, addr + offset, val); 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_ci switch (len) { 168462306a36Sopenharmony_ci case 1: 168562306a36Sopenharmony_ci out_8((u8 *)(addr + offset), val); 168662306a36Sopenharmony_ci break; 168762306a36Sopenharmony_ci case 2: 168862306a36Sopenharmony_ci out_le16((u16 *)(addr + offset), val); 168962306a36Sopenharmony_ci break; 169062306a36Sopenharmony_ci default: 169162306a36Sopenharmony_ci out_le32((u32 *)(addr + offset), val); 169262306a36Sopenharmony_ci break; 169362306a36Sopenharmony_ci } 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_CFG, gpl_cfg); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic struct pci_ops ppc4xx_pciex_pci_ops = 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci .read = ppc4xx_pciex_read_config, 170362306a36Sopenharmony_ci .write = ppc4xx_pciex_write_config, 170462306a36Sopenharmony_ci}; 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_cistatic int __init ppc4xx_setup_one_pciex_POM(struct ppc4xx_pciex_port *port, 170762306a36Sopenharmony_ci struct pci_controller *hose, 170862306a36Sopenharmony_ci void __iomem *mbase, 170962306a36Sopenharmony_ci u64 plb_addr, 171062306a36Sopenharmony_ci u64 pci_addr, 171162306a36Sopenharmony_ci u64 size, 171262306a36Sopenharmony_ci unsigned int flags, 171362306a36Sopenharmony_ci int index) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci u32 lah, lal, pciah, pcial, sa; 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci if (!is_power_of_2(size) || 171862306a36Sopenharmony_ci (index < 2 && size < 0x100000) || 171962306a36Sopenharmony_ci (index == 2 && size < 0x100) || 172062306a36Sopenharmony_ci (plb_addr & (size - 1)) != 0) { 172162306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Resource out of range\n", hose->dn); 172262306a36Sopenharmony_ci return -1; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci /* Calculate register values */ 172662306a36Sopenharmony_ci lah = RES_TO_U32_HIGH(plb_addr); 172762306a36Sopenharmony_ci lal = RES_TO_U32_LOW(plb_addr); 172862306a36Sopenharmony_ci pciah = RES_TO_U32_HIGH(pci_addr); 172962306a36Sopenharmony_ci pcial = RES_TO_U32_LOW(pci_addr); 173062306a36Sopenharmony_ci sa = (0xffffffffu << ilog2(size)) | 0x1; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci /* Program register values */ 173362306a36Sopenharmony_ci switch (index) { 173462306a36Sopenharmony_ci case 0: 173562306a36Sopenharmony_ci out_le32(mbase + PECFG_POM0LAH, pciah); 173662306a36Sopenharmony_ci out_le32(mbase + PECFG_POM0LAL, pcial); 173762306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAH, lah); 173862306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1BAL, lal); 173962306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKH, 0x7fffffff); 174062306a36Sopenharmony_ci /*Enabled and single region */ 174162306a36Sopenharmony_ci if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) 174262306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 174362306a36Sopenharmony_ci sa | DCRO_PEGPL_460SX_OMR1MSKL_UOT 174462306a36Sopenharmony_ci | DCRO_PEGPL_OMRxMSKL_VAL); 174562306a36Sopenharmony_ci else if (of_device_is_compatible( 174662306a36Sopenharmony_ci port->node, "ibm,plb-pciex-476fpe") || 174762306a36Sopenharmony_ci of_device_is_compatible( 174862306a36Sopenharmony_ci port->node, "ibm,plb-pciex-476gtr")) 174962306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 175062306a36Sopenharmony_ci sa | DCRO_PEGPL_476FPE_OMR1MSKL_UOT 175162306a36Sopenharmony_ci | DCRO_PEGPL_OMRxMSKL_VAL); 175262306a36Sopenharmony_ci else 175362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR1MSKL, 175462306a36Sopenharmony_ci sa | DCRO_PEGPL_OMR1MSKL_UOT 175562306a36Sopenharmony_ci | DCRO_PEGPL_OMRxMSKL_VAL); 175662306a36Sopenharmony_ci break; 175762306a36Sopenharmony_ci case 1: 175862306a36Sopenharmony_ci out_le32(mbase + PECFG_POM1LAH, pciah); 175962306a36Sopenharmony_ci out_le32(mbase + PECFG_POM1LAL, pcial); 176062306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAH, lah); 176162306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR2BAL, lal); 176262306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKH, 0x7fffffff); 176362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR2MSKL, 176462306a36Sopenharmony_ci sa | DCRO_PEGPL_OMRxMSKL_VAL); 176562306a36Sopenharmony_ci break; 176662306a36Sopenharmony_ci case 2: 176762306a36Sopenharmony_ci out_le32(mbase + PECFG_POM2LAH, pciah); 176862306a36Sopenharmony_ci out_le32(mbase + PECFG_POM2LAL, pcial); 176962306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAH, lah); 177062306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR3BAL, lal); 177162306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKH, 0x7fffffff); 177262306a36Sopenharmony_ci /* Note that 3 here means enabled | IO space !!! */ 177362306a36Sopenharmony_ci dcr_write(port->dcrs, DCRO_PEGPL_OMR3MSKL, 177462306a36Sopenharmony_ci sa | DCRO_PEGPL_OMR3MSKL_IO 177562306a36Sopenharmony_ci | DCRO_PEGPL_OMRxMSKL_VAL); 177662306a36Sopenharmony_ci break; 177762306a36Sopenharmony_ci } 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_ci return 0; 178062306a36Sopenharmony_ci} 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_cistatic void __init ppc4xx_configure_pciex_POMs(struct ppc4xx_pciex_port *port, 178362306a36Sopenharmony_ci struct pci_controller *hose, 178462306a36Sopenharmony_ci void __iomem *mbase) 178562306a36Sopenharmony_ci{ 178662306a36Sopenharmony_ci int i, j, found_isa_hole = 0; 178762306a36Sopenharmony_ci 178862306a36Sopenharmony_ci /* Setup outbound memory windows */ 178962306a36Sopenharmony_ci for (i = j = 0; i < 3; i++) { 179062306a36Sopenharmony_ci struct resource *res = &hose->mem_resources[i]; 179162306a36Sopenharmony_ci resource_size_t offset = hose->mem_offset[i]; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci /* we only care about memory windows */ 179462306a36Sopenharmony_ci if (!(res->flags & IORESOURCE_MEM)) 179562306a36Sopenharmony_ci continue; 179662306a36Sopenharmony_ci if (j > 1) { 179762306a36Sopenharmony_ci printk(KERN_WARNING "%pOF: Too many ranges\n", 179862306a36Sopenharmony_ci port->node); 179962306a36Sopenharmony_ci break; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci /* Configure the resource */ 180362306a36Sopenharmony_ci if (ppc4xx_setup_one_pciex_POM(port, hose, mbase, 180462306a36Sopenharmony_ci res->start, 180562306a36Sopenharmony_ci res->start - offset, 180662306a36Sopenharmony_ci resource_size(res), 180762306a36Sopenharmony_ci res->flags, 180862306a36Sopenharmony_ci j) == 0) { 180962306a36Sopenharmony_ci j++; 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci /* If the resource PCI address is 0 then we have our 181262306a36Sopenharmony_ci * ISA memory hole 181362306a36Sopenharmony_ci */ 181462306a36Sopenharmony_ci if (res->start == offset) 181562306a36Sopenharmony_ci found_isa_hole = 1; 181662306a36Sopenharmony_ci } 181762306a36Sopenharmony_ci } 181862306a36Sopenharmony_ci 181962306a36Sopenharmony_ci /* Handle ISA memory hole if not already covered */ 182062306a36Sopenharmony_ci if (j <= 1 && !found_isa_hole && hose->isa_mem_size) 182162306a36Sopenharmony_ci if (ppc4xx_setup_one_pciex_POM(port, hose, mbase, 182262306a36Sopenharmony_ci hose->isa_mem_phys, 0, 182362306a36Sopenharmony_ci hose->isa_mem_size, 0, j) == 0) 182462306a36Sopenharmony_ci printk(KERN_INFO "%pOF: Legacy ISA memory support enabled\n", 182562306a36Sopenharmony_ci hose->dn); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci /* Configure IO, always 64K starting at 0. We hard wire it to 64K ! 182862306a36Sopenharmony_ci * Note also that it -has- to be region index 2 on this HW 182962306a36Sopenharmony_ci */ 183062306a36Sopenharmony_ci if (hose->io_resource.flags & IORESOURCE_IO) 183162306a36Sopenharmony_ci ppc4xx_setup_one_pciex_POM(port, hose, mbase, 183262306a36Sopenharmony_ci hose->io_base_phys, 0, 183362306a36Sopenharmony_ci 0x10000, IORESOURCE_IO, 2); 183462306a36Sopenharmony_ci} 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_cistatic void __init ppc4xx_configure_pciex_PIMs(struct ppc4xx_pciex_port *port, 183762306a36Sopenharmony_ci struct pci_controller *hose, 183862306a36Sopenharmony_ci void __iomem *mbase, 183962306a36Sopenharmony_ci struct resource *res) 184062306a36Sopenharmony_ci{ 184162306a36Sopenharmony_ci resource_size_t size = resource_size(res); 184262306a36Sopenharmony_ci u64 sa; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci if (port->endpoint) { 184562306a36Sopenharmony_ci resource_size_t ep_addr = 0; 184662306a36Sopenharmony_ci resource_size_t ep_size = 32 << 20; 184762306a36Sopenharmony_ci 184862306a36Sopenharmony_ci /* Currently we map a fixed 64MByte window to PLB address 184962306a36Sopenharmony_ci * 0 (SDRAM). This should probably be configurable via a dts 185062306a36Sopenharmony_ci * property. 185162306a36Sopenharmony_ci */ 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci /* Calculate window size */ 185462306a36Sopenharmony_ci sa = (0xffffffffffffffffull << ilog2(ep_size)); 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_ci /* Setup BAR0 */ 185762306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa)); 185862306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa) | 185962306a36Sopenharmony_ci PCI_BASE_ADDRESS_MEM_TYPE_64); 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci /* Disable BAR1 & BAR2 */ 186262306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR1MPA, 0); 186362306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR2HMPA, 0); 186462306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR2LMPA, 0); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM01SAH, RES_TO_U32_HIGH(sa)); 186762306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM01SAL, RES_TO_U32_LOW(sa)); 186862306a36Sopenharmony_ci 186962306a36Sopenharmony_ci out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(ep_addr)); 187062306a36Sopenharmony_ci out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(ep_addr)); 187162306a36Sopenharmony_ci } else { 187262306a36Sopenharmony_ci /* Calculate window size */ 187362306a36Sopenharmony_ci sa = (0xffffffffffffffffull << ilog2(size)); 187462306a36Sopenharmony_ci if (res->flags & IORESOURCE_PREFETCH) 187562306a36Sopenharmony_ci sa |= PCI_BASE_ADDRESS_MEM_PREFETCH; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx") || 187862306a36Sopenharmony_ci of_device_is_compatible( 187962306a36Sopenharmony_ci port->node, "ibm,plb-pciex-476fpe") || 188062306a36Sopenharmony_ci of_device_is_compatible( 188162306a36Sopenharmony_ci port->node, "ibm,plb-pciex-476gtr")) 188262306a36Sopenharmony_ci sa |= PCI_BASE_ADDRESS_MEM_TYPE_64; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR0HMPA, RES_TO_U32_HIGH(sa)); 188562306a36Sopenharmony_ci out_le32(mbase + PECFG_BAR0LMPA, RES_TO_U32_LOW(sa)); 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_ci /* The setup of the split looks weird to me ... let's see 188862306a36Sopenharmony_ci * if it works 188962306a36Sopenharmony_ci */ 189062306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM0LAL, 0x00000000); 189162306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM0LAH, 0x00000000); 189262306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM1LAL, 0x00000000); 189362306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM1LAH, 0x00000000); 189462306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM01SAH, 0xffff0000); 189562306a36Sopenharmony_ci out_le32(mbase + PECFG_PIM01SAL, 0x00000000); 189662306a36Sopenharmony_ci 189762306a36Sopenharmony_ci out_le32(mbase + PCI_BASE_ADDRESS_0, RES_TO_U32_LOW(res->start)); 189862306a36Sopenharmony_ci out_le32(mbase + PCI_BASE_ADDRESS_1, RES_TO_U32_HIGH(res->start)); 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci /* Enable inbound mapping */ 190262306a36Sopenharmony_ci out_le32(mbase + PECFG_PIMEN, 0x1); 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci /* Enable I/O, Mem, and Busmaster cycles */ 190562306a36Sopenharmony_ci out_le16(mbase + PCI_COMMAND, 190662306a36Sopenharmony_ci in_le16(mbase + PCI_COMMAND) | 190762306a36Sopenharmony_ci PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); 190862306a36Sopenharmony_ci} 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_cistatic void __init ppc4xx_pciex_port_setup_hose(struct ppc4xx_pciex_port *port) 191162306a36Sopenharmony_ci{ 191262306a36Sopenharmony_ci struct resource dma_window; 191362306a36Sopenharmony_ci struct pci_controller *hose = NULL; 191462306a36Sopenharmony_ci const int *bus_range; 191562306a36Sopenharmony_ci int primary, busses; 191662306a36Sopenharmony_ci void __iomem *mbase = NULL, *cfg_data = NULL; 191762306a36Sopenharmony_ci const u32 *pval; 191862306a36Sopenharmony_ci u32 val; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci /* Check if primary bridge */ 192162306a36Sopenharmony_ci primary = of_property_read_bool(port->node, "primary"); 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci /* Get bus range if any */ 192462306a36Sopenharmony_ci bus_range = of_get_property(port->node, "bus-range", NULL); 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Allocate the host controller data structure */ 192762306a36Sopenharmony_ci hose = pcibios_alloc_controller(port->node); 192862306a36Sopenharmony_ci if (!hose) 192962306a36Sopenharmony_ci goto fail; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci /* We stick the port number in "indirect_type" so the config space 193262306a36Sopenharmony_ci * ops can retrieve the port data structure easily 193362306a36Sopenharmony_ci */ 193462306a36Sopenharmony_ci hose->indirect_type = port->index; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci /* Get bus range */ 193762306a36Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0x0; 193862306a36Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 193962306a36Sopenharmony_ci 194062306a36Sopenharmony_ci /* Because of how big mapping the config space is (1M per bus), we 194162306a36Sopenharmony_ci * limit how many busses we support. In the long run, we could replace 194262306a36Sopenharmony_ci * that with something akin to kmap_atomic instead. We set aside 1 bus 194362306a36Sopenharmony_ci * for the host itself too. 194462306a36Sopenharmony_ci */ 194562306a36Sopenharmony_ci busses = hose->last_busno - hose->first_busno; /* This is off by 1 */ 194662306a36Sopenharmony_ci if (busses > MAX_PCIE_BUS_MAPPED) { 194762306a36Sopenharmony_ci busses = MAX_PCIE_BUS_MAPPED; 194862306a36Sopenharmony_ci hose->last_busno = hose->first_busno + busses; 194962306a36Sopenharmony_ci } 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci if (!port->endpoint) { 195262306a36Sopenharmony_ci /* Only map the external config space in cfg_data for 195362306a36Sopenharmony_ci * PCIe root-complexes. External space is 1M per bus 195462306a36Sopenharmony_ci */ 195562306a36Sopenharmony_ci cfg_data = ioremap(port->cfg_space.start + 195662306a36Sopenharmony_ci (hose->first_busno + 1) * 0x100000, 195762306a36Sopenharmony_ci busses * 0x100000); 195862306a36Sopenharmony_ci if (cfg_data == NULL) { 195962306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map external config space !", 196062306a36Sopenharmony_ci port->node); 196162306a36Sopenharmony_ci goto fail; 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci hose->cfg_data = cfg_data; 196462306a36Sopenharmony_ci } 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci /* Always map the host config space in cfg_addr. 196762306a36Sopenharmony_ci * Internal space is 4K 196862306a36Sopenharmony_ci */ 196962306a36Sopenharmony_ci mbase = ioremap(port->cfg_space.start + 0x10000000, 0x1000); 197062306a36Sopenharmony_ci if (mbase == NULL) { 197162306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't map internal config space !", 197262306a36Sopenharmony_ci port->node); 197362306a36Sopenharmony_ci goto fail; 197462306a36Sopenharmony_ci } 197562306a36Sopenharmony_ci hose->cfg_addr = mbase; 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_ci pr_debug("PCIE %pOF, bus %d..%d\n", port->node, 197862306a36Sopenharmony_ci hose->first_busno, hose->last_busno); 197962306a36Sopenharmony_ci pr_debug(" config space mapped at: root @0x%p, other @0x%p\n", 198062306a36Sopenharmony_ci hose->cfg_addr, hose->cfg_data); 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci /* Setup config space */ 198362306a36Sopenharmony_ci hose->ops = &ppc4xx_pciex_pci_ops; 198462306a36Sopenharmony_ci port->hose = hose; 198562306a36Sopenharmony_ci mbase = (void __iomem *)hose->cfg_addr; 198662306a36Sopenharmony_ci 198762306a36Sopenharmony_ci if (!port->endpoint) { 198862306a36Sopenharmony_ci /* 198962306a36Sopenharmony_ci * Set bus numbers on our root port 199062306a36Sopenharmony_ci */ 199162306a36Sopenharmony_ci out_8(mbase + PCI_PRIMARY_BUS, hose->first_busno); 199262306a36Sopenharmony_ci out_8(mbase + PCI_SECONDARY_BUS, hose->first_busno + 1); 199362306a36Sopenharmony_ci out_8(mbase + PCI_SUBORDINATE_BUS, hose->last_busno); 199462306a36Sopenharmony_ci } 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci /* 199762306a36Sopenharmony_ci * OMRs are already reset, also disable PIMs 199862306a36Sopenharmony_ci */ 199962306a36Sopenharmony_ci out_le32(mbase + PECFG_PIMEN, 0); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci /* Parse outbound mapping resources */ 200262306a36Sopenharmony_ci pci_process_bridge_OF_ranges(hose, port->node, primary); 200362306a36Sopenharmony_ci 200462306a36Sopenharmony_ci /* Parse inbound mapping resources */ 200562306a36Sopenharmony_ci if (ppc4xx_parse_dma_ranges(hose, mbase, &dma_window) != 0) 200662306a36Sopenharmony_ci goto fail; 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_ci /* Configure outbound ranges POMs */ 200962306a36Sopenharmony_ci ppc4xx_configure_pciex_POMs(port, hose, mbase); 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci /* Configure inbound ranges PIMs */ 201262306a36Sopenharmony_ci ppc4xx_configure_pciex_PIMs(port, hose, mbase, &dma_window); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci /* The root complex doesn't show up if we don't set some vendor 201562306a36Sopenharmony_ci * and device IDs into it. The defaults below are the same bogus 201662306a36Sopenharmony_ci * one that the initial code in arch/ppc had. This can be 201762306a36Sopenharmony_ci * overwritten by setting the "vendor-id/device-id" properties 201862306a36Sopenharmony_ci * in the pciex node. 201962306a36Sopenharmony_ci */ 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci /* Get the (optional) vendor-/device-id from the device-tree */ 202262306a36Sopenharmony_ci pval = of_get_property(port->node, "vendor-id", NULL); 202362306a36Sopenharmony_ci if (pval) { 202462306a36Sopenharmony_ci val = *pval; 202562306a36Sopenharmony_ci } else { 202662306a36Sopenharmony_ci if (!port->endpoint) 202762306a36Sopenharmony_ci val = 0xaaa0 + port->index; 202862306a36Sopenharmony_ci else 202962306a36Sopenharmony_ci val = 0xeee0 + port->index; 203062306a36Sopenharmony_ci } 203162306a36Sopenharmony_ci out_le16(mbase + 0x200, val); 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci pval = of_get_property(port->node, "device-id", NULL); 203462306a36Sopenharmony_ci if (pval) { 203562306a36Sopenharmony_ci val = *pval; 203662306a36Sopenharmony_ci } else { 203762306a36Sopenharmony_ci if (!port->endpoint) 203862306a36Sopenharmony_ci val = 0xbed0 + port->index; 203962306a36Sopenharmony_ci else 204062306a36Sopenharmony_ci val = 0xfed0 + port->index; 204162306a36Sopenharmony_ci } 204262306a36Sopenharmony_ci out_le16(mbase + 0x202, val); 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci /* Enable Bus master, memory, and io space */ 204562306a36Sopenharmony_ci if (of_device_is_compatible(port->node, "ibm,plb-pciex-460sx")) 204662306a36Sopenharmony_ci out_le16(mbase + 0x204, 0x7); 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci if (!port->endpoint) { 204962306a36Sopenharmony_ci /* Set Class Code to PCI-PCI bridge and Revision Id to 1 */ 205062306a36Sopenharmony_ci out_le32(mbase + 0x208, 0x06040001); 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: successfully set as root-complex\n", 205362306a36Sopenharmony_ci port->index); 205462306a36Sopenharmony_ci } else { 205562306a36Sopenharmony_ci /* Set Class Code to Processor/PPC */ 205662306a36Sopenharmony_ci out_le32(mbase + 0x208, 0x0b200001); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: successfully set as endpoint\n", 205962306a36Sopenharmony_ci port->index); 206062306a36Sopenharmony_ci } 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci return; 206362306a36Sopenharmony_ci fail: 206462306a36Sopenharmony_ci if (hose) 206562306a36Sopenharmony_ci pcibios_free_controller(hose); 206662306a36Sopenharmony_ci if (cfg_data) 206762306a36Sopenharmony_ci iounmap(cfg_data); 206862306a36Sopenharmony_ci if (mbase) 206962306a36Sopenharmony_ci iounmap(mbase); 207062306a36Sopenharmony_ci} 207162306a36Sopenharmony_ci 207262306a36Sopenharmony_cistatic void __init ppc4xx_probe_pciex_bridge(struct device_node *np) 207362306a36Sopenharmony_ci{ 207462306a36Sopenharmony_ci struct ppc4xx_pciex_port *port; 207562306a36Sopenharmony_ci const u32 *pval; 207662306a36Sopenharmony_ci int portno; 207762306a36Sopenharmony_ci unsigned int dcrs; 207862306a36Sopenharmony_ci 207962306a36Sopenharmony_ci /* First, proceed to core initialization as we assume there's 208062306a36Sopenharmony_ci * only one PCIe core in the system 208162306a36Sopenharmony_ci */ 208262306a36Sopenharmony_ci if (ppc4xx_pciex_check_core_init(np)) 208362306a36Sopenharmony_ci return; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci /* Get the port number from the device-tree */ 208662306a36Sopenharmony_ci pval = of_get_property(np, "port", NULL); 208762306a36Sopenharmony_ci if (pval == NULL) { 208862306a36Sopenharmony_ci printk(KERN_ERR "PCIE: Can't find port number for %pOF\n", np); 208962306a36Sopenharmony_ci return; 209062306a36Sopenharmony_ci } 209162306a36Sopenharmony_ci portno = *pval; 209262306a36Sopenharmony_ci if (portno >= ppc4xx_pciex_port_count) { 209362306a36Sopenharmony_ci printk(KERN_ERR "PCIE: port number out of range for %pOF\n", 209462306a36Sopenharmony_ci np); 209562306a36Sopenharmony_ci return; 209662306a36Sopenharmony_ci } 209762306a36Sopenharmony_ci port = &ppc4xx_pciex_ports[portno]; 209862306a36Sopenharmony_ci port->index = portno; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* 210162306a36Sopenharmony_ci * Check if device is enabled 210262306a36Sopenharmony_ci */ 210362306a36Sopenharmony_ci if (!of_device_is_available(np)) { 210462306a36Sopenharmony_ci printk(KERN_INFO "PCIE%d: Port disabled via device-tree\n", port->index); 210562306a36Sopenharmony_ci return; 210662306a36Sopenharmony_ci } 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci port->node = of_node_get(np); 210962306a36Sopenharmony_ci if (ppc4xx_pciex_hwops->want_sdr) { 211062306a36Sopenharmony_ci pval = of_get_property(np, "sdr-base", NULL); 211162306a36Sopenharmony_ci if (pval == NULL) { 211262306a36Sopenharmony_ci printk(KERN_ERR "PCIE: missing sdr-base for %pOF\n", 211362306a36Sopenharmony_ci np); 211462306a36Sopenharmony_ci return; 211562306a36Sopenharmony_ci } 211662306a36Sopenharmony_ci port->sdr_base = *pval; 211762306a36Sopenharmony_ci } 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci /* Check if device_type property is set to "pci" or "pci-endpoint". 212062306a36Sopenharmony_ci * Resulting from this setup this PCIe port will be configured 212162306a36Sopenharmony_ci * as root-complex or as endpoint. 212262306a36Sopenharmony_ci */ 212362306a36Sopenharmony_ci if (of_node_is_type(port->node, "pci-endpoint")) { 212462306a36Sopenharmony_ci port->endpoint = 1; 212562306a36Sopenharmony_ci } else if (of_node_is_type(port->node, "pci")) { 212662306a36Sopenharmony_ci port->endpoint = 0; 212762306a36Sopenharmony_ci } else { 212862306a36Sopenharmony_ci printk(KERN_ERR "PCIE: missing or incorrect device_type for %pOF\n", 212962306a36Sopenharmony_ci np); 213062306a36Sopenharmony_ci return; 213162306a36Sopenharmony_ci } 213262306a36Sopenharmony_ci 213362306a36Sopenharmony_ci /* Fetch config space registers address */ 213462306a36Sopenharmony_ci if (of_address_to_resource(np, 0, &port->cfg_space)) { 213562306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get PCI-E config space !", np); 213662306a36Sopenharmony_ci return; 213762306a36Sopenharmony_ci } 213862306a36Sopenharmony_ci /* Fetch host bridge internal registers address */ 213962306a36Sopenharmony_ci if (of_address_to_resource(np, 1, &port->utl_regs)) { 214062306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get UTL register base !", np); 214162306a36Sopenharmony_ci return; 214262306a36Sopenharmony_ci } 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci /* Map DCRs */ 214562306a36Sopenharmony_ci dcrs = dcr_resource_start(np, 0); 214662306a36Sopenharmony_ci if (dcrs == 0) { 214762306a36Sopenharmony_ci printk(KERN_ERR "%pOF: Can't get DCR register base !", np); 214862306a36Sopenharmony_ci return; 214962306a36Sopenharmony_ci } 215062306a36Sopenharmony_ci port->dcrs = dcr_map(np, dcrs, dcr_resource_len(np, 0)); 215162306a36Sopenharmony_ci 215262306a36Sopenharmony_ci /* Initialize the port specific registers */ 215362306a36Sopenharmony_ci if (ppc4xx_pciex_port_init(port)) { 215462306a36Sopenharmony_ci printk(KERN_WARNING "PCIE%d: Port init failed\n", port->index); 215562306a36Sopenharmony_ci return; 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci /* Setup the linux hose data structure */ 215962306a36Sopenharmony_ci ppc4xx_pciex_port_setup_hose(port); 216062306a36Sopenharmony_ci} 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci#endif /* CONFIG_PPC4xx_PCI_EXPRESS */ 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_cistatic int __init ppc4xx_pci_find_bridges(void) 216562306a36Sopenharmony_ci{ 216662306a36Sopenharmony_ci struct device_node *np; 216762306a36Sopenharmony_ci 216862306a36Sopenharmony_ci pci_add_flags(PCI_ENABLE_PROC_DOMAINS | PCI_COMPAT_DOMAIN_0); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci#ifdef CONFIG_PPC4xx_PCI_EXPRESS 217162306a36Sopenharmony_ci for_each_compatible_node(np, NULL, "ibm,plb-pciex") 217262306a36Sopenharmony_ci ppc4xx_probe_pciex_bridge(np); 217362306a36Sopenharmony_ci#endif 217462306a36Sopenharmony_ci for_each_compatible_node(np, NULL, "ibm,plb-pcix") 217562306a36Sopenharmony_ci ppc4xx_probe_pcix_bridge(np); 217662306a36Sopenharmony_ci for_each_compatible_node(np, NULL, "ibm,plb-pci") 217762306a36Sopenharmony_ci ppc4xx_probe_pci_bridge(np); 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci return 0; 218062306a36Sopenharmony_ci} 218162306a36Sopenharmony_ciarch_initcall(ppc4xx_pci_find_bridges); 218262306a36Sopenharmony_ci 2183