From 8db57a794595166fd39682ca6429e95a0ccf2152 Mon Sep 17 00:00:00 2001 From: Kolja Waschk Date: Mon, 25 Feb 2008 20:03:12 +0000 Subject: [PATCH] [ 1901129 ] Detect AMD CFI primary vendor-specific extended query table git-svn-id: https://urjtag.svn.sourceforge.net/svnroot/urjtag/trunk@1095 b68d4a1b-bc3d-0410-92ed-d4ac073336b7 --- jtag/ChangeLog | 32 ++++++++ jtag/include/flash/cfi.h | 55 ++++++++++++++ jtag/src/flash/cfi.c | 103 ++++++++++++++++++++++++++ jtag/src/flash/detectflash.c | 140 +++++++++++++++++++++++++++++++++++ 4 files changed, 330 insertions(+) diff --git a/jtag/ChangeLog b/jtag/ChangeLog index 726dc673..5f4fb0fd 100644 --- a/jtag/ChangeLog +++ b/jtag/ChangeLog @@ -3,6 +3,38 @@ * src/bus/buses.c (bus_drivers): bf548_ezkit_bus. * src/bus/buses.h: Declare bf548_ezkit_bus. + * include/flash/cfi.h (MAJOR_VERSION_OFFSET): New macro. + (MAJOR_VERSION_OFFSET): New macro. + (MINOR_VERSION_OFFSET): New macro. + (ADDRESS_SENSITIVE_UNLOCK_OFFSET): New macro. + (ERASE_SUSPEND_OFFSET): New macro. + (SECTOR_PROTECT_OFFSET): New macro. + (SECTOR_TEMPORARY_UNPROTECT_OFFSET): New macro. + (SECTOR_PROTECT_SCHEME_OFFSET): New macro. + (SIMULTANEOUS_OPERATION_OFFSET): New macro. + (BURST_MODE_TYPE_OFFSET): New macro. + (PAGE_MODE_TYPE_OFFSET): New macro. + (ACC_MIN_OFFSET): New macro. + (ACC_MAX_OFFSET): New macro. + (TOP_BOTTOM_SECTOR_FLAG_OFFSET): New macro. + (PROGRAM_SUSPEND_OFFSET): New macro. + (UNLOCK_BYPASS_OFFSET): New macro. + (SECSI_SECTOR_SIZE_OFFSET): New macro. + (EMBEDDED_HWRST_TIMEOUT_MAX_OFFSET): New macro. + (NON_EMBEDDED_HWRST_TIMEOUT_MAX_OFFSET): New macro. + (ERASE_SUSPEND_TIMEOUT_MAX_OFFSET): New macro. + (PROGRAM_SUSPEND_TIMEOUT_MAX_OFFSET): New macro. + (BANK_ORGANIZATION_OFFSET): New macro. + (BANK_REGION_INFO_OFFSET): New macro. + (struct amd_pri_extened_query_structure): Define. + (amd_pri_extened_query_structure_t): Typedef. + * src/flash/cfi.c (cfi_array_free): Free primary vendor table. + (cfi_detect): Detect AMD CFI primary vendor-specific extended + query table. Reverse the order of erase block region information + for top boot devices. + * src/flash/detectflash.c (detectflash): Print out information + of AMD CFI primary vendor-specific extended query table. + 2008-02-24 Kolja Waschk * include/cable.h, include/usbconn.h, include/usbconn/libusb.h, diff --git a/jtag/include/flash/cfi.h b/jtag/include/flash/cfi.h index 5d958f12..c8f6984a 100644 --- a/jtag/include/flash/cfi.h +++ b/jtag/include/flash/cfi.h @@ -35,6 +35,10 @@ * September 1999, Order Number: JESD68 * [2] JEDEC Solid State Technology Association, "Common Flash Interface (CFI) ID Codes", * September 2001, Order Number: JEP137-A + * [3] AMD, "Common Flash Memory Interface Specification", Release 2.0 + * December 1, 2001. + * [4] SPANSION, "Common Flash Interface Version 1.4 Vendor Specific + * Extensions", March 22, 2004. * */ @@ -155,4 +159,55 @@ typedef struct cfi_query_structure { } cfi_query_structure_t; #endif /* __ASSEMBLY__ */ +/* AMD primary vendor-specific extended query structure - see [3] and [4] */ +#define MAJOR_VERSION_OFFSET 0x03 +#define MINOR_VERSION_OFFSET 0x04 +#define ADDRESS_SENSITIVE_UNLOCK_OFFSET 0x05 +#define ERASE_SUSPEND_OFFSET 0x06 +#define SECTOR_PROTECT_OFFSET 0x07 +#define SECTOR_TEMPORARY_UNPROTECT_OFFSET 0x08 +#define SECTOR_PROTECT_SCHEME_OFFSET 0x09 +#define SIMULTANEOUS_OPERATION_OFFSET 0x0A +#define BURST_MODE_TYPE_OFFSET 0x0B +#define PAGE_MODE_TYPE_OFFSET 0x0C +#define ACC_MIN_OFFSET 0x0D +#define ACC_MAX_OFFSET 0x0E +#define TOP_BOTTOM_SECTOR_FLAG_OFFSET 0x0F +#define PROGRAM_SUSPEND_OFFSET 0x10 +#define UNLOCK_BYPASS_OFFSET 0x11 +#define SECSI_SECTOR_SIZE_OFFSET 0x12 +#define EMBEDDED_HWRST_TIMEOUT_MAX_OFFSET 0x13 +#define NON_EMBEDDED_HWRST_TIMEOUT_MAX_OFFSET 0x14 +#define ERASE_SUSPEND_TIMEOUT_MAX_OFFSET 0x15 +#define PROGRAM_SUSPEND_TIMEOUT_MAX_OFFSET 0x16 +#define BANK_ORGANIZATION_OFFSET 0x17 +#define BANK_REGION_INFO_OFFSET 0X18 + +#ifndef __ASSEMBLY__ +typedef struct amd_pri_extened_query_structure { + uint8_t major_version; + uint8_t minor_version; + uint8_t address_sensitive_unlock; + uint8_t erase_suspend; + uint8_t sector_protect; + uint8_t sector_temporary_unprotect; + uint8_t sector_protect_scheme; + uint8_t simultaneous_operation; + uint8_t burst_mode_type; + uint8_t page_mode_type; + uint16_t acc_min; /* in mV */ + uint16_t acc_max; /* in mV */ + uint8_t top_bottom_sector_flag; + uint8_t program_suspend; + uint8_t unlock_bypass; + uint8_t secsi_sector_size; + uint8_t embedded_hwrst_timeout_max; + uint8_t non_embedded_hwrst_timeout_max; /* in ns */ + uint8_t erase_suspend_timeout_max; /* in ns */ + uint8_t program_suspend_timeout_max; /* in us */ + uint8_t bank_organization; /* in us */ + uint8_t bank_region_info[0]; +} amd_pri_extened_query_structure_t; +#endif /* __ASSEMBLY__ */ + #endif /* FLASH_CFI_H */ diff --git a/jtag/src/flash/cfi.c b/jtag/src/flash/cfi.c index 2d943a88..ec7ab190 100644 --- a/jtag/src/flash/cfi.c +++ b/jtag/src/flash/cfi.c @@ -51,6 +51,8 @@ cfi_array_free( cfi_array_t *cfi_array ) continue; free( cfi_array->cfi_chips[i]->cfi.device_geometry.erase_block_regions ); + if (cfi_array->cfi_chips[i]->cfi.identification_string.pri_vendor_tbl) + free (cfi_array->cfi_chips[i]->cfi.identification_string.pri_vendor_tbl); free( cfi_array->cfi_chips[i] ); } free( cfi_array->cfi_chips ); @@ -98,6 +100,7 @@ cfi_detect( bus_t *bus, uint32_t adr, cfi_array_t **cfi_array ) cfi_query_structure_t *cfi; uint32_t tmp; int ret = -4; /* CFI not detected (Q) */ + uint16_t pri_vendor_tbl_adr; /* detect CFI capable devices - see Table 1 in [1] */ for (ma = 1; ma <= 4; ma *= 2) { @@ -203,6 +206,106 @@ cfi_detect( bus_t *bus, uint32_t adr, cfi_array_t **cfi_array ) } } + pri_vendor_tbl_adr = read2(PRI_VENDOR_TABLE_ADR_OFFSET); + + /* AMD CFI Primary Vendor-Specific Extended Query Table - see [3] and [4] */ + if (cfi->identification_string.pri_id_code == CFI_VENDOR_AMD_SCS + && pri_vendor_tbl_adr != 0) { + amd_pri_extened_query_structure_t *pri_vendor_tbl; + uint8_t major_version; + uint8_t minor_version; + uint8_t num_of_banks; + int i; +#undef A +#define A(off) (adr + (pri_vendor_tbl_adr + off) * ba * ma) + + if (read1 (0) != 'P' || read1 (1) != 'R' || read1 (2) != 'I') { + write1 (0, CFI_CMD_READ_ARRAY1); + return -9; /* CFI primary vendor table not detected */ + } + + major_version = read1 (MAJOR_VERSION_OFFSET); + minor_version = read1 (MINOR_VERSION_OFFSET); + if (major_version > '1' || (major_version == '1' && minor_version >= '3')) + num_of_banks = read1 (BANK_ORGANIZATION_OFFSET); + else + num_of_banks = 0; + pri_vendor_tbl = (amd_pri_extened_query_structure_t *) + calloc (1, sizeof (amd_pri_extened_query_structure_t) + + num_of_banks * sizeof (uint8_t)); + if (!pri_vendor_tbl) { + write1 (0, CFI_CMD_READ_ARRAY1); + return -2; /* out of memory */ + } + + if (major_version > '1' || (major_version == '1' && minor_version >= '0')) { + pri_vendor_tbl->major_version = major_version; + pri_vendor_tbl->minor_version = minor_version; + pri_vendor_tbl->address_sensitive_unlock = read1 (ADDRESS_SENSITIVE_UNLOCK_OFFSET); + pri_vendor_tbl->erase_suspend = read1 (ERASE_SUSPEND_OFFSET); + pri_vendor_tbl->sector_protect = read1 (SECTOR_PROTECT_OFFSET); + pri_vendor_tbl->sector_temporary_unprotect = read1 (SECTOR_TEMPORARY_UNPROTECT_OFFSET); + pri_vendor_tbl->sector_protect_scheme = read1 (SECTOR_PROTECT_SCHEME_OFFSET); + pri_vendor_tbl->simultaneous_operation = read1 (SIMULTANEOUS_OPERATION_OFFSET); + pri_vendor_tbl->burst_mode_type = read1 (BURST_MODE_TYPE_OFFSET); + pri_vendor_tbl->page_mode_type = read1 (PAGE_MODE_TYPE_OFFSET); + } + if (major_version > '1' || (major_version == '1' && minor_version >= '1')) { + tmp = read1 (ACC_MIN_OFFSET); + pri_vendor_tbl->acc_min = ((tmp >> 4) & 0xF) * 1000 + (tmp & 0xF) * 100; + tmp = read1 (ACC_MAX_OFFSET); + pri_vendor_tbl->acc_max = ((tmp >> 4) & 0xF) * 1000 + (tmp & 0xF) * 100; + pri_vendor_tbl->top_bottom_sector_flag = read1 (TOP_BOTTOM_SECTOR_FLAG_OFFSET); + } + if (major_version > '1' || (major_version == '1' && minor_version >= '2')) + pri_vendor_tbl->program_suspend = read1 (PROGRAM_SUSPEND_OFFSET); + if (major_version > '1' || (major_version == '1' && minor_version >= '3')) { + if (pri_vendor_tbl->simultaneous_operation) + pri_vendor_tbl->bank_organization = read1 (BANK_ORGANIZATION_OFFSET); + else + pri_vendor_tbl->bank_organization = 0; + for (i = 0; i < pri_vendor_tbl->bank_organization; i++) + pri_vendor_tbl->bank_region_info[i] = read1 (BANK_REGION_INFO_OFFSET + i * sizeof (uint8_t)); + } + if (major_version > '1' || (major_version == '1' && minor_version >= '4')) { + pri_vendor_tbl->unlock_bypass = read1 (UNLOCK_BYPASS_OFFSET); + tmp = read1 (SECSI_SECTOR_SIZE_OFFSET); + pri_vendor_tbl->secsi_sector_size = tmp ? (1 << tmp) : 0; + tmp = read1 (EMBEDDED_HWRST_TIMEOUT_MAX_OFFSET); + pri_vendor_tbl->embedded_hwrst_timeout_max = tmp ? (1 << tmp) : 0; + tmp = read1 (NON_EMBEDDED_HWRST_TIMEOUT_MAX_OFFSET); + pri_vendor_tbl->non_embedded_hwrst_timeout_max = tmp ? (1 << tmp) : 0; + tmp = read1 (ERASE_SUSPEND_TIMEOUT_MAX_OFFSET); + pri_vendor_tbl->erase_suspend_timeout_max = tmp ? (1 << tmp) : 0; + tmp = read1 (PROGRAM_SUSPEND_TIMEOUT_MAX_OFFSET); + pri_vendor_tbl->program_suspend_timeout_max = tmp ? (1 << tmp) : 0; + } + + cfi->identification_string.pri_vendor_tbl = (void *) pri_vendor_tbl; + +#undef A +#define A(off) (adr + (off) * ba * ma) + + /* Reverse the order of erase block region information for top boot devices. */ + if ((major_version > '1' || (major_version == '1' && minor_version >= '1')) + && pri_vendor_tbl->top_bottom_sector_flag == 0x3) + { + uint32_t y, z; + uint32_t n = cfi->device_geometry.number_of_erase_regions; + + for (i = 0; i < n / 2; i++) { + z = cfi->device_geometry.erase_block_regions[i].erase_block_size; + y = cfi->device_geometry.erase_block_regions[i].number_of_erase_blocks; + cfi->device_geometry.erase_block_regions[i].erase_block_size + = cfi->device_geometry.erase_block_regions[n - i - 1].erase_block_size; + cfi->device_geometry.erase_block_regions[i].number_of_erase_blocks + = cfi->device_geometry.erase_block_regions[n - i - 1].number_of_erase_blocks; + cfi->device_geometry.erase_block_regions[n - i - 1].erase_block_size = z; + cfi->device_geometry.erase_block_regions[n - i - 1].number_of_erase_blocks = y; + } + } + } + /* TODO: Intel Primary Algorithm Extended Query Table - see Table 5. in [2] */ /* Read Array */ diff --git a/jtag/src/flash/detectflash.c b/jtag/src/flash/detectflash.c index fae2bc5b..a11a5979 100644 --- a/jtag/src/flash/detectflash.c +++ b/jtag/src/flash/detectflash.c @@ -213,4 +213,144 @@ detectflash( bus_t *bus, uint32_t adr ) printf( _("\t\t\tNumber of Erase Blocks: %d\n"), cfi->device_geometry.erase_block_regions[i].number_of_erase_blocks ); } } + + if (cfi->identification_string.pri_id_code == CFI_VENDOR_AMD_SCS + && cfi->identification_string.pri_vendor_tbl != NULL) + { + amd_pri_extened_query_structure_t *pri_vendor_tbl; + uint8_t major_version; + uint8_t minor_version; + int i; + const char *required_or_not[2] = { + N_("Required"), N_("Not required") + }; + const char *supported_or_not[2] = { + N_("Supported"), N_("Not supported") + }; + const char *process_technology[6] = { + N_("170-nm Floating Gate technology"), N_("230-nm MirrorBit(tm) technology"), + N_("130-nm Floating Gate technology"), N_("110-nm MirrorBit(tm) technology"), + N_("90-nm Floating Gate technology"), N_("90-nm MirrorBit(tm) technology") + }; + const char *process_technology_13[3] = { + N_("CS49"), N_("CS59"), N_("CS99") + }; + const char *erase_suspend[3] = { + N_("Not supported"), N_("Read only"), N_("Read/write") + }; + const char *sector_protect_scheme[8] = { + N_("29F040 mode"), N_("29F016 mode"), N_("29F400 mode"), + N_("29LV800 mode"), N_("29BDS640 mode (Software Command Locking)"), + N_("29BDD160 mode (New Sector Protect)"), + N_("29PDL128 mode (New Sector Protect + 29LV800)"), + N_("Advanced Sector Protect") + }; + const char *page_mode_type[4] = { + N_("Not supported"), N_("4 word Page"), N_("8 word Page"), N_("16 word Page") + }; + + const char *top_bottom[6] = { + N_("No boot"), N_("8x8kb sectors at top and bottom with WP control"), + N_("Bottom boot device"), N_("Top boot device"), + N_("Uniform bottom boot device"), N_("Uniform top boot device") + }; + const char *bad_value = N_("Bad value"); + +#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0])) + + pri_vendor_tbl = cfi->identification_string.pri_vendor_tbl; + major_version = pri_vendor_tbl->major_version; + minor_version = pri_vendor_tbl->minor_version; + + printf (_("Primary Vendor-Specific Extended Query:\n")); + printf (_("\tMajor version number: %c\n"), pri_vendor_tbl->major_version); + printf (_("\tMinor version number: %c\n"), pri_vendor_tbl->minor_version); + if (major_version > '1' || (major_version == '1' && minor_version >= '0')) + { + if ((pri_vendor_tbl->address_sensitive_unlock & 0x3) < ARRAY_SIZE (required_or_not)) + printf (_("\tAddress Sensitive Unlock: %s\n"), + required_or_not[pri_vendor_tbl->address_sensitive_unlock & 0x3]); + else + printf (_("\tAddress Sensitive Unlock: %s\n"), bad_value); + + if (major_version > '1' || (major_version == '1' && minor_version >= '4')) + { + if ((pri_vendor_tbl->address_sensitive_unlock >> 2) < ARRAY_SIZE (process_technology)) + printf (_("\tProcess Technology: %s\n"), + process_technology[pri_vendor_tbl->address_sensitive_unlock >> 2]); + else + printf (_("\tProcess Technology: %s\n"), bad_value); + } + else if (major_version == '1' && minor_version == '3') + { + if ((pri_vendor_tbl->address_sensitive_unlock >> 2) < ARRAY_SIZE (process_technology_13)) + printf (_("\tProcess Technology: %s\n"), + process_technology_13[pri_vendor_tbl->address_sensitive_unlock >> 2]); + else + printf (_("\tProcess Technology: %s\n"), bad_value); + } + if (pri_vendor_tbl->erase_suspend < ARRAY_SIZE (erase_suspend)) + printf (_("\tErase Suspend: %s\n"), erase_suspend[pri_vendor_tbl->erase_suspend]); + if (pri_vendor_tbl->sector_protect == 0) + printf (_("\tSector Protect: Not supported\n")); + else + printf (_("\tSector Protect: %d sectors per group\n"), pri_vendor_tbl->sector_protect); + if (pri_vendor_tbl->sector_temporary_unprotect < ARRAY_SIZE (supported_or_not)) + printf (_("\tSector Temporary Unprotect: %s\n"), supported_or_not[pri_vendor_tbl->sector_temporary_unprotect]); + else + printf (_("\tSector Temporary Unprotect: %s\n"), bad_value); + if (pri_vendor_tbl->sector_protect_scheme < ARRAY_SIZE (sector_protect_scheme)) + printf (_("\tSector Protect/Unprotect Scheme: %s\n"), + sector_protect_scheme[pri_vendor_tbl->sector_protect_scheme]); + else + printf (_("\tSector Protect/Unprotect Scheme: %s\n"), bad_value); + if (pri_vendor_tbl->simultaneous_operation == 0) + printf (_("\tSimultaneous Operation: Not supported\n")); + else + printf (_("\tSimultaneous Operation: %d sectors\n"), pri_vendor_tbl->simultaneous_operation); + if (pri_vendor_tbl->burst_mode_type < ARRAY_SIZE (supported_or_not)) + printf (_("\tBurst Mode Type: %s\n"), supported_or_not[pri_vendor_tbl->burst_mode_type]); + else + printf (_("\tBurst Mode Type: %s\n"), bad_value); + if (pri_vendor_tbl->page_mode_type < ARRAY_SIZE (page_mode_type)) + printf (_("\tPage Mode Type: %s\n"), page_mode_type[pri_vendor_tbl->page_mode_type]); + else + printf (_("\tPage Mode Type: %s\n"), bad_value); + } + if (major_version > '1' || (major_version == '1' && minor_version >= '1')) + { + printf (_("\tACC (Acceleration) Supply Minimum: %d mV\n"), pri_vendor_tbl->acc_min); + printf (_("\tACC (Acceleration) Supply Maximum: %d mV\n"), pri_vendor_tbl->acc_max); + if (pri_vendor_tbl->top_bottom_sector_flag < ARRAY_SIZE (top_bottom)) + printf (_("\tTop/Bottom Sector Flag: %s\n"), top_bottom[pri_vendor_tbl->top_bottom_sector_flag]); + else + printf (_("\tTop/Bottom Sector Flag: %s\n"), bad_value); + } + if (major_version > '1' || (major_version == '1' && minor_version >= '2')) + { + if (pri_vendor_tbl->program_suspend < ARRAY_SIZE (supported_or_not)) + printf (_("\tProgram Suspend: %s\n"), supported_or_not[pri_vendor_tbl->program_suspend]); + else + printf (_("\tProgram Suspend: %s\n"), bad_value); + } + if (major_version > '1' || (major_version == '1' && minor_version >= '4')) + { + if (pri_vendor_tbl->unlock_bypass < ARRAY_SIZE (supported_or_not)) + printf (_("\tUnlock Bypass: %s\n"), supported_or_not[pri_vendor_tbl->unlock_bypass]); + else + printf (_("\tUnlock Bypass: %s\n"), bad_value); + printf (_("\tSecSi Sector (Customer OTP Area) Size: %d bytes\n"), pri_vendor_tbl->secsi_sector_size); + printf (_("\tEmbedded Hardware Reset Timeout Maximum: %d ns\n"), pri_vendor_tbl->embedded_hwrst_timeout_max); + printf (_("\tNon-Embedded Hardware Reset Timeout Maximum: %d ns\n"), pri_vendor_tbl->non_embedded_hwrst_timeout_max); + printf (_("\tErase Suspend Timeout Maximum: %d us\n"), pri_vendor_tbl->erase_suspend_timeout_max); + printf (_("\tProgram Suspend Timeout Maximum: %d us\n"), pri_vendor_tbl->program_suspend_timeout_max); + } + if ((major_version > '1' || (major_version == '1' && minor_version >= '3')) + && pri_vendor_tbl->bank_organization) + { + printf (_("\tBank Organization:\n")); + for (i = 0; i < pri_vendor_tbl->bank_organization; i++) + printf (_("\t\tBank%d: %d sectors\n"), i + 1, pri_vendor_tbl->bank_region_info[i]); + } + } }