stm32lx: refactor and add support for STM32L0xx
This adds initial support for the STM32L0 family, specifically the ID code 417 variant. The 'L0 has 128B rather than 256B pages as well as a different number of pages per sector. It also has several key registers and register sets in different locations from the STM32L1xx parts. This change therefore takes the opportunity to reorganize part information into a const table (it was previously determined by a set of control statements) and abstracts away some of the low-level details to make them generic for L1 and L0 parts. We also include the first bank's size (for dual bank parts) in the new device information table (and correct that size for the 0x437 variant which is 256 rather than 192KB). The 'L0 parts will not use the built-in loader/helper for Flash writing. Tested on STM32L053 (dicovery board and Nucleo board) and STM32L152 (discovery board). Change-Id: I57f7a8ab02caee266de71b31ae82a50d85728a0b Signed-off-by: Andrey Yurovsky <yurovsky@gmail.com> Reviewed-on: http://openocd.zylin.com/2200 Tested-by: jenkins Reviewed-by: Andreas Fritiofson <andreas.fritiofson@gmail.com>__archive__
parent
d50cc1bfea
commit
3751214b9c
|
@ -36,16 +36,15 @@
|
||||||
|
|
||||||
/* stm32lx flash register locations */
|
/* stm32lx flash register locations */
|
||||||
|
|
||||||
#define FLASH_BASE 0x40023C00
|
#define FLASH_ACR 0x00
|
||||||
#define FLASH_ACR 0x40023C00
|
#define FLASH_PECR 0x04
|
||||||
#define FLASH_PECR 0x40023C04
|
#define FLASH_PDKEYR 0x08
|
||||||
#define FLASH_PDKEYR 0x40023C08
|
#define FLASH_PEKEYR 0x0C
|
||||||
#define FLASH_PEKEYR 0x40023C0C
|
#define FLASH_PRGKEYR 0x10
|
||||||
#define FLASH_PRGKEYR 0x40023C10
|
#define FLASH_OPTKEYR 0x14
|
||||||
#define FLASH_OPTKEYR 0x40023C14
|
#define FLASH_SR 0x18
|
||||||
#define FLASH_SR 0x40023C18
|
#define FLASH_OBR 0x1C
|
||||||
#define FLASH_OBR 0x40023C1C
|
#define FLASH_WRPR 0x20
|
||||||
#define FLASH_WRPR 0x40023C20
|
|
||||||
|
|
||||||
/* FLASH_ACR bites */
|
/* FLASH_ACR bites */
|
||||||
#define FLASH_ACR__LATENCY (1<<0)
|
#define FLASH_ACR__LATENCY (1<<0)
|
||||||
|
@ -87,43 +86,123 @@
|
||||||
|
|
||||||
/* other registers */
|
/* other registers */
|
||||||
#define DBGMCU_IDCODE 0xE0042000
|
#define DBGMCU_IDCODE 0xE0042000
|
||||||
#define F_SIZE 0x1FF8004C
|
#define DBGMCU_IDCODE_L0 0x40015800
|
||||||
#define F_SIZE_MP 0x1FF800CC /* on 0x427 Medium+ and 0x436 HD devices */
|
|
||||||
|
|
||||||
/* Constants */
|
/* Constants */
|
||||||
#define FLASH_PAGE_SIZE 256
|
|
||||||
#define FLASH_SECTOR_SIZE 4096
|
#define FLASH_SECTOR_SIZE 4096
|
||||||
#define FLASH_PAGES_PER_SECTOR 16
|
|
||||||
#define FLASH_BANK0_ADDRESS 0x08000000
|
#define FLASH_BANK0_ADDRESS 0x08000000
|
||||||
|
|
||||||
/* stm32lx option byte register location */
|
|
||||||
#define OB_RDP 0x1FF80000
|
|
||||||
#define OB_USER 0x1FF80004
|
|
||||||
#define OB_WRP0_1 0x1FF80008
|
|
||||||
#define OB_WRP2_3 0x1FF8000C
|
|
||||||
|
|
||||||
/* OB_RDP values */
|
|
||||||
#define OB_RDP__LEVEL0 0xFF5500AA
|
|
||||||
#define OB_RDP__LEVEL1 0xFFFF0000
|
|
||||||
|
|
||||||
/* stm32lx RCC register locations */
|
|
||||||
#define RCC_CR 0x40023800
|
|
||||||
#define RCC_ICSCR 0x40023804
|
|
||||||
#define RCC_CFGR 0x40023808
|
|
||||||
|
|
||||||
/* RCC_ICSCR bits */
|
|
||||||
#define RCC_ICSCR__MSIRANGE_MASK (7<<13)
|
|
||||||
|
|
||||||
static int stm32lx_unlock_program_memory(struct flash_bank *bank);
|
static int stm32lx_unlock_program_memory(struct flash_bank *bank);
|
||||||
static int stm32lx_lock_program_memory(struct flash_bank *bank);
|
static int stm32lx_lock_program_memory(struct flash_bank *bank);
|
||||||
static int stm32lx_enable_write_half_page(struct flash_bank *bank);
|
static int stm32lx_enable_write_half_page(struct flash_bank *bank);
|
||||||
static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
|
static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
|
||||||
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
|
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
|
||||||
|
|
||||||
|
struct stm32lx_rev {
|
||||||
|
uint16_t rev;
|
||||||
|
const char *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct stm32lx_part_info {
|
||||||
|
uint16_t id;
|
||||||
|
const char *device_str;
|
||||||
|
const struct stm32lx_rev *revs;
|
||||||
|
size_t num_revs;
|
||||||
|
unsigned int page_size;
|
||||||
|
unsigned int pages_per_sector;
|
||||||
|
uint16_t max_flash_size_kb;
|
||||||
|
uint16_t first_bank_size_kb; /* used when has_dual_banks is true */
|
||||||
|
bool has_dual_banks;
|
||||||
|
|
||||||
|
uint32_t flash_base; /* Flash controller registers location */
|
||||||
|
uint32_t fsize_base; /* Location of FSIZE register */
|
||||||
|
};
|
||||||
|
|
||||||
struct stm32lx_flash_bank {
|
struct stm32lx_flash_bank {
|
||||||
int probed;
|
int probed;
|
||||||
bool has_dual_banks;
|
uint32_t idcode;
|
||||||
uint32_t user_bank_size;
|
uint32_t user_bank_size;
|
||||||
|
uint32_t flash_base;
|
||||||
|
|
||||||
|
const struct stm32lx_part_info *part_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stm32lx_rev stm32_416_revs[] = {
|
||||||
|
{ 0x1000, "A" }, { 0x1008, "Y" }, { 0x1018, "X" }, { 0x1038, "W" },
|
||||||
|
{ 0x1078, "V" },
|
||||||
|
};
|
||||||
|
static const struct stm32lx_rev stm32_417_revs[] = {
|
||||||
|
{ 0x1000, "A" }, { 0x1008, "Z" },
|
||||||
|
};
|
||||||
|
static const struct stm32lx_rev stm32_427_revs[] = {
|
||||||
|
{ 0x1018, "A" },
|
||||||
|
};
|
||||||
|
static const struct stm32lx_rev stm32_436_revs[] = {
|
||||||
|
{ 0x1000, "A" }, { 0x1008, "Z" }, { 0x1018, "Y" },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct stm32lx_part_info stm32lx_parts[] = {
|
||||||
|
{
|
||||||
|
.id = 0x416,
|
||||||
|
.revs = stm32_416_revs,
|
||||||
|
.num_revs = ARRAY_SIZE(stm32_416_revs),
|
||||||
|
.device_str = "STM32L1xx (Low/Medium Density)",
|
||||||
|
.page_size = 256,
|
||||||
|
.pages_per_sector = 16,
|
||||||
|
.max_flash_size_kb = 128,
|
||||||
|
.has_dual_banks = false,
|
||||||
|
.flash_base = 0x40023C00,
|
||||||
|
.fsize_base = 0x1FF8004C,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = 0x417,
|
||||||
|
.revs = stm32_417_revs,
|
||||||
|
.num_revs = ARRAY_SIZE(stm32_417_revs),
|
||||||
|
.device_str = "STM32L0xx",
|
||||||
|
.page_size = 128,
|
||||||
|
.pages_per_sector = 32,
|
||||||
|
.max_flash_size_kb = 64,
|
||||||
|
.has_dual_banks = false,
|
||||||
|
.flash_base = 0x40022000,
|
||||||
|
.fsize_base = 0x1FF8007C,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = 0x427,
|
||||||
|
.revs = stm32_427_revs,
|
||||||
|
.num_revs = ARRAY_SIZE(stm32_427_revs),
|
||||||
|
.device_str = "STM32L1xx (Medium+ Density)",
|
||||||
|
.page_size = 256,
|
||||||
|
.pages_per_sector = 16,
|
||||||
|
.max_flash_size_kb = 256,
|
||||||
|
.first_bank_size_kb = 192,
|
||||||
|
.has_dual_banks = true,
|
||||||
|
.flash_base = 0x40023C00,
|
||||||
|
.fsize_base = 0x1FF800CC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = 0x436,
|
||||||
|
.revs = stm32_436_revs,
|
||||||
|
.num_revs = ARRAY_SIZE(stm32_436_revs),
|
||||||
|
.device_str = "STM32L1xx (Medium+/High Density)",
|
||||||
|
.page_size = 256,
|
||||||
|
.pages_per_sector = 16,
|
||||||
|
.max_flash_size_kb = 384,
|
||||||
|
.first_bank_size_kb = 192,
|
||||||
|
.has_dual_banks = true,
|
||||||
|
.flash_base = 0x40023C00,
|
||||||
|
.fsize_base = 0x1FF800CC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.id = 0x437,
|
||||||
|
.device_str = "STM32L1xx (Medium+/High Density)",
|
||||||
|
.page_size = 256,
|
||||||
|
.pages_per_sector = 16,
|
||||||
|
.max_flash_size_kb = 512,
|
||||||
|
.first_bank_size_kb = 256,
|
||||||
|
.has_dual_banks = true,
|
||||||
|
.flash_base = 0x40023C00,
|
||||||
|
.fsize_base = 0x1FF800CC,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* flash bank stm32lx <base> <size> 0 0 <target#>
|
/* flash bank stm32lx <base> <size> 0 0 <target#>
|
||||||
|
@ -146,7 +225,6 @@ FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
|
||||||
bank->driver_priv = stm32lx_info;
|
bank->driver_priv = stm32lx_info;
|
||||||
|
|
||||||
stm32lx_info->probed = 0;
|
stm32lx_info->probed = 0;
|
||||||
stm32lx_info->has_dual_banks = false;
|
|
||||||
stm32lx_info->user_bank_size = bank->size;
|
stm32lx_info->user_bank_size = bank->size;
|
||||||
|
|
||||||
/* the stm32l erased value is 0x00 */
|
/* the stm32l erased value is 0x00 */
|
||||||
|
@ -159,6 +237,7 @@ static int stm32lx_protect_check(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
int retval;
|
int retval;
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
|
|
||||||
uint32_t wrpr;
|
uint32_t wrpr;
|
||||||
|
|
||||||
|
@ -166,11 +245,12 @@ static int stm32lx_protect_check(struct flash_bank *bank)
|
||||||
* Read the WRPR word, and check each bit (corresponding to each
|
* Read the WRPR word, and check each bit (corresponding to each
|
||||||
* flash sector
|
* flash sector
|
||||||
*/
|
*/
|
||||||
retval = target_read_u32(target, FLASH_WRPR, &wrpr);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_WRPR,
|
||||||
|
&wrpr);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < bank->num_sectors; i++) {
|
||||||
if (wrpr & (1 << i))
|
if (wrpr & (1 << i))
|
||||||
bank->sectors[i].is_protected = 1;
|
bank->sectors[i].is_protected = 1;
|
||||||
else
|
else
|
||||||
|
@ -216,6 +296,9 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
|
||||||
uint32_t offset, uint32_t count)
|
uint32_t offset, uint32_t count)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
|
|
||||||
|
uint32_t hp_nb = stm32lx_info->part_info->page_size / 2;
|
||||||
uint32_t buffer_size = 16384;
|
uint32_t buffer_size = 16384;
|
||||||
struct working_area *write_algorithm;
|
struct working_area *write_algorithm;
|
||||||
struct working_area *source;
|
struct working_area *source;
|
||||||
|
@ -242,12 +325,12 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
|
||||||
0x93, 0x42, /* cmp r3, r2 */
|
0x93, 0x42, /* cmp r3, r2 */
|
||||||
0xf8, 0xd3, /* bcc write_word */
|
0xf8, 0xd3, /* bcc write_word */
|
||||||
0x00, 0xbe, /* bkpt 0 */
|
0x00, 0xbe, /* bkpt 0 */
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Check if there is an even number of half pages (128bytes) */
|
/* Make sure we're performing a half-page aligned write. */
|
||||||
if (count % 128) {
|
if (count % hp_nb) {
|
||||||
LOG_ERROR("there should be an even number "
|
LOG_ERROR("The byte count must be %" PRIu32 "B-aligned but count is %" PRIi32 "B)", hp_nb, count);
|
||||||
"of half pages = 128 bytes (count = %" PRIi32 " bytes)", count);
|
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -275,7 +358,7 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
|
||||||
else
|
else
|
||||||
buffer_size /= 2;
|
buffer_size /= 2;
|
||||||
|
|
||||||
if (buffer_size <= 256) {
|
if (buffer_size <= stm32lx_info->part_info->page_size) {
|
||||||
/* we already allocated the writing code, but failed to get a
|
/* we already allocated the writing code, but failed to get a
|
||||||
* buffer, free the algorithm */
|
* buffer, free the algorithm */
|
||||||
target_free_working_area(target, write_algorithm);
|
target_free_working_area(target, write_algorithm);
|
||||||
|
@ -372,7 +455,7 @@ static int stm32lx_write_half_pages(struct flash_bank *bank, const uint8_t *buff
|
||||||
|
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
uint32_t this_count;
|
uint32_t this_count;
|
||||||
this_count = (count > 128) ? 128 : count;
|
this_count = (count > hp_nb) ? hp_nb : count;
|
||||||
|
|
||||||
/* Write the next half pages */
|
/* Write the next half pages */
|
||||||
retval = target_write_buffer(target, address, this_count, buffer);
|
retval = target_write_buffer(target, address, this_count, buffer);
|
||||||
|
@ -407,7 +490,9 @@ static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||||
uint32_t offset, uint32_t count)
|
uint32_t offset, uint32_t count)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
|
|
||||||
|
uint32_t hp_nb = stm32lx_info->part_info->page_size / 2;
|
||||||
uint32_t halfpages_number;
|
uint32_t halfpages_number;
|
||||||
uint32_t bytes_remaining = 0;
|
uint32_t bytes_remaining = 0;
|
||||||
uint32_t address = bank->base + offset;
|
uint32_t address = bank->base + offset;
|
||||||
|
@ -431,8 +516,8 @@ static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||||
/* first we need to write any unaligned head bytes upto
|
/* first we need to write any unaligned head bytes upto
|
||||||
* the next 128 byte page */
|
* the next 128 byte page */
|
||||||
|
|
||||||
if (offset % 128)
|
if (offset % hp_nb)
|
||||||
bytes_remaining = MIN(count, 128 - (offset % 128));
|
bytes_remaining = MIN(count, hp_nb - (offset % hp_nb));
|
||||||
|
|
||||||
while (bytes_remaining > 0) {
|
while (bytes_remaining > 0) {
|
||||||
uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
|
uint8_t value[4] = {0xff, 0xff, 0xff, 0xff};
|
||||||
|
@ -458,13 +543,13 @@ static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||||
count -= bytes_written;
|
count -= bytes_written;
|
||||||
|
|
||||||
/* this should always pass this check here */
|
/* this should always pass this check here */
|
||||||
assert((offset % 128) == 0);
|
assert((offset % hp_nb) == 0);
|
||||||
|
|
||||||
/* calculate half pages */
|
/* calculate half pages */
|
||||||
halfpages_number = count / 128;
|
halfpages_number = count / hp_nb;
|
||||||
|
|
||||||
if (halfpages_number) {
|
if (halfpages_number) {
|
||||||
retval = stm32lx_write_half_pages(bank, buffer + bytes_written, offset, 128 * halfpages_number);
|
retval = stm32lx_write_half_pages(bank, buffer + bytes_written, offset, hp_nb * halfpages_number);
|
||||||
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
|
if (retval == ERROR_TARGET_RESOURCE_NOT_AVAILABLE) {
|
||||||
/* attempt slow memory writes */
|
/* attempt slow memory writes */
|
||||||
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
|
LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
|
||||||
|
@ -476,7 +561,7 @@ static int stm32lx_write(struct flash_bank *bank, const uint8_t *buffer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* write any remaining bytes */
|
/* write any remaining bytes */
|
||||||
uint32_t page_bytes_written = 128 * halfpages_number;
|
uint32_t page_bytes_written = hp_nb * halfpages_number;
|
||||||
bytes_written += page_bytes_written;
|
bytes_written += page_bytes_written;
|
||||||
address += page_bytes_written;
|
address += page_bytes_written;
|
||||||
bytes_remaining = count - page_bytes_written;
|
bytes_remaining = count - page_bytes_written;
|
||||||
|
@ -513,65 +598,56 @@ reset_pg_and_lock:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int stm32lx_read_id_code(struct target *target, uint32_t *id)
|
||||||
|
{
|
||||||
|
/* read stm32 device id register */
|
||||||
|
int retval = target_read_u32(target, DBGMCU_IDCODE, id);
|
||||||
|
if (retval != ERROR_OK)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/* STM32L0 parts will have 0 there, try reading the L0's location for
|
||||||
|
* DBG_IDCODE in case this is an L0 part. */
|
||||||
|
if (*id == 0)
|
||||||
|
retval = target_read_u32(target, DBGMCU_IDCODE_L0, id);
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
static int stm32lx_probe(struct flash_bank *bank)
|
static int stm32lx_probe(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
int i;
|
int i;
|
||||||
uint16_t flash_size_in_kb;
|
uint16_t flash_size_in_kb;
|
||||||
uint16_t max_flash_size_in_kb;
|
|
||||||
uint32_t device_id;
|
uint32_t device_id;
|
||||||
uint32_t base_address = FLASH_BANK0_ADDRESS;
|
uint32_t base_address = FLASH_BANK0_ADDRESS;
|
||||||
uint32_t second_bank_base;
|
uint32_t second_bank_base;
|
||||||
uint32_t first_bank_size_in_kb;
|
|
||||||
|
|
||||||
stm32lx_info->probed = 0;
|
stm32lx_info->probed = 0;
|
||||||
|
|
||||||
/* read stm32 device id register */
|
int retval = stm32lx_read_id_code(bank->target, &device_id);
|
||||||
int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
|
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
stm32lx_info->idcode = device_id;
|
||||||
|
|
||||||
LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id);
|
LOG_DEBUG("device id = 0x%08" PRIx32 "", device_id);
|
||||||
|
|
||||||
/* set max flash size depending on family */
|
for (unsigned int n = 0; n < ARRAY_SIZE(stm32lx_parts); n++) {
|
||||||
switch (device_id & 0xfff) {
|
if ((device_id & 0xfff) == stm32lx_parts[n].id)
|
||||||
case 0x416:
|
stm32lx_info->part_info = &stm32lx_parts[n];
|
||||||
max_flash_size_in_kb = 128;
|
}
|
||||||
break;
|
|
||||||
case 0x427:
|
if (!stm32lx_info->part_info) {
|
||||||
/* single bank, high density */
|
|
||||||
max_flash_size_in_kb = 256;
|
|
||||||
break;
|
|
||||||
case 0x436:
|
|
||||||
/* According to ST, the devices with id 0x436 have dual bank flash and comes with
|
|
||||||
* a total flash size of 384k or 256kb. However, the first bank is always 192kb,
|
|
||||||
* and second one holds the rest. The reason is that the 256kb version is actually
|
|
||||||
* the same physical flash but only the first 256kb are verified.
|
|
||||||
*/
|
|
||||||
max_flash_size_in_kb = 384;
|
|
||||||
first_bank_size_in_kb = 192;
|
|
||||||
stm32lx_info->has_dual_banks = true;
|
|
||||||
break;
|
|
||||||
case 0x437:
|
|
||||||
/* Dual bank, high density */
|
|
||||||
max_flash_size_in_kb = 512;
|
|
||||||
first_bank_size_in_kb = 192;
|
|
||||||
stm32lx_info->has_dual_banks = true;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LOG_WARNING("Cannot identify target as a STM32L family.");
|
LOG_WARNING("Cannot identify target as a STM32L family.");
|
||||||
return ERROR_FAIL;
|
return ERROR_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the flash size from target. 0x427 and 0x436 devices use a
|
stm32lx_info->flash_base = stm32lx_info->part_info->flash_base;
|
||||||
* different location for the Flash Size register, please see RM0038 r8 or
|
|
||||||
* newer. */
|
/* Get the flash size from target. */
|
||||||
if ((device_id & 0xfff) == 0x427 || (device_id & 0xfff) == 0x436 ||
|
retval = target_read_u16(target, stm32lx_info->part_info->fsize_base,
|
||||||
(device_id & 0xfff) == 0x437)
|
&flash_size_in_kb);
|
||||||
retval = target_read_u16(target, F_SIZE_MP, &flash_size_in_kb);
|
|
||||||
else
|
|
||||||
retval = target_read_u16(target, F_SIZE, &flash_size_in_kb);
|
|
||||||
|
|
||||||
/* 0x436 devices report their flash size as a 0 or 1 code indicating 384K
|
/* 0x436 devices report their flash size as a 0 or 1 code indicating 384K
|
||||||
* or 256K, respectively. Please see RM0038 r8 or newer and refer to
|
* or 256K, respectively. Please see RM0038 r8 or newer and refer to
|
||||||
|
@ -587,26 +663,29 @@ static int stm32lx_probe(struct flash_bank *bank)
|
||||||
* default to max target family */
|
* default to max target family */
|
||||||
if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
|
if (retval != ERROR_OK || flash_size_in_kb == 0xffff || flash_size_in_kb == 0) {
|
||||||
LOG_WARNING("STM32L flash size failed, probe inaccurate - assuming %dk flash",
|
LOG_WARNING("STM32L flash size failed, probe inaccurate - assuming %dk flash",
|
||||||
max_flash_size_in_kb);
|
stm32lx_info->part_info->max_flash_size_kb);
|
||||||
flash_size_in_kb = max_flash_size_in_kb;
|
flash_size_in_kb = stm32lx_info->part_info->max_flash_size_kb;
|
||||||
} else if (flash_size_in_kb > max_flash_size_in_kb) {
|
} else if (flash_size_in_kb > stm32lx_info->part_info->max_flash_size_kb) {
|
||||||
LOG_WARNING("STM32L probed flash size assumed incorrect since FLASH_SIZE=%dk > %dk, - assuming %dk flash",
|
LOG_WARNING("STM32L probed flash size assumed incorrect since FLASH_SIZE=%dk > %dk, - assuming %dk flash",
|
||||||
flash_size_in_kb, max_flash_size_in_kb, max_flash_size_in_kb);
|
flash_size_in_kb, stm32lx_info->part_info->max_flash_size_kb,
|
||||||
flash_size_in_kb = max_flash_size_in_kb;
|
stm32lx_info->part_info->max_flash_size_kb);
|
||||||
|
flash_size_in_kb = stm32lx_info->part_info->max_flash_size_kb;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stm32lx_info->has_dual_banks) {
|
if (stm32lx_info->part_info->has_dual_banks) {
|
||||||
/* Use the configured base address to determine if this is the first or second flash bank.
|
/* Use the configured base address to determine if this is the first or second flash bank.
|
||||||
* Verify that the base address is reasonably correct and determine the flash bank size
|
* Verify that the base address is reasonably correct and determine the flash bank size
|
||||||
*/
|
*/
|
||||||
second_bank_base = base_address + first_bank_size_in_kb * 1024;
|
second_bank_base = base_address +
|
||||||
|
stm32lx_info->part_info->first_bank_size_kb * 1024;
|
||||||
if (bank->base == second_bank_base) {
|
if (bank->base == second_bank_base) {
|
||||||
/* This is the second bank */
|
/* This is the second bank */
|
||||||
base_address = second_bank_base;
|
base_address = second_bank_base;
|
||||||
flash_size_in_kb = flash_size_in_kb - first_bank_size_in_kb;
|
flash_size_in_kb = flash_size_in_kb -
|
||||||
|
stm32lx_info->part_info->first_bank_size_kb;
|
||||||
} else if (bank->base == 0 || bank->base == base_address) {
|
} else if (bank->base == 0 || bank->base == base_address) {
|
||||||
/* This is the first bank */
|
/* This is the first bank */
|
||||||
flash_size_in_kb = first_bank_size_in_kb;
|
flash_size_in_kb = stm32lx_info->part_info->first_bank_size_kb;
|
||||||
} else {
|
} else {
|
||||||
LOG_WARNING("STM32L flash bank base address config is incorrect."
|
LOG_WARNING("STM32L flash bank base address config is incorrect."
|
||||||
" 0x%" PRIx32 " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
|
" 0x%" PRIx32 " but should rather be 0x%" PRIx32 " or 0x%" PRIx32,
|
||||||
|
@ -626,9 +705,6 @@ static int stm32lx_probe(struct flash_bank *bank)
|
||||||
LOG_INFO("ignoring flash probed value, using configured bank size: %dkbytes", flash_size_in_kb);
|
LOG_INFO("ignoring flash probed value, using configured bank size: %dkbytes", flash_size_in_kb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* STM32L - we have 32 sectors, 16 pages per sector -> 512 pages
|
|
||||||
* 16 pages for a protection area */
|
|
||||||
|
|
||||||
/* calculate numbers of sectors (4kB per sector) */
|
/* calculate numbers of sectors (4kB per sector) */
|
||||||
int num_sectors = (flash_size_in_kb * 1024) / FLASH_SECTOR_SIZE;
|
int num_sectors = (flash_size_in_kb * 1024) / FLASH_SECTOR_SIZE;
|
||||||
|
|
||||||
|
@ -718,92 +794,47 @@ static int stm32lx_erase_check(struct flash_bank *bank)
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* This method must return a string displaying information about the bank */
|
||||||
static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
|
static int stm32lx_get_info(struct flash_bank *bank, char *buf, int buf_size)
|
||||||
{
|
{
|
||||||
/* This method must return a string displaying information about the bank */
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
|
|
||||||
uint32_t dbgmcu_idcode;
|
if (!stm32lx_info->probed) {
|
||||||
|
int retval = stm32lx_probe(bank);
|
||||||
/* read stm32 device id register */
|
if (retval != ERROR_OK) {
|
||||||
int retval = target_read_u32(bank->target, DBGMCU_IDCODE, &dbgmcu_idcode);
|
snprintf(buf, buf_size,
|
||||||
if (retval != ERROR_OK)
|
"Unable to find bank information.");
|
||||||
return retval;
|
return retval;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t device_id = dbgmcu_idcode & 0xfff;
|
|
||||||
uint16_t rev_id = dbgmcu_idcode >> 16;
|
const struct stm32lx_part_info *info = stm32lx_info->part_info;
|
||||||
const char *device_str;
|
|
||||||
|
if (info) {
|
||||||
const char *rev_str = NULL;
|
const char *rev_str = NULL;
|
||||||
|
uint16_t rev_id = stm32lx_info->idcode >> 16;
|
||||||
|
|
||||||
switch (device_id) {
|
for (unsigned int i = 0; i < info->num_revs; i++)
|
||||||
case 0x416:
|
if (rev_id == info->revs[i].rev)
|
||||||
device_str = "STM32L1xx (Low/Medium Density)";
|
rev_str = info->revs[i].str;
|
||||||
|
|
||||||
switch (rev_id) {
|
if (rev_str != NULL) {
|
||||||
case 0x1000:
|
snprintf(buf, buf_size,
|
||||||
rev_str = "A";
|
"%s - Rev: %s",
|
||||||
break;
|
stm32lx_info->part_info->device_str, rev_str);
|
||||||
|
} else {
|
||||||
case 0x1008:
|
snprintf(buf, buf_size,
|
||||||
rev_str = "Y";
|
"%s - Rev: unknown (0x%04x)",
|
||||||
break;
|
stm32lx_info->part_info->device_str, rev_id);
|
||||||
|
|
||||||
case 0x1018:
|
|
||||||
rev_str = "X";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1038:
|
|
||||||
rev_str = "W";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1078:
|
|
||||||
rev_str = "V";
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x427:
|
|
||||||
device_str = "STM32L1xx (Medium+ Density)";
|
|
||||||
|
|
||||||
switch (rev_id) {
|
|
||||||
case 0x1018:
|
|
||||||
rev_str = "A";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x436:
|
|
||||||
device_str = "STM32L1xx (Medium+/High Density)";
|
|
||||||
|
|
||||||
switch (rev_id) {
|
|
||||||
case 0x1000:
|
|
||||||
rev_str = "A";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1008:
|
|
||||||
rev_str = "Z";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x1018:
|
|
||||||
rev_str = "Y";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 0x437:
|
|
||||||
device_str = "STM32L1xx (Medium+/High Density)";
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
snprintf(buf, buf_size, "Cannot identify target as a STM32L1");
|
|
||||||
return ERROR_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rev_str != NULL)
|
|
||||||
snprintf(buf, buf_size, "%s - Rev: %s", device_str, rev_str);
|
|
||||||
else
|
|
||||||
snprintf(buf, buf_size, "%s - Rev: unknown (0x%04x)", device_str, rev_id);
|
|
||||||
|
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
} else {
|
||||||
|
snprintf(buf, buf_size, "Cannot identify target as a STM32Lx");
|
||||||
|
|
||||||
|
return ERROR_FAIL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct command_registration stm32lx_exec_command_handlers[] = {
|
static const struct command_registration stm32lx_exec_command_handlers[] = {
|
||||||
|
@ -840,6 +871,7 @@ struct flash_driver stm32lx_flash = {
|
||||||
static int stm32lx_unlock_program_memory(struct flash_bank *bank)
|
static int stm32lx_unlock_program_memory(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
int retval;
|
int retval;
|
||||||
uint32_t reg32;
|
uint32_t reg32;
|
||||||
|
|
||||||
|
@ -849,7 +881,8 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* check flash is not already unlocked */
|
/* check flash is not already unlocked */
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -857,16 +890,19 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
|
||||||
return ERROR_OK;
|
return ERROR_OK;
|
||||||
|
|
||||||
/* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
|
/* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
|
||||||
retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR,
|
||||||
|
PEKEY1);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PEKEYR,
|
||||||
|
PEKEY2);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* Make sure it worked */
|
/* Make sure it worked */
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -875,15 +911,18 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
|
||||||
return ERROR_FLASH_OPERATION_FAILED;
|
return ERROR_FLASH_OPERATION_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY1);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PRGKEYR,
|
||||||
|
PRGKEY1);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY2);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PRGKEYR,
|
||||||
|
PRGKEY2);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
/* Make sure it worked */
|
/* Make sure it worked */
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -898,6 +937,7 @@ static int stm32lx_unlock_program_memory(struct flash_bank *bank)
|
||||||
static int stm32lx_enable_write_half_page(struct flash_bank *bank)
|
static int stm32lx_enable_write_half_page(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
int retval;
|
int retval;
|
||||||
uint32_t reg32;
|
uint32_t reg32;
|
||||||
|
|
||||||
|
@ -908,21 +948,25 @@ static int stm32lx_enable_write_half_page(struct flash_bank *bank)
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
reg32 |= FLASH_PECR__FPRG;
|
reg32 |= FLASH_PECR__FPRG;
|
||||||
retval = target_write_u32(target, FLASH_PECR, reg32);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
reg32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
reg32 |= FLASH_PECR__PROG;
|
reg32 |= FLASH_PECR__PROG;
|
||||||
retval = target_write_u32(target, FLASH_PECR, reg32);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
reg32);
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -930,26 +974,31 @@ static int stm32lx_enable_write_half_page(struct flash_bank *bank)
|
||||||
static int stm32lx_lock_program_memory(struct flash_bank *bank)
|
static int stm32lx_lock_program_memory(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
int retval;
|
int retval;
|
||||||
uint32_t reg32;
|
uint32_t reg32;
|
||||||
|
|
||||||
/* To lock the program memory, simply set the lock bit and lock PECR */
|
/* To lock the program memory, simply set the lock bit and lock PECR */
|
||||||
|
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
reg32 |= FLASH_PECR__PRGLOCK;
|
reg32 |= FLASH_PECR__PRGLOCK;
|
||||||
retval = target_write_u32(target, FLASH_PECR, reg32);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
reg32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
retval = target_read_u32(target, FLASH_PECR, ®32);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
®32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
reg32 |= FLASH_PECR__PELOCK;
|
reg32 |= FLASH_PECR__PELOCK;
|
||||||
retval = target_write_u32(target, FLASH_PECR, reg32);
|
retval = target_write_u32(target, stm32lx_info->flash_base + FLASH_PECR,
|
||||||
|
reg32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -959,11 +1008,12 @@ static int stm32lx_lock_program_memory(struct flash_bank *bank)
|
||||||
static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
|
static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
int retval;
|
int retval;
|
||||||
uint32_t reg32;
|
uint32_t reg32;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* To erase a sector (i.e. FLASH_PAGES_PER_SECTOR pages),
|
* To erase a sector (i.e. stm32lx_info->part_info.pages_per_sector pages),
|
||||||
* first unlock the memory, loop over the pages of this sector
|
* first unlock the memory, loop over the pages of this sector
|
||||||
* and write 0x0 to its first word.
|
* and write 0x0 to its first word.
|
||||||
*/
|
*/
|
||||||
|
@ -972,9 +1022,11 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
for (int page = 0; page < FLASH_PAGES_PER_SECTOR; page++) {
|
for (int page = 0; page < (int)stm32lx_info->part_info->pages_per_sector;
|
||||||
|
page++) {
|
||||||
reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
|
reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
|
||||||
retval = target_write_u32(target, FLASH_PECR, reg32);
|
retval = target_write_u32(target,
|
||||||
|
stm32lx_info->flash_base + FLASH_PECR, reg32);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -983,7 +1035,7 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
uint32_t addr = bank->base + bank->sectors[sector].offset + (page
|
uint32_t addr = bank->base + bank->sectors[sector].offset + (page
|
||||||
* FLASH_PAGE_SIZE);
|
* stm32lx_info->part_info->page_size);
|
||||||
retval = target_write_u32(target, addr, 0x0);
|
retval = target_write_u32(target, addr, 0x0);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -1003,13 +1055,15 @@ static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
|
||||||
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
|
static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
|
||||||
{
|
{
|
||||||
struct target *target = bank->target;
|
struct target *target = bank->target;
|
||||||
|
struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
|
||||||
uint32_t status;
|
uint32_t status;
|
||||||
int retval = ERROR_OK;
|
int retval = ERROR_OK;
|
||||||
int timeout = 100;
|
int timeout = 100;
|
||||||
|
|
||||||
/* wait for busy to clear */
|
/* wait for busy to clear */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
retval = target_read_u32(target, FLASH_SR, &status);
|
retval = target_read_u32(target, stm32lx_info->flash_base + FLASH_SR,
|
||||||
|
&status);
|
||||||
if (retval != ERROR_OK)
|
if (retval != ERROR_OK)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue