Skip to content
Snippets Groups Projects
cfi.c 83.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • /***************************************************************************
     *   Copyright (C) 2005, 2007 by Dominic Rath                              *
     *   Dominic.Rath@gmx.de                                                   *
    
     *   Copyright (C) 2009 Michael Schwingen                                  *
     *   michael@schwingen.org                                                 *
    
     *                                                                         *
     *   This program is free software; you can redistribute it and/or modify  *
     *   it under the terms of the GNU General Public License as published by  *
     *   the Free Software Foundation; either version 2 of the License, or     *
     *   (at your option) any later version.                                   *
     *                                                                         *
     *   This program is distributed in the hope that it will be useful,       *
     *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
     *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
     *   GNU General Public License for more details.                          *
     *                                                                         *
     *   You should have received a copy of the GNU General Public License     *
     *   along with this program; if not, write to the                         *
     *   Free Software Foundation, Inc.,                                       *
     *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
     ***************************************************************************/
    #ifdef HAVE_CONFIG_H
    #include "config.h"
    #endif
    
    #include "cfi.h"
    
    #include "armv4_5.h"
    #include "binarybuffer.h"
    
    
    
    mifi's avatar
    mifi committed
    static int cfi_register_commands(struct command_context_s *cmd_ctx);
    static int cfi_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank);
    static int cfi_erase(struct flash_bank_s *bank, int first, int last);
    static int cfi_protect(struct flash_bank_s *bank, int set, int first, int last);
    
    static int cfi_write(struct flash_bank_s *bank, uint8_t *buffer, uint32_t offset, uint32_t count);
    
    mifi's avatar
    mifi committed
    static int cfi_probe(struct flash_bank_s *bank);
    static int cfi_auto_probe(struct flash_bank_s *bank);
    static int cfi_protect_check(struct flash_bank_s *bank);
    static int cfi_info(struct flash_bank_s *bank, char *buf, int buf_size);
    
    //static int cfi_handle_part_id_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
    
    
    #define CFI_MAX_BUS_WIDTH	4
    #define CFI_MAX_CHIP_WIDTH	4
    
    /* defines internal maximum size for code fragment in cfi_intel_write_block() */
    #define CFI_MAX_INTEL_CODESIZE 256
    
    flash_driver_t cfi_flash =
    {
    	.name = "cfi",
    	.register_commands = cfi_register_commands,
    	.flash_bank_command = cfi_flash_bank_command,
    	.erase = cfi_erase,
    	.protect = cfi_protect,
    	.write = cfi_write,
    	.probe = cfi_probe,
    	.auto_probe = cfi_auto_probe,
    
    oharboe's avatar
     
    oharboe committed
    	.erase_check = default_flash_blank_check,
    
    	.protect_check = cfi_protect_check,
    	.info = cfi_info
    };
    
    
    mifi's avatar
    mifi committed
    static cfi_unlock_addresses_t cfi_unlock_addresses[] =
    
    {
    	[CFI_UNLOCK_555_2AA] = { .unlock1 = 0x555, .unlock2 = 0x2aa },
    	[CFI_UNLOCK_5555_2AAA] = { .unlock1 = 0x5555, .unlock2 = 0x2aaa },
    };
    
    /* CFI fixups foward declarations */
    
    mifi's avatar
    mifi committed
    static void cfi_fixup_0002_erase_regions(flash_bank_t *flash, void *param);
    static void cfi_fixup_0002_unlock_addresses(flash_bank_t *flash, void *param);
    static void cfi_fixup_atmel_reversed_erase_regions(flash_bank_t *flash, void *param);
    
    
    /* fixup after reading cmdset 0002 primary query table */
    
    static const cfi_fixup_t cfi_0002_fixups[] = {
    
    	{CFI_MFR_SST, 0x00D4, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
    	{CFI_MFR_SST, 0x00D5, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
    	{CFI_MFR_SST, 0x00D6, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
    	{CFI_MFR_SST, 0x00D7, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
    	{CFI_MFR_SST, 0x2780, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
    	{CFI_MFR_ATMEL, 0x00C8, cfi_fixup_atmel_reversed_erase_regions, NULL},
    
    	{CFI_MFR_FUJITSU, 0x226b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_5555_2AAA]},
    
    	{CFI_MFR_AMIC, 0xb31a, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]},
    
    	{CFI_MFR_MX, 0x225b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]},
    
    	{CFI_MFR_AMD, 0x225b, cfi_fixup_0002_unlock_addresses, &cfi_unlock_addresses[CFI_UNLOCK_555_2AA]},
    
    	{CFI_MFR_ANY, CFI_ID_ANY, cfi_fixup_0002_erase_regions, NULL},
    	{0, 0, NULL, NULL}
    };
    
    /* fixup after reading cmdset 0001 primary query table */
    
    static const cfi_fixup_t cfi_0001_fixups[] = {
    
    	{0, 0, NULL, NULL}
    };
    
    
    static void cfi_fixup(flash_bank_t *bank, const cfi_fixup_t *fixups)
    
    {
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    
    	for (f = fixups; f->fixup; f++)
    	{
    		if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi_info->manufacturer)) &&
    			((f->id  == CFI_ID_ANY)  || (f->id  == cfi_info->device_id)))
    		{
    			f->fixup(bank, f->param);
    		}
    	}
    }
    
    
    /* inline uint32_t flash_address(flash_bank_t *bank, int sector, uint32_t offset) */
    static __inline__ uint32_t flash_address(flash_bank_t *bank, int sector, uint32_t offset)
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    
    zwelch's avatar
    zwelch committed
    	if (cfi_info->x16_as_x8) offset *= 2;
    
    	/* while the sector list isn't built, only accesses to sector 0 work */
    	if (sector == 0)
    
    		return bank->base + offset * bank->bus_width;
    
    	else
    	{
    		if (!bank->sectors)
    		{
    
    			LOG_ERROR("BUG: sector list not yet built");
    
    		return bank->base + bank->sectors[sector].offset + offset * bank->bus_width;
    
    static void cfi_command(flash_bank_t *bank, uint8_t cmd, uint8_t *cmd_buf)
    
    {
    	int i;
    
    	/* clear whole buffer, to ensure bits that exceed the bus_width
    	 * are set to zero
    	 */
    	for (i = 0; i < CFI_MAX_BUS_WIDTH; i++)
    		cmd_buf[i] = 0;
    
    	if (bank->target->endianness == TARGET_LITTLE_ENDIAN)
    	{
    		for (i = bank->bus_width; i > 0; i--)
    		{
    			*cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd;
    		}
    	}
    	else
    	{
    		for (i = 1; i <= bank->bus_width; i++)
    		{
    			*cmd_buf++ = (i & (bank->chip_width - 1)) ? 0x0 : cmd;
    		}
    	}
    }
    
    /* read unsigned 8-bit value from the bank
     * flash banks are expected to be made of similar chips
     * the query result should be the same for all
     */
    
    static uint8_t cfi_query_u8(flash_bank_t *bank, int sector, uint32_t offset)
    
    {
    	target_t *target = bank->target;
    
    	uint8_t data[CFI_MAX_BUS_WIDTH];
    
    zwelch's avatar
    zwelch committed
    	target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data);
    
    
    	if (bank->target->endianness == TARGET_LITTLE_ENDIAN)
    		return data[0];
    	else
    		return data[bank->bus_width - 1];
    }
    
    /* read unsigned 8-bit value from the bank
     * in case of a bank made of multiple chips,
     * the individual values are ORed
     */
    
    static uint8_t cfi_get_u8(flash_bank_t *bank, int sector, uint32_t offset)
    
    {
    	target_t *target = bank->target;
    
    	uint8_t data[CFI_MAX_BUS_WIDTH];
    
    zwelch's avatar
    zwelch committed
    	target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 1, data);
    
    
    	if (bank->target->endianness == TARGET_LITTLE_ENDIAN)
    	{
    		for (i = 0; i < bank->bus_width / bank->chip_width; i++)
    			data[0] |= data[i];
    
    		return data[0];
    	}
    	else
    	{
    
    		uint8_t value = 0;
    
    		for (i = 0; i < bank->bus_width / bank->chip_width; i++)
    			value |= data[bank->bus_width - 1 - i];
    
    		return value;
    	}
    }
    
    
    static uint16_t cfi_query_u16(flash_bank_t *bank, int sector, uint32_t offset)
    
    {
    	target_t *target = bank->target;
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    	uint8_t data[CFI_MAX_BUS_WIDTH * 2];
    
    zwelch's avatar
    zwelch committed
    	if (cfi_info->x16_as_x8)
    
    		uint8_t i;
    
    zwelch's avatar
    zwelch committed
    		for (i = 0;i < 2;i++)
    
    zwelch's avatar
    zwelch committed
    			target_read_memory(target, flash_address(bank, sector, offset + i), bank->bus_width, 1,
    
    				&data[i*bank->bus_width]);
    
    zwelch's avatar
    zwelch committed
    		target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 2, data);
    
    
    	if (bank->target->endianness == TARGET_LITTLE_ENDIAN)
    		return data[0] | data[bank->bus_width] << 8;
    	else
    		return data[bank->bus_width - 1] | data[(2 * bank->bus_width) - 1] << 8;
    }
    
    
    static uint32_t cfi_query_u32(flash_bank_t *bank, int sector, uint32_t offset)
    
    {
    	target_t *target = bank->target;
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    	uint8_t data[CFI_MAX_BUS_WIDTH * 4];
    
    zwelch's avatar
    zwelch committed
    	if (cfi_info->x16_as_x8)
    
    		uint8_t i;
    
    zwelch's avatar
    zwelch committed
    		for (i = 0;i < 4;i++)
    
    zwelch's avatar
    zwelch committed
    			target_read_memory(target, flash_address(bank, sector, offset + i), bank->bus_width, 1,
    
    				&data[i*bank->bus_width]);
    
    zwelch's avatar
    zwelch committed
    		target_read_memory(target, flash_address(bank, sector, offset), bank->bus_width, 4, data);
    
    
    	if (bank->target->endianness == TARGET_LITTLE_ENDIAN)
    		return data[0] | data[bank->bus_width] << 8 | data[bank->bus_width * 2] << 16 | data[bank->bus_width * 3] << 24;
    	else
    		return data[bank->bus_width - 1] | data[(2* bank->bus_width) - 1] << 8 |
    				data[(3 * bank->bus_width) - 1] << 16 | data[(4 * bank->bus_width) - 1] << 24;
    }
    
    
    mifi's avatar
    mifi committed
    static void cfi_intel_clear_status_register(flash_bank_t *bank)
    
    {
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    
    	if (target->state != TARGET_HALTED)
    	{
    
    		LOG_ERROR("BUG: attempted to clear status register while target wasn't halted");
    
    		exit(-1);
    	}
    
    	cfi_command(bank, 0x50, command);
    
    zwelch's avatar
    zwelch committed
    	target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
    
    uint8_t cfi_intel_wait_status_busy(flash_bank_t *bank, int timeout)
    
    	uint8_t status;
    
    
    	while ((!((status = cfi_get_u8(bank, 0, 0x0)) & 0x80)) && (timeout-- > 0))
    	{
    
    		LOG_DEBUG("status: 0x%x", status);
    
    	}
    
    	/* mask out bit 0 (reserved) */
    	status = status & 0xfe;
    
    
    	LOG_DEBUG("status: 0x%x", status);
    
    
    	if ((status & 0x80) != 0x80)
    	{
    
    		LOG_ERROR("timeout while waiting for WSM to become ready");
    
    	}
    	else if (status != 0x80)
    	{
    
    		LOG_ERROR("status register: 0x%x", status);
    
    		if (status & 0x2)
    
    			LOG_ERROR("Block Lock-Bit Detected, Operation Abort");
    
    		if (status & 0x4)
    
    			LOG_ERROR("Program suspended");
    
    		if (status & 0x8)
    
    			LOG_ERROR("Low Programming Voltage Detected, Operation Aborted");
    
    		if (status & 0x10)
    
    			LOG_ERROR("Program Error / Error in Setting Lock-Bit");
    
    		if (status & 0x20)
    
    			LOG_ERROR("Error in Block Erasure or Clear Lock-Bits");
    
    		if (status & 0x40)
    
    			LOG_ERROR("Block Erase Suspended");
    
    
    		cfi_intel_clear_status_register(bank);
    	}
    
    	return status;
    }
    
    int cfi_spansion_wait_status_busy(flash_bank_t *bank, int timeout)
    {
    
    	uint8_t status, oldstatus;
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    
    	oldstatus = cfi_get_u8(bank, 0, 0x0);
    
    	do {
    		status = cfi_get_u8(bank, 0, 0x0);
    		if ((status ^ oldstatus) & 0x40) {
    
    			if (status & cfi_info->status_poll_mask & 0x20) {
    
    				oldstatus = cfi_get_u8(bank, 0, 0x0);
    				status = cfi_get_u8(bank, 0, 0x0);
    				if ((status ^ oldstatus) & 0x40) {
    
    					LOG_ERROR("dq5 timeout, status: 0x%x", status);
    
    					return(ERROR_FLASH_OPERATION_FAILED);
    				} else {
    
    					LOG_DEBUG("status: 0x%x", status);
    
    					return(ERROR_OK);
    				}
    			}
    
    		} else { /* no toggle: finished, OK */
    
    			LOG_DEBUG("status: 0x%x", status);
    
    			return(ERROR_OK);
    		}
    
    		oldstatus = status;
    
    	} while (timeout-- > 0);
    
    
    	LOG_ERROR("timeout, status: 0x%x", status);
    
    
    	return(ERROR_FLASH_BUSY);
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_read_intel_pri_ext(flash_bank_t *bank)
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_intel_pri_ext_t *pri_ext = malloc(sizeof(cfi_intel_pri_ext_t));
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    
    	cfi_info->pri_ext = pri_ext;
    
    	pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0);
    	pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1);
    	pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2);
    
    	if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I'))
    	{
    		cfi_command(bank, 0xf0, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    		cfi_command(bank, 0xff, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    		LOG_ERROR("Could not read bank flash bank information");
    
    		return ERROR_FLASH_BANK_INVALID;
    	}
    
    	pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3);
    	pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4);
    
    
    	LOG_DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version);
    
    
    	pri_ext->feature_support = cfi_query_u32(bank, 0, cfi_info->pri_addr + 5);
    	pri_ext->suspend_cmd_support = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9);
    	pri_ext->blk_status_reg_mask = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xa);
    
    
    	LOG_DEBUG("feature_support: 0x%" PRIx32 ", suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x",
    		  pri_ext->feature_support,
    		  pri_ext->suspend_cmd_support,
    
    duane's avatar
    duane committed
    		  pri_ext->blk_status_reg_mask);
    
    
    	pri_ext->vcc_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xc);
    	pri_ext->vpp_optimal = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xd);
    
    
    	LOG_DEBUG("Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x",
    
    		  (pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f,
    		  (pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f);
    
    	pri_ext->num_protection_fields = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0xe);
    	if (pri_ext->num_protection_fields != 1)
    	{
    
    		LOG_WARNING("expected one protection register field, but found %i", pri_ext->num_protection_fields);
    
    	}
    
    	pri_ext->prot_reg_addr = cfi_query_u16(bank, 0, cfi_info->pri_addr + 0xf);
    	pri_ext->fact_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x11);
    	pri_ext->user_prot_reg_size = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0x12);
    
    
    	LOG_DEBUG("protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size);
    
    mifi's avatar
    mifi committed
    static int cfi_read_spansion_pri_ext(flash_bank_t *bank)
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_spansion_pri_ext_t *pri_ext = malloc(sizeof(cfi_spansion_pri_ext_t));
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    
    	cfi_info->pri_ext = pri_ext;
    
    	pri_ext->pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0);
    	pri_ext->pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1);
    	pri_ext->pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2);
    
    	if ((pri_ext->pri[0] != 'P') || (pri_ext->pri[1] != 'R') || (pri_ext->pri[2] != 'I'))
    	{
    		cfi_command(bank, 0xf0, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    		LOG_ERROR("Could not read spansion bank information");
    
    		return ERROR_FLASH_BANK_INVALID;
    	}
    
    	pri_ext->major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3);
    	pri_ext->minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4);
    
    
    	LOG_DEBUG("pri: '%c%c%c', version: %c.%c", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version);
    
    
    	pri_ext->SiliconRevision = cfi_query_u8(bank, 0, cfi_info->pri_addr + 5);
    	pri_ext->EraseSuspend    = cfi_query_u8(bank, 0, cfi_info->pri_addr + 6);
    	pri_ext->BlkProt         = cfi_query_u8(bank, 0, cfi_info->pri_addr + 7);
    	pri_ext->TmpBlkUnprotect = cfi_query_u8(bank, 0, cfi_info->pri_addr + 8);
    	pri_ext->BlkProtUnprot   = cfi_query_u8(bank, 0, cfi_info->pri_addr + 9);
    	pri_ext->SimultaneousOps = cfi_query_u8(bank, 0, cfi_info->pri_addr + 10);
    	pri_ext->BurstMode       = cfi_query_u8(bank, 0, cfi_info->pri_addr + 11);
    	pri_ext->PageMode        = cfi_query_u8(bank, 0, cfi_info->pri_addr + 12);
    	pri_ext->VppMin          = cfi_query_u8(bank, 0, cfi_info->pri_addr + 13);
    	pri_ext->VppMax          = cfi_query_u8(bank, 0, cfi_info->pri_addr + 14);
    	pri_ext->TopBottom       = cfi_query_u8(bank, 0, cfi_info->pri_addr + 15);
    
    
    	LOG_DEBUG("Silicon Revision: 0x%x, Erase Suspend: 0x%x, Block protect: 0x%x", pri_ext->SiliconRevision,
    
    	      pri_ext->EraseSuspend, pri_ext->BlkProt);
    
    
    	LOG_DEBUG("Temporary Unprotect: 0x%x, Block Protect Scheme: 0x%x, Simultaneous Ops: 0x%x", pri_ext->TmpBlkUnprotect,
    
    	      pri_ext->BlkProtUnprot, pri_ext->SimultaneousOps);
    
    
    	LOG_DEBUG("Burst Mode: 0x%x, Page Mode: 0x%x, ", pri_ext->BurstMode, pri_ext->PageMode);
    
    	LOG_DEBUG("Vpp min: %2.2d.%1.1d, Vpp max: %2.2d.%1.1x",
    
    		  (pri_ext->VppMin & 0xf0) >> 4, pri_ext->VppMin & 0x0f,
    		  (pri_ext->VppMax & 0xf0) >> 4, pri_ext->VppMax & 0x0f);
    
    
    	LOG_DEBUG("WP# protection 0x%x", pri_ext->TopBottom);
    
    
    	/* default values for implementation specific workarounds */
    	pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1;
    	pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2;
    	pri_ext->_reversed_geometry = 0;
    
    	return ERROR_OK;
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_read_atmel_pri_ext(flash_bank_t *bank)
    
    	cfi_atmel_pri_ext_t atmel_pri_ext;
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_spansion_pri_ext_t *pri_ext = malloc(sizeof(cfi_spansion_pri_ext_t));
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    
    	/* ATMEL devices use the same CFI primary command set (0x2) as AMD/Spansion,
    	 * but a different primary extended query table.
    	 * We read the atmel table, and prepare a valid AMD/Spansion query table.
    	 */
    
    	memset(pri_ext, 0, sizeof(cfi_spansion_pri_ext_t));
    
    	cfi_info->pri_ext = pri_ext;
    
    	atmel_pri_ext.pri[0] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 0);
    	atmel_pri_ext.pri[1] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 1);
    	atmel_pri_ext.pri[2] = cfi_query_u8(bank, 0, cfi_info->pri_addr + 2);
    
    	if ((atmel_pri_ext.pri[0] != 'P') || (atmel_pri_ext.pri[1] != 'R') || (atmel_pri_ext.pri[2] != 'I'))
    	{
    		cfi_command(bank, 0xf0, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    		LOG_ERROR("Could not read atmel bank information");
    
    		return ERROR_FLASH_BANK_INVALID;
    	}
    
    	pri_ext->pri[0] = atmel_pri_ext.pri[0];
    	pri_ext->pri[1] = atmel_pri_ext.pri[1];
    	pri_ext->pri[2] = atmel_pri_ext.pri[2];
    
    	atmel_pri_ext.major_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 3);
    	atmel_pri_ext.minor_version = cfi_query_u8(bank, 0, cfi_info->pri_addr + 4);
    
    
    	LOG_DEBUG("pri: '%c%c%c', version: %c.%c", atmel_pri_ext.pri[0], atmel_pri_ext.pri[1], atmel_pri_ext.pri[2], atmel_pri_ext.major_version, atmel_pri_ext.minor_version);
    
    
    	pri_ext->major_version = atmel_pri_ext.major_version;
    	pri_ext->minor_version = atmel_pri_ext.minor_version;
    
    	atmel_pri_ext.features = cfi_query_u8(bank, 0, cfi_info->pri_addr + 5);
    	atmel_pri_ext.bottom_boot = cfi_query_u8(bank, 0, cfi_info->pri_addr + 6);
    	atmel_pri_ext.burst_mode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 7);
    	atmel_pri_ext.page_mode = cfi_query_u8(bank, 0, cfi_info->pri_addr + 8);
    
    
    	LOG_DEBUG("features: 0x%2.2x, bottom_boot: 0x%2.2x, burst_mode: 0x%2.2x, page_mode: 0x%2.2x",
    
    		atmel_pri_ext.features, atmel_pri_ext.bottom_boot, atmel_pri_ext.burst_mode, atmel_pri_ext.page_mode);
    
    	if (atmel_pri_ext.features & 0x02)
    		pri_ext->EraseSuspend = 2;
    
    	if (atmel_pri_ext.bottom_boot)
    		pri_ext->TopBottom = 2;
    	else
    		pri_ext->TopBottom = 3;
    
    	pri_ext->_unlock1 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock1;
    	pri_ext->_unlock2 = cfi_unlock_addresses[CFI_UNLOCK_555_2AA].unlock2;
    
    	return ERROR_OK;
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_read_0002_pri_ext(flash_bank_t *bank)
    
    {
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    	if (cfi_info->manufacturer == CFI_MFR_ATMEL)
    	{
    		return cfi_read_atmel_pri_ext(bank);
    	}
    	else
    	{
    		return cfi_read_spansion_pri_ext(bank);
    	}
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_spansion_info(struct flash_bank_s *bank, char *buf, int buf_size)
    
    {
    	int printed;
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_spansion_pri_ext_t *pri_ext = cfi_info->pri_ext;
    
    	printed = snprintf(buf, buf_size, "\nSpansion primary algorithm extend information:\n");
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0],
    			   pri_ext->pri[1], pri_ext->pri[2],
    			   pri_ext->major_version, pri_ext->minor_version);
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "Silicon Rev.: 0x%x, Address Sensitive unlock: 0x%x\n",
    			   (pri_ext->SiliconRevision) >> 2,
    			   (pri_ext->SiliconRevision) & 0x03);
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "Erase Suspend: 0x%x, Sector Protect: 0x%x\n",
    			   pri_ext->EraseSuspend,
    			   pri_ext->BlkProt);
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "VppMin: %2.2d.%1.1x, VppMax: %2.2d.%1.1x\n",
    		(pri_ext->VppMin & 0xf0) >> 4, pri_ext->VppMin & 0x0f,
    		(pri_ext->VppMax & 0xf0) >> 4, pri_ext->VppMax & 0x0f);
    
    	return ERROR_OK;
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_intel_info(struct flash_bank_s *bank, char *buf, int buf_size)
    
    {
    	int printed;
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext;
    
    	printed = snprintf(buf, buf_size, "\nintel primary algorithm extend information:\n");
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "pri: '%c%c%c', version: %c.%c\n", pri_ext->pri[0], pri_ext->pri[1], pri_ext->pri[2], pri_ext->major_version, pri_ext->minor_version);
    	buf += printed;
    	buf_size -= printed;
    
    
    duane's avatar
    duane committed
    	printed = snprintf(buf, buf_size, "feature_support: 0x%" PRIx32 ", suspend_cmd_support: 0x%x, blk_status_reg_mask: 0x%x\n", pri_ext->feature_support, pri_ext->suspend_cmd_support, pri_ext->blk_status_reg_mask);
    
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "Vcc opt: %1.1x.%1.1x, Vpp opt: %1.1x.%1.1x\n",
    		(pri_ext->vcc_optimal & 0xf0) >> 4, pri_ext->vcc_optimal & 0x0f,
    		(pri_ext->vpp_optimal & 0xf0) >> 4, pri_ext->vpp_optimal & 0x0f);
    	buf += printed;
    	buf_size -= printed;
    
    	printed = snprintf(buf, buf_size, "protection_fields: %i, prot_reg_addr: 0x%x, factory pre-programmed: %i, user programmable: %i\n", pri_ext->num_protection_fields, pri_ext->prot_reg_addr, 1 << pri_ext->fact_prot_reg_size, 1 << pri_ext->user_prot_reg_size);
    
    	return ERROR_OK;
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_register_commands(struct command_context_s *cmd_ctx)
    
    {
    	/*command_t *cfi_cmd = */
    	register_command(cmd_ctx, NULL, "cfi", NULL, COMMAND_ANY, "flash bank cfi <base> <size> <chip_width> <bus_width> <targetNum> [jedec_probe/x16_as_x8]");
    	/*
    	register_command(cmd_ctx, cfi_cmd, "part_id", cfi_handle_part_id_command, COMMAND_EXEC,
    					 "print part id of cfi flash bank <num>");
    	*/
    	return ERROR_OK;
    }
    
    /* flash_bank cfi <base> <size> <chip_width> <bus_width> <target#> [options]
     */
    
    mifi's avatar
    mifi committed
    static int cfi_flash_bank_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc, struct flash_bank_s *bank)
    
    {
    	cfi_flash_bank_t *cfi_info;
    	int i;
    
    		LOG_WARNING("incomplete flash_bank cfi configuration");
    
    		return ERROR_FLASH_BANK_INVALID;
    	}
    
    	if ((strtoul(args[4], NULL, 0) > CFI_MAX_CHIP_WIDTH)
    		|| (strtoul(args[3], NULL, 0) > CFI_MAX_BUS_WIDTH))
    	{
    
    		LOG_ERROR("chip and bus width have to specified in bytes");
    
    		return ERROR_FLASH_BANK_INVALID;
    	}
    
    	cfi_info = malloc(sizeof(cfi_flash_bank_t));
    	cfi_info->probed = 0;
    	bank->driver_priv = cfi_info;
    
    	cfi_info->write_algorithm = NULL;
    
    	cfi_info->x16_as_x8 = 0;
    	cfi_info->jedec_probe = 0;
    	cfi_info->not_cfi = 0;
    
    	for (i = 6; i < argc; i++)
    	{
    		if (strcmp(args[i], "x16_as_x8") == 0)
    		{
    			cfi_info->x16_as_x8 = 1;
    		}
    		else if (strcmp(args[i], "jedec_probe") == 0)
    		{
    			cfi_info->jedec_probe = 1;
    		}
    	}
    
    	cfi_info->write_algorithm = NULL;
    
    	/* bank wasn't probed yet */
    	cfi_info->qry[0] = -1;
    
    	return ERROR_OK;
    }
    
    
    mifi's avatar
    mifi committed
    static int cfi_intel_erase(struct flash_bank_s *bank, int first, int last)
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    	int i;
    
    	cfi_intel_clear_status_register(bank);
    
    	for (i = first; i <= last; i++)
    	{
    		cfi_command(bank, 0x20, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		cfi_command(bank, 0xd0, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		if (cfi_intel_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == 0x80)
    			bank->sectors[i].is_erased = 1;
    		else
    		{
    			cfi_command(bank, 0xff, command);
    
    zwelch's avatar
    zwelch committed
    			if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    duane's avatar
    duane committed
    			LOG_ERROR("couldn't erase block %i of flash bank at base 0x%" PRIx32 , i, bank->base);
    
    			return ERROR_FLASH_OPERATION_FAILED;
    		}
    	}
    
    	cfi_command(bank, 0xff, command);
    
    zwelch's avatar
    zwelch committed
    	return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
    
    mifi's avatar
    mifi committed
    static int cfi_spansion_erase(struct flash_bank_s *bank, int first, int last)
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_spansion_pri_ext_t *pri_ext = cfi_info->pri_ext;
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    	int i;
    
    	for (i = first; i <= last; i++)
    	{
    		cfi_command(bank, 0xaa, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		cfi_command(bank, 0x55, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		cfi_command(bank, 0x80, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		cfi_command(bank, 0xaa, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock1), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		cfi_command(bank, 0x55, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, 0, pri_ext->_unlock2), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		cfi_command(bank, 0x30, command);
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    		if (cfi_spansion_wait_status_busy(bank, 1000 * (1 << cfi_info->block_erase_timeout_typ)) == ERROR_OK)
    			bank->sectors[i].is_erased = 1;
    		else
    		{
    			cfi_command(bank, 0xf0, command);
    
    zwelch's avatar
    zwelch committed
    			if ((retval = target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    duane's avatar
    duane committed
    			LOG_ERROR("couldn't erase block %i of flash bank at base 0x%" PRIx32, i, bank->base);
    
    			return ERROR_FLASH_OPERATION_FAILED;
    		}
    	}
    
    	cfi_command(bank, 0xf0, command);
    
    zwelch's avatar
    zwelch committed
    	return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
    
    mifi's avatar
    mifi committed
    static int cfi_erase(struct flash_bank_s *bank, int first, int last)
    
    {
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    	if (bank->target->state != TARGET_HALTED)
    	{
    
    oharboe's avatar
    oharboe committed
    		LOG_ERROR("Target not halted");
    
    		return ERROR_TARGET_NOT_HALTED;
    	}
    
    	if ((first < 0) || (last < first) || (last >= bank->num_sectors))
    	{
    		return ERROR_FLASH_SECTOR_INVALID;
    	}
    
    	if (cfi_info->qry[0] != 'Q')
    		return ERROR_FLASH_BANK_NOT_PROBED;
    
    
    	switch (cfi_info->pri_id)
    
    	{
    		case 1:
    		case 3:
    			return cfi_intel_erase(bank, first, last);
    			break;
    		case 2:
    			return cfi_spansion_erase(bank, first, last);
    			break;
    		default:
    
    			LOG_ERROR("cfi primary command set %i unsupported", cfi_info->pri_id);
    
    mifi's avatar
    mifi committed
    static int cfi_intel_protect(struct flash_bank_s *bank, int set, int first, int last)
    
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    	cfi_intel_pri_ext_t *pri_ext = cfi_info->pri_ext;
    	target_t *target = bank->target;
    
    	uint8_t command[8];
    
    	int retry = 0;
    	int i;
    
    	/* if the device supports neither legacy lock/unlock (bit 3) nor
    	 * instant individual block locking (bit 5).
    	 */
    	if (!(pri_ext->feature_support & 0x28))
    		return ERROR_FLASH_OPERATION_FAILED;
    
    	cfi_intel_clear_status_register(bank);
    
    	for (i = first; i <= last; i++)
    	{
    		cfi_command(bank, 0x60, command);
    
    duane's avatar
    duane committed
    		LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32, flash_address(bank, i, 0x0), target_buffer_get_u32(target, command));
    
    zwelch's avatar
    zwelch committed
    		if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    		if (set)
    		{
    			cfi_command(bank, 0x01, command);
    
    duane's avatar
    duane committed
    			LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32 , flash_address(bank, i, 0x0), target_buffer_get_u32(target, command));
    
    zwelch's avatar
    zwelch committed
    			if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    			bank->sectors[i].is_protected = 1;
    		}
    		else
    		{
    			cfi_command(bank, 0xd0, command);
    
    duane's avatar
    duane committed
    			LOG_DEBUG("address: 0x%4.4" PRIx32 ", command: 0x%4.4" PRIx32, flash_address(bank, i, 0x0), target_buffer_get_u32(target, command));
    
    zwelch's avatar
    zwelch committed
    			if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    			bank->sectors[i].is_protected = 0;
    		}
    
    		/* instant individual block locking doesn't require reading of the status register */
    		if (!(pri_ext->feature_support & 0x20))
    		{
    			/* Clear lock bits operation may take up to 1.4s */
    			cfi_intel_wait_status_busy(bank, 1400);
    		}
    		else
    		{
    
    			uint8_t block_status;
    
    			/* read block lock bit, to verify status */
    			cfi_command(bank, 0x90, command);
    
    zwelch's avatar
    zwelch committed
    			if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK)
    
    			block_status = cfi_get_u8(bank, i, 0x2);
    
    			if ((block_status & 0x1) != set)
    			{
    
    				LOG_ERROR("couldn't change block lock status (set = %i, block_status = 0x%2.2x)", set, block_status);
    
    				cfi_command(bank, 0x70, command);
    
    zwelch's avatar
    zwelch committed
    				if ((retval = target_write_memory(target, flash_address(bank, 0, 0x55), bank->bus_width, 1, command)) != ERROR_OK)
    
    				cfi_intel_wait_status_busy(bank, 10);
    
    				if (retry > 10)
    					return ERROR_FLASH_OPERATION_FAILED;
    				else
    				{
    					i--;
    					retry++;
    				}
    			}
    		}
    	}
    
    	/* if the device doesn't support individual block lock bits set/clear,
    	 * all blocks have been unlocked in parallel, so we set those that should be protected
    	 */
    	if ((!set) && (!(pri_ext->feature_support & 0x20)))
    	{
    		for (i = 0; i < bank->num_sectors; i++)
    		{
    			if (bank->sectors[i].is_protected == 1)
    			{
    				cfi_intel_clear_status_register(bank);
    
    				cfi_command(bank, 0x60, command);
    
    zwelch's avatar
    zwelch committed
    				if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    				cfi_command(bank, 0x01, command);
    
    zwelch's avatar
    zwelch committed
    				if ((retval = target_write_memory(target, flash_address(bank, i, 0x0), bank->bus_width, 1, command)) != ERROR_OK)
    
    
    				cfi_intel_wait_status_busy(bank, 100);
    			}
    		}
    	}
    
    	cfi_command(bank, 0xff, command);
    
    zwelch's avatar
    zwelch committed
    	return target_write_memory(target, flash_address(bank, 0, 0x0), bank->bus_width, 1, command);
    
    mifi's avatar
    mifi committed
    static int cfi_protect(struct flash_bank_s *bank, int set, int first, int last)
    
    {
    	cfi_flash_bank_t *cfi_info = bank->driver_priv;
    
    	if (bank->target->state != TARGET_HALTED)
    	{
    
    oharboe's avatar
    oharboe committed
    		LOG_ERROR("Target not halted");
    
    		return ERROR_TARGET_NOT_HALTED;
    	}
    
    	if ((first < 0) || (last < first) || (last >= bank->num_sectors))
    	{
    		return ERROR_FLASH_SECTOR_INVALID;
    	}
    
    	if (cfi_info->qry[0] != 'Q')
    		return ERROR_FLASH_BANK_NOT_PROBED;
    
    
    	switch (cfi_info->pri_id)
    
    	{
    		case 1:
    		case 3:
    			cfi_intel_protect(bank, set, first, last);
    			break;
    		default:
    
    			LOG_ERROR("protect: cfi primary command set %i unsupported", cfi_info->pri_id);
    
    			break;
    	}
    
    	return ERROR_OK;
    }
    
    /* FIXME Replace this by a simple memcpy() - still unsure about sideeffects */
    
    static void cfi_add_byte(struct flash_bank_s *bank, uint8_t *word, uint8_t byte)
    
    	/* target_t *target = bank->target; */
    
    	/* NOTE:
    	 * The data to flash must not be changed in endian! We write a bytestrem in
    	 * target byte order already. Only the control and status byte lane of the flash
    
    	 * WSM is interpreted by the CPU in different ways, when read a uint16_t or uint32_t
    
    zwelch's avatar
    zwelch committed
    	 * word (data seems to be in the upper or lower byte lane for uint16_t accesses).
    
    #if 0
    	if (target->endianness == TARGET_LITTLE_ENDIAN)
    	{
    #endif
    
    		/* shift bytes */
    		for (i = 0; i < bank->bus_width - 1; i++)
    			word[i] = word[i + 1];
    		word[bank->bus_width - 1] = byte;