Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/rzhang/linux
[cascardo/linux.git] / drivers / scsi / mpt3sas / mpt3sas_base.c
index 93ce2b2..1560115 100644 (file)
@@ -3,7 +3,7 @@
  * for access to MPT (Message Passing Technology) firmware.
  *
  * This code is based on drivers/scsi/mpt3sas/mpt3sas_base.c
- * Copyright (C) 2012-2013  LSI Corporation
+ * Copyright (C) 2012-2014  LSI Corporation
  *  (mailto:DL-MPTFusionLinux@lsi.com)
  *
  * This program is free software; you can redistribute it and/or
@@ -91,6 +91,8 @@ static int mpt3sas_fwfault_debug;
 MODULE_PARM_DESC(mpt3sas_fwfault_debug,
        " enable detection of firmware fault and halt firmware - (default=0)");
 
+static int
+_base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag);
 
 /**
  * _scsih_set_fwfault_debug - global setting of ioc->fwfault_debug.
@@ -1482,17 +1484,22 @@ static int
 _base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
 {
        struct sysinfo s;
-       char *desc = NULL;
+       u64 consistent_dma_mask;
+
+       if (ioc->dma_mask)
+               consistent_dma_mask = DMA_BIT_MASK(64);
+       else
+               consistent_dma_mask = DMA_BIT_MASK(32);
 
        if (sizeof(dma_addr_t) > 4) {
                const uint64_t required_mask =
                    dma_get_required_mask(&pdev->dev);
                if ((required_mask > DMA_BIT_MASK(32)) &&
                    !pci_set_dma_mask(pdev, DMA_BIT_MASK(64)) &&
-                   !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
+                   !pci_set_consistent_dma_mask(pdev, consistent_dma_mask)) {
                        ioc->base_add_sg_single = &_base_add_sg_single_64;
                        ioc->sge_size = sizeof(Mpi2SGESimple64_t);
-                       desc = "64";
+                       ioc->dma_mask = 64;
                        goto out;
                }
        }
@@ -1501,19 +1508,30 @@ _base_config_dma_addressing(struct MPT3SAS_ADAPTER *ioc, struct pci_dev *pdev)
            && !pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32))) {
                ioc->base_add_sg_single = &_base_add_sg_single_32;
                ioc->sge_size = sizeof(Mpi2SGESimple32_t);
-               desc = "32";
+               ioc->dma_mask = 32;
        } else
                return -ENODEV;
 
  out:
        si_meminfo(&s);
        pr_info(MPT3SAS_FMT
-               "%s BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n",
-               ioc->name, desc, convert_to_kb(s.totalram));
+               "%d BIT PCI BUS DMA ADDRESSING SUPPORTED, total mem (%ld kB)\n",
+               ioc->name, ioc->dma_mask, convert_to_kb(s.totalram));
 
        return 0;
 }
 
+static int
+_base_change_consistent_dma_mask(struct MPT3SAS_ADAPTER *ioc,
+                                     struct pci_dev *pdev)
+{
+       if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64))) {
+               if (pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)))
+                       return -ENODEV;
+       }
+       return 0;
+}
+
 /**
  * _base_check_enable_msix - checks MSIX capabable.
  * @ioc: per adapter object
@@ -1698,11 +1716,15 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc)
          ": %d, max_msix_vectors: %d\n", ioc->name, ioc->msix_vector_count,
          ioc->cpu_count, max_msix_vectors);
 
+       if (!ioc->rdpq_array_enable && max_msix_vectors == -1)
+               max_msix_vectors = 8;
+
        if (max_msix_vectors > 0) {
                ioc->reply_queue_count = min_t(int, max_msix_vectors,
                        ioc->reply_queue_count);
                ioc->msix_vector_count = ioc->reply_queue_count;
-       }
+       } else if (max_msix_vectors == 0)
+               goto try_ioapic;
 
        entries = kcalloc(ioc->reply_queue_count, sizeof(struct msix_entry),
            GFP_KERNEL);
@@ -1716,10 +1738,10 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc)
        for (i = 0, a = entries; i < ioc->reply_queue_count; i++, a++)
                a->entry = i;
 
-       r = pci_enable_msix(ioc->pdev, entries, ioc->reply_queue_count);
+       r = pci_enable_msix_exact(ioc->pdev, entries, ioc->reply_queue_count);
        if (r) {
                dfailprintk(ioc, pr_info(MPT3SAS_FMT
-                       "pci_enable_msix failed (r=%d) !!!\n",
+                       "pci_enable_msix_exact failed (r=%d) !!!\n",
                        ioc->name, r));
                kfree(entries);
                goto try_ioapic;
@@ -1742,6 +1764,7 @@ _base_enable_msix(struct MPT3SAS_ADAPTER *ioc)
 /* failback to io_apic interrupt routing */
  try_ioapic:
 
+       ioc->reply_queue_count = 1;
        r = _base_request_irq(ioc, 0, ioc->pdev->irq);
 
        return r;
@@ -1821,6 +1844,16 @@ mpt3sas_base_map_resources(struct MPT3SAS_ADAPTER *ioc)
        }
 
        _base_mask_interrupts(ioc);
+
+       r = _base_get_ioc_facts(ioc, CAN_SLEEP);
+       if (r)
+               goto out_fail;
+
+       if (!ioc->rdpq_array_enable_assigned) {
+               ioc->rdpq_array_enable = ioc->rdpq_array_capable;
+               ioc->rdpq_array_enable_assigned = 1;
+       }
+
        r = _base_enable_msix(ioc);
        if (r)
                goto out_fail;
@@ -2185,6 +2218,53 @@ mpt3sas_base_put_smid_default(struct MPT3SAS_ADAPTER *ioc, u16 smid)
            &ioc->scsi_lookup_lock);
 }
 
+/**
+ * _base_display_intel_branding - Display branding string
+ * @ioc: per adapter object
+ *
+ * Return nothing.
+ */
+static void
+_base_display_intel_branding(struct MPT3SAS_ADAPTER *ioc)
+{
+       if (ioc->pdev->subsystem_vendor != PCI_VENDOR_ID_INTEL)
+               return;
+
+       switch (ioc->pdev->device) {
+       case MPI25_MFGPAGE_DEVID_SAS3008:
+               switch (ioc->pdev->subsystem_device) {
+               case MPT3SAS_INTEL_RMS3JC080_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_INTEL_RMS3JC080_BRANDING);
+                       break;
+
+               case MPT3SAS_INTEL_RS3GC008_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_INTEL_RS3GC008_BRANDING);
+                       break;
+               case MPT3SAS_INTEL_RS3FC044_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_INTEL_RS3FC044_BRANDING);
+                       break;
+               case MPT3SAS_INTEL_RS3UC080_SSDID:
+                       pr_info(MPT3SAS_FMT "%s\n", ioc->name,
+                               MPT3SAS_INTEL_RS3UC080_BRANDING);
+                       break;
+               default:
+                       pr_info(MPT3SAS_FMT
+                               "Intel(R) Controller: Subsystem ID: 0x%X\n",
+                               ioc->name, ioc->pdev->subsystem_device);
+                       break;
+               }
+               break;
+       default:
+               pr_info(MPT3SAS_FMT
+                       "Intel(R) Controller: Subsystem ID: 0x%X\n",
+                       ioc->name, ioc->pdev->subsystem_device);
+               break;
+       }
+}
+
 
 
 /**
@@ -2216,6 +2296,8 @@ _base_display_ioc_capabilities(struct MPT3SAS_ADAPTER *ioc)
           (bios_version & 0x0000FF00) >> 8,
            bios_version & 0x000000FF);
 
+       _base_display_intel_branding(ioc);
+
        pr_info(MPT3SAS_FMT "Protocol=(", ioc->name);
 
        if (ioc->facts.ProtocolFlags & MPI2_IOCFACTS_PROTOCOL_SCSI_INITIATOR) {
@@ -2447,7 +2529,8 @@ _base_static_config_pages(struct MPT3SAS_ADAPTER *ioc)
 static void
 _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc)
 {
-       int i;
+       int i = 0;
+       struct reply_post_struct *rps;
 
        dexitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
            __func__));
@@ -2492,15 +2575,25 @@ _base_release_memory_pools(struct MPT3SAS_ADAPTER *ioc)
                ioc->reply_free = NULL;
        }
 
-       if (ioc->reply_post_free) {
-               pci_pool_free(ioc->reply_post_free_dma_pool,
-                   ioc->reply_post_free, ioc->reply_post_free_dma);
+       if (ioc->reply_post) {
+               do {
+                       rps = &ioc->reply_post[i];
+                       if (rps->reply_post_free) {
+                               pci_pool_free(
+                                   ioc->reply_post_free_dma_pool,
+                                   rps->reply_post_free,
+                                   rps->reply_post_free_dma);
+                               dexitprintk(ioc, pr_info(MPT3SAS_FMT
+                                   "reply_post_free_pool(0x%p): free\n",
+                                   ioc->name, rps->reply_post_free));
+                               rps->reply_post_free = NULL;
+                       }
+               } while (ioc->rdpq_array_enable &&
+                          (++i < ioc->reply_queue_count));
+
                if (ioc->reply_post_free_dma_pool)
                        pci_pool_destroy(ioc->reply_post_free_dma_pool);
-               dexitprintk(ioc, pr_info(MPT3SAS_FMT
-                   "reply_post_free_pool(0x%p): free\n", ioc->name,
-                   ioc->reply_post_free));
-               ioc->reply_post_free = NULL;
+               kfree(ioc->reply_post);
        }
 
        if (ioc->config_page) {
@@ -2647,6 +2740,65 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc,  int sleep_flag)
            ioc->max_sges_in_chain_message, ioc->shost->sg_tablesize,
            ioc->chains_needed_per_io));
 
+       /* reply post queue, 16 byte align */
+       reply_post_free_sz = ioc->reply_post_queue_depth *
+           sizeof(Mpi2DefaultReplyDescriptor_t);
+
+       sz = reply_post_free_sz;
+       if (_base_is_controller_msix_enabled(ioc) && !ioc->rdpq_array_enable)
+               sz *= ioc->reply_queue_count;
+
+       ioc->reply_post = kcalloc((ioc->rdpq_array_enable) ?
+           (ioc->reply_queue_count):1,
+           sizeof(struct reply_post_struct), GFP_KERNEL);
+
+       if (!ioc->reply_post) {
+               pr_err(MPT3SAS_FMT "reply_post_free pool: kcalloc failed\n",
+                       ioc->name);
+               goto out;
+       }
+       ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool",
+           ioc->pdev, sz, 16, 0);
+       if (!ioc->reply_post_free_dma_pool) {
+               pr_err(MPT3SAS_FMT
+                "reply_post_free pool: pci_pool_create failed\n",
+                ioc->name);
+               goto out;
+       }
+       i = 0;
+       do {
+               ioc->reply_post[i].reply_post_free =
+                   pci_pool_alloc(ioc->reply_post_free_dma_pool,
+                   GFP_KERNEL,
+                   &ioc->reply_post[i].reply_post_free_dma);
+               if (!ioc->reply_post[i].reply_post_free) {
+                       pr_err(MPT3SAS_FMT
+                       "reply_post_free pool: pci_pool_alloc failed\n",
+                       ioc->name);
+                       goto out;
+               }
+               memset(ioc->reply_post[i].reply_post_free, 0, sz);
+               dinitprintk(ioc, pr_info(MPT3SAS_FMT
+                   "reply post free pool (0x%p): depth(%d),"
+                   "element_size(%d), pool_size(%d kB)\n", ioc->name,
+                   ioc->reply_post[i].reply_post_free,
+                   ioc->reply_post_queue_depth, 8, sz/1024));
+               dinitprintk(ioc, pr_info(MPT3SAS_FMT
+                   "reply_post_free_dma = (0x%llx)\n", ioc->name,
+                   (unsigned long long)
+                   ioc->reply_post[i].reply_post_free_dma));
+               total_sz += sz;
+       } while (ioc->rdpq_array_enable && (++i < ioc->reply_queue_count));
+
+       if (ioc->dma_mask == 64) {
+               if (_base_change_consistent_dma_mask(ioc, ioc->pdev) != 0) {
+                       pr_warn(MPT3SAS_FMT
+                           "no suitable consistent DMA mask for %s\n",
+                           ioc->name, pci_name(ioc->pdev));
+                       goto out;
+               }
+       }
+
        ioc->scsiio_depth = ioc->hba_queue_depth -
            ioc->hi_priority_depth - ioc->internal_depth;
 
@@ -2861,40 +3013,6 @@ _base_allocate_memory_pools(struct MPT3SAS_ADAPTER *ioc,  int sleep_flag)
                ioc->name, (unsigned long long)ioc->reply_free_dma));
        total_sz += sz;
 
-       /* reply post queue, 16 byte align */
-       reply_post_free_sz = ioc->reply_post_queue_depth *
-           sizeof(Mpi2DefaultReplyDescriptor_t);
-       if (_base_is_controller_msix_enabled(ioc))
-               sz = reply_post_free_sz * ioc->reply_queue_count;
-       else
-               sz = reply_post_free_sz;
-       ioc->reply_post_free_dma_pool = pci_pool_create("reply_post_free pool",
-           ioc->pdev, sz, 16, 0);
-       if (!ioc->reply_post_free_dma_pool) {
-               pr_err(MPT3SAS_FMT
-                       "reply_post_free pool: pci_pool_create failed\n",
-                       ioc->name);
-               goto out;
-       }
-       ioc->reply_post_free = pci_pool_alloc(ioc->reply_post_free_dma_pool ,
-           GFP_KERNEL, &ioc->reply_post_free_dma);
-       if (!ioc->reply_post_free) {
-               pr_err(MPT3SAS_FMT
-                       "reply_post_free pool: pci_pool_alloc failed\n",
-                       ioc->name);
-               goto out;
-       }
-       memset(ioc->reply_post_free, 0, sz);
-       dinitprintk(ioc, pr_info(MPT3SAS_FMT "reply post free pool" \
-           "(0x%p): depth(%d), element_size(%d), pool_size(%d kB)\n",
-           ioc->name, ioc->reply_post_free, ioc->reply_post_queue_depth, 8,
-           sz/1024));
-       dinitprintk(ioc, pr_info(MPT3SAS_FMT
-               "reply_post_free_dma = (0x%llx)\n",
-               ioc->name, (unsigned long long)
-           ioc->reply_post_free_dma));
-       total_sz += sz;
-
        ioc->config_page_sz = 512;
        ioc->config_page = pci_alloc_consistent(ioc->pdev,
            ioc->config_page_sz, &ioc->config_page_dma);
@@ -3577,6 +3695,9 @@ _base_get_ioc_facts(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
        facts->IOCCapabilities = le32_to_cpu(mpi_reply.IOCCapabilities);
        if ((facts->IOCCapabilities & MPI2_IOCFACTS_CAPABILITY_INTEGRATED_RAID))
                ioc->ir_firmware = 1;
+       if ((facts->IOCCapabilities &
+             MPI2_IOCFACTS_CAPABILITY_RDPQ_ARRAY_CAPABLE))
+               ioc->rdpq_array_capable = 1;
        facts->FWVersion.Word = le32_to_cpu(mpi_reply.FWVersion.Word);
        facts->IOCRequestFrameSize =
            le16_to_cpu(mpi_reply.IOCRequestFrameSize);
@@ -3613,9 +3734,12 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
 {
        Mpi2IOCInitRequest_t mpi_request;
        Mpi2IOCInitReply_t mpi_reply;
-       int r;
+       int i, r = 0;
        struct timeval current_time;
        u16 ioc_status;
+       u32 reply_post_free_array_sz = 0;
+       Mpi2IOCInitRDPQArrayEntry *reply_post_free_array = NULL;
+       dma_addr_t reply_post_free_array_dma;
 
        dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
            __func__));
@@ -3644,9 +3768,31 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
            cpu_to_le64((u64)ioc->request_dma);
        mpi_request.ReplyFreeQueueAddress =
            cpu_to_le64((u64)ioc->reply_free_dma);
-       mpi_request.ReplyDescriptorPostQueueAddress =
-           cpu_to_le64((u64)ioc->reply_post_free_dma);
 
+       if (ioc->rdpq_array_enable) {
+               reply_post_free_array_sz = ioc->reply_queue_count *
+                   sizeof(Mpi2IOCInitRDPQArrayEntry);
+               reply_post_free_array = pci_alloc_consistent(ioc->pdev,
+                       reply_post_free_array_sz, &reply_post_free_array_dma);
+               if (!reply_post_free_array) {
+                       pr_err(MPT3SAS_FMT
+                       "reply_post_free_array: pci_alloc_consistent failed\n",
+                       ioc->name);
+                       r = -ENOMEM;
+                       goto out;
+               }
+               memset(reply_post_free_array, 0, reply_post_free_array_sz);
+               for (i = 0; i < ioc->reply_queue_count; i++)
+                       reply_post_free_array[i].RDPQBaseAddress =
+                           cpu_to_le64(
+                               (u64)ioc->reply_post[i].reply_post_free_dma);
+               mpi_request.MsgFlags = MPI2_IOCINIT_MSGFLAG_RDPQ_ARRAY_MODE;
+               mpi_request.ReplyDescriptorPostQueueAddress =
+                   cpu_to_le64((u64)reply_post_free_array_dma);
+       } else {
+               mpi_request.ReplyDescriptorPostQueueAddress =
+                   cpu_to_le64((u64)ioc->reply_post[0].reply_post_free_dma);
+       }
 
        /* This time stamp specifies number of milliseconds
         * since epoch ~ midnight January 1, 1970.
@@ -3674,7 +3820,7 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
        if (r != 0) {
                pr_err(MPT3SAS_FMT "%s: handshake failed (r=%d)\n",
                    ioc->name, __func__, r);
-               return r;
+               goto out;
        }
 
        ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & MPI2_IOCSTATUS_MASK;
@@ -3684,7 +3830,12 @@ _base_send_ioc_init(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
                r = -EIO;
        }
 
-       return 0;
+out:
+       if (reply_post_free_array)
+               pci_free_consistent(ioc->pdev, reply_post_free_array_sz,
+                                   reply_post_free_array,
+                                   reply_post_free_array_dma);
+       return r;
 }
 
 /**
@@ -4234,7 +4385,7 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
        struct _tr_list *delayed_tr, *delayed_tr_next;
        struct adapter_reply_queue *reply_q;
        long reply_post_free;
-       u32 reply_post_free_sz;
+       u32 reply_post_free_sz, index = 0;
 
        dinitprintk(ioc, pr_info(MPT3SAS_FMT "%s\n", ioc->name,
            __func__));
@@ -4305,9 +4456,9 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
                _base_assign_reply_queues(ioc);
 
        /* initialize Reply Post Free Queue */
-       reply_post_free = (long)ioc->reply_post_free;
        reply_post_free_sz = ioc->reply_post_queue_depth *
            sizeof(Mpi2DefaultReplyDescriptor_t);
+       reply_post_free = (long)ioc->reply_post[index].reply_post_free;
        list_for_each_entry(reply_q, &ioc->reply_queue_list, list) {
                reply_q->reply_post_host_index = 0;
                reply_q->reply_post_free = (Mpi2ReplyDescriptorsUnion_t *)
@@ -4317,7 +4468,15 @@ _base_make_ioc_operational(struct MPT3SAS_ADAPTER *ioc, int sleep_flag)
                            cpu_to_le64(ULLONG_MAX);
                if (!_base_is_controller_msix_enabled(ioc))
                        goto skip_init_reply_post_free_queue;
-               reply_post_free += reply_post_free_sz;
+               /*
+                * If RDPQ is enabled, switch to the next allocation.
+                * Otherwise advance within the contiguous region.
+                */
+               if (ioc->rdpq_array_enable)
+                       reply_post_free = (long)
+                           ioc->reply_post[++index].reply_post_free;
+               else
+                       reply_post_free += reply_post_free_sz;
        }
  skip_init_reply_post_free_queue:
 
@@ -4428,6 +4587,8 @@ mpt3sas_base_attach(struct MPT3SAS_ADAPTER *ioc)
                goto out_free_resources;
        }
 
+       ioc->rdpq_array_enable_assigned = 0;
+       ioc->dma_mask = 0;
        r = mpt3sas_base_map_resources(ioc);
        if (r)
                goto out_free_resources;
@@ -4804,6 +4965,12 @@ mpt3sas_base_hard_reset_handler(struct MPT3SAS_ADAPTER *ioc, int sleep_flag,
        r = _base_get_ioc_facts(ioc, CAN_SLEEP);
        if (r)
                goto out;
+
+       if (ioc->rdpq_array_enable && !ioc->rdpq_array_capable)
+               panic("%s: Issue occurred with flashing controller firmware."
+                     "Please reboot the system and ensure that the correct"
+                     " firmware version is running\n", ioc->name);
+
        r = _base_make_ioc_operational(ioc, sleep_flag);
        if (!r)
                _base_reset_handler(ioc, MPT3_IOC_DONE_RESET);