diff options
| author | Gavin Shan | 2015-03-17 06:15:02 +0100 | 
|---|---|---|
| committer | Benjamin Herrenschmidt | 2015-03-24 03:15:49 +0100 | 
| commit | cca87d303c85b257a7b0fd34f9d6fce1c59880a2 (patch) | |
| tree | 4145eab3e328314e60b9dcc3590616a93aa7bb90 /arch/powerpc | |
| parent | net/ethernet/sfc: Don't use of_node_to_eeh_dev() (diff) | |
| download | kernel-qcow2-linux-cca87d303c85b257a7b0fd34f9d6fce1c59880a2.tar.gz kernel-qcow2-linux-cca87d303c85b257a7b0fd34f9d6fce1c59880a2.tar.xz kernel-qcow2-linux-cca87d303c85b257a7b0fd34f9d6fce1c59880a2.zip | |
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>
Diffstat (limited to 'arch/powerpc')
| -rw-r--r-- | arch/powerpc/include/asm/device.h | 6 | ||||
| -rw-r--r-- | arch/powerpc/include/asm/pci-bridge.h | 11 | ||||
| -rw-r--r-- | arch/powerpc/kernel/pci_dn.c | 130 | 
3 files changed, 141 insertions, 6 deletions
| diff --git a/arch/powerpc/include/asm/device.h b/arch/powerpc/include/asm/device.h index 38faeded7d59..9f1371bab5fc 100644 --- a/arch/powerpc/include/asm/device.h +++ b/arch/powerpc/include/asm/device.h @@ -8,6 +8,9 @@  struct dma_map_ops;  struct device_node; +#ifdef CONFIG_PPC64 +struct pci_dn; +#endif  /*   * Arch extensions to struct device. @@ -34,6 +37,9 @@ struct dev_archdata {  #ifdef CONFIG_SWIOTLB  	dma_addr_t		max_direct_dma_addr;  #endif +#ifdef CONFIG_PPC64 +	struct pci_dn		*pci_data; +#endif  #ifdef CONFIG_EEH  	struct eeh_dev		*edev;  #endif diff --git a/arch/powerpc/include/asm/pci-bridge.h b/arch/powerpc/include/asm/pci-bridge.h index 546d036fe925..706710b571c3 100644 --- a/arch/powerpc/include/asm/pci-bridge.h +++ b/arch/powerpc/include/asm/pci-bridge.h @@ -89,6 +89,7 @@ struct pci_controller {  #ifdef CONFIG_PPC64  	unsigned long buid; +	struct pci_dn *pci_data;  #endif	/* CONFIG_PPC64 */  	void *private_data; @@ -154,9 +155,12 @@ static inline int isa_vaddr_is_ioport(void __iomem *address)  struct iommu_table;  struct pci_dn { +	int     flags; +  	int	busno;			/* pci bus number */  	int	devfn;			/* pci device and function number */ +	struct  pci_dn *parent;  	struct  pci_controller *phb;	/* for pci devices */  	struct	iommu_table *iommu_table;	/* for phb's or bridges */  	struct	device_node *node;	/* back-pointer to the device_node */ @@ -171,14 +175,17 @@ struct pci_dn {  #ifdef CONFIG_PPC_POWERNV  	int	pe_number;  #endif +	struct list_head child_list; +	struct list_head list;  };  /* Get the pointer to a device_node's pci_dn */  #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 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,  					  u8 *bus, u8 *devfn) diff --git a/arch/powerpc/kernel/pci_dn.c b/arch/powerpc/kernel/pci_dn.c index 83df3075d3df..0ab2dadaf842 100644 --- a/arch/powerpc/kernel/pci_dn.c +++ b/arch/powerpc/kernel/pci_dn.c @@ -32,12 +32,108 @@  #include <asm/ppc-pci.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 device_node *dn = pci_device_to_OF_node(pdev); -	if (!dn) +	struct device_node *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 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;  	const __be32 *type = of_get_property(dn, "ibm,pci-config-space-type", NULL);  	const __be32 *regs; +	struct device_node *parent;  	struct pci_dn *pdn;  	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); + +	/* 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;  } @@ -147,8 +253,11 @@ void pci_devs_phb_init_dynamic(struct pci_controller *phb)  	/* PHB nodes themselves must not match */  	update_dn_pci_info(dn, phb);  	pdn = dn->data; -	if (pdn) +	if (pdn) {  		pdn->devfn = pdn->busno = -1; +		pdn->phb = phb; +		phb->pci_data = pdn; +	}  	/* Update dn->phb ptrs for new phb and children devices */  	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)  		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); | 
