From: Simon Qian Date: Sun, 9 Dec 2018 16:58:41 +0000 (+0800) Subject: add w600 support X-Git-Tag: v0.11.0-rc1~899 X-Git-Url: https://review.openocd.org/gitweb?p=openocd.git;a=commitdiff_plain;h=fdaa8711aef0fb70d289c7cb733313cf3dce3b02 add w600 support w600 is a wifi soc from winner micro(www.winnermicro.com). Change-Id: Ib8ccd6e52baefca6547fb97d29db75db0ee73948 Signed-off-by: Simon Qian Reviewed-on: http://openocd.zylin.com/4801 Tested-by: jenkins Reviewed-by: yichen Reviewed-by: Tomas Vanek --- diff --git a/doc/openocd.texi b/doc/openocd.texi index aa901e6e49..c36ec30b2a 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -6891,6 +6891,17 @@ the flash clock. @end deffn @end deffn +@deffn {Flash Driver} w600 +W60x series Wi-Fi SoC from WinnerMicro +are designed with ARM Cortex-M3 and have 1M Byte QFLASH inside. +The @var{w600} driver uses the @var{target} parameter to select the +correct bank config. + +@example +flash bank $_FLASHNAME w600 0x08000000 0 0 0 $_TARGETNAMEs +@end example +@end deffn + @deffn {Flash Driver} xmc1xxx All members of the XMC1xxx microcontroller family from Infineon. This driver does not require the chip and bus width to be specified. diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 864f7f29a5..7c353c4adb 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -63,6 +63,7 @@ NOR_DRIVERS = \ %D%/str9xpec.c \ %D%/tms470.c \ %D%/virtual.c \ + %D%/w600.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 4ffd5aca5b..1c456ad59c 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -77,6 +77,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 w600_flash; extern struct flash_driver xcf_flash; extern struct flash_driver xmc1xxx_flash; extern struct flash_driver xmc4xxx_flash; @@ -146,6 +147,7 @@ static struct flash_driver *flash_drivers[] = { &xcf_flash, &xmc1xxx_flash, &xmc4xxx_flash, + &w600_flash, NULL, }; diff --git a/src/flash/nor/w600.c b/src/flash/nor/w600.c new file mode 100644 index 0000000000..3d37616178 --- /dev/null +++ b/src/flash/nor/w600.c @@ -0,0 +1,390 @@ +/*************************************************************************** + * Copyright (C) 2018 by Simon Qian * + * SimonQian@SimonQian.com * + * * + * 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 "imp.h" +#include +#include +#include + +#define W600_FLASH_SECSIZE 0x1000 +#define W600_FLASH_PAGESIZE 0x100 +#define W600_FLASH_BASE 0x08000000 +#define W600_FLASH_PROTECT_SIZE 0x2000 + +/* w600 register locations */ + +#define QFLASH_REGBASE 0X40002000 +#define QFLASH_CMD_INFO (QFLASH_REGBASE + 0) +#define QFLASH_CMD_START (QFLASH_REGBASE + 4) +#define QFLASH_BUFFER (QFLASH_REGBASE + 0X200) + +#define QFLASH_CMD_READ (1ul << 14) +#define QFLASH_CMD_WRITE 0 +#define QFLASH_CMD_ADDR (1ul << 31) +#define QFLASH_CMD_DATA (1ul << 15) +#define QFLASH_CMD_DATALEN(len) (((len) & 0x3FF) << 16) + +#define QFLASH_CMD_RDID (QFLASH_CMD_READ | 0x9F) +#define QFLASH_CMD_WREN (QFLASH_CMD_WRITE | 0x06) +#define QFLASH_CMD_WRDI (QFLASH_CMD_WRITE | 0x04) +#define QFLASH_CMD_SE (QFLASH_CMD_WRITE | QFLASH_CMD_ADDR | (1ul << 11) | 0x20) +#define QFLASH_CMD_PP (QFLASH_CMD_WRITE | QFLASH_CMD_ADDR | (1ul << 12) | 0x02) + +#define QFLASH_START (1ul << 28) +#define QFLASH_ADDR(addr) (((addr) & 0xFFFFF) << 8) +#define QFLASH_CRM(crm) (((crm) & 0xFF) << 0) + +struct w600_flash_param { + uint8_t id; + uint8_t se_delay; + uint8_t pp_delay; +}; +static const struct w600_flash_param w600_param[] = { + { + .id = 0x85, + .se_delay = 8, + .pp_delay = 2, + }, + { + .id = 0x1C, + .se_delay = 50, + .pp_delay = 1, + }, + { + .id = 0xC8, + .se_delay = 45, + .pp_delay = 1, + }, + { + .id = 0x0B, + .se_delay = 60, + .pp_delay = 1, + }, + { + .id = 0x68, + .se_delay = 50, + .pp_delay = 1, + }, +}; + +struct w600_flash_bank { + int probed; + + uint32_t id; + const struct w600_flash_param *param; + uint32_t register_base; + uint32_t user_bank_size; +}; + +/* flash bank w600 0 0 + */ +FLASH_BANK_COMMAND_HANDLER(w600_flash_bank_command) +{ + struct w600_flash_bank *w600_info; + + if (CMD_ARGC < 6) + return ERROR_COMMAND_SYNTAX_ERROR; + + w600_info = malloc(sizeof(struct w600_flash_bank)); + + bank->driver_priv = w600_info; + w600_info->probed = 0; + w600_info->register_base = QFLASH_REGBASE; + w600_info->user_bank_size = bank->size; + + return ERROR_OK; +} + +static int w600_get_delay(struct flash_bank *bank, uint32_t cmd) +{ + struct w600_flash_bank *w600_info = bank->driver_priv; + + if (!w600_info->param) + return 0; + + switch (cmd) { + case QFLASH_CMD_SE: + return w600_info->param->se_delay; + case QFLASH_CMD_PP: + return w600_info->param->pp_delay; + default: + return 0; + } +} + +static int w600_start_do(struct flash_bank *bank, uint32_t cmd, uint32_t addr, + uint32_t len, int timeout) +{ + struct target *target = bank->target; + + if (len > 0) + cmd |= QFLASH_CMD_DATALEN(len - 1) | QFLASH_CMD_DATA; + + LOG_DEBUG("WRITE CMD: 0x%08" PRIx32 "", cmd); + int retval = target_write_u32(target, QFLASH_CMD_INFO, cmd); + if (retval != ERROR_OK) + return retval; + + addr |= QFLASH_START; + LOG_DEBUG("WRITE START: 0x%08" PRIx32 "", addr); + retval = target_write_u32(target, QFLASH_CMD_START, addr); + if (retval != ERROR_OK) + return retval; + + LOG_DEBUG("DELAY %dms", timeout); + alive_sleep(timeout); + + int retry = 100; + uint32_t status; + for (;;) { + LOG_DEBUG("READ START..."); + retval = target_read_u32(target, QFLASH_CMD_START, &status); + if (retval == ERROR_OK) + LOG_DEBUG("READ START: 0x%08" PRIx32 "", status); + else + LOG_DEBUG("READ START FAILED"); + + if ((retval != ERROR_OK) || (status & QFLASH_START)) { + if (retry-- <= 0) { + LOG_ERROR("timed out waiting for flash"); + return ERROR_FAIL; + } + continue; + } + break; + } + + return retval; +} + +static int w600_write_enable(struct flash_bank *bank) +{ + return w600_start_do(bank, QFLASH_CMD_WREN, 0, 0, 0); +} + +static int w600_write_disable(struct flash_bank *bank) +{ + return w600_start_do(bank, QFLASH_CMD_WRDI, 0, 0, 0); +} + +static int w600_start(struct flash_bank *bank, uint32_t cmd, uint32_t addr, + uint32_t len) +{ + int retval = w600_write_enable(bank); + if (retval != ERROR_OK) + return retval; + + retval = w600_start_do(bank, cmd, addr, len, w600_get_delay(bank, cmd)); + if (retval != ERROR_OK) + return retval; + + retval = w600_write_disable(bank); + if (retval != ERROR_OK) + return retval; + + return retval; +} + +static int w600_erase(struct flash_bank *bank, int first, int last) +{ + int retval = ERROR_OK; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + if (first < W600_FLASH_PROTECT_SIZE / W600_FLASH_SECSIZE) { + LOG_ERROR("can not erase protected area"); + return ERROR_FAIL; + } + + for (int i = first; i <= last; i++) { + retval = w600_start(bank, QFLASH_CMD_SE, + QFLASH_ADDR(bank->sectors[i].offset), 0); + if (retval != ERROR_OK) + break; + } + + return retval; +} + +static int w600_write(struct flash_bank *bank, const uint8_t *buffer, + uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + int retval = ERROR_OK; + + if (bank->target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + if ((offset % W600_FLASH_PAGESIZE) != 0) { + LOG_WARNING("offset 0x%" PRIx32 " breaks required %" PRIu32 "-byte alignment", + offset, W600_FLASH_PAGESIZE); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + if ((count % W600_FLASH_PAGESIZE) != 0) { + LOG_WARNING("count 0x%" PRIx32 " breaks required %" PRIu32 "-byte alignment", + offset, W600_FLASH_PAGESIZE); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + while (count > 0) { + retval = target_write_buffer(target, QFLASH_BUFFER, W600_FLASH_PAGESIZE, buffer); + if (retval != ERROR_OK) + break; + + retval = w600_start(bank, QFLASH_CMD_PP, QFLASH_ADDR(offset), + W600_FLASH_PAGESIZE); + if (retval != ERROR_OK) + break; + + count -= W600_FLASH_PAGESIZE; + offset += W600_FLASH_PAGESIZE; + buffer += W600_FLASH_PAGESIZE; + } + + return retval; +} + +static int w600_get_flash_id(struct flash_bank *bank, uint32_t *flash_id) +{ + struct target *target = bank->target; + + int retval = w600_start(bank, QFLASH_CMD_RDID, 0, 4); + if (retval != ERROR_OK) + return retval; + + return target_read_u32(target, QFLASH_BUFFER, flash_id); +} + +static int w600_probe(struct flash_bank *bank) +{ + struct w600_flash_bank *w600_info = bank->driver_priv; + uint32_t flash_size; + uint32_t flash_id; + size_t i; + + w600_info->probed = 0; + + /* read stm32 device id register */ + int retval = w600_get_flash_id(bank, &flash_id); + if (retval != ERROR_OK) + return retval; + + LOG_INFO("flash_id id = 0x%08" PRIx32 "", flash_id); + w600_info->id = flash_id; + w600_info->param = NULL; + for (i = 0; i < ARRAY_SIZE(w600_param); i++) { + if (w600_param[i].id == (flash_id & 0xFF)) { + w600_info->param = &w600_param[i]; + break; + } + } + if (!w600_info->param) { + LOG_ERROR("flash_id not supported for w600"); + return ERROR_FAIL; + } + + /* if the user sets the size manually then ignore the probed value + * this allows us to work around devices that have a invalid flash size register value */ + if (w600_info->user_bank_size) { + LOG_INFO("ignoring flash probed value, using configured bank size"); + flash_size = w600_info->user_bank_size; + } else { + flash_size = ((flash_id & 0xFFFFFF) >> 16) & 0xFF; + if ((flash_size != 0x14) && (flash_size != 0x13)) { + LOG_ERROR("w600 flash size failed, probe inaccurate"); + return ERROR_FAIL; + } + + flash_size = 1 << flash_size; + } + + LOG_INFO("flash size = %dkbytes", flash_size / 1024); + + /* calculate numbers of pages */ + size_t num_pages = flash_size / W600_FLASH_SECSIZE; + + /* check that calculation result makes sense */ + assert(num_pages > 0); + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + + bank->base = W600_FLASH_BASE; + bank->size = num_pages * W600_FLASH_SECSIZE; + bank->num_sectors = num_pages; + bank->write_start_alignment = W600_FLASH_PAGESIZE; + bank->write_end_alignment = W600_FLASH_PAGESIZE; + bank->sectors = malloc(sizeof(struct flash_sector) * num_pages); + + for (i = 0; i < num_pages; i++) { + bank->sectors[i].offset = i * W600_FLASH_SECSIZE; + bank->sectors[i].size = W600_FLASH_SECSIZE; + bank->sectors[i].is_erased = -1; + /* offset 0 to W600_FLASH_PROTECT_SIZE shoule be protected */ + bank->sectors[i].is_protected = (i < W600_FLASH_PROTECT_SIZE / W600_FLASH_SECSIZE); + } + + w600_info->probed = 1; + + return ERROR_OK; +} + +static int w600_auto_probe(struct flash_bank *bank) +{ + struct w600_flash_bank *w600_info = bank->driver_priv; + if (w600_info->probed) + return ERROR_OK; + return w600_probe(bank); +} + +static int get_w600_info(struct flash_bank *bank, char *buf, int buf_size) +{ + uint32_t flash_id; + + /* read w600 device id register */ + int retval = w600_get_flash_id(bank, &flash_id); + if (retval != ERROR_OK) + return retval; + + snprintf(buf, buf_size, "w600 : 0x%08" PRIx32 "", flash_id); + return ERROR_OK; +} + +struct flash_driver w600_flash = { + .name = "w600", + .flash_bank_command = w600_flash_bank_command, + .erase = w600_erase, + .write = w600_write, + .read = default_flash_read, + .probe = w600_probe, + .auto_probe = w600_auto_probe, + .erase_check = default_flash_blank_check, + .info = get_w600_info, + .free_driver_priv = default_flash_free_driver_priv, +};