Skip to content

Commit

Permalink
[SPARC64]: Add of_device layer and make ebus/isa use it.
Browse files Browse the repository at this point in the history
Sparcspkr and power drivers are converted, to make sure it works.
Eventually the SBUS device layer will use this as a sub-class.

I really cannot cut loose on that bit until sparc32 is given the
same infrastructure.

Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
David S. Miller committed Jun 24, 2006
1 parent 8cd24ed commit a2bd4fd
Show file tree
Hide file tree
Showing 10 changed files with 585 additions and 157 deletions.
2 changes: 1 addition & 1 deletion arch/sparc64/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ obj-y := process.o setup.o cpu.o idprom.o \
irq.o ptrace.o time.o sys_sparc.o signal.o \
unaligned.o central.o pci.o starfire.o semaphore.o \
power.o sbus.o iommu_common.o sparc64_ksyms.o chmc.o \
visemul.o prom.o
visemul.o prom.o of_device.o

obj-$(CONFIG_PCI) += ebus.o isa.o pci_common.o pci_iommu.o \
pci_psycho.o pci_sabre.o pci_schizo.o \
Expand Down
21 changes: 21 additions & 0 deletions arch/sparc64/kernel/ebus.c
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,16 @@ void __init fill_ebus_device(struct device_node *dp, struct linux_ebus_device *d
}
}

dev->ofdev.node = dp;
dev->ofdev.dev.parent = &dev->bus->ofdev.dev;
dev->ofdev.dev.bus = &ebus_bus_type;
strcpy(dev->ofdev.dev.bus_id, dp->path_component_name);

/* Register with core */
if (of_device_register(&dev->ofdev) != 0)
printk(KERN_DEBUG "ebus: device registration error for %s!\n",
dev->ofdev.dev.bus_id);

dp = dp->child;
if (dp) {
printk(" ->");
Expand Down Expand Up @@ -570,6 +580,17 @@ void __init ebus_init(void)
ebus->self = pdev;
ebus->parent = pbm = cookie->pbm;

ebus->ofdev.node = dp;
ebus->ofdev.dev.parent = &pdev->dev;
ebus->ofdev.dev.bus = &ebus_bus_type;
strcpy(ebus->ofdev.dev.bus_id, dp->path_component_name);

/* Register with core */
if (of_device_register(&ebus->ofdev) != 0)
printk(KERN_DEBUG "ebus: device registration error for %s!\n",
ebus->ofdev.dev.bus_id);


child = dp->child;
if (!child)
goto next_ebus;
Expand Down
37 changes: 33 additions & 4 deletions arch/sparc64/kernel/isa.c
Original file line number Diff line number Diff line change
Expand Up @@ -195,12 +195,25 @@ static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br)

isa_dev = kmalloc(sizeof(*isa_dev), GFP_KERNEL);
if (!isa_dev) {
fatal_err("cannot allocate isa_dev");
prom_halt();
printk(KERN_DEBUG "ISA: cannot allocate isa_dev");
return;
}

memset(isa_dev, 0, sizeof(*isa_dev));

isa_dev->ofdev.node = dp;
isa_dev->ofdev.dev.parent = &isa_br->ofdev.dev;
isa_dev->ofdev.dev.bus = &isa_bus_type;
strcpy(isa_dev->ofdev.dev.bus_id, dp->path_component_name);

/* Register with core */
if (of_device_register(&isa_dev->ofdev) != 0) {
printk(KERN_DEBUG "isa: device registration error for %s!\n",
isa_dev->ofdev.dev.bus_id);
kfree(isa_dev);
goto next_sibling;
}

/* Link it in. */
isa_dev->next = NULL;
if (isa_br->devices == NULL) {
Expand All @@ -226,6 +239,7 @@ static void __init isa_fill_devices(struct sparc_isa_bridge *isa_br)

printk("]");

next_sibling:
dp = dp->sibling;
}
}
Expand All @@ -244,6 +258,7 @@ void __init isa_init(void)
struct pcidev_cookie *pdev_cookie;
struct pci_pbm_info *pbm;
struct sparc_isa_bridge *isa_br;
struct device_node *dp;

pdev_cookie = pdev->sysdata;
if (!pdev_cookie) {
Expand All @@ -252,15 +267,29 @@ void __init isa_init(void)
continue;
}
pbm = pdev_cookie->pbm;
dp = pdev_cookie->prom_node;

isa_br = kmalloc(sizeof(*isa_br), GFP_KERNEL);
if (!isa_br) {
fatal_err("cannot allocate sparc_isa_bridge");
prom_halt();
printk(KERN_DEBUG "isa: cannot allocate sparc_isa_bridge");
return;
}

memset(isa_br, 0, sizeof(*isa_br));

isa_br->ofdev.node = dp;
isa_br->ofdev.dev.parent = &pdev->dev;
isa_br->ofdev.dev.bus = &isa_bus_type;
strcpy(isa_br->ofdev.dev.bus_id, dp->path_component_name);

/* Register with core */
if (of_device_register(&isa_br->ofdev) != 0) {
printk(KERN_DEBUG "isa: device registration error for %s!\n",
isa_br->ofdev.dev.bus_id);
kfree(isa_br);
return;
}

/* Link it in. */
isa_br->next = isa_chain;
isa_chain = isa_br;
Expand Down
279 changes: 279 additions & 0 deletions arch/sparc64/kernel/of_device.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
#include <linux/config.h>
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/slab.h>

#include <asm/errno.h>
#include <asm/of_device.h>

/**
* of_match_device - Tell if an of_device structure has a matching
* of_match structure
* @ids: array of of device match structures to search in
* @dev: the of device structure to match against
*
* Used by a driver to check whether an of_device present in the
* system is in its list of supported devices.
*/
const struct of_device_id *of_match_device(const struct of_device_id *matches,
const struct of_device *dev)
{
if (!dev->node)
return NULL;
while (matches->name[0] || matches->type[0] || matches->compatible[0]) {
int match = 1;
if (matches->name[0])
match &= dev->node->name
&& !strcmp(matches->name, dev->node->name);
if (matches->type[0])
match &= dev->node->type
&& !strcmp(matches->type, dev->node->type);
if (matches->compatible[0])
match &= of_device_is_compatible(dev->node,
matches->compatible);
if (match)
return matches;
matches++;
}
return NULL;
}

static int of_platform_bus_match(struct device *dev, struct device_driver *drv)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * of_drv = to_of_platform_driver(drv);
const struct of_device_id * matches = of_drv->match_table;

if (!matches)
return 0;

return of_match_device(matches, of_dev) != NULL;
}

struct of_device *of_dev_get(struct of_device *dev)
{
struct device *tmp;

if (!dev)
return NULL;
tmp = get_device(&dev->dev);
if (tmp)
return to_of_device(tmp);
else
return NULL;
}

void of_dev_put(struct of_device *dev)
{
if (dev)
put_device(&dev->dev);
}


static int of_device_probe(struct device *dev)
{
int error = -ENODEV;
struct of_platform_driver *drv;
struct of_device *of_dev;
const struct of_device_id *match;

drv = to_of_platform_driver(dev->driver);
of_dev = to_of_device(dev);

if (!drv->probe)
return error;

of_dev_get(of_dev);

match = of_match_device(drv->match_table, of_dev);
if (match)
error = drv->probe(of_dev, match);
if (error)
of_dev_put(of_dev);

return error;
}

static int of_device_remove(struct device *dev)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * drv = to_of_platform_driver(dev->driver);

if (dev->driver && drv->remove)
drv->remove(of_dev);
return 0;
}

static int of_device_suspend(struct device *dev, pm_message_t state)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
int error = 0;

if (dev->driver && drv->suspend)
error = drv->suspend(of_dev, state);
return error;
}

static int of_device_resume(struct device * dev)
{
struct of_device * of_dev = to_of_device(dev);
struct of_platform_driver * drv = to_of_platform_driver(dev->driver);
int error = 0;

if (dev->driver && drv->resume)
error = drv->resume(of_dev);
return error;
}

#ifdef CONFIG_PCI
struct bus_type isa_bus_type = {
.name = "isa",
.match = of_platform_bus_match,
.probe = of_device_probe,
.remove = of_device_remove,
.suspend = of_device_suspend,
.resume = of_device_resume,
};

struct bus_type ebus_bus_type = {
.name = "ebus",
.match = of_platform_bus_match,
.probe = of_device_probe,
.remove = of_device_remove,
.suspend = of_device_suspend,
.resume = of_device_resume,
};
#endif

#ifdef CONFIG_SBUS
struct bus_type sbus_bus_type = {
.name = "sbus",
.match = of_platform_bus_match,
.probe = of_device_probe,
.remove = of_device_remove,
.suspend = of_device_suspend,
.resume = of_device_resume,
};
#endif

static int __init of_bus_driver_init(void)
{
int err = 0;

#ifdef CONFIG_PCI
if (!err)
err = bus_register(&isa_bus_type);
if (!err)
err = bus_register(&ebus_bus_type);
#endif
#ifdef CONFIG_SBUS
if (!err)
err = bus_register(&sbus_bus_type);
#endif
return 0;
}

postcore_initcall(of_bus_driver_init);

int of_register_driver(struct of_platform_driver *drv, struct bus_type *bus)
{
/* initialize common driver fields */
drv->driver.name = drv->name;
drv->driver.bus = bus;

/* register with core */
return driver_register(&drv->driver);
}

void of_unregister_driver(struct of_platform_driver *drv)
{
driver_unregister(&drv->driver);
}


static ssize_t dev_show_devspec(struct device *dev, struct device_attribute *attr, char *buf)
{
struct of_device *ofdev;

ofdev = to_of_device(dev);
return sprintf(buf, "%s", ofdev->node->full_name);
}

static DEVICE_ATTR(devspec, S_IRUGO, dev_show_devspec, NULL);

/**
* of_release_dev - free an of device structure when all users of it are finished.
* @dev: device that's been disconnected
*
* Will be called only by the device core when all users of this of device are
* done.
*/
void of_release_dev(struct device *dev)
{
struct of_device *ofdev;

ofdev = to_of_device(dev);

kfree(ofdev);
}

int of_device_register(struct of_device *ofdev)
{
int rc;

BUG_ON(ofdev->node == NULL);

rc = device_register(&ofdev->dev);
if (rc)
return rc;

device_create_file(&ofdev->dev, &dev_attr_devspec);

return 0;
}

void of_device_unregister(struct of_device *ofdev)
{
device_remove_file(&ofdev->dev, &dev_attr_devspec);
device_unregister(&ofdev->dev);
}

struct of_device* of_platform_device_create(struct device_node *np,
const char *bus_id,
struct device *parent,
struct bus_type *bus)
{
struct of_device *dev;

dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
return NULL;
memset(dev, 0, sizeof(*dev));

dev->dev.parent = parent;
dev->dev.bus = bus;
dev->dev.release = of_release_dev;

strlcpy(dev->dev.bus_id, bus_id, BUS_ID_SIZE);

if (of_device_register(dev) != 0) {
kfree(dev);
return NULL;
}

return dev;
}

EXPORT_SYMBOL(of_match_device);
EXPORT_SYMBOL(of_register_driver);
EXPORT_SYMBOL(of_unregister_driver);
EXPORT_SYMBOL(of_device_register);
EXPORT_SYMBOL(of_device_unregister);
EXPORT_SYMBOL(of_dev_get);
EXPORT_SYMBOL(of_dev_put);
EXPORT_SYMBOL(of_platform_device_create);
EXPORT_SYMBOL(of_release_dev);
Loading

0 comments on commit a2bd4fd

Please sign in to comment.