powerpc/pci: Refactor pci_dn
Currently, the PCI config accessors are implemented based on device node. Unfortunately, SRIOV VFs won't have the corresponding device nodes. pci_dn will be used in replacement with device node for SRIOV VFs. So we have to use pci_dn in PCI config accessors. The patch refactors pci_dn in following aspects to make it ready to be used in PCI config accessors as we do in subsequent patch: * pci_dn is organized as a hierarchy tree. PCI device's pci_dn is put to the child list of pci_dn of its upstream bridge or PHB. VF's pci_dn will be put to the child list of pci_dn of PF's bridge. * For one particular PCI device (VF or not), its pci_dn can be found from pdev->dev.archdata.pci_data, PCI_DN(devnode), or parent's list. The fast path (fetching pci_dn through PCI device instance) is populated during early fixup time. [bhelgaas: changelog] Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
parent
12a89dbac7
commit
cca87d303c
|
@ -8,6 +8,9 @@
|
||||||
|
|
||||||
struct dma_map_ops;
|
struct dma_map_ops;
|
||||||
struct device_node;
|
struct device_node;
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
struct pci_dn;
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arch extensions to struct device.
|
* Arch extensions to struct device.
|
||||||
|
@ -34,6 +37,9 @@ struct dev_archdata {
|
||||||
#ifdef CONFIG_SWIOTLB
|
#ifdef CONFIG_SWIOTLB
|
||||||
dma_addr_t max_direct_dma_addr;
|
dma_addr_t max_direct_dma_addr;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef CONFIG_PPC64
|
||||||
|
struct pci_dn *pci_data;
|
||||||
|
#endif
|
||||||
#ifdef CONFIG_EEH
|
#ifdef CONFIG_EEH
|
||||||
struct eeh_dev *edev;
|
struct eeh_dev *edev;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -89,6 +89,7 @@ struct pci_controller {
|
||||||
|
|
||||||
#ifdef CONFIG_PPC64
|
#ifdef CONFIG_PPC64
|
||||||
unsigned long buid;
|
unsigned long buid;
|
||||||
|
struct pci_dn *pci_data;
|
||||||
#endif /* CONFIG_PPC64 */
|
#endif /* CONFIG_PPC64 */
|
||||||
|
|
||||||
void *private_data;
|
void *private_data;
|
||||||
|
@ -154,9 +155,12 @@ static inline int isa_vaddr_is_ioport(void __iomem *address)
|
||||||
struct iommu_table;
|
struct iommu_table;
|
||||||
|
|
||||||
struct pci_dn {
|
struct pci_dn {
|
||||||
|
int flags;
|
||||||
|
|
||||||
int busno; /* pci bus number */
|
int busno; /* pci bus number */
|
||||||
int devfn; /* pci device and function number */
|
int devfn; /* pci device and function number */
|
||||||
|
|
||||||
|
struct pci_dn *parent;
|
||||||
struct pci_controller *phb; /* for pci devices */
|
struct pci_controller *phb; /* for pci devices */
|
||||||
struct iommu_table *iommu_table; /* for phb's or bridges */
|
struct iommu_table *iommu_table; /* for phb's or bridges */
|
||||||
struct device_node *node; /* back-pointer to the device_node */
|
struct device_node *node; /* back-pointer to the device_node */
|
||||||
|
@ -171,14 +175,17 @@ struct pci_dn {
|
||||||
#ifdef CONFIG_PPC_POWERNV
|
#ifdef CONFIG_PPC_POWERNV
|
||||||
int pe_number;
|
int pe_number;
|
||||||
#endif
|
#endif
|
||||||
|
struct list_head child_list;
|
||||||
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Get the pointer to a device_node's pci_dn */
|
/* Get the pointer to a device_node's pci_dn */
|
||||||
#define PCI_DN(dn) ((struct pci_dn *) (dn)->data)
|
#define PCI_DN(dn) ((struct pci_dn *) (dn)->data)
|
||||||
|
|
||||||
|
extern struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
|
||||||
|
int devfn);
|
||||||
extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev);
|
extern struct pci_dn *pci_get_pdn(struct pci_dev *pdev);
|
||||||
|
extern void *update_dn_pci_info(struct device_node *dn, void *data);
|
||||||
extern void * update_dn_pci_info(struct device_node *dn, void *data);
|
|
||||||
|
|
||||||
static inline int pci_device_from_OF_node(struct device_node *np,
|
static inline int pci_device_from_OF_node(struct device_node *np,
|
||||||
u8 *bus, u8 *devfn)
|
u8 *bus, u8 *devfn)
|
||||||
|
|
|
@ -32,12 +32,108 @@
|
||||||
#include <asm/ppc-pci.h>
|
#include <asm/ppc-pci.h>
|
||||||
#include <asm/firmware.h>
|
#include <asm/firmware.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The function is used to find the firmware data of one
|
||||||
|
* specific PCI device, which is attached to the indicated
|
||||||
|
* PCI bus. For VFs, their firmware data is linked to that
|
||||||
|
* one of PF's bridge. For other devices, their firmware
|
||||||
|
* data is linked to that of their bridge.
|
||||||
|
*/
|
||||||
|
static struct pci_dn *pci_bus_to_pdn(struct pci_bus *bus)
|
||||||
|
{
|
||||||
|
struct pci_bus *pbus;
|
||||||
|
struct device_node *dn;
|
||||||
|
struct pci_dn *pdn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We probably have virtual bus which doesn't
|
||||||
|
* have associated bridge.
|
||||||
|
*/
|
||||||
|
pbus = bus;
|
||||||
|
while (pbus) {
|
||||||
|
if (pci_is_root_bus(pbus) || pbus->self)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pbus = pbus->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Except virtual bus, all PCI buses should
|
||||||
|
* have device nodes.
|
||||||
|
*/
|
||||||
|
dn = pci_bus_to_OF_node(pbus);
|
||||||
|
pdn = dn ? PCI_DN(dn) : NULL;
|
||||||
|
|
||||||
|
return pdn;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct pci_dn *pci_get_pdn_by_devfn(struct pci_bus *bus,
|
||||||
|
int devfn)
|
||||||
|
{
|
||||||
|
struct device_node *dn = NULL;
|
||||||
|
struct pci_dn *parent, *pdn;
|
||||||
|
struct pci_dev *pdev = NULL;
|
||||||
|
|
||||||
|
/* Fast path: fetch from PCI device */
|
||||||
|
list_for_each_entry(pdev, &bus->devices, bus_list) {
|
||||||
|
if (pdev->devfn == devfn) {
|
||||||
|
if (pdev->dev.archdata.pci_data)
|
||||||
|
return pdev->dev.archdata.pci_data;
|
||||||
|
|
||||||
|
dn = pci_device_to_OF_node(pdev);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fast path: fetch from device node */
|
||||||
|
pdn = dn ? PCI_DN(dn) : NULL;
|
||||||
|
if (pdn)
|
||||||
|
return pdn;
|
||||||
|
|
||||||
|
/* Slow path: fetch from firmware data hierarchy */
|
||||||
|
parent = pci_bus_to_pdn(bus);
|
||||||
|
if (!parent)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
list_for_each_entry(pdn, &parent->child_list, list) {
|
||||||
|
if (pdn->busno == bus->number &&
|
||||||
|
pdn->devfn == devfn)
|
||||||
|
return pdn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
|
struct pci_dn *pci_get_pdn(struct pci_dev *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *dn = pci_device_to_OF_node(pdev);
|
struct device_node *dn;
|
||||||
if (!dn)
|
struct pci_dn *parent, *pdn;
|
||||||
|
|
||||||
|
/* Search device directly */
|
||||||
|
if (pdev->dev.archdata.pci_data)
|
||||||
|
return pdev->dev.archdata.pci_data;
|
||||||
|
|
||||||
|
/* Check device node */
|
||||||
|
dn = pci_device_to_OF_node(pdev);
|
||||||
|
pdn = dn ? PCI_DN(dn) : NULL;
|
||||||
|
if (pdn)
|
||||||
|
return pdn;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VFs don't have device nodes. We hook their
|
||||||
|
* firmware data to PF's bridge.
|
||||||
|
*/
|
||||||
|
parent = pci_bus_to_pdn(pdev->bus);
|
||||||
|
if (!parent)
|
||||||
return NULL;
|
return NULL;
|
||||||
return PCI_DN(dn);
|
|
||||||
|
list_for_each_entry(pdn, &parent->child_list, list) {
|
||||||
|
if (pdn->busno == pdev->bus->number &&
|
||||||
|
pdn->devfn == pdev->devfn)
|
||||||
|
return pdn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -49,6 +145,7 @@ void *update_dn_pci_info(struct device_node *dn, void *data)
|
||||||
struct pci_controller *phb = data;
|
struct pci_controller *phb = data;
|
||||||
const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);
|
const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);
|
||||||
const __be32 *regs;
|
const __be32 *regs;
|
||||||
|
struct device_node *parent;
|
||||||
struct pci_dn *pdn;
|
struct pci_dn *pdn;
|
||||||
|
|
||||||
pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL);
|
pdn = zalloc_maybe_bootmem(sizeof(*pdn), GFP_KERNEL);
|
||||||
|
@ -70,6 +167,15 @@ void *update_dn_pci_info(struct device_node *dn, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1);
|
pdn->pci_ext_config_space = (type && of_read_number(type, 1) == 1);
|
||||||
|
|
||||||
|
/* Attach to parent node */
|
||||||
|
INIT_LIST_HEAD(&pdn->child_list);
|
||||||
|
INIT_LIST_HEAD(&pdn->list);
|
||||||
|
parent = of_get_parent(dn);
|
||||||
|
pdn->parent = parent ? PCI_DN(parent) : NULL;
|
||||||
|
if (pdn->parent)
|
||||||
|
list_add_tail(&pdn->list, &pdn->parent->child_list);
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,8 +253,11 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)
|
||||||
/* PHB nodes themselves must not match */
|
/* PHB nodes themselves must not match */
|
||||||
update_dn_pci_info(dn, phb);
|
update_dn_pci_info(dn, phb);
|
||||||
pdn = dn->data;
|
pdn = dn->data;
|
||||||
if (pdn)
|
if (pdn) {
|
||||||
pdn->devfn = pdn->busno = -1;
|
pdn->devfn = pdn->busno = -1;
|
||||||
|
pdn->phb = phb;
|
||||||
|
phb->pci_data = pdn;
|
||||||
|
}
|
||||||
|
|
||||||
/* Update dn->phb ptrs for new phb and children devices */
|
/* Update dn->phb ptrs for new phb and children devices */
|
||||||
traverse_pci_devices(dn, update_dn_pci_info, phb);
|
traverse_pci_devices(dn, update_dn_pci_info, phb);
|
||||||
|
@ -171,3 +280,16 @@ void __init pci_devs_phb_init(void)
|
||||||
list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
|
list_for_each_entry_safe(phb, tmp, &hose_list, list_node)
|
||||||
pci_devs_phb_init_dynamic(phb);
|
pci_devs_phb_init_dynamic(phb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void pci_dev_pdn_setup(struct pci_dev *pdev)
|
||||||
|
{
|
||||||
|
struct pci_dn *pdn;
|
||||||
|
|
||||||
|
if (pdev->dev.archdata.pci_data)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Setup the fast path */
|
||||||
|
pdn = pci_get_pdn(pdev);
|
||||||
|
pdev->dev.archdata.pci_data = pdn;
|
||||||
|
}
|
||||||
|
DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pci_dev_pdn_setup);
|
||||||
|
|
Loading…
Reference in New Issue