Merge branch 'for-linus' of git://git.alsa-project.org/alsa-kernel
[pandora-kernel.git] / drivers / s390 / cio / device_fsm.c
index bfad421..8b5fe57 100644 (file)
@@ -2,8 +2,7 @@
  * drivers/s390/cio/device_fsm.c
  * finite state machine for device handling
  *
- *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
- *                      IBM Corporation
+ *    Copyright IBM Corp. 2002,2008
  *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
  *              Martin Schwidefsky (schwidefsky@de.ibm.com)
  */
 #include "ioasm.h"
 #include "chp.h"
 
-int
-device_is_online(struct subchannel *sch)
-{
-       struct ccw_device *cdev;
+static int timeout_log_enabled;
 
-       if (!sch->dev.driver_data)
-               return 0;
-       cdev = sch->dev.driver_data;
-       return (cdev->private->state == DEV_STATE_ONLINE);
-}
-
-int
-device_is_disconnected(struct subchannel *sch)
+static int __init ccw_timeout_log_setup(char *unused)
 {
-       struct ccw_device *cdev;
-
-       if (!sch->dev.driver_data)
-               return 0;
-       cdev = sch->dev.driver_data;
-       return (cdev->private->state == DEV_STATE_DISCONNECTED ||
-               cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
+       timeout_log_enabled = 1;
+       return 1;
 }
 
-void
-device_set_disconnected(struct subchannel *sch)
-{
-       struct ccw_device *cdev;
-
-       if (!sch->dev.driver_data)
-               return;
-       cdev = sch->dev.driver_data;
-       ccw_device_set_timeout(cdev, 0);
-       cdev->private->flags.fake_irb = 0;
-       cdev->private->state = DEV_STATE_DISCONNECTED;
-}
+__setup("ccw_timeout_log", ccw_timeout_log_setup);
 
-void device_set_intretry(struct subchannel *sch)
+static void ccw_timeout_log(struct ccw_device *cdev)
 {
-       struct ccw_device *cdev;
-
-       cdev = sch->dev.driver_data;
-       if (!cdev)
-               return;
-       cdev->private->flags.intretry = 1;
-}
+       struct schib schib;
+       struct subchannel *sch;
+       struct io_subchannel_private *private;
+       union orb *orb;
+       int cc;
 
-int device_trigger_verify(struct subchannel *sch)
-{
-       struct ccw_device *cdev;
+       sch = to_subchannel(cdev->dev.parent);
+       private = to_io_private(sch);
+       orb = &private->orb;
+       cc = stsch(sch->schid, &schib);
+
+       printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
+              "device information:\n", get_clock());
+       printk(KERN_WARNING "cio: orb:\n");
+       print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                      orb, sizeof(*orb), 0);
+       printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
+       printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+       printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
+              "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
+
+       if (orb->tm.b) {
+               printk(KERN_WARNING "cio: orb indicates transport mode\n");
+               printk(KERN_WARNING "cio: last tcw:\n");
+               print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                              (void *)(addr_t)orb->tm.tcw,
+                              sizeof(struct tcw), 0);
+       } else {
+               printk(KERN_WARNING "cio: orb indicates command mode\n");
+               if ((void *)(addr_t)orb->cmd.cpa == &private->sense_ccw ||
+                   (void *)(addr_t)orb->cmd.cpa == cdev->private->iccws)
+                       printk(KERN_WARNING "cio: last channel program "
+                              "(intern):\n");
+               else
+                       printk(KERN_WARNING "cio: last channel program:\n");
 
-       cdev = sch->dev.driver_data;
-       if (!cdev || !cdev->online)
-               return -EINVAL;
-       dev_fsm_event(cdev, DEV_EVENT_VERIFY);
-       return 0;
+               print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                              (void *)(addr_t)orb->cmd.cpa,
+                              sizeof(struct ccw1), 0);
+       }
+       printk(KERN_WARNING "cio: ccw device state: %d\n",
+              cdev->private->state);
+       printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
+       printk(KERN_WARNING "cio: schib:\n");
+       print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                      &schib, sizeof(schib), 0);
+       printk(KERN_WARNING "cio: ccw device flags:\n");
+       print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+                      &cdev->private->flags, sizeof(cdev->private->flags), 0);
 }
 
 /*
@@ -92,6 +97,8 @@ ccw_device_timeout(unsigned long data)
 
        cdev = (struct ccw_device *) data;
        spin_lock_irq(cdev->ccwlock);
+       if (timeout_log_enabled)
+               ccw_timeout_log(cdev);
        dev_fsm_event(cdev, DEV_EVENT_TIMEOUT);
        spin_unlock_irq(cdev->ccwlock);
 }
@@ -116,18 +123,6 @@ ccw_device_set_timeout(struct ccw_device *cdev, int expires)
        add_timer(&cdev->private->timer);
 }
 
-/* Kill any pending timers after machine check. */
-void
-device_kill_pending_timer(struct subchannel *sch)
-{
-       struct ccw_device *cdev;
-
-       if (!sch->dev.driver_data)
-               return;
-       cdev = sch->dev.driver_data;
-       ccw_device_set_timeout(cdev, 0);
-}
-
 /*
  * Cancel running i/o. This is called repeatedly since halt/clear are
  * asynchronous operations. We do one try with cio_cancel, two tries
@@ -150,15 +145,18 @@ ccw_device_cancel_halt_clear(struct ccw_device *cdev)
                /* Not operational -> done. */
                return 0;
        /* Stage 1: cancel io. */
-       if (!(sch->schib.scsw.actl & SCSW_ACTL_HALT_PEND) &&
-           !(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
-               ret = cio_cancel(sch);
-               if (ret != -EINVAL)
-                       return ret;
-               /* cancel io unsuccessful. From now on it is asynchronous. */
+       if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_HALT_PEND) &&
+           !(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
+               if (!scsw_is_tm(&sch->schib.scsw)) {
+                       ret = cio_cancel(sch);
+                       if (ret != -EINVAL)
+                               return ret;
+               }
+               /* cancel io unsuccessful or not applicable (transport mode).
+                * Continue with asynchronous instructions. */
                cdev->private->iretry = 3;      /* 3 halt retries. */
        }
-       if (!(sch->schib.scsw.actl & SCSW_ACTL_CLEAR_PEND)) {
+       if (!(scsw_actl(&sch->schib.scsw) & SCSW_ACTL_CLEAR_PEND)) {
                /* Stage 2: halt io. */
                if (cdev->private->iretry) {
                        cdev->private->iretry--;
@@ -267,10 +265,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
        same_dev = 0; /* Keep the compiler quiet... */
        switch (state) {
        case DEV_STATE_NOT_OPER:
-               CIO_DEBUG(KERN_WARNING, 2,
-                         "cio: SenseID : unknown device %04x on subchannel "
-                         "0.%x.%04x\n", cdev->private->dev_id.devno,
-                         sch->schid.ssid, sch->schid.sch_no);
+               CIO_MSG_EVENT(2, "SenseID : unknown device %04x on "
+                             "subchannel 0.%x.%04x\n",
+                             cdev->private->dev_id.devno,
+                             sch->schid.ssid, sch->schid.sch_no);
                break;
        case DEV_STATE_OFFLINE:
                if (cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID) {
@@ -293,20 +291,19 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
                        return;
                }
                /* Issue device info message. */
-               CIO_DEBUG(KERN_INFO, 2,
-                         "cio: SenseID : device 0.%x.%04x reports: "
-                         "CU  Type/Mod = %04X/%02X, Dev Type/Mod = "
-                         "%04X/%02X\n",
-                         cdev->private->dev_id.ssid,
-                         cdev->private->dev_id.devno,
-                         cdev->id.cu_type, cdev->id.cu_model,
-                         cdev->id.dev_type, cdev->id.dev_model);
+               CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
+                             "CU  Type/Mod = %04X/%02X, Dev Type/Mod = "
+                             "%04X/%02X\n",
+                             cdev->private->dev_id.ssid,
+                             cdev->private->dev_id.devno,
+                             cdev->id.cu_type, cdev->id.cu_model,
+                             cdev->id.dev_type, cdev->id.dev_model);
                break;
        case DEV_STATE_BOXED:
-               CIO_DEBUG(KERN_WARNING, 2,
-                         "cio: SenseID : boxed device %04x on subchannel "
-                         "0.%x.%04x\n", cdev->private->dev_id.devno,
-                         sch->schid.ssid, sch->schid.sch_no);
+               CIO_MSG_EVENT(0, "SenseID : boxed device %04x on "
+                             " subchannel 0.%x.%04x\n",
+                             cdev->private->dev_id.devno,
+                             sch->schid.ssid, sch->schid.sch_no);
                break;
        }
        cdev->private->state = state;
@@ -334,34 +331,30 @@ ccw_device_sense_id_done(struct ccw_device *cdev, int err)
        }
 }
 
+int ccw_device_notify(struct ccw_device *cdev, int event)
+{
+       if (!cdev->drv)
+               return 0;
+       if (!cdev->online)
+               return 0;
+       return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
+}
+
 static void
 ccw_device_oper_notify(struct work_struct *work)
 {
        struct ccw_device_private *priv;
        struct ccw_device *cdev;
-       struct subchannel *sch;
        int ret;
-       unsigned long flags;
 
        priv = container_of(work, struct ccw_device_private, kick_work);
        cdev = priv->cdev;
-       spin_lock_irqsave(cdev->ccwlock, flags);
-       sch = to_subchannel(cdev->dev.parent);
-       if (sch->driver && sch->driver->notify) {
-               spin_unlock_irqrestore(cdev->ccwlock, flags);
-               ret = sch->driver->notify(&sch->dev, CIO_OPER);
-               spin_lock_irqsave(cdev->ccwlock, flags);
-       } else
-               ret = 0;
+       ret = ccw_device_notify(cdev, CIO_OPER);
        if (ret) {
                /* Reenable channel measurements, if needed. */
-               spin_unlock_irqrestore(cdev->ccwlock, flags);
                cmf_reenable(cdev);
-               spin_lock_irqsave(cdev->ccwlock, flags);
                wake_up(&cdev->private->wait_q);
-       }
-       spin_unlock_irqrestore(cdev->ccwlock, flags);
-       if (!ret)
+       } else
                /* Driver doesn't want device back. */
                ccw_device_do_unreg_rereg(work);
 }
@@ -388,9 +381,8 @@ ccw_device_done(struct ccw_device *cdev, int state)
 
 
        if (state == DEV_STATE_BOXED)
-               CIO_DEBUG(KERN_WARNING, 2,
-                         "cio: Boxed device %04x on subchannel %04x\n",
-                         cdev->private->dev_id.devno, sch->schid.sch_no);
+               CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
+                             cdev->private->dev_id.devno, sch->schid.sch_no);
 
        if (cdev->private->flags.donotify) {
                cdev->private->flags.donotify = 0;
@@ -500,7 +492,7 @@ ccw_device_recognition(struct ccw_device *cdev)
            (cdev->private->state != DEV_STATE_BOXED))
                return -EINVAL;
        sch = to_subchannel(cdev->dev.parent);
-       ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+       ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
        if (ret != 0)
                /* Couldn't enable the subchannel for i/o. Sick device. */
                return ret;
@@ -568,10 +560,11 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
                /* Deliver fake irb to device driver, if needed. */
                if (cdev->private->flags.fake_irb) {
                        memset(&cdev->private->irb, 0, sizeof(struct irb));
-                       cdev->private->irb.scsw.cc = 1;
-                       cdev->private->irb.scsw.fctl = SCSW_FCTL_START_FUNC;
-                       cdev->private->irb.scsw.actl = SCSW_ACTL_START_PEND;
-                       cdev->private->irb.scsw.stctl = SCSW_STCTL_STATUS_PEND;
+                       cdev->private->irb.scsw.cmd.cc = 1;
+                       cdev->private->irb.scsw.cmd.fctl = SCSW_FCTL_START_FUNC;
+                       cdev->private->irb.scsw.cmd.actl = SCSW_ACTL_START_PEND;
+                       cdev->private->irb.scsw.cmd.stctl =
+                               SCSW_STCTL_STATUS_PEND;
                        cdev->private->flags.fake_irb = 0;
                        if (cdev->handler)
                                cdev->handler(cdev, cdev->private->intparm,
@@ -587,9 +580,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
        default:
                /* Reset oper notify indication after verify error. */
                cdev->private->flags.donotify = 0;
-               if (cdev->online)
+               if (cdev->online) {
+                       ccw_device_set_timeout(cdev, 0);
                        dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
-               else
+               else
                        ccw_device_done(cdev, DEV_STATE_NOT_OPER);
                break;
        }
@@ -610,7 +604,7 @@ ccw_device_online(struct ccw_device *cdev)
        sch = to_subchannel(cdev->dev.parent);
        if (css_init_done && !get_device(&cdev->dev))
                return -ENODEV;
-       ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+       ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
        if (ret != 0) {
                /* Couldn't enable the subchannel for i/o. Sick device. */
                if (ret == -ENODEV)
@@ -664,13 +658,10 @@ ccw_device_offline(struct ccw_device *cdev)
        sch = to_subchannel(cdev->dev.parent);
        if (stsch(sch->schid, &sch->schib) || !sch->schib.pmcw.dnv)
                return -ENODEV;
-       if (cdev->private->state != DEV_STATE_ONLINE) {
-               if (sch->schib.scsw.actl != 0)
-                       return -EBUSY;
-               return -EINVAL;
-       }
-       if (sch->schib.scsw.actl != 0)
+       if (scsw_actl(&sch->schib.scsw) != 0)
                return -EBUSY;
+       if (cdev->private->state != DEV_STATE_ONLINE)
+               return -EINVAL;
        /* Are we doing path grouping? */
        if (!cdev->private->options.pgroup) {
                /* No, set state offline immediately. */
@@ -745,9 +736,9 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
         */
        stsch(sch->schid, &sch->schib);
 
-       if (sch->schib.scsw.actl != 0 ||
-           (sch->schib.scsw.stctl & SCSW_STCTL_STATUS_PEND) ||
-           (cdev->private->irb.scsw.stctl & SCSW_STCTL_STATUS_PEND)) {
+       if (scsw_actl(&sch->schib.scsw) != 0 ||
+           (scsw_stctl(&sch->schib.scsw) & SCSW_STCTL_STATUS_PEND) ||
+           (scsw_stctl(&cdev->private->irb.scsw) & SCSW_STCTL_STATUS_PEND)) {
                /*
                 * No final status yet or final status not yet delivered
                 * to the device driver. Can't do path verfication now,
@@ -769,13 +760,13 @@ static void
 ccw_device_irq(struct ccw_device *cdev, enum dev_event dev_event)
 {
        struct irb *irb;
+       int is_cmd;
 
        irb = (struct irb *) __LC_IRB;
+       is_cmd = !scsw_is_tm(&irb->scsw);
        /* Check for unsolicited interrupt. */
-       if ((irb->scsw.stctl ==
-                       (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS))
-           && (!irb->scsw.cc)) {
-               if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) &&
+       if (!scsw_is_solicited(&irb->scsw)) {
+               if (is_cmd && (irb->scsw.cmd.dstat & DEV_STAT_UNIT_CHECK) &&
                    !irb->esw.esw0.erw.cons) {
                        /* Unit check but no sense data. Need basic sense. */
                        if (ccw_device_do_sense(cdev, irb) != 0)
@@ -794,7 +785,7 @@ call_handler_unsol:
        }
        /* Accumulate status and find out if a basic sense is needed. */
        ccw_device_accumulate_irb(cdev, irb);
-       if (cdev->private->flags.dosense) {
+       if (is_cmd && cdev->private->flags.dosense) {
                if (ccw_device_do_sense(cdev, irb) == 0) {
                        cdev->private->state = DEV_STATE_W4SENSE;
                }
@@ -838,13 +829,13 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
 
        irb = (struct irb *) __LC_IRB;
        /* Check for unsolicited interrupt. */
-       if (irb->scsw.stctl ==
-                       (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
-               if (irb->scsw.cc == 1)
+       if (scsw_stctl(&irb->scsw) ==
+           (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
+               if (scsw_cc(&irb->scsw) == 1)
                        /* Basic sense hasn't started. Try again. */
                        ccw_device_do_sense(cdev, irb);
                else {
-                       CIO_MSG_EVENT(2, "Huh? 0.%x.%04x: unsolicited "
+                       CIO_MSG_EVENT(0, "0.%x.%04x: unsolicited "
                                      "interrupt during w4sense...\n",
                                      cdev->private->dev_id.ssid,
                                      cdev->private->dev_id.devno);
@@ -858,7 +849,8 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
         * only deliver the halt/clear interrupt to the device driver as if it
         * had killed the original request.
         */
-       if (irb->scsw.fctl & (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
+       if (scsw_fctl(&irb->scsw) &
+           (SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
                /* Retry Basic Sense if requested. */
                if (cdev->private->flags.intretry) {
                        cdev->private->flags.intretry = 0;
@@ -932,12 +924,10 @@ ccw_device_killing_timeout(struct ccw_device *cdev, enum dev_event dev_event)
                              ERR_PTR(-EIO));
 }
 
-void device_kill_io(struct subchannel *sch)
+void ccw_device_kill_io(struct ccw_device *cdev)
 {
        int ret;
-       struct ccw_device *cdev;
 
-       cdev = sch->dev.driver_data;
        ret = ccw_device_cancel_halt_clear(cdev);
        if (ret == -EBUSY) {
                ccw_device_set_timeout(cdev, 3*HZ);
@@ -967,9 +957,9 @@ ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
        case DEV_EVENT_INTERRUPT:
                irb = (struct irb *) __LC_IRB;
                /* Check for unsolicited interrupt. */
-               if ((irb->scsw.stctl ==
+               if ((scsw_stctl(&irb->scsw) ==
                     (SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
-                   (!irb->scsw.cc))
+                   (!scsw_cc(&irb->scsw)))
                        /* FIXME: we should restart stlck here, but this
                         * is extremely unlikely ... */
                        goto out_wakeup;
@@ -990,7 +980,7 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
        struct subchannel *sch;
 
        sch = to_subchannel(cdev->dev.parent);
-       if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0)
+       if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0)
                /* Couldn't enable the subchannel for i/o. Sick device. */
                return;
 
@@ -1001,17 +991,14 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
        ccw_device_sense_id_start(cdev);
 }
 
-void
-device_trigger_reprobe(struct subchannel *sch)
+void ccw_device_trigger_reprobe(struct ccw_device *cdev)
 {
-       struct ccw_device *cdev;
+       struct subchannel *sch;
 
-       if (!sch->dev.driver_data)
-               return;
-       cdev = sch->dev.driver_data;
        if (cdev->private->state != DEV_STATE_DISCONNECTED)
                return;
 
+       sch = to_subchannel(cdev->dev.parent);
        /* Update some values. */
        if (stsch(sch->schid, &sch->schib))
                return;
@@ -1023,12 +1010,10 @@ device_trigger_reprobe(struct subchannel *sch)
         */
        sch->lpm = sch->schib.pmcw.pam & sch->opm;
        /* Re-set some bits in the pmcw that were lost. */
-       sch->schib.pmcw.isc = 3;
        sch->schib.pmcw.csense = 1;
        sch->schib.pmcw.ena = 0;
        if ((sch->lpm & (sch->lpm - 1)) != 0)
                sch->schib.pmcw.mp = 1;
-       sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
        /* We should also udate ssd info, but this has to wait. */
        /* Check if this is another device which appeared on the same sch. */
        if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
@@ -1114,8 +1099,10 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
 static void
 ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
 {
-       CIO_MSG_EVENT(0, "dev_jumptable[%i][%i] == NULL\n",
-                     cdev->private->state, dev_event);
+       CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device "
+                     "0.%x.%04x\n", cdev->private->state, dev_event,
+                     cdev->private->dev_id.ssid,
+                     cdev->private->dev_id.devno);
        BUG();
 }
 
@@ -1223,21 +1210,4 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
        },
 };
 
-/*
- * io_subchannel_irq is called for "real" interrupts or for status
- * pending conditions on msch.
- */
-void
-io_subchannel_irq (struct device *pdev)
-{
-       struct ccw_device *cdev;
-
-       cdev = to_subchannel(pdev)->dev.driver_data;
-
-       CIO_TRACE_EVENT (3, "IRQ");
-       CIO_TRACE_EVENT (3, pdev->bus_id);
-       if (cdev)
-               dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
-}
-
 EXPORT_SYMBOL_GPL(ccw_device_set_timeout);