This is an automated email from Gerrit. Robert Jordens ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/2844
-- gerrit commit 09f4f4d1e18bec03941bb8e378b023f5563625e9 Author: Robert Jordens <[email protected]> Date: Wed Jul 1 03:18:46 2015 -0600 flash/nor/jtagspi: add JTAG2SPI driver Many FPGA board speak JTAG and have a SPI flash for their bitstream attached to them. The SPI flash is programmed by first uploading a proxy bitstream to the FPGA that connects the JTAG interface to the SPI interface if the IR contains a certain USER instruction. Then the SPI flash can be erase, written, read directly through the JTAG DR. The JTAG and SPI signaling is compatible. Such a proxy bitstream only needs to connect TDO-MISO, TDI-MOSI, TCK-CLK, and the activate the chip select when the IR contains the special instruction and the JTAG state machine is in the DR-SHIFT state. Change-Id: Ibc21d793a83b36fa37e2704966aa5c837c4dd0d2 Signed-off-by: Robert Jordens <[email protected]> diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 878fc26..19ae90e 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -19,6 +19,7 @@ NOR_DRIVERS = \ efm32.c \ em357.c \ faux.c \ + jtagspi.c \ lpc2000.c \ lpc288x.c \ lpc2900.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index fead797..6ae0859 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -59,6 +59,7 @@ extern struct flash_driver nrf51_flash; extern struct flash_driver mrvlqspi_flash; extern struct flash_driver psoc4_flash; extern struct flash_driver sim3x_flash; +extern struct flash_driver jtagspi_flash; /** * The list of built-in flash drivers. @@ -102,6 +103,7 @@ static struct flash_driver *flash_drivers[] = { &mrvlqspi_flash, &psoc4_flash, &sim3x_flash, + &jtagspi_flash, NULL, }; diff --git a/src/flash/nor/jtagspi.c b/src/flash/nor/jtagspi.c new file mode 100644 index 0000000..e66d91d --- /dev/null +++ b/src/flash/nor/jtagspi.c @@ -0,0 +1,380 @@ +/*************************************************************************** + * Copyright (C) 2015 Robert Jordens <[email protected]> * + * * + * 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. * + * * + ***************************************************************************/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "imp.h" +#include <jtag/jtag.h> +#include <flash/nor/spi.h> +#include <helper/time_support.h> + +#define JTAGSPI_MAX_TIMEOUT 3000 + + +struct jtagspi_flash_bank { + int probed; + const struct flash_device *dev; + uint32_t ir; + uint32_t latency; + int write_enabled; +}; + +FLASH_BANK_COMMAND_HANDLER(jtagspi_flash_bank_command) +{ + struct jtagspi_flash_bank *info; + + if (CMD_ARGC < 8) + return ERROR_COMMAND_SYNTAX_ERROR; + + info = malloc(sizeof(struct jtagspi_flash_bank)); + if (info == NULL) { + LOG_ERROR("no memory for flash bank info"); + return ERROR_FAIL; + } + bank->driver_priv = info; + info->probed = 0; + info->write_enabled = 0; + + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[6], info->ir); + COMMAND_PARSE_NUMBER(u32, CMD_ARGV[7], info->latency); + + return ERROR_OK; +} + +static void jtagspi_set_ir(struct flash_bank *bank) +{ + struct jtag_tap *tap = bank->target->tap; + struct jtagspi_flash_bank *info = bank->driver_priv; + struct scan_field field; + uint8_t buf[4]; + if (buf_get_u32(tap->cur_instr, 0, tap->ir_length) == info->ir) + return; + LOG_DEBUG("%s", __func__); + buf_set_u32(buf, 0, tap->ir_length, info->ir); + field.num_bits = tap->ir_length; + field.out_value = buf; + field.in_value = NULL; + jtag_add_ir_scan(tap, &field, TAP_IDLE); +} + +static void flip_u8(uint8_t *in, uint8_t *out, int len) +{ + for (int i = 0; i < len; i++) + out[i] = flip_u32(in[i], 8); +} + +static int jtagspi_cmd(struct flash_bank *bank, uint8_t cmd, + uint32_t *addr, uint8_t *data, int len) +{ + struct jtag_tap *tap = bank->target->tap; + struct jtagspi_flash_bank *info = bank->driver_priv; + struct scan_field fields[3]; + uint8_t cmd_buf[4]; + uint8_t *data_buf; + int is_read, lenb, n; + + LOG_DEBUG("%s cmd=0x%02x len=%i", __func__, cmd, len); + + n = 0; + 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].in_value = NULL; + n++; + + is_read = (len < 0); + if (is_read) + len = -len; + lenb = DIV_ROUND_UP(len, 8); + data_buf = malloc(lenb); + if (lenb > 0) { + if (data_buf == NULL) { + LOG_ERROR("%s no memory", __func__); + return ERROR_FAIL; + } + if (is_read) { + fields[n].num_bits = info->latency; + fields[n].out_value = NULL; + fields[n].in_value = NULL; + n++; + fields[n].out_value = NULL; + fields[n].in_value = data_buf; + } else { + flip_u8(data, data_buf, lenb); + fields[n].out_value = data_buf; + fields[n].in_value = NULL; + } + fields[n].num_bits = len; + n++; + } + jtag_add_dr_scan(tap, n, fields, TAP_IDLE); + jtag_execute_queue(); + if (is_read) + flip_u8(data_buf, data, lenb); + free(data_buf); + return ERROR_OK; +} + +static int jtagspi_probe(struct flash_bank *bank) +{ + struct jtagspi_flash_bank *info = bank->driver_priv; + struct flash_sector *sectors; + uint8_t in_buf[3]; + uint32_t id; + + if (info->probed) + return ERROR_OK; + info->probed = 0; + + if (bank->target->tap == NULL) { + LOG_ERROR("no tap"); + return ERROR_FAIL; + } + jtagspi_set_ir(bank); + jtagspi_cmd(bank, SPIFLASH_READ_ID, NULL, in_buf, -24); + id = be_to_h_u24(in_buf); + + info->dev = NULL; + for (const struct flash_device *p = flash_devices; p->name ; p++) + if (p->device_id == id) { + info->dev = p; + break; + } + + if (!info->dev) { + LOG_ERROR("Unknown flash device (ID 0x%08" PRIx32 ")", id); + return ERROR_FAIL; + } + + LOG_INFO("Found flash device \'%s\' (ID 0x%08" PRIx32 ")", + info->dev->name, info->dev->device_id); + + /* Set correct size value */ + bank->size = info->dev->size_in_bytes; + + /* create and fill sectors array */ + bank->num_sectors = + info->dev->size_in_bytes / info->dev->sectorsize; + sectors = malloc(sizeof(struct flash_sector) * bank->num_sectors); + if (sectors == NULL) { + LOG_ERROR("not enough memory"); + return ERROR_FAIL; + } + + for (int sector = 0; sector < bank->num_sectors; sector++) { + sectors[sector].offset = sector * info->dev->sectorsize; + sectors[sector].size = info->dev->sectorsize; + sectors[sector].is_erased = -1; + sectors[sector].is_protected = 0; + } + + bank->sectors = sectors; + + info->probed = 1; + return ERROR_OK; +} + +static uint8_t jtagspi_read_status(struct flash_bank *bank) +{ + uint8_t status; + jtagspi_cmd(bank, SPIFLASH_READ_STATUS, NULL, &status, -8); + LOG_DEBUG("%s status=0x%08" PRIx32, __func__, status); + return status; +} + +static int jtagspi_wait(struct flash_bank *bank, int timeout_ms) +{ + uint32_t status; + long long t0 = timeval_ms(); + do { + status = jtagspi_read_status(bank); + if ((status & SPIFLASH_BSY_BIT) == 0) + return ERROR_OK; + alive_sleep(1); + } while (timeval_ms() - t0 < timeout_ms); + LOG_ERROR("%s timeout during write/erase", __func__); + return ERROR_FAIL; +} + +static int jtagspi_write_enable(struct flash_bank *bank) +{ + struct jtagspi_flash_bank *info = bank->driver_priv; + uint32_t status; + if (info->write_enabled) + return ERROR_OK; + jtagspi_cmd(bank, SPIFLASH_WRITE_ENABLE, NULL, NULL, 0); + status = jtagspi_read_status(bank); + if ((status & SPIFLASH_WE_BIT) == 0) { + LOG_ERROR("Cannot enable write to flash. Status=0x%08" PRIx32, status); + return ERROR_FAIL; + } + return ERROR_OK; +} + +static int jtagspi_bulk_erase(struct flash_bank *bank) +{ + struct jtagspi_flash_bank *info = bank->driver_priv; + int retval; + retval = jtagspi_write_enable(bank); + if (retval != ERROR_OK) + return retval; + jtagspi_cmd(bank, info->dev->chip_erase_cmd, NULL, NULL, 0); + return jtagspi_wait(bank, bank->num_sectors*JTAGSPI_MAX_TIMEOUT); +} + +static int jtagspi_sector_erase(struct flash_bank *bank, int sector) +{ + struct jtagspi_flash_bank *info = bank->driver_priv; + int retval; + retval = jtagspi_write_enable(bank); + if (retval != ERROR_OK) + return retval; + jtagspi_cmd(bank, info->dev->erase_cmd, &bank->sectors[sector].offset, NULL, 0); + return jtagspi_wait(bank, JTAGSPI_MAX_TIMEOUT); +} + +static int jtagspi_erase(struct flash_bank *bank, int first, int last) +{ + int sector; + struct jtagspi_flash_bank *info = bank->driver_priv; + int retval; + + LOG_DEBUG("erase from sector %d to sector %d", first, last); + + if ((first < 0) || (last < first) || (last >= bank->num_sectors)) { + LOG_ERROR("Flash sector invalid"); + return ERROR_FLASH_SECTOR_INVALID; + } + + if (!(info->probed)) { + LOG_ERROR("Flash bank not probed"); + return ERROR_FLASH_BANK_NOT_PROBED; + } + + for (sector = first; sector <= last; sector++) { + if (bank->sectors[sector].is_protected) { + LOG_ERROR("Flash sector %d protected", sector); + return ERROR_FAIL; + } + } + + jtagspi_set_ir(bank); + + if (first == 0 && last == (bank->num_sectors - 1) + && info->dev->chip_erase_cmd != info->dev->erase_cmd) { + LOG_DEBUG("Trying bulk erase."); + retval = jtagspi_bulk_erase(bank); + if (retval == ERROR_OK) + return retval; + else + LOG_WARNING("Bulk flash erase failed. Falling back to sector erase."); + } + + for (sector = first; sector <= last; sector++) { + retval = jtagspi_sector_erase(bank, sector); + if (retval != ERROR_OK) { + LOG_ERROR("Sector erase failed."); + break; + } + } + + return retval; +} + +static int jtagspi_protect(struct flash_bank *bank, int set, int first, int last) +{ + int sector; + + for (sector = first; sector <= last; sector++) + bank->sectors[sector].is_protected = set; + return ERROR_OK; +} + +static int jtagspi_protect_check(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int jtagspi_read(struct flash_bank *bank, uint8_t *buffer, uint32_t offset, uint32_t count) +{ + jtagspi_set_ir(bank); + jtagspi_cmd(bank, SPIFLASH_READ, &offset, buffer, -count*8); + return ERROR_OK; +} + +static int jtagspi_page_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + int retval; + retval = jtagspi_write_enable(bank); + if (retval != ERROR_OK) + return retval; + jtagspi_cmd(bank, SPIFLASH_PAGE_PROGRAM, &offset, (uint8_t *) buffer, count*8); + return jtagspi_wait(bank, JTAGSPI_MAX_TIMEOUT); +} + +static int jtagspi_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct jtagspi_flash_bank *info = bank->driver_priv; + int retval; + uint32_t n; + jtagspi_set_ir(bank); + for (n = 0; n < count; n += info->dev->pagesize) { + retval = jtagspi_page_write(bank, buffer + n, offset + n, + MIN(count - n, info->dev->pagesize)); + if (retval != ERROR_OK) { + LOG_ERROR("%s page write error", __func__); + return retval; + } + LOG_DEBUG("%s wrote page at 0x%08" PRIx32, __func__, offset + n); + } + return ERROR_OK; +} + +static int jtagspi_info(struct flash_bank *bank, char *buf, int buf_size) +{ + struct jtagspi_flash_bank *info = bank->driver_priv; + + if (!(info->probed)) { + snprintf(buf, buf_size, "\nSPIFI flash bank not probed yet\n"); + return ERROR_OK; + } + + snprintf(buf, buf_size, "\nSPIFI flash information:\n" + " Device \'%s\' (ID 0x%08" PRIx32 ")\n", + info->dev->name, info->dev->device_id); + + return ERROR_OK; +} + +struct flash_driver jtagspi_flash = { + .name = "jtagspi", + .flash_bank_command = jtagspi_flash_bank_command, + .erase = jtagspi_erase, + .protect = jtagspi_protect, + .write = jtagspi_write, + .read = jtagspi_read, + .probe = jtagspi_probe, + .auto_probe = jtagspi_probe, + .erase_check = default_flash_blank_check, + .protect_check = jtagspi_protect_check, + .info = jtagspi_info +}; -- ------------------------------------------------------------------------------ Don't Limit Your Business. Reach for the Cloud. GigeNET's Cloud Solutions provide you with the tools and support that you need to offload your IT needs and focus on growing your business. Configured For All Businesses. Start Your Cloud Today. https://www.gigenetcloud.com/ _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
