[S390] cio: introduce fcx enabled scsw format
[linux-block.git] / drivers / s390 / cio / cio.c
index 08a578161306e1088f4fe6786ea85b518cfd7a12..40b2884126dad46bd88f193a32898e5a07a078be 100644 (file)
@@ -2,7 +2,7 @@
  *  drivers/s390/cio/cio.c
  *   S/390 common I/O routines -- low level i/o calls
  *
- *    Copyright (C) IBM Corp. 1999,2006
+ *    Copyright IBM Corp. 1999,2008
  *    Author(s): Ingo Adlung (adlung@de.ibm.com)
  *              Cornelia Huck (cornelia.huck@de.ibm.com)
  *              Arnd Bergmann (arndb@de.ibm.com)
@@ -39,23 +39,6 @@ debug_info_t *cio_debug_msg_id;
 debug_info_t *cio_debug_trace_id;
 debug_info_t *cio_debug_crw_id;
 
-int cio_show_msg;
-
-static int __init
-cio_setup (char *parm)
-{
-       if (!strcmp (parm, "yes"))
-               cio_show_msg = 1;
-       else if (!strcmp (parm, "no"))
-               cio_show_msg = 0;
-       else
-               printk(KERN_ERR "cio: cio_setup: "
-                      "invalid cio_msg parameter '%s'", parm);
-       return 1;
-}
-
-__setup ("cio_msg=", cio_setup);
-
 /*
  * Function: cio_debug_init
  * Initializes three debug logs for common I/O:
@@ -145,7 +128,7 @@ cio_tpi(void)
        local_bh_disable();
        irq_enter ();
        spin_lock(sch->lock);
-       memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
+       memcpy(&sch->schib.scsw, &irb->scsw, sizeof(union scsw));
        if (sch->driver && sch->driver->irq)
                sch->driver->irq(sch);
        spin_unlock(sch->lock);
@@ -166,7 +149,7 @@ cio_start_handle_notoper(struct subchannel *sch, __u8 lpm)
 
        stsch (sch->schid, &sch->schib);
 
-       CIO_MSG_EVENT(0, "cio_start: 'not oper' status for "
+       CIO_MSG_EVENT(2, "cio_start: 'not oper' status for "
                      "subchannel 0.%x.%04x!\n", sch->schid.ssid,
                      sch->schid.sch_no);
        sprintf(dbf_text, "no%s", sch->dev.bus_id);
@@ -219,7 +202,7 @@ cio_start_key (struct subchannel *sch,      /* subchannel structure */
                /*
                 * initialize device status information
                 */
-               sch->schib.scsw.actl |= SCSW_ACTL_START_PEND;
+               sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
                return 0;
        case 1:         /* status pending */
        case 2:         /* busy */
@@ -254,7 +237,7 @@ cio_resume (struct subchannel *sch)
 
        switch (ccode) {
        case 0:
-               sch->schib.scsw.actl |= SCSW_ACTL_RESUME_PEND;
+               sch->schib.scsw.cmd.actl |= SCSW_ACTL_RESUME_PEND;
                return 0;
        case 1:
                return -EBUSY;
@@ -294,7 +277,7 @@ cio_halt(struct subchannel *sch)
 
        switch (ccode) {
        case 0:
-               sch->schib.scsw.actl |= SCSW_ACTL_HALT_PEND;
+               sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
                return 0;
        case 1:         /* status pending */
        case 2:         /* busy */
@@ -329,7 +312,7 @@ cio_clear(struct subchannel *sch)
 
        switch (ccode) {
        case 0:
-               sch->schib.scsw.actl |= SCSW_ACTL_CLEAR_PEND;
+               sch->schib.scsw.cmd.actl |= SCSW_ACTL_CLEAR_PEND;
                return 0;
        default:                /* device not operational */
                return -ENODEV;
@@ -404,8 +387,10 @@ cio_modify (struct subchannel *sch)
        return ret;
 }
 
-/*
- * Enable subchannel.
+/**
+ * cio_enable_subchannel - enable a subchannel.
+ * @sch: subchannel to be enabled
+ * @intparm: interruption parameter to set
  */
 int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
 {
@@ -451,12 +436,13 @@ int cio_enable_subchannel(struct subchannel *sch, u32 intparm)
        CIO_TRACE_EVENT (2, dbf_txt);
        return ret;
 }
+EXPORT_SYMBOL_GPL(cio_enable_subchannel);
 
-/*
- * Disable subchannel.
+/**
+ * cio_disable_subchannel - disable a subchannel.
+ * @sch: subchannel to disable
  */
-int
-cio_disable_subchannel (struct subchannel *sch)
+int cio_disable_subchannel(struct subchannel *sch)
 {
        char dbf_txt[15];
        int ccode;
@@ -472,7 +458,7 @@ cio_disable_subchannel (struct subchannel *sch)
        if (ccode == 3)         /* Not operational. */
                return -ENODEV;
 
-       if (sch->schib.scsw.actl != 0)
+       if (scsw_actl(&sch->schib.scsw) != 0)
                /*
                 * the disable function must not be called while there are
                 *  requests pending for completion !
@@ -501,6 +487,7 @@ cio_disable_subchannel (struct subchannel *sch)
        CIO_TRACE_EVENT (2, dbf_txt);
        return ret;
 }
+EXPORT_SYMBOL_GPL(cio_disable_subchannel);
 
 int cio_create_sch_lock(struct subchannel *sch)
 {
@@ -511,27 +498,61 @@ int cio_create_sch_lock(struct subchannel *sch)
        return 0;
 }
 
-/*
- * cio_validate_subchannel()
+static int cio_check_devno_blacklisted(struct subchannel *sch)
+{
+       if (is_blacklisted(sch->schid.ssid, sch->schib.pmcw.dev)) {
+               /*
+                * This device must not be known to Linux. So we simply
+                * say that there is no device and return ENODEV.
+                */
+               CIO_MSG_EVENT(6, "Blacklisted device detected "
+                             "at devno %04X, subchannel set %x\n",
+                             sch->schib.pmcw.dev, sch->schid.ssid);
+               return -ENODEV;
+       }
+       return 0;
+}
+
+static int cio_validate_io_subchannel(struct subchannel *sch)
+{
+       /* Initialization for io subchannels. */
+       if (!css_sch_is_valid(&sch->schib))
+               return -ENODEV;
+
+       /* Devno is valid. */
+       return cio_check_devno_blacklisted(sch);
+}
+
+static int cio_validate_msg_subchannel(struct subchannel *sch)
+{
+       /* Initialization for message subchannels. */
+       if (!css_sch_is_valid(&sch->schib))
+               return -ENODEV;
+
+       /* Devno is valid. */
+       return cio_check_devno_blacklisted(sch);
+}
+
+/**
+ * cio_validate_subchannel - basic validation of subchannel
+ * @sch: subchannel structure to be filled out
+ * @schid: subchannel id
  *
  * Find out subchannel type and initialize struct subchannel.
  * Return codes:
- *   SUBCHANNEL_TYPE_IO for a normal io subchannel
- *   SUBCHANNEL_TYPE_CHSC for a chsc subchannel
- *   SUBCHANNEL_TYPE_MESSAGE for a messaging subchannel
- *   SUBCHANNEL_TYPE_ADM for a adm(?) subchannel
+ *   0 on success
  *   -ENXIO for non-defined subchannels
- *   -ENODEV for subchannels with invalid device number or blacklisted devices
+ *   -ENODEV for invalid subchannels or blacklisted devices
+ *   -EIO for subchannels in an invalid subchannel set
  */
-int
-cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
+int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
 {
        char dbf_txt[15];
        int ccode;
        int err;
 
-       sprintf (dbf_txt, "valsch%x", schid.sch_no);
-       CIO_TRACE_EVENT (4, dbf_txt);
+       sprintf(dbf_txt, "valsch%x", schid.sch_no);
+       CIO_TRACE_EVENT(4, dbf_txt);
 
        /* Nuke all fields. */
        memset(sch, 0, sizeof(struct subchannel));
@@ -563,67 +584,21 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
        /* Copy subchannel type from path management control word. */
        sch->st = sch->schib.pmcw.st;
 
-       /*
-        * ... just being curious we check for non I/O subchannels
-        */
-       if (sch->st != 0) {
-               CIO_DEBUG(KERN_INFO, 0,
-                         "Subchannel 0.%x.%04x reports "
-                         "non-I/O subchannel type %04X\n",
-                         sch->schid.ssid, sch->schid.sch_no, sch->st);
-               /* We stop here for non-io subchannels. */
-               err = sch->st;
-               goto out;
+       switch (sch->st) {
+       case SUBCHANNEL_TYPE_IO:
+               err = cio_validate_io_subchannel(sch);
+               break;
+       case SUBCHANNEL_TYPE_MSG:
+               err = cio_validate_msg_subchannel(sch);
+               break;
+       default:
+               err = 0;
        }
-
-       /* Initialization for io subchannels. */
-       if (!css_sch_is_valid(&sch->schib)) {
-               err = -ENODEV;
+       if (err)
                goto out;
-       }
 
-       /* Devno is valid. */
-       if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
-               /*
-                * This device must not be known to Linux. So we simply
-                * say that there is no device and return ENODEV.
-                */
-               CIO_MSG_EVENT(4, "Blacklisted device detected "
-                             "at devno %04X, subchannel set %x\n",
-                             sch->schib.pmcw.dev, sch->schid.ssid);
-               err = -ENODEV;
-               goto out;
-       }
-       if (cio_is_console(sch->schid))
-               sch->opm = 0xff;
-       else
-               sch->opm = chp_get_sch_opm(sch);
-       sch->lpm = sch->schib.pmcw.pam & sch->opm;
-       sch->isc = 3;
-
-       CIO_DEBUG(KERN_INFO, 0,
-                 "Detected device %04x on subchannel 0.%x.%04X"
-                 " - PIM = %02X, PAM = %02X, POM = %02X\n",
-                 sch->schib.pmcw.dev, sch->schid.ssid,
-                 sch->schid.sch_no, sch->schib.pmcw.pim,
-                 sch->schib.pmcw.pam, sch->schib.pmcw.pom);
-
-       /*
-        * We now have to initially ...
-        *  ... enable "concurrent sense"
-        *  ... enable "multipath mode" if more than one
-        *        CHPID is available. This is done regardless
-        *        whether multiple paths are available for us.
-        */
-       sch->schib.pmcw.csense = 1;     /* concurrent sense */
-       sch->schib.pmcw.ena = 0;
-       if ((sch->lpm & (sch->lpm - 1)) != 0)
-               sch->schib.pmcw.mp = 1; /* multipath mode */
-       /* clean up possible residual cmf stuff */
-       sch->schib.pmcw.mme = 0;
-       sch->schib.pmcw.mbfc = 0;
-       sch->schib.pmcw.mbi = 0;
-       sch->schib.mba = 0;
+       CIO_MSG_EVENT(4, "Subchannel 0.%x.%04x reports subchannel type %04X\n",
+                     sch->schid.ssid, sch->schid.sch_no, sch->st);
        return 0;
 out:
        if (!cio_is_console(schid))
@@ -723,9 +698,9 @@ void wait_cons_dev(void)
        if (!console_subchannel_in_use)
                return;
 
-       /* disable all but isc 7 (console device) */
+       /* disable all but isc 1 (console device) */
        __ctl_store (save_cr6, 6, 6);
-       cr6 = 0x01000000;
+       cr6 = 0x40000000;
        __ctl_load (cr6, 6, 6);
 
        do {
@@ -733,7 +708,7 @@ void wait_cons_dev(void)
                if (!cio_tpi())
                        cpu_relax();
                spin_lock(console_subchannel.lock);
-       } while (console_subchannel.schib.scsw.actl != 0);
+       } while (console_subchannel.schib.scsw.cmd.actl != 0);
        /*
         * restore previous isc value
         */
@@ -807,11 +782,10 @@ cio_probe_console(void)
        }
 
        /*
-        * enable console I/O-interrupt subclass 7
+        * enable console I/O-interrupt subclass 1
         */
-       ctl_set_bit(6, 24);
-       console_subchannel.isc = 7;
-       console_subchannel.schib.pmcw.isc = 7;
+       ctl_set_bit(6, 30);
+       console_subchannel.schib.pmcw.isc = 1;
        console_subchannel.schib.pmcw.intparm =
                (u32)(addr_t)&console_subchannel;
        ret = cio_modify(&console_subchannel);
@@ -827,7 +801,7 @@ cio_release_console(void)
 {
        console_subchannel.schib.pmcw.intparm = 0;
        cio_modify(&console_subchannel);
-       ctl_clear_bit(6, 24);
+       ctl_clear_bit(6, 30);
        console_subchannel_in_use = 0;
 }
 
@@ -881,7 +855,7 @@ static void udelay_reset(unsigned long usecs)
 }
 
 static int
-__clear_subchannel_easy(struct subchannel_id schid)
+__clear_io_subchannel_easy(struct subchannel_id schid)
 {
        int retry;
 
@@ -938,11 +912,19 @@ static int __shutdown_subchannel_easy(struct subchannel_id schid, void *data)
        case -ENODEV:
                break;
        default: /* -EBUSY */
-               if (__clear_subchannel_easy(schid))
-                       break; /* give up... */
+               switch (schib.pmcw.st) {
+               case SUBCHANNEL_TYPE_IO:
+                       if (__clear_io_subchannel_easy(schid))
+                               goto out; /* give up... */
+                       break;
+               default:
+                       /* No default clear strategy */
+                       break;
+               }
                stsch(schid, &schib);
                __disable_subchannel_easy(schid, &schib);
        }
+out:
        return 0;
 }