Fedora kernel-2.6.17-1.2142_FC4 patched with stable patch-2.6.17.4-vs2.0.2-rc26.diff
[linux-2.6.git] / drivers / s390 / cio / device.c
index df03255..8e3053c 100644 (file)
@@ -1,12 +1,11 @@
 /*
  *  drivers/s390/cio/device.c
  *  bus driver for ccw devices
- *   $Revision: 1.131 $
  *
  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
  *                      IBM Corporation
  *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
- *              Cornelia Huck (cohuck@de.ibm.com)
+ *              Cornelia Huck (cornelia.huck@de.ibm.com)
  *              Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
 #include <linux/config.h>
@@ -22,6 +21,7 @@
 
 #include <asm/ccwdev.h>
 #include <asm/cio.h>
+#include <asm/param.h>         /* HZ */
 
 #include "cio.h"
 #include "css.h"
@@ -58,7 +58,7 @@ ccw_bus_match (struct device * dev, struct device_driver * drv)
  * Heavily modeled on pci and usb hotplug.
  */
 static int
-ccw_hotplug (struct device *dev, char **envp, int num_envp,
+ccw_uevent (struct device *dev, char **envp, int num_envp,
             char *buffer, int buffer_size)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
@@ -106,33 +106,29 @@ ccw_hotplug (struct device *dev, char **envp, int num_envp,
        return 0;
 }
 
-struct bus_type ccw_bus_type = {
-       .name  = "ccw",
-       .match = &ccw_bus_match,
-       .hotplug = &ccw_hotplug,
-};
+struct bus_type ccw_bus_type;
 
-static int io_subchannel_probe (struct device *);
-static int io_subchannel_remove (struct device *);
+static int io_subchannel_probe (struct subchannel *);
+static int io_subchannel_remove (struct subchannel *);
 void io_subchannel_irq (struct device *);
 static int io_subchannel_notify(struct device *, int);
 static void io_subchannel_verify(struct device *);
 static void io_subchannel_ioterm(struct device *);
-static void io_subchannel_shutdown(struct device *);
+static void io_subchannel_shutdown(struct subchannel *);
 
 struct css_driver io_subchannel_driver = {
        .subchannel_type = SUBCHANNEL_TYPE_IO,
        .drv = {
                .name = "io_subchannel",
                .bus  = &css_bus_type,
-               .probe = &io_subchannel_probe,
-               .remove = &io_subchannel_remove,
-               .shutdown = &io_subchannel_shutdown,
        },
        .irq = io_subchannel_irq,
        .notify = io_subchannel_notify,
        .verify = io_subchannel_verify,
        .termination = io_subchannel_ioterm,
+       .probe = io_subchannel_probe,
+       .remove = io_subchannel_remove,
+       .shutdown = io_subchannel_shutdown,
 };
 
 struct workqueue_struct *ccw_device_work;
@@ -204,7 +200,7 @@ module_exit(cleanup_ccw_bus_type);
  * TODO: Split chpids and pimpampom up? Where is "in use" in the tree?
  */
 static ssize_t
-chpids_show (struct device * dev, char * buf)
+chpids_show (struct device * dev, struct device_attribute *attr, char * buf)
 {
        struct subchannel *sch = to_subchannel(dev);
        struct ssd_info *ssd = &sch->ssd_info;
@@ -219,7 +215,7 @@ chpids_show (struct device * dev, char * buf)
 }
 
 static ssize_t
-pimpampom_show (struct device * dev, char * buf)
+pimpampom_show (struct device * dev, struct device_attribute *attr, char * buf)
 {
        struct subchannel *sch = to_subchannel(dev);
        struct pmcw *pmcw = &sch->schib.pmcw;
@@ -229,7 +225,7 @@ pimpampom_show (struct device * dev, char * buf)
 }
 
 static ssize_t
-devtype_show (struct device *dev, char *buf)
+devtype_show (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        struct ccw_device_id *id = &(cdev->id);
@@ -242,7 +238,7 @@ devtype_show (struct device *dev, char *buf)
 }
 
 static ssize_t
-cutype_show (struct device *dev, char *buf)
+cutype_show (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        struct ccw_device_id *id = &(cdev->id);
@@ -252,7 +248,24 @@ cutype_show (struct device *dev, char *buf)
 }
 
 static ssize_t
-online_show (struct device *dev, char *buf)
+modalias_show (struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct ccw_device *cdev = to_ccwdev(dev);
+       struct ccw_device_id *id = &(cdev->id);
+       int ret;
+
+       ret = sprintf(buf, "ccw:t%04Xm%02X",
+                       id->cu_type, id->cu_model);
+       if (id->dev_type != 0)
+               ret += sprintf(buf + ret, "dt%04Xdm%02X\n",
+                               id->dev_type, id->dev_model);
+       else
+               ret += sprintf(buf + ret, "dtdm\n");
+       return ret;
+}
+
+static ssize_t
+online_show (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
 
@@ -346,17 +359,17 @@ ccw_device_set_online(struct ccw_device *cdev)
        else 
                pr_debug("ccw_device_offline returned %d, device %s\n",
                         ret, cdev->dev.bus_id);
-       return (ret = 0) ? -ENODEV : ret;
+       return (ret == 0) ? -ENODEV : ret;
 }
 
 static ssize_t
-online_store (struct device *dev, const char *buf, size_t count)
+online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        int i, force, ret;
        char *tmp;
 
-       if (atomic_compare_and_swap(0, 1, &cdev->private->onoff))
+       if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0)
                return -EAGAIN;
 
        if (cdev->drv && !try_module_get(cdev->drv->owner)) {
@@ -422,7 +435,7 @@ online_store (struct device *dev, const char *buf, size_t count)
 }
 
 static ssize_t
-available_show (struct device *dev, char *buf)
+available_show (struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct ccw_device *cdev = to_ccwdev(dev);
        struct subchannel *sch;
@@ -448,6 +461,7 @@ static DEVICE_ATTR(chpids, 0444, chpids_show, NULL);
 static DEVICE_ATTR(pimpampom, 0444, pimpampom_show, NULL);
 static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
 static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
+static DEVICE_ATTR(modalias, 0444, modalias_show, NULL);
 static DEVICE_ATTR(online, 0644, online_show, online_store);
 extern struct device_attribute dev_attr_cmb_enable;
 static DEVICE_ATTR(availability, 0444, available_show, NULL);
@@ -471,6 +485,7 @@ subchannel_add_files (struct device *dev)
 static struct attribute * ccwdev_attrs[] = {
        &dev_attr_devtype.attr,
        &dev_attr_cutype.attr,
+       &dev_attr_modalias.attr,
        &dev_attr_online.attr,
        &dev_attr_cmb_enable.attr,
        &dev_attr_availability.attr,
@@ -514,36 +529,43 @@ ccw_device_register(struct ccw_device *cdev)
        return ret;
 }
 
+struct match_data {
+       unsigned int devno;
+       unsigned int ssid;
+       struct ccw_device * sibling;
+};
+
+static int
+match_devno(struct device * dev, void * data)
+{
+       struct match_data * d = (struct match_data *)data;
+       struct ccw_device * cdev;
+
+       cdev = to_ccwdev(dev);
+       if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
+           (cdev->private->devno == d->devno) &&
+           (cdev->private->ssid == d->ssid) &&
+           (cdev != d->sibling)) {
+               cdev->private->state = DEV_STATE_NOT_OPER;
+               return 1;
+       }
+       return 0;
+}
+
 static struct ccw_device *
-get_disc_ccwdev_by_devno(unsigned int devno, struct ccw_device *sibling)
+get_disc_ccwdev_by_devno(unsigned int devno, unsigned int ssid,
+                        struct ccw_device *sibling)
 {
-       struct ccw_device *cdev;
-       struct list_head *entry;
        struct device *dev;
+       struct match_data data = {
+               .devno   = devno,
+               .ssid    = ssid,
+               .sibling = sibling,
+       };
 
-       if (!get_bus(&ccw_bus_type))
-               return NULL;
-       down_read(&ccw_bus_type.subsys.rwsem);
-       cdev = NULL;
-       list_for_each(entry, &ccw_bus_type.devices.list) {
-               dev = get_device(container_of(entry,
-                                             struct device, bus_list));
-               if (!dev)
-                       continue;
-               cdev = to_ccwdev(dev);
-               if ((cdev->private->state == DEV_STATE_DISCONNECTED) &&
-                   (cdev->private->devno == devno) &&
-                   (cdev != sibling)) {
-                       cdev->private->state = DEV_STATE_NOT_OPER;
-                       break;
-               }
-               put_device(dev);
-               cdev = NULL;
-       }
-       up_read(&ccw_bus_type.subsys.rwsem);
-       put_bus(&ccw_bus_type);
+       dev = bus_find_device(&ccw_bus_type, NULL, &data, match_devno);
 
-       return cdev;
+       return dev ? to_ccwdev(dev) : NULL;
 }
 
 static void
@@ -593,13 +615,13 @@ ccw_device_do_unreg_rereg(void *data)
 
                need_rename = 1;
                other_cdev = get_disc_ccwdev_by_devno(sch->schib.pmcw.dev,
-                                                     cdev);
+                                                     sch->schid.ssid, cdev);
                if (other_cdev) {
                        struct subchannel *other_sch;
 
                        other_sch = to_subchannel(other_cdev->dev.parent);
                        if (get_device(&other_sch->dev)) {
-                               stsch(other_sch->irq, &other_sch->schib);
+                               stsch(other_sch->schid, &other_sch->schib);
                                if (other_sch->schib.pmcw.dnv) {
                                        other_sch->schib.pmcw.intparm = 0;
                                        cio_modify(other_sch);
@@ -616,8 +638,8 @@ ccw_device_do_unreg_rereg(void *data)
        if (test_and_clear_bit(1, &cdev->private->registered))
                device_del(&cdev->dev);
        if (need_rename)
-               snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
-                         sch->schib.pmcw.dev);
+               snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
+                         sch->schid.ssid, sch->schib.pmcw.dev);
        PREPARE_WORK(&cdev->private->kick_work,
                     ccw_device_add_changed, (void *)cdev);
        queue_work(ccw_device_work, &cdev->private->kick_work);
@@ -647,7 +669,7 @@ io_subchannel_register(void *data)
        cdev = (struct ccw_device *) data;
        sch = to_subchannel(cdev->dev.parent);
 
-       if (!list_empty(&sch->dev.children)) {
+       if (klist_node_attached(&cdev->dev.knode_parent)) {
                bus_rescan_devices(&ccw_bus_type);
                goto out;
        }
@@ -746,18 +768,20 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
        sch->dev.driver_data = cdev;
        sch->driver = &io_subchannel_driver;
        cdev->ccwlock = &sch->lock;
+
        /* Init private data. */
        priv = cdev->private;
        priv->devno = sch->schib.pmcw.dev;
-       priv->irq = sch->irq;
+       priv->ssid = sch->schid.ssid;
+       priv->sch_no = sch->schid.sch_no;
        priv->state = DEV_STATE_NOT_OPER;
        INIT_LIST_HEAD(&priv->cmb_list);
        init_waitqueue_head(&priv->wait_q);
        init_timer(&priv->timer);
 
        /* Set an initial name for the device. */
-       snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.0.%04x",
-                 sch->schib.pmcw.dev);
+       snprintf (cdev->dev.bus_id, BUS_ID_SIZE, "0.%x.%04x",
+                 sch->schid.ssid, sch->schib.pmcw.dev);
 
        /* Increase counter of devices currently in recognition. */
        atomic_inc(&ccw_device_init_count);
@@ -774,14 +798,12 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 }
 
 static int
-io_subchannel_probe (struct device *pdev)
+io_subchannel_probe (struct subchannel *sch)
 {
-       struct subchannel *sch;
        struct ccw_device *cdev;
        int rc;
        unsigned long flags;
 
-       sch = to_subchannel(pdev);
        if (sch->dev.driver_data) {
                /*
                 * This subchannel already has an associated ccw_device.
@@ -804,20 +826,18 @@ io_subchannel_probe (struct device *pdev)
                        get_device(&cdev->dev);
                return 0;
        }
-       cdev  = kmalloc (sizeof(*cdev), GFP_KERNEL);
+       cdev = kzalloc (sizeof(*cdev), GFP_KERNEL);
        if (!cdev)
                return -ENOMEM;
-       memset(cdev, 0, sizeof(struct ccw_device));
-       cdev->private = kmalloc(sizeof(struct ccw_device_private), 
+       cdev->private = kzalloc(sizeof(struct ccw_device_private),
                                GFP_KERNEL | GFP_DMA);
        if (!cdev->private) {
                kfree(cdev);
                return -ENOMEM;
        }
-       memset(cdev->private, 0, sizeof(struct ccw_device_private));
        atomic_set(&cdev->private->onoff, 0);
        cdev->dev = (struct device) {
-               .parent = pdev,
+               .parent = &sch->dev,
                .release = ccw_device_release,
        };
        INIT_LIST_HEAD(&cdev->private->kick_work.entry);
@@ -830,7 +850,7 @@ io_subchannel_probe (struct device *pdev)
                return -ENODEV;
        }
 
-       rc = io_subchannel_recog(cdev, to_subchannel(pdev));
+       rc = io_subchannel_recog(cdev, sch);
        if (rc) {
                spin_lock_irqsave(&sch->lock, flags);
                sch->dev.driver_data = NULL;
@@ -854,17 +874,17 @@ ccw_device_unregister(void *data)
 }
 
 static int
-io_subchannel_remove (struct device *dev)
+io_subchannel_remove (struct subchannel *sch)
 {
        struct ccw_device *cdev;
        unsigned long flags;
 
-       if (!dev->driver_data)
+       if (!sch->dev.driver_data)
                return 0;
-       cdev = dev->driver_data;
+       cdev = sch->dev.driver_data;
        /* Set ccw device to not operational and drop reference. */
        spin_lock_irqsave(cdev->ccwlock, flags);
-       dev->driver_data = NULL;
+       sch->dev.driver_data = NULL;
        cdev->private->state = DEV_STATE_NOT_OPER;
        spin_unlock_irqrestore(cdev->ccwlock, flags);
        /*
@@ -919,16 +939,14 @@ io_subchannel_ioterm(struct device *dev)
 }
 
 static void
-io_subchannel_shutdown(struct device *dev)
+io_subchannel_shutdown(struct subchannel *sch)
 {
-       struct subchannel *sch;
        struct ccw_device *cdev;
        int ret;
 
-       sch = to_subchannel(dev);
-       cdev = dev->driver_data;
+       cdev = sch->dev.driver_data;
 
-       if (cio_is_console(sch->irq))
+       if (cio_is_console(sch->schid))
                return;
        if (!sch->schib.pmcw.ena)
                /* Nothing to do. */
@@ -963,10 +981,6 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
        cdev->dev = (struct device) {
                .parent = &sch->dev,
        };
-       /* Initialize the subchannel structure */
-       sch->dev.parent = &css_bus_device;
-       sch->dev.bus = &css_bus_type;
-
        rc = io_subchannel_recog(cdev, sch);
        if (rc)
                return rc;
@@ -996,7 +1010,7 @@ ccw_device_probe_console(void)
        int ret;
 
        if (xchg(&console_cdev_in_use, 1) != 0)
-               return NULL;
+               return ERR_PTR(-EBUSY);
        sch = cio_probe_console();
        if (IS_ERR(sch)) {
                console_cdev_in_use = 0;
@@ -1019,30 +1033,29 @@ ccw_device_probe_console(void)
 /*
  * get ccw_device matching the busid, but only if owned by cdrv
  */
+static int
+__ccwdev_check_busid(struct device *dev, void *id)
+{
+       char *bus_id;
+
+       bus_id = (char *)id;
+
+       return (strncmp(bus_id, dev->bus_id, BUS_ID_SIZE) == 0);
+}
+
+
 struct ccw_device *
 get_ccwdev_by_busid(struct ccw_driver *cdrv, const char *bus_id)
 {
-       struct device *d, *dev;
+       struct device *dev;
        struct device_driver *drv;
 
        drv = get_driver(&cdrv->driver);
        if (!drv)
-               return 0;
-
-       down_read(&drv->bus->subsys.rwsem);
-
-       dev = NULL;
-       list_for_each_entry(d, &drv->devices, driver_list) {
-               dev = get_device(d);
+               return NULL;
 
-               if (dev && !strncmp(bus_id, dev->bus_id, BUS_ID_SIZE))
-                       break;
-               else if (dev) {
-                       put_device(dev);
-                       dev = NULL;
-               }
-       }
-       up_read(&drv->bus->subsys.rwsem);
+       dev = driver_find_device(drv, NULL, (void *)bus_id,
+                                __ccwdev_check_busid);
        put_driver(drv);
 
        return dev ? to_ccwdev(dev) : 0;
@@ -1105,6 +1118,14 @@ ccw_device_remove (struct device *dev)
        return 0;
 }
 
+struct bus_type ccw_bus_type = {
+       .name   = "ccw",
+       .match  = ccw_bus_match,
+       .uevent = ccw_uevent,
+       .probe  = ccw_device_probe,
+       .remove = ccw_device_remove,
+};
+
 int
 ccw_driver_register (struct ccw_driver *cdriver)
 {
@@ -1112,8 +1133,6 @@ ccw_driver_register (struct ccw_driver *cdriver)
 
        drv->bus = &ccw_bus_type;
        drv->name = cdriver->name;
-       drv->probe = ccw_device_probe;
-       drv->remove = ccw_device_remove;
 
        return driver_register(drv);
 }
@@ -1124,6 +1143,16 @@ ccw_driver_unregister (struct ccw_driver *cdriver)
        driver_unregister(&cdriver->driver);
 }
 
+/* Helper func for qdio. */
+struct subchannel_id
+ccw_device_get_subchannel_id(struct ccw_device *cdev)
+{
+       struct subchannel *sch;
+
+       sch = to_subchannel(cdev->dev.parent);
+       return sch->schid;
+}
+
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(ccw_device_set_online);
 EXPORT_SYMBOL(ccw_device_set_offline);
@@ -1133,3 +1162,4 @@ EXPORT_SYMBOL(get_ccwdev_by_busid);
 EXPORT_SYMBOL(ccw_bus_type);
 EXPORT_SYMBOL(ccw_device_work);
 EXPORT_SYMBOL(ccw_device_notify_work);
+EXPORT_SYMBOL_GPL(ccw_device_get_subchannel_id);