diff options
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_bsg.c')
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_bsg.c | 360 |
1 files changed, 360 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_bsg.c b/drivers/scsi/qla4xxx/ql4_bsg.c index cf8fdf1d1257..04a0027dbca0 100644 --- a/drivers/scsi/qla4xxx/ql4_bsg.c +++ b/drivers/scsi/qla4xxx/ql4_bsg.c | |||
@@ -446,6 +446,363 @@ leave: | |||
446 | return rval; | 446 | return rval; |
447 | } | 447 | } |
448 | 448 | ||
449 | static void ql4xxx_execute_diag_cmd(struct bsg_job *bsg_job) | ||
450 | { | ||
451 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
452 | struct scsi_qla_host *ha = to_qla_host(host); | ||
453 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
454 | struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; | ||
455 | uint8_t *rsp_ptr = NULL; | ||
456 | uint32_t mbox_cmd[MBOX_REG_COUNT]; | ||
457 | uint32_t mbox_sts[MBOX_REG_COUNT]; | ||
458 | int status = QLA_ERROR; | ||
459 | |||
460 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__)); | ||
461 | |||
462 | if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) { | ||
463 | ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n", | ||
464 | __func__); | ||
465 | bsg_reply->result = DID_ERROR << 16; | ||
466 | goto exit_diag_mem_test; | ||
467 | } | ||
468 | |||
469 | bsg_reply->reply_payload_rcv_len = 0; | ||
470 | memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1], | ||
471 | sizeof(uint32_t) * MBOX_REG_COUNT); | ||
472 | |||
473 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
474 | "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n", | ||
475 | __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2], | ||
476 | mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6], | ||
477 | mbox_cmd[7])); | ||
478 | |||
479 | status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0], | ||
480 | &mbox_sts[0]); | ||
481 | |||
482 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
483 | "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\n", | ||
484 | __func__, mbox_sts[0], mbox_sts[1], mbox_sts[2], | ||
485 | mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6], | ||
486 | mbox_sts[7])); | ||
487 | |||
488 | if (status == QLA_SUCCESS) | ||
489 | bsg_reply->result = DID_OK << 16; | ||
490 | else | ||
491 | bsg_reply->result = DID_ERROR << 16; | ||
492 | |||
493 | /* Send mbox_sts to application */ | ||
494 | bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts); | ||
495 | rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply); | ||
496 | memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts)); | ||
497 | |||
498 | exit_diag_mem_test: | ||
499 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
500 | "%s: bsg_reply->result = x%x, status = %s\n", | ||
501 | __func__, bsg_reply->result, STATUS(status))); | ||
502 | |||
503 | bsg_job_done(bsg_job, bsg_reply->result, | ||
504 | bsg_reply->reply_payload_rcv_len); | ||
505 | } | ||
506 | |||
507 | static int qla4_83xx_wait_for_loopback_config_comp(struct scsi_qla_host *ha, | ||
508 | int wait_for_link) | ||
509 | { | ||
510 | int status = QLA_SUCCESS; | ||
511 | |||
512 | if (!wait_for_completion_timeout(&ha->idc_comp, (IDC_COMP_TOV * HZ))) { | ||
513 | ql4_printk(KERN_INFO, ha, "%s: IDC Complete notification not received, Waiting for another %d timeout", | ||
514 | __func__, ha->idc_extend_tmo); | ||
515 | if (ha->idc_extend_tmo) { | ||
516 | if (!wait_for_completion_timeout(&ha->idc_comp, | ||
517 | (ha->idc_extend_tmo * HZ))) { | ||
518 | ha->notify_idc_comp = 0; | ||
519 | ha->notify_link_up_comp = 0; | ||
520 | ql4_printk(KERN_WARNING, ha, "%s: IDC Complete notification not received", | ||
521 | __func__); | ||
522 | status = QLA_ERROR; | ||
523 | goto exit_wait; | ||
524 | } else { | ||
525 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
526 | "%s: IDC Complete notification received\n", | ||
527 | __func__)); | ||
528 | } | ||
529 | } | ||
530 | } else { | ||
531 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
532 | "%s: IDC Complete notification received\n", | ||
533 | __func__)); | ||
534 | } | ||
535 | ha->notify_idc_comp = 0; | ||
536 | |||
537 | if (wait_for_link) { | ||
538 | if (!wait_for_completion_timeout(&ha->link_up_comp, | ||
539 | (IDC_COMP_TOV * HZ))) { | ||
540 | ha->notify_link_up_comp = 0; | ||
541 | ql4_printk(KERN_WARNING, ha, "%s: LINK UP notification not received", | ||
542 | __func__); | ||
543 | status = QLA_ERROR; | ||
544 | goto exit_wait; | ||
545 | } else { | ||
546 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
547 | "%s: LINK UP notification received\n", | ||
548 | __func__)); | ||
549 | } | ||
550 | ha->notify_link_up_comp = 0; | ||
551 | } | ||
552 | |||
553 | exit_wait: | ||
554 | return status; | ||
555 | } | ||
556 | |||
557 | static int qla4_83xx_pre_loopback_config(struct scsi_qla_host *ha, | ||
558 | uint32_t *mbox_cmd) | ||
559 | { | ||
560 | uint32_t config = 0; | ||
561 | int status = QLA_SUCCESS; | ||
562 | |||
563 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__)); | ||
564 | |||
565 | status = qla4_83xx_get_port_config(ha, &config); | ||
566 | if (status != QLA_SUCCESS) | ||
567 | goto exit_pre_loopback_config; | ||
568 | |||
569 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Default port config=%08X\n", | ||
570 | __func__, config)); | ||
571 | |||
572 | if ((config & ENABLE_INTERNAL_LOOPBACK) || | ||
573 | (config & ENABLE_EXTERNAL_LOOPBACK)) { | ||
574 | ql4_printk(KERN_INFO, ha, "%s: Loopback diagnostics already in progress. Invalid requiest\n", | ||
575 | __func__); | ||
576 | goto exit_pre_loopback_config; | ||
577 | } | ||
578 | |||
579 | if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK) | ||
580 | config |= ENABLE_INTERNAL_LOOPBACK; | ||
581 | |||
582 | if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK) | ||
583 | config |= ENABLE_EXTERNAL_LOOPBACK; | ||
584 | |||
585 | config &= ~ENABLE_DCBX; | ||
586 | |||
587 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: New port config=%08X\n", | ||
588 | __func__, config)); | ||
589 | |||
590 | ha->notify_idc_comp = 1; | ||
591 | ha->notify_link_up_comp = 1; | ||
592 | |||
593 | /* get the link state */ | ||
594 | qla4xxx_get_firmware_state(ha); | ||
595 | |||
596 | status = qla4_83xx_set_port_config(ha, &config); | ||
597 | if (status != QLA_SUCCESS) { | ||
598 | ha->notify_idc_comp = 0; | ||
599 | ha->notify_link_up_comp = 0; | ||
600 | goto exit_pre_loopback_config; | ||
601 | } | ||
602 | exit_pre_loopback_config: | ||
603 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__, | ||
604 | STATUS(status))); | ||
605 | return status; | ||
606 | } | ||
607 | |||
608 | static int qla4_83xx_post_loopback_config(struct scsi_qla_host *ha, | ||
609 | uint32_t *mbox_cmd) | ||
610 | { | ||
611 | int status = QLA_SUCCESS; | ||
612 | uint32_t config = 0; | ||
613 | |||
614 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__)); | ||
615 | |||
616 | status = qla4_83xx_get_port_config(ha, &config); | ||
617 | if (status != QLA_SUCCESS) | ||
618 | goto exit_post_loopback_config; | ||
619 | |||
620 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: port config=%08X\n", __func__, | ||
621 | config)); | ||
622 | |||
623 | if (mbox_cmd[1] == QL_DIAG_CMD_TEST_INT_LOOPBACK) | ||
624 | config &= ~ENABLE_INTERNAL_LOOPBACK; | ||
625 | else if (mbox_cmd[1] == QL_DIAG_CMD_TEST_EXT_LOOPBACK) | ||
626 | config &= ~ENABLE_EXTERNAL_LOOPBACK; | ||
627 | |||
628 | config |= ENABLE_DCBX; | ||
629 | |||
630 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
631 | "%s: Restore default port config=%08X\n", __func__, | ||
632 | config)); | ||
633 | |||
634 | ha->notify_idc_comp = 1; | ||
635 | if (ha->addl_fw_state & FW_ADDSTATE_LINK_UP) | ||
636 | ha->notify_link_up_comp = 1; | ||
637 | |||
638 | status = qla4_83xx_set_port_config(ha, &config); | ||
639 | if (status != QLA_SUCCESS) { | ||
640 | ql4_printk(KERN_INFO, ha, "%s: Scheduling adapter reset\n", | ||
641 | __func__); | ||
642 | set_bit(DPC_RESET_HA, &ha->dpc_flags); | ||
643 | clear_bit(AF_LOOPBACK, &ha->flags); | ||
644 | goto exit_post_loopback_config; | ||
645 | } | ||
646 | |||
647 | exit_post_loopback_config: | ||
648 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: status = %s\n", __func__, | ||
649 | STATUS(status))); | ||
650 | return status; | ||
651 | } | ||
652 | |||
653 | static void qla4xxx_execute_diag_loopback_cmd(struct bsg_job *bsg_job) | ||
654 | { | ||
655 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
656 | struct scsi_qla_host *ha = to_qla_host(host); | ||
657 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
658 | struct iscsi_bsg_reply *bsg_reply = bsg_job->reply; | ||
659 | uint8_t *rsp_ptr = NULL; | ||
660 | uint32_t mbox_cmd[MBOX_REG_COUNT]; | ||
661 | uint32_t mbox_sts[MBOX_REG_COUNT]; | ||
662 | int wait_for_link = 1; | ||
663 | int status = QLA_ERROR; | ||
664 | |||
665 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__)); | ||
666 | |||
667 | bsg_reply->reply_payload_rcv_len = 0; | ||
668 | |||
669 | if (test_bit(AF_LOOPBACK, &ha->flags)) { | ||
670 | ql4_printk(KERN_INFO, ha, "%s: Loopback Diagnostics already in progress. Invalid Request\n", | ||
671 | __func__); | ||
672 | bsg_reply->result = DID_ERROR << 16; | ||
673 | goto exit_loopback_cmd; | ||
674 | } | ||
675 | |||
676 | if (test_bit(DPC_RESET_HA, &ha->dpc_flags)) { | ||
677 | ql4_printk(KERN_INFO, ha, "%s: Adapter reset in progress. Invalid Request\n", | ||
678 | __func__); | ||
679 | bsg_reply->result = DID_ERROR << 16; | ||
680 | goto exit_loopback_cmd; | ||
681 | } | ||
682 | |||
683 | memcpy(mbox_cmd, &bsg_req->rqst_data.h_vendor.vendor_cmd[1], | ||
684 | sizeof(uint32_t) * MBOX_REG_COUNT); | ||
685 | |||
686 | if (is_qla8032(ha) || is_qla8042(ha)) { | ||
687 | status = qla4_83xx_pre_loopback_config(ha, mbox_cmd); | ||
688 | if (status != QLA_SUCCESS) { | ||
689 | bsg_reply->result = DID_ERROR << 16; | ||
690 | goto exit_loopback_cmd; | ||
691 | } | ||
692 | |||
693 | status = qla4_83xx_wait_for_loopback_config_comp(ha, | ||
694 | wait_for_link); | ||
695 | if (status != QLA_SUCCESS) { | ||
696 | bsg_reply->result = DID_TIME_OUT << 16; | ||
697 | goto restore; | ||
698 | } | ||
699 | } | ||
700 | |||
701 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
702 | "%s: mbox_cmd: %08X %08X %08X %08X %08X %08X %08X %08X\n", | ||
703 | __func__, mbox_cmd[0], mbox_cmd[1], mbox_cmd[2], | ||
704 | mbox_cmd[3], mbox_cmd[4], mbox_cmd[5], mbox_cmd[6], | ||
705 | mbox_cmd[7])); | ||
706 | |||
707 | status = qla4xxx_mailbox_command(ha, MBOX_REG_COUNT, 8, &mbox_cmd[0], | ||
708 | &mbox_sts[0]); | ||
709 | |||
710 | if (status == QLA_SUCCESS) | ||
711 | bsg_reply->result = DID_OK << 16; | ||
712 | else | ||
713 | bsg_reply->result = DID_ERROR << 16; | ||
714 | |||
715 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
716 | "%s: mbox_sts: %08X %08X %08X %08X %08X %08X %08X %08X\n", | ||
717 | __func__, mbox_sts[0], mbox_sts[1], mbox_sts[2], | ||
718 | mbox_sts[3], mbox_sts[4], mbox_sts[5], mbox_sts[6], | ||
719 | mbox_sts[7])); | ||
720 | |||
721 | /* Send mbox_sts to application */ | ||
722 | bsg_job->reply_len = sizeof(struct iscsi_bsg_reply) + sizeof(mbox_sts); | ||
723 | rsp_ptr = ((uint8_t *)bsg_reply) + sizeof(struct iscsi_bsg_reply); | ||
724 | memcpy(rsp_ptr, mbox_sts, sizeof(mbox_sts)); | ||
725 | restore: | ||
726 | if (is_qla8032(ha) || is_qla8042(ha)) { | ||
727 | status = qla4_83xx_post_loopback_config(ha, mbox_cmd); | ||
728 | if (status != QLA_SUCCESS) { | ||
729 | bsg_reply->result = DID_ERROR << 16; | ||
730 | goto exit_loopback_cmd; | ||
731 | } | ||
732 | |||
733 | /* for pre_loopback_config() wait for LINK UP only | ||
734 | * if PHY LINK is UP */ | ||
735 | if (!(ha->addl_fw_state & FW_ADDSTATE_LINK_UP)) | ||
736 | wait_for_link = 0; | ||
737 | |||
738 | status = qla4_83xx_wait_for_loopback_config_comp(ha, | ||
739 | wait_for_link); | ||
740 | if (status != QLA_SUCCESS) { | ||
741 | bsg_reply->result = DID_TIME_OUT << 16; | ||
742 | goto exit_loopback_cmd; | ||
743 | } | ||
744 | } | ||
745 | exit_loopback_cmd: | ||
746 | DEBUG2(ql4_printk(KERN_INFO, ha, | ||
747 | "%s: bsg_reply->result = x%x, status = %s\n", | ||
748 | __func__, bsg_reply->result, STATUS(status))); | ||
749 | bsg_job_done(bsg_job, bsg_reply->result, | ||
750 | bsg_reply->reply_payload_rcv_len); | ||
751 | } | ||
752 | |||
753 | static int qla4xxx_execute_diag_test(struct bsg_job *bsg_job) | ||
754 | { | ||
755 | struct Scsi_Host *host = iscsi_job_to_shost(bsg_job); | ||
756 | struct scsi_qla_host *ha = to_qla_host(host); | ||
757 | struct iscsi_bsg_request *bsg_req = bsg_job->request; | ||
758 | uint32_t diag_cmd; | ||
759 | int rval = -EINVAL; | ||
760 | |||
761 | DEBUG2(ql4_printk(KERN_INFO, ha, "%s: in\n", __func__)); | ||
762 | |||
763 | diag_cmd = bsg_req->rqst_data.h_vendor.vendor_cmd[1]; | ||
764 | if (diag_cmd == MBOX_CMD_DIAG_TEST) { | ||
765 | switch (bsg_req->rqst_data.h_vendor.vendor_cmd[2]) { | ||
766 | case QL_DIAG_CMD_TEST_DDR_SIZE: | ||
767 | case QL_DIAG_CMD_TEST_DDR_RW: | ||
768 | case QL_DIAG_CMD_TEST_ONCHIP_MEM_RW: | ||
769 | case QL_DIAG_CMD_TEST_NVRAM: | ||
770 | case QL_DIAG_CMD_TEST_FLASH_ROM: | ||
771 | case QL_DIAG_CMD_TEST_DMA_XFER: | ||
772 | case QL_DIAG_CMD_SELF_DDR_RW: | ||
773 | case QL_DIAG_CMD_SELF_ONCHIP_MEM_RW: | ||
774 | /* Execute diag test for adapter RAM/FLASH */ | ||
775 | ql4xxx_execute_diag_cmd(bsg_job); | ||
776 | /* Always return success as we want to sent bsg_reply | ||
777 | * to Application */ | ||
778 | rval = QLA_SUCCESS; | ||
779 | break; | ||
780 | |||
781 | case QL_DIAG_CMD_TEST_INT_LOOPBACK: | ||
782 | case QL_DIAG_CMD_TEST_EXT_LOOPBACK: | ||
783 | /* Execute diag test for Network */ | ||
784 | qla4xxx_execute_diag_loopback_cmd(bsg_job); | ||
785 | /* Always return success as we want to sent bsg_reply | ||
786 | * to Application */ | ||
787 | rval = QLA_SUCCESS; | ||
788 | break; | ||
789 | default: | ||
790 | ql4_printk(KERN_ERR, ha, "%s: Invalid diag test: 0x%x\n", | ||
791 | __func__, | ||
792 | bsg_req->rqst_data.h_vendor.vendor_cmd[2]); | ||
793 | } | ||
794 | } else if ((diag_cmd == MBOX_CMD_SET_LED_CONFIG) || | ||
795 | (diag_cmd == MBOX_CMD_GET_LED_CONFIG)) { | ||
796 | ql4xxx_execute_diag_cmd(bsg_job); | ||
797 | rval = QLA_SUCCESS; | ||
798 | } else { | ||
799 | ql4_printk(KERN_ERR, ha, "%s: Invalid diag cmd: 0x%x\n", | ||
800 | __func__, diag_cmd); | ||
801 | } | ||
802 | |||
803 | return rval; | ||
804 | } | ||
805 | |||
449 | /** | 806 | /** |
450 | * qla4xxx_process_vendor_specific - handle vendor specific bsg request | 807 | * qla4xxx_process_vendor_specific - handle vendor specific bsg request |
451 | * @job: iscsi_bsg_job to handle | 808 | * @job: iscsi_bsg_job to handle |
@@ -479,6 +836,9 @@ int qla4xxx_process_vendor_specific(struct bsg_job *bsg_job) | |||
479 | case QLISCSI_VND_GET_ACB: | 836 | case QLISCSI_VND_GET_ACB: |
480 | return qla4xxx_bsg_get_acb(bsg_job); | 837 | return qla4xxx_bsg_get_acb(bsg_job); |
481 | 838 | ||
839 | case QLISCSI_VND_DIAG_TEST: | ||
840 | return qla4xxx_execute_diag_test(bsg_job); | ||
841 | |||
482 | default: | 842 | default: |
483 | ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: " | 843 | ql4_printk(KERN_ERR, ha, "%s: invalid BSG vendor command: " |
484 | "0x%x\n", __func__, bsg_req->msgcode); | 844 | "0x%x\n", __func__, bsg_req->msgcode); |