233 lines
6.3 KiB
ArmAsm
233 lines
6.3 KiB
ArmAsm
|
/***************************************************************************
|
||
|
* Copyright (C) 2014 by Mahavir Jain <mjain@marvell.com> *
|
||
|
* *
|
||
|
* Adapted from (contrib/loaders/flash/lpcspifi_write.S): *
|
||
|
* 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
|
||
|
|
||
|
/*
|
||
|
* For compilation:
|
||
|
* arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -c contrib/loaders/flash/mrvlqspi_write.S
|
||
|
* arm-none-eabi-objcopy -O binary mrvlqspi_write.o code.bin
|
||
|
* Copy code.bin into mrvlqspi flash driver
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* Params :
|
||
|
* r0 = workarea start, status (out)
|
||
|
* r1 = workarea end
|
||
|
* r2 = target address (offset from flash base)
|
||
|
* r3 = count (bytes)
|
||
|
* r4 = page size
|
||
|
* r5 = qspi base address
|
||
|
* Clobbered:
|
||
|
* r7 - rp
|
||
|
* r8 - wp, tmp
|
||
|
* r9 - send/receive data
|
||
|
* r10 - current page end address
|
||
|
*/
|
||
|
|
||
|
#define CNTL 0x0
|
||
|
#define CONF 0x4
|
||
|
#define DOUT 0x8
|
||
|
#define DIN 0xc
|
||
|
#define INSTR 0x10
|
||
|
#define ADDR 0x14
|
||
|
#define RDMODE 0x18
|
||
|
#define HDRCNT 0x1c
|
||
|
#define DINCNT 0x20
|
||
|
|
||
|
#define SS_EN (1 << 0)
|
||
|
#define XFER_RDY (1 << 1)
|
||
|
#define RFIFO_EMPTY (1 << 4)
|
||
|
#define WFIFO_EMPTY (1 << 6)
|
||
|
#define WFIFO_FULL (1 << 7)
|
||
|
#define FIFO_FLUSH (1 << 9)
|
||
|
#define RW_EN (1 << 13)
|
||
|
#define XFER_STOP (1 << 14)
|
||
|
#define XFER_START (1 << 15)
|
||
|
|
||
|
#define INS_WRITE_ENABLE 0x06
|
||
|
#define INS_READ_STATUS 0x05
|
||
|
#define INS_PAGE_PROGRAM 0x02
|
||
|
|
||
|
init:
|
||
|
mov.w r10, #0x00
|
||
|
find_next_page_boundary:
|
||
|
add r10, r4 /* Increment to the next page */
|
||
|
cmp r10, r2
|
||
|
/* If we have not reached the next page boundary after the target address, keep going */
|
||
|
bls find_next_page_boundary
|
||
|
write_enable:
|
||
|
/* Flush read/write fifo's */
|
||
|
bl flush_fifo
|
||
|
|
||
|
/* Instruction byte 1 */
|
||
|
movs r8, #0x1
|
||
|
str r8, [r5, #HDRCNT]
|
||
|
|
||
|
/* Set write enable instruction */
|
||
|
movs r8, #INS_WRITE_ENABLE
|
||
|
str r8, [r5, #INSTR]
|
||
|
|
||
|
movs r9, #0x1
|
||
|
bl start_tx
|
||
|
bl stop_tx
|
||
|
page_program:
|
||
|
/* Instruction byte 1, Addr byte 3 */
|
||
|
movs r8, #0x31
|
||
|
str r8, [r5, #HDRCNT]
|
||
|
/* Todo: set addr and data pin to single */
|
||
|
write_address:
|
||
|
mov r8, r2
|
||
|
str r8, [r5, #ADDR]
|
||
|
/* Set page program instruction */
|
||
|
movs r8, #INS_PAGE_PROGRAM
|
||
|
str r8, [r5, #INSTR]
|
||
|
/* Start write transfer */
|
||
|
movs r9, #0x1
|
||
|
bl start_tx
|
||
|
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 */
|
||
|
cmp r3, #0 /* Exit if we have written everything */
|
||
|
beq write_wait
|
||
|
add r2, #1 /* Increment flash address by 1 */
|
||
|
cmp r10, r2 /* See if we have reached the end of a page */
|
||
|
bne wait_fifo /* If not, keep writing bytes */
|
||
|
write_wait:
|
||
|
bl stop_tx /* Otherwise, end the command and keep going w/ the next page */
|
||
|
add r10, r4 /* Move up the end-of-page address by the page size*/
|
||
|
check_flash_busy: /* Wait for the flash to finish the previous page write */
|
||
|
/* Flush read/write fifo's */
|
||
|
bl flush_fifo
|
||
|
/* Instruction byte 1 */
|
||
|
movs r8, #0x1
|
||
|
str r8, [r5, #HDRCNT]
|
||
|
/* Continuous data in of status register */
|
||
|
movs r8, #0x0
|
||
|
str r8, [r5, #DINCNT]
|
||
|
/* Set write enable instruction */
|
||
|
movs r8, #INS_READ_STATUS
|
||
|
str r8, [r5, #INSTR]
|
||
|
/* Start read transfer */
|
||
|
movs r9, #0x0
|
||
|
bl start_tx
|
||
|
wait_flash_busy:
|
||
|
bl read_data
|
||
|
and.w r9, r9, #0x1
|
||
|
cmp r9, #0x0
|
||
|
bne.n wait_flash_busy
|
||
|
bl stop_tx
|
||
|
cmp r3, #0
|
||
|
bne.n write_enable /* If it is done, start a new page write */
|
||
|
b exit /* All data written, exit */
|
||
|
|
||
|
write_data: /* Send/receive 1 byte of data over QSPI */
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
lsls r8, r8, #24
|
||
|
bmi.n write_data
|
||
|
str r9, [r5, #DOUT]
|
||
|
bx lr
|
||
|
|
||
|
read_data: /* Read 1 byte of data over QSPI */
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
lsls r8, r8, #27
|
||
|
bmi.n read_data
|
||
|
ldr r9, [r5, #DIN]
|
||
|
bx lr
|
||
|
|
||
|
flush_fifo: /* Flush read write fifos */
|
||
|
ldr r8, [r5, #CONF]
|
||
|
orr.w r8, r8, #FIFO_FLUSH
|
||
|
str r8, [r5, #CONF]
|
||
|
flush_reset:
|
||
|
ldr r8, [r5, #CONF]
|
||
|
lsls r8, r8, #22
|
||
|
bmi.n flush_reset
|
||
|
bx lr
|
||
|
|
||
|
start_tx:
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
orr.w r8, r8, #SS_EN
|
||
|
str r8, [r5, #CNTL]
|
||
|
xfer_rdy:
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
lsls r8, r8, #30
|
||
|
bpl.n xfer_rdy
|
||
|
ldr r8, [r5, #CONF]
|
||
|
bfi r8, r9, #13, #1
|
||
|
orr.w r8, r8, #XFER_START
|
||
|
str r8, [r5, #CONF]
|
||
|
bx lr
|
||
|
|
||
|
stop_tx:
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
lsls r8, r8, #30
|
||
|
bpl.n stop_tx
|
||
|
wfifo_wait:
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
lsls r8, r8, #25
|
||
|
bpl.n wfifo_wait
|
||
|
ldr r8, [r5, #CONF]
|
||
|
orr.w r8, r8, #XFER_STOP
|
||
|
str r8, [r5, #CONF]
|
||
|
xfer_start:
|
||
|
ldr r8, [r5, #CONF]
|
||
|
lsls r8, r8, #16
|
||
|
bmi.n xfer_start
|
||
|
ss_disable:
|
||
|
# Disable SS_EN
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
bic.w r8, r8, #SS_EN
|
||
|
str r8, [r5, #CNTL]
|
||
|
wait:
|
||
|
ldr r8, [r5, #CNTL]
|
||
|
lsls r8, r8, #30
|
||
|
bpl.n wait
|
||
|
bx lr
|
||
|
|
||
|
error:
|
||
|
movs r0, #0
|
||
|
str r0, [r2, #4] /* set rp = 0 on error */
|
||
|
exit:
|
||
|
mov r0, r6
|
||
|
bkpt #0x00
|
||
|
|
||
|
.end
|