Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for STM32L4, SM32L1 option bytes write. #847

Merged
merged 2 commits into from
Mar 16, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion debian/rules
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/make -f
# See debhelper(7) (uncomment to enable)
# output every command that modifies files on the build system.
#DH_VERBOSE = 1
DH_VERBOSE = 1

# see EXAMPLES in dpkg-buildflags(1) and read /usr/share/dpkg/*
DPKG_EXPORT_BUILDFLAGS = 1
Expand Down
3 changes: 3 additions & 0 deletions include/stm32.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@
#define STM32_G0_OPTION_BYTES_BASE ((uint32_t)0x1FFF7800)
#define STM32_L0_CAT2_OPTION_BYTES_BASE ((uint32_t)0x1FF80000)
#define STM32_F2_OPTION_BYTES_BASE ((uint32_t)0x1FFFC000)
#define STM32_L496X_OPTION_BYTES_BASE ((uint32_t)0x1FFF7800)
#define STM32_L1_OPTION_BYTES_BASE ((uint32_t)0x1FF80000)

#endif /* STM32_H */
191 changes: 189 additions & 2 deletions src/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@

//32L4 register base is at FLASH_REGS_ADDR (0x40022000)
#define STM32L4_FLASH_KEYR (FLASH_REGS_ADDR + 0x08)
#define STM32L4_FLASH_OPTKEYR (FLASH_REGS_ADDR + 0x0C)
#define STM32L4_FLASH_SR (FLASH_REGS_ADDR + 0x10)
#define STM32L4_FLASH_CR (FLASH_REGS_ADDR + 0x14)
#define STM32L4_FLASH_OPTR (FLASH_REGS_ADDR + 0x20)
Expand All @@ -145,13 +146,16 @@
#define STM32L4_FLASH_SR_ERRMASK 0x3f8 /* SR [9:3] */

#define STM32L4_FLASH_CR_LOCK 31 /* Lock control register */
#define STM32L4_FLASH_CR_OPTLOCK 30 /* Lock option bytes */
#define STM32L4_FLASH_CR_PG 0 /* Program */
#define STM32L4_FLASH_CR_PER 1 /* Page erase */
#define STM32L4_FLASH_CR_MER1 2 /* Bank 1 erase */
#define STM32L4_FLASH_CR_MER2 15 /* Bank 2 erase */
#define STM32L4_FLASH_CR_STRT 16 /* Start command */
#define STM32L4_FLASH_CR_OPTSTRT 17 /* Start writing option bytes */
#define STM32L4_FLASH_CR_BKER 11 /* Bank select for page erase */
#define STM32L4_FLASH_CR_PNB 3 /* Page number (8 bits) */
#define STM32L4_FLASH_CR_OBL_LAUNCH 27 /* Option bytes reload */
// Bits requesting flash operations (useful when we want to clear them)
#define STM32L4_FLASH_CR_OPBITS \
((1lu<<STM32L4_FLASH_CR_PG) | (1lu<<STM32L4_FLASH_CR_PER) \
Expand All @@ -178,7 +182,11 @@
#define FLASH_OBR_OFF ((uint32_t) 0x1c)
#define FLASH_WRPR_OFF ((uint32_t) 0x20)


//STM32L1
#define STM32L1_FLASH_REGS_ADDR ((uint32_t)0x40023c00)
#define STM32L1_FLASH_PELOCK_BIT (1u << 0)
#define STM32L1_FLASH_OPTLOCK_BIT (1u << 2)
#define STM32L1_FLASH_OBL_LAUNCH_BIT (1u << 18)

//STM32F4
#define FLASH_F4_REGS_ADDR ((uint32_t)0x40023c00)
Expand Down Expand Up @@ -2662,6 +2670,176 @@ static int stlink_write_option_bytes_l0_cat2(stlink_t *sl, uint8_t* base, uint32
return 0;
}

/**
* Write option bytes
* @param sl
* @param addr of the memory mapped option bytes
* @param base option bytes to write
* @return 0 on success, -ve on failure.
*/
static int stlink_write_option_bytes_l1(stlink_t *sl, uint8_t* base, stm32_addr_t addr, uint32_t len) {

uint32_t val;
uint32_t data;

if(len != 4 && len != 8) {
ELOG("Wrong length for writting option bytes, must be 4 or 8 is %d\n", len);
return -1;
}
stlink_read_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PECR_OFF, &val);
if (val & STM32L1_FLASH_PELOCK_BIT) {
WLOG("Unlocking flash\n");
//Unlock data EEPROM and the FLASH_PECR register (reference page 74)
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PEKEYR_OFF, 0x89ABCDEF);
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PEKEYR_OFF, 0x02030405);

stlink_read_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PECR_OFF, &val);
if (val & STM32L1_FLASH_PELOCK_BIT) {
ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n");
return -1;
}
}

stlink_read_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PECR_OFF, &val);
if ((val & (STM32L1_FLASH_OPTLOCK_BIT))) {
WLOG("Unlocking options\n");
//Unlock the Option bytes area (reference page 76)
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_OPTKEYR_OFF, 0xFBEAD9C8);
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_OPTKEYR_OFF, 0x24252627);

stlink_read_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PECR_OFF, &val);
if (val & STM32L1_FLASH_OPTLOCK_BIT) {
ELOG("Options unlock failed! System reset required to be able to unlock it again!\n");
return -1;
}
}


/* Clear errors */
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_SR_OFF, 0x00003F00);

stlink_read_debug32(sl, addr, &val);
WLOG("Option bytes 0x%08x is 0x%08x\n",addr,val);

/* Write options bytes */
write_uint32((unsigned char*) &data, *(uint32_t*) (base));
if( data != val ) {
WLOG("Writing option bytes 0x%04x\n", data);
stlink_write_debug32(sl, addr, data);

stlink_read_debug32(sl, addr, &val);
WLOG("Option bytes is 0x%08x\n",val);
}


if(len==8) {
/* Clear errors */
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_SR_OFF, 0x00003F00);

stlink_read_debug32(sl, addr+4, &val);
WLOG("2nd option bytes 0x%08x is 0x%08x\n",addr,val);

/* Write options bytes */
write_uint32((unsigned char*) &data, *(uint32_t*) (base+4));
if( data != val ) {
WLOG("Writing 2nd option bytes 0x%04x\n", data);
stlink_write_debug32(sl, addr+4, data);

stlink_read_debug32(sl, addr+4, &val);
WLOG("2nd option bytes is 0x%08x\n",val);
}
}

/* Reload options */
stlink_read_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PECR_OFF, &val);
val |= (STM32L1_FLASH_OBL_LAUNCH_BIT);
stlink_write_debug32(sl, STM32L1_FLASH_REGS_ADDR + FLASH_PECR_OFF, val);

return 0;
}

/**
* Write option bytes
* @param sl
* @param addr of the memory mapped option bytes
* @param base option bytes to write
* @return 0 on success, -ve on failure.
*/
static int stlink_write_option_bytes_l496x(stlink_t *sl, uint8_t* base, uint32_t len) {

uint32_t val;

if(len != 4) {
ELOG("Wrong length for writting option bytes, must be 4 is %d\n", len);
return -1;
}

/* Unlock flash if necessary */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
if ((val & (1u << STM32L4_FLASH_CR_LOCK))) {

/* disable flash write protection. */
stlink_write_debug32(sl, STM32L4_FLASH_KEYR, 0x45670123);
stlink_write_debug32(sl, STM32L4_FLASH_KEYR, 0xCDEF89AB);

// check that the lock is no longer set.
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
if ((val & (1u << STM32L4_FLASH_CR_LOCK))) {
ELOG("Flash unlock failed! System reset required to be able to unlock it again!\n");
return -1;
}
}

/* Unlock option bytes if necessary (ref manuel page 61) */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
if ((val & (1 << STM32L4_FLASH_CR_OPTLOCK))) {

/* disable option byte write protection. */
stlink_write_debug32(sl, STM32L4_FLASH_OPTKEYR, 0x08192A3B);
stlink_write_debug32(sl, STM32L4_FLASH_OPTKEYR, 0x4C5D6E7F);

/* check that the lock is no longer set. */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
if ((val & (1 << STM32L4_FLASH_CR_OPTLOCK))) {
ELOG("Options bytes unlock failed! System reset required to be able to unlock it again!\n");
return -1;
}
}

/* Write options bytes */
uint32_t data;
write_uint32((unsigned char*) &data, *(uint32_t*) (base));
WLOG("Writing option bytes 0x%04x\n", data);
stlink_write_debug32(sl, STM32L4_FLASH_OPTR, data);

/* Set Options Start bit */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
val |= (1 << STM32L4_FLASH_CR_OPTSTRT);
stlink_write_debug32(sl, STM32L4_FLASH_CR, val);

/* Wait for 'busy' bit in FLASH_SR to clear. */
do {
stlink_read_debug32(sl, STM32L4_FLASH_SR, &val);
} while ((val & (1 << 16)) != 0);

/* apply options bytes immediate */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
val |= (1 << STM32L4_FLASH_CR_OBL_LAUNCH);
stlink_write_debug32(sl, STM32L4_FLASH_CR, val);

/* Re-lock option bytes */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
val |= (1u << STM32L4_FLASH_CR_OPTLOCK);
stlink_write_debug32(sl, STM32L4_FLASH_CR, val);
/* Re-lock flash. */
stlink_read_debug32(sl, STM32L4_FLASH_CR, &val);
val |= (1u << STM32L4_FLASH_CR_LOCK);
stlink_write_debug32(sl, STM32L4_FLASH_CR, val);

return 0;
}


/**
* Write option bytes
* @param sl
Expand Down Expand Up @@ -2840,15 +3018,24 @@ int stlink_write_option_bytes(stlink_t *sl, stm32_addr_t addr, uint8_t* base, ui
// Make sure we've loaded the context with the chip details
stlink_core_id(sl);

WLOG("Option bytes write chip_id 0x%08x addr 0x%08x\n",sl->chip_id,addr);

/* Check if chip is supported and for correct address */
if((sl->chip_id == STLINK_CHIPID_STM32_G0X1) && (addr == STM32_G0_OPTION_BYTES_BASE)) {
return stlink_write_option_bytes_g0x1(sl, base, len);
}
else if((sl->chip_id == STLINK_CHIPID_STM32_L0_CAT2) && (addr == STM32_L0_CAT2_OPTION_BYTES_BASE)) {
return stlink_write_option_bytes_l0_cat2(sl, base, len);
}
else if((sl->chip_id == STLINK_CHIPID_STM32_L496X) && (addr == STM32_L496X_OPTION_BYTES_BASE)) {
return stlink_write_option_bytes_l496x(sl, base, len);
}
else if( ( (sl->chip_id == STLINK_CHIPID_STM32_L152_RE) || (sl->chip_id == STLINK_CHIPID_STM32_L1_HIGH) )
&& ( (addr == STM32_L1_OPTION_BYTES_BASE) || (addr == STM32_L1_OPTION_BYTES_BASE+4) ) ) {
return stlink_write_option_bytes_l1(sl, base, addr, len);
}
else {
ELOG("Option bytes writing is currently only supported for the STM32F2, STM32G0 and STM32L0\n");
ELOG("Option bytes writing is currently only supported for the STM32F2, STM32G0, STM32L496x/L4A6x, STM32L1 and STM32L0\n");
return -1;
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ int main(int ac, char** av)
goto on_error;
}
}
else if (o.addr == STM32_G0_OPTION_BYTES_BASE || o.addr == STM32_L0_CAT2_OPTION_BYTES_BASE){
else if (o.addr == STM32_G0_OPTION_BYTES_BASE || o.addr == STM32_L0_CAT2_OPTION_BYTES_BASE || o.addr == STM32_L0_CAT2_OPTION_BYTES_BASE + 4){
err = stlink_fwrite_option_bytes(sl, o.filename, o.addr);
if (err == -1)
{
Expand Down