From dc28a6e593cc53117ba9b8e6e7ab9d20f49c5e4b Mon Sep 17 00:00:00 2001 From: barthess Date: Sun, 11 Dec 2016 15:11:41 +0300 Subject: [PATCH] XCF (Xilinx platfrom flash) support. Change-Id: I4ee6db5f0abdb9fd279cc0edd13f71952a9d295d Signed-off-by: Uladzimir Pylinski Reviewed-on: http://openocd.zylin.com/3914 Tested-by: jenkins Reviewed-by: Paul Fertser --- doc/openocd.texi | 46 ++ src/flash/nor/Makefile.am | 1 + src/flash/nor/drivers.c | 2 + src/flash/nor/xcf.c | 897 ++++++++++++++++++++++++++++++++++++++ tcl/cpld/xilinx-xcf-p.cfg | 18 + tcl/cpld/xilinx-xcf-s.cfg | 18 + 6 files changed, 982 insertions(+) create mode 100644 src/flash/nor/xcf.c create mode 100644 tcl/cpld/xilinx-xcf-p.cfg create mode 100644 tcl/cpld/xilinx-xcf-s.cfg diff --git a/doc/openocd.texi b/doc/openocd.texi index 8ff9010a..7f5b72e0 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -4926,6 +4926,52 @@ flash bank $_FLASHNAME spi 0x0 0 0 0 \ @end example @end deffn +@deffn {Flash Driver} xcf +@cindex Xilinx Platform flash driver +@cindex xcf +Xilinx FPGAs can be configured from specialized flash ICs named Platform Flash. +It is (almost) regular NOR flash with erase sectors, program pages, etc. The +only difference is special registers controlling its FPGA specific behavior. +They must be properly configured for successful FPGA loading using +additional @var{xcf} driver command: + +@deffn Command {xcf ccb} +command accepts additional parameters: +@itemize +@item @var{external|internal} ... selects clock source. +@item @var{serial|parallel} ... selects serial or parallel data bus mode. +@item @var{slave|master} ... selects slave of master mode for flash device. +@item @var{40|20} ... selects clock frequency in MHz for internal clock +in master mode. +@end itemize +@example +xcf ccb 0 external parallel slave 40 +@end example +All of them must be specified even if clock frequency is pointless +in slave mode. If only bank id specified than command prints current +CCB register value. Note: there is no need to write this register +every time you erase/program data sectors because it stores in +dedicated sector. +@end deffn + +@deffn Command {xcf configure} +Initiates FPGA loading procedure. Useful if your board has no "configure" +button. +@example +xcf configure 0 +@end example +@end deffn + +Additional driver notes: +@itemize +@item Only single revision supported. +@item Driver automatically detects need of bit reverse, but +only "bin" (raw binary, do not confuse it with "bit") and "mcs" +(Intel hex) file types supported. +@item For additional info check xapp972.pdf and ug380.pdf. +@end itemize +@end deffn + @deffn {Flash Driver} lpcspifi @cindex NXP SPI Flash Interface @cindex SPIFI diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index ebf47756..6dc61e62 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -55,6 +55,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 eacca034..3b055d19 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -67,6 +67,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; @@ -122,6 +123,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/xcf.c b/src/flash/nor/xcf.c new file mode 100644 index 00000000..035791eb --- /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 . * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include "imp.h" +#include +#include + +/* + ****************************************************************************** + * 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/tcl/cpld/xilinx-xcf-p.cfg b/tcl/cpld/xilinx-xcf-p.cfg new file mode 100644 index 00000000..8e0a26c6 --- /dev/null +++ b/tcl/cpld/xilinx-xcf-p.cfg @@ -0,0 +1,18 @@ +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME xcf +} + +# IDs acquired from Xilinx's DS123.pdf +# XCF08P 5057093 +# XCF16P 5058093 +# XCF32P 5059093 +# The 4 top bits (28:31) are the device revision. Ignore it. +jtag newtap $_CHIPNAME flash -irlen 16 -ignore-version \ + -expected-id 0x05057093 \ + -expected-id 0x05058093 \ + -expected-id 0x05059093 + +target create xcf.flash testee -chain-position $_CHIPNAME.flash +flash bank XCF_P xcf 0 0 0 0 xcf.flash diff --git a/tcl/cpld/xilinx-xcf-s.cfg b/tcl/cpld/xilinx-xcf-s.cfg new file mode 100644 index 00000000..a3c79a38 --- /dev/null +++ b/tcl/cpld/xilinx-xcf-s.cfg @@ -0,0 +1,18 @@ +if { [info exists CHIPNAME] } { + set _CHIPNAME $CHIPNAME +} else { + set _CHIPNAME xcf +} + +# IDs acquired from Xilinx's DS123.pdf +# XCF01S 5044093 +# XCF02S 5045093 +# XCF04S 5046093 +# The 4 top bits (28:31) are the device revision. Ignore it. +jtag newtap $_CHIPNAME flash -irlen 8 -ignore-version \ + -expected-id 0x05044093 \ + -expected-id 0x05045093 \ + -expected-id 0x05046093 + +target create xcf.flash testee -chain-position $_CHIPNAME.flash +flash bank XCF_S xcf 0 0 0 0 xcf.flash -- 2.39.5