/*************************************************************************** * 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 = start address, status (out) * r1 = count * r2 = erase command * r3 = block size */ #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 */ 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 erase: bl cs_down mov.w r9, r2 /* Send the erase command */ bl write_data write_address: lsr r9, r0, #16 /* Send the current 24-bit write address, MSB first */ bl write_data lsr r9, r0, #8 bl write_data mov.w r9, r0 bl write_data bl cs_up wait_flash_busy: /* Wait for the flash to finish the previous erase */ 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 subs r1, r1, #1 /* decrement count */ cbz r1, exit /* Exit if we have written everything */ add r0, r3 /* Move the address up by the block size */ b write_enable /* Start a new block erase */ 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 exit: bkpt #0x00 .end