diff options
author | Tim Newsome <tim@sifive.com> | 2018-02-02 13:36:58 -0800 |
---|---|---|
committer | Tim Newsome <tim@sifive.com> | 2018-02-02 14:17:32 -0800 |
commit | 706af27eeeddb8d8458d93acc594f09b5be0c24b (patch) | |
tree | 3d8f412d5fcc7f796437cffe7c410184387b51f3 /src/flash | |
parent | 6e278cded601044fb3e2e54a717bddb029b8ca1c (diff) | |
parent | c2b2a7a3b84913465420ae7fa0394304943cf035 (diff) | |
download | riscv-openocd-706af27eeeddb8d8458d93acc594f09b5be0c24b.zip riscv-openocd-706af27eeeddb8d8458d93acc594f09b5be0c24b.tar.gz riscv-openocd-706af27eeeddb8d8458d93acc594f09b5be0c24b.tar.bz2 |
Merge branch 'master' into update
Change-Id: I2cd34ed5bb1903736ae8ce109acebaf13bf49805
Diffstat (limited to 'src/flash')
-rw-r--r-- | src/flash/nor/Makefile.am | 1 | ||||
-rw-r--r-- | src/flash/nor/drivers.c | 2 | ||||
-rw-r--r-- | src/flash/nor/jtagspi.c | 67 | ||||
-rw-r--r-- | src/flash/nor/kinetis.c | 66 | ||||
-rw-r--r-- | src/flash/nor/nrf5.c | 1 | ||||
-rw-r--r-- | src/flash/nor/stm32l4x.c | 3 | ||||
-rw-r--r-- | src/flash/nor/xcf.c | 897 | ||||
-rw-r--r-- | src/flash/startup.tcl | 7 |
8 files changed, 1012 insertions, 32 deletions
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 8a57f4f..cc72088 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -56,6 +56,7 @@ NOR_DRIVERS = \ %D%/str9xpec.c \ %D%/tms470.c \ %D%/virtual.c \ + %D%/xcf.c \ %D%/xmc1xxx.c \ %D%/xmc4xxx.c diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 3f0c3c7..8168011 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -68,6 +68,7 @@ extern struct flash_driver str9x_flash; extern struct flash_driver str9xpec_flash; extern struct flash_driver tms470_flash; extern struct flash_driver virtual_flash; +extern struct flash_driver xcf_flash; extern struct flash_driver xmc1xxx_flash; extern struct flash_driver xmc4xxx_flash; @@ -124,6 +125,7 @@ static struct flash_driver *flash_drivers[] = { &str9xpec_flash, &tms470_flash, &virtual_flash, + &xcf_flash, &xmc1xxx_flash, &xmc4xxx_flash, NULL, diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c index a995fc7..a73812d 100644 --- a/src/flash/nor/jtagspi.c +++ b/src/flash/nor/jtagspi.c @@ -32,14 +32,13 @@ struct jtagspi_flash_bank { const struct flash_device *dev; int probed; uint32_t ir; - uint32_t dr_len; }; FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) { struct jtagspi_flash_bank *info; - if (CMD_ARGC < 8) + if (CMD_ARGC < 7) return ERROR_COMMAND_SYNTAX_ERROR; info = malloc(sizeof(struct jtagspi_flash_bank)); @@ -52,7 +51,6 @@ FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) info->tap = NULL; info->probed = 0; COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir); - COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->dr_len); return ERROR_OK; } @@ -63,9 +61,6 @@ static void jtagspi_set_ir(struct flash_bank *bank) struct scan_field field; uint8_t buf[4]; - if (buf_get_u32(info->tap->cur_instr, 0, info->tap->ir_length) == info->ir) - return; - LOG_DEBUG("loading jtagspi ir"); buf_set_u32(buf, 0, info->tap->ir_length, info->ir); field.num_bits = info->tap->ir_length; @@ -84,28 +79,53 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, uint32_t *addr, uint8_t *data, int len) { struct jtagspi_flash_bank *info = bank->driver_priv; - struct scan_field fields[3]; - uint8_t cmd_buf[4]; + struct scan_field fields[6]; + uint8_t marker = 1; + uint8_t xfer_bits_buf[4]; + uint8_t addr_buf[3]; uint8_t *data_buf; + uint32_t xfer_bits; int is_read, lenb, n; /* LOG_DEBUG("cmd=0x%02x len=%i", cmd, len); */ + is_read = (len < 0); + if (is_read) + len = -len; + n = 0; + + fields[n].num_bits = 1; + fields[n].out_value = ▮ + fields[n].in_value = NULL; + n++; + + xfer_bits = 8 + len - 1; + /* cmd + read/write - 1 due to the counter implementation */ + if (addr) + xfer_bits += 24; + h_u32_to_be(xfer_bits_buf, xfer_bits); + flip_u8(xfer_bits_buf, xfer_bits_buf, 4); + fields[n].num_bits = 32; + fields[n].out_value = xfer_bits_buf; + fields[n].in_value = NULL; + n++; + + cmd = flip_u32(cmd, 8); fields[n].num_bits = 8; - cmd_buf[0] = cmd; - if (addr) { - h_u24_to_be(cmd_buf + 1, *addr); - fields[n].num_bits += 24; - } - flip_u8(cmd_buf, cmd_buf, 4); - fields[n].out_value = cmd_buf; + fields[n].out_value = &cmd; fields[n].in_value = NULL; n++; - is_read = (len < 0); - if (is_read) - len = -len; + if (addr) { + h_u24_to_be(addr_buf, *addr); + flip_u8(addr_buf, addr_buf, 3); + fields[n].num_bits = 24; + fields[n].out_value = addr_buf; + fields[n].in_value = NULL; + n++; + } + lenb = DIV_ROUND_UP(len, 8); data_buf = malloc(lenb); if (lenb > 0) { @@ -114,10 +134,11 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, return ERROR_FAIL; } if (is_read) { - fields[n].num_bits = info->dr_len; + fields[n].num_bits = jtag_tap_count_enabled(); fields[n].out_value = NULL; fields[n].in_value = NULL; n++; + fields[n].out_value = NULL; fields[n].in_value = data_buf; } else { @@ -130,6 +151,7 @@ static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, } jtagspi_set_ir(bank); + /* passing from an IR scan to SHIFT-DR clears BYPASS registers */ jtag_add_dr_scan(info->tap, n, fields, TAP_IDLE); jtag_execute_queue(); @@ -202,9 +224,10 @@ static int jtagspi_probe(struct flash_bank *bank) static void jtagspi_read_status(struct flash_bank *bank, uint32_t *status) { uint8_t buf; - jtagspi_cmd(bank, SPIFLASH_READ_STATUS, NULL, &buf, -8); - *status = buf; - /* LOG_DEBUG("status=0x%08" PRIx32, *status); */ + if (jtagspi_cmd(bank, SPIFLASH_READ_STATUS, NULL, &buf, -8) == ERROR_OK) { + *status = buf; + /* LOG_DEBUG("status=0x%08" PRIx32, *status); */ + } } static int jtagspi_wait(struct flash_bank *bank, int timeout_ms) diff --git a/src/flash/nor/kinetis.c b/src/flash/nor/kinetis.c index 5c0ffbd..48a5de4 100644 --- a/src/flash/nor/kinetis.c +++ b/src/flash/nor/kinetis.c @@ -287,6 +287,7 @@ struct kinetis_chip { FS_NO_CMD_BLOCKSTAT = 0x40, FS_WIDTH_256BIT = 0x80, + FS_ECC = 0x100, } flash_support; enum { @@ -388,6 +389,7 @@ static const struct kinetis_type kinetis_types_old[] = { static bool allow_fcf_writes; static uint8_t fcf_fopt = 0xff; +static bool fcf_fopt_configured; static bool create_banks; @@ -1881,9 +1883,13 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer, { int result; bool set_fcf = false; + bool fcf_in_data_valid = false; int sect = 0; struct kinetis_flash_bank *k_bank = bank->driver_priv; struct kinetis_chip *k_chip = k_bank->k_chip; + uint8_t fcf_buffer[FCF_SIZE]; + uint8_t fcf_current[FCF_SIZE]; + uint8_t fcf_in_data[FCF_SIZE]; result = kinetis_check_run_mode(k_chip); if (result != ERROR_OK) @@ -1904,11 +1910,41 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer, } if (set_fcf) { - uint8_t fcf_buffer[FCF_SIZE]; - uint8_t fcf_current[FCF_SIZE]; - kinetis_fill_fcf(bank, fcf_buffer); + fcf_in_data_valid = offset <= FCF_ADDRESS + && offset + count >= FCF_ADDRESS + FCF_SIZE; + if (fcf_in_data_valid) { + memcpy(fcf_in_data, buffer + FCF_ADDRESS - offset, FCF_SIZE); + if (memcmp(fcf_in_data + FCF_FPROT, fcf_buffer, 4)) { + fcf_in_data_valid = false; + LOG_INFO("Flash protection requested in programmed file differs from current setting."); + } + if (fcf_in_data[FCF_FDPROT] != fcf_buffer[FCF_FDPROT]) { + fcf_in_data_valid = false; + LOG_INFO("Data flash protection requested in programmed file differs from current setting."); + } + if ((fcf_in_data[FCF_FSEC] & 3) != 2) { + fcf_in_data_valid = false; + LOG_INFO("Device security requested in programmed file!"); + } else if (k_chip->flash_support & FS_ECC + && fcf_in_data[FCF_FSEC] != fcf_buffer[FCF_FSEC]) { + fcf_in_data_valid = false; + LOG_INFO("Strange unsecure mode 0x%02" PRIx8 + "requested in programmed file!", + fcf_in_data[FCF_FSEC]); + } + if ((k_chip->flash_support & FS_ECC || fcf_fopt_configured) + && fcf_in_data[FCF_FOPT] != fcf_fopt) { + fcf_in_data_valid = false; + LOG_INFO("FOPT requested in programmed file differs from current setting."); + } + if (!fcf_in_data_valid) + LOG_INFO("Expect verify errors at FCF (0x408-0x40f)."); + } + } + + if (set_fcf && !fcf_in_data_valid) { if (offset < FCF_ADDRESS) { /* write part preceding FCF */ result = kinetis_write_inner(bank, buffer, offset, FCF_ADDRESS - offset); @@ -1937,9 +1973,10 @@ static int kinetis_write(struct flash_bank *bank, const uint8_t *buffer, } return result; - } else + } else { /* no FCF fiddling, normal write */ return kinetis_write_inner(bank, buffer, offset, count); + } } @@ -2146,10 +2183,21 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip) k_chip->nvm_sector_size = 4<<10; k_chip->max_flash_prog_size = 1<<10; num_blocks = 4; - k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR; + k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_ECC; cpu_mhz = 180; break; + case KINETIS_SDID_FAMILYID_K2X | KINETIS_SDID_SUBFAMID_KX7: + /* K27FN2M0 */ + case KINETIS_SDID_FAMILYID_K2X | KINETIS_SDID_SUBFAMID_KX8: + /* K28FN2M0 */ + k_chip->pflash_sector_size = 4<<10; + k_chip->max_flash_prog_size = 1<<10; + num_blocks = 4; + k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_ECC; + cpu_mhz = 150; + break; + case KINETIS_SDID_FAMILYID_K8X | KINETIS_SDID_SUBFAMID_KX0: case KINETIS_SDID_FAMILYID_K8X | KINETIS_SDID_SUBFAMID_KX1: case KINETIS_SDID_FAMILYID_K8X | KINETIS_SDID_SUBFAMID_KX2: @@ -2300,7 +2348,7 @@ static int kinetis_probe_chip(struct kinetis_chip *k_chip) k_chip->max_flash_prog_size = 1<<10; num_blocks = 1; maxaddr_shift = 14; - k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_WIDTH_256BIT; + k_chip->flash_support = FS_PROGRAM_PHRASE | FS_PROGRAM_SECTOR | FS_WIDTH_256BIT | FS_ECC; k_chip->pflash_base = 0x10000000; k_chip->progr_accel_ram = 0x18000000; cpu_mhz = 240; @@ -2959,10 +3007,12 @@ COMMAND_HANDLER(kinetis_fopt_handler) if (CMD_ARGC > 1) return ERROR_COMMAND_SYNTAX_ERROR; - if (CMD_ARGC == 1) + if (CMD_ARGC == 1) { fcf_fopt = (uint8_t)strtoul(CMD_ARGV[0], NULL, 0); - else + fcf_fopt_configured = true; + } else { command_print(CMD_CTX, "FCF_FOPT 0x%02" PRIx8, fcf_fopt); + } return ERROR_OK; } diff --git a/src/flash/nor/nrf5.c b/src/flash/nor/nrf5.c index 11e5729..4fa62e3 100644 --- a/src/flash/nor/nrf5.c +++ b/src/flash/nor/nrf5.c @@ -168,6 +168,7 @@ static const struct nrf5_device_spec nrf5_known_devices_table[] = { /* nRF51822 Devices (IC rev 3). */ NRF5_DEVICE_DEF(0x0072, "51822", "QFAA", "H0", 256), + NRF5_DEVICE_DEF(0x00D1, "51822", "QFAA", "H2", 256), NRF5_DEVICE_DEF(0x007B, "51822", "QFAB", "C0", 128), NRF5_DEVICE_DEF(0x0083, "51822", "QFAC", "A0", 256), NRF5_DEVICE_DEF(0x0084, "51822", "QFAC", "A1", 256), diff --git a/src/flash/nor/stm32l4x.c b/src/flash/nor/stm32l4x.c index fa0c48b..6a1fa07 100644 --- a/src/flash/nor/stm32l4x.c +++ b/src/flash/nor/stm32l4x.c @@ -652,6 +652,9 @@ static int stm32l4_probe(struct flash_bank *bank) /* get options to for DUAL BANK. */ retval = target_read_u32(target, STM32_FLASH_OPTR, &options); + if (retval != ERROR_OK) + return retval; + /* only devices with < 1024 kiB may be set to single bank dual banks */ if ((flash_size_in_kb == 1024) || !(options & OPT_DUALBANK)) stm32l4_info->option_bytes.bank_b_start = 256; diff --git a/src/flash/nor/xcf.c b/src/flash/nor/xcf.c new file mode 100644 index 0000000..035791e --- /dev/null +++ b/src/flash/nor/xcf.c @@ -0,0 +1,897 @@ +/*************************************************************************** + * Copyright (C) 2016 by Uladzimir Pylinski aka barthess * + * barthess@yandex.ru * + * * + * 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, see <http://www.gnu.org/licenses/>. * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "imp.h" +#include <jtag/jtag.h> +#include <helper/time_support.h> + +/* + ****************************************************************************** + * DEFINES + ****************************************************************************** + */ + +#define SECTOR_ERASE_TIMEOUT_MS (35 * 1000) + +#define XCF_PAGE_SIZE 32 +#define XCF_DATA_SECTOR_SIZE (1024 * 1024) + +#define ID_XCF01S 0x05044093 +#define ID_XCF02S 0x05045093 +#define ID_XCF04S 0x05046093 +#define ID_XCF08P 0x05057093 +#define ID_XCF16P 0x05058093 +#define ID_XCF32P 0x05059093 +#define ID_MEANINGFUL_MASK 0x0FFFFFFF + +const char *xcf_name_list[] = { + "XCF08P", + "XCF16P", + "XCF32P", + "unknown" +}; + +struct xcf_priv { + bool probed; +}; + +struct xcf_status { + bool isc_error; /* false == OK, true == error */ + bool prog_error; /* false == OK, true == error */ + bool prog_busy; /* false == idle, true == busy */ + bool isc_mode; /* false == normal mode, true == ISC mode */ +}; + +/* + ****************************************************************************** + * GLOBAL VARIABLES + ****************************************************************************** + */ +static const uint8_t CMD_BYPASS[2] = {0xFF, 0xFF}; + +static const uint8_t CMD_ISC_ADDRESS_SHIFT[2] = {0xEB, 0x00}; +static const uint8_t CMD_ISC_DATA_SHIFT[2] = {0xED, 0x00}; +static const uint8_t CMD_ISC_DISABLE[2] = {0xF0, 0x00}; +static const uint8_t CMD_ISC_ENABLE[2] = {0xE8, 0x00}; +static const uint8_t CMD_ISC_ERASE[2] = {0xEC, 0x00}; +static const uint8_t CMD_ISC_PROGRAM[2] = {0xEA, 0x00}; + +static const uint8_t CMD_XSC_BLANK_CHECK[2] = {0x0D, 0x00}; +static const uint8_t CMD_XSC_CONFIG[2] = {0xEE, 0x00}; +static const uint8_t CMD_XSC_DATA_BTC[2] = {0xF2, 0x00}; +static const uint8_t CMD_XSC_DATA_CCB[2] = {0x0C, 0x00}; +static const uint8_t CMD_XSC_DATA_DONE[2] = {0x09, 0x00}; +static const uint8_t CMD_XSC_DATA_SUCR[2] = {0x0E, 0x00}; +static const uint8_t CMD_XSC_DATA_WRPT[2] = {0xF7, 0x00}; +static const uint8_t CMD_XSC_OP_STATUS[2] = {0xE3, 0x00}; +static const uint8_t CMD_XSC_READ[2] = {0xEF, 0x00}; +static const uint8_t CMD_XSC_UNLOCK[2] = {0x55, 0xAA}; + +/* + ****************************************************************************** + * LOCAL FUNCTIONS + ****************************************************************************** + */ + +static const char *product_name(const struct flash_bank *bank) +{ + + switch (bank->target->tap->idcode & ID_MEANINGFUL_MASK) { + case ID_XCF08P: + return xcf_name_list[0]; + case ID_XCF16P: + return xcf_name_list[1]; + case ID_XCF32P: + return xcf_name_list[2]; + default: + return xcf_name_list[3]; + } +} + +static void fill_sector_table(struct flash_bank *bank) +{ + /* Note: is_erased and is_protected fields must be set here to an unknown + * state, they will be correctly filled from other API calls. */ + + int i = 0; + + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = -1; + } + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].size = XCF_DATA_SECTOR_SIZE; + bank->sectors[i].offset = i * XCF_DATA_SECTOR_SIZE; + } + + bank->size = bank->num_sectors * XCF_DATA_SECTOR_SIZE; +} + +static struct xcf_status read_status(struct flash_bank *bank) +{ + struct xcf_status ret; + uint8_t irdata[2]; + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = CMD_BYPASS; + scan.in_value = irdata; + + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + jtag_execute_queue(); + + ret.isc_error = ((irdata[0] >> 7) & 3) == 0b01; + ret.prog_error = ((irdata[0] >> 5) & 3) == 0b01; + ret.prog_busy = ((irdata[0] >> 4) & 1) == 0; + ret.isc_mode = ((irdata[0] >> 3) & 1) == 1; + + return ret; +} + +static int isc_enter(struct flash_bank *bank) +{ + + struct xcf_status status = read_status(bank); + + if (true == status.isc_mode) + return ERROR_OK; + else { + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = CMD_ISC_ENABLE; + scan.in_value = NULL; + + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + jtag_execute_queue(); + + status = read_status(bank); + if (false == status.isc_mode) { + LOG_ERROR("*** XCF: FAILED to enter ISC mode"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; + } +} + +static int isc_leave(struct flash_bank *bank) +{ + + struct xcf_status status = read_status(bank); + + if (false == status.isc_mode) + return ERROR_OK; + else { + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = CMD_ISC_DISABLE; + scan.in_value = NULL; + + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + jtag_execute_queue(); + alive_sleep(1); /* device needs 50 uS to leave ISC mode */ + + status = read_status(bank); + if (true == status.isc_mode) { + LOG_ERROR("*** XCF: FAILED to leave ISC mode"); + return ERROR_FLASH_OPERATION_FAILED; + } + + return ERROR_OK; + } +} + +static int sector_state(uint8_t wrpt, int sector) +{ + if (((wrpt >> sector) & 1) == 1) + return 0; + else + return 1; +} + +static uint8_t fill_select_block(int first, int last) +{ + uint8_t ret = 0; + for (int i = first; i <= last; i++) + ret |= 1 << i; + return ret; +} + +static int isc_read_register(struct flash_bank *bank, const uint8_t *cmd, + uint8_t *data_buf, int num_bits) +{ + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.out_value = cmd; + scan.in_value = NULL; + scan.num_bits = 16; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_DRSHIFT); + + scan.out_value = NULL; + scan.in_value = data_buf; + scan.num_bits = num_bits; + jtag_add_dr_scan(bank->target->tap, 1, &scan, TAP_IDLE); + + return jtag_execute_queue(); +} + +static int isc_wait_erase_program(struct flash_bank *bank, int64_t timeout_ms) +{ + + uint8_t isc_default; + int64_t t0 = timeval_ms(); + int64_t dt; + + do { + isc_read_register(bank, CMD_XSC_OP_STATUS, &isc_default, 8); + if (((isc_default >> 2) & 1) == 1) + return ERROR_OK; + dt = timeval_ms() - t0; + } while (dt <= timeout_ms); + return ERROR_FLASH_OPERATION_FAILED; +} + +/* + * helper function for procedures without program jtag command at the end + */ +static int isc_set_register(struct flash_bank *bank, const uint8_t *cmd, + const uint8_t *data_buf, int num_bits, int64_t timeout_ms) +{ + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = cmd; + scan.in_value = NULL; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_DRSHIFT); + + scan.num_bits = num_bits; + scan.out_value = data_buf; + scan.in_value = NULL; + jtag_add_dr_scan(bank->target->tap, 1, &scan, TAP_IDLE); + + if (0 == timeout_ms) + return jtag_execute_queue(); + else + return isc_wait_erase_program(bank, timeout_ms); +} + +/* + * helper function for procedures required program jtag command at the end + */ +static int isc_program_register(struct flash_bank *bank, const uint8_t *cmd, + const uint8_t *data_buf, int num_bits, int64_t timeout_ms) +{ + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = cmd; + scan.in_value = NULL; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_DRSHIFT); + + scan.num_bits = num_bits; + scan.out_value = data_buf; + scan.in_value = NULL; + jtag_add_dr_scan(bank->target->tap, 1, &scan, TAP_IRSHIFT); + + scan.num_bits = 16; + scan.out_value = CMD_ISC_PROGRAM; + scan.in_value = NULL; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + + if (0 == timeout_ms) + return jtag_execute_queue(); + else + return isc_wait_erase_program(bank, timeout_ms); +} + +static int isc_clear_protect(struct flash_bank *bank, int first, int last) +{ + uint8_t select_block[3] = {0x0, 0x0, 0x0}; + select_block[0] = fill_select_block(first, last); + return isc_set_register(bank, CMD_XSC_UNLOCK, select_block, 24, 0); +} + +static int isc_set_protect(struct flash_bank *bank, int first, int last) +{ + uint8_t wrpt[2] = {0xFF, 0xFF}; + for (int i = first; i <= last; i++) + wrpt[0] &= ~(1 << i); + + return isc_program_register(bank, CMD_XSC_DATA_WRPT, wrpt, 16, 0); +} + +static int isc_erase_sectors(struct flash_bank *bank, int first, int last) +{ + uint8_t select_block[3] = {0, 0, 0}; + select_block[0] = fill_select_block(first, last); + int64_t timeout = SECTOR_ERASE_TIMEOUT_MS * (last - first + 1); + return isc_set_register(bank, CMD_ISC_ERASE, select_block, 24, timeout); +} + +static int isc_adr_shift(struct flash_bank *bank, int adr) +{ + uint8_t adr_buf[3]; + h_u24_to_le(adr_buf, adr); + return isc_set_register(bank, CMD_ISC_ADDRESS_SHIFT, adr_buf, 24, 0); +} + +static int isc_program_data_page(struct flash_bank *bank, const uint8_t *page_buf) +{ + return isc_program_register(bank, CMD_ISC_DATA_SHIFT, page_buf, 8 * XCF_PAGE_SIZE, 100); +} + +static void isc_data_read_out(struct flash_bank *bank, uint8_t *buffer, uint32_t count) +{ + + struct scan_field scan; + + /* Do not change this code with isc_read_register() call because it needs + * transition to IDLE state before data retrieving. */ + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = CMD_XSC_READ; + scan.in_value = NULL; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + + scan.num_bits = 8 * count; + scan.out_value = NULL; + scan.in_value = buffer; + jtag_add_dr_scan(bank->target->tap, 1, &scan, TAP_IDLE); + + jtag_execute_queue(); +} + +static int isc_set_data_done(struct flash_bank *bank, int sector) +{ + uint8_t done = 0xFF; + done &= ~(1 << sector); + return isc_program_register(bank, CMD_XSC_DATA_DONE, &done, 8, 100); +} + +static void flip_u8(uint8_t *out, const uint8_t *in, int len) +{ + for (int i = 0; i < len; i++) + out[i] = flip_u32(in[i], 8); +} + +/* + * Xilinx bin file contains simple fixed header for automatic bus width detection: + * 16 bytes of 0xFF + * 4 byte sync word 0xAA995566 or (bit reversed) 0x5599AA66 in MSC file + * + * Function presumes need of bit reversing if it can not exactly detects + * the opposite. + */ +bool need_bit_reverse(const uint8_t *buffer) +{ + const size_t L = 20; + uint8_t reference[L]; + memset(reference, 0xFF, 16); + reference[16] = 0x55; + reference[17] = 0x99; + reference[18] = 0xAA; + reference[19] = 0x66; + + if (0 == memcmp(reference, buffer, L)) + return false; + else + return true; +} + +/* + * The page address to be programmed is determined by loading the + * internal ADDRESS Register using an ISC_ADDRESS_SHIFT instruction sequence. + * The page address automatically increments to the next 256-bit + * page address after each programming sequence until the last address + * in the 8 Mb block is reached. To continue programming the next block, + * the next 8 Mb block's starting address must be loaded into the + * internal ADDRESS register. + */ +static int read_write_data(struct flash_bank *bank, const uint8_t *w_buffer, + uint8_t *r_buffer, bool write_flag, uint32_t offset, uint32_t count) +{ + int dbg_count = count; + int dbg_written = 0; + int ret = ERROR_OK; + uint8_t *page_buf = malloc(XCF_PAGE_SIZE); + bool revbit = true; + isc_enter(bank); + + if (offset % XCF_PAGE_SIZE != 0) { + ret = ERROR_FLASH_DST_BREAKS_ALIGNMENT; + goto EXIT; + } + + if ((offset + count) > (uint32_t)(bank->num_sectors * XCF_DATA_SECTOR_SIZE)) { + ret = ERROR_FLASH_DST_OUT_OF_BANK; + goto EXIT; + } + + if ((write_flag) && (0 == offset) && (count >= XCF_PAGE_SIZE)) + revbit = need_bit_reverse(w_buffer); + + while (count > 0) { + uint32_t sector_num = offset / XCF_DATA_SECTOR_SIZE; + uint32_t sector_offset = offset - sector_num * XCF_DATA_SECTOR_SIZE; + uint32_t sector_bytes = XCF_DATA_SECTOR_SIZE - sector_offset; + if (count < sector_bytes) + sector_bytes = count; + isc_adr_shift(bank, offset); + offset += sector_bytes; + count -= sector_bytes; + + if (write_flag) { + while (sector_bytes > 0) { + int len; + + if (sector_bytes < XCF_PAGE_SIZE) { + len = sector_bytes; + memset(page_buf, 0xFF, XCF_PAGE_SIZE); + } else + len = XCF_PAGE_SIZE; + + if (revbit) + flip_u8(page_buf, w_buffer, len); + else + memcpy(page_buf, w_buffer, len); + + w_buffer += len; + sector_bytes -= len; + ret = isc_program_data_page(bank, page_buf); + if (ERROR_OK != ret) + goto EXIT; + else { + LOG_DEBUG("written %d bytes from %d", dbg_written, dbg_count); + dbg_written += len; + } + } + } else { + isc_data_read_out(bank, r_buffer, sector_bytes); + flip_u8(r_buffer, r_buffer, sector_bytes); + r_buffer += sector_bytes; + } + } + + /* Set 'done' flags for all data sectors because driver supports + * only single revision. */ + if (write_flag) { + for (int i = 0; i < bank->num_sectors; i++) { + ret = isc_set_data_done(bank, i); + if (ERROR_OK != ret) + goto EXIT; + } + } + +EXIT: + free(page_buf); + isc_leave(bank); + return ret; +} + +static uint16_t isc_read_ccb(struct flash_bank *bank) +{ + uint8_t ccb[2]; + isc_read_register(bank, CMD_XSC_DATA_CCB, ccb, 16); + return le_to_h_u16(ccb); +} + +static int gucr_num(const struct flash_bank *bank) +{ + return bank->num_sectors; +} + +static int sucr_num(const struct flash_bank *bank) +{ + return bank->num_sectors + 1; +} + +static int isc_program_ccb(struct flash_bank *bank, uint16_t ccb) +{ + uint8_t buf[2]; + h_u16_to_le(buf, ccb); + return isc_program_register(bank, CMD_XSC_DATA_CCB, buf, 16, 100); +} + +static int isc_program_singe_revision_sucr(struct flash_bank *bank) +{ + uint8_t sucr[2] = {0xFC, 0xFF}; + return isc_program_register(bank, CMD_XSC_DATA_SUCR, sucr, 16, 100); +} + +static int isc_program_single_revision_btc(struct flash_bank *bank) +{ + uint8_t buf[4]; + uint32_t btc = 0xFFFFFFFF; + btc &= ~0b1111; + btc |= ((bank->num_sectors - 1) << 2); + btc &= ~(1 << 4); + h_u32_to_le(buf, btc); + return isc_program_register(bank, CMD_XSC_DATA_BTC, buf, 32, 100); +} + +static int fpga_configure(struct flash_bank *bank) +{ + struct scan_field scan; + + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = CMD_XSC_CONFIG; + scan.in_value = NULL; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + jtag_execute_queue(); + + return ERROR_OK; +} + +/* + ****************************************************************************** + * EXPORTED FUNCTIONS + ****************************************************************************** + */ + +FLASH_BANK_COMMAND_HANDLER(xcf_flash_bank_command) +{ + struct xcf_priv *priv; + + priv = malloc(sizeof(struct xcf_priv)); + if (priv == NULL) { + LOG_ERROR("no memory for flash bank info"); + return ERROR_FAIL; + } + bank->driver_priv = priv; + priv->probed = false; + return ERROR_OK; +} + +static int xcf_info(struct flash_bank *bank, char *buf, int buf_size) +{ + const struct xcf_priv *priv = bank->driver_priv; + + if (false == priv->probed) { + snprintf(buf, buf_size, "\nXCF flash bank not probed yet\n"); + return ERROR_OK; + } + snprintf(buf, buf_size, "%s", product_name(bank)); + return ERROR_OK; +} + +static int xcf_probe(struct flash_bank *bank) +{ + struct xcf_priv *priv = bank->driver_priv; + uint32_t id; + + if (true == priv->probed) + free(bank->sectors); + priv->probed = false; + + if (bank->target->tap == NULL) { + LOG_ERROR("Target has no JTAG tap"); + return ERROR_FAIL; + } + + /* check idcode and alloc memory for sector table */ + if (!bank->target->tap->hasidcode) + return ERROR_FLASH_OPERATION_FAILED; + + /* guess number of blocks using chip ID */ + id = bank->target->tap->idcode; + switch (id & ID_MEANINGFUL_MASK) { + case ID_XCF08P: + bank->num_sectors = 1; + break; + case ID_XCF16P: + bank->num_sectors = 2; + break; + case ID_XCF32P: + bank->num_sectors = 4; + break; + default: + LOG_ERROR("Unknown flash device ID 0x%X", id); + return ERROR_FAIL; + break; + } + + bank->sectors = malloc(bank->num_sectors * sizeof(struct flash_sector)); + if (NULL == bank->sectors) { + LOG_ERROR("No memory for sector table"); + return ERROR_FAIL; + } + fill_sector_table(bank); + + priv->probed = true; + bank->driver_priv = priv; + + LOG_INFO("product name: %s", product_name(bank)); + LOG_INFO("device id = 0x%X ", bank->target->tap->idcode); + LOG_INFO("flash size = %d configuration bits", + bank->num_sectors * XCF_DATA_SECTOR_SIZE * 8); + LOG_INFO("number of sectors = %d", bank->num_sectors); + + return ERROR_OK; +} + +static int xcf_auto_probe(struct flash_bank *bank) +{ + struct xcf_priv *priv = bank->driver_priv; + + if (true == priv->probed) + return ERROR_OK; + else + return xcf_probe(bank); +} + +static int xcf_protect_check(struct flash_bank *bank) +{ + uint8_t wrpt[2]; + + isc_enter(bank); + isc_read_register(bank, CMD_XSC_DATA_WRPT, wrpt, 16); + isc_leave(bank); + + for (int i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_protected = sector_state(wrpt[0], i); + + return ERROR_OK; +} + +static int xcf_erase_check(struct flash_bank *bank) +{ + uint8_t blankreg; + struct scan_field scan; + + isc_enter(bank); + + /* Do not change this code with isc_read_register() call because it needs + * transition to IDLE state and pause before data retrieving. */ + scan.check_mask = NULL; + scan.check_value = NULL; + scan.num_bits = 16; + scan.out_value = CMD_XSC_BLANK_CHECK; + scan.in_value = NULL; + jtag_add_ir_scan(bank->target->tap, &scan, TAP_IDLE); + jtag_execute_queue(); + alive_sleep(500); /* device needs at least 0.5s to self check */ + + scan.num_bits = 8; + scan.in_value = &blankreg; + jtag_add_dr_scan(bank->target->tap, 1, &scan, TAP_IDLE); + jtag_execute_queue(); + + isc_leave(bank); + + for (int i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = sector_state(blankreg, i); + + return ERROR_OK; +} + +static int xcf_erase(struct flash_bank *bank, int first, int last) +{ + if ((first >= bank->num_sectors) + || (last >= bank->num_sectors) + || (last < first)) + return ERROR_FLASH_SECTOR_INVALID; + else { + isc_enter(bank); + isc_clear_protect(bank, first, last); + int ret = isc_erase_sectors(bank, first, last); + isc_leave(bank); + return ret; + } +} + +static int xcf_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + return read_write_data(bank, NULL, buffer, false, offset, count); +} + +static int xcf_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, + uint32_t count) +{ + return read_write_data(bank, buffer, NULL, true, offset, count); +} + +static int xcf_protect(struct flash_bank *bank, int set, int first, int last) +{ + int ret; + + isc_enter(bank); + if (set) + ret = isc_set_protect(bank, first, last); + else { + /* write protection may be removed only with following erase */ + isc_clear_protect(bank, first, last); + ret = isc_erase_sectors(bank, first, last); + } + isc_leave(bank); + + return ret; +} + +COMMAND_HANDLER(xcf_handle_ccb_command) { + + if (!((CMD_ARGC == 1) || (CMD_ARGC == 5))) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + uint16_t ccb = 0xFFFF; + isc_enter(bank); + uint16_t old_ccb = isc_read_ccb(bank); + isc_leave(bank); + + if (CMD_ARGC == 1) { + LOG_INFO("current CCB = 0x%X", old_ccb); + return ERROR_OK; + } else { + /* skip over flash bank */ + CMD_ARGC--; + CMD_ARGV++; + while (CMD_ARGC) { + if (strcmp("external", CMD_ARGV[0]) == 0) + ccb |= (1 << 0); + else if (strcmp("internal", CMD_ARGV[0]) == 0) + ccb &= ~(1 << 0); + else if (strcmp("serial", CMD_ARGV[0]) == 0) + ccb |= (3 << 1); + else if (strcmp("parallel", CMD_ARGV[0]) == 0) + ccb &= ~(3 << 1); + else if (strcmp("slave", CMD_ARGV[0]) == 0) + ccb |= (1 << 3); + else if (strcmp("master", CMD_ARGV[0]) == 0) + ccb &= ~(1 << 3); + else if (strcmp("40", CMD_ARGV[0]) == 0) + ccb |= (3 << 4); + else if (strcmp("20", CMD_ARGV[0]) == 0) + ccb &= ~(1 << 5); + else + return ERROR_COMMAND_SYNTAX_ERROR; + CMD_ARGC--; + CMD_ARGV++; + } + + isc_enter(bank); + int sector; + + /* GUCR sector */ + sector = gucr_num(bank); + isc_clear_protect(bank, sector, sector); + int ret = isc_erase_sectors(bank, sector, sector); + if (ERROR_OK != ret) + goto EXIT; + ret = isc_program_ccb(bank, ccb); + if (ERROR_OK != ret) + goto EXIT; + ret = isc_program_single_revision_btc(bank); + if (ERROR_OK != ret) + goto EXIT; + ret = isc_set_data_done(bank, sector); + if (ERROR_OK != ret) + goto EXIT; + + /* SUCR sector */ + sector = sucr_num(bank); + isc_clear_protect(bank, sector, sector); + ret = isc_erase_sectors(bank, sector, sector); + if (ERROR_OK != ret) + goto EXIT; + ret = isc_program_singe_revision_sucr(bank); + if (ERROR_OK != ret) + goto EXIT; + ret = isc_set_data_done(bank, sector); + if (ERROR_OK != ret) + goto EXIT; + +EXIT: + isc_leave(bank); + return ret; + } +} + +COMMAND_HANDLER(xcf_handle_configure_command) { + + if (CMD_ARGC != 1) + return ERROR_COMMAND_SYNTAX_ERROR; + + struct flash_bank *bank; + int retval = CALL_COMMAND_HANDLER(flash_command_get_bank, 0, &bank); + if (ERROR_OK != retval) + return retval; + + return fpga_configure(bank); +} + +static const struct command_registration xcf_exec_command_handlers[] = { + { + .name = "configure", + .handler = xcf_handle_configure_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Initiate FPGA loading procedure." + }, + { + .name = "ccb", + .handler = xcf_handle_ccb_command, + .mode = COMMAND_EXEC, + .usage = "bank_id [('external'|'internal') " + "('serial'|'parallel') " + "('slave'|'master') " + "('40'|'20')]", + .help = "Write CCB register with supplied options and (silently) BTC " + "register with single revision options. Display current " + "CCB value when only bank_id supplied. " + "Following options available: " + "1) external or internal clock source; " + "2) serial or parallel bus mode; " + "3) slave or master mode; " + "4) clock frequency in MHz for internal clock in master mode;" + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration xcf_command_handlers[] = { + { + .name = "xcf", + .mode = COMMAND_ANY, + .help = "Xilinx platform flash command group", + .usage = "", + .chain = xcf_exec_command_handlers + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver xcf_flash = { + .name = "xcf", + .usage = NULL, + .commands = xcf_command_handlers, + .flash_bank_command = xcf_flash_bank_command, + .erase = xcf_erase, + .protect = xcf_protect, + .write = xcf_write, + .read = xcf_read, + .probe = xcf_probe, + .auto_probe = xcf_auto_probe, + .erase_check = xcf_erase_check, + .protect_check = xcf_protect_check, + .info = xcf_info +}; diff --git a/src/flash/startup.tcl b/src/flash/startup.tcl index fbb8d8e..ff053ae 100644 --- a/src/flash/startup.tcl +++ b/src/flash/startup.tcl @@ -42,6 +42,7 @@ proc program {filename args} { # start programming phase echo "** Programming Started **" + set filename \{$filename\} if {[info exists address]} { set flash_args "$filename $address" } else { @@ -62,8 +63,10 @@ proc program {filename args} { if {[info exists reset]} { # reset target if requested - # also disable target polling, we are shutting down anyway - poll off + if {$exit == 1} { + # also disable target polling, we are shutting down anyway + poll off + } echo "** Resetting Target **" reset run } |