add w600 support 01/4801/6
authorSimon Qian <versaloon@simonqian.com>
Sun, 9 Dec 2018 16:58:41 +0000 (00:58 +0800)
committerTomas Vanek <vanekt@fbl.cz>
Fri, 21 Dec 2018 07:11:19 +0000 (07:11 +0000)
w600 is a wifi soc from winner micro(www.winnermicro.com).

Change-Id: Ib8ccd6e52baefca6547fb97d29db75db0ee73948
Signed-off-by: Simon Qian <versaloon@simonqian.com>
Reviewed-on: http://openocd.zylin.com/4801
Tested-by: jenkins
Reviewed-by: yichen <wdyichen@wdyichen.cn>
Reviewed-by: Tomas Vanek <vanekt@fbl.cz>
doc/openocd.texi
src/flash/nor/Makefile.am
src/flash/nor/drivers.c
src/flash/nor/w600.c [new file with mode: 0644]

index aa901e6..c36ec30 100644 (file)
@@ -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.
index 864f7f2..7c353c4 100644 (file)
@@ -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
index 4ffd5ac..1c456ad 100644 (file)
@@ -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 (file)
index 0000000..3d37616
--- /dev/null
@@ -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 <http://www.gnu.org/licenses/>. *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+#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 <base> <size> 0 0 <target#>
+ */
+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,
+};