2008-02-25 02:01:21 -06:00
/***************************************************************************
* Copyright ( C ) 2005 by Dominic Rath *
* Dominic . Rath @ gmx . de *
* *
2009-08-13 08:54:53 -05:00
* LPC1700 support Copyright ( C ) 2009 by Audrius Urmanavicius *
* didele . deze @ gmail . com *
* *
2008-02-25 02:01:21 -06:00
* 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 . *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
# ifdef HAVE_CONFIG_H
# include "config.h"
# endif
2009-12-04 16:06:20 -06:00
# include "imp.h"
2008-02-25 02:01:21 -06:00
# include "lpc2000.h"
2009-12-03 06:14:25 -06:00
# include <helper/binarybuffer.h>
2009-12-03 06:14:35 -06:00
# include <target/algorithm.h>
2009-12-07 16:54:12 -06:00
# include <target/arm_opcodes.h>
2009-12-04 16:06:20 -06:00
# include <target/armv7m.h>
2008-02-25 02:01:21 -06:00
2010-03-02 17:00:14 -06:00
/**
* @ file
* flash programming support for NXP LPC17xx and LPC2xxx devices .
*
* @ todo Provide a way to update CCLK after declaring the flash bank .
* The value which is correct after chip reset will rarely still work
* right after the clocks switch to use the PLL ( e . g . 4 MHz - - > 100 MHz ) .
*/
/*
2008-02-25 02:01:21 -06:00
* currently supported devices :
* variant 1 ( lpc2000_v1 ) :
2009-08-13 08:54:53 -05:00
* - 2104 | 5 | 6
2009-06-23 17:45:15 -05:00
* - 2114 | 9
* - 2124 | 9
2008-02-25 02:01:21 -06:00
* - 2194
2009-06-23 17:45:15 -05:00
* - 2212 | 4
* - 2292 | 4
2008-02-25 02:01:21 -06:00
*
* variant 2 ( lpc2000_v2 ) :
* - 213 x
* - 214 x
2009-08-13 08:54:53 -05:00
* - 2101 | 2 | 3
* - 2364 | 6 | 8
2008-02-25 02:01:21 -06:00
* - 2378
2009-08-13 08:54:53 -05:00
*
* lpc1700 :
* - 175 x
* - 176 x ( tested with LPC1768 )
2008-02-25 02:01:21 -06:00
*/
2009-11-13 13:32:28 -06:00
static int lpc2000_build_sector_list ( struct flash_bank * bank )
2008-02-25 02:01:21 -06:00
{
2009-11-13 09:38:03 -06:00
struct lpc2000_flash_bank * lpc2000_info = bank - > driver_priv ;
2009-08-13 08:54:53 -05:00
int i ;
uint32_t offset = 0 ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* default to a 4096 write buffer */
lpc2000_info - > cmd51_max_buffer = 4096 ;
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
if ( lpc2000_info - > variant = = lpc2000_v1 )
2008-02-25 02:01:21 -06:00
{
/* variant 1 has different layout for 128kb and 256kb flashes */
if ( bank - > size = = 128 * 1024 )
{
bank - > num_sectors = 16 ;
2009-11-13 09:37:54 -06:00
bank - > sectors = malloc ( sizeof ( struct flash_sector ) * 16 ) ;
2008-02-25 02:01:21 -06:00
for ( i = 0 ; i < 16 ; i + + )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 8 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
}
else if ( bank - > size = = 256 * 1024 )
{
bank - > num_sectors = 18 ;
2009-11-13 09:37:54 -06:00
bank - > sectors = malloc ( sizeof ( struct flash_sector ) * 18 ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
for ( i = 0 ; i < 8 ; i + + )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 8 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
for ( i = 8 ; i < 10 ; i + + )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 64 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
for ( i = 10 ; i < 18 ; i + + )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 8 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
}
else
{
2008-03-25 10:45:17 -05:00
LOG_ERROR ( " BUG: unknown bank->size encountered " ) ;
2008-02-25 02:01:21 -06:00
exit ( - 1 ) ;
}
}
2009-08-13 08:54:53 -05:00
else if ( lpc2000_info - > variant = = lpc2000_v2 )
2008-02-25 02:01:21 -06:00
{
/* variant 2 has a uniform layout, only number of sectors differs */
switch ( bank - > size )
{
case 4 * 1024 :
lpc2000_info - > cmd51_max_buffer = 1024 ;
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 1 ;
2008-02-25 02:01:21 -06:00
break ;
case 8 * 1024 :
lpc2000_info - > cmd51_max_buffer = 1024 ;
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 2 ;
2008-02-25 02:01:21 -06:00
break ;
case 16 * 1024 :
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 4 ;
2008-02-25 02:01:21 -06:00
break ;
case 32 * 1024 :
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 8 ;
2008-02-25 02:01:21 -06:00
break ;
case 64 * 1024 :
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 9 ;
2008-02-25 02:01:21 -06:00
break ;
case 128 * 1024 :
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 11 ;
2008-02-25 02:01:21 -06:00
break ;
case 256 * 1024 :
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 15 ;
2008-02-25 02:01:21 -06:00
break ;
case 512 * 1024 :
case 500 * 1024 :
2009-08-13 08:54:53 -05:00
bank - > num_sectors = 27 ;
2008-02-25 02:01:21 -06:00
break ;
default :
2008-03-25 10:45:17 -05:00
LOG_ERROR ( " BUG: unknown bank->size encountered " ) ;
2008-02-25 02:01:21 -06:00
exit ( - 1 ) ;
break ;
}
2009-04-30 03:15:27 -05:00
2009-11-13 09:37:54 -06:00
bank - > sectors = malloc ( sizeof ( struct flash_sector ) * bank - > num_sectors ) ;
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
for ( i = 0 ; i < bank - > num_sectors ; i + + )
2008-02-25 02:01:21 -06:00
{
if ( ( i > = 0 ) & & ( i < 8 ) )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 4 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
if ( ( i > = 8 ) & & ( i < 22 ) )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 32 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
if ( ( i > = 22 ) & & ( i < 27 ) )
{
bank - > sectors [ i ] . offset = offset ;
bank - > sectors [ i ] . size = 4 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
}
}
2009-08-13 08:54:53 -05:00
else if ( lpc2000_info - > variant = = lpc1700 )
{
switch ( bank - > size )
{
case 32 * 1024 :
bank - > num_sectors = 8 ;
break ;
case 64 * 1024 :
bank - > num_sectors = 16 ;
break ;
case 128 * 1024 :
bank - > num_sectors = 18 ;
break ;
case 256 * 1024 :
bank - > num_sectors = 22 ;
break ;
case 512 * 1024 :
bank - > num_sectors = 30 ;
break ;
default :
LOG_ERROR ( " BUG: unknown bank->size encountered " ) ;
exit ( - 1 ) ;
}
2009-11-13 09:37:54 -06:00
bank - > sectors = malloc ( sizeof ( struct flash_sector ) * bank - > num_sectors ) ;
2009-08-13 08:54:53 -05:00
for ( i = 0 ; i < bank - > num_sectors ; i + + )
{
bank - > sectors [ i ] . offset = offset ;
/* sectors 0-15 are 4kB-sized, 16 and above are 32kB-sized for LPC17xx devices */
bank - > sectors [ i ] . size = ( i < 16 ) ? 4 * 1024 : 32 * 1024 ;
offset + = bank - > sectors [ i ] . size ;
bank - > sectors [ i ] . is_erased = - 1 ;
bank - > sectors [ i ] . is_protected = 1 ;
}
}
2008-02-25 02:01:21 -06:00
else
{
2008-03-25 10:45:17 -05:00
LOG_ERROR ( " BUG: unknown lpc2000_info->variant encountered " ) ;
2008-02-25 02:01:21 -06:00
exit ( - 1 ) ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-08-13 08:54:53 -05:00
/* call LPC1700/LPC2000 IAP function
* uses 180 bytes working area
2008-02-25 02:01:21 -06:00
* 0x0 to 0x7 : jump gate ( BX to thumb state , b - 2 to wait )
2009-08-13 08:54:53 -05:00
* 0x8 to 0x1f : command parameter table ( 1 + 5 words )
* 0x20 to 0x33 : command result table ( 1 + 4 words )
* 0x34 to 0xb3 : stack ( only 128 b needed )
2008-02-25 02:01:21 -06:00
*/
2009-11-13 13:32:28 -06:00
static int lpc2000_iap_call ( struct flash_bank * bank , int code , uint32_t param_table [ 5 ] , uint32_t result_table [ 4 ] )
2008-02-25 02:01:21 -06:00
{
2008-10-15 06:44:36 -05:00
int retval ;
2009-11-13 09:38:03 -06:00
struct lpc2000_flash_bank * lpc2000_info = bank - > driver_priv ;
2009-11-13 12:11:13 -06:00
struct target * target = bank - > target ;
2009-11-13 10:39:30 -06:00
struct mem_param mem_params [ 2 ] ;
2009-11-13 10:39:42 -06:00
struct reg_param reg_params [ 5 ] ;
2009-12-04 22:19:49 -06:00
struct arm_algorithm armv4_5_info ; /* for LPC2000 */
2009-11-13 10:41:43 -06:00
struct armv7m_algorithm armv7m_info ; /* for LPC1700 */
2009-08-13 08:54:53 -05:00
uint32_t status_code ;
uint32_t iap_entry_point = 0 ; /* to make compiler happier */
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* regrab previously allocated working_area, or allocate a new one */
if ( ! lpc2000_info - > iap_working_area )
{
2009-06-18 02:06:25 -05:00
uint8_t jump_gate [ 8 ] ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* make sure we have a working area */
2009-08-13 08:54:53 -05:00
if ( target_alloc_working_area ( target , 180 , & lpc2000_info - > iap_working_area ) ! = ERROR_OK )
2008-02-25 02:01:21 -06:00
{
2008-03-25 10:45:17 -05:00
LOG_ERROR ( " no working area specified, can't write LPC2000 internal flash " ) ;
2008-02-25 02:01:21 -06:00
return ERROR_FLASH_OPERATION_FAILED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* write IAP code to working area */
2009-08-13 08:54:53 -05:00
switch ( lpc2000_info - > variant )
{
case lpc1700 :
2009-12-07 16:54:12 -06:00
target_buffer_set_u32 ( target , jump_gate ,
ARMV4_5_T_BX ( 12 ) ) ;
target_buffer_set_u32 ( target , jump_gate + 4 ,
2010-02-02 07:22:07 -06:00
ARMV5_T_BKPT ( 0 ) ) ;
2009-08-13 08:54:53 -05:00
break ;
case lpc2000_v1 :
case lpc2000_v2 :
target_buffer_set_u32 ( target , jump_gate , ARMV4_5_BX ( 12 ) ) ;
target_buffer_set_u32 ( target , jump_gate + 4 , ARMV4_5_B ( 0xfffffe , 0 ) ) ;
break ;
default :
LOG_ERROR ( " BUG: unknown bank->size encountered " ) ;
exit ( - 1 ) ;
}
2009-06-23 17:35:09 -05:00
if ( ( retval = target_write_memory ( target , lpc2000_info - > iap_working_area - > address , 4 , 2 , jump_gate ) ) ! = ERROR_OK )
2008-10-15 06:44:36 -05:00
{
2009-08-13 08:54:53 -05:00
LOG_ERROR ( " Write memory at address 0x%8.8 " PRIx32 " failed (check work_area definition) " , lpc2000_info - > iap_working_area - > address ) ;
2008-10-15 06:44:36 -05:00
return retval ;
}
2008-02-25 02:01:21 -06:00
}
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
switch ( lpc2000_info - > variant )
{
case lpc1700 :
armv7m_info . common_magic = ARMV7M_COMMON_MAGIC ;
armv7m_info . core_mode = ARMV7M_MODE_ANY ;
iap_entry_point = 0x1fff1ff1 ;
break ;
case lpc2000_v1 :
case lpc2000_v2 :
2009-12-04 21:46:44 -06:00
armv4_5_info . common_magic = ARM_COMMON_MAGIC ;
2009-12-04 21:21:14 -06:00
armv4_5_info . core_mode = ARM_MODE_SVC ;
2009-12-04 21:14:48 -06:00
armv4_5_info . core_state = ARM_STATE_ARM ;
2009-08-13 08:54:53 -05:00
iap_entry_point = 0x7ffffff1 ;
break ;
default :
LOG_ERROR ( " BUG: unknown lpc2000->variant encountered " ) ;
exit ( - 1 ) ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* command parameter table */
2009-08-13 08:54:53 -05:00
init_mem_param ( & mem_params [ 0 ] , lpc2000_info - > iap_working_area - > address + 8 , 6 * 4 , PARAM_OUT ) ;
2008-02-25 02:01:21 -06:00
target_buffer_set_u32 ( target , mem_params [ 0 ] . value , code ) ;
2009-08-13 08:54:53 -05:00
target_buffer_set_u32 ( target , mem_params [ 0 ] . value + 0x04 , param_table [ 0 ] ) ;
target_buffer_set_u32 ( target , mem_params [ 0 ] . value + 0x08 , param_table [ 1 ] ) ;
target_buffer_set_u32 ( target , mem_params [ 0 ] . value + 0x0c , param_table [ 2 ] ) ;
2008-02-25 02:01:21 -06:00
target_buffer_set_u32 ( target , mem_params [ 0 ] . value + 0x10 , param_table [ 3 ] ) ;
target_buffer_set_u32 ( target , mem_params [ 0 ] . value + 0x14 , param_table [ 4 ] ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
init_reg_param ( & reg_params [ 0 ] , " r0 " , 32 , PARAM_OUT ) ;
2009-08-13 08:54:53 -05:00
buf_set_u32 ( reg_params [ 0 ] . value , 0 , 32 , lpc2000_info - > iap_working_area - > address + 0x08 ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* command result table */
2009-08-13 08:54:53 -05:00
init_mem_param ( & mem_params [ 1 ] , lpc2000_info - > iap_working_area - > address + 0x20 , 5 * 4 , PARAM_IN ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
init_reg_param ( & reg_params [ 1 ] , " r1 " , 32 , PARAM_OUT ) ;
buf_set_u32 ( reg_params [ 1 ] . value , 0 , 32 , lpc2000_info - > iap_working_area - > address + 0x20 ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* IAP entry point */
init_reg_param ( & reg_params [ 2 ] , " r12 " , 32 , PARAM_OUT ) ;
2009-08-13 08:54:53 -05:00
buf_set_u32 ( reg_params [ 2 ] . value , 0 , 32 , iap_entry_point ) ;
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
switch ( lpc2000_info - > variant )
{
case lpc1700 :
/* IAP stack */
init_reg_param ( & reg_params [ 3 ] , " sp " , 32 , PARAM_OUT ) ;
buf_set_u32 ( reg_params [ 3 ] . value , 0 , 32 , lpc2000_info - > iap_working_area - > address + 0xb4 ) ;
2008-02-25 02:01:21 -06:00
2009-08-13 08:54:53 -05:00
/* return address */
init_reg_param ( & reg_params [ 4 ] , " lr " , 32 , PARAM_OUT ) ;
buf_set_u32 ( reg_params [ 4 ] . value , 0 , 32 , ( lpc2000_info - > iap_working_area - > address + 0x04 ) | 1 ) ; /* bit0 of LR = 1 to return in Thumb mode */
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
target_run_algorithm ( target , 2 , mem_params , 5 , reg_params , lpc2000_info - > iap_working_area - > address , lpc2000_info - > iap_working_area - > address + 0x4 , 10000 , & armv7m_info ) ;
break ;
case lpc2000_v1 :
case lpc2000_v2 :
/* IAP stack */
2009-12-12 17:43:02 -06:00
init_reg_param ( & reg_params [ 3 ] , " sp_svc " , 32 , PARAM_OUT ) ;
2009-08-13 08:54:53 -05:00
buf_set_u32 ( reg_params [ 3 ] . value , 0 , 32 , lpc2000_info - > iap_working_area - > address + 0xb4 ) ;
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
/* return address */
init_reg_param ( & reg_params [ 4 ] , " lr_svc " , 32 , PARAM_OUT ) ;
buf_set_u32 ( reg_params [ 4 ] . value , 0 , 32 , lpc2000_info - > iap_working_area - > address + 0x04 ) ;
target_run_algorithm ( target , 2 , mem_params , 5 , reg_params , lpc2000_info - > iap_working_area - > address , lpc2000_info - > iap_working_area - > address + 0x4 , 10000 , & armv4_5_info ) ;
break ;
default :
LOG_ERROR ( " BUG: unknown lpc2000->variant encountered " ) ;
exit ( - 1 ) ;
}
status_code = target_buffer_get_u32 ( target , mem_params [ 1 ] . value ) ;
result_table [ 0 ] = target_buffer_get_u32 ( target , mem_params [ 1 ] . value + 0x04 ) ;
result_table [ 1 ] = target_buffer_get_u32 ( target , mem_params [ 1 ] . value + 0x08 ) ;
result_table [ 2 ] = target_buffer_get_u32 ( target , mem_params [ 1 ] . value + 0x0c ) ;
result_table [ 3 ] = target_buffer_get_u32 ( target , mem_params [ 1 ] . value + 0x10 ) ;
LOG_DEBUG ( " IAP command = %i (0x%8.8 " PRIx32 " , 0x%8.8 " PRIx32 " , 0x%8.8 " PRIx32 " , 0x%8.8 " PRIx32 " , 0x%8.8 " PRIx32 " ) completed with result = %8.8 " PRIx32 ,
code , param_table [ 0 ] , param_table [ 1 ] , param_table [ 2 ] , param_table [ 3 ] , param_table [ 4 ] , status_code ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
destroy_mem_param ( & mem_params [ 0 ] ) ;
destroy_mem_param ( & mem_params [ 1 ] ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
destroy_reg_param ( & reg_params [ 0 ] ) ;
destroy_reg_param ( & reg_params [ 1 ] ) ;
destroy_reg_param ( & reg_params [ 2 ] ) ;
destroy_reg_param ( & reg_params [ 3 ] ) ;
destroy_reg_param ( & reg_params [ 4 ] ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return status_code ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_iap_blank_check ( struct flash_bank * bank , int first , int last )
2008-02-25 02:01:21 -06:00
{
2009-06-18 02:10:25 -05:00
uint32_t param_table [ 5 ] ;
2009-08-13 08:54:53 -05:00
uint32_t result_table [ 4 ] ;
2008-02-25 02:01:21 -06:00
int status_code ;
int i ;
2009-04-30 03:15:27 -05:00
2009-04-18 05:08:13 -05:00
if ( ( first < 0 ) | | ( last > = bank - > num_sectors ) )
2008-02-25 02:01:21 -06:00
return ERROR_FLASH_SECTOR_INVALID ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
for ( i = first ; i < = last ; i + + )
{
/* check single sector */
param_table [ 0 ] = param_table [ 1 ] = i ;
status_code = lpc2000_iap_call ( bank , 53 , param_table , result_table ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
switch ( status_code )
{
case ERROR_FLASH_OPERATION_FAILED :
return ERROR_FLASH_OPERATION_FAILED ;
case LPC2000_CMD_SUCCESS :
bank - > sectors [ i ] . is_erased = 1 ;
break ;
case LPC2000_SECTOR_NOT_BLANK :
bank - > sectors [ i ] . is_erased = 0 ;
break ;
case LPC2000_INVALID_SECTOR :
bank - > sectors [ i ] . is_erased = 0 ;
break ;
case LPC2000_BUSY :
return ERROR_FLASH_BUSY ;
break ;
default :
2009-08-13 08:54:53 -05:00
LOG_ERROR ( " BUG: unknown LPC2000 status code %i " , status_code ) ;
2008-02-25 02:01:21 -06:00
exit ( - 1 ) ;
}
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-08-13 08:54:53 -05:00
/*
* flash bank lpc2000 < base > < size > 0 0 < target # > < lpc_variant > < cclk > [ calc_checksum ]
2008-02-25 02:01:21 -06:00
*/
2009-11-10 03:41:30 -06:00
FLASH_BANK_COMMAND_HANDLER ( lpc2000_flash_bank_command )
2008-02-25 02:01:21 -06:00
{
2009-11-13 09:38:03 -06:00
struct lpc2000_flash_bank * lpc2000_info ;
2009-04-30 03:15:27 -05:00
2009-11-15 06:57:12 -06:00
if ( CMD_ARGC < 8 )
2008-02-25 02:01:21 -06:00
{
2008-03-25 10:45:17 -05:00
LOG_WARNING ( " incomplete flash_bank lpc2000 configuration " ) ;
2008-02-25 02:01:21 -06:00
return ERROR_FLASH_BANK_INVALID ;
}
2009-04-30 03:15:27 -05:00
2009-11-13 09:38:03 -06:00
lpc2000_info = malloc ( sizeof ( struct lpc2000_flash_bank ) ) ;
2008-02-25 02:01:21 -06:00
bank - > driver_priv = lpc2000_info ;
2009-04-30 03:15:27 -05:00
2009-11-15 10:15:59 -06:00
if ( strcmp ( CMD_ARGV [ 6 ] , " lpc2000_v1 " ) = = 0 )
2008-02-25 02:01:21 -06:00
{
2009-08-13 08:54:53 -05:00
lpc2000_info - > variant = lpc2000_v1 ;
2008-02-25 02:01:21 -06:00
lpc2000_info - > cmd51_dst_boundary = 512 ;
lpc2000_info - > cmd51_can_256b = 0 ;
lpc2000_info - > cmd51_can_8192b = 1 ;
2009-08-13 08:54:53 -05:00
lpc2000_info - > checksum_vector = 5 ;
2008-02-25 02:01:21 -06:00
}
2009-11-15 10:15:59 -06:00
else if ( strcmp ( CMD_ARGV [ 6 ] , " lpc2000_v2 " ) = = 0 )
2008-02-25 02:01:21 -06:00
{
2009-08-13 08:54:53 -05:00
lpc2000_info - > variant = lpc2000_v2 ;
2008-02-25 02:01:21 -06:00
lpc2000_info - > cmd51_dst_boundary = 256 ;
lpc2000_info - > cmd51_can_256b = 1 ;
lpc2000_info - > cmd51_can_8192b = 0 ;
2009-08-13 08:54:53 -05:00
lpc2000_info - > checksum_vector = 5 ;
}
2009-11-15 10:15:59 -06:00
else if ( strcmp ( CMD_ARGV [ 6 ] , " lpc1700 " ) = = 0 )
2009-08-13 08:54:53 -05:00
{
lpc2000_info - > variant = lpc1700 ;
lpc2000_info - > cmd51_dst_boundary = 256 ;
lpc2000_info - > cmd51_can_256b = 1 ;
lpc2000_info - > cmd51_can_8192b = 0 ;
lpc2000_info - > checksum_vector = 7 ;
2008-02-25 02:01:21 -06:00
}
else
{
2009-11-15 10:15:59 -06:00
LOG_ERROR ( " unknown LPC2000 variant: %s " , CMD_ARGV [ 6 ] ) ;
2008-02-25 02:01:21 -06:00
free ( lpc2000_info ) ;
return ERROR_FLASH_BANK_INVALID ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
lpc2000_info - > iap_working_area = NULL ;
2009-11-15 10:15:59 -06:00
COMMAND_PARSE_NUMBER ( u32 , CMD_ARGV [ 7 ] , lpc2000_info - > cclk ) ;
2008-02-25 02:01:21 -06:00
lpc2000_info - > calc_checksum = 0 ;
lpc2000_build_sector_list ( bank ) ;
2009-04-30 03:15:27 -05:00
2009-11-15 06:57:12 -06:00
if ( CMD_ARGC > = 9 )
2008-02-25 02:01:21 -06:00
{
2009-11-15 10:15:59 -06:00
if ( strcmp ( CMD_ARGV [ 8 ] , " calc_checksum " ) = = 0 )
2008-02-25 02:01:21 -06:00
lpc2000_info - > calc_checksum = 1 ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_erase ( struct flash_bank * bank , int first , int last )
2008-02-25 02:01:21 -06:00
{
2009-11-13 09:38:03 -06:00
struct lpc2000_flash_bank * lpc2000_info = bank - > driver_priv ;
2009-06-18 02:10:25 -05:00
uint32_t param_table [ 5 ] ;
2009-08-13 08:54:53 -05:00
uint32_t result_table [ 4 ] ;
2008-02-25 02:01:21 -06:00
int status_code ;
2009-04-30 03:15:27 -05:00
2008-02-28 04:44:41 -06:00
if ( bank - > target - > state ! = TARGET_HALTED )
{
2008-08-17 14:40:17 -05:00
LOG_ERROR ( " Target not halted " ) ;
2008-02-28 04:44:41 -06:00
return ERROR_TARGET_NOT_HALTED ;
}
2008-02-25 02:01:21 -06:00
param_table [ 0 ] = first ;
param_table [ 1 ] = last ;
param_table [ 2 ] = lpc2000_info - > cclk ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* Prepare sectors */
status_code = lpc2000_iap_call ( bank , 50 , param_table , result_table ) ;
switch ( status_code )
{
case ERROR_FLASH_OPERATION_FAILED :
return ERROR_FLASH_OPERATION_FAILED ;
case LPC2000_CMD_SUCCESS :
break ;
case LPC2000_INVALID_SECTOR :
return ERROR_FLASH_SECTOR_INVALID ;
break ;
default :
2008-03-25 10:45:17 -05:00
LOG_WARNING ( " lpc2000 prepare sectors returned %i " , status_code ) ;
2008-02-25 02:01:21 -06:00
return ERROR_FLASH_OPERATION_FAILED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* Erase sectors */
status_code = lpc2000_iap_call ( bank , 52 , param_table , result_table ) ;
switch ( status_code )
{
case ERROR_FLASH_OPERATION_FAILED :
return ERROR_FLASH_OPERATION_FAILED ;
case LPC2000_CMD_SUCCESS :
break ;
case LPC2000_INVALID_SECTOR :
return ERROR_FLASH_SECTOR_INVALID ;
break ;
default :
2008-03-25 10:45:17 -05:00
LOG_WARNING ( " lpc2000 erase sectors returned %i " , status_code ) ;
2008-02-25 02:01:21 -06:00
return ERROR_FLASH_OPERATION_FAILED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_protect ( struct flash_bank * bank , int set , int first , int last )
2008-02-25 02:01:21 -06:00
{
/* can't protect/unprotect on the lpc2000 */
return ERROR_OK ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_write ( struct flash_bank * bank , uint8_t * buffer , uint32_t offset , uint32_t count )
2008-02-25 02:01:21 -06:00
{
2009-11-13 09:38:03 -06:00
struct lpc2000_flash_bank * lpc2000_info = bank - > driver_priv ;
2009-11-13 12:11:13 -06:00
struct target * target = bank - > target ;
2009-06-18 02:10:25 -05:00
uint32_t dst_min_alignment ;
uint32_t bytes_remaining = count ;
uint32_t bytes_written = 0 ;
2008-02-25 02:01:21 -06:00
int first_sector = 0 ;
int last_sector = 0 ;
2009-06-18 02:10:25 -05:00
uint32_t param_table [ 5 ] ;
2009-08-13 08:54:53 -05:00
uint32_t result_table [ 4 ] ;
2008-02-25 02:01:21 -06:00
int status_code ;
int i ;
2009-11-13 10:44:30 -06:00
struct working_area * download_area ;
2008-06-03 14:36:42 -05:00
int retval = ERROR_OK ;
2009-04-30 03:15:27 -05:00
2008-02-28 04:44:41 -06:00
if ( bank - > target - > state ! = TARGET_HALTED )
{
2008-08-17 14:40:17 -05:00
LOG_ERROR ( " Target not halted " ) ;
2008-02-28 04:44:41 -06:00
return ERROR_TARGET_NOT_HALTED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
if ( offset + count > bank - > size )
2008-06-03 14:36:42 -05:00
return ERROR_FLASH_DST_OUT_OF_BANK ;
2009-04-30 03:15:27 -05:00
2009-08-13 08:54:53 -05:00
dst_min_alignment = lpc2000_info - > cmd51_dst_boundary ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
if ( offset % dst_min_alignment )
{
2009-06-20 22:20:17 -05:00
LOG_WARNING ( " offset 0x% " PRIx32 " breaks required alignment 0x% " PRIx32 , offset , dst_min_alignment ) ;
2008-06-03 14:36:42 -05:00
return ERROR_FLASH_DST_BREAKS_ALIGNMENT ;
2008-02-25 02:01:21 -06:00
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
for ( i = 0 ; i < bank - > num_sectors ; i + + )
{
if ( offset > = bank - > sectors [ i ] . offset )
first_sector = i ;
2009-11-16 04:53:57 -06:00
if ( offset + DIV_ROUND_UP ( count , dst_min_alignment ) * dst_min_alignment > bank - > sectors [ i ] . offset )
2008-02-25 02:01:21 -06:00
last_sector = i ;
}
2009-04-30 03:15:27 -05:00
2008-03-25 10:45:17 -05:00
LOG_DEBUG ( " first_sector: %i, last_sector: %i " , first_sector , last_sector ) ;
2008-02-25 02:01:21 -06:00
/* check if exception vectors should be flashed */
if ( ( offset = = 0 ) & & ( count > = 0x20 ) & & lpc2000_info - > calc_checksum )
{
2009-06-18 02:10:25 -05:00
uint32_t checksum = 0 ;
2009-08-13 08:54:53 -05:00
int i ;
2008-02-25 02:01:21 -06:00
for ( i = 0 ; i < 8 ; i + + )
{
2009-08-13 08:54:53 -05:00
LOG_DEBUG ( " Vector 0x%2.2x: 0x%8.8 " PRIx32 , i * 4 , buf_get_u32 ( buffer + ( i * 4 ) , 0 , 32 ) ) ;
if ( i ! = lpc2000_info - > checksum_vector )
2008-02-25 02:01:21 -06:00
checksum + = buf_get_u32 ( buffer + ( i * 4 ) , 0 , 32 ) ;
}
checksum = 0 - checksum ;
2009-06-20 22:20:17 -05:00
LOG_DEBUG ( " checksum: 0x%8.8 " PRIx32 , checksum ) ;
2009-04-30 04:39:24 -05:00
2009-08-13 08:54:53 -05:00
uint32_t original_value = buf_get_u32 ( buffer + ( lpc2000_info - > checksum_vector * 4 ) , 0 , 32 ) ;
2009-06-23 17:38:12 -05:00
if ( original_value ! = checksum )
2009-04-30 04:39:24 -05:00
{
2009-08-13 08:54:53 -05:00
LOG_WARNING ( " Verification will fail since checksum in image (0x%8.8 " PRIx32 " ) to be written to flash is different from calculated vector checksum (0x%8.8 " PRIx32 " ). " ,
2009-04-30 04:39:24 -05:00
original_value , checksum ) ;
LOG_WARNING ( " To remove this warning modify build tools on developer PC to inject correct LPC vector checksum. " ) ;
}
2009-08-13 08:54:53 -05:00
buf_set_u32 ( buffer + ( lpc2000_info - > checksum_vector * 4 ) , 0 , 32 , checksum ) ;
2008-02-25 02:01:21 -06:00
}
2009-04-30 03:15:27 -05:00
2008-06-03 14:36:42 -05:00
/* allocate a working area */
if ( target_alloc_working_area ( target , lpc2000_info - > cmd51_max_buffer , & download_area ) ! = ERROR_OK )
{
LOG_ERROR ( " no working area specified, can't write LPC2000 internal flash " ) ;
return ERROR_FLASH_OPERATION_FAILED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
while ( bytes_remaining > 0 )
{
2009-06-18 02:10:25 -05:00
uint32_t thisrun_bytes ;
2008-02-25 02:01:21 -06:00
if ( bytes_remaining > = lpc2000_info - > cmd51_max_buffer )
thisrun_bytes = lpc2000_info - > cmd51_max_buffer ;
else if ( bytes_remaining > = 1024 )
thisrun_bytes = 1024 ;
else if ( ( bytes_remaining > = 512 ) | | ( ! lpc2000_info - > cmd51_can_256b ) )
thisrun_bytes = 512 ;
else
thisrun_bytes = 256 ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* Prepare sectors */
param_table [ 0 ] = first_sector ;
param_table [ 1 ] = last_sector ;
status_code = lpc2000_iap_call ( bank , 50 , param_table , result_table ) ;
switch ( status_code )
{
case ERROR_FLASH_OPERATION_FAILED :
2008-06-03 05:21:47 -05:00
retval = ERROR_FLASH_OPERATION_FAILED ;
2008-06-03 14:36:42 -05:00
break ;
2008-02-25 02:01:21 -06:00
case LPC2000_CMD_SUCCESS :
break ;
case LPC2000_INVALID_SECTOR :
2008-06-03 05:21:47 -05:00
retval = ERROR_FLASH_SECTOR_INVALID ;
2008-02-25 02:01:21 -06:00
break ;
default :
2008-03-25 10:45:17 -05:00
LOG_WARNING ( " lpc2000 prepare sectors returned %i " , status_code ) ;
2008-06-03 05:21:47 -05:00
retval = ERROR_FLASH_OPERATION_FAILED ;
2008-06-03 14:36:42 -05:00
break ;
2008-02-25 02:01:21 -06:00
}
2008-06-03 05:21:47 -05:00
2008-06-03 14:36:42 -05:00
/* Exit if error occured */
if ( retval ! = ERROR_OK )
break ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
if ( bytes_remaining > = thisrun_bytes )
2008-12-13 06:44:39 -06:00
{
2008-06-03 14:36:42 -05:00
if ( ( retval = target_write_buffer ( bank - > target , download_area - > address , thisrun_bytes , buffer + bytes_written ) ) ! = ERROR_OK )
2008-02-25 02:01:21 -06:00
{
2008-06-03 14:36:42 -05:00
retval = ERROR_FLASH_OPERATION_FAILED ;
break ;
2008-02-25 02:01:21 -06:00
}
}
else
{
2009-06-18 02:06:25 -05:00
uint8_t * last_buffer = malloc ( thisrun_bytes ) ;
2008-02-25 02:01:21 -06:00
memcpy ( last_buffer , buffer + bytes_written , bytes_remaining ) ;
2009-08-13 08:54:53 -05:00
memset ( last_buffer + bytes_remaining , 0xff , thisrun_bytes - bytes_remaining ) ;
2008-02-25 02:01:21 -06:00
target_write_buffer ( bank - > target , download_area - > address , thisrun_bytes , last_buffer ) ;
free ( last_buffer ) ;
}
2009-04-30 03:15:27 -05:00
2009-06-20 22:20:17 -05:00
LOG_DEBUG ( " writing 0x% " PRIx32 " bytes to address 0x% " PRIx32 , thisrun_bytes , bank - > base + offset + bytes_written ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
/* Write data */
param_table [ 0 ] = bank - > base + offset + bytes_written ;
param_table [ 1 ] = download_area - > address ;
param_table [ 2 ] = thisrun_bytes ;
param_table [ 3 ] = lpc2000_info - > cclk ;
status_code = lpc2000_iap_call ( bank , 51 , param_table , result_table ) ;
switch ( status_code )
{
case ERROR_FLASH_OPERATION_FAILED :
2008-06-03 05:21:47 -05:00
retval = ERROR_FLASH_OPERATION_FAILED ;
2008-06-03 14:36:42 -05:00
break ;
2008-02-25 02:01:21 -06:00
case LPC2000_CMD_SUCCESS :
break ;
case LPC2000_INVALID_SECTOR :
2008-06-03 05:21:47 -05:00
retval = ERROR_FLASH_SECTOR_INVALID ;
2008-02-25 02:01:21 -06:00
break ;
default :
2008-03-25 10:45:17 -05:00
LOG_WARNING ( " lpc2000 returned %i " , status_code ) ;
2008-06-03 05:21:47 -05:00
retval = ERROR_FLASH_OPERATION_FAILED ;
2008-06-03 14:36:42 -05:00
break ;
2008-02-25 02:01:21 -06:00
}
2009-04-30 03:15:27 -05:00
2008-06-03 14:36:42 -05:00
/* Exit if error occured */
if ( retval ! = ERROR_OK )
break ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
if ( bytes_remaining > thisrun_bytes )
bytes_remaining - = thisrun_bytes ;
else
bytes_remaining = 0 ;
bytes_written + = thisrun_bytes ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
target_free_working_area ( target , download_area ) ;
2009-04-30 03:15:27 -05:00
2008-06-03 05:21:47 -05:00
return retval ;
2008-02-25 02:01:21 -06:00
}
2009-11-13 13:32:28 -06:00
static int lpc2000_probe ( struct flash_bank * bank )
2008-02-25 02:01:21 -06:00
{
2009-04-30 03:15:27 -05:00
/* we can't probe on an lpc2000
2008-02-25 02:01:21 -06:00
* if this is an lpc2xxx , it has the configured flash
*/
return ERROR_OK ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_erase_check ( struct flash_bank * bank )
2008-02-25 02:01:21 -06:00
{
if ( bank - > target - > state ! = TARGET_HALTED )
{
2008-08-17 14:40:17 -05:00
LOG_ERROR ( " Target not halted " ) ;
2008-02-25 02:01:21 -06:00
return ERROR_TARGET_NOT_HALTED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return lpc2000_iap_blank_check ( bank , 0 , bank - > num_sectors - 1 ) ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_protect_check ( struct flash_bank * bank )
2008-02-25 02:01:21 -06:00
{
/* sectors are always protected */
return ERROR_OK ;
}
2009-11-13 13:32:28 -06:00
static int lpc2000_info ( struct flash_bank * bank , char * buf , int buf_size )
2008-02-25 02:01:21 -06:00
{
2009-11-13 09:38:03 -06:00
struct lpc2000_flash_bank * lpc2000_info = bank - > driver_priv ;
2008-02-25 02:01:21 -06:00
2009-08-13 08:54:53 -05:00
snprintf ( buf , buf_size , " lpc2000 flash driver variant: %i, clk: % " PRIi32 " kHz " , lpc2000_info - > variant , lpc2000_info - > cclk ) ;
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-11-10 01:56:52 -06:00
COMMAND_HANDLER ( lpc2000_handle_part_id_command )
2008-02-25 02:01:21 -06:00
{
2009-06-18 02:10:25 -05:00
uint32_t param_table [ 5 ] ;
2009-08-13 08:54:53 -05:00
uint32_t result_table [ 4 ] ;
2008-02-25 02:01:21 -06:00
int status_code ;
2009-11-15 06:57:12 -06:00
if ( CMD_ARGC < 1 )
2008-02-25 02:01:21 -06:00
{
2008-02-25 01:50:03 -06:00
return ERROR_COMMAND_SYNTAX_ERROR ;
2008-02-25 02:01:21 -06:00
}
2009-04-30 03:15:27 -05:00
2009-11-13 13:32:28 -06:00
struct flash_bank * bank ;
2009-11-17 15:07:36 -06:00
int retval = CALL_COMMAND_HANDLER ( flash_command_get_bank , 0 , & bank ) ;
2009-10-23 04:17:17 -05:00
if ( ERROR_OK ! = retval )
return retval ;
2008-02-25 02:01:21 -06:00
if ( bank - > target - > state ! = TARGET_HALTED )
{
2008-08-17 14:40:17 -05:00
LOG_ERROR ( " Target not halted " ) ;
2008-02-25 02:01:21 -06:00
return ERROR_TARGET_NOT_HALTED ;
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
if ( ( status_code = lpc2000_iap_call ( bank , 54 , param_table , result_table ) ) ! = 0x0 )
{
if ( status_code = = ERROR_FLASH_OPERATION_FAILED )
{
2009-11-15 07:57:37 -06:00
command_print ( CMD_CTX , " no sufficient working area specified, can't access LPC2000 IAP interface " ) ;
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-11-15 07:57:37 -06:00
command_print ( CMD_CTX , " lpc2000 IAP returned status code %i " , status_code ) ;
2008-02-25 02:01:21 -06:00
}
else
{
2009-11-15 07:57:37 -06:00
command_print ( CMD_CTX , " lpc2000 part id: 0x%8.8 " PRIx32 , result_table [ 0 ] ) ;
2008-02-25 02:01:21 -06:00
}
2009-04-30 03:15:27 -05:00
2008-02-25 02:01:21 -06:00
return ERROR_OK ;
}
2009-11-09 11:20:33 -06:00
2009-11-22 06:13:56 -06:00
static const struct command_registration lpc2000_exec_command_handlers [ ] = {
{
. name = " part_id " ,
2010-01-29 15:52:08 -06:00
. handler = lpc2000_handle_part_id_command ,
2009-11-22 06:13:56 -06:00
. mode = COMMAND_EXEC ,
. help = " print part id of lpc2000 flash bank <num> " ,
} ,
COMMAND_REGISTRATION_DONE
} ;
static const struct command_registration lpc2000_command_handlers [ ] = {
{
. name = " lpc2000 " ,
. mode = COMMAND_ANY ,
. help = " lpc2000 flash command group " ,
. chain = lpc2000_exec_command_handlers ,
} ,
COMMAND_REGISTRATION_DONE
} ;
2009-11-13 09:38:01 -06:00
struct flash_driver lpc2000_flash = {
2010-01-29 15:52:08 -06:00
. name = " lpc2000 " ,
. commands = lpc2000_command_handlers ,
. flash_bank_command = lpc2000_flash_bank_command ,
. erase = lpc2000_erase ,
. protect = lpc2000_protect ,
. write = lpc2000_write ,
2010-05-10 22:16:33 -05:00
. read = default_flash_read ,
2010-01-29 15:52:08 -06:00
. probe = lpc2000_probe ,
. auto_probe = lpc2000_probe ,
. erase_check = lpc2000_erase_check ,
. protect_check = lpc2000_protect_check ,
. info = lpc2000_info ,
} ;