示例#1
0
文件: intel.c 项目: klammerj/urjtag
static int
intel_flash_autodetect8 (urj_flash_cfi_array_t *cfi_array)
{
    urj_bus_area_t area;

    if (URJ_BUS_AREA (cfi_array->bus, cfi_array->address,
                      &area) != URJ_STATUS_OK)
        return 0;

    return ((cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code
             == CFI_VENDOR_MITSUBISHI_SCS)
            || (cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code
                == CFI_VENDOR_MITSUBISHI_ECS)
            || (cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code
                == CFI_VENDOR_INTEL_ECS)
            || (cfi_array->cfi_chips[0]->cfi.identification_string.pri_id_code
                == CFI_VENDOR_INTEL_SCS))
           && (area.width == 8);
}
示例#2
0
文件: cmd_print.c 项目: cktben/urjtag
static int
cmd_print_run (urj_chain_t *chain, char *params[])
{
#define FORMAT_LENGTH   128
    char format[FORMAT_LENGTH];
#if HAVE_SWPRINTF
    wchar_t wformat[FORMAT_LENGTH];
#endif /* HAVE_SWPRINTF */
    wchar_t wheader[FORMAT_LENGTH];
    char header[FORMAT_LENGTH];
    int i;
    int noheader = 0;

    if (urj_cmd_params (params) > 2)
    {
        urj_error_set (URJ_ERROR_SYNTAX,
                       "%s: #parameters should be <= %d, not %d",
                       params[0], 2, urj_cmd_params (params));
        return URJ_STATUS_FAIL;
    }

    if (urj_cmd_test_cable (chain) != URJ_STATUS_OK)
        return URJ_STATUS_FAIL;

    if (!chain->parts)
    {
        urj_error_set (URJ_ERROR_ILLEGAL_STATE, "Run \"detect\" first");
        return URJ_STATUS_FAIL;
    }

    if (urj_cmd_params (params) == 2)
    {
        if (strcasecmp (params[1], "bus") == 0)
            noheader = 1;

        if (strcasecmp (params[1], "signals") == 0)
        {

            urj_log (URJ_LOG_LEVEL_NORMAL, "Signals:\n");
            urj_part_t *part;
            urj_part_signal_t *s;
            part = chain->parts->parts[chain->active_part];
            for (s = part->signals; s != NULL; s = s->next)
            {
                urj_part_salias_t *sa;
                if (s->pin)
                    urj_log (URJ_LOG_LEVEL_NORMAL, "%s %s", s->name, s->pin);
                else
                    urj_log (URJ_LOG_LEVEL_NORMAL, "%s", s->name);
                if (s->input)
                    urj_log (URJ_LOG_LEVEL_NORMAL, "\tinput=%s",
                             s->input->name);
                if (s->output)
                    urj_log (URJ_LOG_LEVEL_NORMAL, "\toutput=%s",
                             s->output->name);

                for (sa = part->saliases; sa != NULL; sa = sa->next)
                {
                    if (s == sa->signal)
                        urj_log (URJ_LOG_LEVEL_NORMAL, "\tsalias=%s", sa->name);
                }
                urj_log (URJ_LOG_LEVEL_NORMAL, "\n");
            }
            return URJ_STATUS_OK;
        }

        if (strcasecmp (params[1], "instructions") == 0)
        {
            urj_part_t *part;
            urj_part_instruction_t *inst;

            snprintf (format, sizeof format, _(" Active %%-%ds %%-%ds\n"),
                      URJ_INSTRUCTION_MAXLEN_INSTRUCTION,
                      URJ_DATA_REGISTER_MAXLEN);
#if HAVE_SWPRINTF
            if (mbstowcs (wformat, format, sizeof format) == -1)
                // @@@@ RFHH throw urj_error?
                printf (_("(%d) String conversion failed!\n"), __LINE__);
            swprintf (wheader, sizeof format, wformat, _("Instruction"), _("Register"));
            if (wcstombs (header, wheader, sizeof format) == -1)
                // @@@@ RFHH throw urj_error?
                printf (_("(%d) String conversion failed!\n"), __LINE__);
#else /* HAVE_SWPRINTF */
            snprintf (header, sizeof format, format, _("Instruction"), _("Register"));
            if (mbstowcs (wheader, header, sizeof format) == -1)
                // @@@@ RFHH throw urj_error?
                printf (_("(%d) String conversion failed!\n"), __LINE__);
#endif /* HAVE_SWPRINTF */
            urj_log (URJ_LOG_LEVEL_NORMAL, "%s", header);

            for (i = 0; i < wcslen (wheader); i++)
                urj_log (URJ_LOG_LEVEL_NORMAL, "%c", '-');
            urj_log (URJ_LOG_LEVEL_NORMAL, "%c", '\n');

            snprintf (format, sizeof format, _("   %%c    %%-%ds %%-%ds\n"),
                      URJ_INSTRUCTION_MAXLEN_INSTRUCTION,
                      URJ_DATA_REGISTER_MAXLEN);

            part = chain->parts->parts[chain->active_part];
            for (inst = part->instructions; inst != NULL; inst = inst->next)
            {
                urj_log (URJ_LOG_LEVEL_NORMAL, format,
                         (inst == part->active_instruction) ? 'X' : ' ',
                         inst->name, inst->data_register->name);
            }
            return URJ_STATUS_OK;
        }
    }

    if (noheader == 0)
    {
        snprintf (format, sizeof format,
                  _(" No. %%-%ds %%-%ds %%-%ds %%-%ds %%-%ds\n"),
                  URJ_PART_MANUFACTURER_MAXLEN, URJ_PART_PART_MAXLEN,
                  URJ_PART_STEPPING_MAXLEN,
                  URJ_INSTRUCTION_MAXLEN_INSTRUCTION,
                  URJ_DATA_REGISTER_MAXLEN);
#if HAVE_SWPRINTF
        if (mbstowcs (wformat, format, sizeof format) == -1)
            // @@@@ RFHH throw urj_error?
            printf (_("(%d) String conversion failed!\n"), __LINE__);
        swprintf (wheader, sizeof format, wformat, _("Manufacturer"), _("Part"),
                  _("Stepping"), _("Instruction"), _("Register"));
        if (wcstombs (header, wheader, sizeof format) == -1)
            // @@@@ RFHH throw urj_error?
            printf (_("(%d) String conversion failed!\n"), __LINE__);
#else /* HAVE_SWPRINTF */
        snprintf (header, sizeof format, format, _("Manufacturer"), _("Part"),
                  _("Stepping"), _("Instruction"), _("Register"));
        if (mbstowcs (wheader, header, sizeof format) == -1)
            // @@@@ RFHH throw urj_error?
            printf (_("(%d) String conversion failed!\n"), __LINE__);
#endif /* HAVE_SWPRINTF */
        urj_log (URJ_LOG_LEVEL_NORMAL, "%s", header);

        for (i = 0; i < wcslen (wheader); i++)
            urj_log (URJ_LOG_LEVEL_NORMAL, "%c", '-');
        urj_log (URJ_LOG_LEVEL_NORMAL, "%c", '\n');
    }

    if (urj_cmd_params (params) == 1)
    {
        int r = URJ_STATUS_OK;

        if (chain->parts->len > chain->active_part)
        {
            if (chain->parts->parts[chain->active_part]->alias)
                urj_log (URJ_LOG_LEVEL_NORMAL, _(" %3d %s "),
                         chain->active_part,
                         chain->parts->parts[chain->active_part]->alias);
            else
                urj_log (URJ_LOG_LEVEL_NORMAL, _(" %3d "), chain->active_part);

            urj_part_print (URJ_LOG_LEVEL_NORMAL,
                            chain->parts->parts[chain->active_part]);
        }
        if (urj_bus != NULL)
        {
            int i;
            uint64_t a;
            urj_bus_area_t area;

            for (i = 0; i < urj_buses.len; i++)
                if (urj_buses.buses[i] == urj_bus)
                    break;
            urj_log (URJ_LOG_LEVEL_NORMAL, _("\nActive bus:\n*%d: "), i);
            URJ_BUS_PRINTINFO (URJ_LOG_LEVEL_NORMAL, urj_bus);

            for (a = 0; a < UINT64_C (0x100000000);
                 a = area.start + area.length)
            {
                r = URJ_BUS_AREA (urj_bus, a, &area);
                if (r != URJ_STATUS_OK)
                {
                    urj_log (URJ_LOG_LEVEL_NORMAL,
                             _("Error in bus area discovery at 0x%08llX\n"),
                             (long long unsigned int) a);
                    break;
                }
                if (area.width != 0)
                {
                    if (area.description != NULL)
                        urj_log (URJ_LOG_LEVEL_NORMAL,
                                 _("\tstart: 0x%08lX, length: 0x%08llX, data width: %d bit, (%s)\n"),
                                 (long unsigned) area.start,
                                 (long long unsigned int) area.length,
                                 area.width, _(area.description));
                    else
                        urj_log (URJ_LOG_LEVEL_NORMAL,
                                 _("\tstart: 0x%08lX, length: 0x%08llX, data width: %d bit\n"),
                                 (long unsigned) area.start,
                                 (long long unsigned int) area.length,
                                 area.width);
                }
            }
        }

        return r;
    }

    if (strcasecmp (params[1], "chain") == 0)
    {
        urj_part_parts_print (URJ_LOG_LEVEL_NORMAL, chain->parts);
        return URJ_STATUS_OK;
    }

    for (i = 0; i < urj_buses.len; i++)
    {
        if (urj_buses.buses[i] == urj_bus)
            urj_log (URJ_LOG_LEVEL_NORMAL, _("*%d: "), i);
        else
            urj_log (URJ_LOG_LEVEL_NORMAL, _("%d: "), i);
        URJ_BUS_PRINTINFO (URJ_LOG_LEVEL_NORMAL, urj_buses.buses[i]);
    }

    return URJ_STATUS_OK;
}
示例#3
0
文件: cfi.c 项目: klammerj/urjtag
int
urj_flash_cfi_detect (urj_bus_t *bus, uint32_t adr,
                      urj_flash_cfi_array_t **cfi_array)
{
    unsigned int bw;            /* bus width */
    unsigned int d;             /* data offset */
    size_t ba;                  /* bus width address multiplier */
    int ma;                     /* flash mode address multiplier */
    urj_bus_area_t area;

    if (!cfi_array || !bus)
    {
        urj_error_set (URJ_ERROR_INVALID, "cfi_array or bus");
        return URJ_STATUS_FAIL;
    }

    *cfi_array = calloc (1, sizeof (urj_flash_cfi_array_t));
    if (!*cfi_array)
    {
        urj_error_set (URJ_ERROR_OUT_OF_MEMORY, "calloc(%zd,%zd) fails",
                       (size_t) 1, sizeof (urj_flash_cfi_array_t));
        return URJ_STATUS_FAIL;
    }

    (*cfi_array)->bus = bus;
    (*cfi_array)->address = adr;
    if (URJ_BUS_AREA (bus, adr, &area) != URJ_STATUS_OK)
        // retain error state
        return URJ_STATUS_FAIL;
    if (URJ_BUS_TYPE (bus) != URJ_BUS_TYPE_PARALLEL)
        return URJ_STATUS_FAIL;
    bw = area.width;
    if (bw != 8 && bw != 16 && bw != 32)
    {
        urj_error_set (URJ_ERROR_INVALID, "bus width = %d", bw);
        return URJ_STATUS_FAIL;
    }
    (*cfi_array)->bus_width = ba = bw / 8;
    (*cfi_array)->cfi_chips = calloc (ba, sizeof (urj_flash_cfi_chip_t *));
    if (!(*cfi_array)->cfi_chips)
    {
        urj_error_set (URJ_ERROR_OUT_OF_MEMORY, "calloc(%zd,%zd) fails",
                       ba, sizeof (urj_flash_cfi_chip_t *));
        return URJ_STATUS_FAIL;
    }

    for (d = 0; d < bw; d += 8)
    {
#define A(off)                  (adr + (off) * ba * ma)
#define D(data)                 ((data) << d)
#define gD(data)                (((data) >> d) & 0xFF)
#define read1(off)              gD(URJ_BUS_READ( bus, A(off) ))
        // @@@@ RFHH check status of URJ_BUS_READ_START
#define read2(off)              (URJ_BUS_READ_START (bus, A(off)), gD (URJ_BUS_READ_NEXT (bus, A((off) + 1))) | gD (URJ_BUS_READ_END (bus)) << 8)
#define write1(off,data)        URJ_BUS_WRITE( bus, A(off), D(data) )

        urj_flash_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)
        {
            write1 (CFI_CMD_QUERY_OFFSET, CFI_CMD_QUERY);

            if (read1 (CFI_QUERY_ID_OFFSET) == 'Q')
            {
                ret = -5;       /* CFI not detected (R) */
                if (read1 (CFI_QUERY_ID_OFFSET + 1) == 'R')
                    break;
            }

            write1 (0, CFI_CMD_READ_ARRAY1);
            write1 (0, CFI_CMD_READ_ARRAY2);
        }

        if (ma > 4)
        {
            if (ret == -4)
                urj_error_set (URJ_ERROR_FLASH, "CFI not detected (Q)");
            else
                urj_error_set (URJ_ERROR_FLASH, "CFI not detected (R)");
            return URJ_STATUS_FAIL;
        }

        if (read1 (CFI_QUERY_ID_OFFSET + 2) != 'Y')
        {
            write1 (0, CFI_CMD_READ_ARRAY1);
            write1 (0, CFI_CMD_READ_ARRAY2);
            urj_error_set (URJ_ERROR_FLASH, "CFI not detected (Y)");
            return URJ_STATUS_FAIL;
        }

        (*cfi_array)->cfi_chips[d / 8] = calloc (1,
                                                 sizeof (urj_flash_cfi_chip_t));
        if (!(*cfi_array)->cfi_chips[d / 8])
        {
            write1 (0, CFI_CMD_READ_ARRAY1);
            write1 (0, CFI_CMD_READ_ARRAY2);
            urj_error_set (URJ_ERROR_OUT_OF_MEMORY, "calloc(%zd,%zd) fails",
                           (size_t) 1, sizeof (urj_flash_cfi_chip_t));
            return URJ_STATUS_FAIL;
        }
        cfi = &(*cfi_array)->cfi_chips[d / 8]->cfi;

        /* Identification string - see Table 6 in [1] */
        cfi->identification_string.pri_id_code = read2 (PRI_VENDOR_ID_OFFSET);
        cfi->identification_string.pri_vendor_tbl = NULL;
        cfi->identification_string.alt_id_code = read2 (ALT_VENDOR_ID_OFFSET);
        cfi->identification_string.alt_vendor_tbl = NULL;

        /* System interface information - see Table 7 in [1] */
        tmp = read1 (VCC_MIN_WEV_OFFSET);
        cfi->system_interface_info.vcc_min_wev =
            ((tmp >> 4) & 0xF) * 1000 + (tmp & 0xF) * 100;
        tmp = read1 (VCC_MAX_WEV_OFFSET);
        cfi->system_interface_info.vcc_max_wev =
            ((tmp >> 4) & 0xF) * 1000 + (tmp & 0xF) * 100;
        tmp = read1 (VPP_MIN_WEV_OFFSET);
        cfi->system_interface_info.vpp_min_wev =
            ((tmp >> 4) & 0xF) * 1000 + (tmp & 0xF) * 100;
        tmp = read1 (VPP_MAX_WEV_OFFSET);
        cfi->system_interface_info.vpp_max_wev =
            ((tmp >> 4) & 0xF) * 1000 + (tmp & 0xF) * 100;

        /* TODO: Add out of range checks for timeouts */
        tmp = read1 (TYP_SINGLE_WRITE_TIMEOUT_OFFSET);
        cfi->system_interface_info.typ_single_write_timeout =
            tmp ? (1 << tmp) : 0;

        tmp = read1 (TYP_BUFFER_WRITE_TIMEOUT_OFFSET);
        cfi->system_interface_info.typ_buffer_write_timeout =
            tmp ? (1 << tmp) : 0;

        tmp = read1 (TYP_BLOCK_ERASE_TIMEOUT_OFFSET);
        cfi->system_interface_info.typ_block_erase_timeout =
            tmp ? (1 << tmp) : 0;

        tmp = read1 (TYP_CHIP_ERASE_TIMEOUT_OFFSET);
        cfi->system_interface_info.typ_chip_erase_timeout =
            tmp ? (1 << tmp) : 0;

        tmp = read1 (MAX_SINGLE_WRITE_TIMEOUT_OFFSET);
        cfi->system_interface_info.max_single_write_timeout =
            (tmp ? (1 << tmp) : 0) *
            cfi->system_interface_info.typ_single_write_timeout;

        tmp = read1 (MAX_BUFFER_WRITE_TIMEOUT_OFFSET);
        cfi->system_interface_info.max_buffer_write_timeout =
            (tmp ? (1 << tmp) : 0) *
            cfi->system_interface_info.typ_buffer_write_timeout;

        tmp = read1 (MAX_BLOCK_ERASE_TIMEOUT_OFFSET);
        cfi->system_interface_info.max_block_erase_timeout =
            (tmp ? (1 << tmp) : 0) *
            cfi->system_interface_info.typ_block_erase_timeout;

        tmp = read1 (MAX_CHIP_ERASE_TIMEOUT_OFFSET);
        cfi->system_interface_info.max_chip_erase_timeout =
            (tmp ? (1 << tmp) : 0) *
            cfi->system_interface_info.typ_chip_erase_timeout;

        /* Device geometry - see Table 8 in [1] */
        /* TODO: Add out of range check */
        cfi->device_geometry.device_size = 1 << read1 (DEVICE_SIZE_OFFSET);

        cfi->device_geometry.device_interface =
            read2 (FLASH_DEVICE_INTERFACE_OFFSET);

        /* TODO: Add out of range check */
        cfi->device_geometry.max_bytes_write =
            1 << read2 (MAX_BYTES_WRITE_OFFSET);

        tmp = cfi->device_geometry.number_of_erase_regions =
            read1 (NUMBER_OF_ERASE_REGIONS_OFFSET);

        cfi->device_geometry.erase_block_regions =
            malloc (tmp * sizeof (urj_flash_cfi_erase_block_region_t));
        if (!cfi->device_geometry.erase_block_regions)
        {
            write1 (0, CFI_CMD_READ_ARRAY1);
            write1 (0, CFI_CMD_READ_ARRAY2);
            urj_error_set (URJ_ERROR_OUT_OF_MEMORY, "malloc(%zd) fails",
                           (size_t) tmp
                           * sizeof (urj_flash_cfi_erase_block_region_t));
            return URJ_STATUS_FAIL;
        }

        {
            int a;
            int i;

            for (i = 0, a = ERASE_BLOCK_REGION_OFFSET; i < tmp; i++, a += 4)
            {
                uint32_t y = read2 (a);
                uint32_t z = read2 (a + 2) << 8;
                if (z == 0)
                    z = 128;
                cfi->device_geometry.erase_block_regions[i].erase_block_size =
                    z;
                cfi->device_geometry.erase_block_regions[i].
                    number_of_erase_blocks = y + 1;
            }
        }

        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)
        {
            urj_flash_cfi_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);
                write1 (0, CFI_CMD_READ_ARRAY2);
                urj_error_set (URJ_ERROR_FLASH,
                               "CFI primary vendor table not detected");
                return URJ_STATUS_FAIL;
            }

            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 = calloc (1,
                    sizeof (urj_flash_cfi_amd_pri_extened_query_structure_t)
                            + num_of_banks * sizeof (uint8_t));
            if (!pri_vendor_tbl)
            {
                write1 (0, CFI_CMD_READ_ARRAY1);
                write1 (0, CFI_CMD_READ_ARRAY2);
                urj_error_set (URJ_ERROR_OUT_OF_MEMORY, "calloc(%zd,%zd) fails",
                               (size_t) 1,
                               sizeof (urj_flash_cfi_amd_pri_extened_query_structure_t)
                                   + num_of_banks * sizeof (uint8_t));
                return URJ_STATUS_FAIL;
            }

            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;
                }
            }
        }