From: anton@samba.org

From: Hollis Blanchard <hollisb@us.ibm.com>

integrate vio.c with 2.6 driver model



---

 arch/ppc64/kernel/vio.c |  302 ++++++++++++++++++++++++++----------------------
 include/asm-ppc64/vio.h |   57 ++-------
 2 files changed, 185 insertions(+), 174 deletions(-)

diff -puN arch/ppc64/kernel/vio.c~ppc64-vio_updates arch/ppc64/kernel/vio.c
--- 25/arch/ppc64/kernel/vio.c~ppc64-vio_updates	2004-02-04 00:12:05.000000000 -0800
+++ 25-akpm/arch/ppc64/kernel/vio.c	2004-02-04 00:12:05.000000000 -0800
@@ -4,6 +4,7 @@
  *    Copyright (c) 2003 IBM Corp.
  *     Dave Engebretsen engebret@us.ibm.com
  *     Santiago Leon santil@us.ibm.com
+ *     Hollis Blanchard <hollisb@us.ibm.com>
  *
  *      This program is free software; you can redistribute it and/or
  *      modify it under the terms of the GNU General Public License
@@ -16,14 +17,16 @@
 #include <linux/pci.h>
 #include <linux/version.h>
 #include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/mm.h>
 #include <asm/rtas.h>
 #include <asm/pci_dma.h>
 #include <asm/dma.h>
 #include <asm/ppcdebug.h>
 #include <asm/vio.h>
 #include <asm/hvcall.h>
-#include <linux/mm.h>
-#include "open_pic.h" 	/* temporary, until we merge large irq support */
+
+#define DBGENTER() pr_debug("%s entered\n", __FUNCTION__)
 
 extern struct TceTable *build_tce_table(struct TceTable *tbl);
 
@@ -32,83 +35,74 @@ extern dma_addr_t get_tces(struct TceTab
 extern void tce_free(struct TceTable *tbl, dma_addr_t dma_addr,
 		     unsigned order, unsigned num_pages);
 
+static int vio_num_address_cells;
+static struct vio_dev *vio_bus_device; /* fake "parent" device */
 
-static struct vio_bus vio_bus;
-static LIST_HEAD(registered_vio_drivers);
-int vio_num_address_cells;
-EXPORT_SYMBOL(vio_num_address_cells);
-
-/* TODO:
- *   really fit into driver model (see include/linux/device.h)
- *   locking around list accesses
- */
+/* convert from struct device to struct vio_dev and pass to driver.
+ * dev->driver has already been set by generic code because vio_bus_match
+ * succeeded. */
+static int vio_bus_probe(struct device *dev)
+{
+	struct vio_dev *viodev = to_vio_dev(dev);
+	struct vio_driver *viodrv = to_vio_driver(dev->driver);
+	const struct vio_device_id *id;
+	int error = -ENODEV;
+
+	DBGENTER();
+
+	if (!viodrv->probe)
+		return error;
+
+	id = vio_match_device(viodrv->id_table, viodev);
+	if (id) {
+		error = viodrv->probe(viodev, id);
+	}
+
+	return error;
+}
+
+/* convert from struct device to struct vio_dev and pass to driver. */
+static int vio_bus_remove(struct device *dev)
+{
+	struct vio_dev *viodev = to_vio_dev(dev);
+	struct vio_driver *viodrv = to_vio_driver(dev->driver);
+
+	DBGENTER();
+
+	if (viodrv->remove) {
+		return viodrv->remove(viodev);
+	}
+
+	/* driver can't remove */
+	return 1;
+}
 
 /**
  * vio_register_driver: - Register a new vio driver
  * @drv:	The vio_driver structure to be registered.
- *
- * Adds the driver structure to the list of registered drivers
- * Returns the number of vio devices which were claimed by the driver
- * during registration.  The driver remains registered even if the
- * return value is zero.
  */
-int vio_register_driver(struct vio_driver *drv)
+int vio_register_driver(struct vio_driver *viodrv)
 {
-	int count = 0;
-	struct vio_dev *dev;
-
-	printk(KERN_DEBUG "%s: driver %s/%s registering\n", __FUNCTION__,
-		drv->id_table[0].type, drv->id_table[0].type);
-
-	/* find matching devices not already claimed by other drivers and pass
-	 * them to probe() */
-	list_for_each_entry(dev, &vio_bus.devices, devices_list) {
-		const struct vio_device_id* id;
-
-		if (dev->driver)
-			continue; /* this device is already owned */
-
-		id = vio_match_device(drv->id_table, dev);
-		if (drv && id) {
-			if (0 == drv->probe(dev, id)) {
-				printk(KERN_DEBUG "  took device %p\n", dev);
-				dev->driver = drv;
-				count++;
-			}
-		}
-	}
+	printk(KERN_DEBUG "%s: driver %s registering\n", __FUNCTION__,
+		viodrv->name);
 
-	list_add_tail(&drv->node, &registered_vio_drivers);
+	/* fill in 'struct driver' fields */
+	viodrv->driver.name = viodrv->name;
+	viodrv->driver.bus = &vio_bus_type;
+	viodrv->driver.probe = vio_bus_probe;
+	viodrv->driver.remove = vio_bus_remove;
 
-	return count;
+	return driver_register(&viodrv->driver);
 }
 EXPORT_SYMBOL(vio_register_driver);
 
 /**
  * vio_unregister_driver - Remove registration of vio driver.
  * @driver:	The vio_driver struct to be removed form registration
- *
- * Searches for devices that are assigned to the driver and calls
- * driver->remove() for each one.  Removes the driver from the list
- * of registered drivers.  Returns the number of devices that were
- * assigned to that driver.
  */
-int vio_unregister_driver(struct vio_driver *driver)
+void vio_unregister_driver(struct vio_driver *viodrv)
 {
-	struct vio_dev *dev;
-	int devices_found = 0;
-
-	list_for_each_entry(dev, &vio_bus.devices, devices_list) {
-		if (dev->driver == driver) {
-			driver->remove(dev);
-			dev->driver = NULL;
-			devices_found++;
-		}
-	}
-
-	list_del(&driver->node);
-
-	return devices_found;
+	driver_unregister(&viodrv->driver);
 }
 EXPORT_SYMBOL(vio_unregister_driver);
 
@@ -121,9 +115,11 @@ EXPORT_SYMBOL(vio_unregister_driver);
  * system is in its list of supported devices. Returns the matching
  * vio_device_id structure or NULL if there is no match.
  */
-const struct vio_device_id *
-vio_match_device(const struct vio_device_id *ids, const struct vio_dev *dev)
+const struct vio_device_id * vio_match_device(const struct vio_device_id *ids,
+	const struct vio_dev *dev)
 {
+	DBGENTER();
+
 	while (ids->type) {
 		if ((strncmp(dev->archdata->type, ids->type, strlen(ids->type)) == 0) &&
 			device_is_compatible((struct device_node*)dev->archdata, ids->compat))
@@ -136,18 +132,33 @@ vio_match_device(const struct vio_device
 /**
  * vio_bus_init: - Initialize the virtual IO bus
  */
-int __init
-vio_bus_init(void)
+static int __init vio_bus_init(void)
 {
-	struct device_node *node_vroot, *node_vdev;
+	struct device_node *node_vroot, *of_node;
+	int err;
 
-	INIT_LIST_HEAD(&vio_bus.devices);
+	err = bus_register(&vio_bus_type);
+	if (err) {
+		printk(KERN_ERR "failed to register VIO bus\n");
+		return err;
+	}
+
+	/* the fake parent of all vio devices, just to give us a nice directory */
+	vio_bus_device = kmalloc(sizeof(struct vio_dev), GFP_KERNEL);
+	if (!vio_bus_device) {
+		return 1;
+	}
+	memset(vio_bus_device, 0, sizeof(struct vio_dev));
+	strcpy(vio_bus_device->dev.bus_id, "vdevice");
+
+	err = device_register(&vio_bus_device->dev);
+	if (err) {
+		printk(KERN_WARNING "%s: device_register returned %i\n", __FUNCTION__,
+			err);
+		kfree(vio_bus_device);
+		return err;
+	}
 
-	/*
-	 * Create device node entries for each virtual device
-	 * identified in the device tree.
-	 * Functionally takes the place of pci_scan_bus
-	 */
 	node_vroot = find_devices("vdevice");
 	if ((node_vroot == NULL) || (node_vroot->child == NULL)) {
 		printk(KERN_INFO "VIO: missing or empty /vdevice node; no virtual IO"
@@ -157,12 +168,16 @@ vio_bus_init(void)
 
 	vio_num_address_cells = prom_n_addr_cells(node_vroot->child);
 
-	for (node_vdev = node_vroot->child;
-			node_vdev != NULL;
-			node_vdev = node_vdev->sibling) {
-		printk(KERN_DEBUG "%s: processing %p\n", __FUNCTION__, node_vdev);
+	/*
+	 * Create struct vio_devices for each virtual device in the device tree.
+	 * Drivers will associate with them later.
+	 */
+	for (of_node = node_vroot->child;
+			of_node != NULL;
+			of_node = of_node->sibling) {
+		printk(KERN_DEBUG "%s: processing %p\n", __FUNCTION__, of_node);
 
-		vio_register_device(node_vdev);
+		vio_register_device(of_node);
 	}
 
 	return 0;
@@ -171,98 +186,91 @@ vio_bus_init(void)
 __initcall(vio_bus_init);
 
 
-/**
- * vio_probe_device - attach dev to appropriate driver
- * @dev:	device to find a driver for
- *
- * Walks the list of registered VIO drivers looking for one to take this
- * device.
- *
- * Returns a pointer to the matched driver or NULL if driver is not
- * found.
- */
-struct vio_driver * __devinit vio_probe_device(struct vio_dev* dev)
+/* vio_dev refcount hit 0 */
+static void __devinit vio_dev_release(struct device *dev)
 {
-	struct vio_driver *driver;
-
-	list_for_each_entry(driver, &registered_vio_drivers, node) {
-		const struct vio_device_id* id;
+	struct vio_dev *viodev = to_vio_dev(dev);
 
-		id = vio_match_device(driver->id_table, dev);
-		if (id && (0 < driver->probe(dev, id))) {
-			printk(KERN_DEBUG "%s: driver %s/%s took device %p\n",
-				__FUNCTION__, id->type, id->compat, dev);
-			dev->driver = driver;
-			return driver;
-		}
-	}
+	DBGENTER();
 
-	printk(KERN_DEBUG "%s: device %p found no driver\n", __FUNCTION__, dev);
-	return NULL;
+	/* XXX free TCE table */
+	of_node_put(viodev->archdata);
+	kfree(viodev);
 }
 
 /**
  * vio_register_device: - Register a new vio device.
- * @archdata:	The OF node for this device.
+ * @of_node:	The OF node for this device.
  *
  * Creates and initializes a vio_dev structure from the data in
- * node_vdev (archdata) and adds it to the list of virtual devices.
+ * of_node (archdata) and adds it to the list of virtual devices.
  * Returns a pointer to the created vio_dev or NULL if node has
  * NULL device_type or compatible fields.
  */
-struct vio_dev * __devinit vio_register_device(struct device_node *node_vdev)
+struct vio_dev * __devinit vio_register_device(struct device_node *of_node)
 {
-	struct vio_dev *dev;
+	struct vio_dev *viodev;
 	unsigned int *unit_address;
 	unsigned int *irq_p;
 
-	/* guarantee all vio_devs have 'device_type' field*/
-	if ((NULL == node_vdev->type)) {
+	DBGENTER();
+
+	/* we need the 'device_type' property, in order to match with drivers */
+	if ((NULL == of_node->type)) {
 		printk(KERN_WARNING
 			"%s: node %s missing 'device_type'\n", __FUNCTION__,
-			node_vdev->name ? node_vdev->name : "<unknown>");
+			of_node->name ? of_node->name : "<unknown>");
 		return NULL;
 	}
 
-	unit_address = (unsigned int *)get_property(node_vdev, "reg", NULL);
+	unit_address = (unsigned int *)get_property(of_node, "reg", NULL);
 	if (!unit_address) {
 		printk(KERN_WARNING "%s: node %s missing 'reg'\n", __FUNCTION__,
-			node_vdev->name ? node_vdev->name : "<unknown>");
+			of_node->name ? of_node->name : "<unknown>");
 		return NULL;
 	}
 
 	/* allocate a vio_dev for this node */
-	dev = kmalloc(sizeof(*dev), GFP_KERNEL);
-	if (!dev)
+	viodev = kmalloc(sizeof(struct vio_dev), GFP_KERNEL);
+	if (!viodev) {
 		return NULL;
-	memset(dev, 0, sizeof(*dev));
-
-	dev->archdata = (void*)of_node_get(node_vdev);
-	dev->bus = &vio_bus;
-	dev->unit_address = *unit_address;
-	dev->tce_table = vio_build_tce_table(dev);
-
-	irq_p = (unsigned int *) get_property(node_vdev, "interrupts", 0);
-	if(irq_p) {
-		dev->irq = openpic_to_irq(virt_irq_create_mapping(*irq_p));
-	} else {
-		dev->irq = (unsigned int) -1;
 	}
+	memset(viodev, 0, sizeof(struct vio_dev));
 
-	list_add_tail(&dev->devices_list, &vio_bus.devices);
-
-	vio_probe_device(dev); /* finally, assign it to a driver */
+	viodev->archdata = (void *)of_node_get(of_node);
+	viodev->unit_address = *unit_address;
+	viodev->tce_table = vio_build_tce_table(viodev);
+
+	viodev->irq = (unsigned int) -1;
+	irq_p = (unsigned int *)get_property(of_node, "interrupts", 0);
+	if (irq_p) {
+		viodev->irq = irq_offset_up(*irq_p);
+	}
+
+	/* init generic 'struct device' fields: */
+	viodev->dev.parent = &vio_bus_device->dev;
+	viodev->dev.bus = &vio_bus_type;
+	snprintf(viodev->dev.bus_id, BUS_ID_SIZE, "%s@%lx",
+		of_node->name, viodev->unit_address);
+	viodev->dev.release = vio_dev_release;
+
+	/* register with generic device framework */
+	if (device_register(&viodev->dev)) {
+		printk(KERN_ERR "%s: failed to register device %s\n", __FUNCTION__,
+			viodev->dev.bus_id);
+		/* XXX free TCE table */
+		kfree(viodev);
+		return NULL;
+	}
 
-	return dev;
+	return viodev;
 }
 EXPORT_SYMBOL(vio_register_device);
 
-int __devinit vio_unregister_device(struct vio_dev *dev)
+void __devinit vio_unregister_device(struct vio_dev *viodev)
 {
-	list_del(&dev->devices_list);
-	of_node_put(dev->archdata);
-
-	return 0;
+	DBGENTER();
+	device_unregister(&viodev->dev);
 }
 EXPORT_SYMBOL(vio_unregister_device);
 
@@ -532,5 +540,29 @@ void vio_free_consistent(struct vio_dev 
 }
 EXPORT_SYMBOL(vio_free_consistent);
 
+static int vio_bus_match(struct device *dev, struct device_driver *drv)
+{
+	const struct vio_dev *vio_dev = to_vio_dev(dev);
+	struct vio_driver *vio_drv = to_vio_driver(drv);
+	const struct vio_device_id *ids = vio_drv->id_table;
+	const struct vio_device_id *found_id;
+
+	DBGENTER();
+
+	if (!ids)
+		return 0;
+
+	found_id = vio_match_device(ids, vio_dev);
+	if (found_id)
+		return 1;
+
+	return 0;
+}
+
+struct bus_type vio_bus_type = {
+	.name = "vio",
+	.match = vio_bus_match,
+};
+
 EXPORT_SYMBOL(plpar_hcall_norets);
 EXPORT_SYMBOL(plpar_hcall_8arg_2ret);
diff -puN include/asm-ppc64/vio.h~ppc64-vio_updates include/asm-ppc64/vio.h
--- 25/include/asm-ppc64/vio.h~ppc64-vio_updates	2004-02-04 00:12:05.000000000 -0800
+++ 25-akpm/include/asm-ppc64/vio.h	2004-02-04 00:12:05.000000000 -0800
@@ -16,6 +16,7 @@
 
 #include <linux/init.h>
 #include <linux/errno.h>
+#include <linux/device.h>
 #include <asm/hvcall.h>
 #include <asm/prom.h>
 #include <asm/scatterlist.h>
@@ -40,11 +41,11 @@ struct vio_device_id;
 struct TceTable;
 
 int vio_register_driver(struct vio_driver *drv);
-int vio_unregister_driver(struct vio_driver *drv);
+void vio_unregister_driver(struct vio_driver *drv);
 const struct vio_device_id * vio_match_device(const struct vio_device_id *ids, 
 						const struct vio_dev *dev);
 struct vio_dev * __devinit vio_register_device(struct device_node *node_vdev);
-int __devinit vio_unregister_device(struct vio_dev *dev);
+void __devinit vio_unregister_device(struct vio_dev *dev);
 const void * vio_get_attribute(struct vio_dev *vdev, void* which, int* length);
 int vio_get_irq(struct vio_dev *dev);
 struct TceTable * vio_build_tce_table(struct vio_dev *dev);
@@ -64,11 +65,11 @@ void *vio_alloc_consistent(struct vio_de
 void vio_free_consistent(struct vio_dev *dev, size_t size, void *vaddr, 
 			 dma_addr_t dma_handle);
 
+extern struct bus_type vio_bus_type;
+
 struct vio_device_id {
 	char *type;
 	char *compat;
-/* I don't think we need this
-	unsigned long driver_data;	*/ /* Data private to the driver */
 };
 
 struct vio_driver {
@@ -76,55 +77,33 @@ struct vio_driver {
 	char *name;
 	const struct vio_device_id *id_table;	/* NULL if wants all devices */
 	int  (*probe)  (struct vio_dev *dev, const struct vio_device_id *id);	/* New device inserted */
-	void (*remove) (struct vio_dev *dev);	/* Device removed (NULL if not a hot-plug capable driver) */
+	int (*remove) (struct vio_dev *dev);	/* Device removed (NULL if not a hot-plug capable driver) */
 	unsigned long driver_data;
+
+	struct device_driver driver;
 };
 
-struct vio_bus;
+static inline struct vio_driver *to_vio_driver(struct device_driver *drv)
+{
+	return container_of(drv, struct vio_driver, driver);
+}
+
 /*
  * The vio_dev structure is used to describe virtual I/O devices.
  */
 struct vio_dev {
-	struct list_head devices_list;   /* node in list of all vio devices */
-	struct device_node *archdata;    /* Open Firmware node */
-	struct vio_bus *bus;            /* bus this device is on */
-	struct vio_driver *driver;      /* owning driver */
+	struct device_node *archdata;   /* Open Firmware node */
 	void *driver_data;              /* data private to the driver */
 	unsigned long unit_address;	
-
-	struct TceTable *tce_table; /* vio_map_* uses this */
+	struct TceTable *tce_table;     /* vio_map_* uses this */
 	unsigned int irq;
-	struct proc_dir_entry *procent; /* device entry in /proc/bus/vio */
-};
 
-struct vio_bus {
-	struct list_head devices;       /* list of virtual devices */
+	struct device dev;
 };
 
-
-static inline int vio_module_init(struct vio_driver *drv)
+static inline struct vio_dev *to_vio_dev(struct device *dev)
 {
-        int rc = vio_register_driver (drv);
-
-        if (rc > 0)
-                return 0;
-
-        /* iff CONFIG_HOTPLUG and built into kernel, we should
-         * leave the driver around for future hotplug events.
-         * For the module case, a hotplug daemon of some sort
-         * should load a module in response to an insert event. */
-#if defined(CONFIG_HOTPLUG) && !defined(MODULE)
-        if (rc == 0)
-                return 0;
-#else
-        if (rc == 0)
-                rc = -ENODEV;
-#endif
-
-        /* if we get here, we need to clean up vio driver instance
-         * and return some sort of error */
-
-        return rc;
+	return container_of(dev, struct vio_dev, dev);
 }
 
 #endif /* _PHYP_H */

_