]> err.no Git - linux-2.6/blobdiff - drivers/s390/cio/css.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/drzeus/mmc
[linux-2.6] / drivers / s390 / cio / css.c
index fcc641e578f4e990517508facc3ba033229cf8e6..5635e656c1a369b92bd0027c0e8beca261baed3c 100644 (file)
@@ -21,6 +21,7 @@
 #include "chsc.h"
 #include "device.h"
 #include "idset.h"
+#include "chp.h"
 
 int css_init_done = 0;
 static int need_reprobe = 0;
@@ -78,6 +79,7 @@ css_alloc_subchannel(struct subchannel_id schid)
        sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
        ret = cio_modify(sch);
        if (ret) {
+               kfree(sch->lock);
                kfree(sch);
                return ERR_PTR(ret);
        }
@@ -108,7 +110,7 @@ css_subchannel_release(struct device *dev)
        }
 }
 
-int css_sch_device_register(struct subchannel *sch)
+static int css_sch_device_register(struct subchannel *sch)
 {
        int ret;
 
@@ -125,8 +127,52 @@ void css_sch_device_unregister(struct subchannel *sch)
        mutex_unlock(&sch->reg_mutex);
 }
 
-static int
-css_register_subchannel(struct subchannel *sch)
+static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
+{
+       int i;
+       int mask;
+
+       memset(ssd, 0, sizeof(struct chsc_ssd_info));
+       ssd->path_mask = pmcw->pim;
+       for (i = 0; i < 8; i++) {
+               mask = 0x80 >> i;
+               if (pmcw->pim & mask) {
+                       chp_id_init(&ssd->chpid[i]);
+                       ssd->chpid[i].id = pmcw->chpid[i];
+               }
+       }
+}
+
+static void ssd_register_chpids(struct chsc_ssd_info *ssd)
+{
+       int i;
+       int mask;
+
+       for (i = 0; i < 8; i++) {
+               mask = 0x80 >> i;
+               if (ssd->path_mask & mask)
+                       if (!chp_is_registered(ssd->chpid[i]))
+                               chp_new(ssd->chpid[i]);
+       }
+}
+
+void css_update_ssd_info(struct subchannel *sch)
+{
+       int ret;
+
+       if (cio_is_console(sch->schid)) {
+               /* Console is initialized too early for functions requiring
+                * memory allocation. */
+               ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+       } else {
+               ret = chsc_get_ssd_info(sch->schid, &sch->ssd_info);
+               if (ret)
+                       ssd_from_pmcw(&sch->ssd_info, &sch->schib.pmcw);
+               ssd_register_chpids(&sch->ssd_info);
+       }
+}
+
+static int css_register_subchannel(struct subchannel *sch)
 {
        int ret;
 
@@ -135,21 +181,18 @@ css_register_subchannel(struct subchannel *sch)
        sch->dev.bus = &css_bus_type;
        sch->dev.release = &css_subchannel_release;
        sch->dev.groups = subch_attr_groups;
-
-       css_get_ssd_info(sch);
-
+       css_update_ssd_info(sch);
        /* make it known to the system */
        ret = css_sch_device_register(sch);
        if (ret) {
-               printk (KERN_WARNING "%s: could not register %s\n",
-                       __func__, sch->dev.bus_id);
+               CIO_MSG_EVENT(0, "Could not register sch 0.%x.%04x: %d\n",
+                             sch->schid.ssid, sch->schid.sch_no, ret);
                return ret;
        }
        return ret;
 }
 
-int
-css_probe_device(struct subchannel_id schid)
+static int css_probe_device(struct subchannel_id schid)
 {
        int ret;
        struct subchannel *sch;
@@ -329,15 +372,12 @@ static int __init slow_subchannel_init(void)
        spin_lock_init(&slow_subchannel_lock);
        slow_subchannel_set = idset_sch_new();
        if (!slow_subchannel_set) {
-               printk(KERN_WARNING "cio: could not allocate slow subchannel "
-                      "set\n");
+               CIO_MSG_EVENT(0, "could not allocate slow subchannel set\n");
                return -ENOMEM;
        }
        return 0;
 }
 
-subsys_initcall(slow_subchannel_init);
-
 static void css_slow_path_func(struct work_struct *unused)
 {
        struct subchannel_id schid;
@@ -383,8 +423,8 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data)
        struct subchannel *sch;
        int ret;
 
-       CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n",
-                 schid.ssid, schid.sch_no);
+       CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
+                     schid.ssid, schid.sch_no);
        if (need_reprobe)
                return -EAGAIN;
 
@@ -600,9 +640,20 @@ init_channel_subsystem (void)
 {
        int ret, i;
 
-       if (chsc_determine_css_characteristics() == 0)
+       ret = chsc_determine_css_characteristics();
+       if (ret == -ENOMEM)
+               goto out; /* No need to continue. */
+       if (ret == 0)
                css_characteristics_avail = 1;
 
+       ret = chsc_alloc_sei_area();
+       if (ret)
+               goto out;
+
+       ret = slow_subchannel_init();
+       if (ret)
+               goto out;
+
        if ((ret = bus_register(&css_bus_type)))
                goto out;
 
@@ -668,6 +719,10 @@ out_unregister:
 out_bus:
        bus_unregister(&css_bus_type);
 out:
+       chsc_free_sei_area();
+       kfree(slow_subchannel_set);
+       printk(KERN_WARNING"cio: failed to initialize css driver (%d)!\n",
+              ret);
        return ret;
 }