]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/message/fusion/mptsas.c
Merge master.kernel.org:/home/rmk/linux-2.6-arm
[linux-2.6-omap-h63xx.git] / drivers / message / fusion / mptsas.c
index 7de19a84dc745dd2ea4199ffef4f17f79397d65a..17e9757e728be780211d1e7a0008aaa9e2724d18 100644 (file)
@@ -83,6 +83,7 @@ MODULE_PARM_DESC(mpt_pt_clear,
 static int     mptsasDoneCtx = -1;
 static int     mptsasTaskCtx = -1;
 static int     mptsasInternalCtx = -1; /* Used only for internal commands */
+static int     mptsasMgmtCtx = -1;
 
 
 /*
@@ -123,87 +124,6 @@ struct mptsas_portinfo {
        struct mptsas_phyinfo *phy_info;
 };
 
-/*
- * This is pretty ugly.  We will be able to seriously clean it up
- * once the DV code in mptscsih goes away and we can properly
- * implement ->target_alloc.
- */
-static int
-mptsas_slave_alloc(struct scsi_device *device)
-{
-       struct Scsi_Host        *host = device->host;
-       MPT_SCSI_HOST           *hd = (MPT_SCSI_HOST *)host->hostdata;
-       struct sas_rphy         *rphy;
-       struct mptsas_portinfo  *p;
-       VirtDevice              *vdev;
-       uint                    target = device->id;
-       int i;
-
-       if ((vdev = hd->Targets[target]) != NULL)
-               goto out;
-
-       vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL);
-       if (!vdev) {
-               printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
-                               hd->ioc->name, sizeof(VirtDevice));
-               return -ENOMEM;
-       }
-
-       memset(vdev, 0, sizeof(VirtDevice));
-       vdev->tflags = MPT_TARGET_FLAGS_Q_YES|MPT_TARGET_FLAGS_VALID_INQUIRY;
-       vdev->ioc_id = hd->ioc->id;
-
-       rphy = dev_to_rphy(device->sdev_target->dev.parent);
-       list_for_each_entry(p, &hd->ioc->sas_topology, list) {
-               for (i = 0; i < p->num_phys; i++) {
-                       if (p->phy_info[i].attached.sas_address ==
-                                       rphy->identify.sas_address) {
-                               vdev->target_id =
-                                       p->phy_info[i].attached.target;
-                               vdev->bus_id = p->phy_info[i].attached.bus;
-                               hd->Targets[device->id] = vdev;
-                               goto out;
-                       }
-               }
-       }
-
-       printk("No matching SAS device found!!\n");
-       kfree(vdev);
-       return -ENODEV;
-
- out:
-       vdev->num_luns++;
-       device->hostdata = vdev;
-       return 0;
-}
-
-static struct scsi_host_template mptsas_driver_template = {
-       .proc_name                      = "mptsas",
-       .proc_info                      = mptscsih_proc_info,
-       .name                           = "MPT SPI Host",
-       .info                           = mptscsih_info,
-       .queuecommand                   = mptscsih_qcmd,
-       .slave_alloc                    = mptsas_slave_alloc,
-       .slave_configure                = mptscsih_slave_configure,
-       .slave_destroy                  = mptscsih_slave_destroy,
-       .change_queue_depth             = mptscsih_change_queue_depth,
-       .eh_abort_handler               = mptscsih_abort,
-       .eh_device_reset_handler        = mptscsih_dev_reset,
-       .eh_bus_reset_handler           = mptscsih_bus_reset,
-       .eh_host_reset_handler          = mptscsih_host_reset,
-       .bios_param                     = mptscsih_bios_param,
-       .can_queue                      = MPT_FC_CAN_QUEUE,
-       .this_id                        = -1,
-       .sg_tablesize                   = MPT_SCSI_SG_DEPTH,
-       .max_sectors                    = 8192,
-       .cmd_per_lun                    = 7,
-       .use_clustering                 = ENABLE_CLUSTERING,
-};
-
-static struct sas_function_template mptsas_transport_functions = {
-};
-
-static struct scsi_transport_template *mptsas_transport_template;
 
 #ifdef SASDEBUG
 static void mptsas_print_phy_data(MPI_SAS_IO_UNIT0_PHY_DATA *phy_data)
@@ -244,6 +164,17 @@ static void mptsas_print_phy_pg0(SasPhyPage0_t *pg0)
        printk("\n");
 }
 
+static void mptsas_print_phy_pg1(SasPhyPage1_t *pg1)
+{
+       printk("---- SAS PHY PAGE 1 ------------\n");
+       printk("Invalid Dword Count=0x%x\n", pg1->InvalidDwordCount);
+       printk("Running Disparity Error Count=0x%x\n",
+                       pg1->RunningDisparityErrorCount);
+       printk("Loss Dword Synch Count=0x%x\n", pg1->LossDwordSynchCount);
+       printk("PHY Reset Problem Count=0x%x\n", pg1->PhyResetProblemCount);
+       printk("\n");
+}
+
 static void mptsas_print_device_pg0(SasDevicePage0_t *pg0)
 {
        __le64 sas_address;
@@ -257,6 +188,9 @@ static void mptsas_print_device_pg0(SasDevicePage0_t *pg0)
        printk("SAS Address=0x%llX\n", le64_to_cpu(sas_address));
        printk("Target ID=0x%X\n", pg0->TargetID);
        printk("Bus=0x%X\n", pg0->Bus);
+       /* The PhyNum field specifies the PHY number of the parent
+        * device this device is linked to
+        */
        printk("Parent Phy Num=0x%X\n", pg0->PhyNum);
        printk("Access Status=0x%X\n", le16_to_cpu(pg0->AccessStatus));
        printk("Device Info=0x%X\n", le32_to_cpu(pg0->DeviceInfo));
@@ -282,10 +216,252 @@ static void mptsas_print_expander_pg1(SasExpanderPage1_t *pg1)
 #else
 #define mptsas_print_phy_data(phy_data)                do { } while (0)
 #define mptsas_print_phy_pg0(pg0)              do { } while (0)
+#define mptsas_print_phy_pg1(pg1)              do { } while (0)
 #define mptsas_print_device_pg0(pg0)           do { } while (0)
 #define mptsas_print_expander_pg1(pg1)         do { } while (0)
 #endif
 
+
+/*
+ * This is pretty ugly.  We will be able to seriously clean it up
+ * once the DV code in mptscsih goes away and we can properly
+ * implement ->target_alloc.
+ */
+static int
+mptsas_slave_alloc(struct scsi_device *sdev)
+{
+       struct Scsi_Host        *host = sdev->host;
+       MPT_SCSI_HOST           *hd = (MPT_SCSI_HOST *)host->hostdata;
+       struct sas_rphy         *rphy;
+       struct mptsas_portinfo  *p;
+       VirtTarget              *vtarget;
+       VirtDevice              *vdev;
+       struct scsi_target      *starget;
+       int i;
+
+       vdev = kmalloc(sizeof(VirtDevice), GFP_KERNEL);
+       if (!vdev) {
+               printk(MYIOC_s_ERR_FMT "slave_alloc kmalloc(%zd) FAILED!\n",
+                               hd->ioc->name, sizeof(VirtDevice));
+               return -ENOMEM;
+       }
+       memset(vdev, 0, sizeof(VirtDevice));
+       vdev->ioc_id = hd->ioc->id;
+       sdev->hostdata = vdev;
+       starget = scsi_target(sdev);
+       vtarget = starget->hostdata;
+       vdev->vtarget = vtarget;
+       if (vtarget->num_luns == 0) {
+               vtarget->tflags = MPT_TARGET_FLAGS_Q_YES|MPT_TARGET_FLAGS_VALID_INQUIRY;
+               hd->Targets[sdev->id] = vtarget;
+       }
+
+       rphy = dev_to_rphy(sdev->sdev_target->dev.parent);
+       list_for_each_entry(p, &hd->ioc->sas_topology, list) {
+               for (i = 0; i < p->num_phys; i++) {
+                       if (p->phy_info[i].attached.sas_address ==
+                                       rphy->identify.sas_address) {
+                               vdev->target_id =
+                                       p->phy_info[i].attached.target;
+                               vdev->bus_id = p->phy_info[i].attached.bus;
+                               vdev->lun = sdev->lun;
+                               goto out;
+                       }
+               }
+       }
+
+       printk("No matching SAS device found!!\n");
+       kfree(vdev);
+       return -ENODEV;
+
+ out:
+       vtarget->ioc_id = vdev->ioc_id;
+       vtarget->target_id = vdev->target_id;
+       vtarget->bus_id = vdev->bus_id;
+       vtarget->num_luns++;
+       return 0;
+}
+
+static struct scsi_host_template mptsas_driver_template = {
+       .module                         = THIS_MODULE,
+       .proc_name                      = "mptsas",
+       .proc_info                      = mptscsih_proc_info,
+       .name                           = "MPT SPI Host",
+       .info                           = mptscsih_info,
+       .queuecommand                   = mptscsih_qcmd,
+       .target_alloc                   = mptscsih_target_alloc,
+       .slave_alloc                    = mptsas_slave_alloc,
+       .slave_configure                = mptscsih_slave_configure,
+       .target_destroy                 = mptscsih_target_destroy,
+       .slave_destroy                  = mptscsih_slave_destroy,
+       .change_queue_depth             = mptscsih_change_queue_depth,
+       .eh_abort_handler               = mptscsih_abort,
+       .eh_device_reset_handler        = mptscsih_dev_reset,
+       .eh_bus_reset_handler           = mptscsih_bus_reset,
+       .eh_host_reset_handler          = mptscsih_host_reset,
+       .bios_param                     = mptscsih_bios_param,
+       .can_queue                      = MPT_FC_CAN_QUEUE,
+       .this_id                        = -1,
+       .sg_tablesize                   = MPT_SCSI_SG_DEPTH,
+       .max_sectors                    = 8192,
+       .cmd_per_lun                    = 7,
+       .use_clustering                 = ENABLE_CLUSTERING,
+};
+
+static inline MPT_ADAPTER *phy_to_ioc(struct sas_phy *phy)
+{
+       struct Scsi_Host *shost = dev_to_shost(phy->dev.parent);
+       return ((MPT_SCSI_HOST *)shost->hostdata)->ioc;
+}
+
+static int mptsas_get_linkerrors(struct sas_phy *phy)
+{
+       MPT_ADAPTER *ioc = phy_to_ioc(phy);
+       ConfigExtendedPageHeader_t hdr;
+       CONFIGPARMS cfg;
+       SasPhyPage1_t *buffer;
+       dma_addr_t dma_handle;
+       int error;
+
+       hdr.PageVersion = MPI_SASPHY1_PAGEVERSION;
+       hdr.ExtPageLength = 0;
+       hdr.PageNumber = 1 /* page number 1*/;
+       hdr.Reserved1 = 0;
+       hdr.Reserved2 = 0;
+       hdr.PageType = MPI_CONFIG_PAGETYPE_EXTENDED;
+       hdr.ExtPageType = MPI_CONFIG_EXTPAGETYPE_SAS_PHY;
+
+       cfg.cfghdr.ehdr = &hdr;
+       cfg.physAddr = -1;
+       cfg.pageAddr = phy->identify.phy_identifier;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+       cfg.dir = 0;    /* read */
+       cfg.timeout = 10;
+
+       error = mpt_config(ioc, &cfg);
+       if (error)
+               return error;
+       if (!hdr.ExtPageLength)
+               return -ENXIO;
+
+       buffer = pci_alloc_consistent(ioc->pcidev, hdr.ExtPageLength * 4,
+                                     &dma_handle);
+       if (!buffer)
+               return -ENOMEM;
+
+       cfg.physAddr = dma_handle;
+       cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+
+       error = mpt_config(ioc, &cfg);
+       if (error)
+               goto out_free_consistent;
+
+       mptsas_print_phy_pg1(buffer);
+
+       phy->invalid_dword_count = le32_to_cpu(buffer->InvalidDwordCount);
+       phy->running_disparity_error_count =
+               le32_to_cpu(buffer->RunningDisparityErrorCount);
+       phy->loss_of_dword_sync_count =
+               le32_to_cpu(buffer->LossDwordSynchCount);
+       phy->phy_reset_problem_count =
+               le32_to_cpu(buffer->PhyResetProblemCount);
+
+ out_free_consistent:
+       pci_free_consistent(ioc->pcidev, hdr.ExtPageLength * 4,
+                           buffer, dma_handle);
+       return error;
+}
+
+static int mptsas_mgmt_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *req,
+               MPT_FRAME_HDR *reply)
+{
+       ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_COMMAND_GOOD;
+       if (reply != NULL) {
+               ioc->sas_mgmt.status |= MPT_SAS_MGMT_STATUS_RF_VALID;
+               memcpy(ioc->sas_mgmt.reply, reply,
+                   min(ioc->reply_sz, 4 * reply->u.reply.MsgLength));
+       }
+       complete(&ioc->sas_mgmt.done);
+       return 1;
+}
+
+static int mptsas_phy_reset(struct sas_phy *phy, int hard_reset)
+{
+       MPT_ADAPTER *ioc = phy_to_ioc(phy);
+       SasIoUnitControlRequest_t *req;
+       SasIoUnitControlReply_t *reply;
+       MPT_FRAME_HDR *mf;
+       MPIHeader_t *hdr;
+       unsigned long timeleft;
+       int error = -ERESTARTSYS;
+
+       /* not implemented for expanders */
+       if (phy->identify.target_port_protocols & SAS_PROTOCOL_SMP)
+               return -ENXIO;
+
+       if (down_interruptible(&ioc->sas_mgmt.mutex))
+               goto out;
+
+       mf = mpt_get_msg_frame(mptsasMgmtCtx, ioc);
+       if (!mf) {
+               error = -ENOMEM;
+               goto out_unlock;
+       }
+
+       hdr = (MPIHeader_t *) mf;
+       req = (SasIoUnitControlRequest_t *)mf;
+       memset(req, 0, sizeof(SasIoUnitControlRequest_t));
+       req->Function = MPI_FUNCTION_SAS_IO_UNIT_CONTROL;
+       req->MsgContext = hdr->MsgContext;
+       req->Operation = hard_reset ?
+               MPI_SAS_OP_PHY_HARD_RESET : MPI_SAS_OP_PHY_LINK_RESET;
+       req->PhyNum = phy->identify.phy_identifier;
+
+       mpt_put_msg_frame(mptsasMgmtCtx, ioc, mf);
+
+       timeleft = wait_for_completion_timeout(&ioc->sas_mgmt.done,
+                       10 * HZ);
+       if (!timeleft) {
+               /* On timeout reset the board */
+               mpt_free_msg_frame(ioc, mf);
+               mpt_HardResetHandler(ioc, CAN_SLEEP);
+               error = -ETIMEDOUT;
+               goto out_unlock;
+       }
+
+       /* a reply frame is expected */
+       if ((ioc->sas_mgmt.status &
+           MPT_IOCTL_STATUS_RF_VALID) == 0) {
+               error = -ENXIO;
+               goto out_unlock;
+       }
+
+       /* process the completed Reply Message Frame */
+       reply = (SasIoUnitControlReply_t *)ioc->sas_mgmt.reply;
+       if (reply->IOCStatus != MPI_IOCSTATUS_SUCCESS) {
+               printk("%s: IOCStatus=0x%X IOCLogInfo=0x%X\n",
+                   __FUNCTION__,
+                   reply->IOCStatus,
+                   reply->IOCLogInfo);
+               error = -ENXIO;
+               goto out_unlock;
+       }
+
+       error = 0;
+
+ out_unlock:
+       up(&ioc->sas_mgmt.mutex);
+ out:
+       return error;
+}
+
+static struct sas_function_template mptsas_transport_functions = {
+       .get_linkerrors         = mptsas_get_linkerrors,
+       .phy_reset              = mptsas_phy_reset,
+};
+
+static struct scsi_transport_template *mptsas_transport_template;
+
 static int
 mptsas_sas_io_unit_pg0(MPT_ADAPTER *ioc, struct mptsas_portinfo *port_info)
 {
@@ -680,7 +856,7 @@ mptsas_parse_device_info(struct sas_identify *identify,
 }
 
 static int mptsas_probe_one_phy(struct device *dev,
-               struct mptsas_phyinfo *phy_info, int index)
+               struct mptsas_phyinfo *phy_info, int index, int local)
 {
        struct sas_phy *port;
        int error;
@@ -773,6 +949,9 @@ static int mptsas_probe_one_phy(struct device *dev,
                break;
        }
 
+       if (local)
+               port->local_attached = 1;
+
        error = sas_phy_add(port);
        if (error) {
                sas_phy_free(port);
@@ -816,7 +995,6 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index)
                goto out_free_port_info;
 
        list_add_tail(&port_info->list, &ioc->sas_topology);
-
        for (i = 0; i < port_info->num_phys; i++) {
                mptsas_sas_phy_pg0(ioc, &port_info->phy_info[i],
                        (MPI_SAS_PHY_PGAD_FORM_PHY_NUMBER <<
@@ -838,7 +1016,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc, int *index)
                }
 
                mptsas_probe_one_phy(&ioc->sh->shost_gendev,
-                                    &port_info->phy_info[i], *index);
+                                    &port_info->phy_info[i], *index, 1);
                (*index)++;
        }
 
@@ -909,7 +1087,8 @@ mptsas_probe_expander_phys(MPT_ADAPTER *ioc, u32 *handle, int *index)
                        }
                }
 
-               mptsas_probe_one_phy(parent, &port_info->phy_info[i], *index);
+               mptsas_probe_one_phy(parent, &port_info->phy_info[i],
+                                    *index, 0);
                (*index)++;
        }
 
@@ -962,13 +1141,15 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                printk(MYIOC_s_WARN_FMT
                  "Skipping because it's not operational!\n",
                  ioc->name);
-               return -ENODEV;
+               error = -ENODEV;
+               goto out_mptsas_probe;
        }
 
        if (!ioc->active) {
                printk(MYIOC_s_WARN_FMT "Skipping because it's disabled!\n",
                  ioc->name);
-               return -ENODEV;
+               error = -ENODEV;
+               goto out_mptsas_probe;
        }
 
        /*  Sanity check - ensure at least 1 port is INITIATOR capable
@@ -992,7 +1173,8 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
                printk(MYIOC_s_WARN_FMT
                        "Unable to register controller with SCSI subsystem\n",
                        ioc->name);
-                return -1;
+               error = -1;
+               goto out_mptsas_probe;
         }
 
        spin_lock_irqsave(&ioc->FreeQlock, flags);
@@ -1021,6 +1203,8 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        sh->unique_id = ioc->id;
 
        INIT_LIST_HEAD(&ioc->sas_topology);
+       init_MUTEX(&ioc->sas_mgmt.mutex);
+       init_completion(&ioc->sas_mgmt.done);
 
        /* Verify that we won't exceed the maximum
         * number of chain buffers
@@ -1064,7 +1248,7 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        mem = kmalloc(sz, GFP_ATOMIC);
        if (mem == NULL) {
                error = -ENOMEM;
-               goto mptsas_probe_failed;
+               goto out_mptsas_probe;
        }
 
        memset(mem, 0, sz);
@@ -1082,14 +1266,14 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        mem = kmalloc(sz, GFP_ATOMIC);
        if (mem == NULL) {
                error = -ENOMEM;
-               goto mptsas_probe_failed;
+               goto out_mptsas_probe;
        }
 
        memset(mem, 0, sz);
-       hd->Targets = (VirtDevice **) mem;
+       hd->Targets = (VirtTarget **) mem;
 
        dprintk((KERN_INFO
-         "  Targets @ %p, sz=%d\n", hd->Targets, sz));
+         "  vtarget @ %p, sz=%d\n", hd->Targets, sz));
 
        /* Clear the TM flags
         */
@@ -1135,14 +1319,14 @@ mptsas_probe(struct pci_dev *pdev, const struct pci_device_id *id)
        if (error) {
                dprintk((KERN_ERR MYNAM
                  "scsi_add_host failed\n"));
-               goto mptsas_probe_failed;
+               goto out_mptsas_probe;
        }
 
        mptsas_scan_sas_topology(ioc);
 
        return 0;
 
-mptsas_probe_failed:
+out_mptsas_probe:
 
        mptscsih_remove(pdev);
        return error;
@@ -1207,6 +1391,7 @@ mptsas_init(void)
        mptsasTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSAS_DRIVER);
        mptsasInternalCtx =
                mpt_register(mptscsih_scandv_complete, MPTSAS_DRIVER);
+       mptsasMgmtCtx = mpt_register(mptsas_mgmt_done, MPTSAS_DRIVER);
 
        if (mpt_event_register(mptsasDoneCtx, mptscsih_event_process) == 0) {
                devtprintk((KERN_INFO MYNAM
@@ -1230,6 +1415,7 @@ mptsas_exit(void)
        mpt_reset_deregister(mptsasDoneCtx);
        mpt_event_deregister(mptsasDoneCtx);
 
+       mpt_deregister(mptsasMgmtCtx);
        mpt_deregister(mptsasInternalCtx);
        mpt_deregister(mptsasTaskCtx);
        mpt_deregister(mptsasDoneCtx);