diff options
Diffstat (limited to 'drivers/scsi/qla2xxx/qla_sup.c')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_sup.c | 315 |
1 files changed, 251 insertions, 64 deletions
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 26822c8807ee..1728ab3ccb20 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * QLogic Fibre Channel HBA Driver | 2 | * QLogic Fibre Channel HBA Driver |
3 | * Copyright (c) 2003-2005 QLogic Corporation | 3 | * Copyright (c) 2003-2008 QLogic Corporation |
4 | * | 4 | * |
5 | * See LICENSE.qla2xxx for copyright and licensing details. | 5 | * See LICENSE.qla2xxx for copyright and licensing details. |
6 | */ | 6 | */ |
@@ -543,79 +543,174 @@ qla24xx_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, | |||
543 | } | 543 | } |
544 | } | 544 | } |
545 | 545 | ||
546 | static int | 546 | void |
547 | qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, | 547 | qla2xxx_get_flash_info(scsi_qla_host_t *ha) |
548 | uint32_t dwords) | ||
549 | { | 548 | { |
550 | int ret; | 549 | #define FLASH_BLK_SIZE_32K 0x8000 |
551 | uint32_t liter, miter; | 550 | #define FLASH_BLK_SIZE_64K 0x10000 |
552 | uint32_t sec_mask, rest_addr, conf_addr; | 551 | uint16_t cnt, chksum; |
553 | uint32_t fdata, findex, cnt; | 552 | uint16_t *wptr; |
553 | struct qla_fdt_layout *fdt; | ||
554 | uint8_t man_id, flash_id; | 554 | uint8_t man_id, flash_id; |
555 | struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; | ||
556 | dma_addr_t optrom_dma; | ||
557 | void *optrom = NULL; | ||
558 | uint32_t *s, *d; | ||
559 | 555 | ||
560 | ret = QLA_SUCCESS; | 556 | if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) |
557 | return; | ||
561 | 558 | ||
562 | /* Prepare burst-capable write on supported ISPs. */ | 559 | wptr = (uint16_t *)ha->request_ring; |
563 | if (IS_QLA25XX(ha) && !(faddr & 0xfff) && | 560 | fdt = (struct qla_fdt_layout *)ha->request_ring; |
564 | dwords > OPTROM_BURST_DWORDS) { | 561 | ha->isp_ops->read_optrom(ha, (uint8_t *)ha->request_ring, |
565 | optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, | 562 | FA_FLASH_DESCR_ADDR << 2, OPTROM_BURST_SIZE); |
566 | &optrom_dma, GFP_KERNEL); | 563 | if (*wptr == __constant_cpu_to_le16(0xffff)) |
567 | if (!optrom) { | 564 | goto no_flash_data; |
568 | qla_printk(KERN_DEBUG, ha, | 565 | if (fdt->sig[0] != 'Q' || fdt->sig[1] != 'L' || fdt->sig[2] != 'I' || |
569 | "Unable to allocate memory for optrom burst write " | 566 | fdt->sig[3] != 'D') |
570 | "(%x KB).\n", OPTROM_BURST_SIZE / 1024); | 567 | goto no_flash_data; |
571 | } | 568 | |
569 | for (cnt = 0, chksum = 0; cnt < sizeof(struct qla_fdt_layout) >> 1; | ||
570 | cnt++) | ||
571 | chksum += le16_to_cpu(*wptr++); | ||
572 | if (chksum) { | ||
573 | DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FDT detected: " | ||
574 | "checksum=0x%x id=%c version=0x%x.\n", chksum, fdt->sig[0], | ||
575 | le16_to_cpu(fdt->version))); | ||
576 | DEBUG9(qla2x00_dump_buffer((uint8_t *)fdt, sizeof(*fdt))); | ||
577 | goto no_flash_data; | ||
572 | } | 578 | } |
573 | 579 | ||
574 | qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); | 580 | ha->fdt_odd_index = le16_to_cpu(fdt->man_id) == 0x1f; |
575 | DEBUG9(printk("%s(%ld): Flash man_id=%d flash_id=%d\n", __func__, | 581 | ha->fdt_wrt_disable = fdt->wrt_disable_bits; |
576 | ha->host_no, man_id, flash_id)); | 582 | ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0300 | fdt->erase_cmd); |
583 | ha->fdt_block_size = le32_to_cpu(fdt->block_size); | ||
584 | if (fdt->unprotect_sec_cmd) { | ||
585 | ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0300 | | ||
586 | fdt->unprotect_sec_cmd); | ||
587 | ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ? | ||
588 | flash_conf_to_access_addr(0x0300 | fdt->protect_sec_cmd): | ||
589 | flash_conf_to_access_addr(0x0336); | ||
590 | } | ||
577 | 591 | ||
578 | conf_addr = flash_conf_to_access_addr(0x03d8); | 592 | DEBUG2(qla_printk(KERN_DEBUG, ha, "Flash[FDT]: (0x%x/0x%x) erase=0x%x " |
593 | "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", | ||
594 | le16_to_cpu(fdt->man_id), le16_to_cpu(fdt->id), ha->fdt_erase_cmd, | ||
595 | ha->fdt_protect_sec_cmd, ha->fdt_unprotect_sec_cmd, | ||
596 | ha->fdt_odd_index, ha->fdt_wrt_disable, ha->fdt_block_size)); | ||
597 | return; | ||
598 | |||
599 | no_flash_data: | ||
600 | qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id); | ||
601 | ha->fdt_wrt_disable = 0x9c; | ||
602 | ha->fdt_erase_cmd = flash_conf_to_access_addr(0x03d8); | ||
579 | switch (man_id) { | 603 | switch (man_id) { |
580 | case 0xbf: /* STT flash. */ | 604 | case 0xbf: /* STT flash. */ |
581 | if (flash_id == 0x8e) { | 605 | if (flash_id == 0x8e) |
582 | rest_addr = 0x3fff; | 606 | ha->fdt_block_size = FLASH_BLK_SIZE_64K; |
583 | sec_mask = 0x7c000; | 607 | else |
584 | } else { | 608 | ha->fdt_block_size = FLASH_BLK_SIZE_32K; |
585 | rest_addr = 0x1fff; | 609 | |
586 | sec_mask = 0x7e000; | ||
587 | } | ||
588 | if (flash_id == 0x80) | 610 | if (flash_id == 0x80) |
589 | conf_addr = flash_conf_to_access_addr(0x0352); | 611 | ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0352); |
590 | break; | 612 | break; |
591 | case 0x13: /* ST M25P80. */ | 613 | case 0x13: /* ST M25P80. */ |
592 | rest_addr = 0x3fff; | 614 | ha->fdt_block_size = FLASH_BLK_SIZE_64K; |
593 | sec_mask = 0x7c000; | ||
594 | break; | 615 | break; |
595 | case 0x1f: // Atmel 26DF081A | 616 | case 0x1f: /* Atmel 26DF081A. */ |
596 | rest_addr = 0x3fff; | 617 | ha->fdt_odd_index = 1; |
597 | sec_mask = 0x7c000; | 618 | ha->fdt_block_size = FLASH_BLK_SIZE_64K; |
598 | conf_addr = flash_conf_to_access_addr(0x0320); | 619 | ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0320); |
620 | ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0339); | ||
621 | ha->fdt_protect_sec_cmd = flash_conf_to_access_addr(0x0336); | ||
599 | break; | 622 | break; |
600 | default: | 623 | default: |
601 | /* Default to 64 kb sector size. */ | 624 | /* Default to 64 kb sector size. */ |
602 | rest_addr = 0x3fff; | 625 | ha->fdt_block_size = FLASH_BLK_SIZE_64K; |
603 | sec_mask = 0x7c000; | ||
604 | break; | 626 | break; |
605 | } | 627 | } |
606 | 628 | ||
629 | DEBUG2(qla_printk(KERN_DEBUG, ha, "Flash[MID]: (0x%x/0x%x) erase=0x%x " | ||
630 | "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", man_id, flash_id, | ||
631 | ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd, | ||
632 | ha->fdt_unprotect_sec_cmd, ha->fdt_odd_index, ha->fdt_wrt_disable, | ||
633 | ha->fdt_block_size)); | ||
634 | } | ||
635 | |||
636 | static void | ||
637 | qla24xx_unprotect_flash(scsi_qla_host_t *ha) | ||
638 | { | ||
639 | struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; | ||
640 | |||
607 | /* Enable flash write. */ | 641 | /* Enable flash write. */ |
608 | WRT_REG_DWORD(®->ctrl_status, | 642 | WRT_REG_DWORD(®->ctrl_status, |
609 | RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); | 643 | RD_REG_DWORD(®->ctrl_status) | CSRX_FLASH_ENABLE); |
610 | RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ | 644 | RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ |
611 | 645 | ||
646 | if (!ha->fdt_wrt_disable) | ||
647 | return; | ||
648 | |||
612 | /* Disable flash write-protection. */ | 649 | /* Disable flash write-protection. */ |
613 | qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); | 650 | qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); |
614 | /* Some flash parts need an additional zero-write to clear bits.*/ | 651 | /* Some flash parts need an additional zero-write to clear bits.*/ |
615 | qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); | 652 | qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0); |
653 | } | ||
654 | |||
655 | static void | ||
656 | qla24xx_protect_flash(scsi_qla_host_t *ha) | ||
657 | { | ||
658 | uint32_t cnt; | ||
659 | struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; | ||
660 | |||
661 | if (!ha->fdt_wrt_disable) | ||
662 | goto skip_wrt_protect; | ||
663 | |||
664 | /* Enable flash write-protection and wait for completion. */ | ||
665 | qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), | ||
666 | ha->fdt_wrt_disable); | ||
667 | for (cnt = 300; cnt && | ||
668 | qla24xx_read_flash_dword(ha, | ||
669 | flash_conf_to_access_addr(0x005)) & BIT_0; | ||
670 | cnt--) { | ||
671 | udelay(10); | ||
672 | } | ||
673 | |||
674 | skip_wrt_protect: | ||
675 | /* Disable flash write. */ | ||
676 | WRT_REG_DWORD(®->ctrl_status, | ||
677 | RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); | ||
678 | RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ | ||
679 | } | ||
680 | |||
681 | static int | ||
682 | qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, | ||
683 | uint32_t dwords) | ||
684 | { | ||
685 | int ret; | ||
686 | uint32_t liter, miter; | ||
687 | uint32_t sec_mask, rest_addr; | ||
688 | uint32_t fdata, findex; | ||
689 | dma_addr_t optrom_dma; | ||
690 | void *optrom = NULL; | ||
691 | uint32_t *s, *d; | ||
692 | |||
693 | ret = QLA_SUCCESS; | ||
694 | |||
695 | /* Prepare burst-capable write on supported ISPs. */ | ||
696 | if (IS_QLA25XX(ha) && !(faddr & 0xfff) && | ||
697 | dwords > OPTROM_BURST_DWORDS) { | ||
698 | optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, | ||
699 | &optrom_dma, GFP_KERNEL); | ||
700 | if (!optrom) { | ||
701 | qla_printk(KERN_DEBUG, ha, | ||
702 | "Unable to allocate memory for optrom burst write " | ||
703 | "(%x KB).\n", OPTROM_BURST_SIZE / 1024); | ||
704 | } | ||
705 | } | ||
706 | |||
707 | rest_addr = (ha->fdt_block_size >> 2) - 1; | ||
708 | sec_mask = 0x80000 - (ha->fdt_block_size >> 2); | ||
709 | |||
710 | qla24xx_unprotect_flash(ha); | ||
616 | 711 | ||
617 | for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { | 712 | for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { |
618 | if (man_id == 0x1f) { | 713 | if (ha->fdt_odd_index) { |
619 | findex = faddr << 2; | 714 | findex = faddr << 2; |
620 | fdata = findex & sec_mask; | 715 | fdata = findex & sec_mask; |
621 | } else { | 716 | } else { |
@@ -625,13 +720,13 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, | |||
625 | 720 | ||
626 | /* Are we at the beginning of a sector? */ | 721 | /* Are we at the beginning of a sector? */ |
627 | if ((findex & rest_addr) == 0) { | 722 | if ((findex & rest_addr) == 0) { |
628 | /* Do sector unprotect at 4K boundry for Atmel part. */ | 723 | /* Do sector unprotect. */ |
629 | if (man_id == 0x1f) | 724 | if (ha->fdt_unprotect_sec_cmd) |
630 | qla24xx_write_flash_dword(ha, | 725 | qla24xx_write_flash_dword(ha, |
631 | flash_conf_to_access_addr(0x0339), | 726 | ha->fdt_unprotect_sec_cmd, |
632 | (fdata & 0xff00) | ((fdata << 16) & | 727 | (fdata & 0xff00) | ((fdata << 16) & |
633 | 0xff0000) | ((fdata >> 16) & 0xff)); | 728 | 0xff0000) | ((fdata >> 16) & 0xff)); |
634 | ret = qla24xx_write_flash_dword(ha, conf_addr, | 729 | ret = qla24xx_write_flash_dword(ha, ha->fdt_erase_cmd, |
635 | (fdata & 0xff00) |((fdata << 16) & | 730 | (fdata & 0xff00) |((fdata << 16) & |
636 | 0xff0000) | ((fdata >> 16) & 0xff)); | 731 | 0xff0000) | ((fdata >> 16) & 0xff)); |
637 | if (ret != QLA_SUCCESS) { | 732 | if (ret != QLA_SUCCESS) { |
@@ -681,28 +776,16 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, | |||
681 | break; | 776 | break; |
682 | } | 777 | } |
683 | 778 | ||
684 | /* Do sector protect at 4K boundry for Atmel part. */ | 779 | /* Do sector protect. */ |
685 | if (man_id == 0x1f && | 780 | if (ha->fdt_unprotect_sec_cmd && |
686 | ((faddr & rest_addr) == rest_addr)) | 781 | ((faddr & rest_addr) == rest_addr)) |
687 | qla24xx_write_flash_dword(ha, | 782 | qla24xx_write_flash_dword(ha, |
688 | flash_conf_to_access_addr(0x0336), | 783 | ha->fdt_protect_sec_cmd, |
689 | (fdata & 0xff00) | ((fdata << 16) & | 784 | (fdata & 0xff00) | ((fdata << 16) & |
690 | 0xff0000) | ((fdata >> 16) & 0xff)); | 785 | 0xff0000) | ((fdata >> 16) & 0xff)); |
691 | } | 786 | } |
692 | 787 | ||
693 | /* Enable flash write-protection and wait for completion. */ | 788 | qla24xx_protect_flash(ha); |
694 | qla24xx_write_flash_dword(ha, flash_conf_to_access_addr(0x101), 0x9c); | ||
695 | for (cnt = 300; cnt && | ||
696 | qla24xx_read_flash_dword(ha, | ||
697 | flash_conf_to_access_addr(0x005)) & BIT_0; | ||
698 | cnt--) { | ||
699 | udelay(10); | ||
700 | } | ||
701 | |||
702 | /* Disable flash write. */ | ||
703 | WRT_REG_DWORD(®->ctrl_status, | ||
704 | RD_REG_DWORD(®->ctrl_status) & ~CSRX_FLASH_ENABLE); | ||
705 | RD_REG_DWORD(®->ctrl_status); /* PCI Posting. */ | ||
706 | 789 | ||
707 | if (optrom) | 790 | if (optrom) |
708 | dma_free_coherent(&ha->pdev->dev, | 791 | dma_free_coherent(&ha->pdev->dev, |
@@ -2221,3 +2304,107 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) | |||
2221 | 2304 | ||
2222 | return ret; | 2305 | return ret; |
2223 | } | 2306 | } |
2307 | |||
2308 | static int | ||
2309 | qla2xxx_hw_event_store(scsi_qla_host_t *ha, uint32_t *fdata) | ||
2310 | { | ||
2311 | uint32_t d[2], faddr; | ||
2312 | |||
2313 | /* Locate first empty entry. */ | ||
2314 | for (;;) { | ||
2315 | if (ha->hw_event_ptr >= | ||
2316 | ha->hw_event_start + FA_HW_EVENT_SIZE) { | ||
2317 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
2318 | "HW event -- Log Full!\n")); | ||
2319 | return QLA_MEMORY_ALLOC_FAILED; | ||
2320 | } | ||
2321 | |||
2322 | qla24xx_read_flash_data(ha, d, ha->hw_event_ptr, 2); | ||
2323 | faddr = flash_data_to_access_addr(ha->hw_event_ptr); | ||
2324 | ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; | ||
2325 | if (d[0] == __constant_cpu_to_le32(0xffffffff) && | ||
2326 | d[1] == __constant_cpu_to_le32(0xffffffff)) { | ||
2327 | qla24xx_unprotect_flash(ha); | ||
2328 | |||
2329 | qla24xx_write_flash_dword(ha, faddr++, | ||
2330 | cpu_to_le32(jiffies)); | ||
2331 | qla24xx_write_flash_dword(ha, faddr++, 0); | ||
2332 | qla24xx_write_flash_dword(ha, faddr++, *fdata++); | ||
2333 | qla24xx_write_flash_dword(ha, faddr++, *fdata); | ||
2334 | |||
2335 | qla24xx_protect_flash(ha); | ||
2336 | break; | ||
2337 | } | ||
2338 | } | ||
2339 | return QLA_SUCCESS; | ||
2340 | } | ||
2341 | |||
2342 | int | ||
2343 | qla2xxx_hw_event_log(scsi_qla_host_t *ha, uint16_t code, uint16_t d1, | ||
2344 | uint16_t d2, uint16_t d3) | ||
2345 | { | ||
2346 | #define QMARK(a, b, c, d) \ | ||
2347 | cpu_to_le32(LSB(a) << 24 | LSB(b) << 16 | LSB(c) << 8 | LSB(d)) | ||
2348 | |||
2349 | int rval; | ||
2350 | uint32_t marker[2], fdata[4]; | ||
2351 | |||
2352 | if (ha->hw_event_start == 0) | ||
2353 | return QLA_FUNCTION_FAILED; | ||
2354 | |||
2355 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
2356 | "HW event -- code=%x, d1=%x, d2=%x, d3=%x.\n", code, d1, d2, d3)); | ||
2357 | |||
2358 | /* If marker not already found, locate or write. */ | ||
2359 | if (!ha->flags.hw_event_marker_found) { | ||
2360 | /* Create marker. */ | ||
2361 | marker[0] = QMARK('L', ha->fw_major_version, | ||
2362 | ha->fw_minor_version, ha->fw_subminor_version); | ||
2363 | marker[1] = QMARK(QLA_DRIVER_MAJOR_VER, QLA_DRIVER_MINOR_VER, | ||
2364 | QLA_DRIVER_PATCH_VER, QLA_DRIVER_BETA_VER); | ||
2365 | |||
2366 | /* Locate marker. */ | ||
2367 | ha->hw_event_ptr = ha->hw_event_start; | ||
2368 | for (;;) { | ||
2369 | qla24xx_read_flash_data(ha, fdata, ha->hw_event_ptr, | ||
2370 | 4); | ||
2371 | if (fdata[0] == __constant_cpu_to_le32(0xffffffff) && | ||
2372 | fdata[1] == __constant_cpu_to_le32(0xffffffff)) | ||
2373 | break; | ||
2374 | ha->hw_event_ptr += FA_HW_EVENT_ENTRY_SIZE; | ||
2375 | if (ha->hw_event_ptr >= | ||
2376 | ha->hw_event_start + FA_HW_EVENT_SIZE) { | ||
2377 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
2378 | "HW event -- Log Full!\n")); | ||
2379 | return QLA_MEMORY_ALLOC_FAILED; | ||
2380 | } | ||
2381 | if (fdata[2] == marker[0] && fdata[3] == marker[1]) { | ||
2382 | ha->flags.hw_event_marker_found = 1; | ||
2383 | break; | ||
2384 | } | ||
2385 | } | ||
2386 | /* No marker, write it. */ | ||
2387 | if (!ha->flags.hw_event_marker_found) { | ||
2388 | rval = qla2xxx_hw_event_store(ha, marker); | ||
2389 | if (rval != QLA_SUCCESS) { | ||
2390 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
2391 | "HW event -- Failed marker write=%x.!\n", | ||
2392 | rval)); | ||
2393 | return rval; | ||
2394 | } | ||
2395 | ha->flags.hw_event_marker_found = 1; | ||
2396 | } | ||
2397 | } | ||
2398 | |||
2399 | /* Store error. */ | ||
2400 | fdata[0] = cpu_to_le32(code << 16 | d1); | ||
2401 | fdata[1] = cpu_to_le32(d2 << 16 | d3); | ||
2402 | rval = qla2xxx_hw_event_store(ha, fdata); | ||
2403 | if (rval != QLA_SUCCESS) { | ||
2404 | DEBUG2(qla_printk(KERN_WARNING, ha, | ||
2405 | "HW event -- Failed error write=%x.!\n", | ||
2406 | rval)); | ||
2407 | } | ||
2408 | |||
2409 | return rval; | ||
2410 | } | ||