diff options
author | Sakthivel K <Sakthivel.SaravananKamalRaju@pmcs.com> | 2013-03-19 08:26:17 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2013-05-10 10:47:47 -0400 |
commit | 1245ee5996a1270e4fd04f9c2e399521a656c930 (patch) | |
tree | 162c4e63c70363e9debe13bad3d8178238860b00 | |
parent | f74cf271e692848833b3845b4036a87e5b683fa8 (diff) |
[SCSI] pm80xx: MSI-X implementation for using 64 interrupts
Implementation of interrupt handlers and tasklets to support
upto 64 interrupt for the device.
Signed-off-by: Sakthivel K <Sakthivel.SaravananKamalRaju@pmcs.com>
Signed-off-by: Anand Kumar S <AnandKumar.Santhanam@pmcs.com>
Acked-by: Jack Wang <jack_wang@usish.com>
Reviewed-by: Hannes Reinecke <hare@suse.de>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r-- | drivers/scsi/pm8001/pm8001_init.c | 141 | ||||
-rw-r--r-- | drivers/scsi/pm8001/pm8001_sas.h | 2 |
2 files changed, 113 insertions, 30 deletions
diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 19fbd03b4190..75270ee1a7f0 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c | |||
@@ -159,33 +159,71 @@ static void pm8001_free(struct pm8001_hba_info *pm8001_ha) | |||
159 | } | 159 | } |
160 | 160 | ||
161 | #ifdef PM8001_USE_TASKLET | 161 | #ifdef PM8001_USE_TASKLET |
162 | |||
163 | /** | ||
164 | * tasklet for 64 msi-x interrupt handler | ||
165 | * @opaque: the passed general host adapter struct | ||
166 | * Note: pm8001_tasklet is common for pm8001 & pm80xx | ||
167 | */ | ||
162 | static void pm8001_tasklet(unsigned long opaque) | 168 | static void pm8001_tasklet(unsigned long opaque) |
163 | { | 169 | { |
164 | struct pm8001_hba_info *pm8001_ha; | 170 | struct pm8001_hba_info *pm8001_ha; |
171 | u32 vec; | ||
165 | pm8001_ha = (struct pm8001_hba_info *)opaque; | 172 | pm8001_ha = (struct pm8001_hba_info *)opaque; |
166 | if (unlikely(!pm8001_ha)) | 173 | if (unlikely(!pm8001_ha)) |
167 | BUG_ON(1); | 174 | BUG_ON(1); |
168 | PM8001_CHIP_DISP->isr(pm8001_ha, 0); | 175 | vec = pm8001_ha->int_vector; |
176 | PM8001_CHIP_DISP->isr(pm8001_ha, vec); | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | static struct pm8001_hba_info *outq_to_hba(u8 *outq) | ||
181 | { | ||
182 | return container_of((outq - *outq), struct pm8001_hba_info, outq[0]); | ||
169 | } | 183 | } |
184 | |||
185 | /** | ||
186 | * pm8001_interrupt_handler_msix - main MSIX interrupt handler. | ||
187 | * It obtains the vector number and calls the equivalent bottom | ||
188 | * half or services directly. | ||
189 | * @opaque: the passed outbound queue/vector. Host structure is | ||
190 | * retrieved from the same. | ||
191 | */ | ||
192 | static irqreturn_t pm8001_interrupt_handler_msix(int irq, void *opaque) | ||
193 | { | ||
194 | struct pm8001_hba_info *pm8001_ha = outq_to_hba(opaque); | ||
195 | u8 outq = *(u8 *)opaque; | ||
196 | irqreturn_t ret = IRQ_HANDLED; | ||
197 | if (unlikely(!pm8001_ha)) | ||
198 | return IRQ_NONE; | ||
199 | if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) | ||
200 | return IRQ_NONE; | ||
201 | pm8001_ha->int_vector = outq; | ||
202 | #ifdef PM8001_USE_TASKLET | ||
203 | tasklet_schedule(&pm8001_ha->tasklet); | ||
204 | #else | ||
205 | ret = PM8001_CHIP_DISP->isr(pm8001_ha, outq); | ||
170 | #endif | 206 | #endif |
207 | return ret; | ||
208 | } | ||
171 | 209 | ||
210 | /** | ||
211 | * pm8001_interrupt_handler_intx - main INTx interrupt handler. | ||
212 | * @dev_id: sas_ha structure. The HBA is retrieved from sas_has structure. | ||
213 | */ | ||
172 | 214 | ||
173 | /** | 215 | static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id) |
174 | * pm8001_interrupt - when HBA originate a interrupt,we should invoke this | ||
175 | * dispatcher to handle each case. | ||
176 | * @irq: irq number. | ||
177 | * @opaque: the passed general host adapter struct | ||
178 | */ | ||
179 | static irqreturn_t pm8001_interrupt(int irq, void *opaque) | ||
180 | { | 216 | { |
181 | struct pm8001_hba_info *pm8001_ha; | 217 | struct pm8001_hba_info *pm8001_ha; |
182 | irqreturn_t ret = IRQ_HANDLED; | 218 | irqreturn_t ret = IRQ_HANDLED; |
183 | struct sas_ha_struct *sha = opaque; | 219 | struct sas_ha_struct *sha = dev_id; |
184 | pm8001_ha = sha->lldd_ha; | 220 | pm8001_ha = sha->lldd_ha; |
185 | if (unlikely(!pm8001_ha)) | 221 | if (unlikely(!pm8001_ha)) |
186 | return IRQ_NONE; | 222 | return IRQ_NONE; |
187 | if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) | 223 | if (!PM8001_CHIP_DISP->is_our_interupt(pm8001_ha)) |
188 | return IRQ_NONE; | 224 | return IRQ_NONE; |
225 | |||
226 | pm8001_ha->int_vector = 0; | ||
189 | #ifdef PM8001_USE_TASKLET | 227 | #ifdef PM8001_USE_TASKLET |
190 | tasklet_schedule(&pm8001_ha->tasklet); | 228 | tasklet_schedule(&pm8001_ha->tasklet); |
191 | #else | 229 | #else |
@@ -427,8 +465,12 @@ static struct pm8001_hba_info *pm8001_pci_alloc(struct pci_dev *pdev, | |||
427 | pm8001_ha->iomb_size = IOMB_SIZE_SPC; | 465 | pm8001_ha->iomb_size = IOMB_SIZE_SPC; |
428 | 466 | ||
429 | #ifdef PM8001_USE_TASKLET | 467 | #ifdef PM8001_USE_TASKLET |
468 | /** | ||
469 | * default tasklet for non msi-x interrupt handler/first msi-x | ||
470 | * interrupt handler | ||
471 | **/ | ||
430 | tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, | 472 | tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, |
431 | (unsigned long)pm8001_ha); | 473 | (unsigned long)pm8001_ha); |
432 | #endif | 474 | #endif |
433 | pm8001_ioremap(pm8001_ha); | 475 | pm8001_ioremap(pm8001_ha); |
434 | if (!pm8001_alloc(pm8001_ha, ent)) | 476 | if (!pm8001_alloc(pm8001_ha, ent)) |
@@ -591,31 +633,50 @@ static void pm8001_init_sas_add(struct pm8001_hba_info *pm8001_ha) | |||
591 | * @chip_info: our ha struct. | 633 | * @chip_info: our ha struct. |
592 | * @irq_handler: irq_handler | 634 | * @irq_handler: irq_handler |
593 | */ | 635 | */ |
594 | static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha, | 636 | static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha) |
595 | irq_handler_t irq_handler) | ||
596 | { | 637 | { |
597 | u32 i = 0, j = 0; | 638 | u32 i = 0, j = 0; |
598 | u32 number_of_intr = 1; | 639 | u32 number_of_intr; |
599 | int flag = 0; | 640 | int flag = 0; |
600 | u32 max_entry; | 641 | u32 max_entry; |
601 | int rc; | 642 | int rc; |
643 | static char intr_drvname[PM8001_MAX_MSIX_VEC][sizeof(DRV_NAME)+3]; | ||
644 | |||
645 | /* SPCv controllers supports 64 msi-x */ | ||
646 | if (pm8001_ha->chip_id == chip_8001) { | ||
647 | number_of_intr = 1; | ||
648 | flag |= IRQF_DISABLED; | ||
649 | } else { | ||
650 | number_of_intr = PM8001_MAX_MSIX_VEC; | ||
651 | flag &= ~IRQF_SHARED; | ||
652 | flag |= IRQF_DISABLED; | ||
653 | } | ||
654 | |||
602 | max_entry = sizeof(pm8001_ha->msix_entries) / | 655 | max_entry = sizeof(pm8001_ha->msix_entries) / |
603 | sizeof(pm8001_ha->msix_entries[0]); | 656 | sizeof(pm8001_ha->msix_entries[0]); |
604 | flag |= IRQF_DISABLED; | ||
605 | for (i = 0; i < max_entry ; i++) | 657 | for (i = 0; i < max_entry ; i++) |
606 | pm8001_ha->msix_entries[i].entry = i; | 658 | pm8001_ha->msix_entries[i].entry = i; |
607 | rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries, | 659 | rc = pci_enable_msix(pm8001_ha->pdev, pm8001_ha->msix_entries, |
608 | number_of_intr); | 660 | number_of_intr); |
609 | pm8001_ha->number_of_intr = number_of_intr; | 661 | pm8001_ha->number_of_intr = number_of_intr; |
610 | if (!rc) { | 662 | if (!rc) { |
663 | PM8001_INIT_DBG(pm8001_ha, pm8001_printk( | ||
664 | "pci_enable_msix request ret:%d no of intr %d\n", | ||
665 | rc, pm8001_ha->number_of_intr)); | ||
666 | |||
667 | for (i = 0; i < number_of_intr; i++) | ||
668 | pm8001_ha->outq[i] = i; | ||
669 | |||
611 | for (i = 0; i < number_of_intr; i++) { | 670 | for (i = 0; i < number_of_intr; i++) { |
671 | snprintf(intr_drvname[i], sizeof(intr_drvname[0]), | ||
672 | DRV_NAME"%d", i); | ||
612 | if (request_irq(pm8001_ha->msix_entries[i].vector, | 673 | if (request_irq(pm8001_ha->msix_entries[i].vector, |
613 | irq_handler, flag, DRV_NAME, | 674 | pm8001_interrupt_handler_msix, flag, |
614 | SHOST_TO_SAS_HA(pm8001_ha->shost))) { | 675 | intr_drvname[i], &pm8001_ha->outq[i])) { |
615 | for (j = 0; j < i; j++) | 676 | for (j = 0; j < i; j++) |
616 | free_irq( | 677 | free_irq( |
617 | pm8001_ha->msix_entries[j].vector, | 678 | pm8001_ha->msix_entries[j].vector, |
618 | SHOST_TO_SAS_HA(pm8001_ha->shost)); | 679 | &pm8001_ha->outq[j]); |
619 | pci_disable_msix(pm8001_ha->pdev); | 680 | pci_disable_msix(pm8001_ha->pdev); |
620 | break; | 681 | break; |
621 | } | 682 | } |
@@ -632,22 +693,24 @@ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha, | |||
632 | static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) | 693 | static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) |
633 | { | 694 | { |
634 | struct pci_dev *pdev; | 695 | struct pci_dev *pdev; |
635 | irq_handler_t irq_handler = pm8001_interrupt; | ||
636 | int rc; | 696 | int rc; |
637 | 697 | ||
638 | pdev = pm8001_ha->pdev; | 698 | pdev = pm8001_ha->pdev; |
639 | 699 | ||
640 | #ifdef PM8001_USE_MSIX | 700 | #ifdef PM8001_USE_MSIX |
641 | if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) | 701 | if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) |
642 | return pm8001_setup_msix(pm8001_ha, irq_handler); | 702 | return pm8001_setup_msix(pm8001_ha); |
643 | else | 703 | else { |
704 | PM8001_INIT_DBG(pm8001_ha, | ||
705 | pm8001_printk("MSIX not supported!!!\n")); | ||
644 | goto intx; | 706 | goto intx; |
707 | } | ||
645 | #endif | 708 | #endif |
646 | 709 | ||
647 | intx: | 710 | intx: |
648 | /* initialize the INT-X interrupt */ | 711 | /* initialize the INT-X interrupt */ |
649 | rc = request_irq(pdev->irq, irq_handler, IRQF_SHARED, DRV_NAME, | 712 | rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED, |
650 | SHOST_TO_SAS_HA(pm8001_ha->shost)); | 713 | DRV_NAME, SHOST_TO_SAS_HA(pm8001_ha->shost)); |
651 | return rc; | 714 | return rc; |
652 | } | 715 | } |
653 | 716 | ||
@@ -665,6 +728,7 @@ static int pm8001_pci_probe(struct pci_dev *pdev, | |||
665 | { | 728 | { |
666 | unsigned int rc; | 729 | unsigned int rc; |
667 | u32 pci_reg; | 730 | u32 pci_reg; |
731 | u8 i = 0; | ||
668 | struct pm8001_hba_info *pm8001_ha; | 732 | struct pm8001_hba_info *pm8001_ha; |
669 | struct Scsi_Host *shost = NULL; | 733 | struct Scsi_Host *shost = NULL; |
670 | const struct pm8001_chip_info *chip; | 734 | const struct pm8001_chip_info *chip; |
@@ -729,6 +793,11 @@ static int pm8001_pci_probe(struct pci_dev *pdev, | |||
729 | goto err_out_shost; | 793 | goto err_out_shost; |
730 | 794 | ||
731 | PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); | 795 | PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); |
796 | if (pm8001_ha->chip_id != chip_8001) { | ||
797 | for (i = 1; i < pm8001_ha->number_of_intr; i++) | ||
798 | PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i); | ||
799 | } | ||
800 | |||
732 | pm8001_init_sas_add(pm8001_ha); | 801 | pm8001_init_sas_add(pm8001_ha); |
733 | pm8001_post_sas_ha_init(shost, chip); | 802 | pm8001_post_sas_ha_init(shost, chip); |
734 | rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); | 803 | rc = sas_register_ha(SHOST_TO_SAS_HA(shost)); |
@@ -764,14 +833,15 @@ static void pm8001_pci_remove(struct pci_dev *pdev) | |||
764 | sas_remove_host(pm8001_ha->shost); | 833 | sas_remove_host(pm8001_ha->shost); |
765 | list_del(&pm8001_ha->list); | 834 | list_del(&pm8001_ha->list); |
766 | scsi_remove_host(pm8001_ha->shost); | 835 | scsi_remove_host(pm8001_ha->shost); |
767 | PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0); | 836 | PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF); |
768 | PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); | 837 | PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); |
769 | 838 | ||
770 | #ifdef PM8001_USE_MSIX | 839 | #ifdef PM8001_USE_MSIX |
771 | for (i = 0; i < pm8001_ha->number_of_intr; i++) | 840 | for (i = 0; i < pm8001_ha->number_of_intr; i++) |
772 | synchronize_irq(pm8001_ha->msix_entries[i].vector); | 841 | synchronize_irq(pm8001_ha->msix_entries[i].vector); |
773 | for (i = 0; i < pm8001_ha->number_of_intr; i++) | 842 | for (i = 0; i < pm8001_ha->number_of_intr; i++) |
774 | free_irq(pm8001_ha->msix_entries[i].vector, sha); | 843 | free_irq(pm8001_ha->msix_entries[i].vector, |
844 | &pm8001_ha->outq[i]); | ||
775 | pci_disable_msix(pdev); | 845 | pci_disable_msix(pdev); |
776 | #else | 846 | #else |
777 | free_irq(pm8001_ha->irq, sha); | 847 | free_irq(pm8001_ha->irq, sha); |
@@ -808,13 +878,14 @@ static int pm8001_pci_suspend(struct pci_dev *pdev, pm_message_t state) | |||
808 | printk(KERN_ERR " PCI PM not supported\n"); | 878 | printk(KERN_ERR " PCI PM not supported\n"); |
809 | return -ENODEV; | 879 | return -ENODEV; |
810 | } | 880 | } |
811 | PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0); | 881 | PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF); |
812 | PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); | 882 | PM8001_CHIP_DISP->chip_soft_rst(pm8001_ha, 0x252acbcd); |
813 | #ifdef PM8001_USE_MSIX | 883 | #ifdef PM8001_USE_MSIX |
814 | for (i = 0; i < pm8001_ha->number_of_intr; i++) | 884 | for (i = 0; i < pm8001_ha->number_of_intr; i++) |
815 | synchronize_irq(pm8001_ha->msix_entries[i].vector); | 885 | synchronize_irq(pm8001_ha->msix_entries[i].vector); |
816 | for (i = 0; i < pm8001_ha->number_of_intr; i++) | 886 | for (i = 0; i < pm8001_ha->number_of_intr; i++) |
817 | free_irq(pm8001_ha->msix_entries[i].vector, sha); | 887 | free_irq(pm8001_ha->msix_entries[i].vector, |
888 | &pm8001_ha->outq[i]); | ||
818 | pci_disable_msix(pdev); | 889 | pci_disable_msix(pdev); |
819 | #else | 890 | #else |
820 | free_irq(pm8001_ha->irq, sha); | 891 | free_irq(pm8001_ha->irq, sha); |
@@ -843,6 +914,7 @@ static int pm8001_pci_resume(struct pci_dev *pdev) | |||
843 | struct sas_ha_struct *sha = pci_get_drvdata(pdev); | 914 | struct sas_ha_struct *sha = pci_get_drvdata(pdev); |
844 | struct pm8001_hba_info *pm8001_ha; | 915 | struct pm8001_hba_info *pm8001_ha; |
845 | int rc; | 916 | int rc; |
917 | u8 i = 0; | ||
846 | u32 device_state; | 918 | u32 device_state; |
847 | pm8001_ha = sha->lldd_ha; | 919 | pm8001_ha = sha->lldd_ha; |
848 | device_state = pdev->current_state; | 920 | device_state = pdev->current_state; |
@@ -869,15 +941,24 @@ static int pm8001_pci_resume(struct pci_dev *pdev) | |||
869 | rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); | 941 | rc = PM8001_CHIP_DISP->chip_init(pm8001_ha); |
870 | if (rc) | 942 | if (rc) |
871 | goto err_out_disable; | 943 | goto err_out_disable; |
872 | PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0); | 944 | |
945 | /* disable all the interrupt bits */ | ||
946 | PM8001_CHIP_DISP->interrupt_disable(pm8001_ha, 0xFF); | ||
947 | |||
873 | rc = pm8001_request_irq(pm8001_ha); | 948 | rc = pm8001_request_irq(pm8001_ha); |
874 | if (rc) | 949 | if (rc) |
875 | goto err_out_disable; | 950 | goto err_out_disable; |
876 | #ifdef PM8001_USE_TASKLET | 951 | #ifdef PM8001_USE_TASKLET |
952 | /* default tasklet for non msi-x interrupt handler/first msi-x | ||
953 | * interrupt handler */ | ||
877 | tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, | 954 | tasklet_init(&pm8001_ha->tasklet, pm8001_tasklet, |
878 | (unsigned long)pm8001_ha); | 955 | (unsigned long)pm8001_ha); |
879 | #endif | 956 | #endif |
880 | PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); | 957 | PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, 0); |
958 | if (pm8001_ha->chip_id != chip_8001) { | ||
959 | for (i = 1; i < pm8001_ha->number_of_intr; i++) | ||
960 | PM8001_CHIP_DISP->interrupt_enable(pm8001_ha, i); | ||
961 | } | ||
881 | scsi_unblock_requests(pm8001_ha->shost); | 962 | scsi_unblock_requests(pm8001_ha->shost); |
882 | return 0; | 963 | return 0; |
883 | 964 | ||
diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index e0faa9597c26..8e281c8deff2 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h | |||
@@ -453,7 +453,9 @@ struct pm8001_hba_info { | |||
453 | #endif | 453 | #endif |
454 | u32 logging_level; | 454 | u32 logging_level; |
455 | u32 fw_status; | 455 | u32 fw_status; |
456 | u32 int_vector; | ||
456 | const struct firmware *fw_image; | 457 | const struct firmware *fw_image; |
458 | u8 outq[PM8001_MAX_MSIX_VEC]; | ||
457 | }; | 459 | }; |
458 | 460 | ||
459 | struct pm8001_work { | 461 | struct pm8001_work { |