diff options
Diffstat (limited to 'arch/powerpc/platforms/wsp/wsp_pci.c')
| -rw-r--r-- | arch/powerpc/platforms/wsp/wsp_pci.c | 1133 | 
1 files changed, 0 insertions, 1133 deletions
diff --git a/arch/powerpc/platforms/wsp/wsp_pci.c b/arch/powerpc/platforms/wsp/wsp_pci.c deleted file mode 100644 index 62cb527493e..00000000000 --- a/arch/powerpc/platforms/wsp/wsp_pci.c +++ /dev/null @@ -1,1133 +0,0 @@ -/* - * Copyright 2010 Ben Herrenschmidt, IBM Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#define DEBUG - -#include <linux/kernel.h> -#include <linux/pci.h> -#include <linux/delay.h> -#include <linux/string.h> -#include <linux/init.h> -#include <linux/bootmem.h> -#include <linux/irq.h> -#include <linux/interrupt.h> -#include <linux/debugfs.h> - -#include <asm/sections.h> -#include <asm/io.h> -#include <asm/prom.h> -#include <asm/pci-bridge.h> -#include <asm/machdep.h> -#include <asm/ppc-pci.h> -#include <asm/iommu.h> -#include <asm/io-workarounds.h> -#include <asm/debug.h> - -#include "wsp.h" -#include "wsp_pci.h" -#include "msi.h" - - -/* Max number of TVTs for one table. Only 32-bit tables can use - * multiple TVTs and so the max currently supported is thus 8 - * since only 2G of DMA space is supported - */ -#define MAX_TABLE_TVT_COUNT		8 - -struct wsp_dma_table { -	struct list_head	link; -	struct iommu_table	table; -	struct wsp_phb	*phb; -	struct page		*tces[MAX_TABLE_TVT_COUNT]; -}; - -/* We support DMA regions from 0...2G in 32bit space (no support for - * 64-bit DMA just yet). Each device gets a separate TCE table (TVT - * entry) with validation enabled (though not supported by SimiCS - * just yet). - * - * To simplify things, we divide this 2G space into N regions based - * on the constant below which could be turned into a tunable eventually - * - * We then assign dynamically those regions to devices as they show up. - * - * We use a bitmap as an allocator for these. - * - * Tables are allocated/created dynamically as devices are discovered, - * multiple TVT entries are used if needed - * - * When 64-bit DMA support is added we should simply use a separate set - * of larger regions (the HW supports 64 TVT entries). We can - * additionally create a bypass region in 64-bit space for performances - * though that would have a cost in term of security. - * - * If you set NUM_DMA32_REGIONS to 1, then a single table is shared - * for all devices and bus/dev/fn validation is disabled - * - * Note that a DMA32 region cannot be smaller than 256M so the max - * supported here for now is 8. We don't yet support sharing regions - * between multiple devices so the max number of devices supported - * is MAX_TABLE_TVT_COUNT. - */ -#define NUM_DMA32_REGIONS	1 - -struct wsp_phb { -	struct pci_controller	*hose; - -	/* Lock controlling access to the list of dma tables. -	 * It does -not- protect against dma_* operations on -	 * those tables, those should be stopped before an entry -	 * is removed from the list. -	 * -	 * The lock is also used for error handling operations -	 */ -	spinlock_t		lock; -	struct list_head	dma_tables; -	unsigned long		dma32_map; -	unsigned long		dma32_base; -	unsigned int		dma32_num_regions; -	unsigned long		dma32_region_size; - -	/* Debugfs stuff */ -	struct dentry		*ddir; - -	struct list_head	all; -}; -static LIST_HEAD(wsp_phbs); - -//#define cfg_debug(fmt...)	pr_debug(fmt) -#define cfg_debug(fmt...) - - -static int wsp_pcie_read_config(struct pci_bus *bus, unsigned int devfn, -				  int offset, int len, u32 *val) -{ -	struct pci_controller *hose; -	int suboff; -	u64 addr; - -	hose = pci_bus_to_host(bus); -	if (hose == NULL) -		return PCIBIOS_DEVICE_NOT_FOUND; -	if (offset >= 0x1000) -		return  PCIBIOS_BAD_REGISTER_NUMBER; -	addr = PCIE_REG_CA_ENABLE | -		((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT | -		((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT | -		((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT; -	suboff = offset & 3; - -	/* -	 * Note: the caller has already checked that offset is -	 * suitably aligned and that len is 1, 2 or 4. -	 */ - -	switch (len) { -	case 1: -		addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT; -		out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); -		*val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA) -			>> (suboff << 3)) & 0xff; -		cfg_debug("read 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n", -			  bus->number, devfn >> 3, devfn & 7, -			  offset, suboff, addr, *val); -		break; -	case 2: -		addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT; -		out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); -		*val = (in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA) -			>> (suboff << 3)) & 0xffff; -		cfg_debug("read 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n", -			  bus->number, devfn >> 3, devfn & 7, -			  offset, suboff, addr, *val); -		break; -	default: -		addr |= 0xful << PCIE_REG_CA_BE_SHIFT; -		out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); -		*val = in_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA); -		cfg_debug("read 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n", -			  bus->number, devfn >> 3, devfn & 7, -			  offset, suboff, addr, *val); -		break; -	} -	return PCIBIOS_SUCCESSFUL; -} - -static int wsp_pcie_write_config(struct pci_bus *bus, unsigned int devfn, -				   int offset, int len, u32 val) -{ -	struct pci_controller *hose; -	int suboff; -	u64 addr; - -	hose = pci_bus_to_host(bus); -	if (hose == NULL) -		return PCIBIOS_DEVICE_NOT_FOUND; -	if (offset >= 0x1000) -		return  PCIBIOS_BAD_REGISTER_NUMBER; -	addr = PCIE_REG_CA_ENABLE | -		((u64)bus->number) << PCIE_REG_CA_BUS_SHIFT | -		((u64)devfn) << PCIE_REG_CA_FUNC_SHIFT | -		((u64)offset & ~3) << PCIE_REG_CA_REG_SHIFT; -	suboff = offset & 3; - -	/* -	 * Note: the caller has already checked that offset is -	 * suitably aligned and that len is 1, 2 or 4. -	 */ -	switch (len) { -	case 1: -		addr |= (0x8ul >> suboff) << PCIE_REG_CA_BE_SHIFT; -		val <<= suboff << 3; -		out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); -		out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); -		cfg_debug("write 1 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%02x\n", -			  bus->number, devfn >> 3, devfn & 7, -			  offset, suboff, addr, val); -		break; -	case 2: -		addr |= (0xcul >> suboff) << PCIE_REG_CA_BE_SHIFT; -		val <<= suboff << 3; -		out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); -		out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); -		cfg_debug("write 2 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%04x\n", -			  bus->number, devfn >> 3, devfn & 7, -			  offset, suboff, addr, val); -		break; -	default: -		addr |= 0xful << PCIE_REG_CA_BE_SHIFT; -		out_be64(hose->cfg_data + PCIE_REG_CONFIG_ADDRESS, addr); -		out_le32(hose->cfg_data + PCIE_REG_CONFIG_DATA, val); -		cfg_debug("write 4 %02x:%02x:%02x + %02x/%x addr=0x%llx val=%08x\n", -			  bus->number, devfn >> 3, devfn & 7, -			  offset, suboff, addr, val); -		break; -	} -	return PCIBIOS_SUCCESSFUL; -} - -static struct pci_ops wsp_pcie_pci_ops = -{ -	.read = wsp_pcie_read_config, -	.write = wsp_pcie_write_config, -}; - -#define TCE_SHIFT		12 -#define TCE_PAGE_SIZE		(1 << TCE_SHIFT) -#define TCE_PCI_WRITE		0x2		 /* write from PCI allowed */ -#define TCE_PCI_READ		0x1	 	 /* read from PCI allowed */ -#define TCE_RPN_MASK		0x3fffffffffful  /* 42-bit RPN (4K pages) */ -#define TCE_RPN_SHIFT		12 - -//#define dma_debug(fmt...)	pr_debug(fmt) -#define dma_debug(fmt...) - -static int tce_build_wsp(struct iommu_table *tbl, long index, long npages, -			   unsigned long uaddr, enum dma_data_direction direction, -			   struct dma_attrs *attrs) -{ -	struct wsp_dma_table *ptbl = container_of(tbl, -						    struct wsp_dma_table, -						    table); -	u64 proto_tce; -	u64 *tcep; -	u64 rpn; - -	proto_tce = TCE_PCI_READ; -#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS -	proto_tce |= TCE_PCI_WRITE; -#else -	if (direction != DMA_TO_DEVICE) -		proto_tce |= TCE_PCI_WRITE; -#endif - -	/* XXX Make this faster by factoring out the page address for -	 * within a TCE table -	 */ -	while (npages--) { -		/* We don't use it->base as the table can be scattered */ -		tcep = (u64 *)page_address(ptbl->tces[index >> 16]); -		tcep += (index & 0xffff); - -		/* can't move this out since we might cross LMB boundary */ -		rpn = __pa(uaddr) >> TCE_SHIFT; -		*tcep = proto_tce | (rpn & TCE_RPN_MASK) << TCE_RPN_SHIFT; - -		dma_debug("[DMA] TCE %p set to 0x%016llx (dma addr: 0x%lx)\n", -			  tcep, *tcep, (tbl->it_offset + index) << IOMMU_PAGE_SHIFT); - -		uaddr += TCE_PAGE_SIZE; -		index++; -	} -	return 0; -} - -static void tce_free_wsp(struct iommu_table *tbl, long index, long npages) -{ -	struct wsp_dma_table *ptbl = container_of(tbl, -						    struct wsp_dma_table, -						    table); -#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS -	struct pci_controller *hose = ptbl->phb->hose; -#endif -	u64 *tcep; - -	/* XXX Make this faster by factoring out the page address for -	 * within a TCE table. Also use line-kill option to kill multiple -	 * TCEs at once -	 */ -	while (npages--) { -		/* We don't use it->base as the table can be scattered */ -		tcep = (u64 *)page_address(ptbl->tces[index >> 16]); -		tcep += (index & 0xffff); -		dma_debug("[DMA] TCE %p cleared\n", tcep); -		*tcep = 0; -#ifndef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS -		/* Don't write there since it would pollute other MMIO accesses */ -		out_be64(hose->cfg_data + PCIE_REG_TCE_KILL, -			 PCIE_REG_TCEKILL_SINGLE | PCIE_REG_TCEKILL_PS_4K | -			 (__pa(tcep) & PCIE_REG_TCEKILL_ADDR_MASK)); -#endif -		index++; -	} -} - -static struct wsp_dma_table *wsp_pci_create_dma32_table(struct wsp_phb *phb, -							    unsigned int region, -							    struct pci_dev *validate) -{ -	struct pci_controller *hose = phb->hose; -	unsigned long size = phb->dma32_region_size; -	unsigned long addr = phb->dma32_region_size * region + phb->dma32_base; -	struct wsp_dma_table *tbl; -	int tvts_per_table, i, tvt, nid; -	unsigned long flags; - -	nid = of_node_to_nid(phb->hose->dn); - -	/* Calculate how many TVTs are needed */ -	tvts_per_table = size / 0x10000000; -	if (tvts_per_table == 0) -		tvts_per_table = 1; - -	/* Calculate the base TVT index. We know all tables have the same -	 * size so we just do a simple multiply here -	 */ -	tvt = region * tvts_per_table; - -	pr_debug("         Region : %d\n", region); -	pr_debug("      DMA range : 0x%08lx..0x%08lx\n", addr, addr + size - 1); -	pr_debug(" Number of TVTs : %d\n", tvts_per_table); -	pr_debug("       Base TVT : %d\n", tvt); -	pr_debug("         Node   : %d\n", nid); - -	tbl = kzalloc_node(sizeof(struct wsp_dma_table), GFP_KERNEL, nid); -	if (!tbl) -		return ERR_PTR(-ENOMEM); -	tbl->phb = phb; - -	/* Create as many TVTs as needed, each represents 256M at most */ -	for (i = 0; i < tvts_per_table; i++) { -		u64 tvt_data1, tvt_data0; - -		/* Allocate table. We use a 4K TCE size for now always so -		 * one table is always 8 * (258M / 4K) == 512K -		 */ -		tbl->tces[i] = alloc_pages_node(nid, GFP_KERNEL, get_order(0x80000)); -		if (tbl->tces[i] == NULL) -			goto fail; -		memset(page_address(tbl->tces[i]), 0, 0x80000); - -		pr_debug(" TCE table %d at : %p\n", i, page_address(tbl->tces[i])); - -		/* Table size. We currently set it to be the whole 256M region */ -		tvt_data0 = 2ull << IODA_TVT0_TCE_TABLE_SIZE_SHIFT; -		/* IO page size set to 4K */ -		tvt_data1 = 1ull << IODA_TVT1_IO_PAGE_SIZE_SHIFT; -		/* Shift in the address */ -		tvt_data0 |= __pa(page_address(tbl->tces[i])) << IODA_TVT0_TTA_SHIFT; - -		/* Validation stuff. We only validate fully bus/dev/fn for now -		 * one day maybe we can group devices but that isn't the case -		 * at the moment -		 */ -		if (validate) { -			tvt_data0 |= IODA_TVT0_BUSNUM_VALID_MASK; -			tvt_data0 |= validate->bus->number; -			tvt_data1 |= IODA_TVT1_DEVNUM_VALID; -			tvt_data1 |= ((u64)PCI_SLOT(validate->devfn)) -				<< IODA_TVT1_DEVNUM_VALUE_SHIFT; -			tvt_data1 |= IODA_TVT1_FUNCNUM_VALID; -			tvt_data1 |= ((u64)PCI_FUNC(validate->devfn)) -				<< IODA_TVT1_FUNCNUM_VALUE_SHIFT; -		} - -		/* XX PE number is always 0 for now */ - -		/* Program the values using the PHB lock */ -		spin_lock_irqsave(&phb->lock, flags); -		out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR, -			 (tvt + i) | PCIE_REG_IODA_AD_TBL_TVT); -		out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, tvt_data1); -		out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, tvt_data0); -		spin_unlock_irqrestore(&phb->lock, flags); -	} - -	/* Init bits and pieces */ -	tbl->table.it_blocksize = 16; -	tbl->table.it_offset = addr >> IOMMU_PAGE_SHIFT; -	tbl->table.it_size = size >> IOMMU_PAGE_SHIFT; - -	/* -	 * It's already blank but we clear it anyway. -	 * Consider an aditiona interface that makes cleaing optional -	 */ -	iommu_init_table(&tbl->table, nid); - -	list_add(&tbl->link, &phb->dma_tables); -	return tbl; - - fail: -	pr_debug("  Failed to allocate a 256M TCE table !\n"); -	for (i = 0; i < tvts_per_table; i++) -		if (tbl->tces[i]) -			__free_pages(tbl->tces[i], get_order(0x80000)); -	kfree(tbl); -	return ERR_PTR(-ENOMEM); -} - -static void wsp_pci_dma_dev_setup(struct pci_dev *pdev) -{ -	struct dev_archdata *archdata = &pdev->dev.archdata; -	struct pci_controller *hose = pci_bus_to_host(pdev->bus); -	struct wsp_phb *phb = hose->private_data; -	struct wsp_dma_table *table = NULL; -	unsigned long flags; -	int i; - -	/* Don't assign an iommu table to a bridge */ -	if (pdev->hdr_type == PCI_HEADER_TYPE_BRIDGE) -		return; - -	pr_debug("%s: Setting up DMA...\n", pci_name(pdev)); - -	spin_lock_irqsave(&phb->lock, flags); - -	/* If only one region, check if it already exist */ -	if (phb->dma32_num_regions == 1) { -		spin_unlock_irqrestore(&phb->lock, flags); -		if (list_empty(&phb->dma_tables)) -			table = wsp_pci_create_dma32_table(phb, 0, NULL); -		else -			table = list_first_entry(&phb->dma_tables, -						 struct wsp_dma_table, -						 link); -	} else { -		/* else find a free region */ -		for (i = 0; i < phb->dma32_num_regions && !table; i++) { -			if (__test_and_set_bit(i, &phb->dma32_map)) -				continue; -			spin_unlock_irqrestore(&phb->lock, flags); -			table = wsp_pci_create_dma32_table(phb, i, pdev); -		} -	} - -	/* Check if we got an error */ -	if (IS_ERR(table)) { -		pr_err("%s: Failed to create DMA table, err %ld !\n", -		       pci_name(pdev), PTR_ERR(table)); -		return; -	} - -	/* Or a valid table */ -	if (table) { -		pr_info("%s: Setup iommu: 32-bit DMA region 0x%08lx..0x%08lx\n", -			pci_name(pdev), -			table->table.it_offset << IOMMU_PAGE_SHIFT, -			(table->table.it_offset << IOMMU_PAGE_SHIFT) -			+ phb->dma32_region_size - 1); -		archdata->dma_data.iommu_table_base = &table->table; -		return; -	} - -	/* Or no room */ -	spin_unlock_irqrestore(&phb->lock, flags); -	pr_err("%s: Out of DMA space !\n", pci_name(pdev)); -} - -static void __init wsp_pcie_configure_hw(struct pci_controller *hose) -{ -	u64 val; -	int i; - -#define DUMP_REG(x) \ -	pr_debug("%-30s : 0x%016llx\n", #x, in_be64(hose->cfg_data + x)) - -	/* -	 * Some WSP variants  has a bogus class code by default in the PCI-E -	 * root complex's built-in P2P bridge -	 */ -	val = in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1); -	pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", val); -	out_be64(hose->cfg_data + PCIE_REG_SYS_CFG1, -		 (val & ~PCIE_REG_SYS_CFG1_CLASS_CODE) | (PCI_CLASS_BRIDGE_PCI << 8)); -	pr_debug("PCI-E SYS_CFG1 : 0x%llx\n", in_be64(hose->cfg_data + PCIE_REG_SYS_CFG1)); - -#ifdef CONFIG_WSP_DD1_WORKAROUND_DD1_TCE_BUGS -	/* XXX Disable TCE caching, it doesn't work on DD1 */ -	out_be64(hose->cfg_data + 0xe50, -		 in_be64(hose->cfg_data + 0xe50) | (3ull << 62)); -	printk("PCI-E DEBUG CONTROL 5 = 0x%llx\n", in_be64(hose->cfg_data + 0xe50)); -#endif - -	/* Configure M32A and IO. IO is hard wired to be 1M for now */ -	out_be64(hose->cfg_data + PCIE_REG_IO_BASE_ADDR, hose->io_base_phys); -	out_be64(hose->cfg_data + PCIE_REG_IO_BASE_MASK, -		 (~(hose->io_resource.end - hose->io_resource.start)) & -		 0x3fffffff000ul); -	out_be64(hose->cfg_data + PCIE_REG_IO_START_ADDR, 0 | 1); - -	out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_ADDR, -		 hose->mem_resources[0].start); -	printk("Want to write to M32A_BASE_MASK : 0x%llx\n", -		 (~(hose->mem_resources[0].end - -		    hose->mem_resources[0].start)) & 0x3ffffff0000ul); -	out_be64(hose->cfg_data + PCIE_REG_M32A_BASE_MASK, -		 (~(hose->mem_resources[0].end - -		    hose->mem_resources[0].start)) & 0x3ffffff0000ul); -	out_be64(hose->cfg_data + PCIE_REG_M32A_START_ADDR, -		 (hose->mem_resources[0].start - hose->mem_offset[0]) | 1); - -	/* Clear all TVT entries -	 * -	 * XX Might get TVT count from device-tree -	 */ -	for (i = 0; i < IODA_TVT_COUNT; i++) { -		out_be64(hose->cfg_data + PCIE_REG_IODA_ADDR, -			 PCIE_REG_IODA_AD_TBL_TVT | i); -		out_be64(hose->cfg_data + PCIE_REG_IODA_DATA1, 0); -		out_be64(hose->cfg_data + PCIE_REG_IODA_DATA0, 0); -	} - -	/* Kill the TCE cache */ -	out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, -		 in_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG) | -		 PCIE_REG_PHBC_64B_TCE_EN); - -	/* Enable 32 & 64-bit MSIs, IO space and M32A */ -	val = PCIE_REG_PHBC_32BIT_MSI_EN | -	      PCIE_REG_PHBC_IO_EN | -	      PCIE_REG_PHBC_64BIT_MSI_EN | -	      PCIE_REG_PHBC_M32A_EN; -	if (iommu_is_off) -		val |= PCIE_REG_PHBC_DMA_XLATE_BYPASS; -	pr_debug("Will write config: 0x%llx\n", val); -	out_be64(hose->cfg_data + PCIE_REG_PHB_CONFIG, val); - -	/* Enable error reporting */ -	out_be64(hose->cfg_data + 0xe00, -		 in_be64(hose->cfg_data + 0xe00) | 0x0008000000000000ull); - -	/* Mask an error that's generated when doing config space probe -	 * -	 * XXX Maybe we should only mask it around config space cycles... that or -	 * ignore it when we know we had a config space cycle recently ? -	 */ -	out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS_MASK, 0x8000000000000000ull); -	out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS_MASK, 0x8000000000000000ull); - -	/* Enable UTL errors, for now, all of them got to UTL irq 1 -	 * -	 * We similarily mask one UTL error caused apparently during normal -	 * probing. We also mask the link up error -	 */ -	out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_ERR_SEV, 0); -	out_be64(hose->cfg_data + PCIE_UTL_RC_ERR_SEVERITY, 0); -	out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_ERROR_SEV, 0); -	out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_IRQ_EN, 0xffffffff00000000ull); -	out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_IRQ_EN, 0xff5fffff00000000ull); -	out_be64(hose->cfg_data + PCIE_UTL_EP_ERR_IRQ_EN, 0xffffffff00000000ull); - -	DUMP_REG(PCIE_REG_IO_BASE_ADDR); -	DUMP_REG(PCIE_REG_IO_BASE_MASK); -	DUMP_REG(PCIE_REG_IO_START_ADDR); -	DUMP_REG(PCIE_REG_M32A_BASE_ADDR); -	DUMP_REG(PCIE_REG_M32A_BASE_MASK); -	DUMP_REG(PCIE_REG_M32A_START_ADDR); -	DUMP_REG(PCIE_REG_M32B_BASE_ADDR); -	DUMP_REG(PCIE_REG_M32B_BASE_MASK); -	DUMP_REG(PCIE_REG_M32B_START_ADDR); -	DUMP_REG(PCIE_REG_M64_BASE_ADDR); -	DUMP_REG(PCIE_REG_M64_BASE_MASK); -	DUMP_REG(PCIE_REG_M64_START_ADDR); -	DUMP_REG(PCIE_REG_PHB_CONFIG); -} - -static void wsp_pci_wait_io_idle(struct wsp_phb *phb, unsigned long port) -{ -	u64 val; -	int i; - -	for (i = 0; i < 10000; i++) { -		val = in_be64(phb->hose->cfg_data + 0xe08); -		if ((val & 0x1900000000000000ull) == 0x0100000000000000ull) -			return; -		udelay(1); -	} -	pr_warning("PCI IO timeout on domain %d port 0x%lx\n", -		   phb->hose->global_number, port); -} - -#define DEF_PCI_AC_RET_pio(name, ret, at, al, aa)		\ -static ret wsp_pci_##name at					\ -{								\ -	struct iowa_bus *bus;					\ -	struct wsp_phb *phb;					\ -	unsigned long flags;					\ -	ret rval;						\ -	bus = iowa_pio_find_bus(aa);				\ -	WARN_ON(!bus);						\ -	phb = bus->private;					\ -	spin_lock_irqsave(&phb->lock, flags);			\ -	wsp_pci_wait_io_idle(phb, aa);				\ -	rval = __do_##name al;					\ -	spin_unlock_irqrestore(&phb->lock, flags);		\ -	return rval;						\ -} - -#define DEF_PCI_AC_NORET_pio(name, at, al, aa)			\ -static void wsp_pci_##name at					\ -{								\ -	struct iowa_bus *bus;					\ -	struct wsp_phb *phb;					\ -	unsigned long flags;					\ -	bus = iowa_pio_find_bus(aa);				\ -	WARN_ON(!bus);						\ -	phb = bus->private;					\ -	spin_lock_irqsave(&phb->lock, flags);			\ -	wsp_pci_wait_io_idle(phb, aa);				\ -	__do_##name al;						\ -	spin_unlock_irqrestore(&phb->lock, flags);		\ -} - -#define DEF_PCI_AC_RET_mem(name, ret, at, al, aa) -#define DEF_PCI_AC_NORET_mem(name, at, al, aa) - -#define DEF_PCI_AC_RET(name, ret, at, al, space, aa)		\ -	DEF_PCI_AC_RET_##space(name, ret, at, al, aa) - -#define DEF_PCI_AC_NORET(name, at, al, space, aa)		\ -	DEF_PCI_AC_NORET_##space(name, at, al, aa)		\ - - -#include <asm/io-defs.h> - -#undef DEF_PCI_AC_RET -#undef DEF_PCI_AC_NORET - -static struct ppc_pci_io wsp_pci_iops = { -	.inb = wsp_pci_inb, -	.inw = wsp_pci_inw, -	.inl = wsp_pci_inl, -	.outb = wsp_pci_outb, -	.outw = wsp_pci_outw, -	.outl = wsp_pci_outl, -	.insb = wsp_pci_insb, -	.insw = wsp_pci_insw, -	.insl = wsp_pci_insl, -	.outsb = wsp_pci_outsb, -	.outsw = wsp_pci_outsw, -	.outsl = wsp_pci_outsl, -}; - -static int __init wsp_setup_one_phb(struct device_node *np) -{ -	struct pci_controller *hose; -	struct wsp_phb *phb; - -	pr_info("PCI: Setting up PCIe host bridge 0x%s\n", np->full_name); - -	phb = zalloc_maybe_bootmem(sizeof(struct wsp_phb), GFP_KERNEL); -	if (!phb) -		return -ENOMEM; -	hose = pcibios_alloc_controller(np); -	if (!hose) { -		/* Can't really free the phb */ -		return -ENOMEM; -	} -	hose->private_data = phb; -	phb->hose = hose; - -	INIT_LIST_HEAD(&phb->dma_tables); -	spin_lock_init(&phb->lock); - -	/* XXX Use bus-range property ? */ -	hose->first_busno = 0; -	hose->last_busno = 0xff; - -	/* We use cfg_data as the address for the whole bridge MMIO space -	 */ -	hose->cfg_data = of_iomap(hose->dn, 0); - -	pr_debug("PCIe registers mapped at 0x%p\n", hose->cfg_data); - -	/* Get the ranges of the device-tree */ -	pci_process_bridge_OF_ranges(hose, np, 0); - -	/* XXX Force re-assigning of everything for now */ -	pci_add_flags(PCI_REASSIGN_ALL_BUS | PCI_REASSIGN_ALL_RSRC | -		      PCI_ENABLE_PROC_DOMAINS); - -	/* Calculate how the TCE space is divided */ -	phb->dma32_base		= 0; -	phb->dma32_num_regions	= NUM_DMA32_REGIONS; -	if (phb->dma32_num_regions > MAX_TABLE_TVT_COUNT) { -		pr_warning("IOMMU: Clamped to %d DMA32 regions\n", -			   MAX_TABLE_TVT_COUNT); -		phb->dma32_num_regions = MAX_TABLE_TVT_COUNT; -	} -	phb->dma32_region_size	= 0x80000000 / phb->dma32_num_regions; - -	BUG_ON(!is_power_of_2(phb->dma32_region_size)); - -	/* Setup config ops */ -	hose->ops = &wsp_pcie_pci_ops; - -	/* Configure the HW */ -	wsp_pcie_configure_hw(hose); - -	/* Instanciate IO workarounds */ -	iowa_register_bus(hose, &wsp_pci_iops, NULL, phb); -#ifdef CONFIG_PCI_MSI -	wsp_setup_phb_msi(hose); -#endif - -	/* Add to global list */ -	list_add(&phb->all, &wsp_phbs); - -	return 0; -} - -void __init wsp_setup_pci(void) -{ -	struct device_node *np; -	int rc; - -	/* Find host bridges */ -	for_each_compatible_node(np, "pciex", PCIE_COMPATIBLE) { -		rc = wsp_setup_one_phb(np); -		if (rc) -			pr_err("Failed to setup PCIe bridge %s, rc=%d\n", -			       np->full_name, rc); -	} - -	/* Establish device-tree linkage */ -	pci_devs_phb_init(); - -	/* Set DMA ops to use TCEs */ -	if (iommu_is_off) { -		pr_info("PCI-E: Disabled TCEs, using direct DMA\n"); -		set_pci_dma_ops(&dma_direct_ops); -	} else { -		ppc_md.pci_dma_dev_setup = wsp_pci_dma_dev_setup; -		ppc_md.tce_build = tce_build_wsp; -		ppc_md.tce_free = tce_free_wsp; -		set_pci_dma_ops(&dma_iommu_ops); -	} -} - -#define err_debug(fmt...)	pr_debug(fmt) -//#define err_debug(fmt...) - -static int __init wsp_pci_get_err_irq_no_dt(struct device_node *np) -{ -	const u32 *prop; -	int hw_irq; - -	/* Ok, no interrupts property, let's try to find our child P2P */ -	np = of_get_next_child(np, NULL); -	if (np == NULL) -		return 0; - -	/* Grab it's interrupt map */ -	prop = of_get_property(np, "interrupt-map", NULL); -	if (prop == NULL) -		return 0; - -	/* Grab one of the interrupts in there, keep the low 4 bits */ -	hw_irq = prop[5] & 0xf; - -	/* 0..4 for PHB 0 and 5..9 for PHB 1 */ -	if (hw_irq < 5) -		hw_irq = 4; -	else -		hw_irq = 9; -	hw_irq |= prop[5] & ~0xf; - -	err_debug("PCI: Using 0x%x as error IRQ for %s\n", -		  hw_irq, np->parent->full_name); -	return irq_create_mapping(NULL, hw_irq); -} - -static const struct { -	u32 offset; -	const char *name; -} wsp_pci_regs[] = { -#define DREG(x) { PCIE_REG_##x, #x } -#define DUTL(x) { PCIE_UTL_##x, "UTL_" #x } -	/* Architected registers except CONFIG_ and IODA -         * to avoid side effects -	 */ -	DREG(DMA_CHAN_STATUS), -	DREG(CPU_LOADSTORE_STATUS), -	DREG(LOCK0), -	DREG(LOCK1), -	DREG(PHB_CONFIG), -	DREG(IO_BASE_ADDR), -	DREG(IO_BASE_MASK), -	DREG(IO_START_ADDR), -	DREG(M32A_BASE_ADDR), -	DREG(M32A_BASE_MASK), -	DREG(M32A_START_ADDR), -	DREG(M32B_BASE_ADDR), -	DREG(M32B_BASE_MASK), -	DREG(M32B_START_ADDR), -	DREG(M64_BASE_ADDR), -	DREG(M64_BASE_MASK), -	DREG(M64_START_ADDR), -	DREG(TCE_KILL), -	DREG(LOCK2), -	DREG(PHB_GEN_CAP), -	DREG(PHB_TCE_CAP), -	DREG(PHB_IRQ_CAP), -	DREG(PHB_EEH_CAP), -	DREG(PAPR_ERR_INJ_CONTROL), -	DREG(PAPR_ERR_INJ_ADDR), -	DREG(PAPR_ERR_INJ_MASK), - -	/* UTL core regs */ -	DUTL(SYS_BUS_CONTROL), -	DUTL(STATUS), -	DUTL(SYS_BUS_AGENT_STATUS), -	DUTL(SYS_BUS_AGENT_ERR_SEV), -	DUTL(SYS_BUS_AGENT_IRQ_EN), -	DUTL(SYS_BUS_BURST_SZ_CONF), -	DUTL(REVISION_ID), -	DUTL(OUT_POST_HDR_BUF_ALLOC), -	DUTL(OUT_POST_DAT_BUF_ALLOC), -	DUTL(IN_POST_HDR_BUF_ALLOC), -	DUTL(IN_POST_DAT_BUF_ALLOC), -	DUTL(OUT_NP_BUF_ALLOC), -	DUTL(IN_NP_BUF_ALLOC), -	DUTL(PCIE_TAGS_ALLOC), -	DUTL(GBIF_READ_TAGS_ALLOC), - -	DUTL(PCIE_PORT_CONTROL), -	DUTL(PCIE_PORT_STATUS), -	DUTL(PCIE_PORT_ERROR_SEV), -	DUTL(PCIE_PORT_IRQ_EN), -	DUTL(RC_STATUS), -	DUTL(RC_ERR_SEVERITY), -	DUTL(RC_IRQ_EN), -	DUTL(EP_STATUS), -	DUTL(EP_ERR_SEVERITY), -	DUTL(EP_ERR_IRQ_EN), -	DUTL(PCI_PM_CTRL1), -	DUTL(PCI_PM_CTRL2), - -	/* PCIe stack regs */ -	DREG(SYSTEM_CONFIG1), -	DREG(SYSTEM_CONFIG2), -	DREG(EP_SYSTEM_CONFIG), -	DREG(EP_FLR), -	DREG(EP_BAR_CONFIG), -	DREG(LINK_CONFIG), -	DREG(PM_CONFIG), -	DREG(DLP_CONTROL), -	DREG(DLP_STATUS), -	DREG(ERR_REPORT_CONTROL), -	DREG(SLOT_CONTROL1), -	DREG(SLOT_CONTROL2), -	DREG(UTL_CONFIG), -	DREG(BUFFERS_CONFIG), -	DREG(ERROR_INJECT), -	DREG(SRIOV_CONFIG), -	DREG(PF0_SRIOV_STATUS), -	DREG(PF1_SRIOV_STATUS), -	DREG(PORT_NUMBER), -	DREG(POR_SYSTEM_CONFIG), - -	/* Internal logic regs */ -	DREG(PHB_VERSION), -	DREG(RESET), -	DREG(PHB_CONTROL), -	DREG(PHB_TIMEOUT_CONTROL1), -	DREG(PHB_QUIESCE_DMA), -	DREG(PHB_DMA_READ_TAG_ACTV), -	DREG(PHB_TCE_READ_TAG_ACTV), - -	/* FIR registers */ -	DREG(LEM_FIR_ACCUM), -	DREG(LEM_FIR_AND_MASK), -	DREG(LEM_FIR_OR_MASK), -	DREG(LEM_ACTION0), -	DREG(LEM_ACTION1), -	DREG(LEM_ERROR_MASK), -	DREG(LEM_ERROR_AND_MASK), -	DREG(LEM_ERROR_OR_MASK), - -	/* Error traps registers */ -	DREG(PHB_ERR_STATUS), -	DREG(PHB_ERR_STATUS), -	DREG(PHB_ERR1_STATUS), -	DREG(PHB_ERR_INJECT), -	DREG(PHB_ERR_LEM_ENABLE), -	DREG(PHB_ERR_IRQ_ENABLE), -	DREG(PHB_ERR_FREEZE_ENABLE), -	DREG(PHB_ERR_SIDE_ENABLE), -	DREG(PHB_ERR_LOG_0), -	DREG(PHB_ERR_LOG_1), -	DREG(PHB_ERR_STATUS_MASK), -	DREG(PHB_ERR1_STATUS_MASK), -	DREG(MMIO_ERR_STATUS), -	DREG(MMIO_ERR1_STATUS), -	DREG(MMIO_ERR_INJECT), -	DREG(MMIO_ERR_LEM_ENABLE), -	DREG(MMIO_ERR_IRQ_ENABLE), -	DREG(MMIO_ERR_FREEZE_ENABLE), -	DREG(MMIO_ERR_SIDE_ENABLE), -	DREG(MMIO_ERR_LOG_0), -	DREG(MMIO_ERR_LOG_1), -	DREG(MMIO_ERR_STATUS_MASK), -	DREG(MMIO_ERR1_STATUS_MASK), -	DREG(DMA_ERR_STATUS), -	DREG(DMA_ERR1_STATUS), -	DREG(DMA_ERR_INJECT), -	DREG(DMA_ERR_LEM_ENABLE), -	DREG(DMA_ERR_IRQ_ENABLE), -	DREG(DMA_ERR_FREEZE_ENABLE), -	DREG(DMA_ERR_SIDE_ENABLE), -	DREG(DMA_ERR_LOG_0), -	DREG(DMA_ERR_LOG_1), -	DREG(DMA_ERR_STATUS_MASK), -	DREG(DMA_ERR1_STATUS_MASK), - -	/* Debug and Trace registers */ -	DREG(PHB_DEBUG_CONTROL0), -	DREG(PHB_DEBUG_STATUS0), -	DREG(PHB_DEBUG_CONTROL1), -	DREG(PHB_DEBUG_STATUS1), -	DREG(PHB_DEBUG_CONTROL2), -	DREG(PHB_DEBUG_STATUS2), -	DREG(PHB_DEBUG_CONTROL3), -	DREG(PHB_DEBUG_STATUS3), -	DREG(PHB_DEBUG_CONTROL4), -	DREG(PHB_DEBUG_STATUS4), -	DREG(PHB_DEBUG_CONTROL5), -	DREG(PHB_DEBUG_STATUS5), - -	/* Don't seem to exist ... -	DREG(PHB_DEBUG_CONTROL6), -	DREG(PHB_DEBUG_STATUS6), -	*/ -}; - -static int wsp_pci_regs_show(struct seq_file *m, void *private) -{ -	struct wsp_phb *phb = m->private; -	struct pci_controller *hose = phb->hose; -	int i; - -	for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) { -		/* Skip write-only regs */ -		if (wsp_pci_regs[i].offset == 0xc08 || -		    wsp_pci_regs[i].offset == 0xc10 || -		    wsp_pci_regs[i].offset == 0xc38 || -		    wsp_pci_regs[i].offset == 0xc40) -			continue; -		seq_printf(m, "0x%03x: 0x%016llx %s\n", -			   wsp_pci_regs[i].offset, -			   in_be64(hose->cfg_data + wsp_pci_regs[i].offset), -			   wsp_pci_regs[i].name); -	} -	return 0; -} - -static int wsp_pci_regs_open(struct inode *inode, struct file *file) -{ -	return single_open(file, wsp_pci_regs_show, inode->i_private); -} - -static const struct file_operations wsp_pci_regs_fops = { -	.open = wsp_pci_regs_open, -	.read = seq_read, -	.llseek = seq_lseek, -	.release = single_release, -}; - -static int wsp_pci_reg_set(void *data, u64 val) -{ -	out_be64((void __iomem *)data, val); -	return 0; -} - -static int wsp_pci_reg_get(void *data, u64 *val) -{ -	*val = in_be64((void __iomem *)data); -	return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(wsp_pci_reg_fops, wsp_pci_reg_get, wsp_pci_reg_set, "0x%llx\n"); - -static irqreturn_t wsp_pci_err_irq(int irq, void *dev_id) -{ -	struct wsp_phb *phb = dev_id; -	struct pci_controller *hose = phb->hose; -	irqreturn_t handled = IRQ_NONE; -	struct wsp_pcie_err_log_data ed; - -	pr_err("PCI: Error interrupt on %s (PHB %d)\n", -	       hose->dn->full_name, hose->global_number); - again: -	memset(&ed, 0, sizeof(ed)); - -	/* Read and clear UTL errors */ -	ed.utl_sys_err = in_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS); -	if (ed.utl_sys_err) -		out_be64(hose->cfg_data + PCIE_UTL_SYS_BUS_AGENT_STATUS, ed.utl_sys_err); -	ed.utl_port_err = in_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS); -	if (ed.utl_port_err) -		out_be64(hose->cfg_data + PCIE_UTL_PCIE_PORT_STATUS, ed.utl_port_err); -	ed.utl_rc_err = in_be64(hose->cfg_data + PCIE_UTL_RC_STATUS); -	if (ed.utl_rc_err) -		out_be64(hose->cfg_data + PCIE_UTL_RC_STATUS, ed.utl_rc_err); - -	/* Read and clear main trap errors */ -	ed.phb_err = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS); -	if (ed.phb_err) { -		ed.phb_err1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS); -		ed.phb_log0 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_0); -		ed.phb_log1 = in_be64(hose->cfg_data + PCIE_REG_PHB_ERR_LOG_1); -		out_be64(hose->cfg_data + PCIE_REG_PHB_ERR1_STATUS, 0); -		out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_STATUS, 0); -	} -	ed.mmio_err = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS); -	if (ed.mmio_err) { -		ed.mmio_err1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS); -		ed.mmio_log0 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_0); -		ed.mmio_log1 = in_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_LOG_1); -		out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR1_STATUS, 0); -		out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_STATUS, 0); -	} -	ed.dma_err = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS); -	if (ed.dma_err) { -		ed.dma_err1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS); -		ed.dma_log0 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_0); -		ed.dma_log1 = in_be64(hose->cfg_data + PCIE_REG_DMA_ERR_LOG_1); -		out_be64(hose->cfg_data + PCIE_REG_DMA_ERR1_STATUS, 0); -		out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_STATUS, 0); -	} - -	/* Now print things out */ -	if (ed.phb_err) { -		pr_err("   PHB Error Status      : 0x%016llx\n", ed.phb_err); -		pr_err("   PHB First Error Status: 0x%016llx\n", ed.phb_err1); -		pr_err("   PHB Error Log 0       : 0x%016llx\n", ed.phb_log0); -		pr_err("   PHB Error Log 1       : 0x%016llx\n", ed.phb_log1); -	} -	if (ed.mmio_err) { -		pr_err("  MMIO Error Status      : 0x%016llx\n", ed.mmio_err); -		pr_err("  MMIO First Error Status: 0x%016llx\n", ed.mmio_err1); -		pr_err("  MMIO Error Log 0       : 0x%016llx\n", ed.mmio_log0); -		pr_err("  MMIO Error Log 1       : 0x%016llx\n", ed.mmio_log1); -	} -	if (ed.dma_err) { -		pr_err("   DMA Error Status      : 0x%016llx\n", ed.dma_err); -		pr_err("   DMA First Error Status: 0x%016llx\n", ed.dma_err1); -		pr_err("   DMA Error Log 0       : 0x%016llx\n", ed.dma_log0); -		pr_err("   DMA Error Log 1       : 0x%016llx\n", ed.dma_log1); -	} -	if (ed.utl_sys_err) -		pr_err("   UTL Sys Error Status  : 0x%016llx\n", ed.utl_sys_err); -	if (ed.utl_port_err) -		pr_err("   UTL Port Error Status : 0x%016llx\n", ed.utl_port_err); -	if (ed.utl_rc_err) -		pr_err("   UTL RC Error Status   : 0x%016llx\n", ed.utl_rc_err); - -	/* Interrupts are caused by the error traps. If we had any error there -	 * we loop again in case the UTL buffered some new stuff between -	 * going there and going to the traps -	 */ -	if (ed.dma_err || ed.mmio_err || ed.phb_err) { -		handled = IRQ_HANDLED; -		goto again; -	} -	return handled; -} - -static void __init wsp_setup_pci_err_reporting(struct wsp_phb *phb) -{ -	struct pci_controller *hose = phb->hose; -	int err_irq, i, rc; -	char fname[16]; - -	/* Create a debugfs file for that PHB */ -	sprintf(fname, "phb%d", phb->hose->global_number); -	phb->ddir = debugfs_create_dir(fname, powerpc_debugfs_root); - -	/* Some useful debug output */ -	if (phb->ddir) { -		struct dentry *d = debugfs_create_dir("regs", phb->ddir); -		char tmp[64]; - -		for (i = 0; i < ARRAY_SIZE(wsp_pci_regs); i++) { -			sprintf(tmp, "%03x_%s", wsp_pci_regs[i].offset, -				wsp_pci_regs[i].name); -			debugfs_create_file(tmp, 0600, d, -					    hose->cfg_data + wsp_pci_regs[i].offset, -					    &wsp_pci_reg_fops); -		} -		debugfs_create_file("all_regs", 0600, phb->ddir, phb, &wsp_pci_regs_fops); -	} - -	/* Find the IRQ number for that PHB */ -	err_irq = irq_of_parse_and_map(hose->dn, 0); -	if (err_irq == 0) -		/* XXX Error IRQ lacking from device-tree */ -		err_irq = wsp_pci_get_err_irq_no_dt(hose->dn); -	if (err_irq == 0) { -		pr_err("PCI: Failed to fetch error interrupt for %s\n", -		       hose->dn->full_name); -		return; -	} -	/* Request it */ -	rc = request_irq(err_irq, wsp_pci_err_irq, 0, "wsp_pci error", phb); -	if (rc) { -		pr_err("PCI: Failed to request interrupt for %s\n", -		       hose->dn->full_name); -	} -	/* Enable interrupts for all errors for now */ -	out_be64(hose->cfg_data + PCIE_REG_PHB_ERR_IRQ_ENABLE, 0xffffffffffffffffull); -	out_be64(hose->cfg_data + PCIE_REG_MMIO_ERR_IRQ_ENABLE, 0xffffffffffffffffull); -	out_be64(hose->cfg_data + PCIE_REG_DMA_ERR_IRQ_ENABLE, 0xffffffffffffffffull); -} - -/* - * This is called later to hookup with the error interrupt - */ -static int __init wsp_setup_pci_late(void) -{ -	struct wsp_phb *phb; - -	list_for_each_entry(phb, &wsp_phbs, all) -		wsp_setup_pci_err_reporting(phb); - -	return 0; -} -arch_initcall(wsp_setup_pci_late);  | 
