This is an automated email from Gerrit. Icenowy Zheng ([email protected]) just uploaded a new patch set to Gerrit, which you can find at http://openocd.zylin.com/4927
-- gerrit commit 236ef50f9aaae9ba37f8ab207367e204442cae6a Author: Icenowy Zheng <[email protected]> Date: Tue Feb 19 11:52:20 2019 +0800 flash/nor: flash driver for Synwit SWM050 MCUs SWM050 is a series of MCU product by Foshan Synwit Tech, which is available in TSSOP-8 or SSOP-16 packages. Adds flash driver for the internal 8KiB flash of the MCU. The registers are based on reverse engineering the J-Flash blob provided by the vendor. Change-Id: I0b29f0c0d062883542ee743e0750a4c6b6609ebd Signed-off-by: Icenowy Zheng <[email protected]> diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am index 135128e..34f91ce 100644 --- a/src/flash/nor/Makefile.am +++ b/src/flash/nor/Makefile.am @@ -63,6 +63,7 @@ NOR_DRIVERS = \ %D%/str7x.c \ %D%/str9x.c \ %D%/str9xpec.c \ + %D%/swm050.c \ %D%/tms470.c \ %D%/virtual.c \ %D%/w600.c \ diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c index 31caa5a..b041e0e 100644 --- a/src/flash/nor/drivers.c +++ b/src/flash/nor/drivers.c @@ -77,6 +77,7 @@ extern struct flash_driver stmsmi_flash; extern struct flash_driver str7x_flash; extern struct flash_driver str9x_flash; extern struct flash_driver str9xpec_flash; +extern struct flash_driver swm050_flash; extern struct flash_driver tms470_flash; extern struct flash_driver virtual_flash; extern struct flash_driver w600_flash; @@ -146,6 +147,7 @@ static struct flash_driver *flash_drivers[] = { &str7x_flash, &str9x_flash, &str9xpec_flash, + &swm050_flash, &tms470_flash, &virtual_flash, &xcf_flash, diff --git a/src/flash/nor/swm050.c b/src/flash/nor/swm050.c new file mode 100644 index 0000000..2856653 --- /dev/null +++ b/src/flash/nor/swm050.c @@ -0,0 +1,347 @@ +/*************************************************************************** + * Copyright (C) 2019 Icenowy Zheng <[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. * + * * + * 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 <target/image.h> + +#define SWM050_FLASH_PAGE_SIZE 0x200 +#define SWM050_FLASH_PAGES 16 + +#define SWM050_CPU_ID 0xE000ED00 +#define SWM050_CPU_ID_VAL 0x410CC200 + +#define SWM050_FLASH_REG1 0x1F000000 +#define SWM050_FLASH_REG2 0x1F000038 +#define SWM050_FLASH_KEY 0xAAAAAAAA + +#define SWM050_SYSCTL_CFG_0 0x400F0000 +#define SWM050_SYSCTL_DBLF 0x400F0008 + +static int swm050_erase(struct flash_bank *bank, int first, int last) +{ + struct target *target = bank->target; + uint32_t cfg_0_val, dblf_val; + int retval, curr_page; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Save original clock configuration registers value */ + retval = target_read_u32(target, SWM050_SYSCTL_CFG_0, &cfg_0_val); + if (retval != ERROR_OK) + return retval; + retval = target_read_u32(target, SWM050_SYSCTL_DBLF, &dblf_val); + if (retval != ERROR_OK) + return retval; + + /* Configure clock to 18MHz */ + retval = target_write_u32(target, SWM050_SYSCTL_CFG_0, 0x1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_SYSCTL_DBLF, 0x0); + if (retval != ERROR_OK) + return retval; + + alive_sleep(1); + + /* Perform erase */ + retval = target_write_u32(target, SWM050_FLASH_REG1, 0x4); + if (retval != ERROR_OK) + return retval; + + for (curr_page = first; curr_page <= last; curr_page++) { + retval = target_write_u32(target, SWM050_FLASH_PAGE_SIZE * curr_page, SWM050_FLASH_KEY); + if (retval != ERROR_OK) + return retval; + + alive_sleep(1); + } + + /* Close flash interface */ + retval = target_write_u32(target, SWM050_FLASH_REG1, 0x0); + if (retval != ERROR_OK) + return retval; + + /* Restore clock configuration */ + retval = target_write_u32(target, SWM050_SYSCTL_CFG_0, cfg_0_val); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_SYSCTL_DBLF, dblf_val); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int swm050_write(struct flash_bank *bank, const uint8_t *buffer, uint32_t offset, uint32_t count) +{ + struct target *target = bank->target; + uint32_t cfg_0_val, dblf_val, words_remaining; + int retval; + + /* Check offset alignment */ + if (offset % 4) { + LOG_ERROR("offset 0x%" PRIx32 " breaks required 4-byte alignment", offset); + return ERROR_FLASH_DST_BREAKS_ALIGNMENT; + } + + /* Pad the buffer for word align */ + if (count % 4) { + uint32_t new_count = (count / 4 + 1) * 4; + uint8_t *new_buffer = malloc(new_count); + + if (new_buffer == NULL) { + LOG_ERROR("data to write not word aligned and no memory for padding buffer"); + return ERROR_FAIL; + } + LOG_INFO("data to write not word aligned, padding with 0xff"); + + buffer = memcpy(new_buffer, buffer, count); + + if (count % 4 == 1) + new_buffer[count++] = 0xff; + if (count % 4 == 2) + new_buffer[count++] = 0xff; + if (count % 4 == 3) + new_buffer[count++] = 0xff; + } + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Save original clock configuration registers value */ + retval = target_read_u32(target, SWM050_SYSCTL_CFG_0, &cfg_0_val); + if (retval != ERROR_OK) + return retval; + retval = target_read_u32(target, SWM050_SYSCTL_DBLF, &dblf_val); + if (retval != ERROR_OK) + return retval; + + /* Configure clock to 18MHz */ + retval = target_write_u32(target, SWM050_SYSCTL_CFG_0, 0x1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_SYSCTL_DBLF, 0x0); + if (retval != ERROR_OK) + return retval; + + alive_sleep(1); + + /* Perform write */ + retval = target_write_u32(target, SWM050_FLASH_REG1, 0x1); + if (retval != ERROR_OK) + return retval; + + words_remaining = count / 4; + + while (words_remaining) { + uint32_t val; + memcpy(&val, buffer, sizeof(uint32_t)); + + retval = target_write_u32(target, offset, val); + if (retval != ERROR_OK) + return retval; + + words_remaining--; + offset += 4; + buffer += 4; + } + + /* Close flash interface */ + retval = target_write_u32(target, SWM050_FLASH_REG1, 0x0); + if (retval != ERROR_OK) + return retval; + + /* Restore clock configuration */ + retval = target_write_u32(target, SWM050_SYSCTL_CFG_0, cfg_0_val); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_SYSCTL_DBLF, dblf_val); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +static int swm050_info(struct flash_bank *bank, char *buf, int buf_size) +{ + snprintf(buf, buf_size, "swm050 flash driver"); + return ERROR_OK; +} + +static int swm050_probe(struct flash_bank *bank) +{ + return ERROR_OK; +} + +static int swm050_mass_erase(struct flash_bank *bank) +{ + struct target *target = bank->target; + uint32_t cfg_0_val, dblf_val; + int retval; + + if (target->state != TARGET_HALTED) { + LOG_ERROR("Target not halted"); + return ERROR_TARGET_NOT_HALTED; + } + + /* Save original clock configuration registers value */ + retval = target_read_u32(target, SWM050_SYSCTL_CFG_0, &cfg_0_val); + if (retval != ERROR_OK) + return retval; + retval = target_read_u32(target, SWM050_SYSCTL_DBLF, &dblf_val); + if (retval != ERROR_OK) + return retval; + + /* Configure clock to 18MHz */ + retval = target_write_u32(target, SWM050_SYSCTL_CFG_0, 0x1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_SYSCTL_DBLF, 0x0); + if (retval != ERROR_OK) + return retval; + + alive_sleep(1); + + /* Perform mass erase */ + retval = target_write_u32(target, SWM050_FLASH_REG1, 0x6); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_FLASH_REG2, 0x1); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, 0x0, SWM050_FLASH_KEY); + if (retval != ERROR_OK) + return retval; + + alive_sleep(1); + + /* Close flash interface */ + retval = target_write_u32(target, SWM050_FLASH_REG1, 0x0); + if (retval != ERROR_OK) + return retval; + + /* Restore clock configuration */ + retval = target_write_u32(target, SWM050_SYSCTL_CFG_0, cfg_0_val); + if (retval != ERROR_OK) + return retval; + retval = target_write_u32(target, SWM050_SYSCTL_DBLF, dblf_val); + if (retval != ERROR_OK) + return retval; + + return ERROR_OK; +} + +COMMAND_HANDLER(swm050_handle_mass_erase_command) +{ + int i; + + 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; + + retval = swm050_mass_erase(bank); + if (retval == ERROR_OK) { + /* set all sectors as erased */ + for (i = 0; i < bank->num_sectors; i++) + bank->sectors[i].is_erased = 1; + + command_print(CMD_CTX, "swm050 mass erase complete"); + } else + command_print(CMD_CTX, "swm050 mass erase failed"); + + return retval; +} + +FLASH_BANK_COMMAND_HANDLER(swm050_flash_bank_command) +{ + uint32_t offset; + int i; + + if (bank->sectors) { + free(bank->sectors); + bank->sectors = NULL; + } + bank->default_padded_value = bank->erased_value = 0xff; + + bank->base = 0x0; + bank->size = SWM050_FLASH_PAGE_SIZE * SWM050_FLASH_PAGES; + + bank->num_sectors = SWM050_FLASH_PAGES; + bank->sectors = alloc_block_array(0, SWM050_FLASH_PAGE_SIZE, SWM050_FLASH_PAGES); + if (!bank->sectors) + return ERROR_FAIL; + + offset = 0; + for (i = 0; i < bank->num_sectors; i++) { + bank->sectors[i].offset = offset; + bank->sectors[i].size = SWM050_FLASH_PAGE_SIZE; + offset += bank->sectors[i].size; + bank->sectors[i].is_erased = -1; + bank->sectors[i].is_protected = 0; + } + + return ERROR_OK; +} + +static const struct command_registration swm050_exec_command_handlers[] = { + { + .name = "mass_erase", + .handler = swm050_handle_mass_erase_command, + .mode = COMMAND_EXEC, + .usage = "bank_id", + .help = "Erase entire flash device.", + }, + COMMAND_REGISTRATION_DONE +}; + +static const struct command_registration swm050_command_handlers[] = { + { + .name = "swm050", + .mode = COMMAND_ANY, + .help = "swm050 flash command group", + .usage = "", + .chain = swm050_exec_command_handlers, + }, + COMMAND_REGISTRATION_DONE +}; + +struct flash_driver swm050_flash = { + .name = "swm050", + .commands = swm050_command_handlers, + .flash_bank_command = swm050_flash_bank_command, + .erase = swm050_erase, + .write = swm050_write, + .read = default_flash_read, + .probe = swm050_probe, + .auto_probe = swm050_probe, + .erase_check = default_flash_blank_check, + .info = swm050_info, + .free_driver_priv = default_flash_free_driver_priv, +}; -- _______________________________________________ OpenOCD-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/openocd-devel
