diff options
author | Andrew Vasquez <andrew.vasquez@qlogic.com> | 2007-01-29 13:22:21 -0500 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.il.steeleye.com> | 2007-01-31 12:06:38 -0500 |
commit | 30c4766213aeb684ee477ac7f36703f9134ac7ad (patch) | |
tree | 91b95d4e765aa74856e4e7a4b9ee5aefb04e01dc | |
parent | d88021a6710e6ed5d1899ba2e54d4638026e277d (diff) |
[SCSI] qla2xxx: Export OptionROM boot-codes version information.
This includes BIOS, EFI, FCODE and firmware versions.
Signed-off-by: Andrew Vasquez <andrew.vasquez@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
-rw-r--r-- | drivers/scsi/qla2xxx/qla_attr.c | 49 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 11 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 3 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_init.c | 4 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_sup.c | 347 |
6 files changed, 416 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 7b18a6c7b7eb..78a5867bf515 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c | |||
@@ -653,6 +653,43 @@ qla2x00_beacon_store(struct class_device *cdev, const char *buf, | |||
653 | return count; | 653 | return count; |
654 | } | 654 | } |
655 | 655 | ||
656 | static ssize_t | ||
657 | qla2x00_optrom_bios_version_show(struct class_device *cdev, char *buf) | ||
658 | { | ||
659 | scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); | ||
660 | |||
661 | return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1], | ||
662 | ha->bios_revision[0]); | ||
663 | } | ||
664 | |||
665 | static ssize_t | ||
666 | qla2x00_optrom_efi_version_show(struct class_device *cdev, char *buf) | ||
667 | { | ||
668 | scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); | ||
669 | |||
670 | return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1], | ||
671 | ha->efi_revision[0]); | ||
672 | } | ||
673 | |||
674 | static ssize_t | ||
675 | qla2x00_optrom_fcode_version_show(struct class_device *cdev, char *buf) | ||
676 | { | ||
677 | scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); | ||
678 | |||
679 | return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1], | ||
680 | ha->fcode_revision[0]); | ||
681 | } | ||
682 | |||
683 | static ssize_t | ||
684 | qla2x00_optrom_fw_version_show(struct class_device *cdev, char *buf) | ||
685 | { | ||
686 | scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); | ||
687 | |||
688 | return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n", | ||
689 | ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2], | ||
690 | ha->fw_revision[3]); | ||
691 | } | ||
692 | |||
656 | static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, | 693 | static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, |
657 | NULL); | 694 | NULL); |
658 | static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); | 695 | static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); |
@@ -669,6 +706,14 @@ static CLASS_DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show, | |||
669 | qla2x00_zio_timer_store); | 706 | qla2x00_zio_timer_store); |
670 | static CLASS_DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show, | 707 | static CLASS_DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show, |
671 | qla2x00_beacon_store); | 708 | qla2x00_beacon_store); |
709 | static CLASS_DEVICE_ATTR(optrom_bios_version, S_IRUGO, | ||
710 | qla2x00_optrom_bios_version_show, NULL); | ||
711 | static CLASS_DEVICE_ATTR(optrom_efi_version, S_IRUGO, | ||
712 | qla2x00_optrom_efi_version_show, NULL); | ||
713 | static CLASS_DEVICE_ATTR(optrom_fcode_version, S_IRUGO, | ||
714 | qla2x00_optrom_fcode_version_show, NULL); | ||
715 | static CLASS_DEVICE_ATTR(optrom_fw_version, S_IRUGO, | ||
716 | qla2x00_optrom_fw_version_show, NULL); | ||
672 | 717 | ||
673 | struct class_device_attribute *qla2x00_host_attrs[] = { | 718 | struct class_device_attribute *qla2x00_host_attrs[] = { |
674 | &class_device_attr_driver_version, | 719 | &class_device_attr_driver_version, |
@@ -683,6 +728,10 @@ struct class_device_attribute *qla2x00_host_attrs[] = { | |||
683 | &class_device_attr_zio, | 728 | &class_device_attr_zio, |
684 | &class_device_attr_zio_timer, | 729 | &class_device_attr_zio_timer, |
685 | &class_device_attr_beacon, | 730 | &class_device_attr_beacon, |
731 | &class_device_attr_optrom_bios_version, | ||
732 | &class_device_attr_optrom_efi_version, | ||
733 | &class_device_attr_optrom_fcode_version, | ||
734 | &class_device_attr_optrom_fw_version, | ||
686 | NULL, | 735 | NULL, |
687 | }; | 736 | }; |
688 | 737 | ||
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 3b26f8e00ace..2a59faa632df 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h | |||
@@ -2044,6 +2044,8 @@ struct isp_operations { | |||
2044 | uint32_t, uint32_t); | 2044 | uint32_t, uint32_t); |
2045 | int (*write_optrom) (struct scsi_qla_host *, uint8_t *, uint32_t, | 2045 | int (*write_optrom) (struct scsi_qla_host *, uint8_t *, uint32_t, |
2046 | uint32_t); | 2046 | uint32_t); |
2047 | |||
2048 | int (*get_flash_version) (struct scsi_qla_host *, void *); | ||
2047 | }; | 2049 | }; |
2048 | 2050 | ||
2049 | /* MSI-X Support *************************************************************/ | 2051 | /* MSI-X Support *************************************************************/ |
@@ -2400,6 +2402,15 @@ typedef struct scsi_qla_host { | |||
2400 | #define QLA_SREADING 1 | 2402 | #define QLA_SREADING 1 |
2401 | #define QLA_SWRITING 2 | 2403 | #define QLA_SWRITING 2 |
2402 | 2404 | ||
2405 | /* PCI expansion ROM image information. */ | ||
2406 | #define ROM_CODE_TYPE_BIOS 0 | ||
2407 | #define ROM_CODE_TYPE_FCODE 1 | ||
2408 | #define ROM_CODE_TYPE_EFI 3 | ||
2409 | uint8_t bios_revision[2]; | ||
2410 | uint8_t efi_revision[2]; | ||
2411 | uint8_t fcode_revision[16]; | ||
2412 | uint32_t fw_revision[4]; | ||
2413 | |||
2403 | /* Needed for BEACON */ | 2414 | /* Needed for BEACON */ |
2404 | uint16_t beacon_blink_led; | 2415 | uint16_t beacon_blink_led; |
2405 | uint8_t beacon_color_state; | 2416 | uint8_t beacon_color_state; |
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index e6ca2bc9a28d..f8bddec4fe85 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h | |||
@@ -263,6 +263,9 @@ extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, | |||
263 | extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, | 263 | extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, |
264 | uint32_t, uint32_t); | 264 | uint32_t, uint32_t); |
265 | 265 | ||
266 | extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *); | ||
267 | extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); | ||
268 | |||
266 | /* | 269 | /* |
267 | * Global Function Prototypes in qla_dbg.c source file. | 270 | * Global Function Prototypes in qla_dbg.c source file. |
268 | */ | 271 | */ |
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index b6f0494e034c..570f5f8e6c28 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c | |||
@@ -84,6 +84,8 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) | |||
84 | 84 | ||
85 | ha->isp_ops.reset_chip(ha); | 85 | ha->isp_ops.reset_chip(ha); |
86 | 86 | ||
87 | ha->isp_ops.get_flash_version(ha, ha->request_ring); | ||
88 | |||
87 | qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); | 89 | qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); |
88 | 90 | ||
89 | ha->isp_ops.nvram_config(ha); | 91 | ha->isp_ops.nvram_config(ha); |
@@ -3109,6 +3111,8 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) | |||
3109 | } | 3111 | } |
3110 | spin_unlock_irqrestore(&ha->hardware_lock, flags); | 3112 | spin_unlock_irqrestore(&ha->hardware_lock, flags); |
3111 | 3113 | ||
3114 | ha->isp_ops.get_flash_version(ha, ha->request_ring); | ||
3115 | |||
3112 | ha->isp_ops.nvram_config(ha); | 3116 | ha->isp_ops.nvram_config(ha); |
3113 | 3117 | ||
3114 | if (!qla2x00_restart_isp(ha)) { | 3118 | if (!qla2x00_restart_isp(ha)) { |
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 2bd70bb82aab..d35ff4a2a764 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c | |||
@@ -1482,6 +1482,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1482 | ha->isp_ops.fw_dump = qla2100_fw_dump; | 1482 | ha->isp_ops.fw_dump = qla2100_fw_dump; |
1483 | ha->isp_ops.read_optrom = qla2x00_read_optrom_data; | 1483 | ha->isp_ops.read_optrom = qla2x00_read_optrom_data; |
1484 | ha->isp_ops.write_optrom = qla2x00_write_optrom_data; | 1484 | ha->isp_ops.write_optrom = qla2x00_write_optrom_data; |
1485 | ha->isp_ops.get_flash_version = qla2x00_get_flash_version; | ||
1485 | if (IS_QLA2100(ha)) { | 1486 | if (IS_QLA2100(ha)) { |
1486 | host->max_id = MAX_TARGETS_2100; | 1487 | host->max_id = MAX_TARGETS_2100; |
1487 | ha->mbx_count = MAILBOX_REGISTER_COUNT_2100; | 1488 | ha->mbx_count = MAILBOX_REGISTER_COUNT_2100; |
@@ -1547,6 +1548,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) | |||
1547 | ha->isp_ops.beacon_on = qla24xx_beacon_on; | 1548 | ha->isp_ops.beacon_on = qla24xx_beacon_on; |
1548 | ha->isp_ops.beacon_off = qla24xx_beacon_off; | 1549 | ha->isp_ops.beacon_off = qla24xx_beacon_off; |
1549 | ha->isp_ops.beacon_blink = qla24xx_beacon_blink; | 1550 | ha->isp_ops.beacon_blink = qla24xx_beacon_blink; |
1551 | ha->isp_ops.get_flash_version = qla24xx_get_flash_version; | ||
1550 | ha->gid_list_info_size = 8; | 1552 | ha->gid_list_info_size = 8; |
1551 | ha->optrom_size = OPTROM_SIZE_24XX; | 1553 | ha->optrom_size = OPTROM_SIZE_24XX; |
1552 | } | 1554 | } |
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 7bbe751a7334..ff1dd4175a7f 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c | |||
@@ -1382,6 +1382,29 @@ qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, | |||
1382 | qla2x00_write_flash_byte(ha, 0x5555, 0xf0); | 1382 | qla2x00_write_flash_byte(ha, 0x5555, 0xf0); |
1383 | } | 1383 | } |
1384 | 1384 | ||
1385 | static void | ||
1386 | qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr, | ||
1387 | uint32_t length) | ||
1388 | { | ||
1389 | struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; | ||
1390 | uint32_t midpoint, ilength; | ||
1391 | uint8_t data; | ||
1392 | |||
1393 | midpoint = length / 2; | ||
1394 | |||
1395 | WRT_REG_WORD(®->nvram, 0); | ||
1396 | RD_REG_WORD(®->nvram); | ||
1397 | for (ilength = 0; ilength < length; saddr++, ilength++, tmp_buf++) { | ||
1398 | if (ilength == midpoint) { | ||
1399 | WRT_REG_WORD(®->nvram, NVR_SELECT); | ||
1400 | RD_REG_WORD(®->nvram); | ||
1401 | } | ||
1402 | data = qla2x00_read_flash_byte(ha, saddr); | ||
1403 | if (saddr % 100) | ||
1404 | udelay(10); | ||
1405 | *tmp_buf = data; | ||
1406 | } | ||
1407 | } | ||
1385 | 1408 | ||
1386 | static inline void | 1409 | static inline void |
1387 | qla2x00_suspend_hba(struct scsi_qla_host *ha) | 1410 | qla2x00_suspend_hba(struct scsi_qla_host *ha) |
@@ -1721,3 +1744,327 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, | |||
1721 | 1744 | ||
1722 | return rval; | 1745 | return rval; |
1723 | } | 1746 | } |
1747 | |||
1748 | /** | ||
1749 | * qla2x00_get_fcode_version() - Determine an FCODE image's version. | ||
1750 | * @ha: HA context | ||
1751 | * @pcids: Pointer to the FCODE PCI data structure | ||
1752 | * | ||
1753 | * The process of retrieving the FCODE version information is at best | ||
1754 | * described as interesting. | ||
1755 | * | ||
1756 | * Within the first 100h bytes of the image an ASCII string is present | ||
1757 | * which contains several pieces of information including the FCODE | ||
1758 | * version. Unfortunately it seems the only reliable way to retrieve | ||
1759 | * the version is by scanning for another sentinel within the string, | ||
1760 | * the FCODE build date: | ||
1761 | * | ||
1762 | * ... 2.00.02 10/17/02 ... | ||
1763 | * | ||
1764 | * Returns QLA_SUCCESS on successful retrieval of version. | ||
1765 | */ | ||
1766 | static void | ||
1767 | qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids) | ||
1768 | { | ||
1769 | int ret = QLA_FUNCTION_FAILED; | ||
1770 | uint32_t istart, iend, iter, vend; | ||
1771 | uint8_t do_next, rbyte, *vbyte; | ||
1772 | |||
1773 | memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); | ||
1774 | |||
1775 | /* Skip the PCI data structure. */ | ||
1776 | istart = pcids + | ||
1777 | ((qla2x00_read_flash_byte(ha, pcids + 0x0B) << 8) | | ||
1778 | qla2x00_read_flash_byte(ha, pcids + 0x0A)); | ||
1779 | iend = istart + 0x100; | ||
1780 | do { | ||
1781 | /* Scan for the sentinel date string...eeewww. */ | ||
1782 | do_next = 0; | ||
1783 | iter = istart; | ||
1784 | while ((iter < iend) && !do_next) { | ||
1785 | iter++; | ||
1786 | if (qla2x00_read_flash_byte(ha, iter) == '/') { | ||
1787 | if (qla2x00_read_flash_byte(ha, iter + 2) == | ||
1788 | '/') | ||
1789 | do_next++; | ||
1790 | else if (qla2x00_read_flash_byte(ha, | ||
1791 | iter + 3) == '/') | ||
1792 | do_next++; | ||
1793 | } | ||
1794 | } | ||
1795 | if (!do_next) | ||
1796 | break; | ||
1797 | |||
1798 | /* Backtrack to previous ' ' (space). */ | ||
1799 | do_next = 0; | ||
1800 | while ((iter > istart) && !do_next) { | ||
1801 | iter--; | ||
1802 | if (qla2x00_read_flash_byte(ha, iter) == ' ') | ||
1803 | do_next++; | ||
1804 | } | ||
1805 | if (!do_next) | ||
1806 | break; | ||
1807 | |||
1808 | /* | ||
1809 | * Mark end of version tag, and find previous ' ' (space) or | ||
1810 | * string length (recent FCODE images -- major hack ahead!!!). | ||
1811 | */ | ||
1812 | vend = iter - 1; | ||
1813 | do_next = 0; | ||
1814 | while ((iter > istart) && !do_next) { | ||
1815 | iter--; | ||
1816 | rbyte = qla2x00_read_flash_byte(ha, iter); | ||
1817 | if (rbyte == ' ' || rbyte == 0xd || rbyte == 0x10) | ||
1818 | do_next++; | ||
1819 | } | ||
1820 | if (!do_next) | ||
1821 | break; | ||
1822 | |||
1823 | /* Mark beginning of version tag, and copy data. */ | ||
1824 | iter++; | ||
1825 | if ((vend - iter) && | ||
1826 | ((vend - iter) < sizeof(ha->fcode_revision))) { | ||
1827 | vbyte = ha->fcode_revision; | ||
1828 | while (iter <= vend) { | ||
1829 | *vbyte++ = qla2x00_read_flash_byte(ha, iter); | ||
1830 | iter++; | ||
1831 | } | ||
1832 | ret = QLA_SUCCESS; | ||
1833 | } | ||
1834 | } while (0); | ||
1835 | |||
1836 | if (ret != QLA_SUCCESS) | ||
1837 | memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); | ||
1838 | } | ||
1839 | |||
1840 | int | ||
1841 | qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) | ||
1842 | { | ||
1843 | int ret = QLA_SUCCESS; | ||
1844 | uint8_t code_type, last_image; | ||
1845 | uint32_t pcihdr, pcids; | ||
1846 | uint8_t *dbyte; | ||
1847 | uint16_t *dcode; | ||
1848 | |||
1849 | if (!ha->pio_address || !mbuf) | ||
1850 | return QLA_FUNCTION_FAILED; | ||
1851 | |||
1852 | memset(ha->bios_revision, 0, sizeof(ha->bios_revision)); | ||
1853 | memset(ha->efi_revision, 0, sizeof(ha->efi_revision)); | ||
1854 | memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); | ||
1855 | memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); | ||
1856 | |||
1857 | qla2x00_flash_enable(ha); | ||
1858 | |||
1859 | /* Begin with first PCI expansion ROM header. */ | ||
1860 | pcihdr = 0; | ||
1861 | last_image = 1; | ||
1862 | do { | ||
1863 | /* Verify PCI expansion ROM header. */ | ||
1864 | if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 || | ||
1865 | qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) { | ||
1866 | /* No signature */ | ||
1867 | DEBUG2(printk("scsi(%ld): No matching ROM " | ||
1868 | "signature.\n", ha->host_no)); | ||
1869 | ret = QLA_FUNCTION_FAILED; | ||
1870 | break; | ||
1871 | } | ||
1872 | |||
1873 | /* Locate PCI data structure. */ | ||
1874 | pcids = pcihdr + | ||
1875 | ((qla2x00_read_flash_byte(ha, pcihdr + 0x19) << 8) | | ||
1876 | qla2x00_read_flash_byte(ha, pcihdr + 0x18)); | ||
1877 | |||
1878 | /* Validate signature of PCI data structure. */ | ||
1879 | if (qla2x00_read_flash_byte(ha, pcids) != 'P' || | ||
1880 | qla2x00_read_flash_byte(ha, pcids + 0x1) != 'C' || | ||
1881 | qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' || | ||
1882 | qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') { | ||
1883 | /* Incorrect header. */ | ||
1884 | DEBUG2(printk("%s(): PCI data struct not found " | ||
1885 | "pcir_adr=%x.\n", __func__, pcids)); | ||
1886 | ret = QLA_FUNCTION_FAILED; | ||
1887 | break; | ||
1888 | } | ||
1889 | |||
1890 | /* Read version */ | ||
1891 | code_type = qla2x00_read_flash_byte(ha, pcids + 0x14); | ||
1892 | switch (code_type) { | ||
1893 | case ROM_CODE_TYPE_BIOS: | ||
1894 | /* Intel x86, PC-AT compatible. */ | ||
1895 | ha->bios_revision[0] = | ||
1896 | qla2x00_read_flash_byte(ha, pcids + 0x12); | ||
1897 | ha->bios_revision[1] = | ||
1898 | qla2x00_read_flash_byte(ha, pcids + 0x13); | ||
1899 | DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__, | ||
1900 | ha->bios_revision[1], ha->bios_revision[0])); | ||
1901 | break; | ||
1902 | case ROM_CODE_TYPE_FCODE: | ||
1903 | /* Open Firmware standard for PCI (FCode). */ | ||
1904 | /* Eeeewww... */ | ||
1905 | qla2x00_get_fcode_version(ha, pcids); | ||
1906 | break; | ||
1907 | case ROM_CODE_TYPE_EFI: | ||
1908 | /* Extensible Firmware Interface (EFI). */ | ||
1909 | ha->efi_revision[0] = | ||
1910 | qla2x00_read_flash_byte(ha, pcids + 0x12); | ||
1911 | ha->efi_revision[1] = | ||
1912 | qla2x00_read_flash_byte(ha, pcids + 0x13); | ||
1913 | DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__, | ||
1914 | ha->efi_revision[1], ha->efi_revision[0])); | ||
1915 | break; | ||
1916 | default: | ||
1917 | DEBUG2(printk("%s(): Unrecognized code type %x at " | ||
1918 | "pcids %x.\n", __func__, code_type, pcids)); | ||
1919 | break; | ||
1920 | } | ||
1921 | |||
1922 | last_image = qla2x00_read_flash_byte(ha, pcids + 0x15) & BIT_7; | ||
1923 | |||
1924 | /* Locate next PCI expansion ROM. */ | ||
1925 | pcihdr += ((qla2x00_read_flash_byte(ha, pcids + 0x11) << 8) | | ||
1926 | qla2x00_read_flash_byte(ha, pcids + 0x10)) * 512; | ||
1927 | } while (!last_image); | ||
1928 | |||
1929 | if (IS_QLA2322(ha)) { | ||
1930 | /* Read firmware image information. */ | ||
1931 | memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); | ||
1932 | dbyte = mbuf; | ||
1933 | memset(dbyte, 0, 8); | ||
1934 | dcode = (uint16_t *)dbyte; | ||
1935 | |||
1936 | qla2x00_read_flash_data(ha, dbyte, FA_RISC_CODE_ADDR * 4 + 10, | ||
1937 | 8); | ||
1938 | DEBUG3(printk("%s(%ld): dumping fw ver from flash:\n", | ||
1939 | __func__, ha->host_no)); | ||
1940 | DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 8)); | ||
1941 | |||
1942 | if ((dcode[0] == 0xffff && dcode[1] == 0xffff && | ||
1943 | dcode[2] == 0xffff && dcode[3] == 0xffff) || | ||
1944 | (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && | ||
1945 | dcode[3] == 0)) { | ||
1946 | DEBUG2(printk("%s(): Unrecognized fw revision at " | ||
1947 | "%x.\n", __func__, FA_RISC_CODE_ADDR * 4)); | ||
1948 | } else { | ||
1949 | /* values are in big endian */ | ||
1950 | ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1]; | ||
1951 | ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3]; | ||
1952 | ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5]; | ||
1953 | } | ||
1954 | } | ||
1955 | |||
1956 | qla2x00_flash_disable(ha); | ||
1957 | |||
1958 | return ret; | ||
1959 | } | ||
1960 | |||
1961 | int | ||
1962 | qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) | ||
1963 | { | ||
1964 | int ret = QLA_SUCCESS; | ||
1965 | uint32_t pcihdr, pcids; | ||
1966 | uint32_t *dcode; | ||
1967 | uint8_t *bcode; | ||
1968 | uint8_t code_type, last_image; | ||
1969 | int i; | ||
1970 | |||
1971 | if (!mbuf) | ||
1972 | return QLA_FUNCTION_FAILED; | ||
1973 | |||
1974 | memset(ha->bios_revision, 0, sizeof(ha->bios_revision)); | ||
1975 | memset(ha->efi_revision, 0, sizeof(ha->efi_revision)); | ||
1976 | memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); | ||
1977 | memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); | ||
1978 | |||
1979 | dcode = mbuf; | ||
1980 | |||
1981 | /* Begin with first PCI expansion ROM header. */ | ||
1982 | pcihdr = 0; | ||
1983 | last_image = 1; | ||
1984 | do { | ||
1985 | /* Verify PCI expansion ROM header. */ | ||
1986 | qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20); | ||
1987 | bcode = mbuf + (pcihdr % 4); | ||
1988 | if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) { | ||
1989 | /* No signature */ | ||
1990 | DEBUG2(printk("scsi(%ld): No matching ROM " | ||
1991 | "signature.\n", ha->host_no)); | ||
1992 | ret = QLA_FUNCTION_FAILED; | ||
1993 | break; | ||
1994 | } | ||
1995 | |||
1996 | /* Locate PCI data structure. */ | ||
1997 | pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]); | ||
1998 | |||
1999 | qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20); | ||
2000 | bcode = mbuf + (pcihdr % 4); | ||
2001 | |||
2002 | /* Validate signature of PCI data structure. */ | ||
2003 | if (bcode[0x0] != 'P' || bcode[0x1] != 'C' || | ||
2004 | bcode[0x2] != 'I' || bcode[0x3] != 'R') { | ||
2005 | /* Incorrect header. */ | ||
2006 | DEBUG2(printk("%s(): PCI data struct not found " | ||
2007 | "pcir_adr=%x.\n", __func__, pcids)); | ||
2008 | ret = QLA_FUNCTION_FAILED; | ||
2009 | break; | ||
2010 | } | ||
2011 | |||
2012 | /* Read version */ | ||
2013 | code_type = bcode[0x14]; | ||
2014 | switch (code_type) { | ||
2015 | case ROM_CODE_TYPE_BIOS: | ||
2016 | /* Intel x86, PC-AT compatible. */ | ||
2017 | ha->bios_revision[0] = bcode[0x12]; | ||
2018 | ha->bios_revision[1] = bcode[0x13]; | ||
2019 | DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__, | ||
2020 | ha->bios_revision[1], ha->bios_revision[0])); | ||
2021 | break; | ||
2022 | case ROM_CODE_TYPE_FCODE: | ||
2023 | /* Open Firmware standard for PCI (FCode). */ | ||
2024 | ha->fcode_revision[0] = bcode[0x12]; | ||
2025 | ha->fcode_revision[1] = bcode[0x13]; | ||
2026 | DEBUG3(printk("%s(): read FCODE %d.%d.\n", __func__, | ||
2027 | ha->fcode_revision[1], ha->fcode_revision[0])); | ||
2028 | break; | ||
2029 | case ROM_CODE_TYPE_EFI: | ||
2030 | /* Extensible Firmware Interface (EFI). */ | ||
2031 | ha->efi_revision[0] = bcode[0x12]; | ||
2032 | ha->efi_revision[1] = bcode[0x13]; | ||
2033 | DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__, | ||
2034 | ha->efi_revision[1], ha->efi_revision[0])); | ||
2035 | break; | ||
2036 | default: | ||
2037 | DEBUG2(printk("%s(): Unrecognized code type %x at " | ||
2038 | "pcids %x.\n", __func__, code_type, pcids)); | ||
2039 | break; | ||
2040 | } | ||
2041 | |||
2042 | last_image = bcode[0x15] & BIT_7; | ||
2043 | |||
2044 | /* Locate next PCI expansion ROM. */ | ||
2045 | pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512; | ||
2046 | } while (!last_image); | ||
2047 | |||
2048 | /* Read firmware image information. */ | ||
2049 | memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); | ||
2050 | dcode = mbuf; | ||
2051 | |||
2052 | qla24xx_read_flash_data(ha, dcode, FA_RISC_CODE_ADDR + 4, 4); | ||
2053 | for (i = 0; i < 4; i++) | ||
2054 | dcode[i] = be32_to_cpu(dcode[i]); | ||
2055 | |||
2056 | if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff && | ||
2057 | dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || | ||
2058 | (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && | ||
2059 | dcode[3] == 0)) { | ||
2060 | DEBUG2(printk("%s(): Unrecognized fw version at %x.\n", | ||
2061 | __func__, FA_RISC_CODE_ADDR)); | ||
2062 | } else { | ||
2063 | ha->fw_revision[0] = dcode[0]; | ||
2064 | ha->fw_revision[1] = dcode[1]; | ||
2065 | ha->fw_revision[2] = dcode[2]; | ||
2066 | ha->fw_revision[3] = dcode[3]; | ||
2067 | } | ||
2068 | |||
2069 | return ret; | ||
2070 | } | ||