Merge branch 'origin'
[pandora-kernel.git] / drivers / s390 / cio / css.c
index b6225cb..3c77d65 100644 (file)
@@ -1,12 +1,11 @@
 /*
  *  drivers/s390/cio/css.c
  *  driver for channel subsystem
- *   $Revision: 1.85 $
  *
  *    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)
  */
 #include <linux/module.h>
 #include <linux/init.h>
@@ -23,6 +22,7 @@
 
 int need_rescan = 0;
 int css_init_done = 0;
+static int max_ssid = 0;
 
 struct channel_subsystem *css[__MAX_CSSID + 1];
 
@@ -37,10 +37,13 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
        init_subchannel_id(&schid);
        ret = -ENODEV;
        do {
-               ret = fn(schid, data);
-               if (ret)
-                       break;
-       } while (schid.sch_no++ < __MAX_SUBCHANNEL);
+               do {
+                       ret = fn(schid, data);
+                       if (ret)
+                               break;
+               } while (schid.sch_no++ < __MAX_SUBCHANNEL);
+               schid.sch_no = 0;
+       } while (schid.ssid++ < max_ssid);
        return ret;
 }
 
@@ -205,8 +208,8 @@ css_evaluate_subchannel(struct subchannel_id schid, int slow)
                return -EAGAIN; /* Will be done on the slow path. */
        }
        event = css_get_subchannel_status(sch, schid);
-       CIO_MSG_EVENT(4, "Evaluating schid %04x, event %d, %s, %s path.\n",
-                     schid.sch_no, event,
+       CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n",
+                     schid.ssid, schid.sch_no, event,
                      sch?(disc?"disconnected":"normal"):"unknown",
                      slow?"slow":"fast");
        switch (event) {
@@ -352,19 +355,23 @@ css_reiterate_subchannels(void)
  * Called from the machine check handler for subchannel report words.
  */
 int
-css_process_crw(int irq)
+css_process_crw(int rsid1, int rsid2)
 {
        int ret;
        struct subchannel_id mchk_schid;
 
-       CIO_CRW_EVENT(2, "source is subchannel %04X\n", irq);
+       CIO_CRW_EVENT(2, "source is subchannel %04X, subsystem id %x\n",
+                     rsid1, rsid2);
 
        if (need_rescan)
                /* We need to iterate all subchannels anyway. */
                return -EAGAIN;
 
        init_subchannel_id(&mchk_schid);
-       mchk_schid.sch_no = irq;
+       mchk_schid.sch_no = rsid1;
+       if (rsid2 != 0)
+               mchk_schid.ssid = (rsid2 >> 8) & 3;
+
        /* 
         * Since we are always presented with IPI in the CRW, we have to
         * use stsch() to find out if the subchannel in question has come
@@ -402,6 +409,9 @@ __init_channel_subsystem(struct subchannel_id schid, void *data)
                /* -ENXIO: no more subchannels. */
                case -ENXIO:
                        return ret;
+               /* -EIO: this subchannel set not supported. */
+               case -EIO:
+                       return ret;
                default:
                        return 0;
                }
@@ -436,6 +446,15 @@ css_generate_pgid(struct channel_subsystem *css, u32 tod_high)
 
 }
 
+static void
+channel_subsystem_release(struct device *dev)
+{
+       struct channel_subsystem *css;
+
+       css = to_css(dev);
+       kfree(css);
+}
+
 static inline void __init
 setup_css(int nr)
 {
@@ -445,6 +464,7 @@ setup_css(int nr)
        css[nr]->valid = 1;
        css[nr]->cssid = nr;
        sprintf(css[nr]->device.bus_id, "css%x", nr);
+       css[nr]->device.release = channel_subsystem_release;
        tod_high = (u32) (get_clock() >> 32);
        css_generate_pgid(css[nr], tod_high);
 }
@@ -465,12 +485,23 @@ init_channel_subsystem (void)
        if ((ret = bus_register(&css_bus_type)))
                goto out;
 
+       /* Try to enable MSS. */
+       ret = chsc_enable_facility(CHSC_SDA_OC_MSS);
+       switch (ret) {
+       case 0: /* Success. */
+               max_ssid = __MAX_SSID;
+               break;
+       case -ENOMEM:
+               goto out_bus;
+       default:
+               max_ssid = 0;
+       }
        /* Setup css structure. */
        for (i = 0; i <= __MAX_CSSID; i++) {
                css[i] = kmalloc(sizeof(struct channel_subsystem), GFP_KERNEL);
                if (!css[i]) {
                        ret = -ENOMEM;
-                       goto out_bus;
+                       goto out_unregister;
                }
                setup_css(i);
                ret = device_register(&css[i]->device);
@@ -485,11 +516,12 @@ init_channel_subsystem (void)
        return 0;
 out_free:
        kfree(css[i]);
-out_bus:
+out_unregister:
        while (i > 0) {
                i--;
                device_unregister(&css[i]->device);
        }
+out_bus:
        bus_unregister(&css_bus_type);
 out:
        return ret;
@@ -512,9 +544,41 @@ css_bus_match (struct device *dev, struct device_driver *drv)
        return 0;
 }
 
+static int
+css_probe (struct device *dev)
+{
+       struct subchannel *sch;
+
+       sch = to_subchannel(dev);
+       sch->driver = container_of (dev->driver, struct css_driver, drv);
+       return (sch->driver->probe ? sch->driver->probe(sch) : 0);
+}
+
+static int
+css_remove (struct device *dev)
+{
+       struct subchannel *sch;
+
+       sch = to_subchannel(dev);
+       return (sch->driver->remove ? sch->driver->remove(sch) : 0);
+}
+
+static void
+css_shutdown (struct device *dev)
+{
+       struct subchannel *sch;
+
+       sch = to_subchannel(dev);
+       if (sch->driver->shutdown)
+               sch->driver->shutdown(sch);
+}
+
 struct bus_type css_bus_type = {
-       .name  = "css",
-       .match = &css_bus_match,
+       .name     = "css",
+       .match    = css_bus_match,
+       .probe    = css_probe,
+       .remove   = css_remove,
+       .shutdown = css_shutdown,
 };
 
 subsys_initcall(init_channel_subsystem);