Skip to content

Commit

Permalink
pci/of: Match PCI devices to OF nodes dynamically
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
ozbenh committed Jun 7, 2011
1 parent 1fa7b6a commit 98d9f30
Show file tree
Hide file tree
Showing 22 changed files with 275 additions and 376 deletions.
14 changes: 7 additions & 7 deletions arch/microblaze/include/asm/pci-bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
15 changes: 0 additions & 15 deletions arch/microblaze/include/asm/prom.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
40 changes: 7 additions & 33 deletions arch/microblaze/pci/pci_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down
35 changes: 11 additions & 24 deletions arch/powerpc/include/asm/pci-bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
{
Expand All @@ -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);

Expand Down
3 changes: 1 addition & 2 deletions arch/powerpc/include/asm/pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
14 changes: 0 additions & 14 deletions arch/powerpc/include/asm/prom.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
11 changes: 7 additions & 4 deletions arch/powerpc/kernel/pci-common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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
Expand All @@ -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;

Expand Down
150 changes: 13 additions & 137 deletions arch/powerpc/kernel/pci_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
return -ENODEV;
if (!scan_OF_pci_childs(hose->dn,
find_OF_pci_device_filter, (void *)node))
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;
reg = of_get_property(node, "reg", NULL);
if (!reg)

reg = of_get_property(node, "reg", &size);
if (!reg || size < 5 * sizeof(u32))
return -ENODEV;
*bus = (reg[0] >> 16) & 0xff;
*devfn = ((reg[0] >> 8) & 0xff);

*bus = (be32_to_cpup(&reg[0]) >> 16) & 0xff;
*devfn = (be32_to_cpup(&reg[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.
Expand Down
Loading

0 comments on commit 98d9f30

Please sign in to comment.