1 /* SPDX-License-Identifier: GPL-2.0-or-later */
3 /***************************************************************************
4 * Copyright (C) 2012 by George Harris *
5 * george@luminairecoffee.com *
6 ***************************************************************************/
16 * r0 = start address, status (out)
22 #define SSP_BASE_HIGH 0x4008
23 #define SSP_BASE_LOW 0x3000
24 #define SSP_CR0_OFFSET 0x00
25 #define SSP_CR1_OFFSET 0x04
26 #define SSP_DATA_OFFSET 0x08
27 #define SSP_CPSR_OFFSET 0x10
28 #define SSP_SR_OFFSET 0x0c
30 #define SSP_CLOCK_BASE_HIGH 0x4005
31 #define SSP_CLOCK_BASE_LOW 0x0000
32 #define SSP_BRANCH_CLOCK_BASE_HIGH 0x4005
33 #define SSP_BRANCH_CLOCK_BASE_LOW 0x2000
34 #define SSP_BASE_CLOCK_OFFSET 0x94
35 #define SSP_BRANCH_CLOCK_OFFSET 0x700
37 #define IOCONFIG_BASE_HIGH 0x4008
38 #define IOCONFIG_BASE_LOW 0x6000
39 #define IOCONFIG_SCK_OFFSET 0x18c
40 #define IOCONFIG_HOLD_OFFSET 0x190
41 #define IOCONFIG_WP_OFFSET 0x194
42 #define IOCONFIG_MISO_OFFSET 0x198
43 #define IOCONFIG_MOSI_OFFSET 0x19c
44 #define IOCONFIG_CS_OFFSET 0x1a0
46 #define IO_BASE_HIGH 0x400f
47 #define IO_BASE_LOW 0x4000
48 #define IO_CS_OFFSET 0xab
49 #define IODIR_BASE_HIGH 0x400f
50 #define IODIR_BASE_LOW 0x6000
51 #define IO_CS_DIR_OFFSET 0x14
54 setup: /* Initialize SSP pins and module */
55 mov.w r10, #IOCONFIG_BASE_LOW
56 movt r10, #IOCONFIG_BASE_HIGH
58 str.w r8, [r10, #IOCONFIG_SCK_OFFSET] /* Configure SCK pin function */
60 str.w r8, [r10, #IOCONFIG_HOLD_OFFSET] /* Configure /HOLD pin function */
62 str.w r8, [r10, #IOCONFIG_WP_OFFSET] /* Configure /WP pin function */
64 str.w r8, [r10, #IOCONFIG_MISO_OFFSET] /* Configure MISO pin function */
66 str.w r8, [r10, #IOCONFIG_MOSI_OFFSET] /* Configure MOSI pin function */
68 str.w r8, [r10, #IOCONFIG_CS_OFFSET] /* Configure CS pin function */
70 mov.w r10, #IODIR_BASE_LOW
71 movt r10, #IODIR_BASE_HIGH
73 str r8, [r10, #IO_CS_DIR_OFFSET] /* Set CS as output */
74 mov.w r10, #IO_BASE_LOW
75 movt r10, #IO_BASE_HIGH
77 str.w r8, [r10, #IO_CS_OFFSET] /* Set CS high */
79 mov.w r10, #SSP_CLOCK_BASE_LOW
80 movt r10, #SSP_CLOCK_BASE_HIGH
83 str.w r8, [r10, #SSP_BASE_CLOCK_OFFSET] /* Configure SSP0 base clock (use 12 MHz IRC) */
85 mov.w r10, #SSP_BRANCH_CLOCK_BASE_LOW
86 movt r10, #SSP_BRANCH_CLOCK_BASE_HIGH
88 str.w r8, [r10, #SSP_BRANCH_CLOCK_OFFSET] /* Configure (enable) SSP0 branch clock */
90 mov.w r10, #SSP_BASE_LOW
91 movt r10, #SSP_BASE_HIGH
93 str.w r8, [r10, #SSP_CR0_OFFSET] /* Set clock postscale */
95 str.w r8, [r10, #SSP_CPSR_OFFSET] /* Set clock prescale */
96 str.w r8, [r10, #SSP_CR1_OFFSET] /* Enable SSP in SPI mode */
99 mov.w r9, #0x06 /* Send the write enable command */
104 mov.w r9, #0x05 /* Get status register */
106 mov.w r9, #0x00 /* Dummy data to clock in status */
110 tst r9, #0x02 /* If the WE bit isn't set, we have a problem. */
114 mov.w r9, r2 /* Send the erase command */
117 lsr r9, r0, #16 /* Send the current 24-bit write address, MSB first */
124 wait_flash_busy: /* Wait for the flash to finish the previous erase */
126 mov.w r9, #0x05 /* Get status register */
128 mov.w r9, #0x00 /* Dummy data to clock in status */
131 tst r9, #0x01 /* If it isn't done, keep waiting */
134 subs r1, r1, #1 /* decrement count */
135 cbz r1, exit /* Exit if we have written everything */
136 add r0, r3 /* Move the address up by the block size */
137 b write_enable /* Start a new block erase */
138 write_data: /* Send/receive 1 byte of data over SSP */
139 mov.w r10, #SSP_BASE_LOW
140 movt r10, #SSP_BASE_HIGH
141 str.w r9, [r10, #SSP_DATA_OFFSET] /* Write supplied data to the SSP data reg */
143 ldr r9, [r10, #SSP_SR_OFFSET] /* Check SSP status */
144 tst r9, #0x0010 /* Check if BSY bit is set */
145 bne wait_transmit /* If still transmitting, keep waiting */
146 ldr r9, [r10, #SSP_DATA_OFFSET] /* Load received data */
147 bx lr /* Exit subroutine */
154 mov.w r10, #IO_BASE_LOW
155 movt r10, #IO_BASE_HIGH
156 str.w r8, [r10, #IO_CS_OFFSET]