6098e2ee14
Currently, the numa_node of OF-devices will be overwritten during device_register, which simply sets the node to -1. On cell machines, this means that devices can't find their IOMMU, which is referenced through the device's numa node. Set the numa node for OF devices with no parent, and use the lower-level device_initialize and device_add functions, so that the node is preserved. We can remove the call to set_dev_node in of_device_alloc, as it will be overwritten during register. Signed-off-by: Jeremy Kerr <jk@ozlabs.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
139 lines
3.2 KiB
C
139 lines
3.2 KiB
C
#include <linux/string.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/of.h>
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/mod_devicetable.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/of_device.h>
|
|
|
|
#include <asm/errno.h>
|
|
#include <asm/dcr.h>
|
|
|
|
static void of_device_make_bus_id(struct of_device *dev)
|
|
{
|
|
static atomic_t bus_no_reg_magic;
|
|
struct device_node *node = dev->node;
|
|
char *name = dev->dev.bus_id;
|
|
const u32 *reg;
|
|
u64 addr;
|
|
int magic;
|
|
|
|
/*
|
|
* If it's a DCR based device, use 'd' for native DCRs
|
|
* and 'D' for MMIO DCRs.
|
|
*/
|
|
#ifdef CONFIG_PPC_DCR
|
|
reg = of_get_property(node, "dcr-reg", NULL);
|
|
if (reg) {
|
|
#ifdef CONFIG_PPC_DCR_NATIVE
|
|
snprintf(name, BUS_ID_SIZE, "d%x.%s",
|
|
*reg, node->name);
|
|
#else /* CONFIG_PPC_DCR_NATIVE */
|
|
addr = of_translate_dcr_address(node, *reg, NULL);
|
|
if (addr != OF_BAD_ADDR) {
|
|
snprintf(name, BUS_ID_SIZE,
|
|
"D%llx.%s", (unsigned long long)addr,
|
|
node->name);
|
|
return;
|
|
}
|
|
#endif /* !CONFIG_PPC_DCR_NATIVE */
|
|
}
|
|
#endif /* CONFIG_PPC_DCR */
|
|
|
|
/*
|
|
* For MMIO, get the physical address
|
|
*/
|
|
reg = of_get_property(node, "reg", NULL);
|
|
if (reg) {
|
|
addr = of_translate_address(node, reg);
|
|
if (addr != OF_BAD_ADDR) {
|
|
snprintf(name, BUS_ID_SIZE,
|
|
"%llx.%s", (unsigned long long)addr,
|
|
node->name);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* No BusID, use the node name and add a globally incremented
|
|
* counter (and pray...)
|
|
*/
|
|
magic = atomic_add_return(1, &bus_no_reg_magic);
|
|
snprintf(name, BUS_ID_SIZE, "%s.%d", node->name, magic - 1);
|
|
}
|
|
|
|
struct of_device *of_device_alloc(struct device_node *np,
|
|
const char *bus_id,
|
|
struct device *parent)
|
|
{
|
|
struct of_device *dev;
|
|
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return NULL;
|
|
|
|
dev->node = of_node_get(np);
|
|
dev->dev.dma_mask = &dev->dma_mask;
|
|
dev->dev.parent = parent;
|
|
dev->dev.release = of_release_dev;
|
|
dev->dev.archdata.of_node = np;
|
|
|
|
if (bus_id)
|
|
strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);
|
|
else
|
|
of_device_make_bus_id(dev);
|
|
|
|
return dev;
|
|
}
|
|
EXPORT_SYMBOL(of_device_alloc);
|
|
|
|
int of_device_uevent(struct device *dev, struct kobj_uevent_env *env)
|
|
{
|
|
struct of_device *ofdev;
|
|
const char *compat;
|
|
int seen = 0, cplen, sl;
|
|
|
|
if (!dev)
|
|
return -ENODEV;
|
|
|
|
ofdev = to_of_device(dev);
|
|
|
|
if (add_uevent_var(env, "OF_NAME=%s", ofdev->node->name))
|
|
return -ENOMEM;
|
|
|
|
if (add_uevent_var(env, "OF_TYPE=%s", ofdev->node->type))
|
|
return -ENOMEM;
|
|
|
|
/* Since the compatible field can contain pretty much anything
|
|
* it's not really legal to split it out with commas. We split it
|
|
* up using a number of environment variables instead. */
|
|
|
|
compat = of_get_property(ofdev->node, "compatible", &cplen);
|
|
while (compat && *compat && cplen > 0) {
|
|
if (add_uevent_var(env, "OF_COMPATIBLE_%d=%s", seen, compat))
|
|
return -ENOMEM;
|
|
|
|
sl = strlen (compat) + 1;
|
|
compat += sl;
|
|
cplen -= sl;
|
|
seen++;
|
|
}
|
|
|
|
if (add_uevent_var(env, "OF_COMPATIBLE_N=%d", seen))
|
|
return -ENOMEM;
|
|
|
|
/* modalias is trickier, we add it in 2 steps */
|
|
if (add_uevent_var(env, "MODALIAS="))
|
|
return -ENOMEM;
|
|
sl = of_device_get_modalias(ofdev, &env->buf[env->buflen-1],
|
|
sizeof(env->buf) - env->buflen);
|
|
if (sl >= (sizeof(env->buf) - env->buflen))
|
|
return -ENOMEM;
|
|
env->buflen += sl;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(of_device_uevent);
|
|
EXPORT_SYMBOL(of_device_get_modalias);
|