diff options
author | Andrew Vasquez <andrew.vasquez@qlogic.com> | 2005-07-06 13:31:17 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.(none)> | 2005-07-14 11:00:07 -0400 |
commit | 2b6c0cee90cecbce35fb6d65fed94f22e5063be0 (patch) | |
tree | b0bc0240e95d72d67cf3f23ee84375b629128063 /drivers | |
parent | 459c537807bd72cce7b007fb218bb5a658a6c3c1 (diff) |
[SCSI] qla2xxx: Add ISP24xx IOCB manipulation routines.
Add ISP24xx IOCB manipulation routines.
Add appropriate glue-code for ISP24xx support while
manipulting IOCB packets. Add an ISP24xx specific
'start_scsi' routine due to command-type-7 layout
changes.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 1 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_inline.h | 13 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_iocb.c | 325 |
3 files changed, 323 insertions, 16 deletions
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 0dd732486e24..15a05682f012 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h | |||
@@ -94,6 +94,7 @@ extern uint16_t qla2x00_calc_iocbs_64(uint16_t); | |||
94 | extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t); | 94 | extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t); |
95 | extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t); | 95 | extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t); |
96 | extern int qla2x00_start_scsi(srb_t *sp); | 96 | extern int qla2x00_start_scsi(srb_t *sp); |
97 | extern int qla24xx_start_scsi(srb_t *sp); | ||
97 | int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); | 98 | int qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); |
98 | int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); | 99 | int __qla2x00_marker(scsi_qla_host_t *, uint16_t, uint16_t, uint8_t); |
99 | 100 | ||
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 6cf7036ccf4c..a4357f0aeea3 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h | |||
@@ -236,6 +236,19 @@ qla2x00_delete_timer_from_cmd(srb_t *sp) | |||
236 | } | 236 | } |
237 | } | 237 | } |
238 | 238 | ||
239 | static inline uint8_t *host_to_fcp_swap(uint8_t *, uint32_t); | ||
240 | static inline uint8_t * | ||
241 | host_to_fcp_swap(uint8_t *fcp, uint32_t bsize) | ||
242 | { | ||
243 | uint32_t *ifcp = (uint32_t *) fcp; | ||
244 | uint32_t *ofcp = (uint32_t *) fcp; | ||
245 | uint32_t iter = bsize >> 2; | ||
246 | |||
247 | for (; iter ; iter--) | ||
248 | *ofcp++ = swab32(*ifcp++); | ||
249 | |||
250 | return fcp; | ||
251 | } | ||
239 | 252 | ||
240 | static inline int qla2x00_is_reserved_id(scsi_qla_host_t *, uint16_t); | 253 | static inline int qla2x00_is_reserved_id(scsi_qla_host_t *, uint16_t); |
241 | static inline int | 254 | static inline int |
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 846dbfe26be3..4343c65523a5 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c | |||
@@ -468,29 +468,37 @@ queuing_error: | |||
468 | * | 468 | * |
469 | * Returns non-zero if a failure occured, else zero. | 469 | * Returns non-zero if a failure occured, else zero. |
470 | */ | 470 | */ |
471 | int | 471 | int |
472 | __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, | 472 | __qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, |
473 | uint8_t type) | 473 | uint8_t type) |
474 | { | 474 | { |
475 | mrk_entry_t *pkt; | 475 | mrk_entry_t *mrk; |
476 | struct mrk_entry_24xx *mrk24; | ||
476 | 477 | ||
477 | pkt = (mrk_entry_t *)qla2x00_req_pkt(ha); | 478 | mrk24 = NULL; |
478 | if (pkt == NULL) { | 479 | mrk = (mrk_entry_t *)qla2x00_req_pkt(ha); |
479 | DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__)); | 480 | if (mrk == NULL) { |
481 | DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n", | ||
482 | __func__, ha->host_no)); | ||
480 | 483 | ||
481 | return (QLA_FUNCTION_FAILED); | 484 | return (QLA_FUNCTION_FAILED); |
482 | } | 485 | } |
483 | 486 | ||
484 | pkt->entry_type = MARKER_TYPE; | 487 | mrk->entry_type = MARKER_TYPE; |
485 | pkt->modifier = type; | 488 | mrk->modifier = type; |
486 | |||
487 | if (type != MK_SYNC_ALL) { | 489 | if (type != MK_SYNC_ALL) { |
488 | pkt->lun = cpu_to_le16(lun); | 490 | if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) { |
489 | SET_TARGET_ID(ha, pkt->target, loop_id); | 491 | mrk24 = (struct mrk_entry_24xx *) mrk; |
492 | mrk24->nport_handle = cpu_to_le16(loop_id); | ||
493 | mrk24->lun[1] = LSB(lun); | ||
494 | mrk24->lun[2] = MSB(lun); | ||
495 | } else { | ||
496 | SET_TARGET_ID(ha, mrk->target, loop_id); | ||
497 | mrk->lun = cpu_to_le16(lun); | ||
498 | } | ||
490 | } | 499 | } |
491 | wmb(); | 500 | wmb(); |
492 | 501 | ||
493 | /* Issue command to ISP */ | ||
494 | qla2x00_isp_cmd(ha); | 502 | qla2x00_isp_cmd(ha); |
495 | 503 | ||
496 | return (QLA_SUCCESS); | 504 | return (QLA_SUCCESS); |
@@ -521,7 +529,7 @@ qla2x00_marker(scsi_qla_host_t *ha, uint16_t loop_id, uint16_t lun, | |||
521 | static request_t * | 529 | static request_t * |
522 | qla2x00_req_pkt(scsi_qla_host_t *ha) | 530 | qla2x00_req_pkt(scsi_qla_host_t *ha) |
523 | { | 531 | { |
524 | struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; | 532 | device_reg_t __iomem *reg = ha->iobase; |
525 | request_t *pkt = NULL; | 533 | request_t *pkt = NULL; |
526 | uint16_t cnt; | 534 | uint16_t cnt; |
527 | uint32_t *dword_ptr; | 535 | uint32_t *dword_ptr; |
@@ -532,7 +540,12 @@ qla2x00_req_pkt(scsi_qla_host_t *ha) | |||
532 | for (timer = HZ; timer; timer--) { | 540 | for (timer = HZ; timer; timer--) { |
533 | if ((req_cnt + 2) >= ha->req_q_cnt) { | 541 | if ((req_cnt + 2) >= ha->req_q_cnt) { |
534 | /* Calculate number of free request entries. */ | 542 | /* Calculate number of free request entries. */ |
535 | cnt = qla2x00_debounce_register(ISP_REQ_Q_OUT(ha, reg)); | 543 | if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) |
544 | cnt = (uint16_t)RD_REG_DWORD( | ||
545 | ®->isp24.req_q_out); | ||
546 | else | ||
547 | cnt = qla2x00_debounce_register( | ||
548 | ISP_REQ_Q_OUT(ha, ®->isp)); | ||
536 | if (ha->req_ring_index < cnt) | 549 | if (ha->req_ring_index < cnt) |
537 | ha->req_q_cnt = cnt - ha->req_ring_index; | 550 | ha->req_q_cnt = cnt - ha->req_ring_index; |
538 | else | 551 | else |
@@ -586,7 +599,7 @@ qla2x00_req_pkt(scsi_qla_host_t *ha) | |||
586 | void | 599 | void |
587 | qla2x00_isp_cmd(scsi_qla_host_t *ha) | 600 | qla2x00_isp_cmd(scsi_qla_host_t *ha) |
588 | { | 601 | { |
589 | struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; | 602 | device_reg_t __iomem *reg = ha->iobase; |
590 | 603 | ||
591 | DEBUG5(printk("%s(): IOCB data:\n", __func__)); | 604 | DEBUG5(printk("%s(): IOCB data:\n", __func__)); |
592 | DEBUG5(qla2x00_dump_buffer( | 605 | DEBUG5(qla2x00_dump_buffer( |
@@ -601,6 +614,286 @@ qla2x00_isp_cmd(scsi_qla_host_t *ha) | |||
601 | ha->request_ring_ptr++; | 614 | ha->request_ring_ptr++; |
602 | 615 | ||
603 | /* Set chip new ring index. */ | 616 | /* Set chip new ring index. */ |
604 | WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), ha->req_ring_index); | 617 | if (IS_QLA24XX(ha) || IS_QLA25XX(ha)) { |
605 | RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */ | 618 | WRT_REG_DWORD(®->isp24.req_q_in, ha->req_ring_index); |
619 | RD_REG_DWORD_RELAXED(®->isp24.req_q_in); | ||
620 | } else { | ||
621 | WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), ha->req_ring_index); | ||
622 | RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); | ||
623 | } | ||
624 | |||
625 | } | ||
626 | |||
627 | /** | ||
628 | * qla24xx_calc_iocbs() - Determine number of Command Type 3 and | ||
629 | * Continuation Type 1 IOCBs to allocate. | ||
630 | * | ||
631 | * @dsds: number of data segment decriptors needed | ||
632 | * | ||
633 | * Returns the number of IOCB entries needed to store @dsds. | ||
634 | */ | ||
635 | static inline uint16_t | ||
636 | qla24xx_calc_iocbs(uint16_t dsds) | ||
637 | { | ||
638 | uint16_t iocbs; | ||
639 | |||
640 | iocbs = 1; | ||
641 | if (dsds > 1) { | ||
642 | iocbs += (dsds - 1) / 5; | ||
643 | if ((dsds - 1) % 5) | ||
644 | iocbs++; | ||
645 | } | ||
646 | return iocbs; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * qla24xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7 | ||
651 | * IOCB types. | ||
652 | * | ||
653 | * @sp: SRB command to process | ||
654 | * @cmd_pkt: Command type 3 IOCB | ||
655 | * @tot_dsds: Total number of segments to transfer | ||
656 | */ | ||
657 | static inline void | ||
658 | qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, | ||
659 | uint16_t tot_dsds) | ||
660 | { | ||
661 | uint16_t avail_dsds; | ||
662 | uint32_t *cur_dsd; | ||
663 | scsi_qla_host_t *ha; | ||
664 | struct scsi_cmnd *cmd; | ||
665 | |||
666 | cmd = sp->cmd; | ||
667 | |||
668 | /* Update entry type to indicate Command Type 3 IOCB */ | ||
669 | *((uint32_t *)(&cmd_pkt->entry_type)) = | ||
670 | __constant_cpu_to_le32(COMMAND_TYPE_7); | ||
671 | |||
672 | /* No data transfer */ | ||
673 | if (cmd->request_bufflen == 0 || cmd->sc_data_direction == DMA_NONE) { | ||
674 | cmd_pkt->byte_count = __constant_cpu_to_le32(0); | ||
675 | return; | ||
676 | } | ||
677 | |||
678 | ha = sp->ha; | ||
679 | |||
680 | /* Set transfer direction */ | ||
681 | if (cmd->sc_data_direction == DMA_TO_DEVICE) | ||
682 | cmd_pkt->task_mgmt_flags = | ||
683 | __constant_cpu_to_le16(TMF_WRITE_DATA); | ||
684 | else if (cmd->sc_data_direction == DMA_FROM_DEVICE) | ||
685 | cmd_pkt->task_mgmt_flags = | ||
686 | __constant_cpu_to_le16(TMF_READ_DATA); | ||
687 | |||
688 | /* One DSD is available in the Command Type 3 IOCB */ | ||
689 | avail_dsds = 1; | ||
690 | cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address; | ||
691 | |||
692 | /* Load data segments */ | ||
693 | if (cmd->use_sg != 0) { | ||
694 | struct scatterlist *cur_seg; | ||
695 | struct scatterlist *end_seg; | ||
696 | |||
697 | cur_seg = (struct scatterlist *)cmd->request_buffer; | ||
698 | end_seg = cur_seg + tot_dsds; | ||
699 | while (cur_seg < end_seg) { | ||
700 | dma_addr_t sle_dma; | ||
701 | cont_a64_entry_t *cont_pkt; | ||
702 | |||
703 | /* Allocate additional continuation packets? */ | ||
704 | if (avail_dsds == 0) { | ||
705 | /* | ||
706 | * Five DSDs are available in the Continuation | ||
707 | * Type 1 IOCB. | ||
708 | */ | ||
709 | cont_pkt = qla2x00_prep_cont_type1_iocb(ha); | ||
710 | cur_dsd = (uint32_t *)cont_pkt->dseg_0_address; | ||
711 | avail_dsds = 5; | ||
712 | } | ||
713 | |||
714 | sle_dma = sg_dma_address(cur_seg); | ||
715 | *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); | ||
716 | *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); | ||
717 | *cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg)); | ||
718 | avail_dsds--; | ||
719 | |||
720 | cur_seg++; | ||
721 | } | ||
722 | } else { | ||
723 | *cur_dsd++ = cpu_to_le32(LSD(sp->dma_handle)); | ||
724 | *cur_dsd++ = cpu_to_le32(MSD(sp->dma_handle)); | ||
725 | *cur_dsd++ = cpu_to_le32(cmd->request_bufflen); | ||
726 | } | ||
727 | } | ||
728 | |||
729 | |||
730 | /** | ||
731 | * qla24xx_start_scsi() - Send a SCSI command to the ISP | ||
732 | * @sp: command to send to the ISP | ||
733 | * | ||
734 | * Returns non-zero if a failure occured, else zero. | ||
735 | */ | ||
736 | int | ||
737 | qla24xx_start_scsi(srb_t *sp) | ||
738 | { | ||
739 | int ret; | ||
740 | unsigned long flags; | ||
741 | scsi_qla_host_t *ha; | ||
742 | struct scsi_cmnd *cmd; | ||
743 | uint32_t *clr_ptr; | ||
744 | uint32_t index; | ||
745 | uint32_t handle; | ||
746 | struct cmd_type_7 *cmd_pkt; | ||
747 | struct scatterlist *sg; | ||
748 | uint16_t cnt; | ||
749 | uint16_t req_cnt; | ||
750 | uint16_t tot_dsds; | ||
751 | struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; | ||
752 | char tag[2]; | ||
753 | |||
754 | /* Setup device pointers. */ | ||
755 | ret = 0; | ||
756 | ha = sp->ha; | ||
757 | reg = &ha->iobase->isp24; | ||
758 | cmd = sp->cmd; | ||
759 | /* So we know we haven't pci_map'ed anything yet */ | ||
760 | tot_dsds = 0; | ||
761 | |||
762 | /* Send marker if required */ | ||
763 | if (ha->marker_needed != 0) { | ||
764 | if (qla2x00_marker(ha, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) { | ||
765 | return QLA_FUNCTION_FAILED; | ||
766 | } | ||
767 | ha->marker_needed = 0; | ||
768 | } | ||
769 | |||
770 | /* Acquire ring specific lock */ | ||
771 | spin_lock_irqsave(&ha->hardware_lock, flags); | ||
772 | |||
773 | /* Check for room in outstanding command list. */ | ||
774 | handle = ha->current_outstanding_cmd; | ||
775 | for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { | ||
776 | handle++; | ||
777 | if (handle == MAX_OUTSTANDING_COMMANDS) | ||
778 | handle = 1; | ||
779 | if (ha->outstanding_cmds[handle] == 0) | ||
780 | break; | ||
781 | } | ||
782 | if (index == MAX_OUTSTANDING_COMMANDS) | ||
783 | goto queuing_error; | ||
784 | |||
785 | /* Map the sg table so we have an accurate count of sg entries needed */ | ||
786 | if (cmd->use_sg) { | ||
787 | sg = (struct scatterlist *) cmd->request_buffer; | ||
788 | tot_dsds = pci_map_sg(ha->pdev, sg, cmd->use_sg, | ||
789 | cmd->sc_data_direction); | ||
790 | if (tot_dsds == 0) | ||
791 | goto queuing_error; | ||
792 | } else if (cmd->request_bufflen) { | ||
793 | dma_addr_t req_dma; | ||
794 | |||
795 | req_dma = pci_map_single(ha->pdev, cmd->request_buffer, | ||
796 | cmd->request_bufflen, cmd->sc_data_direction); | ||
797 | if (dma_mapping_error(req_dma)) | ||
798 | goto queuing_error; | ||
799 | |||
800 | sp->dma_handle = req_dma; | ||
801 | tot_dsds = 1; | ||
802 | } | ||
803 | |||
804 | req_cnt = qla24xx_calc_iocbs(tot_dsds); | ||
805 | if (ha->req_q_cnt < (req_cnt + 2)) { | ||
806 | cnt = (uint16_t)RD_REG_DWORD_RELAXED(®->req_q_out); | ||
807 | if (ha->req_ring_index < cnt) | ||
808 | ha->req_q_cnt = cnt - ha->req_ring_index; | ||
809 | else | ||
810 | ha->req_q_cnt = ha->request_q_length - | ||
811 | (ha->req_ring_index - cnt); | ||
812 | } | ||
813 | if (ha->req_q_cnt < (req_cnt + 2)) { | ||
814 | if (cmd->use_sg) | ||
815 | pci_unmap_sg(ha->pdev, sg, cmd->use_sg, | ||
816 | cmd->sc_data_direction); | ||
817 | goto queuing_error; | ||
818 | } | ||
819 | |||
820 | /* Build command packet. */ | ||
821 | ha->current_outstanding_cmd = handle; | ||
822 | ha->outstanding_cmds[handle] = sp; | ||
823 | sp->ha = ha; | ||
824 | sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; | ||
825 | ha->req_q_cnt -= req_cnt; | ||
826 | |||
827 | cmd_pkt = (struct cmd_type_7 *)ha->request_ring_ptr; | ||
828 | cmd_pkt->handle = handle; | ||
829 | |||
830 | /* Zero out remaining portion of packet. */ | ||
831 | clr_ptr = (uint32_t *)cmd_pkt + 2; | ||
832 | memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); | ||
833 | cmd_pkt->dseg_count = cpu_to_le16(tot_dsds); | ||
834 | |||
835 | /* Set NPORT-ID and LUN number*/ | ||
836 | cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id); | ||
837 | cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa; | ||
838 | cmd_pkt->port_id[1] = sp->fcport->d_id.b.area; | ||
839 | cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain; | ||
840 | |||
841 | cmd_pkt->lun[1] = LSB(sp->cmd->device->lun); | ||
842 | cmd_pkt->lun[2] = MSB(sp->cmd->device->lun); | ||
843 | |||
844 | /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */ | ||
845 | if (scsi_populate_tag_msg(cmd, tag)) { | ||
846 | switch (tag[0]) { | ||
847 | case MSG_HEAD_TAG: | ||
848 | cmd_pkt->task = TSK_HEAD_OF_QUEUE; | ||
849 | break; | ||
850 | case MSG_ORDERED_TAG: | ||
851 | cmd_pkt->task = TSK_ORDERED; | ||
852 | break; | ||
853 | } | ||
854 | } | ||
855 | |||
856 | /* Load SCSI command packet. */ | ||
857 | memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len); | ||
858 | host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb)); | ||
859 | |||
860 | cmd_pkt->byte_count = cpu_to_le32((uint32_t)cmd->request_bufflen); | ||
861 | |||
862 | /* Build IOCB segments */ | ||
863 | qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds); | ||
864 | |||
865 | /* Set total data segment count. */ | ||
866 | cmd_pkt->entry_count = (uint8_t)req_cnt; | ||
867 | wmb(); | ||
868 | |||
869 | /* Adjust ring index. */ | ||
870 | ha->req_ring_index++; | ||
871 | if (ha->req_ring_index == ha->request_q_length) { | ||
872 | ha->req_ring_index = 0; | ||
873 | ha->request_ring_ptr = ha->request_ring; | ||
874 | } else | ||
875 | ha->request_ring_ptr++; | ||
876 | |||
877 | sp->flags |= SRB_DMA_VALID; | ||
878 | sp->state = SRB_ACTIVE_STATE; | ||
879 | |||
880 | /* Set chip new ring index. */ | ||
881 | WRT_REG_DWORD(®->req_q_in, ha->req_ring_index); | ||
882 | RD_REG_DWORD_RELAXED(®->req_q_in); /* PCI Posting. */ | ||
883 | |||
884 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
885 | return QLA_SUCCESS; | ||
886 | |||
887 | queuing_error: | ||
888 | if (cmd->use_sg && tot_dsds) { | ||
889 | sg = (struct scatterlist *) cmd->request_buffer; | ||
890 | pci_unmap_sg(ha->pdev, sg, cmd->use_sg, | ||
891 | cmd->sc_data_direction); | ||
892 | } else if (tot_dsds) { | ||
893 | pci_unmap_single(ha->pdev, sp->dma_handle, | ||
894 | cmd->request_bufflen, cmd->sc_data_direction); | ||
895 | } | ||
896 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | ||
897 | |||
898 | return QLA_FUNCTION_FAILED; | ||
606 | } | 899 | } |