18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * MPC83xx/85xx/86xx PCI/PCIE support routing. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2007-2012 Freescale Semiconductor, Inc. 68c2ecf20Sopenharmony_ci * Copyright 2008-2009 MontaVista Software, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Initial author: Xianghua Xiao <x.xiao@freescale.com> 98c2ecf20Sopenharmony_ci * Recode: ZHANG WEI <wei.zhang@freescale.com> 108c2ecf20Sopenharmony_ci * Rewrite the routing for Frescale PCI and PCI Express 118c2ecf20Sopenharmony_ci * Roy Zang <tie-fei.zang@freescale.com> 128c2ecf20Sopenharmony_ci * MPC83xx PCI-Express support: 138c2ecf20Sopenharmony_ci * Tony Li <tony.li@freescale.com> 148c2ecf20Sopenharmony_ci * Anton Vorontsov <avorontsov@ru.mvista.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci#include <linux/kernel.h> 178c2ecf20Sopenharmony_ci#include <linux/pci.h> 188c2ecf20Sopenharmony_ci#include <linux/delay.h> 198c2ecf20Sopenharmony_ci#include <linux/string.h> 208c2ecf20Sopenharmony_ci#include <linux/fsl/edac.h> 218c2ecf20Sopenharmony_ci#include <linux/init.h> 228c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 238c2ecf20Sopenharmony_ci#include <linux/memblock.h> 248c2ecf20Sopenharmony_ci#include <linux/log2.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci#include <linux/slab.h> 278c2ecf20Sopenharmony_ci#include <linux/suspend.h> 288c2ecf20Sopenharmony_ci#include <linux/syscore_ops.h> 298c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#include <asm/io.h> 328c2ecf20Sopenharmony_ci#include <asm/prom.h> 338c2ecf20Sopenharmony_ci#include <asm/pci-bridge.h> 348c2ecf20Sopenharmony_ci#include <asm/ppc-pci.h> 358c2ecf20Sopenharmony_ci#include <asm/machdep.h> 368c2ecf20Sopenharmony_ci#include <asm/mpc85xx.h> 378c2ecf20Sopenharmony_ci#include <asm/disassemble.h> 388c2ecf20Sopenharmony_ci#include <asm/ppc-opcode.h> 398c2ecf20Sopenharmony_ci#include <asm/swiotlb.h> 408c2ecf20Sopenharmony_ci#include <sysdev/fsl_soc.h> 418c2ecf20Sopenharmony_ci#include <sysdev/fsl_pci.h> 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic int fsl_pcie_bus_fixup, is_mpc83xx_pci; 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic void quirk_fsl_pcie_early(struct pci_dev *dev) 468c2ecf20Sopenharmony_ci{ 478c2ecf20Sopenharmony_ci u8 hdr_type; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci /* if we aren't a PCIe don't bother */ 508c2ecf20Sopenharmony_ci if (!pci_is_pcie(dev)) 518c2ecf20Sopenharmony_ci return; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci /* if we aren't in host mode don't bother */ 548c2ecf20Sopenharmony_ci pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type); 558c2ecf20Sopenharmony_ci if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) 568c2ecf20Sopenharmony_ci return; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci dev->class = PCI_CLASS_BRIDGE_PCI << 8; 598c2ecf20Sopenharmony_ci fsl_pcie_bus_fixup = 1; 608c2ecf20Sopenharmony_ci return; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic int fsl_indirect_read_config(struct pci_bus *, unsigned int, 648c2ecf20Sopenharmony_ci int, int, u32 *); 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic int fsl_pcie_check_link(struct pci_controller *hose) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci u32 val = 0; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_FSL_CFG_REG_LINK) { 718c2ecf20Sopenharmony_ci if (hose->ops->read == fsl_indirect_read_config) 728c2ecf20Sopenharmony_ci __indirect_read_config(hose, hose->first_busno, 0, 738c2ecf20Sopenharmony_ci PCIE_LTSSM, 4, &val); 748c2ecf20Sopenharmony_ci else 758c2ecf20Sopenharmony_ci early_read_config_dword(hose, 0, 0, PCIE_LTSSM, &val); 768c2ecf20Sopenharmony_ci if (val < PCIE_LTSSM_L0) 778c2ecf20Sopenharmony_ci return 1; 788c2ecf20Sopenharmony_ci } else { 798c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci = hose->private_data; 808c2ecf20Sopenharmony_ci /* for PCIe IP rev 3.0 or greater use CSR0 for link state */ 818c2ecf20Sopenharmony_ci val = (in_be32(&pci->pex_csr0) & PEX_CSR0_LTSSM_MASK) 828c2ecf20Sopenharmony_ci >> PEX_CSR0_LTSSM_SHIFT; 838c2ecf20Sopenharmony_ci if (val != PEX_CSR0_LTSSM_L0) 848c2ecf20Sopenharmony_ci return 1; 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci return 0; 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic int fsl_indirect_read_config(struct pci_bus *bus, unsigned int devfn, 918c2ecf20Sopenharmony_ci int offset, int len, u32 *val) 928c2ecf20Sopenharmony_ci{ 938c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if (fsl_pcie_check_link(hose)) 968c2ecf20Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK; 978c2ecf20Sopenharmony_ci else 988c2ecf20Sopenharmony_ci hose->indirect_type &= ~PPC_INDIRECT_TYPE_NO_PCIE_LINK; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return indirect_read_config(bus, devfn, offset, len, val); 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_cistatic struct pci_ops fsl_indirect_pcie_ops = 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci .read = fsl_indirect_read_config, 1088c2ecf20Sopenharmony_ci .write = indirect_write_config, 1098c2ecf20Sopenharmony_ci}; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic u64 pci64_dma_offset; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci#ifdef CONFIG_SWIOTLB 1148c2ecf20Sopenharmony_cistatic void pci_dma_dev_setup_swiotlb(struct pci_dev *pdev) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(pdev->bus); 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci pdev->dev.bus_dma_limit = 1198c2ecf20Sopenharmony_ci hose->dma_window_base_cur + hose->dma_window_size - 1; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic void setup_swiotlb_ops(struct pci_controller *hose) 1238c2ecf20Sopenharmony_ci{ 1248c2ecf20Sopenharmony_ci if (ppc_swiotlb_enable) 1258c2ecf20Sopenharmony_ci hose->controller_ops.dma_dev_setup = pci_dma_dev_setup_swiotlb; 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci#else 1288c2ecf20Sopenharmony_cistatic inline void setup_swiotlb_ops(struct pci_controller *hose) {} 1298c2ecf20Sopenharmony_ci#endif 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_cistatic void fsl_pci_dma_set_mask(struct device *dev, u64 dma_mask) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci /* 1348c2ecf20Sopenharmony_ci * Fix up PCI devices that are able to DMA to the large inbound 1358c2ecf20Sopenharmony_ci * mapping that allows addressing any RAM address from across PCI. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_ci if (dev_is_pci(dev) && dma_mask >= pci64_dma_offset * 2 - 1) { 1388c2ecf20Sopenharmony_ci dev->bus_dma_limit = 0; 1398c2ecf20Sopenharmony_ci dev->archdata.dma_offset = pci64_dma_offset; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int setup_one_atmu(struct ccsr_pci __iomem *pci, 1448c2ecf20Sopenharmony_ci unsigned int index, const struct resource *res, 1458c2ecf20Sopenharmony_ci resource_size_t offset) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci resource_size_t pci_addr = res->start - offset; 1488c2ecf20Sopenharmony_ci resource_size_t phys_addr = res->start; 1498c2ecf20Sopenharmony_ci resource_size_t size = resource_size(res); 1508c2ecf20Sopenharmony_ci u32 flags = 0x80044000; /* enable & mem R/W */ 1518c2ecf20Sopenharmony_ci unsigned int i; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci pr_debug("PCI MEM resource start 0x%016llx, size 0x%016llx.\n", 1548c2ecf20Sopenharmony_ci (u64)res->start, (u64)size); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (res->flags & IORESOURCE_PREFETCH) 1578c2ecf20Sopenharmony_ci flags |= 0x10000000; /* enable relaxed ordering */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci for (i = 0; size > 0; i++) { 1608c2ecf20Sopenharmony_ci unsigned int bits = min_t(u32, ilog2(size), 1618c2ecf20Sopenharmony_ci __ffs(pci_addr | phys_addr)); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci if (index + i >= 5) 1648c2ecf20Sopenharmony_ci return -1; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci out_be32(&pci->pow[index + i].potar, pci_addr >> 12); 1678c2ecf20Sopenharmony_ci out_be32(&pci->pow[index + i].potear, (u64)pci_addr >> 44); 1688c2ecf20Sopenharmony_ci out_be32(&pci->pow[index + i].powbar, phys_addr >> 12); 1698c2ecf20Sopenharmony_ci out_be32(&pci->pow[index + i].powar, flags | (bits - 1)); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci pci_addr += (resource_size_t)1U << bits; 1728c2ecf20Sopenharmony_ci phys_addr += (resource_size_t)1U << bits; 1738c2ecf20Sopenharmony_ci size -= (resource_size_t)1U << bits; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return i; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic bool is_kdump(void) 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci struct device_node *node; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci node = of_find_node_by_type(NULL, "memory"); 1848c2ecf20Sopenharmony_ci if (!node) { 1858c2ecf20Sopenharmony_ci WARN_ON_ONCE(1); 1868c2ecf20Sopenharmony_ci return false; 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci return of_property_read_bool(node, "linux,usable-memory"); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci/* atmu setup for fsl pci/pcie controller */ 1938c2ecf20Sopenharmony_cistatic void setup_pci_atmu(struct pci_controller *hose) 1948c2ecf20Sopenharmony_ci{ 1958c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci = hose->private_data; 1968c2ecf20Sopenharmony_ci int i, j, n, mem_log, win_idx = 3, start_idx = 1, end_idx = 4; 1978c2ecf20Sopenharmony_ci u64 mem, sz, paddr_hi = 0; 1988c2ecf20Sopenharmony_ci u64 offset = 0, paddr_lo = ULLONG_MAX; 1998c2ecf20Sopenharmony_ci u32 pcicsrbar = 0, pcicsrbar_sz; 2008c2ecf20Sopenharmony_ci u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL | 2018c2ecf20Sopenharmony_ci PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; 2028c2ecf20Sopenharmony_ci const u64 *reg; 2038c2ecf20Sopenharmony_ci int len; 2048c2ecf20Sopenharmony_ci bool setup_inbound; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* 2078c2ecf20Sopenharmony_ci * If this is kdump, we don't want to trigger a bunch of PCI 2088c2ecf20Sopenharmony_ci * errors by closing the window on in-flight DMA. 2098c2ecf20Sopenharmony_ci * 2108c2ecf20Sopenharmony_ci * We still run most of the function's logic so that things like 2118c2ecf20Sopenharmony_ci * hose->dma_window_size still get set. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci setup_inbound = !is_kdump(); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci if (of_device_is_compatible(hose->dn, "fsl,bsc9132-pcie")) { 2168c2ecf20Sopenharmony_ci /* 2178c2ecf20Sopenharmony_ci * BSC9132 Rev1.0 has an issue where all the PEX inbound 2188c2ecf20Sopenharmony_ci * windows have implemented the default target value as 0xf 2198c2ecf20Sopenharmony_ci * for CCSR space.In all Freescale legacy devices the target 2208c2ecf20Sopenharmony_ci * of 0xf is reserved for local memory space. 9132 Rev1.0 2218c2ecf20Sopenharmony_ci * now has local mempry space mapped to target 0x0 instead of 2228c2ecf20Sopenharmony_ci * 0xf. Hence adding a workaround to remove the target 0xf 2238c2ecf20Sopenharmony_ci * defined for memory space from Inbound window attributes. 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci piwar &= ~PIWAR_TGI_LOCAL; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) { 2298c2ecf20Sopenharmony_ci if (in_be32(&pci->block_rev1) >= PCIE_IP_REV_2_2) { 2308c2ecf20Sopenharmony_ci win_idx = 2; 2318c2ecf20Sopenharmony_ci start_idx = 0; 2328c2ecf20Sopenharmony_ci end_idx = 3; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci } 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* Disable all windows (except powar0 since it's ignored) */ 2378c2ecf20Sopenharmony_ci for(i = 1; i < 5; i++) 2388c2ecf20Sopenharmony_ci out_be32(&pci->pow[i].powar, 0); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if (setup_inbound) { 2418c2ecf20Sopenharmony_ci for (i = start_idx; i < end_idx; i++) 2428c2ecf20Sopenharmony_ci out_be32(&pci->piw[i].piwar, 0); 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Setup outbound MEM window */ 2468c2ecf20Sopenharmony_ci for(i = 0, j = 1; i < 3; i++) { 2478c2ecf20Sopenharmony_ci if (!(hose->mem_resources[i].flags & IORESOURCE_MEM)) 2488c2ecf20Sopenharmony_ci continue; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci paddr_lo = min(paddr_lo, (u64)hose->mem_resources[i].start); 2518c2ecf20Sopenharmony_ci paddr_hi = max(paddr_hi, (u64)hose->mem_resources[i].end); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci /* We assume all memory resources have the same offset */ 2548c2ecf20Sopenharmony_ci offset = hose->mem_offset[i]; 2558c2ecf20Sopenharmony_ci n = setup_one_atmu(pci, j, &hose->mem_resources[i], offset); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci if (n < 0 || j >= 5) { 2588c2ecf20Sopenharmony_ci pr_err("Ran out of outbound PCI ATMUs for resource %d!\n", i); 2598c2ecf20Sopenharmony_ci hose->mem_resources[i].flags |= IORESOURCE_DISABLED; 2608c2ecf20Sopenharmony_ci } else 2618c2ecf20Sopenharmony_ci j += n; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* Setup outbound IO window */ 2658c2ecf20Sopenharmony_ci if (hose->io_resource.flags & IORESOURCE_IO) { 2668c2ecf20Sopenharmony_ci if (j >= 5) { 2678c2ecf20Sopenharmony_ci pr_err("Ran out of outbound PCI ATMUs for IO resource\n"); 2688c2ecf20Sopenharmony_ci } else { 2698c2ecf20Sopenharmony_ci pr_debug("PCI IO resource start 0x%016llx, size 0x%016llx, " 2708c2ecf20Sopenharmony_ci "phy base 0x%016llx.\n", 2718c2ecf20Sopenharmony_ci (u64)hose->io_resource.start, 2728c2ecf20Sopenharmony_ci (u64)resource_size(&hose->io_resource), 2738c2ecf20Sopenharmony_ci (u64)hose->io_base_phys); 2748c2ecf20Sopenharmony_ci out_be32(&pci->pow[j].potar, (hose->io_resource.start >> 12)); 2758c2ecf20Sopenharmony_ci out_be32(&pci->pow[j].potear, 0); 2768c2ecf20Sopenharmony_ci out_be32(&pci->pow[j].powbar, (hose->io_base_phys >> 12)); 2778c2ecf20Sopenharmony_ci /* Enable, IO R/W */ 2788c2ecf20Sopenharmony_ci out_be32(&pci->pow[j].powar, 0x80088000 2798c2ecf20Sopenharmony_ci | (ilog2(hose->io_resource.end 2808c2ecf20Sopenharmony_ci - hose->io_resource.start + 1) - 1)); 2818c2ecf20Sopenharmony_ci } 2828c2ecf20Sopenharmony_ci } 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci /* convert to pci address space */ 2858c2ecf20Sopenharmony_ci paddr_hi -= offset; 2868c2ecf20Sopenharmony_ci paddr_lo -= offset; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci if (paddr_hi == paddr_lo) { 2898c2ecf20Sopenharmony_ci pr_err("%pOF: No outbound window space\n", hose->dn); 2908c2ecf20Sopenharmony_ci return; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (paddr_lo == 0) { 2948c2ecf20Sopenharmony_ci pr_err("%pOF: No space for inbound window\n", hose->dn); 2958c2ecf20Sopenharmony_ci return; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* setup PCSRBAR/PEXCSRBAR */ 2998c2ecf20Sopenharmony_ci early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, 0xffffffff); 3008c2ecf20Sopenharmony_ci early_read_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, &pcicsrbar_sz); 3018c2ecf20Sopenharmony_ci pcicsrbar_sz = ~pcicsrbar_sz + 1; 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci if (paddr_hi < (0x100000000ull - pcicsrbar_sz) || 3048c2ecf20Sopenharmony_ci (paddr_lo > 0x100000000ull)) 3058c2ecf20Sopenharmony_ci pcicsrbar = 0x100000000ull - pcicsrbar_sz; 3068c2ecf20Sopenharmony_ci else 3078c2ecf20Sopenharmony_ci pcicsrbar = (paddr_lo - pcicsrbar_sz) & -pcicsrbar_sz; 3088c2ecf20Sopenharmony_ci early_write_config_dword(hose, 0, 0, PCI_BASE_ADDRESS_0, pcicsrbar); 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci paddr_lo = min(paddr_lo, (u64)pcicsrbar); 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci pr_info("%pOF: PCICSRBAR @ 0x%x\n", hose->dn, pcicsrbar); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* Setup inbound mem window */ 3158c2ecf20Sopenharmony_ci mem = memblock_end_of_DRAM(); 3168c2ecf20Sopenharmony_ci pr_info("%s: end of DRAM %llx\n", __func__, mem); 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* 3198c2ecf20Sopenharmony_ci * The msi-address-64 property, if it exists, indicates the physical 3208c2ecf20Sopenharmony_ci * address of the MSIIR register. Normally, this register is located 3218c2ecf20Sopenharmony_ci * inside CCSR, so the ATMU that covers all of CCSR is used. But if 3228c2ecf20Sopenharmony_ci * this property exists, then we normally need to create a new ATMU 3238c2ecf20Sopenharmony_ci * for it. For now, however, we cheat. The only entity that creates 3248c2ecf20Sopenharmony_ci * this property is the Freescale hypervisor, and the address is 3258c2ecf20Sopenharmony_ci * specified in the partition configuration. Typically, the address 3268c2ecf20Sopenharmony_ci * is located in the page immediately after the end of DDR. If so, we 3278c2ecf20Sopenharmony_ci * can avoid allocating a new ATMU by extending the DDR ATMU by one 3288c2ecf20Sopenharmony_ci * page. 3298c2ecf20Sopenharmony_ci */ 3308c2ecf20Sopenharmony_ci reg = of_get_property(hose->dn, "msi-address-64", &len); 3318c2ecf20Sopenharmony_ci if (reg && (len == sizeof(u64))) { 3328c2ecf20Sopenharmony_ci u64 address = be64_to_cpup(reg); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if ((address >= mem) && (address < (mem + PAGE_SIZE))) { 3358c2ecf20Sopenharmony_ci pr_info("%pOF: extending DDR ATMU to cover MSIIR", hose->dn); 3368c2ecf20Sopenharmony_ci mem += PAGE_SIZE; 3378c2ecf20Sopenharmony_ci } else { 3388c2ecf20Sopenharmony_ci /* TODO: Create a new ATMU for MSIIR */ 3398c2ecf20Sopenharmony_ci pr_warn("%pOF: msi-address-64 address of %llx is " 3408c2ecf20Sopenharmony_ci "unsupported\n", hose->dn, address); 3418c2ecf20Sopenharmony_ci } 3428c2ecf20Sopenharmony_ci } 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci sz = min(mem, paddr_lo); 3458c2ecf20Sopenharmony_ci mem_log = ilog2(sz); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci /* PCIe can overmap inbound & outbound since RX & TX are separated */ 3488c2ecf20Sopenharmony_ci if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) { 3498c2ecf20Sopenharmony_ci /* Size window to exact size if power-of-two or one size up */ 3508c2ecf20Sopenharmony_ci if ((1ull << mem_log) != mem) { 3518c2ecf20Sopenharmony_ci mem_log++; 3528c2ecf20Sopenharmony_ci if ((1ull << mem_log) > mem) 3538c2ecf20Sopenharmony_ci pr_info("%pOF: Setting PCI inbound window " 3548c2ecf20Sopenharmony_ci "greater than memory size\n", hose->dn); 3558c2ecf20Sopenharmony_ci } 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci piwar |= ((mem_log - 1) & PIWAR_SZ_MASK); 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci if (setup_inbound) { 3608c2ecf20Sopenharmony_ci /* Setup inbound memory window */ 3618c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].pitar, 0x00000000); 3628c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwbar, 0x00000000); 3638c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwar, piwar); 3648c2ecf20Sopenharmony_ci } 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci win_idx--; 3678c2ecf20Sopenharmony_ci hose->dma_window_base_cur = 0x00000000; 3688c2ecf20Sopenharmony_ci hose->dma_window_size = (resource_size_t)sz; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci /* 3718c2ecf20Sopenharmony_ci * if we have >4G of memory setup second PCI inbound window to 3728c2ecf20Sopenharmony_ci * let devices that are 64-bit address capable to work w/o 3738c2ecf20Sopenharmony_ci * SWIOTLB and access the full range of memory 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_ci if (sz != mem) { 3768c2ecf20Sopenharmony_ci mem_log = ilog2(mem); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Size window up if we dont fit in exact power-of-2 */ 3798c2ecf20Sopenharmony_ci if ((1ull << mem_log) != mem) 3808c2ecf20Sopenharmony_ci mem_log++; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci piwar = (piwar & ~PIWAR_SZ_MASK) | (mem_log - 1); 3838c2ecf20Sopenharmony_ci pci64_dma_offset = 1ULL << mem_log; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci if (setup_inbound) { 3868c2ecf20Sopenharmony_ci /* Setup inbound memory window */ 3878c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].pitar, 0x00000000); 3888c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwbear, 3898c2ecf20Sopenharmony_ci pci64_dma_offset >> 44); 3908c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwbar, 3918c2ecf20Sopenharmony_ci pci64_dma_offset >> 12); 3928c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwar, piwar); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * install our own dma_set_mask handler to fixup dma_ops 3978c2ecf20Sopenharmony_ci * and dma_offset 3988c2ecf20Sopenharmony_ci */ 3998c2ecf20Sopenharmony_ci ppc_md.dma_set_mask = fsl_pci_dma_set_mask; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci pr_info("%pOF: Setup 64-bit PCI DMA window\n", hose->dn); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci } else { 4048c2ecf20Sopenharmony_ci u64 paddr = 0; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (setup_inbound) { 4078c2ecf20Sopenharmony_ci /* Setup inbound memory window */ 4088c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].pitar, paddr >> 12); 4098c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwbar, paddr >> 12); 4108c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwar, 4118c2ecf20Sopenharmony_ci (piwar | (mem_log - 1))); 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci win_idx--; 4158c2ecf20Sopenharmony_ci paddr += 1ull << mem_log; 4168c2ecf20Sopenharmony_ci sz -= 1ull << mem_log; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (sz) { 4198c2ecf20Sopenharmony_ci mem_log = ilog2(sz); 4208c2ecf20Sopenharmony_ci piwar |= (mem_log - 1); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci if (setup_inbound) { 4238c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].pitar, 4248c2ecf20Sopenharmony_ci paddr >> 12); 4258c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwbar, 4268c2ecf20Sopenharmony_ci paddr >> 12); 4278c2ecf20Sopenharmony_ci out_be32(&pci->piw[win_idx].piwar, piwar); 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci win_idx--; 4318c2ecf20Sopenharmony_ci paddr += 1ull << mem_log; 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci hose->dma_window_base_cur = 0x00000000; 4358c2ecf20Sopenharmony_ci hose->dma_window_size = (resource_size_t)paddr; 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci if (hose->dma_window_size < mem) { 4398c2ecf20Sopenharmony_ci#ifdef CONFIG_SWIOTLB 4408c2ecf20Sopenharmony_ci ppc_swiotlb_enable = 1; 4418c2ecf20Sopenharmony_ci#else 4428c2ecf20Sopenharmony_ci pr_err("%pOF: ERROR: Memory size exceeds PCI ATMU ability to " 4438c2ecf20Sopenharmony_ci "map - enable CONFIG_SWIOTLB to avoid dma errors.\n", 4448c2ecf20Sopenharmony_ci hose->dn); 4458c2ecf20Sopenharmony_ci#endif 4468c2ecf20Sopenharmony_ci /* adjusting outbound windows could reclaim space in mem map */ 4478c2ecf20Sopenharmony_ci if (paddr_hi < 0xffffffffull) 4488c2ecf20Sopenharmony_ci pr_warn("%pOF: WARNING: Outbound window cfg leaves " 4498c2ecf20Sopenharmony_ci "gaps in memory map. Adjusting the memory map " 4508c2ecf20Sopenharmony_ci "could reduce unnecessary bounce buffering.\n", 4518c2ecf20Sopenharmony_ci hose->dn); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci pr_info("%pOF: DMA window size is 0x%llx\n", hose->dn, 4548c2ecf20Sopenharmony_ci (u64)hose->dma_window_size); 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci} 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_cistatic void __init setup_pci_cmd(struct pci_controller *hose) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci u16 cmd; 4618c2ecf20Sopenharmony_ci int cap_x; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci early_read_config_word(hose, 0, 0, PCI_COMMAND, &cmd); 4648c2ecf20Sopenharmony_ci cmd |= PCI_COMMAND_SERR | PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY 4658c2ecf20Sopenharmony_ci | PCI_COMMAND_IO; 4668c2ecf20Sopenharmony_ci early_write_config_word(hose, 0, 0, PCI_COMMAND, cmd); 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci cap_x = early_find_capability(hose, 0, 0, PCI_CAP_ID_PCIX); 4698c2ecf20Sopenharmony_ci if (cap_x) { 4708c2ecf20Sopenharmony_ci int pci_x_cmd = cap_x + PCI_X_CMD; 4718c2ecf20Sopenharmony_ci cmd = PCI_X_CMD_MAX_SPLIT | PCI_X_CMD_MAX_READ 4728c2ecf20Sopenharmony_ci | PCI_X_CMD_ERO | PCI_X_CMD_DPERR_E; 4738c2ecf20Sopenharmony_ci early_write_config_word(hose, 0, 0, pci_x_cmd, cmd); 4748c2ecf20Sopenharmony_ci } else { 4758c2ecf20Sopenharmony_ci early_write_config_byte(hose, 0, 0, PCI_LATENCY_TIMER, 0x80); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci} 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_civoid fsl_pcibios_fixup_bus(struct pci_bus *bus) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 4828c2ecf20Sopenharmony_ci int i, is_pcie = 0, no_link; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci /* The root complex bridge comes up with bogus resources, 4858c2ecf20Sopenharmony_ci * we copy the PHB ones in. 4868c2ecf20Sopenharmony_ci * 4878c2ecf20Sopenharmony_ci * With the current generic PCI code, the PHB bus no longer 4888c2ecf20Sopenharmony_ci * has bus->resource[0..4] set, so things are a bit more 4898c2ecf20Sopenharmony_ci * tricky. 4908c2ecf20Sopenharmony_ci */ 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (fsl_pcie_bus_fixup) 4938c2ecf20Sopenharmony_ci is_pcie = early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP); 4948c2ecf20Sopenharmony_ci no_link = !!(hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci if (bus->parent == hose->bus && (is_pcie || no_link)) { 4978c2ecf20Sopenharmony_ci for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; ++i) { 4988c2ecf20Sopenharmony_ci struct resource *res = bus->resource[i]; 4998c2ecf20Sopenharmony_ci struct resource *par; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci if (!res) 5028c2ecf20Sopenharmony_ci continue; 5038c2ecf20Sopenharmony_ci if (i == 0) 5048c2ecf20Sopenharmony_ci par = &hose->io_resource; 5058c2ecf20Sopenharmony_ci else if (i < 4) 5068c2ecf20Sopenharmony_ci par = &hose->mem_resources[i-1]; 5078c2ecf20Sopenharmony_ci else par = NULL; 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci res->start = par ? par->start : 0; 5108c2ecf20Sopenharmony_ci res->end = par ? par->end : 0; 5118c2ecf20Sopenharmony_ci res->flags = par ? par->flags : 0; 5128c2ecf20Sopenharmony_ci } 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ciint fsl_add_bridge(struct platform_device *pdev, int is_primary) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci int len; 5198c2ecf20Sopenharmony_ci struct pci_controller *hose; 5208c2ecf20Sopenharmony_ci struct resource rsrc; 5218c2ecf20Sopenharmony_ci const int *bus_range; 5228c2ecf20Sopenharmony_ci u8 hdr_type, progif; 5238c2ecf20Sopenharmony_ci u32 class_code; 5248c2ecf20Sopenharmony_ci struct device_node *dev; 5258c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci; 5268c2ecf20Sopenharmony_ci u16 temp; 5278c2ecf20Sopenharmony_ci u32 svr = mfspr(SPRN_SVR); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci dev = pdev->dev.of_node; 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci if (!of_device_is_available(dev)) { 5328c2ecf20Sopenharmony_ci pr_warn("%pOF: disabled\n", dev); 5338c2ecf20Sopenharmony_ci return -ENODEV; 5348c2ecf20Sopenharmony_ci } 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci pr_debug("Adding PCI host bridge %pOF\n", dev); 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci /* Fetch host bridge registers address */ 5398c2ecf20Sopenharmony_ci if (of_address_to_resource(dev, 0, &rsrc)) { 5408c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get pci register base!"); 5418c2ecf20Sopenharmony_ci return -ENOMEM; 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci /* Get bus range if any */ 5458c2ecf20Sopenharmony_ci bus_range = of_get_property(dev, "bus-range", &len); 5468c2ecf20Sopenharmony_ci if (bus_range == NULL || len < 2 * sizeof(int)) 5478c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF, assume" 5488c2ecf20Sopenharmony_ci " bus 0\n", dev); 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci pci_add_flags(PCI_REASSIGN_ALL_BUS); 5518c2ecf20Sopenharmony_ci hose = pcibios_alloc_controller(dev); 5528c2ecf20Sopenharmony_ci if (!hose) 5538c2ecf20Sopenharmony_ci return -ENOMEM; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci /* set platform device as the parent */ 5568c2ecf20Sopenharmony_ci hose->parent = &pdev->dev; 5578c2ecf20Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0x0; 5588c2ecf20Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci pr_debug("PCI memory map start 0x%016llx, size 0x%016llx\n", 5618c2ecf20Sopenharmony_ci (u64)rsrc.start, (u64)resource_size(&rsrc)); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci pci = hose->private_data = ioremap(rsrc.start, resource_size(&rsrc)); 5648c2ecf20Sopenharmony_ci if (!hose->private_data) 5658c2ecf20Sopenharmony_ci goto no_bridge; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci setup_indirect_pci(hose, rsrc.start, rsrc.start + 0x4, 5688c2ecf20Sopenharmony_ci PPC_INDIRECT_TYPE_BIG_ENDIAN); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci if (in_be32(&pci->block_rev1) < PCIE_IP_REV_3_0) 5718c2ecf20Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_FSL_CFG_REG_LINK; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) { 5748c2ecf20Sopenharmony_ci /* use fsl_indirect_read_config for PCIe */ 5758c2ecf20Sopenharmony_ci hose->ops = &fsl_indirect_pcie_ops; 5768c2ecf20Sopenharmony_ci /* For PCIE read HEADER_TYPE to identify controller mode */ 5778c2ecf20Sopenharmony_ci early_read_config_byte(hose, 0, 0, PCI_HEADER_TYPE, &hdr_type); 5788c2ecf20Sopenharmony_ci if ((hdr_type & 0x7f) != PCI_HEADER_TYPE_BRIDGE) 5798c2ecf20Sopenharmony_ci goto no_bridge; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci } else { 5828c2ecf20Sopenharmony_ci /* For PCI read PROG to identify controller mode */ 5838c2ecf20Sopenharmony_ci early_read_config_byte(hose, 0, 0, PCI_CLASS_PROG, &progif); 5848c2ecf20Sopenharmony_ci if ((progif & 1) && 5858c2ecf20Sopenharmony_ci !of_property_read_bool(dev, "fsl,pci-agent-force-enum")) 5868c2ecf20Sopenharmony_ci goto no_bridge; 5878c2ecf20Sopenharmony_ci } 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci setup_pci_cmd(hose); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci /* check PCI express link status */ 5928c2ecf20Sopenharmony_ci if (early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP)) { 5938c2ecf20Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_EXT_REG | 5948c2ecf20Sopenharmony_ci PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS; 5958c2ecf20Sopenharmony_ci if (fsl_pcie_check_link(hose)) 5968c2ecf20Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK; 5978c2ecf20Sopenharmony_ci /* Fix Class Code to PCI_CLASS_BRIDGE_PCI_NORMAL for pre-3.0 controller */ 5988c2ecf20Sopenharmony_ci if (in_be32(&pci->block_rev1) < PCIE_IP_REV_3_0) { 5998c2ecf20Sopenharmony_ci early_read_config_dword(hose, 0, 0, PCIE_FSL_CSR_CLASSCODE, &class_code); 6008c2ecf20Sopenharmony_ci class_code &= 0xff; 6018c2ecf20Sopenharmony_ci class_code |= PCI_CLASS_BRIDGE_PCI_NORMAL << 8; 6028c2ecf20Sopenharmony_ci early_write_config_dword(hose, 0, 0, PCIE_FSL_CSR_CLASSCODE, class_code); 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci } else { 6058c2ecf20Sopenharmony_ci /* 6068c2ecf20Sopenharmony_ci * Set PBFR(PCI Bus Function Register)[10] = 1 to 6078c2ecf20Sopenharmony_ci * disable the combining of crossing cacheline 6088c2ecf20Sopenharmony_ci * boundary requests into one burst transaction. 6098c2ecf20Sopenharmony_ci * PCI-X operation is not affected. 6108c2ecf20Sopenharmony_ci * Fix erratum PCI 5 on MPC8548 6118c2ecf20Sopenharmony_ci */ 6128c2ecf20Sopenharmony_ci#define PCI_BUS_FUNCTION 0x44 6138c2ecf20Sopenharmony_ci#define PCI_BUS_FUNCTION_MDS 0x400 /* Master disable streaming */ 6148c2ecf20Sopenharmony_ci if (((SVR_SOC_VER(svr) == SVR_8543) || 6158c2ecf20Sopenharmony_ci (SVR_SOC_VER(svr) == SVR_8545) || 6168c2ecf20Sopenharmony_ci (SVR_SOC_VER(svr) == SVR_8547) || 6178c2ecf20Sopenharmony_ci (SVR_SOC_VER(svr) == SVR_8548)) && 6188c2ecf20Sopenharmony_ci !early_find_capability(hose, 0, 0, PCI_CAP_ID_PCIX)) { 6198c2ecf20Sopenharmony_ci early_read_config_word(hose, 0, 0, 6208c2ecf20Sopenharmony_ci PCI_BUS_FUNCTION, &temp); 6218c2ecf20Sopenharmony_ci temp |= PCI_BUS_FUNCTION_MDS; 6228c2ecf20Sopenharmony_ci early_write_config_word(hose, 0, 0, 6238c2ecf20Sopenharmony_ci PCI_BUS_FUNCTION, temp); 6248c2ecf20Sopenharmony_ci } 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci printk(KERN_INFO "Found FSL PCI host bridge at 0x%016llx. " 6288c2ecf20Sopenharmony_ci "Firmware bus number: %d->%d\n", 6298c2ecf20Sopenharmony_ci (unsigned long long)rsrc.start, hose->first_busno, 6308c2ecf20Sopenharmony_ci hose->last_busno); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci pr_debug(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n", 6338c2ecf20Sopenharmony_ci hose, hose->cfg_addr, hose->cfg_data); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci /* Interpret the "ranges" property */ 6368c2ecf20Sopenharmony_ci /* This also maps the I/O region and sets isa_io/mem_base */ 6378c2ecf20Sopenharmony_ci pci_process_bridge_OF_ranges(hose, dev, is_primary); 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci /* Setup PEX window registers */ 6408c2ecf20Sopenharmony_ci setup_pci_atmu(hose); 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci /* Set up controller operations */ 6438c2ecf20Sopenharmony_ci setup_swiotlb_ops(hose); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci return 0; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_cino_bridge: 6488c2ecf20Sopenharmony_ci iounmap(hose->private_data); 6498c2ecf20Sopenharmony_ci /* unmap cfg_data & cfg_addr separately if not on same page */ 6508c2ecf20Sopenharmony_ci if (((unsigned long)hose->cfg_data & PAGE_MASK) != 6518c2ecf20Sopenharmony_ci ((unsigned long)hose->cfg_addr & PAGE_MASK)) 6528c2ecf20Sopenharmony_ci iounmap(hose->cfg_data); 6538c2ecf20Sopenharmony_ci iounmap(hose->cfg_addr); 6548c2ecf20Sopenharmony_ci pcibios_free_controller(hose); 6558c2ecf20Sopenharmony_ci return -ENODEV; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci#endif /* CONFIG_FSL_SOC_BOOKE || CONFIG_PPC_86xx */ 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, 6608c2ecf20Sopenharmony_ci quirk_fsl_pcie_early); 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci#if defined(CONFIG_PPC_83xx) || defined(CONFIG_PPC_MPC512x) 6638c2ecf20Sopenharmony_cistruct mpc83xx_pcie_priv { 6648c2ecf20Sopenharmony_ci void __iomem *cfg_type0; 6658c2ecf20Sopenharmony_ci void __iomem *cfg_type1; 6668c2ecf20Sopenharmony_ci u32 dev_base; 6678c2ecf20Sopenharmony_ci}; 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_cistruct pex_inbound_window { 6708c2ecf20Sopenharmony_ci u32 ar; 6718c2ecf20Sopenharmony_ci u32 tar; 6728c2ecf20Sopenharmony_ci u32 barl; 6738c2ecf20Sopenharmony_ci u32 barh; 6748c2ecf20Sopenharmony_ci}; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci/* 6778c2ecf20Sopenharmony_ci * With the convention of u-boot, the PCIE outbound window 0 serves 6788c2ecf20Sopenharmony_ci * as configuration transactions outbound. 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_ci#define PEX_OUTWIN0_BAR 0xCA4 6818c2ecf20Sopenharmony_ci#define PEX_OUTWIN0_TAL 0xCA8 6828c2ecf20Sopenharmony_ci#define PEX_OUTWIN0_TAH 0xCAC 6838c2ecf20Sopenharmony_ci#define PEX_RC_INWIN_BASE 0xE60 6848c2ecf20Sopenharmony_ci#define PEX_RCIWARn_EN 0x1 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_cistatic int mpc83xx_pcie_exclude_device(struct pci_bus *bus, unsigned int devfn) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci if (hose->indirect_type & PPC_INDIRECT_TYPE_NO_PCIE_LINK) 6918c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 6928c2ecf20Sopenharmony_ci /* 6938c2ecf20Sopenharmony_ci * Workaround for the HW bug: for Type 0 configure transactions the 6948c2ecf20Sopenharmony_ci * PCI-E controller does not check the device number bits and just 6958c2ecf20Sopenharmony_ci * assumes that the device number bits are 0. 6968c2ecf20Sopenharmony_ci */ 6978c2ecf20Sopenharmony_ci if (bus->number == hose->first_busno || 6988c2ecf20Sopenharmony_ci bus->primary == hose->first_busno) { 6998c2ecf20Sopenharmony_ci if (devfn & 0xf8) 7008c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 7018c2ecf20Sopenharmony_ci } 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (ppc_md.pci_exclude_device) { 7048c2ecf20Sopenharmony_ci if (ppc_md.pci_exclude_device(hose, bus->number, devfn)) 7058c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 7068c2ecf20Sopenharmony_ci } 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void __iomem *mpc83xx_pcie_remap_cfg(struct pci_bus *bus, 7128c2ecf20Sopenharmony_ci unsigned int devfn, int offset) 7138c2ecf20Sopenharmony_ci{ 7148c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 7158c2ecf20Sopenharmony_ci struct mpc83xx_pcie_priv *pcie = hose->dn->data; 7168c2ecf20Sopenharmony_ci u32 dev_base = bus->number << 24 | devfn << 16; 7178c2ecf20Sopenharmony_ci int ret; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ret = mpc83xx_pcie_exclude_device(bus, devfn); 7208c2ecf20Sopenharmony_ci if (ret) 7218c2ecf20Sopenharmony_ci return NULL; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci offset &= 0xfff; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci /* Type 0 */ 7268c2ecf20Sopenharmony_ci if (bus->number == hose->first_busno) 7278c2ecf20Sopenharmony_ci return pcie->cfg_type0 + offset; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (pcie->dev_base == dev_base) 7308c2ecf20Sopenharmony_ci goto mapped; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci out_le32(pcie->cfg_type0 + PEX_OUTWIN0_TAL, dev_base); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci pcie->dev_base = dev_base; 7358c2ecf20Sopenharmony_cimapped: 7368c2ecf20Sopenharmony_ci return pcie->cfg_type1 + offset; 7378c2ecf20Sopenharmony_ci} 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_cistatic int mpc83xx_pcie_write_config(struct pci_bus *bus, unsigned int devfn, 7408c2ecf20Sopenharmony_ci int offset, int len, u32 val) 7418c2ecf20Sopenharmony_ci{ 7428c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bus); 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci /* PPC_INDIRECT_TYPE_SURPRESS_PRIMARY_BUS */ 7458c2ecf20Sopenharmony_ci if (offset == PCI_PRIMARY_BUS && bus->number == hose->first_busno) 7468c2ecf20Sopenharmony_ci val &= 0xffffff00; 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci return pci_generic_config_write(bus, devfn, offset, len, val); 7498c2ecf20Sopenharmony_ci} 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_cistatic struct pci_ops mpc83xx_pcie_ops = { 7528c2ecf20Sopenharmony_ci .map_bus = mpc83xx_pcie_remap_cfg, 7538c2ecf20Sopenharmony_ci .read = pci_generic_config_read, 7548c2ecf20Sopenharmony_ci .write = mpc83xx_pcie_write_config, 7558c2ecf20Sopenharmony_ci}; 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_cistatic int __init mpc83xx_pcie_setup(struct pci_controller *hose, 7588c2ecf20Sopenharmony_ci struct resource *reg) 7598c2ecf20Sopenharmony_ci{ 7608c2ecf20Sopenharmony_ci struct mpc83xx_pcie_priv *pcie; 7618c2ecf20Sopenharmony_ci u32 cfg_bar; 7628c2ecf20Sopenharmony_ci int ret = -ENOMEM; 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_ci pcie = zalloc_maybe_bootmem(sizeof(*pcie), GFP_KERNEL); 7658c2ecf20Sopenharmony_ci if (!pcie) 7668c2ecf20Sopenharmony_ci return ret; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci pcie->cfg_type0 = ioremap(reg->start, resource_size(reg)); 7698c2ecf20Sopenharmony_ci if (!pcie->cfg_type0) 7708c2ecf20Sopenharmony_ci goto err0; 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_ci cfg_bar = in_le32(pcie->cfg_type0 + PEX_OUTWIN0_BAR); 7738c2ecf20Sopenharmony_ci if (!cfg_bar) { 7748c2ecf20Sopenharmony_ci /* PCI-E isn't configured. */ 7758c2ecf20Sopenharmony_ci ret = -ENODEV; 7768c2ecf20Sopenharmony_ci goto err1; 7778c2ecf20Sopenharmony_ci } 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci pcie->cfg_type1 = ioremap(cfg_bar, 0x1000); 7808c2ecf20Sopenharmony_ci if (!pcie->cfg_type1) 7818c2ecf20Sopenharmony_ci goto err1; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci WARN_ON(hose->dn->data); 7848c2ecf20Sopenharmony_ci hose->dn->data = pcie; 7858c2ecf20Sopenharmony_ci hose->ops = &mpc83xx_pcie_ops; 7868c2ecf20Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_FSL_CFG_REG_LINK; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci out_le32(pcie->cfg_type0 + PEX_OUTWIN0_TAH, 0); 7898c2ecf20Sopenharmony_ci out_le32(pcie->cfg_type0 + PEX_OUTWIN0_TAL, 0); 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_ci if (fsl_pcie_check_link(hose)) 7928c2ecf20Sopenharmony_ci hose->indirect_type |= PPC_INDIRECT_TYPE_NO_PCIE_LINK; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci return 0; 7958c2ecf20Sopenharmony_cierr1: 7968c2ecf20Sopenharmony_ci iounmap(pcie->cfg_type0); 7978c2ecf20Sopenharmony_cierr0: 7988c2ecf20Sopenharmony_ci kfree(pcie); 7998c2ecf20Sopenharmony_ci return ret; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ciint __init mpc83xx_add_bridge(struct device_node *dev) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci int ret; 8068c2ecf20Sopenharmony_ci int len; 8078c2ecf20Sopenharmony_ci struct pci_controller *hose; 8088c2ecf20Sopenharmony_ci struct resource rsrc_reg; 8098c2ecf20Sopenharmony_ci struct resource rsrc_cfg; 8108c2ecf20Sopenharmony_ci const int *bus_range; 8118c2ecf20Sopenharmony_ci int primary; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci is_mpc83xx_pci = 1; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (!of_device_is_available(dev)) { 8168c2ecf20Sopenharmony_ci pr_warn("%pOF: disabled by the firmware.\n", 8178c2ecf20Sopenharmony_ci dev); 8188c2ecf20Sopenharmony_ci return -ENODEV; 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci pr_debug("Adding PCI host bridge %pOF\n", dev); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci /* Fetch host bridge registers address */ 8238c2ecf20Sopenharmony_ci if (of_address_to_resource(dev, 0, &rsrc_reg)) { 8248c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get pci register base!\n"); 8258c2ecf20Sopenharmony_ci return -ENOMEM; 8268c2ecf20Sopenharmony_ci } 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci memset(&rsrc_cfg, 0, sizeof(rsrc_cfg)); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (of_address_to_resource(dev, 1, &rsrc_cfg)) { 8318c2ecf20Sopenharmony_ci printk(KERN_WARNING 8328c2ecf20Sopenharmony_ci "No pci config register base in dev tree, " 8338c2ecf20Sopenharmony_ci "using default\n"); 8348c2ecf20Sopenharmony_ci /* 8358c2ecf20Sopenharmony_ci * MPC83xx supports up to two host controllers 8368c2ecf20Sopenharmony_ci * one at 0x8500 has config space registers at 0x8300 8378c2ecf20Sopenharmony_ci * one at 0x8600 has config space registers at 0x8380 8388c2ecf20Sopenharmony_ci */ 8398c2ecf20Sopenharmony_ci if ((rsrc_reg.start & 0xfffff) == 0x8500) 8408c2ecf20Sopenharmony_ci rsrc_cfg.start = (rsrc_reg.start & 0xfff00000) + 0x8300; 8418c2ecf20Sopenharmony_ci else if ((rsrc_reg.start & 0xfffff) == 0x8600) 8428c2ecf20Sopenharmony_ci rsrc_cfg.start = (rsrc_reg.start & 0xfff00000) + 0x8380; 8438c2ecf20Sopenharmony_ci } 8448c2ecf20Sopenharmony_ci /* 8458c2ecf20Sopenharmony_ci * Controller at offset 0x8500 is primary 8468c2ecf20Sopenharmony_ci */ 8478c2ecf20Sopenharmony_ci if ((rsrc_reg.start & 0xfffff) == 0x8500) 8488c2ecf20Sopenharmony_ci primary = 1; 8498c2ecf20Sopenharmony_ci else 8508c2ecf20Sopenharmony_ci primary = 0; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci /* Get bus range if any */ 8538c2ecf20Sopenharmony_ci bus_range = of_get_property(dev, "bus-range", &len); 8548c2ecf20Sopenharmony_ci if (bus_range == NULL || len < 2 * sizeof(int)) { 8558c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF, assume" 8568c2ecf20Sopenharmony_ci " bus 0\n", dev); 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci pci_add_flags(PCI_REASSIGN_ALL_BUS); 8608c2ecf20Sopenharmony_ci hose = pcibios_alloc_controller(dev); 8618c2ecf20Sopenharmony_ci if (!hose) 8628c2ecf20Sopenharmony_ci return -ENOMEM; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0; 8658c2ecf20Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (of_device_is_compatible(dev, "fsl,mpc8314-pcie")) { 8688c2ecf20Sopenharmony_ci ret = mpc83xx_pcie_setup(hose, &rsrc_reg); 8698c2ecf20Sopenharmony_ci if (ret) 8708c2ecf20Sopenharmony_ci goto err0; 8718c2ecf20Sopenharmony_ci } else { 8728c2ecf20Sopenharmony_ci setup_indirect_pci(hose, rsrc_cfg.start, 8738c2ecf20Sopenharmony_ci rsrc_cfg.start + 4, 0); 8748c2ecf20Sopenharmony_ci } 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci printk(KERN_INFO "Found FSL PCI host bridge at 0x%016llx. " 8778c2ecf20Sopenharmony_ci "Firmware bus number: %d->%d\n", 8788c2ecf20Sopenharmony_ci (unsigned long long)rsrc_reg.start, hose->first_busno, 8798c2ecf20Sopenharmony_ci hose->last_busno); 8808c2ecf20Sopenharmony_ci 8818c2ecf20Sopenharmony_ci pr_debug(" ->Hose at 0x%p, cfg_addr=0x%p,cfg_data=0x%p\n", 8828c2ecf20Sopenharmony_ci hose, hose->cfg_addr, hose->cfg_data); 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_ci /* Interpret the "ranges" property */ 8858c2ecf20Sopenharmony_ci /* This also maps the I/O region and sets isa_io/mem_base */ 8868c2ecf20Sopenharmony_ci pci_process_bridge_OF_ranges(hose, dev, primary); 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci return 0; 8898c2ecf20Sopenharmony_cierr0: 8908c2ecf20Sopenharmony_ci pcibios_free_controller(hose); 8918c2ecf20Sopenharmony_ci return ret; 8928c2ecf20Sopenharmony_ci} 8938c2ecf20Sopenharmony_ci#endif /* CONFIG_PPC_83xx */ 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ciu64 fsl_pci_immrbar_base(struct pci_controller *hose) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci#ifdef CONFIG_PPC_83xx 8988c2ecf20Sopenharmony_ci if (is_mpc83xx_pci) { 8998c2ecf20Sopenharmony_ci struct mpc83xx_pcie_priv *pcie = hose->dn->data; 9008c2ecf20Sopenharmony_ci struct pex_inbound_window *in; 9018c2ecf20Sopenharmony_ci int i; 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci /* Walk the Root Complex Inbound windows to match IMMR base */ 9048c2ecf20Sopenharmony_ci in = pcie->cfg_type0 + PEX_RC_INWIN_BASE; 9058c2ecf20Sopenharmony_ci for (i = 0; i < 4; i++) { 9068c2ecf20Sopenharmony_ci /* not enabled, skip */ 9078c2ecf20Sopenharmony_ci if (!(in_le32(&in[i].ar) & PEX_RCIWARn_EN)) 9088c2ecf20Sopenharmony_ci continue; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci if (get_immrbase() == in_le32(&in[i].tar)) 9118c2ecf20Sopenharmony_ci return (u64)in_le32(&in[i].barh) << 32 | 9128c2ecf20Sopenharmony_ci in_le32(&in[i].barl); 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci printk(KERN_WARNING "could not find PCI BAR matching IMMR\n"); 9168c2ecf20Sopenharmony_ci } 9178c2ecf20Sopenharmony_ci#endif 9188c2ecf20Sopenharmony_ci 9198c2ecf20Sopenharmony_ci#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) 9208c2ecf20Sopenharmony_ci if (!is_mpc83xx_pci) { 9218c2ecf20Sopenharmony_ci u32 base; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci pci_bus_read_config_dword(hose->bus, 9248c2ecf20Sopenharmony_ci PCI_DEVFN(0, 0), PCI_BASE_ADDRESS_0, &base); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci /* 9278c2ecf20Sopenharmony_ci * For PEXCSRBAR, bit 3-0 indicate prefetchable and 9288c2ecf20Sopenharmony_ci * address type. So when getting base address, these 9298c2ecf20Sopenharmony_ci * bits should be masked 9308c2ecf20Sopenharmony_ci */ 9318c2ecf20Sopenharmony_ci base &= PCI_BASE_ADDRESS_MEM_MASK; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci return base; 9348c2ecf20Sopenharmony_ci } 9358c2ecf20Sopenharmony_ci#endif 9368c2ecf20Sopenharmony_ci 9378c2ecf20Sopenharmony_ci return 0; 9388c2ecf20Sopenharmony_ci} 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci#ifdef CONFIG_E500 9418c2ecf20Sopenharmony_cistatic int mcheck_handle_load(struct pt_regs *regs, u32 inst) 9428c2ecf20Sopenharmony_ci{ 9438c2ecf20Sopenharmony_ci unsigned int rd, ra, rb, d; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci rd = get_rt(inst); 9468c2ecf20Sopenharmony_ci ra = get_ra(inst); 9478c2ecf20Sopenharmony_ci rb = get_rb(inst); 9488c2ecf20Sopenharmony_ci d = get_d(inst); 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci switch (get_op(inst)) { 9518c2ecf20Sopenharmony_ci case 31: 9528c2ecf20Sopenharmony_ci switch (get_xop(inst)) { 9538c2ecf20Sopenharmony_ci case OP_31_XOP_LWZX: 9548c2ecf20Sopenharmony_ci case OP_31_XOP_LWBRX: 9558c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffffffff; 9568c2ecf20Sopenharmony_ci break; 9578c2ecf20Sopenharmony_ci 9588c2ecf20Sopenharmony_ci case OP_31_XOP_LWZUX: 9598c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffffffff; 9608c2ecf20Sopenharmony_ci regs->gpr[ra] += regs->gpr[rb]; 9618c2ecf20Sopenharmony_ci break; 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci case OP_31_XOP_LBZX: 9648c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xff; 9658c2ecf20Sopenharmony_ci break; 9668c2ecf20Sopenharmony_ci 9678c2ecf20Sopenharmony_ci case OP_31_XOP_LBZUX: 9688c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xff; 9698c2ecf20Sopenharmony_ci regs->gpr[ra] += regs->gpr[rb]; 9708c2ecf20Sopenharmony_ci break; 9718c2ecf20Sopenharmony_ci 9728c2ecf20Sopenharmony_ci case OP_31_XOP_LHZX: 9738c2ecf20Sopenharmony_ci case OP_31_XOP_LHBRX: 9748c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffff; 9758c2ecf20Sopenharmony_ci break; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci case OP_31_XOP_LHZUX: 9788c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffff; 9798c2ecf20Sopenharmony_ci regs->gpr[ra] += regs->gpr[rb]; 9808c2ecf20Sopenharmony_ci break; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci case OP_31_XOP_LHAX: 9838c2ecf20Sopenharmony_ci regs->gpr[rd] = ~0UL; 9848c2ecf20Sopenharmony_ci break; 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci case OP_31_XOP_LHAUX: 9878c2ecf20Sopenharmony_ci regs->gpr[rd] = ~0UL; 9888c2ecf20Sopenharmony_ci regs->gpr[ra] += regs->gpr[rb]; 9898c2ecf20Sopenharmony_ci break; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci default: 9928c2ecf20Sopenharmony_ci return 0; 9938c2ecf20Sopenharmony_ci } 9948c2ecf20Sopenharmony_ci break; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci case OP_LWZ: 9978c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffffffff; 9988c2ecf20Sopenharmony_ci break; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci case OP_LWZU: 10018c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffffffff; 10028c2ecf20Sopenharmony_ci regs->gpr[ra] += (s16)d; 10038c2ecf20Sopenharmony_ci break; 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci case OP_LBZ: 10068c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xff; 10078c2ecf20Sopenharmony_ci break; 10088c2ecf20Sopenharmony_ci 10098c2ecf20Sopenharmony_ci case OP_LBZU: 10108c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xff; 10118c2ecf20Sopenharmony_ci regs->gpr[ra] += (s16)d; 10128c2ecf20Sopenharmony_ci break; 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci case OP_LHZ: 10158c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffff; 10168c2ecf20Sopenharmony_ci break; 10178c2ecf20Sopenharmony_ci 10188c2ecf20Sopenharmony_ci case OP_LHZU: 10198c2ecf20Sopenharmony_ci regs->gpr[rd] = 0xffff; 10208c2ecf20Sopenharmony_ci regs->gpr[ra] += (s16)d; 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci case OP_LHA: 10248c2ecf20Sopenharmony_ci regs->gpr[rd] = ~0UL; 10258c2ecf20Sopenharmony_ci break; 10268c2ecf20Sopenharmony_ci 10278c2ecf20Sopenharmony_ci case OP_LHAU: 10288c2ecf20Sopenharmony_ci regs->gpr[rd] = ~0UL; 10298c2ecf20Sopenharmony_ci regs->gpr[ra] += (s16)d; 10308c2ecf20Sopenharmony_ci break; 10318c2ecf20Sopenharmony_ci 10328c2ecf20Sopenharmony_ci default: 10338c2ecf20Sopenharmony_ci return 0; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return 1; 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_cistatic int is_in_pci_mem_space(phys_addr_t addr) 10408c2ecf20Sopenharmony_ci{ 10418c2ecf20Sopenharmony_ci struct pci_controller *hose; 10428c2ecf20Sopenharmony_ci struct resource *res; 10438c2ecf20Sopenharmony_ci int i; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci list_for_each_entry(hose, &hose_list, list_node) { 10468c2ecf20Sopenharmony_ci if (!(hose->indirect_type & PPC_INDIRECT_TYPE_EXT_REG)) 10478c2ecf20Sopenharmony_ci continue; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci for (i = 0; i < 3; i++) { 10508c2ecf20Sopenharmony_ci res = &hose->mem_resources[i]; 10518c2ecf20Sopenharmony_ci if ((res->flags & IORESOURCE_MEM) && 10528c2ecf20Sopenharmony_ci addr >= res->start && addr <= res->end) 10538c2ecf20Sopenharmony_ci return 1; 10548c2ecf20Sopenharmony_ci } 10558c2ecf20Sopenharmony_ci } 10568c2ecf20Sopenharmony_ci return 0; 10578c2ecf20Sopenharmony_ci} 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ciint fsl_pci_mcheck_exception(struct pt_regs *regs) 10608c2ecf20Sopenharmony_ci{ 10618c2ecf20Sopenharmony_ci u32 inst; 10628c2ecf20Sopenharmony_ci int ret; 10638c2ecf20Sopenharmony_ci phys_addr_t addr = 0; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci /* Let KVM/QEMU deal with the exception */ 10668c2ecf20Sopenharmony_ci if (regs->msr & MSR_GS) 10678c2ecf20Sopenharmony_ci return 0; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci#ifdef CONFIG_PHYS_64BIT 10708c2ecf20Sopenharmony_ci addr = mfspr(SPRN_MCARU); 10718c2ecf20Sopenharmony_ci addr <<= 32; 10728c2ecf20Sopenharmony_ci#endif 10738c2ecf20Sopenharmony_ci addr += mfspr(SPRN_MCAR); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci if (is_in_pci_mem_space(addr)) { 10768c2ecf20Sopenharmony_ci if (user_mode(regs)) 10778c2ecf20Sopenharmony_ci ret = copy_from_user_nofault(&inst, 10788c2ecf20Sopenharmony_ci (void __user *)regs->nip, sizeof(inst)); 10798c2ecf20Sopenharmony_ci else 10808c2ecf20Sopenharmony_ci ret = get_kernel_nofault(inst, (void *)regs->nip); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (!ret && mcheck_handle_load(regs, inst)) { 10838c2ecf20Sopenharmony_ci regs->nip += 4; 10848c2ecf20Sopenharmony_ci return 1; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return 0; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci#endif 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci#if defined(CONFIG_FSL_SOC_BOOKE) || defined(CONFIG_PPC_86xx) 10938c2ecf20Sopenharmony_cistatic const struct of_device_id pci_ids[] = { 10948c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc8540-pci", }, 10958c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc8548-pcie", }, 10968c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc8610-pci", }, 10978c2ecf20Sopenharmony_ci { .compatible = "fsl,mpc8641-pcie", }, 10988c2ecf20Sopenharmony_ci { .compatible = "fsl,qoriq-pcie", }, 10998c2ecf20Sopenharmony_ci { .compatible = "fsl,qoriq-pcie-v2.1", }, 11008c2ecf20Sopenharmony_ci { .compatible = "fsl,qoriq-pcie-v2.2", }, 11018c2ecf20Sopenharmony_ci { .compatible = "fsl,qoriq-pcie-v2.3", }, 11028c2ecf20Sopenharmony_ci { .compatible = "fsl,qoriq-pcie-v2.4", }, 11038c2ecf20Sopenharmony_ci { .compatible = "fsl,qoriq-pcie-v3.0", }, 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* 11068c2ecf20Sopenharmony_ci * The following entries are for compatibility with older device 11078c2ecf20Sopenharmony_ci * trees. 11088c2ecf20Sopenharmony_ci */ 11098c2ecf20Sopenharmony_ci { .compatible = "fsl,p1022-pcie", }, 11108c2ecf20Sopenharmony_ci { .compatible = "fsl,p4080-pcie", }, 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci {}, 11138c2ecf20Sopenharmony_ci}; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistruct device_node *fsl_pci_primary; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_civoid fsl_pci_assign_primary(void) 11188c2ecf20Sopenharmony_ci{ 11198c2ecf20Sopenharmony_ci struct device_node *np; 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* Callers can specify the primary bus using other means. */ 11228c2ecf20Sopenharmony_ci if (fsl_pci_primary) 11238c2ecf20Sopenharmony_ci return; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* If a PCI host bridge contains an ISA node, it's primary. */ 11268c2ecf20Sopenharmony_ci np = of_find_node_by_type(NULL, "isa"); 11278c2ecf20Sopenharmony_ci while ((fsl_pci_primary = of_get_parent(np))) { 11288c2ecf20Sopenharmony_ci of_node_put(np); 11298c2ecf20Sopenharmony_ci np = fsl_pci_primary; 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci if (of_match_node(pci_ids, np) && of_device_is_available(np)) 11328c2ecf20Sopenharmony_ci return; 11338c2ecf20Sopenharmony_ci } 11348c2ecf20Sopenharmony_ci 11358c2ecf20Sopenharmony_ci /* 11368c2ecf20Sopenharmony_ci * If there's no PCI host bridge with ISA, arbitrarily 11378c2ecf20Sopenharmony_ci * designate one as primary. This can go away once 11388c2ecf20Sopenharmony_ci * various bugs with primary-less systems are fixed. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ci for_each_matching_node(np, pci_ids) { 11418c2ecf20Sopenharmony_ci if (of_device_is_available(np)) { 11428c2ecf20Sopenharmony_ci fsl_pci_primary = np; 11438c2ecf20Sopenharmony_ci of_node_put(np); 11448c2ecf20Sopenharmony_ci return; 11458c2ecf20Sopenharmony_ci } 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci} 11488c2ecf20Sopenharmony_ci 11498c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 11508c2ecf20Sopenharmony_cistatic irqreturn_t fsl_pci_pme_handle(int irq, void *dev_id) 11518c2ecf20Sopenharmony_ci{ 11528c2ecf20Sopenharmony_ci struct pci_controller *hose = dev_id; 11538c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci = hose->private_data; 11548c2ecf20Sopenharmony_ci u32 dr; 11558c2ecf20Sopenharmony_ci 11568c2ecf20Sopenharmony_ci dr = in_be32(&pci->pex_pme_mes_dr); 11578c2ecf20Sopenharmony_ci if (!dr) 11588c2ecf20Sopenharmony_ci return IRQ_NONE; 11598c2ecf20Sopenharmony_ci 11608c2ecf20Sopenharmony_ci out_be32(&pci->pex_pme_mes_dr, dr); 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return IRQ_HANDLED; 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic int fsl_pci_pme_probe(struct pci_controller *hose) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci; 11688c2ecf20Sopenharmony_ci struct pci_dev *dev; 11698c2ecf20Sopenharmony_ci int pme_irq; 11708c2ecf20Sopenharmony_ci int res; 11718c2ecf20Sopenharmony_ci u16 pms; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* Get hose's pci_dev */ 11748c2ecf20Sopenharmony_ci dev = list_first_entry(&hose->bus->devices, typeof(*dev), bus_list); 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci /* PME Disable */ 11778c2ecf20Sopenharmony_ci pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pms); 11788c2ecf20Sopenharmony_ci pms &= ~PCI_PM_CTRL_PME_ENABLE; 11798c2ecf20Sopenharmony_ci pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pms); 11808c2ecf20Sopenharmony_ci 11818c2ecf20Sopenharmony_ci pme_irq = irq_of_parse_and_map(hose->dn, 0); 11828c2ecf20Sopenharmony_ci if (!pme_irq) { 11838c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Failed to map PME interrupt.\n"); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return -ENXIO; 11868c2ecf20Sopenharmony_ci } 11878c2ecf20Sopenharmony_ci 11888c2ecf20Sopenharmony_ci res = devm_request_irq(hose->parent, pme_irq, 11898c2ecf20Sopenharmony_ci fsl_pci_pme_handle, 11908c2ecf20Sopenharmony_ci IRQF_SHARED, 11918c2ecf20Sopenharmony_ci "[PCI] PME", hose); 11928c2ecf20Sopenharmony_ci if (res < 0) { 11938c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Unable to request irq %d for PME\n", pme_irq); 11948c2ecf20Sopenharmony_ci irq_dispose_mapping(pme_irq); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return -ENODEV; 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci pci = hose->private_data; 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci /* Enable PTOD, ENL23D & EXL23D */ 12028c2ecf20Sopenharmony_ci clrbits32(&pci->pex_pme_mes_disr, 12038c2ecf20Sopenharmony_ci PME_DISR_EN_PTOD | PME_DISR_EN_ENL23D | PME_DISR_EN_EXL23D); 12048c2ecf20Sopenharmony_ci 12058c2ecf20Sopenharmony_ci out_be32(&pci->pex_pme_mes_ier, 0); 12068c2ecf20Sopenharmony_ci setbits32(&pci->pex_pme_mes_ier, 12078c2ecf20Sopenharmony_ci PME_DISR_EN_PTOD | PME_DISR_EN_ENL23D | PME_DISR_EN_EXL23D); 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci /* PME Enable */ 12108c2ecf20Sopenharmony_ci pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pms); 12118c2ecf20Sopenharmony_ci pms |= PCI_PM_CTRL_PME_ENABLE; 12128c2ecf20Sopenharmony_ci pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pms); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci return 0; 12158c2ecf20Sopenharmony_ci} 12168c2ecf20Sopenharmony_ci 12178c2ecf20Sopenharmony_cistatic void send_pme_turnoff_message(struct pci_controller *hose) 12188c2ecf20Sopenharmony_ci{ 12198c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci = hose->private_data; 12208c2ecf20Sopenharmony_ci u32 dr; 12218c2ecf20Sopenharmony_ci int i; 12228c2ecf20Sopenharmony_ci 12238c2ecf20Sopenharmony_ci /* Send PME_Turn_Off Message Request */ 12248c2ecf20Sopenharmony_ci setbits32(&pci->pex_pmcr, PEX_PMCR_PTOMR); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* Wait trun off done */ 12278c2ecf20Sopenharmony_ci for (i = 0; i < 150; i++) { 12288c2ecf20Sopenharmony_ci dr = in_be32(&pci->pex_pme_mes_dr); 12298c2ecf20Sopenharmony_ci if (dr) { 12308c2ecf20Sopenharmony_ci out_be32(&pci->pex_pme_mes_dr, dr); 12318c2ecf20Sopenharmony_ci break; 12328c2ecf20Sopenharmony_ci } 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci udelay(1000); 12358c2ecf20Sopenharmony_ci } 12368c2ecf20Sopenharmony_ci} 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_cistatic void fsl_pci_syscore_do_suspend(struct pci_controller *hose) 12398c2ecf20Sopenharmony_ci{ 12408c2ecf20Sopenharmony_ci send_pme_turnoff_message(hose); 12418c2ecf20Sopenharmony_ci} 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_cistatic int fsl_pci_syscore_suspend(void) 12448c2ecf20Sopenharmony_ci{ 12458c2ecf20Sopenharmony_ci struct pci_controller *hose, *tmp; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci list_for_each_entry_safe(hose, tmp, &hose_list, list_node) 12488c2ecf20Sopenharmony_ci fsl_pci_syscore_do_suspend(hose); 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci return 0; 12518c2ecf20Sopenharmony_ci} 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_cistatic void fsl_pci_syscore_do_resume(struct pci_controller *hose) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct ccsr_pci __iomem *pci = hose->private_data; 12568c2ecf20Sopenharmony_ci u32 dr; 12578c2ecf20Sopenharmony_ci int i; 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci /* Send Exit L2 State Message */ 12608c2ecf20Sopenharmony_ci setbits32(&pci->pex_pmcr, PEX_PMCR_EXL2S); 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci /* Wait exit done */ 12638c2ecf20Sopenharmony_ci for (i = 0; i < 150; i++) { 12648c2ecf20Sopenharmony_ci dr = in_be32(&pci->pex_pme_mes_dr); 12658c2ecf20Sopenharmony_ci if (dr) { 12668c2ecf20Sopenharmony_ci out_be32(&pci->pex_pme_mes_dr, dr); 12678c2ecf20Sopenharmony_ci break; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci udelay(1000); 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_ci setup_pci_atmu(hose); 12748c2ecf20Sopenharmony_ci} 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_cistatic void fsl_pci_syscore_resume(void) 12778c2ecf20Sopenharmony_ci{ 12788c2ecf20Sopenharmony_ci struct pci_controller *hose, *tmp; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci list_for_each_entry_safe(hose, tmp, &hose_list, list_node) 12818c2ecf20Sopenharmony_ci fsl_pci_syscore_do_resume(hose); 12828c2ecf20Sopenharmony_ci} 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_cistatic struct syscore_ops pci_syscore_pm_ops = { 12858c2ecf20Sopenharmony_ci .suspend = fsl_pci_syscore_suspend, 12868c2ecf20Sopenharmony_ci .resume = fsl_pci_syscore_resume, 12878c2ecf20Sopenharmony_ci}; 12888c2ecf20Sopenharmony_ci#endif 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_civoid fsl_pcibios_fixup_phb(struct pci_controller *phb) 12918c2ecf20Sopenharmony_ci{ 12928c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 12938c2ecf20Sopenharmony_ci fsl_pci_pme_probe(phb); 12948c2ecf20Sopenharmony_ci#endif 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic int add_err_dev(struct platform_device *pdev) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci struct platform_device *errdev; 13008c2ecf20Sopenharmony_ci struct mpc85xx_edac_pci_plat_data pd = { 13018c2ecf20Sopenharmony_ci .of_node = pdev->dev.of_node 13028c2ecf20Sopenharmony_ci }; 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci errdev = platform_device_register_resndata(&pdev->dev, 13058c2ecf20Sopenharmony_ci "mpc85xx-pci-edac", 13068c2ecf20Sopenharmony_ci PLATFORM_DEVID_AUTO, 13078c2ecf20Sopenharmony_ci pdev->resource, 13088c2ecf20Sopenharmony_ci pdev->num_resources, 13098c2ecf20Sopenharmony_ci &pd, sizeof(pd)); 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(errdev); 13128c2ecf20Sopenharmony_ci} 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_cistatic int fsl_pci_probe(struct platform_device *pdev) 13158c2ecf20Sopenharmony_ci{ 13168c2ecf20Sopenharmony_ci struct device_node *node; 13178c2ecf20Sopenharmony_ci int ret; 13188c2ecf20Sopenharmony_ci 13198c2ecf20Sopenharmony_ci node = pdev->dev.of_node; 13208c2ecf20Sopenharmony_ci ret = fsl_add_bridge(pdev, fsl_pci_primary == node); 13218c2ecf20Sopenharmony_ci if (ret) 13228c2ecf20Sopenharmony_ci return ret; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci ret = add_err_dev(pdev); 13258c2ecf20Sopenharmony_ci if (ret) 13268c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "couldn't register error device: %d\n", 13278c2ecf20Sopenharmony_ci ret); 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci return 0; 13308c2ecf20Sopenharmony_ci} 13318c2ecf20Sopenharmony_ci 13328c2ecf20Sopenharmony_cistatic struct platform_driver fsl_pci_driver = { 13338c2ecf20Sopenharmony_ci .driver = { 13348c2ecf20Sopenharmony_ci .name = "fsl-pci", 13358c2ecf20Sopenharmony_ci .of_match_table = pci_ids, 13368c2ecf20Sopenharmony_ci }, 13378c2ecf20Sopenharmony_ci .probe = fsl_pci_probe, 13388c2ecf20Sopenharmony_ci}; 13398c2ecf20Sopenharmony_ci 13408c2ecf20Sopenharmony_cistatic int __init fsl_pci_init(void) 13418c2ecf20Sopenharmony_ci{ 13428c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 13438c2ecf20Sopenharmony_ci register_syscore_ops(&pci_syscore_pm_ops); 13448c2ecf20Sopenharmony_ci#endif 13458c2ecf20Sopenharmony_ci return platform_driver_register(&fsl_pci_driver); 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ciarch_initcall(fsl_pci_init); 13488c2ecf20Sopenharmony_ci#endif 1349