sparc64: Add missing rtc_close() in update_persistent_clock()
[safe/jmp/linux-2.6] / arch / sparc64 / kernel / vio.c
index 7eccc91..a490077 100644 (file)
 #include <asm/mdesc.h>
 #include <asm/vio.h>
 
-static inline int find_in_proplist(const char *list, const char *match,
-                                  int len)
-{
-       while (len > 0) {
-               int l;
-
-               if (!strcmp(list, match))
-                       return 1;
-               l = strlen(list) + 1;
-               list += l;
-               len -= l;
-       }
-       return 0;
-}
-
 static const struct vio_device_id *vio_match_device(
        const struct vio_device_id *matches,
        const struct vio_dev *dev)
@@ -44,13 +29,12 @@ static const struct vio_device_id *vio_match_device(
 
        while (matches->type[0] || matches->compat[0]) {
                int match = 1;
-               if (matches->type[0]) {
-                       match &= type
-                               && !strcmp(matches->type, type);
-               }
+               if (matches->type[0])
+                       match &= !strcmp(matches->type, type);
+
                if (matches->compat[0]) {
-                       match &= compat &&
-                               find_in_proplist(compat, matches->compat, len);
+                       match &= len &&
+                               of_find_in_proplist(compat, matches->compat, len);
                }
                if (match)
                        return matches;
@@ -104,9 +88,9 @@ static ssize_t devspec_show(struct device *dev,
        struct vio_dev *vdev = to_vio_dev(dev);
        const char *str = "none";
 
-       if (!strcmp(vdev->type, "network"))
+       if (!strcmp(vdev->type, "vnet-port"))
                str = "vnet";
-       else if (!strcmp(vdev->type, "block"))
+       else if (!strcmp(vdev->type, "vdc-port"))
                str = "vdisk";
 
        return sprintf(buf, "%s\n", str);
@@ -147,31 +131,7 @@ void vio_unregister_driver(struct vio_driver *viodrv)
 }
 EXPORT_SYMBOL(vio_unregister_driver);
 
-struct mdesc_node *vio_find_endpoint(struct vio_dev *vdev)
-{
-       struct mdesc_node *endp, *mp = vdev->mp;
-       int i;
-
-       endp = NULL;
-       for (i = 0; i < mp->num_arcs; i++) {
-               struct mdesc_node *t;
-
-               if (strcmp(mp->arcs[i].name, "fwd"))
-                       continue;
-
-               t = mp->arcs[i].arc;
-               if (strcmp(t->name, "channel-endpoint"))
-                       continue;
-
-               endp = t;
-               break;
-       }
-
-       return endp;
-}
-EXPORT_SYMBOL(vio_find_endpoint);
-
-static void __devinit vio_dev_release(struct device *dev)
+static void vio_dev_release(struct device *dev)
 {
        kfree(to_vio_dev(dev));
 }
@@ -197,22 +157,87 @@ struct device_node *cdev_node;
 static struct vio_dev *root_vdev;
 static u64 cdev_cfg_handle;
 
-static struct vio_dev *vio_create_one(struct mdesc_node *mp,
+static void vio_fill_channel_info(struct mdesc_handle *hp, u64 mp,
+                                 struct vio_dev *vdev)
+{
+       u64 a;
+
+       mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_FWD) {
+               const u64 *chan_id;
+               const u64 *irq;
+               u64 target;
+
+               target = mdesc_arc_target(hp, a);
+
+               irq = mdesc_get_property(hp, target, "tx-ino", NULL);
+               if (irq)
+                       vdev->tx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
+
+               irq = mdesc_get_property(hp, target, "rx-ino", NULL);
+               if (irq)
+                       vdev->rx_irq = sun4v_build_virq(cdev_cfg_handle, *irq);
+
+               chan_id = mdesc_get_property(hp, target, "id", NULL);
+               if (chan_id)
+                       vdev->channel_id = *chan_id;
+       }
+}
+
+static struct vio_dev *vio_create_one(struct mdesc_handle *hp, u64 mp,
                                      struct device *parent)
 {
-       const char *type, *compat;
+       const char *type, *compat, *bus_id_name;
        struct device_node *dp;
        struct vio_dev *vdev;
-       const u64 *irq;
-       int err, clen;
+       int err, tlen, clen;
+       const u64 *id, *cfg_handle;
+       u64 a;
 
-       type = md_get_property(mp, "device-type", NULL);
+       type = mdesc_get_property(hp, mp, "device-type", &tlen);
        if (!type) {
-               type = md_get_property(mp, "name", NULL);
-               if (!type)
-                       type = mp->name;
+               type = mdesc_get_property(hp, mp, "name", &tlen);
+               if (!type) {
+                       type = mdesc_node_name(hp, mp);
+                       tlen = strlen(type) + 1;
+               }
+       }
+       if (tlen > VIO_MAX_TYPE_LEN) {
+               printk(KERN_ERR "VIO: Type string [%s] is too long.\n",
+                      type);
+               return NULL;
+       }
+
+       id = mdesc_get_property(hp, mp, "id", NULL);
+
+       cfg_handle = NULL;
+       mdesc_for_each_arc(a, hp, mp, MDESC_ARC_TYPE_BACK) {
+               u64 target;
+
+               target = mdesc_arc_target(hp, a);
+               cfg_handle = mdesc_get_property(hp, target,
+                                               "cfg-handle", NULL);
+               if (cfg_handle)
+                       break;
+       }
+
+       bus_id_name = type;
+       if (!strcmp(type, "domain-services-port"))
+               bus_id_name = "ds";
+
+       if (strlen(bus_id_name) >= BUS_ID_SIZE - 4) {
+               printk(KERN_ERR "VIO: bus_id_name [%s] is too long.\n",
+                      bus_id_name);
+               return NULL;
+       }
+
+       compat = mdesc_get_property(hp, mp, "device-type", &clen);
+       if (!compat) {
+               clen = 0;
+       } else if (clen > VIO_MAX_COMPAT_LEN) {
+               printk(KERN_ERR "VIO: Compat len %d for [%s] is too long.\n",
+                      clen, type);
+               return NULL;
        }
-       compat = md_get_property(mp, "device-type", &clen);
 
        vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
        if (!vdev) {
@@ -221,19 +246,31 @@ static struct vio_dev *vio_create_one(struct mdesc_node *mp,
        }
 
        vdev->mp = mp;
-       vdev->type = type;
-       vdev->compat = compat;
+       memcpy(vdev->type, type, tlen);
+       if (compat)
+               memcpy(vdev->compat, compat, clen);
+       else
+               memset(vdev->compat, 0, sizeof(vdev->compat));
        vdev->compat_len = clen;
 
-       irq = md_get_property(mp, "tx-ino", NULL);
-       if (irq)
-               mp->irqs[0] = sun4v_build_virq(cdev_cfg_handle, *irq);
+       vdev->channel_id = ~0UL;
+       vdev->tx_irq = ~0;
+       vdev->rx_irq = ~0;
 
-       irq = md_get_property(mp, "rx-ino", NULL);
-       if (irq)
-               mp->irqs[1] = sun4v_build_virq(cdev_cfg_handle, *irq);
+       vio_fill_channel_info(hp, mp, vdev);
+
+       if (!id) {
+               dev_set_name(&vdev->dev, "%s", bus_id_name);
+               vdev->dev_no = ~(u64)0;
+       } else if (!cfg_handle) {
+               dev_set_name(&vdev->dev, "%s-%lu", bus_id_name, *id);
+               vdev->dev_no = *id;
+       } else {
+               dev_set_name(&vdev->dev, "%s-%lu-%lu", bus_id_name,
+                            *cfg_handle, *id);
+               vdev->dev_no = *cfg_handle;
+       }
 
-       snprintf(vdev->dev.bus_id, BUS_ID_SIZE, "%lx", mp->node);
        vdev->dev.parent = parent;
        vdev->dev.bus = &vio_bus_type;
        vdev->dev.release = vio_dev_release;
@@ -253,10 +290,12 @@ static struct vio_dev *vio_create_one(struct mdesc_node *mp,
        }
        vdev->dp = dp;
 
+       printk(KERN_INFO "VIO: Adding device %s\n", dev_name(&vdev->dev));
+
        err = device_register(&vdev->dev);
        if (err) {
                printk(KERN_ERR "VIO: Could not register device %s, err=%d\n",
-                      vdev->dev.bus_id, err);
+                      dev_name(&vdev->dev), err);
                kfree(vdev);
                return NULL;
        }
@@ -267,103 +306,146 @@ static struct vio_dev *vio_create_one(struct mdesc_node *mp,
        return vdev;
 }
 
-static void walk_tree(struct mdesc_node *n, struct vio_dev *parent)
+static void vio_add(struct mdesc_handle *hp, u64 node)
 {
-       int i;
-
-       for (i = 0; i < n->num_arcs; i++) {
-               struct mdesc_node *mp;
-               struct vio_dev *vdev;
+       (void) vio_create_one(hp, node, &root_vdev->dev);
+}
 
-               if (strcmp(n->arcs[i].name, "fwd"))
-                       continue;
+static int vio_md_node_match(struct device *dev, void *arg)
+{
+       struct vio_dev *vdev = to_vio_dev(dev);
 
-               mp = n->arcs[i].arc;
+       if (vdev->mp == (u64) arg)
+               return 1;
 
-               vdev = vio_create_one(mp, &parent->dev);
-               if (vdev && mp->num_arcs)
-                       walk_tree(mp, vdev);
-       }
+       return 0;
 }
 
-static void create_devices(struct mdesc_node *root)
+static void vio_remove(struct mdesc_handle *hp, u64 node)
 {
-       struct mdesc_node *mp;
+       struct device *dev;
 
-       root_vdev = vio_create_one(root, NULL);
-       if (!root_vdev) {
-               printk(KERN_ERR "VIO: Coult not create root device.\n");
-               return;
+       dev = device_find_child(&root_vdev->dev, (void *) node,
+                               vio_md_node_match);
+       if (dev) {
+               printk(KERN_INFO "VIO: Removing device %s\n", dev_name(dev));
+
+               device_unregister(dev);
        }
+}
+
+static struct mdesc_notifier_client vio_device_notifier = {
+       .add            = vio_add,
+       .remove         = vio_remove,
+       .node_name      = "virtual-device-port",
+};
 
-       walk_tree(root, root_vdev);
+/* We are only interested in domain service ports under the
+ * "domain-services" node.  On control nodes there is another port
+ * under "openboot" that we should not mess with as aparently that is
+ * reserved exclusively for OBP use.
+ */
+static void vio_add_ds(struct mdesc_handle *hp, u64 node)
+{
+       int found;
+       u64 a;
 
-       /* Domain services is odd as it doesn't sit underneath the
-        * channel-devices node, so we plug it in manually.
-        */
-       mp = md_find_node_by_name(NULL, "domain-services");
-       if (mp) {
-               struct vio_dev *parent = vio_create_one(mp, &root_vdev->dev);
+       found = 0;
+       mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {
+               u64 target = mdesc_arc_target(hp, a);
+               const char *name = mdesc_node_name(hp, target);
 
-               if (parent)
-                       walk_tree(mp, parent);
+               if (!strcmp(name, "domain-services")) {
+                       found = 1;
+                       break;
+               }
        }
+
+       if (found)
+               (void) vio_create_one(hp, node, &root_vdev->dev);
 }
 
+static struct mdesc_notifier_client vio_ds_notifier = {
+       .add            = vio_add_ds,
+       .remove         = vio_remove,
+       .node_name      = "domain-services-port",
+};
+
 const char *channel_devices_node = "channel-devices";
 const char *channel_devices_compat = "SUNW,sun4v-channel-devices";
 const char *cfg_handle_prop = "cfg-handle";
 
 static int __init vio_init(void)
 {
-       struct mdesc_node *root;
+       struct mdesc_handle *hp;
        const char *compat;
        const u64 *cfg_handle;
        int err, len;
+       u64 root;
+
+       err = bus_register(&vio_bus_type);
+       if (err) {
+               printk(KERN_ERR "VIO: Could not register bus type err=%d\n",
+                      err);
+               return err;
+       }
+
+       hp = mdesc_grab();
+       if (!hp)
+               return 0;
 
-       root = md_find_node_by_name(NULL, channel_devices_node);
-       if (!root) {
+       root = mdesc_node_by_name(hp, MDESC_NODE_NULL, channel_devices_node);
+       if (root == MDESC_NODE_NULL) {
                printk(KERN_INFO "VIO: No channel-devices MDESC node.\n");
+               mdesc_release(hp);
                return 0;
        }
 
        cdev_node = of_find_node_by_name(NULL, "channel-devices");
+       err = -ENODEV;
        if (!cdev_node) {
                printk(KERN_INFO "VIO: No channel-devices OBP node.\n");
-               return -ENODEV;
+               goto out_release;
        }
 
-       compat = md_get_property(root, "compatible", &len);
+       compat = mdesc_get_property(hp, root, "compatible", &len);
        if (!compat) {
                printk(KERN_ERR "VIO: Channel devices lacks compatible "
                       "property\n");
-               return -ENODEV;
+               goto out_release;
        }
-       if (!find_in_proplist(compat, channel_devices_compat, len)) {
+       if (!of_find_in_proplist(compat, channel_devices_compat, len)) {
                printk(KERN_ERR "VIO: Channel devices node lacks (%s) "
                       "compat entry.\n", channel_devices_compat);
-               return -ENODEV;
+               goto out_release;
        }
 
-       cfg_handle = md_get_property(root, cfg_handle_prop, NULL);
+       cfg_handle = mdesc_get_property(hp, root, cfg_handle_prop, NULL);
        if (!cfg_handle) {
                printk(KERN_ERR "VIO: Channel devices lacks %s property\n",
                       cfg_handle_prop);
-               return -ENODEV;
+               goto out_release;
        }
 
        cdev_cfg_handle = *cfg_handle;
 
-       err = bus_register(&vio_bus_type);
-       if (err) {
-               printk(KERN_ERR "VIO: Could not register bus type err=%d\n",
-                      err);
-               return err;
+       root_vdev = vio_create_one(hp, root, NULL);
+       err = -ENODEV;
+       if (!root_vdev) {
+               printk(KERN_ERR "VIO: Coult not create root device.\n");
+               goto out_release;
        }
 
-       create_devices(root);
+       mdesc_register_notifier(&vio_device_notifier);
+       mdesc_register_notifier(&vio_ds_notifier);
 
-       return 0;
+       mdesc_release(hp);
+
+       return err;
+
+out_release:
+       mdesc_release(hp);
+       return err;
 }
 
 postcore_initcall(vio_init);