1662c854e2
After SPI flash was written by the assembly language stub, the last SPI command was not terminated by raising CS. This left the SPI device in a hung state that prevented the flash from being read by the M4 SPIFI controller, even after the M4 was fully reset. To access the flash via SPIFI, it was necessary to completely power cycle the board. This fix adds the missing instructions to raise CS and terminate the SPI command after the last byte. This allows the M4 to be resumed or reset cleanly after flashing. The SPIFI memory is now immediately accessable at address 0x1400 0000 after flashing is complete. Change-Id: I4d5e03bded0fa00c430c2991f182dc18611d5f48 Signed-off-by: Anders <anders@openpuma.org> Reviewed-on: http://openocd.zylin.com/2359 Tested-by: jenkins Reviewed-by: Spencer Oliver <spen@spen-soft.co.uk>
223 lines
7.5 KiB
ArmAsm
223 lines
7.5 KiB
ArmAsm
/***************************************************************************
|
|
* Copyright (C) 2012 by George Harris *
|
|
* george@luminairecoffee.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., *
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *
|
|
***************************************************************************/
|
|
|
|
.text
|
|
.syntax unified
|
|
.cpu cortex-m3
|
|
.thumb
|
|
.thumb_func
|
|
|
|
/*
|
|
* Params :
|
|
* r0 = workarea start, status (out)
|
|
* r1 = workarea end
|
|
* r2 = target address (offset from flash base)
|
|
* r3 = count (bytes)
|
|
* r4 = page size
|
|
* Clobbered:
|
|
* r7 - rp
|
|
* r8 - wp, tmp
|
|
* r9 - send/receive data
|
|
* r10 - temp
|
|
* r11 - current page end address
|
|
*/
|
|
|
|
/*
|
|
* This code is embedded within: src/flash/nor/lpcspifi.c as a "C" array.
|
|
*
|
|
* To rebuild:
|
|
* arm-none-eabi-gcc -c lpcspifi_write.S
|
|
* arm-none-eabi-objcopy -O binary lpcspifi_write.o lpcspifi_write.bin
|
|
* xxd -c 8 -i lpcspifi_write.bin > lpcspifi_write.txt
|
|
*
|
|
* Then read and edit this result into the "C" source.
|
|
*/
|
|
|
|
#define SSP_BASE_HIGH 0x4008
|
|
#define SSP_BASE_LOW 0x3000
|
|
#define SSP_CR0_OFFSET 0x00
|
|
#define SSP_CR1_OFFSET 0x04
|
|
#define SSP_DATA_OFFSET 0x08
|
|
#define SSP_CPSR_OFFSET 0x10
|
|
#define SSP_SR_OFFSET 0x0c
|
|
|
|
#define SSP_CLOCK_BASE_HIGH 0x4005
|
|
#define SSP_CLOCK_BASE_LOW 0x0000
|
|
#define SSP_BRANCH_CLOCK_BASE_HIGH 0x4005
|
|
#define SSP_BRANCH_CLOCK_BASE_LOW 0x2000
|
|
#define SSP_BASE_CLOCK_OFFSET 0x94
|
|
#define SSP_BRANCH_CLOCK_OFFSET 0x700
|
|
|
|
#define IOCONFIG_BASE_HIGH 0x4008
|
|
#define IOCONFIG_BASE_LOW 0x6000
|
|
#define IOCONFIG_SCK_OFFSET 0x18c
|
|
#define IOCONFIG_HOLD_OFFSET 0x190
|
|
#define IOCONFIG_WP_OFFSET 0x194
|
|
#define IOCONFIG_MISO_OFFSET 0x198
|
|
#define IOCONFIG_MOSI_OFFSET 0x19c
|
|
#define IOCONFIG_CS_OFFSET 0x1a0
|
|
|
|
#define IO_BASE_HIGH 0x400f
|
|
#define IO_BASE_LOW 0x4000
|
|
#define IO_CS_OFFSET 0xab
|
|
#define IODIR_BASE_HIGH 0x400f
|
|
#define IODIR_BASE_LOW 0x6000
|
|
#define IO_CS_DIR_OFFSET 0x14
|
|
|
|
|
|
setup: /* Initialize SSP pins and module */
|
|
mov.w r10, #IOCONFIG_BASE_LOW
|
|
movt r10, #IOCONFIG_BASE_HIGH
|
|
mov.w r8, #0xea
|
|
str.w r8, [r10, #IOCONFIG_SCK_OFFSET] /* Configure SCK pin function */
|
|
mov.w r8, #0x40
|
|
str.w r8, [r10, #IOCONFIG_HOLD_OFFSET] /* Configure /HOLD pin function */
|
|
mov.w r8, #0x40
|
|
str.w r8, [r10, #IOCONFIG_WP_OFFSET] /* Configure /WP pin function */
|
|
mov.w r8, #0xed
|
|
str.w r8, [r10, #IOCONFIG_MISO_OFFSET] /* Configure MISO pin function */
|
|
mov.w r8, #0xed
|
|
str.w r8, [r10, #IOCONFIG_MOSI_OFFSET] /* Configure MOSI pin function */
|
|
mov.w r8, #0x44
|
|
str.w r8, [r10, #IOCONFIG_CS_OFFSET] /* Configure CS pin function */
|
|
|
|
mov.w r10, #IODIR_BASE_LOW
|
|
movt r10, #IODIR_BASE_HIGH
|
|
mov.w r8, #0x800
|
|
str r8, [r10, #IO_CS_DIR_OFFSET] /* Set CS as output */
|
|
mov.w r10, #IO_BASE_LOW
|
|
movt r10, #IO_BASE_HIGH
|
|
mov.w r8, #0xff
|
|
str.w r8, [r10, #IO_CS_OFFSET] /* Set CS high */
|
|
|
|
mov.w r10, #SSP_CLOCK_BASE_LOW
|
|
movt r10, #SSP_CLOCK_BASE_HIGH
|
|
mov.w r8, #0x0000
|
|
movt r8, #0x0100
|
|
str.w r8, [r10, #SSP_BASE_CLOCK_OFFSET] /* Configure SSP0 base clock (use 12 MHz IRC) */
|
|
|
|
mov.w r10, #SSP_BRANCH_CLOCK_BASE_LOW
|
|
movt r10, #SSP_BRANCH_CLOCK_BASE_HIGH
|
|
mov.w r8, #0x01
|
|
str.w r8, [r10, #SSP_BRANCH_CLOCK_OFFSET] /* Configure (enable) SSP0 branch clock */
|
|
|
|
mov.w r10, #SSP_BASE_LOW
|
|
movt r10, #SSP_BASE_HIGH
|
|
mov.w r8, #0x07
|
|
str.w r8, [r10, #SSP_CR0_OFFSET] /* Set clock postscale */
|
|
mov.w r8, #0x02
|
|
str.w r8, [r10, #SSP_CPSR_OFFSET] /* Set clock prescale */
|
|
str.w r8, [r10, #SSP_CR1_OFFSET] /* Enable SSP in SPI mode */
|
|
|
|
mov.w r11, #0x00
|
|
find_next_page_boundary:
|
|
add r11, r4 /* Increment to the next page */
|
|
cmp r11, r2
|
|
/* If we have not reached the next page boundary after the target address, keep going */
|
|
bls find_next_page_boundary
|
|
write_enable:
|
|
bl cs_down
|
|
mov.w r9, #0x06 /* Send the write enable command */
|
|
bl write_data
|
|
bl cs_up
|
|
|
|
bl cs_down
|
|
mov.w r9, #0x05 /* Get status register */
|
|
bl write_data
|
|
mov.w r9, #0x00 /* Dummy data to clock in status */
|
|
bl write_data
|
|
bl cs_up
|
|
|
|
tst r9, #0x02 /* If the WE bit isn't set, we have a problem. */
|
|
beq error
|
|
page_program:
|
|
bl cs_down
|
|
mov.w r9, #0x02 /* Send the page program command */
|
|
bl write_data
|
|
write_address:
|
|
lsr r9, r2, #16 /* Send the current 24-bit write address, MSB first */
|
|
bl write_data
|
|
lsr r9, r2, #8
|
|
bl write_data
|
|
mov.w r9, r2
|
|
bl write_data
|
|
wait_fifo:
|
|
ldr r8, [r0] /* read the write pointer */
|
|
cmp r8, #0 /* if it's zero, we're gonzo */
|
|
beq exit
|
|
ldr r7, [r0, #4] /* read the read pointer */
|
|
cmp r7, r8 /* wait until they are not equal */
|
|
beq wait_fifo
|
|
write:
|
|
ldrb r9, [r7], #0x01 /* Load one byte from the FIFO, increment the read pointer by 1 */
|
|
bl write_data /* send the byte to the flash chip */
|
|
|
|
cmp r7, r1 /* wrap the read pointer if it is at the end */
|
|
it cs
|
|
addcs r7, r0, #8 /* skip loader args */
|
|
str r7, [r0, #4] /* store the new read pointer */
|
|
subs r3, r3, #1 /* decrement count */
|
|
cbz r3, exit /* Exit if we have written everything */
|
|
|
|
add r2, #1 /* Increment flash address by 1 */
|
|
cmp r11, r2 /* See if we have reached the end of a page */
|
|
bne wait_fifo /* If not, keep writing bytes */
|
|
bl cs_up /* Otherwise, end the command and keep going w/ the next page */
|
|
add r11, r4 /* Move up the end-of-page address by the page size*/
|
|
wait_flash_busy: /* Wait for the flash to finish the previous page write */
|
|
bl cs_down
|
|
mov.w r9, #0x05 /* Get status register */
|
|
bl write_data
|
|
mov.w r9, #0x00 /* Dummy data to clock in status */
|
|
bl write_data
|
|
bl cs_up
|
|
tst r9, #0x01 /* If it isn't done, keep waiting */
|
|
bne wait_flash_busy
|
|
b write_enable /* If it is done, start a new page write */
|
|
write_data: /* Send/receive 1 byte of data over SSP */
|
|
mov.w r10, #SSP_BASE_LOW
|
|
movt r10, #SSP_BASE_HIGH
|
|
str.w r9, [r10, #SSP_DATA_OFFSET] /* Write supplied data to the SSP data reg */
|
|
wait_transmit:
|
|
ldr r9, [r10, #SSP_SR_OFFSET] /* Check SSP status */
|
|
tst r9, #0x0010 /* Check if BSY bit is set */
|
|
bne wait_transmit /* If still transmitting, keep waiting */
|
|
ldr r9, [r10, #SSP_DATA_OFFSET] /* Load received data */
|
|
bx lr /* Exit subroutine */
|
|
cs_up:
|
|
mov.w r8, #0xff
|
|
b cs_write
|
|
cs_down:
|
|
mov.w r8, #0x0000
|
|
cs_write:
|
|
mov.w r10, #IO_BASE_LOW
|
|
movt r10, #IO_BASE_HIGH
|
|
str.w r8, [r10, #IO_CS_OFFSET]
|
|
bx lr
|
|
error:
|
|
movs r0, #0
|
|
str r0, [r2, #4] /* set rp = 0 on error */
|
|
exit:
|
|
bl cs_up /* end the command before returning */
|
|
mov r0, r6
|
|
bkpt #0x00
|
|
|
|
.end
|