Merge branch 'release-2.6.27' of git://git.kernel.org/pub/scm/linux/kernel/git/ak...
[pandora-kernel.git] / drivers / message / fusion / mptbase.c
index d40d6d1..34402c4 100644 (file)
@@ -5,7 +5,7 @@
  *      For use with LSI PCI chip/adapter(s)
  *      running LSI Fusion MPT (Message Passing Technology) firmware.
  *
- *  Copyright (c) 1999-2007 LSI Corporation
+ *  Copyright (c) 1999-2008 LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  */
@@ -103,7 +103,7 @@ static int mfcounter = 0;
  *  Public data...
  */
 
-struct proc_dir_entry *mpt_proc_root_dir;
+static struct proc_dir_entry *mpt_proc_root_dir;
 
 #define WHOINIT_UNKNOWN                0xAA
 
@@ -253,6 +253,55 @@ mpt_get_cb_idx(MPT_DRIVER_CLASS dclass)
        return 0;
 }
 
+/**
+ *     mpt_fault_reset_work - work performed on workq after ioc fault
+ *     @work: input argument, used to derive ioc
+ *
+**/
+static void
+mpt_fault_reset_work(struct work_struct *work)
+{
+       MPT_ADAPTER     *ioc =
+           container_of(work, MPT_ADAPTER, fault_reset_work.work);
+       u32              ioc_raw_state;
+       int              rc;
+       unsigned long    flags;
+
+       if (ioc->diagPending || !ioc->active)
+               goto out;
+
+       ioc_raw_state = mpt_GetIocState(ioc, 0);
+       if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT) {
+               printk(MYIOC_s_WARN_FMT "IOC is in FAULT state (%04xh)!!!\n",
+                   ioc->name, ioc_raw_state & MPI_DOORBELL_DATA_MASK);
+               printk(MYIOC_s_WARN_FMT "Issuing HardReset from %s!!\n",
+                   ioc->name, __FUNCTION__);
+               rc = mpt_HardResetHandler(ioc, CAN_SLEEP);
+               printk(MYIOC_s_WARN_FMT "%s: HardReset: %s\n", ioc->name,
+                   __FUNCTION__, (rc == 0) ? "success" : "failed");
+               ioc_raw_state = mpt_GetIocState(ioc, 0);
+               if ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_FAULT)
+                       printk(MYIOC_s_WARN_FMT "IOC is in FAULT state after "
+                           "reset (%04xh)\n", ioc->name, ioc_raw_state &
+                           MPI_DOORBELL_DATA_MASK);
+       }
+
+ out:
+       /*
+        * Take turns polling alternate controller
+        */
+       if (ioc->alt_ioc)
+               ioc = ioc->alt_ioc;
+
+       /* rearm the timer */
+       spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+       if (ioc->reset_work_q)
+               queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
+                       msecs_to_jiffies(MPT_POLLING_INTERVAL));
+       spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+}
+
+
 /*
  *  Process turbo (context) reply...
  */
@@ -1616,6 +1665,23 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        /* Find lookup slot. */
        INIT_LIST_HEAD(&ioc->list);
 
+
+       /* Initialize workqueue */
+       INIT_DELAYED_WORK(&ioc->fault_reset_work, mpt_fault_reset_work);
+       spin_lock_init(&ioc->fault_reset_work_lock);
+
+       snprintf(ioc->reset_work_q_name, sizeof(ioc->reset_work_q_name),
+                "mpt_poll_%d", ioc->id);
+       ioc->reset_work_q =
+               create_singlethread_workqueue(ioc->reset_work_q_name);
+       if (!ioc->reset_work_q) {
+               printk(MYIOC_s_ERR_FMT "Insufficient memory to add adapter!\n",
+                   ioc->name);
+               pci_release_selected_regions(pdev, ioc->bars);
+               kfree(ioc);
+               return -ENOMEM;
+       }
+
        dinitprintk(ioc, printk(MYIOC_s_INFO_FMT "facts @ %p, pfacts[0] @ %p\n",
            ioc->name, &ioc->facts, &ioc->pfacts[0]));
 
@@ -1727,6 +1793,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
                iounmap(ioc->memmap);
                if (r != -5)
                        pci_release_selected_regions(pdev, ioc->bars);
+
+               destroy_workqueue(ioc->reset_work_q);
+               ioc->reset_work_q = NULL;
+
                kfree(ioc);
                pci_set_drvdata(pdev, NULL);
                return r;
@@ -1759,6 +1829,10 @@ mpt_attach(struct pci_dev *pdev, const struct pci_device_id *id)
        }
 #endif
 
+       if (!ioc->alt_ioc)
+               queue_delayed_work(ioc->reset_work_q, &ioc->fault_reset_work,
+                       msecs_to_jiffies(MPT_POLLING_INTERVAL));
+
        return 0;
 }
 
@@ -1774,6 +1848,19 @@ mpt_detach(struct pci_dev *pdev)
        MPT_ADAPTER     *ioc = pci_get_drvdata(pdev);
        char pname[32];
        u8 cb_idx;
+       unsigned long flags;
+       struct workqueue_struct *wq;
+
+       /*
+        * Stop polling ioc for fault condition
+        */
+       spin_lock_irqsave(&ioc->fault_reset_work_lock, flags);
+       wq = ioc->reset_work_q;
+       ioc->reset_work_q = NULL;
+       spin_unlock_irqrestore(&ioc->fault_reset_work_lock, flags);
+       cancel_delayed_work(&ioc->fault_reset_work);
+       destroy_workqueue(wq);
+
 
        sprintf(pname, MPT_PROCFS_MPTBASEDIR "/%s/summary", ioc->name);
        remove_proc_entry(pname, NULL);
@@ -7456,7 +7543,6 @@ EXPORT_SYMBOL(mpt_resume);
 EXPORT_SYMBOL(mpt_suspend);
 #endif
 EXPORT_SYMBOL(ioc_list);
-EXPORT_SYMBOL(mpt_proc_root_dir);
 EXPORT_SYMBOL(mpt_register);
 EXPORT_SYMBOL(mpt_deregister);
 EXPORT_SYMBOL(mpt_event_register);