mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-05 01:54:09 +08:00
pci/of: Match PCI devices to OF nodes dynamically
powerpc has two different ways of matching PCI devices to their corresponding OF node (if any) for historical reasons. The ppc64 one does a scan looking for matching bus/dev/fn, while the ppc32 one does a scan looking only for matching dev/fn on each level in order to be agnostic to busses being renumbered (which Linux does on some platforms). This removes both and instead moves the matching code to the PCI core itself. It's the most logical place to do it: when a pci_dev is created, we know the parent and thus can do a single level scan for the matching device_node (if any). The benefit is that all archs now get the matching for free. There's one hook the arch might want to provide to match a PHB bus to its device node. A default weak implementation is provided that looks for the parent device device node, but it's not entirely reliable on powerpc for various reasons so powerpc provides its own. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Acked-by: Michal Simek <monstr@monstr.eu> Acked-by: Jesse Barnes <jbarnes@virtuousgeek.org>
This commit is contained in:
parent
1fa7b6a29c
commit
98d9f30c82
@ -105,19 +105,19 @@ struct pci_controller {
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
|
||||
static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev)
|
||||
{
|
||||
return bus->sysdata;
|
||||
return dev->dev.of_node;
|
||||
}
|
||||
|
||||
static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_controller *host;
|
||||
return bus->dev.of_node;
|
||||
}
|
||||
|
||||
if (bus->self)
|
||||
return pci_device_to_OF_node(bus->self);
|
||||
host = pci_bus_to_host(bus);
|
||||
return host ? host->dn : NULL;
|
||||
static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
|
||||
{
|
||||
return bus->sysdata;
|
||||
}
|
||||
|
||||
static inline int isa_vaddr_is_ioport(void __iomem *address)
|
||||
|
@ -29,21 +29,6 @@
|
||||
extern int early_uartlite_console(void);
|
||||
extern int early_uart16550_console(void);
|
||||
|
||||
#ifdef CONFIG_PCI
|
||||
/*
|
||||
* PCI <-> OF matching functions
|
||||
* (XXX should these be here?)
|
||||
*/
|
||||
struct pci_bus;
|
||||
struct pci_dev;
|
||||
extern int pci_device_from_OF_node(struct device_node *node,
|
||||
u8 *bus, u8 *devfn);
|
||||
extern struct device_node *pci_busdev_to_OF_node(struct pci_bus *bus,
|
||||
int devfn);
|
||||
extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev);
|
||||
extern void pci_create_OF_bus_map(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OF address retreival & translation
|
||||
*/
|
||||
|
@ -210,38 +210,6 @@ static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus)
|
||||
return np;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scans the OF tree for a device node matching a PCI device
|
||||
*/
|
||||
struct device_node *
|
||||
pci_busdev_to_OF_node(struct pci_bus *bus, int devfn)
|
||||
{
|
||||
struct device_node *parent, *np;
|
||||
|
||||
pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn);
|
||||
parent = scan_OF_for_pci_bus(bus);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
pr_debug(" parent is %s\n", parent ? parent->full_name : "<NULL>");
|
||||
np = scan_OF_for_pci_dev(parent, devfn);
|
||||
of_node_put(parent);
|
||||
pr_debug(" result is %s\n", np ? np->full_name : "<NULL>");
|
||||
|
||||
/* XXX most callers don't release the returned node
|
||||
* mostly because ppc64 doesn't increase the refcount,
|
||||
* we need to fix that.
|
||||
*/
|
||||
return np;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_busdev_to_OF_node);
|
||||
|
||||
struct device_node*
|
||||
pci_device_to_OF_node(struct pci_dev *dev)
|
||||
{
|
||||
return pci_busdev_to_OF_node(dev->bus, dev->devfn);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_device_to_OF_node);
|
||||
|
||||
static int
|
||||
find_OF_pci_device_filter(struct device_node *node, void *data)
|
||||
{
|
||||
@ -315,6 +283,13 @@ pci_create_OF_bus_map(void)
|
||||
}
|
||||
}
|
||||
|
||||
struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_controller *hose = bus->sysdata;
|
||||
|
||||
return of_node_get(hose->dn);
|
||||
}
|
||||
|
||||
static void __devinit pcibios_scan_phb(struct pci_controller *hose)
|
||||
{
|
||||
struct pci_bus *bus;
|
||||
@ -332,7 +307,6 @@ static void __devinit pcibios_scan_phb(struct pci_controller *hose)
|
||||
hose->global_number);
|
||||
return;
|
||||
}
|
||||
bus.dev->of_node = of_node_get(node);
|
||||
bus->secondary = hose->first_busno;
|
||||
hose->bus = bus;
|
||||
|
||||
|
@ -169,18 +169,22 @@ static inline struct pci_controller *pci_bus_to_host(const struct pci_bus *bus)
|
||||
return bus->sysdata;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PPC64
|
||||
static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev)
|
||||
{
|
||||
return dev->dev.of_node;
|
||||
}
|
||||
|
||||
static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_controller *host;
|
||||
|
||||
if (bus->self)
|
||||
return pci_device_to_OF_node(bus->self);
|
||||
host = pci_bus_to_host(bus);
|
||||
return host ? host->dn : NULL;
|
||||
return bus->dev.of_node;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_PPC64
|
||||
|
||||
extern int pci_device_from_OF_node(struct device_node *node,
|
||||
u8 *bus, u8 *devfn);
|
||||
extern void pci_create_OF_bus_map(void);
|
||||
|
||||
static inline int isa_vaddr_is_ioport(void __iomem *address)
|
||||
{
|
||||
/* No specific ISA handling on ppc32 at this stage, it
|
||||
@ -223,17 +227,8 @@ struct pci_dn {
|
||||
/* Get the pointer to a device_node's pci_dn */
|
||||
#define PCI_DN(dn) ((struct pci_dn *) (dn)->data)
|
||||
|
||||
extern struct device_node *fetch_dev_dn(struct pci_dev *dev);
|
||||
extern void * update_dn_pci_info(struct device_node *dn, void *data);
|
||||
|
||||
/* Get a device_node from a pci_dev. This code must be fast except
|
||||
* in the case where the sysdata is incorrect and needs to be fixed
|
||||
* up (this will only happen once). */
|
||||
static inline struct device_node *pci_device_to_OF_node(struct pci_dev *dev)
|
||||
{
|
||||
return dev->dev.of_node ? dev->dev.of_node : fetch_dev_dn(dev);
|
||||
}
|
||||
|
||||
static inline int pci_device_from_OF_node(struct device_node *np,
|
||||
u8 *bus, u8 *devfn)
|
||||
{
|
||||
@ -244,14 +239,6 @@ static inline int pci_device_from_OF_node(struct device_node *np,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct device_node *pci_bus_to_OF_node(struct pci_bus *bus)
|
||||
{
|
||||
if (bus->self)
|
||||
return pci_device_to_OF_node(bus->self);
|
||||
else
|
||||
return bus->dev.of_node; /* Must be root bus (PHB) */
|
||||
}
|
||||
|
||||
/** Find the bus corresponding to the indicated device node */
|
||||
extern struct pci_bus *pcibios_find_pci_bus(struct device_node *dn);
|
||||
|
||||
|
@ -179,8 +179,7 @@ extern int remove_phb_dynamic(struct pci_controller *phb);
|
||||
extern struct pci_dev *of_create_pci_dev(struct device_node *node,
|
||||
struct pci_bus *bus, int devfn);
|
||||
|
||||
extern void of_scan_pci_bridge(struct device_node *node,
|
||||
struct pci_dev *dev);
|
||||
extern void of_scan_pci_bridge(struct pci_dev *dev);
|
||||
|
||||
extern void of_scan_bus(struct device_node *node, struct pci_bus *bus);
|
||||
extern void of_rescan_bus(struct device_node *node, struct pci_bus *bus);
|
||||
|
@ -22,20 +22,6 @@
|
||||
|
||||
#define HAVE_ARCH_DEVTREE_FIXUPS
|
||||
|
||||
#ifdef CONFIG_PPC32
|
||||
/*
|
||||
* PCI <-> OF matching functions
|
||||
* (XXX should these be here?)
|
||||
*/
|
||||
struct pci_bus;
|
||||
struct pci_dev;
|
||||
extern int pci_device_from_OF_node(struct device_node *node,
|
||||
u8* bus, u8* devfn);
|
||||
extern struct device_node* pci_busdev_to_OF_node(struct pci_bus *, int);
|
||||
extern struct device_node* pci_device_to_OF_node(struct pci_dev *);
|
||||
extern void pci_create_OF_bus_map(void);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* OF address retreival & translation
|
||||
*/
|
||||
|
@ -1097,9 +1097,6 @@ void __devinit pcibios_setup_bus_devices(struct pci_bus *bus)
|
||||
if (dev->is_added)
|
||||
continue;
|
||||
|
||||
/* Setup OF node pointer in the device */
|
||||
dev->dev.of_node = pci_device_to_OF_node(dev);
|
||||
|
||||
/* Fixup NUMA node as it may not be setup yet by the generic
|
||||
* code and is needed by the DMA init
|
||||
*/
|
||||
@ -1685,6 +1682,13 @@ int early_find_capability(struct pci_controller *hose, int bus, int devfn,
|
||||
return pci_bus_find_capability(fake_pci_bus(hose, bus), devfn, cap);
|
||||
}
|
||||
|
||||
struct device_node *pcibios_get_phb_of_node(struct pci_bus *bus)
|
||||
{
|
||||
struct pci_controller *hose = bus->sysdata;
|
||||
|
||||
return of_node_get(hose->dn);
|
||||
}
|
||||
|
||||
/**
|
||||
* pci_scan_phb - Given a pci_controller, setup and scan the PCI bus
|
||||
* @hose: Pointer to the PCI host controller instance structure
|
||||
@ -1705,7 +1709,6 @@ void __devinit pcibios_scan_phb(struct pci_controller *hose)
|
||||
hose->global_number);
|
||||
return;
|
||||
}
|
||||
bus->dev.of_node = of_node_get(node);
|
||||
bus->secondary = hose->first_busno;
|
||||
hose->bus = bus;
|
||||
|
||||
|
@ -167,150 +167,26 @@ pcibios_make_OF_bus_map(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
typedef int (*pci_OF_scan_iterator)(struct device_node* node, void* data);
|
||||
|
||||
static struct device_node*
|
||||
scan_OF_pci_childs(struct device_node *parent, pci_OF_scan_iterator filter, void* data)
|
||||
{
|
||||
struct device_node *node;
|
||||
struct device_node* sub_node;
|
||||
|
||||
for_each_child_of_node(parent, node) {
|
||||
const unsigned int *class_code;
|
||||
|
||||
if (filter(node, data)) {
|
||||
of_node_put(node);
|
||||
return node;
|
||||
}
|
||||
|
||||
/* For PCI<->PCI bridges or CardBus bridges, we go down
|
||||
* Note: some OFs create a parent node "multifunc-device" as
|
||||
* a fake root for all functions of a multi-function device,
|
||||
* we go down them as well.
|
||||
*/
|
||||
class_code = of_get_property(node, "class-code", NULL);
|
||||
if ((!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI &&
|
||||
(*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) &&
|
||||
strcmp(node->name, "multifunc-device"))
|
||||
continue;
|
||||
sub_node = scan_OF_pci_childs(node, filter, data);
|
||||
if (sub_node) {
|
||||
of_node_put(node);
|
||||
return sub_node;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct device_node *scan_OF_for_pci_dev(struct device_node *parent,
|
||||
unsigned int devfn)
|
||||
{
|
||||
struct device_node *np, *cnp;
|
||||
const u32 *reg;
|
||||
unsigned int psize;
|
||||
|
||||
for_each_child_of_node(parent, np) {
|
||||
reg = of_get_property(np, "reg", &psize);
|
||||
if (reg && psize >= 4 && ((reg[0] >> 8) & 0xff) == devfn)
|
||||
return np;
|
||||
|
||||
/* Note: some OFs create a parent node "multifunc-device" as
|
||||
* a fake root for all functions of a multi-function device,
|
||||
* we go down them as well. */
|
||||
if (!strcmp(np->name, "multifunc-device")) {
|
||||
cnp = scan_OF_for_pci_dev(np, devfn);
|
||||
if (cnp)
|
||||
return cnp;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static struct device_node *scan_OF_for_pci_bus(struct pci_bus *bus)
|
||||
{
|
||||
struct device_node *parent, *np;
|
||||
|
||||
/* Are we a root bus ? */
|
||||
if (bus->self == NULL || bus->parent == NULL) {
|
||||
struct pci_controller *hose = pci_bus_to_host(bus);
|
||||
if (hose == NULL)
|
||||
return NULL;
|
||||
return of_node_get(hose->dn);
|
||||
}
|
||||
|
||||
/* not a root bus, we need to get our parent */
|
||||
parent = scan_OF_for_pci_bus(bus->parent);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
|
||||
/* now iterate for children for a match */
|
||||
np = scan_OF_for_pci_dev(parent, bus->self->devfn);
|
||||
of_node_put(parent);
|
||||
|
||||
return np;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scans the OF tree for a device node matching a PCI device
|
||||
*/
|
||||
struct device_node *
|
||||
pci_busdev_to_OF_node(struct pci_bus *bus, int devfn)
|
||||
{
|
||||
struct device_node *parent, *np;
|
||||
|
||||
pr_debug("pci_busdev_to_OF_node(%d,0x%x)\n", bus->number, devfn);
|
||||
parent = scan_OF_for_pci_bus(bus);
|
||||
if (parent == NULL)
|
||||
return NULL;
|
||||
pr_debug(" parent is %s\n", parent ? parent->full_name : "<NULL>");
|
||||
np = scan_OF_for_pci_dev(parent, devfn);
|
||||
of_node_put(parent);
|
||||
pr_debug(" result is %s\n", np ? np->full_name : "<NULL>");
|
||||
|
||||
/* XXX most callers don't release the returned node
|
||||
* mostly because ppc64 doesn't increase the refcount,
|
||||
* we need to fix that.
|
||||
*/
|
||||
return np;
|
||||
}
|
||||
EXPORT_SYMBOL(pci_busdev_to_OF_node);
|
||||
|
||||
struct device_node*
|
||||
pci_device_to_OF_node(struct pci_dev *dev)
|
||||
{
|
||||
return pci_busdev_to_OF_node(dev->bus, dev->devfn);
|
||||
}
|
||||
EXPORT_SYMBOL(pci_device_to_OF_node);
|
||||
|
||||
static int
|
||||
find_OF_pci_device_filter(struct device_node* node, void* data)
|
||||
{
|
||||
return ((void *)node == data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the PCI device matching a given OF node
|
||||
*/
|
||||
int
|
||||
pci_device_from_OF_node(struct device_node* node, u8* bus, u8* devfn)
|
||||
int pci_device_from_OF_node(struct device_node *node, u8 *bus, u8 *devfn)
|
||||
{
|
||||
const unsigned int *reg;
|
||||
struct pci_controller* hose;
|
||||
struct pci_dev* dev = NULL;
|
||||
|
||||
/* Make sure it's really a PCI device */
|
||||
hose = pci_find_hose_for_OF_device(node);
|
||||
if (!hose || !hose->dn)
|
||||
struct pci_dev *dev = NULL;
|
||||
const __be32 *reg;
|
||||
int size;
|
||||
|
||||
/* Check if it might have a chance to be a PCI device */
|
||||
if (!pci_find_hose_for_OF_device(node))
|
||||
return -ENODEV;
|
||||
if (!scan_OF_pci_childs(hose->dn,
|
||||
find_OF_pci_device_filter, (void *)node))
|
||||
|
||||
reg = of_get_property(node, "reg", &size);
|
||||
if (!reg || size < 5 * sizeof(u32))
|
||||
return -ENODEV;
|
||||
reg = of_get_property(node, "reg", NULL);
|
||||
if (!reg)
|
||||
return -ENODEV;
|
||||
*bus = (reg[0] >> 16) & 0xff;
|
||||
*devfn = ((reg[0] >> 8) & 0xff);
|
||||
|
||||
*bus = (be32_to_cpup(®[0]) >> 16) & 0xff;
|
||||
*devfn = (be32_to_cpup(®[0]) >> 8) & 0xff;
|
||||
|
||||
/* Ok, here we need some tweak. If we have already renumbered
|
||||
* all busses, we can't rely on the OF bus number any more.
|
||||
|
@ -142,53 +142,6 @@ void __devinit pci_devs_phb_init_dynamic(struct pci_controller *phb)
|
||||
traverse_pci_devices(dn, update_dn_pci_info, phb);
|
||||
}
|
||||
|
||||
/*
|
||||
* Traversal func that looks for a <busno,devfcn> value.
|
||||
* If found, the pci_dn is returned (thus terminating the traversal).
|
||||
*/
|
||||
static void *is_devfn_node(struct device_node *dn, void *data)
|
||||
{
|
||||
int busno = ((unsigned long)data >> 8) & 0xff;
|
||||
int devfn = ((unsigned long)data) & 0xff;
|
||||
struct pci_dn *pci = dn->data;
|
||||
|
||||
if (pci && (devfn == pci->devfn) && (busno == pci->busno))
|
||||
return dn;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the "slow" path for looking up a device_node from a
|
||||
* pci_dev. It will hunt for the device under its parent's
|
||||
* phb and then update of_node pointer.
|
||||
*
|
||||
* It may also do fixups on the actual device since this happens
|
||||
* on the first read/write.
|
||||
*
|
||||
* Note that it also must deal with devices that don't exist.
|
||||
* In this case it may probe for real hardware ("just in case")
|
||||
* and add a device_node to the device tree if necessary.
|
||||
*
|
||||
* Is this function necessary anymore now that dev->dev.of_node is
|
||||
* used to store the node pointer?
|
||||
*
|
||||
*/
|
||||
struct device_node *fetch_dev_dn(struct pci_dev *dev)
|
||||
{
|
||||
struct pci_controller *phb = dev->sysdata;
|
||||
struct device_node *dn;
|
||||
unsigned long searchval = (dev->bus->number << 8) | dev->devfn;
|
||||
|
||||
if (WARN_ON(!phb))
|
||||
return NULL;
|
||||
|
||||
dn = traverse_pci_devices(phb->dn, is_devfn_node, (void *)searchval);
|
||||
if (dn)
|
||||
dev->dev.of_node = dn;
|
||||
return dn;
|
||||
}
|
||||
EXPORT_SYMBOL(fetch_dev_dn);
|
||||
|
||||
/**
|
||||
* pci_devs_phb_init - Initialize phbs and pci devs under them.
|
||||
*
|
||||
|
@ -202,9 +202,9 @@ EXPORT_SYMBOL(of_create_pci_dev);
|
||||
* this routine in turn call of_scan_bus() recusively to scan for more child
|
||||
* devices.
|
||||
*/
|
||||
void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
struct pci_dev *dev)
|
||||
void __devinit of_scan_pci_bridge(struct pci_dev *dev)
|
||||
{
|
||||
struct device_node *node = dev->dev.of_node;
|
||||
struct pci_bus *bus;
|
||||
const u32 *busrange, *ranges;
|
||||
int len, i, mode;
|
||||
@ -238,7 +238,6 @@ void __devinit of_scan_pci_bridge(struct device_node *node,
|
||||
bus->primary = dev->bus->number;
|
||||
bus->subordinate = busrange[1];
|
||||
bus->bridge_ctl = 0;
|
||||
bus->dev.of_node = of_node_get(node);
|
||||
|
||||
/* parse ranges property */
|
||||
/* PCI #address-cells == 3 and #size-cells == 2 always */
|
||||
@ -335,9 +334,7 @@ static void __devinit __of_scan_bus(struct device_node *node,
|
||||
list_for_each_entry(dev, &bus->devices, bus_list) {
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
||||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) {
|
||||
struct device_node *child = pci_device_to_OF_node(dev);
|
||||
if (child)
|
||||
of_scan_pci_bridge(child, dev);
|
||||
of_scan_pci_bridge(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/of_pci.h>
|
||||
|
||||
#include <asm/sections.h>
|
||||
#include <asm/io.h>
|
||||
@ -235,7 +236,7 @@ static int chaos_validate_dev(struct pci_bus *bus, int devfn, int offset)
|
||||
|
||||
if (offset >= 0x100)
|
||||
return PCIBIOS_BAD_REGISTER_NUMBER;
|
||||
np = pci_busdev_to_OF_node(bus, devfn);
|
||||
np = of_pci_find_child_device(bus->dev.of_node, devfn);
|
||||
if (np == NULL)
|
||||
return PCIBIOS_DEVICE_NOT_FOUND;
|
||||
|
||||
|
@ -284,7 +284,7 @@ static struct pci_dev *of_create_pci_dev(struct pci_pbm_info *pbm,
|
||||
dev->sysdata = node;
|
||||
dev->dev.parent = bus->bridge;
|
||||
dev->dev.bus = &pci_bus_type;
|
||||
dev->dev.of_node = node;
|
||||
dev->dev.of_node = of_node_get(node);
|
||||
dev->devfn = devfn;
|
||||
dev->multifunction = 0; /* maybe a lie? */
|
||||
set_pcie_port_type(dev);
|
||||
|
@ -71,8 +71,14 @@ config OF_MDIO
|
||||
|
||||
config OF_PCI
|
||||
def_tristate PCI
|
||||
depends on PCI && (PPC || MICROBLAZE || X86)
|
||||
depends on PCI
|
||||
help
|
||||
OpenFirmware PCI bus accessors
|
||||
|
||||
config OF_PCI_IRQ
|
||||
def_tristate PCI
|
||||
depends on OF_PCI && OF_IRQ
|
||||
help
|
||||
OpenFirmware PCI IRQ routing helpers
|
||||
|
||||
endmenu # OF
|
||||
|
@ -10,3 +10,4 @@ obj-$(CONFIG_OF_NET) += of_net.o
|
||||
obj-$(CONFIG_OF_SPI) += of_spi.o
|
||||
obj-$(CONFIG_OF_MDIO) += of_mdio.o
|
||||
obj-$(CONFIG_OF_PCI) += of_pci.o
|
||||
obj-$(CONFIG_OF_PCI_IRQ) += of_pci_irq.o
|
||||
|
@ -1,92 +1,40 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
/**
|
||||
* of_irq_map_pci - Resolve the interrupt for a PCI device
|
||||
* @pdev: the device whose interrupt is to be resolved
|
||||
* @out_irq: structure of_irq filled by this function
|
||||
*
|
||||
* This function resolves the PCI interrupt for a given PCI device. If a
|
||||
* device-node exists for a given pci_dev, it will use normal OF tree
|
||||
* walking. If not, it will implement standard swizzling and walk up the
|
||||
* PCI tree until an device-node is found, at which point it will finish
|
||||
* resolving using the OF tree walking.
|
||||
*/
|
||||
int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq)
|
||||
static inline int __of_pci_pci_compare(struct device_node *node,
|
||||
unsigned int devfn)
|
||||
{
|
||||
struct device_node *dn, *ppnode;
|
||||
struct pci_dev *ppdev;
|
||||
u32 lspec;
|
||||
__be32 lspec_be;
|
||||
__be32 laddr[3];
|
||||
u8 pin;
|
||||
int rc;
|
||||
unsigned int size;
|
||||
const __be32 *reg = of_get_property(node, "reg", &size);
|
||||
|
||||
/* Check if we have a device node, if yes, fallback to standard
|
||||
* device tree parsing
|
||||
*/
|
||||
dn = pci_device_to_OF_node(pdev);
|
||||
if (dn) {
|
||||
rc = of_irq_map_one(dn, 0, out_irq);
|
||||
if (!rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Ok, we don't, time to have fun. Let's start by building up an
|
||||
* interrupt spec. we assume #interrupt-cells is 1, which is standard
|
||||
* for PCI. If you do different, then don't use that routine.
|
||||
*/
|
||||
rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
/* No pin, exit */
|
||||
if (pin == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* Now we walk up the PCI tree */
|
||||
lspec = pin;
|
||||
for (;;) {
|
||||
/* Get the pci_dev of our parent */
|
||||
ppdev = pdev->bus->self;
|
||||
|
||||
/* Ouch, it's a host bridge... */
|
||||
if (ppdev == NULL) {
|
||||
ppnode = pci_bus_to_OF_node(pdev->bus);
|
||||
|
||||
/* No node for host bridge ? give up */
|
||||
if (ppnode == NULL)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* We found a P2P bridge, check if it has a node */
|
||||
ppnode = pci_device_to_OF_node(ppdev);
|
||||
}
|
||||
|
||||
/* Ok, we have found a parent with a device-node, hand over to
|
||||
* the OF parsing code.
|
||||
* We build a unit address from the linux device to be used for
|
||||
* resolution. Note that we use the linux bus number which may
|
||||
* not match your firmware bus numbering.
|
||||
* Fortunately, in most cases, interrupt-map-mask doesn't
|
||||
* include the bus number as part of the matching.
|
||||
* You should still be careful about that though if you intend
|
||||
* to rely on this function (you ship a firmware that doesn't
|
||||
* create device nodes for all PCI devices).
|
||||
*/
|
||||
if (ppnode)
|
||||
break;
|
||||
|
||||
/* We can only get here if we hit a P2P bridge with no node,
|
||||
* let's do standard swizzling and try again
|
||||
*/
|
||||
lspec = pci_swizzle_interrupt_pin(pdev, lspec);
|
||||
pdev = ppdev;
|
||||
}
|
||||
|
||||
lspec_be = cpu_to_be32(lspec);
|
||||
laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
|
||||
laddr[1] = laddr[2] = cpu_to_be32(0);
|
||||
return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq);
|
||||
if (!reg || size < 5 * sizeof(__be32))
|
||||
return 0;
|
||||
return ((be32_to_cpup(®[0]) >> 8) & 0xff) == devfn;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_irq_map_pci);
|
||||
|
||||
struct device_node *of_pci_find_child_device(struct device_node *parent,
|
||||
unsigned int devfn)
|
||||
{
|
||||
struct device_node *node, *node2;
|
||||
|
||||
for_each_child_of_node(parent, node) {
|
||||
if (__of_pci_pci_compare(node, devfn))
|
||||
return node;
|
||||
/*
|
||||
* Some OFs create a parent node "multifunc-device" as
|
||||
* a fake root for all functions of a multi-function
|
||||
* device we go down them as well.
|
||||
*/
|
||||
if (!strcmp(node->name, "multifunc-device")) {
|
||||
for_each_child_of_node(node, node2) {
|
||||
if (__of_pci_pci_compare(node2, devfn)) {
|
||||
of_node_put(node);
|
||||
return node2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_pci_find_child_device);
|
||||
|
92
drivers/of/of_pci_irq.c
Normal file
92
drivers/of/of_pci_irq.c
Normal file
@ -0,0 +1,92 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
/**
|
||||
* of_irq_map_pci - Resolve the interrupt for a PCI device
|
||||
* @pdev: the device whose interrupt is to be resolved
|
||||
* @out_irq: structure of_irq filled by this function
|
||||
*
|
||||
* This function resolves the PCI interrupt for a given PCI device. If a
|
||||
* device-node exists for a given pci_dev, it will use normal OF tree
|
||||
* walking. If not, it will implement standard swizzling and walk up the
|
||||
* PCI tree until an device-node is found, at which point it will finish
|
||||
* resolving using the OF tree walking.
|
||||
*/
|
||||
int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq)
|
||||
{
|
||||
struct device_node *dn, *ppnode;
|
||||
struct pci_dev *ppdev;
|
||||
u32 lspec;
|
||||
__be32 lspec_be;
|
||||
__be32 laddr[3];
|
||||
u8 pin;
|
||||
int rc;
|
||||
|
||||
/* Check if we have a device node, if yes, fallback to standard
|
||||
* device tree parsing
|
||||
*/
|
||||
dn = pci_device_to_OF_node(pdev);
|
||||
if (dn) {
|
||||
rc = of_irq_map_one(dn, 0, out_irq);
|
||||
if (!rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Ok, we don't, time to have fun. Let's start by building up an
|
||||
* interrupt spec. we assume #interrupt-cells is 1, which is standard
|
||||
* for PCI. If you do different, then don't use that routine.
|
||||
*/
|
||||
rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
/* No pin, exit */
|
||||
if (pin == 0)
|
||||
return -ENODEV;
|
||||
|
||||
/* Now we walk up the PCI tree */
|
||||
lspec = pin;
|
||||
for (;;) {
|
||||
/* Get the pci_dev of our parent */
|
||||
ppdev = pdev->bus->self;
|
||||
|
||||
/* Ouch, it's a host bridge... */
|
||||
if (ppdev == NULL) {
|
||||
ppnode = pci_bus_to_OF_node(pdev->bus);
|
||||
|
||||
/* No node for host bridge ? give up */
|
||||
if (ppnode == NULL)
|
||||
return -EINVAL;
|
||||
} else {
|
||||
/* We found a P2P bridge, check if it has a node */
|
||||
ppnode = pci_device_to_OF_node(ppdev);
|
||||
}
|
||||
|
||||
/* Ok, we have found a parent with a device-node, hand over to
|
||||
* the OF parsing code.
|
||||
* We build a unit address from the linux device to be used for
|
||||
* resolution. Note that we use the linux bus number which may
|
||||
* not match your firmware bus numbering.
|
||||
* Fortunately, in most cases, interrupt-map-mask doesn't
|
||||
* include the bus number as part of the matching.
|
||||
* You should still be careful about that though if you intend
|
||||
* to rely on this function (you ship a firmware that doesn't
|
||||
* create device nodes for all PCI devices).
|
||||
*/
|
||||
if (ppnode)
|
||||
break;
|
||||
|
||||
/* We can only get here if we hit a P2P bridge with no node,
|
||||
* let's do standard swizzling and try again
|
||||
*/
|
||||
lspec = pci_swizzle_interrupt_pin(pdev, lspec);
|
||||
pdev = ppdev;
|
||||
}
|
||||
|
||||
lspec_be = cpu_to_be32(lspec);
|
||||
laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
|
||||
laddr[1] = laddr[2] = cpu_to_be32(0);
|
||||
return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(of_irq_map_pci);
|
@ -70,4 +70,6 @@ obj-$(CONFIG_PCI_STUB) += pci-stub.o
|
||||
|
||||
obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen-pcifront.o
|
||||
|
||||
obj-$(CONFIG_OF) += of.o
|
||||
|
||||
ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
|
||||
|
@ -158,7 +158,7 @@ static void dlpar_pci_add_bus(struct device_node *dn)
|
||||
/* Scan below the new bridge */
|
||||
if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE ||
|
||||
dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)
|
||||
of_scan_pci_bridge(dn, dev);
|
||||
of_scan_pci_bridge(dev);
|
||||
|
||||
/* Map IO space for child bus, which may or may not succeed */
|
||||
pcibios_map_io_space(dev->subordinate);
|
||||
|
61
drivers/pci/of.c
Normal file
61
drivers/pci/of.c
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* PCI <-> OF mapping helpers
|
||||
*
|
||||
* Copyright 2011 IBM Corp.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_pci.h>
|
||||
#include "pci.h"
|
||||
|
||||
void pci_set_of_node(struct pci_dev *dev)
|
||||
{
|
||||
if (!dev->bus->dev.of_node)
|
||||
return;
|
||||
dev->dev.of_node = of_pci_find_child_device(dev->bus->dev.of_node,
|
||||
dev->devfn);
|
||||
}
|
||||
|
||||
void pci_release_of_node(struct pci_dev *dev)
|
||||
{
|
||||
of_node_put(dev->dev.of_node);
|
||||
dev->dev.of_node = NULL;
|
||||
}
|
||||
|
||||
void pci_set_bus_of_node(struct pci_bus *bus)
|
||||
{
|
||||
if (bus->self == NULL)
|
||||
bus->dev.of_node = pcibios_get_phb_of_node(bus);
|
||||
else
|
||||
bus->dev.of_node = of_node_get(bus->self->dev.of_node);
|
||||
}
|
||||
|
||||
void pci_release_bus_of_node(struct pci_bus *bus)
|
||||
{
|
||||
of_node_put(bus->dev.of_node);
|
||||
bus->dev.of_node = NULL;
|
||||
}
|
||||
|
||||
struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus)
|
||||
{
|
||||
/* This should only be called for PHBs */
|
||||
if (WARN_ON(bus->self || bus->parent))
|
||||
return NULL;
|
||||
|
||||
/* Look for a node pointer in either the intermediary device we
|
||||
* create above the root bus or it's own parent. Normally only
|
||||
* the later is populated.
|
||||
*/
|
||||
if (bus->bridge->of_node)
|
||||
return of_node_get(bus->bridge->of_node);
|
||||
if (bus->bridge->parent->of_node)
|
||||
return of_node_get(bus->bridge->parent->of_node);
|
||||
return NULL;
|
||||
}
|
@ -52,6 +52,7 @@ static void release_pcibus_dev(struct device *dev)
|
||||
if (pci_bus->bridge)
|
||||
put_device(pci_bus->bridge);
|
||||
pci_bus_remove_resources(pci_bus);
|
||||
pci_release_bus_of_node(pci_bus);
|
||||
kfree(pci_bus);
|
||||
}
|
||||
|
||||
@ -588,7 +589,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent,
|
||||
|
||||
child->self = bridge;
|
||||
child->bridge = get_device(&bridge->dev);
|
||||
|
||||
pci_set_bus_of_node(child);
|
||||
pci_set_bus_speed(child);
|
||||
|
||||
/* Set up default resource pointers and names.. */
|
||||
@ -1038,6 +1039,7 @@ static void pci_release_dev(struct device *dev)
|
||||
|
||||
pci_dev = to_pci_dev(dev);
|
||||
pci_release_capabilities(pci_dev);
|
||||
pci_release_of_node(pci_dev);
|
||||
kfree(pci_dev);
|
||||
}
|
||||
|
||||
@ -1157,6 +1159,8 @@ static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn)
|
||||
dev->vendor = l & 0xffff;
|
||||
dev->device = (l >> 16) & 0xffff;
|
||||
|
||||
pci_set_of_node(dev);
|
||||
|
||||
if (pci_setup_device(dev)) {
|
||||
kfree(dev);
|
||||
return NULL;
|
||||
@ -1409,6 +1413,7 @@ struct pci_bus * pci_create_bus(struct device *parent,
|
||||
goto dev_reg_err;
|
||||
b->bridge = get_device(dev);
|
||||
device_enable_async_suspend(b->bridge);
|
||||
pci_set_bus_of_node(b);
|
||||
|
||||
if (!parent)
|
||||
set_dev_node(b->bridge, pcibus_to_node(b));
|
||||
|
@ -6,4 +6,9 @@
|
||||
struct pci_dev;
|
||||
struct of_irq;
|
||||
int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq);
|
||||
|
||||
struct device_node;
|
||||
struct device_node *of_pci_find_child_device(struct device_node *parent,
|
||||
unsigned int devfn);
|
||||
|
||||
#endif
|
||||
|
@ -1589,5 +1589,23 @@ int pci_vpd_find_tag(const u8 *buf, unsigned int off, unsigned int len, u8 rdt);
|
||||
int pci_vpd_find_info_keyword(const u8 *buf, unsigned int off,
|
||||
unsigned int len, const char *kw);
|
||||
|
||||
/* PCI <-> OF binding helpers */
|
||||
#ifdef CONFIG_OF
|
||||
struct device_node;
|
||||
extern void pci_set_of_node(struct pci_dev *dev);
|
||||
extern void pci_release_of_node(struct pci_dev *dev);
|
||||
extern void pci_set_bus_of_node(struct pci_bus *bus);
|
||||
extern void pci_release_bus_of_node(struct pci_bus *bus);
|
||||
|
||||
/* Arch may override this (weak) */
|
||||
extern struct device_node * __weak pcibios_get_phb_of_node(struct pci_bus *bus);
|
||||
|
||||
#else /* CONFIG_OF */
|
||||
static inline void pci_set_of_node(struct pci_dev *dev) { }
|
||||
static inline void pci_release_of_node(struct pci_dev *dev) { }
|
||||
static inline void pci_set_bus_of_node(struct pci_bus *bus) { }
|
||||
static inline void pci_release_bus_of_node(struct pci_bus *bus) { }
|
||||
#endif /* CONFIG_OF */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif /* LINUX_PCI_H */
|
||||
|
Loading…
Reference in New Issue
Block a user