New patch attached.

Cheers,
Clément

On 09/19/2011 06:00 PM, Øyvind Harboe wrote:
Some more nit-picking:

1. Switch more LOG_INFO() to LOG_DEBUG(). LOG_INFO() is intended for
'users'

+       LOG_INFO("allocating working area for algo (%u bytes)", reg32);

2. Purge unused prototypes and reorder fn's to avoid unecessary ones:

+/* Private prototypes */
+static int stm32lx_unlock_pecr(struct flash_bank *bank);
+static int stm32lx_lock_pecr(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_unlock_option_byte(struct flash_bank *bank);
+static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
+static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);


3. Remove warning or change to LOG_DEBUG()

+       if (target_alloc_working_area(target, 
reg32,&stm32lx_info->write_algorithm)
+                       != ERROR_OK)
+       {
+               LOG_WARNING("no working area available, can't do block memory 
writes");
+               return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+       };


4. Add missing error propagation:

+       // Lock back program memory
+       stm32lx_lock_program_memory(bank);
+

5. Change to LOG_DEBUG() or remove.

+               retval = stm32lx_write_rdp(bank, 0xAA);
+               if (retval != ERROR_OK)
+               {
+                       LOG_ERROR("failed to write RDP byte");
+                       return retval;
+               }
+       }


6.






--
Clément Burin des Roziers
HiKoB
http://openlab.hikob.com

>From d66aa49ee0d5834f330fefbb3988923b0d365d84 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Cl=C3=A9ment=20Burin=20des=20Roziers?= <clement.burin-des-rozi...@hikob.com>
Date: Fri, 16 Sep 2011 15:55:54 +0200
Subject: [PATCH] STM32L: Added flash driver and target

Added the flash driver for the STM32L family, which highly differ from the STM32F family.
Added the TCL target file for JTAG access.
---
 contrib/loaders/flash/stm32lx.S |   63 +++
 src/flash/nor/Makefile.am       |    1 +
 src/flash/nor/drivers.c         |    2 +
 src/flash/nor/stm32lx.c         |  982 +++++++++++++++++++++++++++++++++++++++
 tcl/target/stm32l.cfg           |   81 ++++
 5 files changed, 1129 insertions(+), 0 deletions(-)
 create mode 100644 contrib/loaders/flash/stm32lx.S
 create mode 100644 src/flash/nor/stm32lx.c
 create mode 100644 tcl/target/stm32l.cfg

diff --git a/contrib/loaders/flash/stm32lx.S b/contrib/loaders/flash/stm32lx.S
new file mode 100644
index 0000000..6e8ccb0
--- /dev/null
+++ b/contrib/loaders/flash/stm32lx.S
@@ -0,0 +1,63 @@
+/***************************************************************************
+ *   Copyright (C) 2010 by Spencer Oliver                                  *
+ *   s...@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   Copyright (C) 2011 Øyvind Harboe                                      *
+ *   oyvind.har...@zylin.com                                               *
+ *                                                                         *
+ *   Copyright (C) 2011 Clement Burin des Roziers                          *
+ *   clement.burin-des-rozi...@hikob.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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+
+
+// Build : arm-eabi-gcc -c stm32lx.S
+	.text
+	.syntax unified
+	.cpu cortex-m3
+	.thumb
+	.thumb_func
+	.global write
+
+/*
+	r0 - destination address
+	r1 - source address
+	r2 - count
+*/
+
+	// Set 0 to r3
+	movs	r3, #0
+	// Go to compare
+	b.n test_done
+
+write_word:
+	// Load one word from address in r0, increment by 4
+	ldr.w	ip, [r1], #4
+	// Store the word to address in r1, increment by 4
+	str.w	ip, [r0], #4
+	// Increment r3
+	adds	r3, #1
+
+test_done:
+	// Compare r3 and r2
+	cmp 	r3, r2
+	// Loop if not zero
+	bcc.n	write_word
+
+	// Set breakpoint to exit
+	bkpt	#0x00
+
diff --git a/src/flash/nor/Makefile.am b/src/flash/nor/Makefile.am
index a966826..d5832ca 100644
--- a/src/flash/nor/Makefile.am
+++ b/src/flash/nor/Makefile.am
@@ -26,6 +26,7 @@ NOR_DRIVERS = \
 	stellaris.c \
 	stm32f1x.c \
 	stm32f2x.c \
+	stm32lx.c \
 	str7x.c \
 	str9x.c \
 	str9xpec.c \
diff --git a/src/flash/nor/drivers.c b/src/flash/nor/drivers.c
index a437d84..6b0cc36 100644
--- a/src/flash/nor/drivers.c
+++ b/src/flash/nor/drivers.c
@@ -34,6 +34,7 @@ extern struct flash_driver stellaris_flash;
 extern struct flash_driver str9xpec_flash;
 extern struct flash_driver stm32f1x_flash;
 extern struct flash_driver stm32f2x_flash;
+extern struct flash_driver stm32lx_flash;
 extern struct flash_driver tms470_flash;
 extern struct flash_driver ecosflash_flash;
 extern struct flash_driver ocl_flash;
@@ -65,6 +66,7 @@ static struct flash_driver *flash_drivers[] = {
 	&str9xpec_flash,
 	&stm32f1x_flash,
 	&stm32f2x_flash,
+	&stm32lx_flash,
 	&tms470_flash,
 	&ecosflash_flash,
 	&ocl_flash,
diff --git a/src/flash/nor/stm32lx.c b/src/flash/nor/stm32lx.c
new file mode 100644
index 0000000..9dc7b83
--- /dev/null
+++ b/src/flash/nor/stm32lx.c
@@ -0,0 +1,982 @@
+/***************************************************************************
+ *   Copyright (C) 2005 by Dominic Rath                                    *
+ *   dominic.r...@gmx.de                                                   *
+ *                                                                         *
+ *   Copyright (C) 2008 by Spencer Oliver                                  *
+ *   s...@spen-soft.co.uk                                                  *
+ *                                                                         *
+ *   Copyright (C) 2011 by Clement Burin des Roziers                       *
+ *   clement.burin-des-rozi...@hikob.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, write to the                         *
+ *   Free Software Foundation, Inc.,                                       *
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
+ ***************************************************************************/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "imp.h"
+#include <helper/binarybuffer.h>
+#include <target/algorithm.h>
+#include <target/armv7m.h>
+
+/* stm32lx flash register locations */
+
+#define FLASH_BASE		0x40023C00
+#define FLASH_ACR		0x40023C00
+#define FLASH_PECR		0x40023C04
+#define FLASH_PDKEYR	0x40023C08
+#define FLASH_PEKEYR	0x40023C0C
+#define FLASH_PRGKEYR	0x40023C10
+#define FLASH_OPTKEYR	0x40023C14
+#define FLASH_SR		0x40023C18
+#define FLASH_OBR		0x40023C1C
+#define FLASH_WRPR		0x40023C20
+
+/* FLASH_ACR bites */
+#define FLASH_ACR__LATENCY		(1<<0)
+#define FLASH_ACR__PRFTEN		(1<<1)
+#define FLASH_ACR__ACC64		(1<<2)
+#define FLASH_ACR__SLEEP_PD		(1<<3)
+#define FLASH_ACR__RUN_PD		(1<<4)
+
+/* FLASH_PECR bits */
+#define FLASH_PECR__PELOCK		(1<<0)
+#define FLASH_PECR__PRGLOCK		(1<<1)
+#define FLASH_PECR__OPTLOCK		(1<<2)
+#define FLASH_PECR__PROG		(1<<3)
+#define FLASH_PECR__DATA		(1<<4)
+#define FLASH_PECR__FTDW		(1<<8)
+#define FLASH_PECR__ERASE		(1<<9)
+#define FLASH_PECR__FPRG		(1<<10)
+#define FLASH_PECR__EOPIE		(1<<16)
+#define FLASH_PECR__ERRIE		(1<<17)
+#define FLASH_PECR__OBL_LAUNCH	(1<<18)
+
+/* FLASH_SR bits */
+#define FLASH_SR__BSY		(1<<0)
+#define FLASH_SR__EOP		(1<<1)
+#define FLASH_SR__ENDHV		(1<<2)
+#define FLASH_SR__READY		(1<<3)
+#define FLASH_SR__WRPERR	(1<<8)
+#define FLASH_SR__PGAERR	(1<<9)
+#define FLASH_SR__SIZERR	(1<<10)
+#define FLASH_SR__OPTVERR	(1<<11)
+
+/* Unlock keys */
+#define PEKEY1			0x89ABCDEF
+#define PEKEY2			0x02030405
+#define PRGKEY1			0x8C9DAEBF
+#define PRGKEY2			0x13141516
+#define OPTKEY1			0xFBEAD9C8
+#define OPTKEY2			0x24252627
+
+/* other registers */
+#define DBGMCU_IDCODE	0xE0042000
+#define F_SIZE			0x1FF8004C
+
+/* Constants */
+#define FLASH_PAGE_SIZE 256
+#define FLASH_SECTOR_SIZE 4096
+#define FLASH_PAGES_PER_SECTOR 16
+#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_lock_program_memory(struct flash_bank *bank);
+static int stm32lx_erase_sector(struct flash_bank *bank, int sector);
+static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank);
+
+struct stm32lx_flash_bank
+{
+	struct working_area *write_algorithm;
+	int probed;
+};
+
+/* flash bank stm32lx <base> <size> 0 0 <target#>
+ */
+FLASH_BANK_COMMAND_HANDLER(stm32lx_flash_bank_command)
+{
+	struct stm32lx_flash_bank *stm32lx_info;
+	if (CMD_ARGC < 6)
+	{
+		LOG_ERROR("incomplete flash_bank stm32lx configuration");
+		return ERROR_FLASH_BANK_INVALID;
+	}
+
+	// Create the bank structure
+	stm32lx_info = malloc(sizeof(struct stm32lx_flash_bank));
+
+	// Check allocation
+	if (stm32lx_info == NULL)
+	{
+		LOG_ERROR("failed to allocate bank structure");
+		return ERROR_FAIL;
+	}
+
+	bank->driver_priv = stm32lx_info;
+
+	stm32lx_info->write_algorithm = NULL;
+	stm32lx_info->probed = 0;
+
+	return ERROR_OK;
+}
+
+static int stm32lx_protect_check(struct flash_bank *bank)
+{
+	int retval;
+	struct target *target = bank->target;
+
+	uint32_t wrpr;
+
+	if (target->state != TARGET_HALTED)
+	{
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	/*
+	 * Read the WRPR word, and check each bit (corresponding to each
+	 * flash sector
+	 */
+	retval = target_read_u32(target, FLASH_WRPR, &wrpr);
+	if (retval != ERROR_OK)
+		return retval;
+
+	for (int i = 0; i < 32; i++)
+	{
+		if (wrpr & (1 << i))
+		{
+			bank->sectors[i].is_protected = 1;
+		}
+		else
+		{
+			bank->sectors[i].is_protected = 0;
+		}
+	}
+	return ERROR_OK;
+}
+
+static int stm32lx_erase(struct flash_bank *bank, int first, int last)
+{
+	int retval;
+
+	/*
+	 * It could be possible to do a mass erase if all sectors must be
+	 * erased, but it is not implemented yet.
+	 */
+
+	if (bank->target->state != TARGET_HALTED)
+	{
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	/*
+	 * Loop over the selected sectors and erase them
+	 */
+	for (int i = first; i <= last; i++)
+	{
+		retval = stm32lx_erase_sector(bank, i);
+		if (retval != ERROR_OK)
+			return retval;
+		bank->sectors[i].is_erased = 1;
+	}
+	return ERROR_OK;
+}
+
+static int stm32lx_protect(struct flash_bank *bank, int set, int first,
+		int last)
+{
+	LOG_WARNING("protection of the STM32L flash is not implemented");
+	return ERROR_OK;
+}
+
+static int stm32lx_write_half_pages(struct flash_bank *bank, uint8_t *buffer,
+		uint32_t offset, uint32_t count)
+{
+	struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+	struct target *target = bank->target;
+	uint32_t buffer_size = 4096 * 4;
+	struct working_area *source;
+	uint32_t address = bank->base + offset;
+
+	struct reg_param reg_params[5];
+	struct armv7m_algorithm armv7m_info;
+
+	int retval = ERROR_OK;
+	uint32_t reg32;
+
+	/* see contib/loaders/flash/stm32lx.s for src */
+
+	static const uint16_t stm32lx_flash_write_code_16[] =
+	{
+	//	00000000 <write_word-0x4>:
+			0x2300, // 0:	2300	  	movs	r3, #0
+			0xe004, // 2:	e004	  	b.n	e <test_done>
+
+			//	00000004 <write_word>:
+			0xf851, 0xcb04, // 4:	f851 cb04 	ldr.w	ip, [r1], #4
+			0xf840, 0xcb04, // 8:	f840 cb04 	str.w	ip, [r0], #4
+			0x3301, // c:	3301	  	adds	r3, #1
+
+			//	0000000e <test_done>:
+			0x4293, // e:	4293	  	cmp	r3, r2
+			0xd3f8, // 10:	d3f8	  	bcc.n	4 <write_word>
+			0xbe00, // 12:	be00	  	bkpt	0x0000
+
+			};
+
+	// Flip endian
+	uint8_t stm32lx_flash_write_code[sizeof(stm32lx_flash_write_code_16)];
+	for (unsigned int i = 0; i < sizeof(stm32lx_flash_write_code_16) / 2; i++)
+	{
+		stm32lx_flash_write_code[i * 2 + 0] = stm32lx_flash_write_code_16[i]
+				& 0xff;
+		stm32lx_flash_write_code[i * 2 + 1] = (stm32lx_flash_write_code_16[i]
+				>> 8) & 0xff;
+	}
+	// Check there are an even number of half pages (128bytes)
+	if (count % 128)
+	{
+		LOG_ERROR("there should be an even number "
+				"of half pages = 128 bytes (count = %i bytes)", count);
+		return ERROR_FAIL;
+	}
+
+	// Allocate working area
+	reg32 = sizeof(stm32lx_flash_write_code);
+	// Add bytes to make 4byte aligned
+	reg32 += (4 - (reg32 % 4)) % 4;
+	LOG_DEBUG("allocating working area for algo (%u bytes)", reg32);
+	retval = target_alloc_working_area(target, reg32,
+			&stm32lx_info->write_algorithm);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// Write the flashing code
+	LOG_DEBUG("writing flash code (%lu bytes)", sizeof(stm32lx_flash_write_code));
+	retval = target_write_buffer(target,
+			stm32lx_info->write_algorithm->address,
+			sizeof(stm32lx_flash_write_code),
+			(uint8_t*) stm32lx_flash_write_code);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// Allocate half pages memory
+	while (target_alloc_working_area_try(target, buffer_size, &source)
+			!= ERROR_OK)
+	{
+		if (buffer_size > 1024)
+		{
+			buffer_size -= 1024;
+		}
+		else
+		{
+			buffer_size /= 2;
+		}
+
+		if (buffer_size <= 256)
+		{
+			/* if we already allocated the writing code, but failed to get a
+			 * buffer, free the algorithm */
+			if (stm32lx_info->write_algorithm)
+				target_free_working_area(target, stm32lx_info->write_algorithm);
+
+			LOG_WARNING("no large enough working area available, can't do block memory writes");
+			return ERROR_TARGET_RESOURCE_NOT_AVAILABLE;
+		}
+	};
+	LOG_DEBUG("allocated working area for data (%i bytes)", buffer_size);
+
+	// Prepare the code execution
+	armv7m_info.common_magic = ARMV7M_COMMON_MAGIC;
+	armv7m_info.core_mode = ARMV7M_MODE_ANY;
+
+	// Prepare the register interaction
+	init_reg_param(&reg_params[0], "r0", 32, PARAM_OUT);
+	init_reg_param(&reg_params[1], "r1", 32, PARAM_OUT);
+	init_reg_param(&reg_params[2], "r2", 32, PARAM_OUT);
+	init_reg_param(&reg_params[3], "r3", 32, PARAM_IN_OUT);
+	init_reg_param(&reg_params[4], "r4", 32, PARAM_OUT);
+
+	// Flash the half page:
+	// 1: unlock program memory
+	retval = stm32lx_unlock_program_memory(bank);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// 2: Set the FPRG bit in PECR
+	target_read_u32(target, FLASH_PECR, &reg32);
+	reg32 |= FLASH_PECR__FPRG;
+	retval = target_write_u32(target, FLASH_PECR, reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// 3: Set the PROG bit in PECR
+	target_read_u32(target, FLASH_PECR, &reg32);
+	reg32 |= FLASH_PECR__PROG;
+	retval = target_write_u32(target, FLASH_PECR, reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// Loop while there are bytes to write
+	while (count > 0)
+	{
+		uint32_t this_count;
+		this_count = (count > buffer_size) ? buffer_size : count;
+
+		// Write the next half pages
+		retval = target_write_buffer(target, source->address, this_count,
+				buffer);
+		if (retval != ERROR_OK)
+			break;
+
+		// 4: Store useful information in the registers
+		// the destination address of the copy (R0)
+		buf_set_u32(reg_params[0].value, 0, 32, address);
+		// The source address of the copy (R1)
+		buf_set_u32(reg_params[1].value, 0, 32, source->address);
+		// The length of the copy (R2)
+		buf_set_u32(reg_params[2].value, 0, 32, this_count / 4);
+
+		// 5: Execute the bunch of code
+		retval = target_run_algorithm(target, 0, NULL, sizeof(reg_params)
+				/ sizeof(*reg_params), reg_params,
+				stm32lx_info->write_algorithm->address, 0, 20000, &armv7m_info);
+		if (retval != ERROR_OK)
+			break;
+
+		// 6: Wait while busy
+		stm32lx_wait_until_bsy_clear(bank);
+
+		// Update variables
+		buffer += this_count;
+		address += this_count;
+		count -= this_count;
+	}
+
+	// Lock back program memory
+	retval = stm32lx_lock_program_memory(bank);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// Free the working areas
+	target_free_working_area(target, source);
+	target_free_working_area(target, stm32lx_info->write_algorithm);
+
+	// Destroy the registers
+	destroy_reg_param(&reg_params[0]);
+	destroy_reg_param(&reg_params[1]);
+	destroy_reg_param(&reg_params[2]);
+	destroy_reg_param(&reg_params[3]);
+
+	return retval;
+}
+static int stm32lx_write(struct flash_bank *bank, uint8_t *buffer,
+		uint32_t offset, uint32_t count)
+{
+	struct target *target = bank->target;
+
+	// Number of half pages
+	uint32_t halfpages_number;
+	// Number of full words (32bit)
+	uint32_t words_remaining;
+	// Number of orphan bytes
+	uint32_t bytes_remaining;
+	uint32_t address = bank->base + offset;
+	uint32_t bytes_written = 0;
+	int retval;
+
+	if (bank->target->state != TARGET_HALTED)
+	{
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	// check byte alignement
+	if (offset & 0x1)
+	{
+		LOG_ERROR("offset 0x%.8x breaks required 2-byte alignment", offset);
+		return ERROR_FLASH_DST_BREAKS_ALIGNMENT;
+	}
+
+	// Check if there are some entire half pages
+	if (((offset % 128) == 0) && (count >= 128))
+	{
+		halfpages_number = count / 128;
+		words_remaining = (count - 128 * halfpages_number) / 4;
+		bytes_remaining = (count & 0x3);
+	}
+	else
+	{
+		halfpages_number = 0;
+		words_remaining = (count / 4);
+		bytes_remaining = (count & 0x3);
+	}
+
+	// Write the half pages if any
+	if (halfpages_number)
+	{
+		retval = stm32lx_write_half_pages(bank, buffer, offset, 128
+				* halfpages_number);
+		if (retval != ERROR_OK)
+		{
+			return ERROR_FAIL;
+		}
+	}
+
+	// Update counts
+	bytes_written = 128 * halfpages_number;
+
+	retval = stm32lx_unlock_program_memory(bank);
+	if (retval != ERROR_OK)
+		return retval;
+
+	while (words_remaining > 0)
+	{
+		uint32_t value;
+		uint8_t* p = buffer + bytes_written;
+
+		// Prepare the word, Little endian conversion
+		value = p[0] + (p[1] << 8) + (p[2] << 16) + (p[3] << 24);
+
+		// Write data
+		retval = target_write_u32(target, address, value);
+		if (retval != ERROR_OK)
+			return retval;
+
+		bytes_written += 4;
+		words_remaining--;
+		address += 4;
+
+		// Wait for busy clear
+		retval = stm32lx_wait_until_bsy_clear(bank);
+		return retval;
+	}
+
+	// If there are single bytes (too few to form words), compute a word, with other bits cleared
+	if (bytes_remaining)
+	{
+		uint32_t value = 0;
+		for (int i = 0; i < 4; i++)
+		{
+			if (bytes_remaining)
+			{
+				value += (buffer[i] << (8 * i));
+				bytes_remaining--;
+			}
+		}
+
+		// Write data
+		retval = target_write_u32(target, address, value);
+		if (retval != ERROR_OK)
+			return retval;
+
+		// Wait for busy clear
+		retval = stm32lx_wait_until_bsy_clear(bank);
+		if (retval != ERROR_OK)
+			return retval;
+	}
+
+	// Lock back the program memory
+	retval = stm32lx_lock_program_memory(bank);
+	if (retval != ERROR_OK)
+		return retval;
+
+	return ERROR_OK;
+}
+
+static int stm32lx_probe(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+	int i;
+	uint16_t flash_size;
+	uint32_t device_id;
+	uint32_t reg32;
+
+	// Set probe flag to 0
+	stm32lx_info->probed = 0;
+
+	/* read stm32 device id register */
+	int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+	if (retval != ERROR_OK)
+		return retval;
+
+	LOG_DEBUG("device id = 0x%08x", device_id);
+
+	if ((device_id & 0x7ff) != 0x416)
+	{
+		LOG_WARNING("Cannot identify target as a STM32L family.");
+		return ERROR_FAIL;
+	}
+
+	// Read the RDP byte
+	uint8_t rdp;
+
+	// Read the RDPRT field in FLASH_OBR
+	retval = target_read_u32(target, FLASH_OBR, &reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	// Bit mask the RDP part of the register
+	rdp = reg32 & 0xFF;
+
+	// If RDP is not 0xAA (level 0) we need to go to level 0
+	if (rdp != 0xAA)
+	{
+
+		/*
+		 * Unlocking the option byte is done by unlocking the PECR, then
+		 * by writing the 2 option byte keys to OPTKEYR
+		 */
+
+		/* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
+		retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
+		if (retval != ERROR_OK)
+			return retval;
+
+		retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
+		if (retval != ERROR_OK)
+			return retval;
+
+		/* Make sure it worked */
+		retval = target_read_u32(target, FLASH_PECR, &reg32);
+		if (retval != ERROR_OK)
+			return retval;
+
+		if (reg32 & FLASH_PECR__PELOCK)
+			return ERROR_FLASH_OPERATION_FAILED;
+
+		retval = target_write_u32(target, FLASH_OPTKEYR, OPTKEY1);
+		if (retval != ERROR_OK)
+			return retval;
+		retval = target_write_u32(target, FLASH_OPTKEYR, OPTKEY2);
+		if (retval != ERROR_OK)
+			return retval;
+
+		retval = target_read_u32(target, FLASH_PECR, &reg32);
+		if (retval != ERROR_OK)
+			return retval;
+
+		if (reg32 & FLASH_PECR__OPTLOCK)
+		{
+			LOG_ERROR("OPTLOCK is not cleared");
+			return ERROR_FLASH_OPERATION_FAILED;
+		}
+
+		// Then, write RDP to 0x00 to set level 1
+		reg32 = ((~0xAA) << 16) | (0xAA);
+		retval = target_write_u32(target, OB_RDP, reg32);
+		if (retval != ERROR_OK)
+			return retval;
+
+		// Set Automatic update of the option byte, by setting OBL_LAUNCH in FLASH_PECR
+		reg32 = FLASH_PECR__OBL_LAUNCH;
+		retval = target_write_u32(target, FLASH_PECR, reg32);
+		if (retval != ERROR_OK)
+			return retval;
+	}
+
+	/* get flash size from target. */
+	retval = target_read_u16(target, F_SIZE, &flash_size);
+	if (retval != ERROR_OK)
+		return retval;
+
+	/* check for valid flash size */
+	if (flash_size == 0xffff)
+	{
+		/* number of sectors incorrect on revA */
+		LOG_ERROR("STM32 flash size failed, probe inaccurate");
+		return ERROR_FAIL;
+	}
+
+	/* STM32L - we have 32 sectors, 16 pages per sector -> 512 pages
+	 * 16 pages for a protection area */
+
+	/* calculate numbers of sectors (4kB per sector) */
+	int num_sectors = (flash_size * 1024) / FLASH_SECTOR_SIZE;
+	LOG_INFO("flash size = %dkbytes", flash_size);
+
+	if (bank->sectors)
+	{
+		free(bank->sectors);
+		bank->sectors = NULL;
+	}
+
+	bank->base = FLASH_BANK0_ADDRESS;
+	bank->size = flash_size * 1024;
+	bank->num_sectors = num_sectors;
+	bank->sectors = malloc(sizeof(struct flash_sector) * num_sectors);
+
+	// Check allocation
+	if (bank->sectors == NULL)
+	{
+		LOG_ERROR("failed to allocate bank sectors");
+		return ERROR_FAIL;
+	}
+
+	for (i = 0; i < num_sectors; i++)
+	{
+		bank->sectors[i].offset = i * FLASH_SECTOR_SIZE;
+		bank->sectors[i].size = FLASH_SECTOR_SIZE;
+		bank->sectors[i].is_erased = -1;
+		bank->sectors[i].is_protected = 1;
+	}
+
+	// Probe is done, remember
+	stm32lx_info->probed = 1;
+
+	return ERROR_OK;
+}
+
+static int stm32lx_auto_probe(struct flash_bank *bank)
+{
+	struct stm32lx_flash_bank *stm32lx_info = bank->driver_priv;
+
+	// Check if already probe
+	if (stm32lx_info->probed)
+	{
+		return ERROR_OK;
+	}
+
+	// If not, probe !
+	return stm32lx_probe(bank);
+}
+
+static int stm32lx_erase_check(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	const int buffer_size = 4096;
+	int i;
+	uint32_t nBytes;
+	int retval = ERROR_OK;
+
+	if (bank->target->state != TARGET_HALTED)
+	{
+		LOG_ERROR("Target not halted");
+		return ERROR_TARGET_NOT_HALTED;
+	}
+
+	// allocate a buffer to read memory
+	uint8_t *buffer = malloc(buffer_size);
+
+	// Check allocation
+	if (buffer == NULL)
+	{
+		LOG_ERROR("failed to allocate read buffer");
+		return ERROR_FAIL;
+	}
+
+	// Loop over the sectors
+	for (i = 0; i < bank->num_sectors; i++)
+	{
+		uint32_t j;
+		// Flag it erased
+		bank->sectors[i].is_erased = 1;
+
+		// Loop chunk by chunk over the sector
+		for (j = 0; j < bank->sectors[i].size; j += buffer_size)
+		{
+			uint32_t chunk;
+			chunk = buffer_size;
+			if (chunk > (j - bank->sectors[i].size))
+			{
+				chunk = (j - bank->sectors[i].size);
+			}
+
+			retval = target_read_memory(target, bank->base
+					+ bank->sectors[i].offset + j, 4, chunk / 4, buffer);
+			if (retval != ERROR_OK)
+				break;
+
+			for (nBytes = 0; nBytes < chunk; nBytes++)
+			{
+				if (buffer[nBytes] != 0x00)
+				{
+					bank->sectors[i].is_erased = 0;
+					break;
+				}
+			}
+		}
+		if (retval != ERROR_OK)
+		{
+			break;
+		}
+	}
+	// finally free the buffer
+	free(buffer);
+
+	return retval;
+}
+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 target *target = bank->target;
+	uint32_t device_id;
+	int printed;
+
+	/* read stm32 device id register */
+	int retval = target_read_u32(target, DBGMCU_IDCODE, &device_id);
+	if (retval != ERROR_OK)
+		return retval;
+
+	if ((device_id & 0x7ff) == 0x416)
+	{
+		printed = snprintf(buf, buf_size, "stm32lx - Rev: ");
+		buf += printed;
+		buf_size -= printed;
+
+		switch (device_id >> 16)
+		{
+			case 0x1000:
+				snprintf(buf, buf_size, "A");
+				break;
+
+			case 0x1008:
+				snprintf(buf, buf_size, "Y");
+				break;
+			default:
+				snprintf(buf, buf_size, "unknown (rev_id=0x%.4x)", (device_id
+						>> 16));
+				break;
+		}
+	}
+	else
+	{
+		snprintf(buf, buf_size, "Cannot identify target as a stm32lx");
+		return ERROR_FAIL;
+	}
+
+	return ERROR_OK;
+}
+
+static const struct command_registration stm32lx_exec_command_handlers[] =
+{
+	COMMAND_REGISTRATION_DONE
+};
+
+static const struct command_registration stm32lx_command_handlers[] =
+{
+	{
+		.name = "stm32lx",
+		.mode = COMMAND_ANY,
+		.help = "stm32lx flash command group",
+		.chain = stm32lx_exec_command_handlers,
+	},
+	COMMAND_REGISTRATION_DONE
+};
+
+struct flash_driver stm32lx_flash =
+{
+		.name = "stm32lx",
+		.commands = stm32lx_command_handlers,
+		.flash_bank_command = stm32lx_flash_bank_command,
+		.erase = stm32lx_erase,
+		.protect = stm32lx_protect,
+		.write = stm32lx_write,
+		.read = default_flash_read,
+		.probe = stm32lx_probe,
+		.auto_probe = stm32lx_auto_probe,
+		.erase_check = stm32lx_erase_check,
+		.protect_check = stm32lx_protect_check,
+		.info = stm32lx_get_info,
+};
+
+// Static methods implementation
+
+static int stm32lx_unlock_program_memory(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	int retval;
+	uint32_t reg32;
+
+	/*
+	 * Unlocking the program memory is done by unlocking the PECR,
+	 * then by writing the 2 PRGKEY to the PRGKEYR register
+	 */
+
+	/* To unlock the PECR write the 2 PEKEY to the PEKEYR register */
+	retval = target_write_u32(target, FLASH_PEKEYR, PEKEY1);
+	if (retval != ERROR_OK)
+		return retval;
+
+	retval = target_write_u32(target, FLASH_PEKEYR, PEKEY2);
+	if (retval != ERROR_OK)
+		return retval;
+
+	/* Make sure it worked */
+	retval = target_read_u32(target, FLASH_PECR, &reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	if (reg32 & FLASH_PECR__PELOCK)
+	{
+		LOG_ERROR("PELOCK is not cleared :(");
+		return ERROR_FLASH_OPERATION_FAILED;
+	}
+
+	retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY1);
+	if (retval != ERROR_OK)
+		return retval;
+	retval = target_write_u32(target, FLASH_PRGKEYR, PRGKEY2);
+	if (retval != ERROR_OK)
+		return retval;
+
+	/* Make sure it worked */
+	retval = target_read_u32(target, FLASH_PECR, &reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	if (reg32 & FLASH_PECR__PRGLOCK)
+	{
+		LOG_ERROR("PRGLOCK is not cleared :(");
+		return ERROR_FLASH_OPERATION_FAILED;
+	}
+	return ERROR_OK;
+}
+static int stm32lx_lock_program_memory(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	int retval;
+	uint32_t reg32;
+
+	/* To lock the program memory, simply set the lock bit and lock PECR */
+
+	retval = target_read_u32(target, FLASH_PECR, &reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	reg32 |= FLASH_PECR__PRGLOCK;
+	retval = target_write_u32(target, FLASH_PECR, reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	retval = target_read_u32(target, FLASH_PECR, &reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	reg32 |= FLASH_PECR__PELOCK;
+	retval = target_write_u32(target, FLASH_PECR, reg32);
+	if (retval != ERROR_OK)
+		return retval;
+
+	return ERROR_OK;
+}
+
+static int stm32lx_erase_sector(struct flash_bank *bank, int sector)
+{
+	struct target *target = bank->target;
+	int retval;
+	uint32_t reg32;
+
+	/*
+	 * To erase a sector (i.e. FLASH_PAGES_PER_SECTOR pages),
+	 * first unlock the memory, loop over the pages of this sector
+	 * and write 0x0 to its first word.
+	 */
+
+	retval = stm32lx_unlock_program_memory(bank);
+	if (retval != ERROR_OK)
+		return retval;
+
+	for (int page = 0; page < FLASH_PAGES_PER_SECTOR; page++)
+	{
+		reg32 = FLASH_PECR__PROG | FLASH_PECR__ERASE;
+		retval = target_write_u32(target, FLASH_PECR, reg32);
+		if (retval != ERROR_OK)
+			return retval;
+
+		retval = stm32lx_wait_until_bsy_clear(bank);
+		if (retval != ERROR_OK)
+			return retval;
+
+		uint32_t addr = bank->base + bank->sectors[sector].offset + (page
+				* FLASH_PAGE_SIZE);
+		retval = target_write_u32(target, addr, 0x0);
+		if (retval != ERROR_OK)
+			return retval;
+
+		retval = stm32lx_wait_until_bsy_clear(bank);
+		if (retval != ERROR_OK)
+			return retval;
+	}
+
+	retval = stm32lx_lock_program_memory(bank);
+	if (retval != ERROR_OK)
+		return retval;
+
+	return ERROR_OK;
+}
+
+static int stm32lx_wait_until_bsy_clear(struct flash_bank *bank)
+{
+	struct target *target = bank->target;
+	uint32_t status;
+	int retval = ERROR_OK;
+	int timeout = 100;
+
+	/* wait for busy to clear */
+	for (;;)
+	{
+		retval = target_read_u32(target, FLASH_SR, &status);
+		if (retval != ERROR_OK)
+			return retval;
+
+		if ((status & FLASH_SR__BSY) == 0)
+		{
+			break;
+		}
+		if (timeout-- <= 0)
+		{
+			LOG_ERROR("timed out waiting for flash");
+			return ERROR_FAIL;
+		}
+		alive_sleep(1);
+	}
+
+	if (status & FLASH_SR__WRPERR)
+	{
+		LOG_ERROR("access denied / write protected");
+		retval = ERROR_FAIL;
+	}
+
+	if (status & FLASH_SR__PGAERR)
+	{
+		LOG_ERROR("invalid program address");
+		retval = ERROR_FAIL;
+	}
+
+	return retval;
+}
diff --git a/tcl/target/stm32l.cfg b/tcl/target/stm32l.cfg
new file mode 100644
index 0000000..5c3d368
--- /dev/null
+++ b/tcl/target/stm32l.cfg
@@ -0,0 +1,81 @@
+# script for stm32l
+
+if { [info exists CHIPNAME] } {
+   set  _CHIPNAME $CHIPNAME
+} else {
+   set  _CHIPNAME stm32l
+}
+
+if { [info exists ENDIAN] } {
+   set  _ENDIAN $ENDIAN
+} else {
+   set  _ENDIAN little
+}
+
+# Work-area is a space in RAM used for flash programming
+# By default use 14kB
+if { [info exists WORKAREASIZE] } {
+   set  _WORKAREASIZE $WORKAREASIZE
+} else {
+   set  _WORKAREASIZE 0x3800
+}
+
+# JTAG speed should be <= F_CPU/6.
+# F_CPU after reset is 2MHz, so use F_JTAG max = 333kHz
+adapter_khz 100
+
+adapter_nsrst_delay 100
+jtag_ntrst_delay 100
+
+#jtag scan chain
+if { [info exists CPUTAPID ] } {
+   set _CPUTAPID $CPUTAPID
+} else {
+  # See STM Document RM0038
+  # Section 24.6.3
+   set _CPUTAPID 0x4ba00477
+}
+jtag newtap $_CHIPNAME cpu -irlen 4 -ircapture 0x1 -irmask 0xf -expected-id $_CPUTAPID
+
+if { [info exists BSTAPID ] } {
+   # FIXME this never gets used to override defaults...
+   set _BSTAPID $BSTAPID
+} else {
+  # See STM Document RM0038
+  # Section 24.6.2
+  set _BSTAPID 0x06416041
+}
+jtag newtap $_CHIPNAME bs -irlen 5 -expected-id $_BSTAPID
+
+set _TARGETNAME $_CHIPNAME.cpu
+target create $_TARGETNAME cortex_m3 -endian $_ENDIAN -chain-position $_TARGETNAME
+
+$_TARGETNAME configure -work-area-phys 0x20000000 -work-area-size $_WORKAREASIZE -work-area-backup 0
+
+
+# flash size will be probed
+set _FLASHNAME $_CHIPNAME.flash
+flash bank $_FLASHNAME stm32lx 0x08000000 0 0 0 $_TARGETNAME
+
+# if srst is not fitted use SYSRESETREQ to
+# perform a soft reset
+cortex_m3 reset_config sysresetreq
+
+proc stm32l_enable_HSI {} {
+	# Enable HSI as clock source
+	echo "STM32L: Enabling HSI"
+	
+	# Set HSION in RCC_CR
+	mww 0x40023800 0x00000101
+	
+	# Set HSI as SYSCLK
+	mww 0x40023808 0x00000001
+	
+	# Increase JTAG speed
+	adapter_khz 2000
+}
+
+$_TARGETNAME configure -event reset-init {
+	stm32l_enable_HSI
+}
+
-- 
1.7.4.1

_______________________________________________
Openocd-development mailing list
Openocd-development@lists.berlios.de
https://lists.berlios.de/mailman/listinfo/openocd-development

Reply via email to