diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_nx.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_nx.c | 910 |
1 files changed, 884 insertions, 26 deletions
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index 049807cda419..43a0a9a4556f 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c | |||
@@ -7,6 +7,8 @@ | |||
7 | #include "qla_def.h" | 7 | #include "qla_def.h" |
8 | #include <linux/delay.h> | 8 | #include <linux/delay.h> |
9 | #include <linux/pci.h> | 9 | #include <linux/pci.h> |
10 | #include <linux/ratelimit.h> | ||
11 | #include <linux/vmalloc.h> | ||
10 | #include <scsi/scsi_tcq.h> | 12 | #include <scsi/scsi_tcq.h> |
11 | 13 | ||
12 | #define MASK(n) ((1ULL<<(n))-1) | 14 | #define MASK(n) ((1ULL<<(n))-1) |
@@ -328,7 +330,7 @@ unsigned qla82xx_crb_hub_agt[64] = { | |||
328 | }; | 330 | }; |
329 | 331 | ||
330 | /* Device states */ | 332 | /* Device states */ |
331 | char *qdev_state[] = { | 333 | char *q_dev_state[] = { |
332 | "Unknown", | 334 | "Unknown", |
333 | "Cold", | 335 | "Cold", |
334 | "Initializing", | 336 | "Initializing", |
@@ -339,6 +341,11 @@ char *qdev_state[] = { | |||
339 | "Quiescent", | 341 | "Quiescent", |
340 | }; | 342 | }; |
341 | 343 | ||
344 | char *qdev_state(uint32_t dev_state) | ||
345 | { | ||
346 | return q_dev_state[dev_state]; | ||
347 | } | ||
348 | |||
342 | /* | 349 | /* |
343 | * In: 'off' is offset from CRB space in 128M pci map | 350 | * In: 'off' is offset from CRB space in 128M pci map |
344 | * Out: 'off' is 2M pci map addr | 351 | * Out: 'off' is 2M pci map addr |
@@ -2355,9 +2362,13 @@ qla82xx_need_reset(struct qla_hw_data *ha) | |||
2355 | uint32_t drv_state; | 2362 | uint32_t drv_state; |
2356 | int rval; | 2363 | int rval; |
2357 | 2364 | ||
2358 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); | 2365 | if (ha->flags.isp82xx_reset_owner) |
2359 | rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); | 2366 | return 1; |
2360 | return rval; | 2367 | else { |
2368 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); | ||
2369 | rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); | ||
2370 | return rval; | ||
2371 | } | ||
2361 | } | 2372 | } |
2362 | 2373 | ||
2363 | static inline void | 2374 | static inline void |
@@ -2374,8 +2385,8 @@ qla82xx_set_rst_ready(struct qla_hw_data *ha) | |||
2374 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); | 2385 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); |
2375 | } | 2386 | } |
2376 | drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); | 2387 | drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); |
2377 | ql_log(ql_log_info, vha, 0x00bb, | 2388 | ql_dbg(ql_dbg_init, vha, 0x00bb, |
2378 | "drv_state = 0x%x.\n", drv_state); | 2389 | "drv_state = 0x%08x.\n", drv_state); |
2379 | qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); | 2390 | qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); |
2380 | } | 2391 | } |
2381 | 2392 | ||
@@ -3528,7 +3539,7 @@ qla82xx_dev_failed_handler(scsi_qla_host_t *vha) | |||
3528 | static void | 3539 | static void |
3529 | qla82xx_need_reset_handler(scsi_qla_host_t *vha) | 3540 | qla82xx_need_reset_handler(scsi_qla_host_t *vha) |
3530 | { | 3541 | { |
3531 | uint32_t dev_state, drv_state, drv_active; | 3542 | uint32_t dev_state, drv_state, drv_active, active_mask; |
3532 | unsigned long reset_timeout; | 3543 | unsigned long reset_timeout; |
3533 | struct qla_hw_data *ha = vha->hw; | 3544 | struct qla_hw_data *ha = vha->hw; |
3534 | struct req_que *req = ha->req_q_map[0]; | 3545 | struct req_que *req = ha->req_q_map[0]; |
@@ -3541,15 +3552,32 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) | |||
3541 | qla82xx_idc_lock(ha); | 3552 | qla82xx_idc_lock(ha); |
3542 | } | 3553 | } |
3543 | 3554 | ||
3544 | qla82xx_set_rst_ready(ha); | 3555 | drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); |
3556 | if (!ha->flags.isp82xx_reset_owner) { | ||
3557 | ql_dbg(ql_dbg_p3p, vha, 0xb028, | ||
3558 | "reset_acknowledged by 0x%x\n", ha->portnum); | ||
3559 | qla82xx_set_rst_ready(ha); | ||
3560 | } else { | ||
3561 | active_mask = ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4)); | ||
3562 | drv_active &= active_mask; | ||
3563 | ql_dbg(ql_dbg_p3p, vha, 0xb029, | ||
3564 | "active_mask: 0x%08x\n", active_mask); | ||
3565 | } | ||
3545 | 3566 | ||
3546 | /* wait for 10 seconds for reset ack from all functions */ | 3567 | /* wait for 10 seconds for reset ack from all functions */ |
3547 | reset_timeout = jiffies + (ha->nx_reset_timeout * HZ); | 3568 | reset_timeout = jiffies + (ha->nx_reset_timeout * HZ); |
3548 | 3569 | ||
3549 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); | 3570 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); |
3550 | drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); | 3571 | drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); |
3572 | dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); | ||
3551 | 3573 | ||
3552 | while (drv_state != drv_active) { | 3574 | ql_dbg(ql_dbg_p3p, vha, 0xb02a, |
3575 | "drv_state: 0x%08x, drv_active: 0x%08x, " | ||
3576 | "dev_state: 0x%08x, active_mask: 0x%08x\n", | ||
3577 | drv_state, drv_active, dev_state, active_mask); | ||
3578 | |||
3579 | while (drv_state != drv_active && | ||
3580 | dev_state != QLA82XX_DEV_INITIALIZING) { | ||
3553 | if (time_after_eq(jiffies, reset_timeout)) { | 3581 | if (time_after_eq(jiffies, reset_timeout)) { |
3554 | ql_log(ql_log_warn, vha, 0x00b5, | 3582 | ql_log(ql_log_warn, vha, 0x00b5, |
3555 | "Reset timeout.\n"); | 3583 | "Reset timeout.\n"); |
@@ -3560,22 +3588,78 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) | |||
3560 | qla82xx_idc_lock(ha); | 3588 | qla82xx_idc_lock(ha); |
3561 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); | 3589 | drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); |
3562 | drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); | 3590 | drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); |
3591 | if (ha->flags.isp82xx_reset_owner) | ||
3592 | drv_active &= active_mask; | ||
3593 | dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); | ||
3563 | } | 3594 | } |
3564 | 3595 | ||
3565 | dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); | 3596 | ql_dbg(ql_dbg_p3p, vha, 0xb02b, |
3597 | "drv_state: 0x%08x, drv_active: 0x%08x, " | ||
3598 | "dev_state: 0x%08x, active_mask: 0x%08x\n", | ||
3599 | drv_state, drv_active, dev_state, active_mask); | ||
3600 | |||
3566 | ql_log(ql_log_info, vha, 0x00b6, | 3601 | ql_log(ql_log_info, vha, 0x00b6, |
3567 | "Device state is 0x%x = %s.\n", | 3602 | "Device state is 0x%x = %s.\n", |
3568 | dev_state, | 3603 | dev_state, |
3569 | dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown"); | 3604 | dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); |
3570 | 3605 | ||
3571 | /* Force to DEV_COLD unless someone else is starting a reset */ | 3606 | /* Force to DEV_COLD unless someone else is starting a reset */ |
3572 | if (dev_state != QLA82XX_DEV_INITIALIZING) { | 3607 | if (dev_state != QLA82XX_DEV_INITIALIZING && |
3608 | dev_state != QLA82XX_DEV_COLD) { | ||
3573 | ql_log(ql_log_info, vha, 0x00b7, | 3609 | ql_log(ql_log_info, vha, 0x00b7, |
3574 | "HW State: COLD/RE-INIT.\n"); | 3610 | "HW State: COLD/RE-INIT.\n"); |
3575 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); | 3611 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); |
3612 | if (ql2xmdenable) { | ||
3613 | if (qla82xx_md_collect(vha)) | ||
3614 | ql_log(ql_log_warn, vha, 0xb02c, | ||
3615 | "Not able to collect minidump.\n"); | ||
3616 | } else | ||
3617 | ql_log(ql_log_warn, vha, 0xb04f, | ||
3618 | "Minidump disabled.\n"); | ||
3576 | } | 3619 | } |
3577 | } | 3620 | } |
3578 | 3621 | ||
3622 | static void | ||
3623 | qla82xx_check_md_needed(scsi_qla_host_t *vha) | ||
3624 | { | ||
3625 | struct qla_hw_data *ha = vha->hw; | ||
3626 | uint16_t fw_major_version, fw_minor_version, fw_subminor_version; | ||
3627 | uint16_t fw_attributes; | ||
3628 | uint32_t fw_memory_size, mpi_capabilities; | ||
3629 | uint8_t mpi_version[3], phy_version[3]; | ||
3630 | |||
3631 | if (!ha->fw_dumped) { | ||
3632 | qla2x00_get_fw_version(vha, | ||
3633 | &fw_major_version, | ||
3634 | &fw_minor_version, | ||
3635 | &fw_subminor_version, | ||
3636 | &fw_attributes, &fw_memory_size, | ||
3637 | mpi_version, &mpi_capabilities, | ||
3638 | phy_version); | ||
3639 | |||
3640 | if (fw_major_version != ha->fw_major_version || | ||
3641 | fw_minor_version != ha->fw_minor_version || | ||
3642 | fw_subminor_version != ha->fw_subminor_version) { | ||
3643 | ql_log(ql_log_info, vha, 0xb02d, | ||
3644 | "Firmware version differs " | ||
3645 | "Previous version: %d:%d:%d - " | ||
3646 | "New version: %d:%d:%d\n", | ||
3647 | ha->fw_major_version, | ||
3648 | ha->fw_minor_version, ha->fw_subminor_version, | ||
3649 | fw_major_version, fw_minor_version, | ||
3650 | fw_subminor_version); | ||
3651 | /* Release MiniDump resources */ | ||
3652 | qla82xx_md_free(vha); | ||
3653 | /* ALlocate MiniDump resources */ | ||
3654 | qla82xx_md_prep(vha); | ||
3655 | } | ||
3656 | } else | ||
3657 | ql_log(ql_log_info, vha, 0xb02e, | ||
3658 | "Firmware dump available to retrieve\n", | ||
3659 | vha->host_no); | ||
3660 | } | ||
3661 | |||
3662 | |||
3579 | int | 3663 | int |
3580 | qla82xx_check_fw_alive(scsi_qla_host_t *vha) | 3664 | qla82xx_check_fw_alive(scsi_qla_host_t *vha) |
3581 | { | 3665 | { |
@@ -3637,7 +3721,7 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) | |||
3637 | ql_log(ql_log_info, vha, 0x009b, | 3721 | ql_log(ql_log_info, vha, 0x009b, |
3638 | "Device state is 0x%x = %s.\n", | 3722 | "Device state is 0x%x = %s.\n", |
3639 | dev_state, | 3723 | dev_state, |
3640 | dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown"); | 3724 | dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); |
3641 | 3725 | ||
3642 | /* wait for 30 seconds for device to go ready */ | 3726 | /* wait for 30 seconds for device to go ready */ |
3643 | dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ); | 3727 | dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ); |
@@ -3659,16 +3743,18 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) | |||
3659 | ql_log(ql_log_info, vha, 0x009d, | 3743 | ql_log(ql_log_info, vha, 0x009d, |
3660 | "Device state is 0x%x = %s.\n", | 3744 | "Device state is 0x%x = %s.\n", |
3661 | dev_state, | 3745 | dev_state, |
3662 | dev_state < MAX_STATES ? qdev_state[dev_state] : | 3746 | dev_state < MAX_STATES ? qdev_state(dev_state) : |
3663 | "Unknown"); | 3747 | "Unknown"); |
3664 | } | 3748 | } |
3665 | 3749 | ||
3666 | switch (dev_state) { | 3750 | switch (dev_state) { |
3667 | case QLA82XX_DEV_READY: | 3751 | case QLA82XX_DEV_READY: |
3752 | qla82xx_check_md_needed(vha); | ||
3753 | ha->flags.isp82xx_reset_owner = 0; | ||
3668 | goto exit; | 3754 | goto exit; |
3669 | case QLA82XX_DEV_COLD: | 3755 | case QLA82XX_DEV_COLD: |
3670 | rval = qla82xx_device_bootstrap(vha); | 3756 | rval = qla82xx_device_bootstrap(vha); |
3671 | goto exit; | 3757 | break; |
3672 | case QLA82XX_DEV_INITIALIZING: | 3758 | case QLA82XX_DEV_INITIALIZING: |
3673 | qla82xx_idc_unlock(ha); | 3759 | qla82xx_idc_unlock(ha); |
3674 | msleep(1000); | 3760 | msleep(1000); |
@@ -3791,6 +3877,28 @@ int qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) | |||
3791 | return rval; | 3877 | return rval; |
3792 | } | 3878 | } |
3793 | 3879 | ||
3880 | void | ||
3881 | qla82xx_set_reset_owner(scsi_qla_host_t *vha) | ||
3882 | { | ||
3883 | struct qla_hw_data *ha = vha->hw; | ||
3884 | uint32_t dev_state; | ||
3885 | |||
3886 | dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); | ||
3887 | if (dev_state == QLA82XX_DEV_READY) { | ||
3888 | ql_log(ql_log_info, vha, 0xb02f, | ||
3889 | "HW State: NEED RESET\n"); | ||
3890 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | ||
3891 | QLA82XX_DEV_NEED_RESET); | ||
3892 | ha->flags.isp82xx_reset_owner = 1; | ||
3893 | ql_dbg(ql_dbg_p3p, vha, 0xb030, | ||
3894 | "reset_owner is 0x%x\n", ha->portnum); | ||
3895 | } else | ||
3896 | ql_log(ql_log_info, vha, 0xb031, | ||
3897 | "Device state is 0x%x = %s.\n", | ||
3898 | dev_state, | ||
3899 | dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); | ||
3900 | } | ||
3901 | |||
3794 | /* | 3902 | /* |
3795 | * qla82xx_abort_isp | 3903 | * qla82xx_abort_isp |
3796 | * Resets ISP and aborts all outstanding commands. | 3904 | * Resets ISP and aborts all outstanding commands. |
@@ -3806,7 +3914,6 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) | |||
3806 | { | 3914 | { |
3807 | int rval; | 3915 | int rval; |
3808 | struct qla_hw_data *ha = vha->hw; | 3916 | struct qla_hw_data *ha = vha->hw; |
3809 | uint32_t dev_state; | ||
3810 | 3917 | ||
3811 | if (vha->device_flags & DFLG_DEV_FAILED) { | 3918 | if (vha->device_flags & DFLG_DEV_FAILED) { |
3812 | ql_log(ql_log_warn, vha, 0x8024, | 3919 | ql_log(ql_log_warn, vha, 0x8024, |
@@ -3816,16 +3923,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) | |||
3816 | ha->flags.isp82xx_reset_hdlr_active = 1; | 3923 | ha->flags.isp82xx_reset_hdlr_active = 1; |
3817 | 3924 | ||
3818 | qla82xx_idc_lock(ha); | 3925 | qla82xx_idc_lock(ha); |
3819 | dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); | 3926 | qla82xx_set_reset_owner(vha); |
3820 | if (dev_state == QLA82XX_DEV_READY) { | ||
3821 | ql_log(ql_log_info, vha, 0x8025, | ||
3822 | "HW State: NEED RESET.\n"); | ||
3823 | qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, | ||
3824 | QLA82XX_DEV_NEED_RESET); | ||
3825 | } else | ||
3826 | ql_log(ql_log_info, vha, 0x8026, | ||
3827 | "Hw State: %s.\n", dev_state < MAX_STATES ? | ||
3828 | qdev_state[dev_state] : "Unknown"); | ||
3829 | qla82xx_idc_unlock(ha); | 3927 | qla82xx_idc_unlock(ha); |
3830 | 3928 | ||
3831 | rval = qla82xx_device_state_handler(vha); | 3929 | rval = qla82xx_device_state_handler(vha); |
@@ -4016,3 +4114,763 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) | |||
4016 | } | 4114 | } |
4017 | } | 4115 | } |
4018 | } | 4116 | } |
4117 | |||
4118 | /* Minidump related functions */ | ||
4119 | int | ||
4120 | qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag) | ||
4121 | { | ||
4122 | uint32_t off_value, rval = 0; | ||
4123 | |||
4124 | WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase), | ||
4125 | (off & 0xFFFF0000)); | ||
4126 | |||
4127 | /* Read back value to make sure write has gone through */ | ||
4128 | RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase)); | ||
4129 | off_value = (off & 0x0000FFFF); | ||
4130 | |||
4131 | if (flag) | ||
4132 | WRT_REG_DWORD((void *) | ||
4133 | (off_value + CRB_INDIRECT_2M + ha->nx_pcibase), | ||
4134 | data); | ||
4135 | else | ||
4136 | rval = RD_REG_DWORD((void *) | ||
4137 | (off_value + CRB_INDIRECT_2M + ha->nx_pcibase)); | ||
4138 | |||
4139 | return rval; | ||
4140 | } | ||
4141 | |||
4142 | static int | ||
4143 | qla82xx_minidump_process_control(scsi_qla_host_t *vha, | ||
4144 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4145 | { | ||
4146 | struct qla_hw_data *ha = vha->hw; | ||
4147 | struct qla82xx_md_entry_crb *crb_entry; | ||
4148 | uint32_t read_value, opcode, poll_time; | ||
4149 | uint32_t addr, index, crb_addr; | ||
4150 | unsigned long wtime; | ||
4151 | struct qla82xx_md_template_hdr *tmplt_hdr; | ||
4152 | uint32_t rval = QLA_SUCCESS; | ||
4153 | int i; | ||
4154 | |||
4155 | tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr; | ||
4156 | crb_entry = (struct qla82xx_md_entry_crb *)entry_hdr; | ||
4157 | crb_addr = crb_entry->addr; | ||
4158 | |||
4159 | for (i = 0; i < crb_entry->op_count; i++) { | ||
4160 | opcode = crb_entry->crb_ctrl.opcode; | ||
4161 | if (opcode & QLA82XX_DBG_OPCODE_WR) { | ||
4162 | qla82xx_md_rw_32(ha, crb_addr, | ||
4163 | crb_entry->value_1, 1); | ||
4164 | opcode &= ~QLA82XX_DBG_OPCODE_WR; | ||
4165 | } | ||
4166 | |||
4167 | if (opcode & QLA82XX_DBG_OPCODE_RW) { | ||
4168 | read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); | ||
4169 | qla82xx_md_rw_32(ha, crb_addr, read_value, 1); | ||
4170 | opcode &= ~QLA82XX_DBG_OPCODE_RW; | ||
4171 | } | ||
4172 | |||
4173 | if (opcode & QLA82XX_DBG_OPCODE_AND) { | ||
4174 | read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); | ||
4175 | read_value &= crb_entry->value_2; | ||
4176 | opcode &= ~QLA82XX_DBG_OPCODE_AND; | ||
4177 | if (opcode & QLA82XX_DBG_OPCODE_OR) { | ||
4178 | read_value |= crb_entry->value_3; | ||
4179 | opcode &= ~QLA82XX_DBG_OPCODE_OR; | ||
4180 | } | ||
4181 | qla82xx_md_rw_32(ha, crb_addr, read_value, 1); | ||
4182 | } | ||
4183 | |||
4184 | if (opcode & QLA82XX_DBG_OPCODE_OR) { | ||
4185 | read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); | ||
4186 | read_value |= crb_entry->value_3; | ||
4187 | qla82xx_md_rw_32(ha, crb_addr, read_value, 1); | ||
4188 | opcode &= ~QLA82XX_DBG_OPCODE_OR; | ||
4189 | } | ||
4190 | |||
4191 | if (opcode & QLA82XX_DBG_OPCODE_POLL) { | ||
4192 | poll_time = crb_entry->crb_strd.poll_timeout; | ||
4193 | wtime = jiffies + poll_time; | ||
4194 | read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0); | ||
4195 | |||
4196 | do { | ||
4197 | if ((read_value & crb_entry->value_2) | ||
4198 | == crb_entry->value_1) | ||
4199 | break; | ||
4200 | else if (time_after_eq(jiffies, wtime)) { | ||
4201 | /* capturing dump failed */ | ||
4202 | rval = QLA_FUNCTION_FAILED; | ||
4203 | break; | ||
4204 | } else | ||
4205 | read_value = qla82xx_md_rw_32(ha, | ||
4206 | crb_addr, 0, 0); | ||
4207 | } while (1); | ||
4208 | opcode &= ~QLA82XX_DBG_OPCODE_POLL; | ||
4209 | } | ||
4210 | |||
4211 | if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) { | ||
4212 | if (crb_entry->crb_strd.state_index_a) { | ||
4213 | index = crb_entry->crb_strd.state_index_a; | ||
4214 | addr = tmplt_hdr->saved_state_array[index]; | ||
4215 | } else | ||
4216 | addr = crb_addr; | ||
4217 | |||
4218 | read_value = qla82xx_md_rw_32(ha, addr, 0, 0); | ||
4219 | index = crb_entry->crb_ctrl.state_index_v; | ||
4220 | tmplt_hdr->saved_state_array[index] = read_value; | ||
4221 | opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE; | ||
4222 | } | ||
4223 | |||
4224 | if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) { | ||
4225 | if (crb_entry->crb_strd.state_index_a) { | ||
4226 | index = crb_entry->crb_strd.state_index_a; | ||
4227 | addr = tmplt_hdr->saved_state_array[index]; | ||
4228 | } else | ||
4229 | addr = crb_addr; | ||
4230 | |||
4231 | if (crb_entry->crb_ctrl.state_index_v) { | ||
4232 | index = crb_entry->crb_ctrl.state_index_v; | ||
4233 | read_value = | ||
4234 | tmplt_hdr->saved_state_array[index]; | ||
4235 | } else | ||
4236 | read_value = crb_entry->value_1; | ||
4237 | |||
4238 | qla82xx_md_rw_32(ha, addr, read_value, 1); | ||
4239 | opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE; | ||
4240 | } | ||
4241 | |||
4242 | if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) { | ||
4243 | index = crb_entry->crb_ctrl.state_index_v; | ||
4244 | read_value = tmplt_hdr->saved_state_array[index]; | ||
4245 | read_value <<= crb_entry->crb_ctrl.shl; | ||
4246 | read_value >>= crb_entry->crb_ctrl.shr; | ||
4247 | if (crb_entry->value_2) | ||
4248 | read_value &= crb_entry->value_2; | ||
4249 | read_value |= crb_entry->value_3; | ||
4250 | read_value += crb_entry->value_1; | ||
4251 | tmplt_hdr->saved_state_array[index] = read_value; | ||
4252 | opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE; | ||
4253 | } | ||
4254 | crb_addr += crb_entry->crb_strd.addr_stride; | ||
4255 | } | ||
4256 | return rval; | ||
4257 | } | ||
4258 | |||
4259 | static void | ||
4260 | qla82xx_minidump_process_rdocm(scsi_qla_host_t *vha, | ||
4261 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4262 | { | ||
4263 | struct qla_hw_data *ha = vha->hw; | ||
4264 | uint32_t r_addr, r_stride, loop_cnt, i, r_value; | ||
4265 | struct qla82xx_md_entry_rdocm *ocm_hdr; | ||
4266 | uint32_t *data_ptr = *d_ptr; | ||
4267 | |||
4268 | ocm_hdr = (struct qla82xx_md_entry_rdocm *)entry_hdr; | ||
4269 | r_addr = ocm_hdr->read_addr; | ||
4270 | r_stride = ocm_hdr->read_addr_stride; | ||
4271 | loop_cnt = ocm_hdr->op_count; | ||
4272 | |||
4273 | for (i = 0; i < loop_cnt; i++) { | ||
4274 | r_value = RD_REG_DWORD((void *)(r_addr + ha->nx_pcibase)); | ||
4275 | *data_ptr++ = cpu_to_le32(r_value); | ||
4276 | r_addr += r_stride; | ||
4277 | } | ||
4278 | *d_ptr = data_ptr; | ||
4279 | } | ||
4280 | |||
4281 | static void | ||
4282 | qla82xx_minidump_process_rdmux(scsi_qla_host_t *vha, | ||
4283 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4284 | { | ||
4285 | struct qla_hw_data *ha = vha->hw; | ||
4286 | uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value; | ||
4287 | struct qla82xx_md_entry_mux *mux_hdr; | ||
4288 | uint32_t *data_ptr = *d_ptr; | ||
4289 | |||
4290 | mux_hdr = (struct qla82xx_md_entry_mux *)entry_hdr; | ||
4291 | r_addr = mux_hdr->read_addr; | ||
4292 | s_addr = mux_hdr->select_addr; | ||
4293 | s_stride = mux_hdr->select_value_stride; | ||
4294 | s_value = mux_hdr->select_value; | ||
4295 | loop_cnt = mux_hdr->op_count; | ||
4296 | |||
4297 | for (i = 0; i < loop_cnt; i++) { | ||
4298 | qla82xx_md_rw_32(ha, s_addr, s_value, 1); | ||
4299 | r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0); | ||
4300 | *data_ptr++ = cpu_to_le32(s_value); | ||
4301 | *data_ptr++ = cpu_to_le32(r_value); | ||
4302 | s_value += s_stride; | ||
4303 | } | ||
4304 | *d_ptr = data_ptr; | ||
4305 | } | ||
4306 | |||
4307 | static void | ||
4308 | qla82xx_minidump_process_rdcrb(scsi_qla_host_t *vha, | ||
4309 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4310 | { | ||
4311 | struct qla_hw_data *ha = vha->hw; | ||
4312 | uint32_t r_addr, r_stride, loop_cnt, i, r_value; | ||
4313 | struct qla82xx_md_entry_crb *crb_hdr; | ||
4314 | uint32_t *data_ptr = *d_ptr; | ||
4315 | |||
4316 | crb_hdr = (struct qla82xx_md_entry_crb *)entry_hdr; | ||
4317 | r_addr = crb_hdr->addr; | ||
4318 | r_stride = crb_hdr->crb_strd.addr_stride; | ||
4319 | loop_cnt = crb_hdr->op_count; | ||
4320 | |||
4321 | for (i = 0; i < loop_cnt; i++) { | ||
4322 | r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0); | ||
4323 | *data_ptr++ = cpu_to_le32(r_addr); | ||
4324 | *data_ptr++ = cpu_to_le32(r_value); | ||
4325 | r_addr += r_stride; | ||
4326 | } | ||
4327 | *d_ptr = data_ptr; | ||
4328 | } | ||
4329 | |||
4330 | static int | ||
4331 | qla82xx_minidump_process_l2tag(scsi_qla_host_t *vha, | ||
4332 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4333 | { | ||
4334 | struct qla_hw_data *ha = vha->hw; | ||
4335 | uint32_t addr, r_addr, c_addr, t_r_addr; | ||
4336 | uint32_t i, k, loop_count, t_value, r_cnt, r_value; | ||
4337 | unsigned long p_wait, w_time, p_mask; | ||
4338 | uint32_t c_value_w, c_value_r; | ||
4339 | struct qla82xx_md_entry_cache *cache_hdr; | ||
4340 | int rval = QLA_FUNCTION_FAILED; | ||
4341 | uint32_t *data_ptr = *d_ptr; | ||
4342 | |||
4343 | cache_hdr = (struct qla82xx_md_entry_cache *)entry_hdr; | ||
4344 | loop_count = cache_hdr->op_count; | ||
4345 | r_addr = cache_hdr->read_addr; | ||
4346 | c_addr = cache_hdr->control_addr; | ||
4347 | c_value_w = cache_hdr->cache_ctrl.write_value; | ||
4348 | |||
4349 | t_r_addr = cache_hdr->tag_reg_addr; | ||
4350 | t_value = cache_hdr->addr_ctrl.init_tag_value; | ||
4351 | r_cnt = cache_hdr->read_ctrl.read_addr_cnt; | ||
4352 | p_wait = cache_hdr->cache_ctrl.poll_wait; | ||
4353 | p_mask = cache_hdr->cache_ctrl.poll_mask; | ||
4354 | |||
4355 | for (i = 0; i < loop_count; i++) { | ||
4356 | qla82xx_md_rw_32(ha, t_r_addr, t_value, 1); | ||
4357 | if (c_value_w) | ||
4358 | qla82xx_md_rw_32(ha, c_addr, c_value_w, 1); | ||
4359 | |||
4360 | if (p_mask) { | ||
4361 | w_time = jiffies + p_wait; | ||
4362 | do { | ||
4363 | c_value_r = qla82xx_md_rw_32(ha, c_addr, 0, 0); | ||
4364 | if ((c_value_r & p_mask) == 0) | ||
4365 | break; | ||
4366 | else if (time_after_eq(jiffies, w_time)) { | ||
4367 | /* capturing dump failed */ | ||
4368 | ql_dbg(ql_dbg_p3p, vha, 0xb032, | ||
4369 | "c_value_r: 0x%x, poll_mask: 0x%lx, " | ||
4370 | "w_time: 0x%lx\n", | ||
4371 | c_value_r, p_mask, w_time); | ||
4372 | return rval; | ||
4373 | } | ||
4374 | } while (1); | ||
4375 | } | ||
4376 | |||
4377 | addr = r_addr; | ||
4378 | for (k = 0; k < r_cnt; k++) { | ||
4379 | r_value = qla82xx_md_rw_32(ha, addr, 0, 0); | ||
4380 | *data_ptr++ = cpu_to_le32(r_value); | ||
4381 | addr += cache_hdr->read_ctrl.read_addr_stride; | ||
4382 | } | ||
4383 | t_value += cache_hdr->addr_ctrl.tag_value_stride; | ||
4384 | } | ||
4385 | *d_ptr = data_ptr; | ||
4386 | return QLA_SUCCESS; | ||
4387 | } | ||
4388 | |||
4389 | static void | ||
4390 | qla82xx_minidump_process_l1cache(scsi_qla_host_t *vha, | ||
4391 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4392 | { | ||
4393 | struct qla_hw_data *ha = vha->hw; | ||
4394 | uint32_t addr, r_addr, c_addr, t_r_addr; | ||
4395 | uint32_t i, k, loop_count, t_value, r_cnt, r_value; | ||
4396 | uint32_t c_value_w; | ||
4397 | struct qla82xx_md_entry_cache *cache_hdr; | ||
4398 | uint32_t *data_ptr = *d_ptr; | ||
4399 | |||
4400 | cache_hdr = (struct qla82xx_md_entry_cache *)entry_hdr; | ||
4401 | loop_count = cache_hdr->op_count; | ||
4402 | r_addr = cache_hdr->read_addr; | ||
4403 | c_addr = cache_hdr->control_addr; | ||
4404 | c_value_w = cache_hdr->cache_ctrl.write_value; | ||
4405 | |||
4406 | t_r_addr = cache_hdr->tag_reg_addr; | ||
4407 | t_value = cache_hdr->addr_ctrl.init_tag_value; | ||
4408 | r_cnt = cache_hdr->read_ctrl.read_addr_cnt; | ||
4409 | |||
4410 | for (i = 0; i < loop_count; i++) { | ||
4411 | qla82xx_md_rw_32(ha, t_r_addr, t_value, 1); | ||
4412 | qla82xx_md_rw_32(ha, c_addr, c_value_w, 1); | ||
4413 | addr = r_addr; | ||
4414 | for (k = 0; k < r_cnt; k++) { | ||
4415 | r_value = qla82xx_md_rw_32(ha, addr, 0, 0); | ||
4416 | *data_ptr++ = cpu_to_le32(r_value); | ||
4417 | addr += cache_hdr->read_ctrl.read_addr_stride; | ||
4418 | } | ||
4419 | t_value += cache_hdr->addr_ctrl.tag_value_stride; | ||
4420 | } | ||
4421 | *d_ptr = data_ptr; | ||
4422 | } | ||
4423 | |||
4424 | static void | ||
4425 | qla82xx_minidump_process_queue(scsi_qla_host_t *vha, | ||
4426 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4427 | { | ||
4428 | struct qla_hw_data *ha = vha->hw; | ||
4429 | uint32_t s_addr, r_addr; | ||
4430 | uint32_t r_stride, r_value, r_cnt, qid = 0; | ||
4431 | uint32_t i, k, loop_cnt; | ||
4432 | struct qla82xx_md_entry_queue *q_hdr; | ||
4433 | uint32_t *data_ptr = *d_ptr; | ||
4434 | |||
4435 | q_hdr = (struct qla82xx_md_entry_queue *)entry_hdr; | ||
4436 | s_addr = q_hdr->select_addr; | ||
4437 | r_cnt = q_hdr->rd_strd.read_addr_cnt; | ||
4438 | r_stride = q_hdr->rd_strd.read_addr_stride; | ||
4439 | loop_cnt = q_hdr->op_count; | ||
4440 | |||
4441 | for (i = 0; i < loop_cnt; i++) { | ||
4442 | qla82xx_md_rw_32(ha, s_addr, qid, 1); | ||
4443 | r_addr = q_hdr->read_addr; | ||
4444 | for (k = 0; k < r_cnt; k++) { | ||
4445 | r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0); | ||
4446 | *data_ptr++ = cpu_to_le32(r_value); | ||
4447 | r_addr += r_stride; | ||
4448 | } | ||
4449 | qid += q_hdr->q_strd.queue_id_stride; | ||
4450 | } | ||
4451 | *d_ptr = data_ptr; | ||
4452 | } | ||
4453 | |||
4454 | static void | ||
4455 | qla82xx_minidump_process_rdrom(scsi_qla_host_t *vha, | ||
4456 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4457 | { | ||
4458 | struct qla_hw_data *ha = vha->hw; | ||
4459 | uint32_t r_addr, r_value; | ||
4460 | uint32_t i, loop_cnt; | ||
4461 | struct qla82xx_md_entry_rdrom *rom_hdr; | ||
4462 | uint32_t *data_ptr = *d_ptr; | ||
4463 | |||
4464 | rom_hdr = (struct qla82xx_md_entry_rdrom *)entry_hdr; | ||
4465 | r_addr = rom_hdr->read_addr; | ||
4466 | loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t); | ||
4467 | |||
4468 | for (i = 0; i < loop_cnt; i++) { | ||
4469 | qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW, | ||
4470 | (r_addr & 0xFFFF0000), 1); | ||
4471 | r_value = qla82xx_md_rw_32(ha, | ||
4472 | MD_DIRECT_ROM_READ_BASE + | ||
4473 | (r_addr & 0x0000FFFF), 0, 0); | ||
4474 | *data_ptr++ = cpu_to_le32(r_value); | ||
4475 | r_addr += sizeof(uint32_t); | ||
4476 | } | ||
4477 | *d_ptr = data_ptr; | ||
4478 | } | ||
4479 | |||
4480 | static int | ||
4481 | qla82xx_minidump_process_rdmem(scsi_qla_host_t *vha, | ||
4482 | qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr) | ||
4483 | { | ||
4484 | struct qla_hw_data *ha = vha->hw; | ||
4485 | uint32_t r_addr, r_value, r_data; | ||
4486 | uint32_t i, j, loop_cnt; | ||
4487 | struct qla82xx_md_entry_rdmem *m_hdr; | ||
4488 | unsigned long flags; | ||
4489 | int rval = QLA_FUNCTION_FAILED; | ||
4490 | uint32_t *data_ptr = *d_ptr; | ||
4491 | |||
4492 | m_hdr = (struct qla82xx_md_entry_rdmem *)entry_hdr; | ||
4493 | r_addr = m_hdr->read_addr; | ||
4494 | loop_cnt = m_hdr->read_data_size/16; | ||
4495 | |||
4496 | if (r_addr & 0xf) { | ||
4497 | ql_log(ql_log_warn, vha, 0xb033, | ||
4498 | "Read addr 0x%x not 16 bytes alligned\n", r_addr); | ||
4499 | return rval; | ||
4500 | } | ||
4501 | |||
4502 | if (m_hdr->read_data_size % 16) { | ||
4503 | ql_log(ql_log_warn, vha, 0xb034, | ||
4504 | "Read data[0x%x] not multiple of 16 bytes\n", | ||
4505 | m_hdr->read_data_size); | ||
4506 | return rval; | ||
4507 | } | ||
4508 | |||
4509 | ql_dbg(ql_dbg_p3p, vha, 0xb035, | ||
4510 | "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n", | ||
4511 | __func__, r_addr, m_hdr->read_data_size, loop_cnt); | ||
4512 | |||
4513 | write_lock_irqsave(&ha->hw_lock, flags); | ||
4514 | for (i = 0; i < loop_cnt; i++) { | ||
4515 | qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1); | ||
4516 | r_value = 0; | ||
4517 | qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1); | ||
4518 | r_value = MIU_TA_CTL_ENABLE; | ||
4519 | qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1); | ||
4520 | r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE; | ||
4521 | qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1); | ||
4522 | |||
4523 | for (j = 0; j < MAX_CTL_CHECK; j++) { | ||
4524 | r_value = qla82xx_md_rw_32(ha, | ||
4525 | MD_MIU_TEST_AGT_CTRL, 0, 0); | ||
4526 | if ((r_value & MIU_TA_CTL_BUSY) == 0) | ||
4527 | break; | ||
4528 | } | ||
4529 | |||
4530 | if (j >= MAX_CTL_CHECK) { | ||
4531 | printk_ratelimited(KERN_ERR | ||
4532 | "failed to read through agent\n"); | ||
4533 | write_unlock_irqrestore(&ha->hw_lock, flags); | ||
4534 | return rval; | ||
4535 | } | ||
4536 | |||
4537 | for (j = 0; j < 4; j++) { | ||
4538 | r_data = qla82xx_md_rw_32(ha, | ||
4539 | MD_MIU_TEST_AGT_RDDATA[j], 0, 0); | ||
4540 | *data_ptr++ = cpu_to_le32(r_data); | ||
4541 | } | ||
4542 | r_addr += 16; | ||
4543 | } | ||
4544 | write_unlock_irqrestore(&ha->hw_lock, flags); | ||
4545 | *d_ptr = data_ptr; | ||
4546 | return QLA_SUCCESS; | ||
4547 | } | ||
4548 | |||
4549 | static int | ||
4550 | qla82xx_validate_template_chksum(scsi_qla_host_t *vha) | ||
4551 | { | ||
4552 | struct qla_hw_data *ha = vha->hw; | ||
4553 | uint64_t chksum = 0; | ||
4554 | uint32_t *d_ptr = (uint32_t *)ha->md_tmplt_hdr; | ||
4555 | int count = ha->md_template_size/sizeof(uint32_t); | ||
4556 | |||
4557 | while (count-- > 0) | ||
4558 | chksum += *d_ptr++; | ||
4559 | while (chksum >> 32) | ||
4560 | chksum = (chksum & 0xFFFFFFFF) + (chksum >> 32); | ||
4561 | return ~chksum; | ||
4562 | } | ||
4563 | |||
4564 | static void | ||
4565 | qla82xx_mark_entry_skipped(scsi_qla_host_t *vha, | ||
4566 | qla82xx_md_entry_hdr_t *entry_hdr, int index) | ||
4567 | { | ||
4568 | entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG; | ||
4569 | ql_dbg(ql_dbg_p3p, vha, 0xb036, | ||
4570 | "Skipping entry[%d]: " | ||
4571 | "ETYPE[0x%x]-ELEVEL[0x%x]\n", | ||
4572 | index, entry_hdr->entry_type, | ||
4573 | entry_hdr->d_ctrl.entry_capture_mask); | ||
4574 | } | ||
4575 | |||
4576 | int | ||
4577 | qla82xx_md_collect(scsi_qla_host_t *vha) | ||
4578 | { | ||
4579 | struct qla_hw_data *ha = vha->hw; | ||
4580 | int no_entry_hdr = 0; | ||
4581 | qla82xx_md_entry_hdr_t *entry_hdr; | ||
4582 | struct qla82xx_md_template_hdr *tmplt_hdr; | ||
4583 | uint32_t *data_ptr; | ||
4584 | uint32_t total_data_size = 0, f_capture_mask, data_collected = 0; | ||
4585 | int i = 0, rval = QLA_FUNCTION_FAILED; | ||
4586 | |||
4587 | tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr; | ||
4588 | data_ptr = (uint32_t *)ha->md_dump; | ||
4589 | |||
4590 | if (ha->fw_dumped) { | ||
4591 | ql_log(ql_log_info, vha, 0xb037, | ||
4592 | "Firmware dump available to retrive\n"); | ||
4593 | goto md_failed; | ||
4594 | } | ||
4595 | |||
4596 | ha->fw_dumped = 0; | ||
4597 | |||
4598 | if (!ha->md_tmplt_hdr || !ha->md_dump) { | ||
4599 | ql_log(ql_log_warn, vha, 0xb038, | ||
4600 | "Memory not allocated for minidump capture\n"); | ||
4601 | goto md_failed; | ||
4602 | } | ||
4603 | |||
4604 | if (qla82xx_validate_template_chksum(vha)) { | ||
4605 | ql_log(ql_log_info, vha, 0xb039, | ||
4606 | "Template checksum validation error\n"); | ||
4607 | goto md_failed; | ||
4608 | } | ||
4609 | |||
4610 | no_entry_hdr = tmplt_hdr->num_of_entries; | ||
4611 | ql_dbg(ql_dbg_p3p, vha, 0xb03a, | ||
4612 | "No of entry headers in Template: 0x%x\n", no_entry_hdr); | ||
4613 | |||
4614 | ql_dbg(ql_dbg_p3p, vha, 0xb03b, | ||
4615 | "Capture Mask obtained: 0x%x\n", tmplt_hdr->capture_debug_level); | ||
4616 | |||
4617 | f_capture_mask = tmplt_hdr->capture_debug_level & 0xFF; | ||
4618 | |||
4619 | /* Validate whether required debug level is set */ | ||
4620 | if ((f_capture_mask & 0x3) != 0x3) { | ||
4621 | ql_log(ql_log_warn, vha, 0xb03c, | ||
4622 | "Minimum required capture mask[0x%x] level not set\n", | ||
4623 | f_capture_mask); | ||
4624 | goto md_failed; | ||
4625 | } | ||
4626 | tmplt_hdr->driver_capture_mask = ql2xmdcapmask; | ||
4627 | |||
4628 | tmplt_hdr->driver_info[0] = vha->host_no; | ||
4629 | tmplt_hdr->driver_info[1] = (QLA_DRIVER_MAJOR_VER << 24) | | ||
4630 | (QLA_DRIVER_MINOR_VER << 16) | (QLA_DRIVER_PATCH_VER << 8) | | ||
4631 | QLA_DRIVER_BETA_VER; | ||
4632 | |||
4633 | total_data_size = ha->md_dump_size; | ||
4634 | |||
4635 | ql_dbg(ql_log_info, vha, 0xb03d, | ||
4636 | "Total minidump data_size 0x%x to be captured\n", total_data_size); | ||
4637 | |||
4638 | /* Check whether template obtained is valid */ | ||
4639 | if (tmplt_hdr->entry_type != QLA82XX_TLHDR) { | ||
4640 | ql_log(ql_log_warn, vha, 0xb04e, | ||
4641 | "Bad template header entry type: 0x%x obtained\n", | ||
4642 | tmplt_hdr->entry_type); | ||
4643 | goto md_failed; | ||
4644 | } | ||
4645 | |||
4646 | entry_hdr = (qla82xx_md_entry_hdr_t *) \ | ||
4647 | (((uint8_t *)ha->md_tmplt_hdr) + tmplt_hdr->first_entry_offset); | ||
4648 | |||
4649 | /* Walk through the entry headers */ | ||
4650 | for (i = 0; i < no_entry_hdr; i++) { | ||
4651 | |||
4652 | if (data_collected > total_data_size) { | ||
4653 | ql_log(ql_log_warn, vha, 0xb03e, | ||
4654 | "More MiniDump data collected: [0x%x]\n", | ||
4655 | data_collected); | ||
4656 | goto md_failed; | ||
4657 | } | ||
4658 | |||
4659 | if (!(entry_hdr->d_ctrl.entry_capture_mask & | ||
4660 | ql2xmdcapmask)) { | ||
4661 | entry_hdr->d_ctrl.driver_flags |= | ||
4662 | QLA82XX_DBG_SKIPPED_FLAG; | ||
4663 | ql_dbg(ql_dbg_p3p, vha, 0xb03f, | ||
4664 | "Skipping entry[%d]: " | ||
4665 | "ETYPE[0x%x]-ELEVEL[0x%x]\n", | ||
4666 | i, entry_hdr->entry_type, | ||
4667 | entry_hdr->d_ctrl.entry_capture_mask); | ||
4668 | goto skip_nxt_entry; | ||
4669 | } | ||
4670 | |||
4671 | ql_dbg(ql_dbg_p3p, vha, 0xb040, | ||
4672 | "[%s]: data ptr[%d]: %p, entry_hdr: %p\n" | ||
4673 | "entry_type: 0x%x, captrue_mask: 0x%x\n", | ||
4674 | __func__, i, data_ptr, entry_hdr, | ||
4675 | entry_hdr->entry_type, | ||
4676 | entry_hdr->d_ctrl.entry_capture_mask); | ||
4677 | |||
4678 | ql_dbg(ql_dbg_p3p, vha, 0xb041, | ||
4679 | "Data collected: [0x%x], Dump size left:[0x%x]\n", | ||
4680 | data_collected, (ha->md_dump_size - data_collected)); | ||
4681 | |||
4682 | /* Decode the entry type and take | ||
4683 | * required action to capture debug data */ | ||
4684 | switch (entry_hdr->entry_type) { | ||
4685 | case QLA82XX_RDEND: | ||
4686 | qla82xx_mark_entry_skipped(vha, entry_hdr, i); | ||
4687 | break; | ||
4688 | case QLA82XX_CNTRL: | ||
4689 | rval = qla82xx_minidump_process_control(vha, | ||
4690 | entry_hdr, &data_ptr); | ||
4691 | if (rval != QLA_SUCCESS) { | ||
4692 | qla82xx_mark_entry_skipped(vha, entry_hdr, i); | ||
4693 | goto md_failed; | ||
4694 | } | ||
4695 | break; | ||
4696 | case QLA82XX_RDCRB: | ||
4697 | qla82xx_minidump_process_rdcrb(vha, | ||
4698 | entry_hdr, &data_ptr); | ||
4699 | break; | ||
4700 | case QLA82XX_RDMEM: | ||
4701 | rval = qla82xx_minidump_process_rdmem(vha, | ||
4702 | entry_hdr, &data_ptr); | ||
4703 | if (rval != QLA_SUCCESS) { | ||
4704 | qla82xx_mark_entry_skipped(vha, entry_hdr, i); | ||
4705 | goto md_failed; | ||
4706 | } | ||
4707 | break; | ||
4708 | case QLA82XX_BOARD: | ||
4709 | case QLA82XX_RDROM: | ||
4710 | qla82xx_minidump_process_rdrom(vha, | ||
4711 | entry_hdr, &data_ptr); | ||
4712 | break; | ||
4713 | case QLA82XX_L2DTG: | ||
4714 | case QLA82XX_L2ITG: | ||
4715 | case QLA82XX_L2DAT: | ||
4716 | case QLA82XX_L2INS: | ||
4717 | rval = qla82xx_minidump_process_l2tag(vha, | ||
4718 | entry_hdr, &data_ptr); | ||
4719 | if (rval != QLA_SUCCESS) { | ||
4720 | qla82xx_mark_entry_skipped(vha, entry_hdr, i); | ||
4721 | goto md_failed; | ||
4722 | } | ||
4723 | break; | ||
4724 | case QLA82XX_L1DAT: | ||
4725 | case QLA82XX_L1INS: | ||
4726 | qla82xx_minidump_process_l1cache(vha, | ||
4727 | entry_hdr, &data_ptr); | ||
4728 | break; | ||
4729 | case QLA82XX_RDOCM: | ||
4730 | qla82xx_minidump_process_rdocm(vha, | ||
4731 | entry_hdr, &data_ptr); | ||
4732 | break; | ||
4733 | case QLA82XX_RDMUX: | ||
4734 | qla82xx_minidump_process_rdmux(vha, | ||
4735 | entry_hdr, &data_ptr); | ||
4736 | break; | ||
4737 | case QLA82XX_QUEUE: | ||
4738 | qla82xx_minidump_process_queue(vha, | ||
4739 | entry_hdr, &data_ptr); | ||
4740 | break; | ||
4741 | case QLA82XX_RDNOP: | ||
4742 | default: | ||
4743 | qla82xx_mark_entry_skipped(vha, entry_hdr, i); | ||
4744 | break; | ||
4745 | } | ||
4746 | |||
4747 | ql_dbg(ql_dbg_p3p, vha, 0xb042, | ||
4748 | "[%s]: data ptr[%d]: %p\n", __func__, i, data_ptr); | ||
4749 | |||
4750 | data_collected = (uint8_t *)data_ptr - | ||
4751 | (uint8_t *)ha->md_dump; | ||
4752 | skip_nxt_entry: | ||
4753 | entry_hdr = (qla82xx_md_entry_hdr_t *) \ | ||
4754 | (((uint8_t *)entry_hdr) + entry_hdr->entry_size); | ||
4755 | } | ||
4756 | |||
4757 | if (data_collected != total_data_size) { | ||
4758 | ql_dbg(ql_log_warn, vha, 0xb043, | ||
4759 | "MiniDump data mismatch: Data collected: [0x%x]," | ||
4760 | "total_data_size:[0x%x]\n", | ||
4761 | data_collected, total_data_size); | ||
4762 | goto md_failed; | ||
4763 | } | ||
4764 | |||
4765 | ql_log(ql_log_info, vha, 0xb044, | ||
4766 | "Firmware dump saved to temp buffer (%ld/%p %ld/%p).\n", | ||
4767 | vha->host_no, ha->md_tmplt_hdr, vha->host_no, ha->md_dump); | ||
4768 | ha->fw_dumped = 1; | ||
4769 | qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP); | ||
4770 | |||
4771 | md_failed: | ||
4772 | return rval; | ||
4773 | } | ||
4774 | |||
4775 | int | ||
4776 | qla82xx_md_alloc(scsi_qla_host_t *vha) | ||
4777 | { | ||
4778 | struct qla_hw_data *ha = vha->hw; | ||
4779 | int i, k; | ||
4780 | struct qla82xx_md_template_hdr *tmplt_hdr; | ||
4781 | |||
4782 | tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr; | ||
4783 | |||
4784 | if (ql2xmdcapmask < 0x3 || ql2xmdcapmask > 0x7F) { | ||
4785 | ql2xmdcapmask = tmplt_hdr->capture_debug_level & 0xFF; | ||
4786 | ql_log(ql_log_info, vha, 0xb045, | ||
4787 | "Forcing driver capture mask to firmware default capture mask: 0x%x.\n", | ||
4788 | ql2xmdcapmask); | ||
4789 | } | ||
4790 | |||
4791 | for (i = 0x2, k = 1; (i & QLA82XX_DEFAULT_CAP_MASK); i <<= 1, k++) { | ||
4792 | if (i & ql2xmdcapmask) | ||
4793 | ha->md_dump_size += tmplt_hdr->capture_size_array[k]; | ||
4794 | } | ||
4795 | |||
4796 | if (ha->md_dump) { | ||
4797 | ql_log(ql_log_warn, vha, 0xb046, | ||
4798 | "Firmware dump previously allocated.\n"); | ||
4799 | return 1; | ||
4800 | } | ||
4801 | |||
4802 | ha->md_dump = vmalloc(ha->md_dump_size); | ||
4803 | if (ha->md_dump == NULL) { | ||
4804 | ql_log(ql_log_warn, vha, 0xb047, | ||
4805 | "Unable to allocate memory for Minidump size " | ||
4806 | "(0x%x).\n", ha->md_dump_size); | ||
4807 | return 1; | ||
4808 | } | ||
4809 | return 0; | ||
4810 | } | ||
4811 | |||
4812 | void | ||
4813 | qla82xx_md_free(scsi_qla_host_t *vha) | ||
4814 | { | ||
4815 | struct qla_hw_data *ha = vha->hw; | ||
4816 | |||
4817 | /* Release the template header allocated */ | ||
4818 | if (ha->md_tmplt_hdr) { | ||
4819 | ql_log(ql_log_info, vha, 0xb048, | ||
4820 | "Free MiniDump template: %p, size (%d KB)\n", | ||
4821 | ha->md_tmplt_hdr, ha->md_template_size / 1024); | ||
4822 | dma_free_coherent(&ha->pdev->dev, ha->md_template_size, | ||
4823 | ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma); | ||
4824 | ha->md_tmplt_hdr = 0; | ||
4825 | } | ||
4826 | |||
4827 | /* Release the template data buffer allocated */ | ||
4828 | if (ha->md_dump) { | ||
4829 | ql_log(ql_log_info, vha, 0xb049, | ||
4830 | "Free MiniDump memory: %p, size (%d KB)\n", | ||
4831 | ha->md_dump, ha->md_dump_size / 1024); | ||
4832 | vfree(ha->md_dump); | ||
4833 | ha->md_dump_size = 0; | ||
4834 | ha->md_dump = 0; | ||
4835 | } | ||
4836 | } | ||
4837 | |||
4838 | void | ||
4839 | qla82xx_md_prep(scsi_qla_host_t *vha) | ||
4840 | { | ||
4841 | struct qla_hw_data *ha = vha->hw; | ||
4842 | int rval; | ||
4843 | |||
4844 | /* Get Minidump template size */ | ||
4845 | rval = qla82xx_md_get_template_size(vha); | ||
4846 | if (rval == QLA_SUCCESS) { | ||
4847 | ql_log(ql_log_info, vha, 0xb04a, | ||
4848 | "MiniDump Template size obtained (%d KB)\n", | ||
4849 | ha->md_template_size / 1024); | ||
4850 | |||
4851 | /* Get Minidump template */ | ||
4852 | rval = qla82xx_md_get_template(vha); | ||
4853 | if (rval == QLA_SUCCESS) { | ||
4854 | ql_dbg(ql_dbg_p3p, vha, 0xb04b, | ||
4855 | "MiniDump Template obtained\n"); | ||
4856 | |||
4857 | /* Allocate memory for minidump */ | ||
4858 | rval = qla82xx_md_alloc(vha); | ||
4859 | if (rval == QLA_SUCCESS) | ||
4860 | ql_log(ql_log_info, vha, 0xb04c, | ||
4861 | "MiniDump memory allocated (%d KB)\n", | ||
4862 | ha->md_dump_size / 1024); | ||
4863 | else { | ||
4864 | ql_log(ql_log_info, vha, 0xb04d, | ||
4865 | "Free MiniDump template: %p, size: (%d KB)\n", | ||
4866 | ha->md_tmplt_hdr, | ||
4867 | ha->md_template_size / 1024); | ||
4868 | dma_free_coherent(&ha->pdev->dev, | ||
4869 | ha->md_template_size, | ||
4870 | ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma); | ||
4871 | ha->md_tmplt_hdr = 0; | ||
4872 | } | ||
4873 | |||
4874 | } | ||
4875 | } | ||
4876 | } | ||