powerpc/powernv: PCI support for p7IOC under OPAL v2

This adds support for p7IOC (and possibly other IODA v1 IO Hubs)
using OPAL v2 interfaces.

We completely take over resource assignment and assign them using an
algorithm that hands out device BARs in a way that makes them fit in
individual segments of the M32 window of the bridge, which enables us
to assign individual PEs to devices and functions.

The current implementation gives out a PE per functions on PCIe, and a
PE for the entire bridge for PCIe to PCI-X bridges.

This can be adjusted / fine tuned later.

We also setup DMA resources (32-bit only for now) and MSIs (both 32-bit
and 64-bit MSI are supported).

The DMA allocation tries to divide the available 256M segments of the
32-bit DMA address space "fairly" among PEs. This is done using a
"weight" heuristic which assigns less value to things like OHCI USB
controllers than, for example SCSI RAID controllers. This algorithm
will probably want some fine tuning for specific devices or device
types.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
Benjamin Herrenschmidt 2011-11-15 17:29:08 +00:00
parent ca45cfe31e
commit 184cd4a3b9
6 changed files with 1434 additions and 6 deletions

View File

@ -153,8 +153,8 @@ struct pci_dn {
int pci_ext_config_space; /* for pci devices */
#ifdef CONFIG_EEH
struct pci_dev *pcidev; /* back-pointer to the pci device */
#ifdef CONFIG_EEH
int class_code; /* pci device class */
int eeh_mode; /* See eeh.h for possible EEH_MODEs */
int eeh_config_addr;
@ -164,6 +164,10 @@ struct pci_dn {
int eeh_false_positives; /* # times this device reported #ff's */
u32 config_space[16]; /* saved PCI config space */
#endif
#define IODA_INVALID_PE (-1)
#ifdef CONFIG_PPC_POWERNV
int pe_number;
#endif
};
/* Get the pointer to a device_node's pci_dn */

View File

@ -50,6 +50,9 @@ void * __devinit update_dn_pci_info(struct device_node *dn, void *data)
dn->data = pdn;
pdn->node = dn;
pdn->phb = phb;
#ifdef CONFIG_PPC_POWERNV
pdn->pe_number = IODA_INVALID_PE;
#endif
regs = of_get_property(dn, "reg", NULL);
if (regs) {
/* First register entry is addr (00BBSS00) */

View File

@ -2,4 +2,4 @@ obj-y += setup.o opal-takeover.o opal-wrappers.o opal.o
obj-y += opal-rtc.o opal-nvram.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o
obj-$(CONFIG_PCI) += pci.o pci-p5ioc2.o pci-ioda.o

File diff suppressed because it is too large Load Diff

View File

@ -467,12 +467,24 @@ void __init pnv_pci_init(void)
init_pci_config_tokens();
find_and_init_phbs();
#endif /* CONFIG_PPC_POWERNV_RTAS */
} else {
/* OPAL is here, do our normal stuff */
}
/* OPAL is here, do our normal stuff */
else {
int found_ioda = 0;
/* Look for IODA IO-Hubs. We don't support mixing IODA
* and p5ioc2 due to the need to change some global
* probing flags
*/
for_each_compatible_node(np, NULL, "ibm,ioda-hub") {
pnv_pci_init_ioda_hub(np);
found_ioda = 1;
}
/* Look for p5ioc2 IO-Hubs */
for_each_compatible_node(np, NULL, "ibm,p5ioc2")
pnv_pci_init_p5ioc2_hub(np);
if (!found_ioda)
for_each_compatible_node(np, NULL, "ibm,p5ioc2")
pnv_pci_init_p5ioc2_hub(np);
}
/* Setup the linkage between OF nodes and PHBs */

View File

@ -9,6 +9,50 @@ enum pnv_phb_type {
PNV_PHB_IODA2,
};
/* Data associated with a PE, including IOMMU tracking etc.. */
struct pnv_ioda_pe {
/* A PE can be associated with a single device or an
* entire bus (& children). In the former case, pdev
* is populated, in the later case, pbus is.
*/
struct pci_dev *pdev;
struct pci_bus *pbus;
/* Effective RID (device RID for a device PE and base bus
* RID with devfn 0 for a bus PE)
*/
unsigned int rid;
/* PE number */
unsigned int pe_number;
/* "Weight" assigned to the PE for the sake of DMA resource
* allocations
*/
unsigned int dma_weight;
/* This is a PCI-E -> PCI-X bridge, this points to the
* corresponding bus PE
*/
struct pnv_ioda_pe *bus_pe;
/* "Base" iommu table, ie, 4K TCEs, 32-bit DMA */
int tce32_seg;
int tce32_segcount;
struct iommu_table tce32_table;
/* XXX TODO: Add support for additional 64-bit iommus */
/* MSIs. MVE index is identical for for 32 and 64 bit MSI
* and -1 if not supported. (It's actually identical to the
* PE number)
*/
int mve_number;
/* Link in list of PE#s */
struct list_head link;
};
struct pnv_phb {
struct pci_controller *hose;
enum pnv_phb_type type;
@ -34,6 +78,45 @@ struct pnv_phb {
struct {
struct iommu_table iommu_table;
} p5ioc2;
struct {
/* Global bridge info */
unsigned int total_pe;
unsigned int m32_size;
unsigned int m32_segsize;
unsigned int m32_pci_base;
unsigned int io_size;
unsigned int io_segsize;
unsigned int io_pci_base;
/* PE allocation bitmap */
unsigned long *pe_alloc;
/* M32 & IO segment maps */
unsigned int *m32_segmap;
unsigned int *io_segmap;
struct pnv_ioda_pe *pe_array;
/* Reverse map of PEs, will have to extend if
* we are to support more than 256 PEs, indexed
* bus { bus, devfn }
*/
unsigned char pe_rmap[0x10000];
/* 32-bit TCE tables allocation */
unsigned long tce32_count;
/* Total "weight" for the sake of DMA resources
* allocation
*/
unsigned int dma_weight;
unsigned int dma_pe_count;
/* Sorted list of used PE's, sorted at
* boot for resource allocation purposes
*/
struct list_head pe_list;
} ioda;
};
};
@ -43,6 +126,7 @@ extern void pnv_pci_setup_iommu_table(struct iommu_table *tbl,
void *tce_mem, u64 tce_size,
u64 dma_offset);
extern void pnv_pci_init_p5ioc2_hub(struct device_node *np);
extern void pnv_pci_init_ioda_hub(struct device_node *np);
#endif /* __POWERNV_PCI_H */