1/***************************************************************************
2 *   Copyright (C) 2012 by George Harris                                   *
3 *   george@luminairecoffee.com                                            *
4 *                                                                         *
5 *   This program is free software; you can redistribute it and/or modify  *
6 *   it under the terms of the GNU General Public License as published by  *
7 *   the Free Software Foundation; either version 2 of the License, or     *
8 *   (at your option) any later version.                                   *
9 *                                                                         *
10 *   This program is distributed in the hope that it will be useful,       *
11 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13 *   GNU General Public License for more details.                          *
14 *                                                                         *
15 *   You should have received a copy of the GNU General Public License     *
16 *   along with this program; if not, write to the                         *
17 *   Free Software Foundation, Inc.,                                       *
18 *   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.           *
19 ***************************************************************************/
20
21	.text
22	.syntax unified
23	.cpu cortex-m3
24	.thumb
25	.thumb_func
26
27/*
28 * Params :
29 * r0 = workarea start, status (out)
30 * r1 = workarea end
31 * r2 = target address (offset from flash base)
32 * r3 = count (bytes)
33 * r4 = page size
34 * Clobbered:
35 * r7 - rp
36 * r8 - wp, tmp
37 * r9 - send/receive data
38 * r10 - temp
39 * r11 - current page end address
40 */
41
42/*
43 * This code is embedded within: src/flash/nor/lpcspifi.c as a "C" array.
44 *
45 * To rebuild:
46 *   arm-none-eabi-gcc -c lpcspifi_write.S
47 *   arm-none-eabi-objcopy -O binary lpcspifi_write.o lpcspifi_write.bin
48 *   xxd -c 8 -i lpcspifi_write.bin > lpcspifi_write.txt
49 *
50 * Then read and edit this result into the "C" source.
51 */
52
53#define SSP_BASE_HIGH				0x4008
54#define SSP_BASE_LOW				0x3000
55#define SSP_CR0_OFFSET				0x00
56#define SSP_CR1_OFFSET				0x04
57#define SSP_DATA_OFFSET 			0x08
58#define SSP_CPSR_OFFSET 			0x10
59#define SSP_SR_OFFSET				0x0c
60
61#define SSP_CLOCK_BASE_HIGH 		0x4005
62#define SSP_CLOCK_BASE_LOW 			0x0000
63#define SSP_BRANCH_CLOCK_BASE_HIGH 	0x4005
64#define SSP_BRANCH_CLOCK_BASE_LOW	0x2000
65#define SSP_BASE_CLOCK_OFFSET		0x94
66#define SSP_BRANCH_CLOCK_OFFSET		0x700
67
68#define IOCONFIG_BASE_HIGH			0x4008
69#define IOCONFIG_BASE_LOW			0x6000
70#define IOCONFIG_SCK_OFFSET			0x18c
71#define IOCONFIG_HOLD_OFFSET		0x190
72#define IOCONFIG_WP_OFFSET			0x194
73#define IOCONFIG_MISO_OFFSET		0x198
74#define IOCONFIG_MOSI_OFFSET		0x19c
75#define IOCONFIG_CS_OFFSET			0x1a0
76
77#define IO_BASE_HIGH 				0x400f
78#define IO_BASE_LOW 				0x4000
79#define IO_CS_OFFSET 				0xab
80#define IODIR_BASE_HIGH 			0x400f
81#define IODIR_BASE_LOW				0x6000
82#define IO_CS_DIR_OFFSET 			0x14
83
84
85setup: /* Initialize SSP pins and module */
86	mov.w	r10, #IOCONFIG_BASE_LOW
87	movt	r10, #IOCONFIG_BASE_HIGH
88	mov.w	r8, #0xea
89	str.w	r8, [r10, #IOCONFIG_SCK_OFFSET]		/* Configure SCK pin function */
90	mov.w	r8, #0x40
91	str.w	r8, [r10, #IOCONFIG_HOLD_OFFSET]	/* Configure /HOLD pin function */
92	mov.w	r8, #0x40
93	str.w	r8, [r10, #IOCONFIG_WP_OFFSET]		/* Configure /WP pin function */
94	mov.w	r8, #0xed
95	str.w	r8, [r10, #IOCONFIG_MISO_OFFSET]	/* Configure MISO pin function */
96	mov.w	r8, #0xed
97	str.w	r8, [r10, #IOCONFIG_MOSI_OFFSET]	/* Configure MOSI pin function */
98	mov.w	r8, #0x44
99	str.w	r8, [r10, #IOCONFIG_CS_OFFSET]		/* Configure CS pin function */
100
101	mov.w	r10, #IODIR_BASE_LOW
102	movt	r10, #IODIR_BASE_HIGH
103	mov.w	r8, #0x800
104	str 	r8, [r10, #IO_CS_DIR_OFFSET]		/* Set CS as output */
105	mov.w	r10, #IO_BASE_LOW
106	movt	r10, #IO_BASE_HIGH
107	mov.w	r8, #0xff
108	str.w	r8, [r10, #IO_CS_OFFSET]			/* Set CS high */
109
110	mov.w 	r10, #SSP_CLOCK_BASE_LOW
111	movt 	r10, #SSP_CLOCK_BASE_HIGH
112	mov.w 	r8, #0x0000
113	movt 	r8, #0x0100
114	str.w 	r8, [r10, #SSP_BASE_CLOCK_OFFSET] 	/* Configure SSP0 base clock (use 12 MHz IRC) */
115
116	mov.w 	r10, #SSP_BRANCH_CLOCK_BASE_LOW
117	movt 	r10, #SSP_BRANCH_CLOCK_BASE_HIGH
118	mov.w 	r8, #0x01
119	str.w 	r8, [r10, #SSP_BRANCH_CLOCK_OFFSET] /* Configure (enable) SSP0 branch clock */
120
121	mov.w 	r10, #SSP_BASE_LOW
122	movt	r10, #SSP_BASE_HIGH
123	mov.w 	r8, #0x07
124	str.w 	r8, [r10, #SSP_CR0_OFFSET] 			/* Set clock postscale */
125	mov.w 	r8, #0x02
126	str.w 	r8, [r10, #SSP_CPSR_OFFSET] 		/* Set clock prescale */
127	str.w 	r8, [r10, #SSP_CR1_OFFSET] 			/* Enable SSP in SPI mode */
128
129	mov.w 	r11, #0x00
130find_next_page_boundary:
131	add 	r11, r4			/* Increment to the next page */
132	cmp 	r11, r2
133	/* If we have not reached the next page boundary after the target address, keep going */
134	bls 	find_next_page_boundary
135write_enable:
136	bl 		cs_down
137	mov.w 	r9, #0x06 		/* Send the write enable command */
138	bl 		write_data
139	bl 		cs_up
140
141	bl 		cs_down
142	mov.w 	r9, #0x05 		/* Get status register */
143	bl 		write_data
144	mov.w 	r9, #0x00 		/* Dummy data to clock in status */
145	bl 		write_data
146	bl 		cs_up
147
148	tst 	r9, #0x02 		/* If the WE bit isn't set, we have a problem. */
149	beq 	error
150page_program:
151	bl 		cs_down
152	mov.w 	r9, #0x02 		/* Send the page program command */
153	bl 		write_data
154write_address:
155	lsr 	r9, r2, #16 	/* Send the current 24-bit write address, MSB first */
156	bl 		write_data
157	lsr 	r9, r2, #8
158	bl 		write_data
159	mov.w 	r9, r2
160	bl 		write_data
161wait_fifo:
162	ldr 	r8, [r0]  		/* read the write pointer */
163	cmp 	r8, #0 			/* if it's zero, we're gonzo */
164	beq 	exit
165	ldr 	r7, [r0, #4] 	/* read the read pointer */
166	cmp 	r7, r8 			/* wait until they are not equal */
167	beq 	wait_fifo
168write:
169	ldrb 	r9, [r7], #0x01 /* Load one byte from the FIFO, increment the read pointer by 1 */
170	bl 		write_data 		/* send the byte to the flash chip */
171
172	cmp 	r7, r1			/* wrap the read pointer if it is at the end */
173	it  	cs
174	addcs	r7, r0, #8		/* skip loader args */
175	str 	r7, [r0, #4]	/* store the new read pointer */
176	subs	r3, r3, #1		/* decrement count */
177	cbz		r3, exit 		/* Exit if we have written everything */
178
179	add 	r2, #1 			/* Increment flash address by 1 */
180	cmp 	r11, r2   		/* See if we have reached the end of a page */
181	bne 	wait_fifo 		/* If not, keep writing bytes */
182	bl 		cs_up			/* Otherwise, end the command and keep going w/ the next page */
183	add 	r11, r4 		/* Move up the end-of-page address by the page size*/
184wait_flash_busy:			/* Wait for the flash to finish the previous page write */
185	bl 		cs_down
186	mov.w 	r9, #0x05 					/* Get status register */
187	bl 		write_data
188	mov.w 	r9, #0x00 					/* Dummy data to clock in status */
189	bl 		write_data
190	bl 		cs_up
191	tst 	r9, #0x01 					/* If it isn't done, keep waiting */
192	bne 	wait_flash_busy
193	b 		write_enable 				/* If it is done, start a new page write */
194write_data: 							/* Send/receive 1 byte of data over SSP */
195	mov.w	r10, #SSP_BASE_LOW
196	movt	r10, #SSP_BASE_HIGH
197	str.w 	r9, [r10, #SSP_DATA_OFFSET]	/* Write supplied data to the SSP data reg */
198wait_transmit:
199	ldr 	r9, [r10, #SSP_SR_OFFSET] 	/* Check SSP status */
200	tst 	r9, #0x0010					/* Check if BSY bit is set */
201	bne 	wait_transmit 				/* If still transmitting, keep waiting */
202	ldr 	r9, [r10, #SSP_DATA_OFFSET]	/* Load received data */
203	bx 		lr 							/* Exit subroutine */
204cs_up:
205	mov.w 	r8, #0xff
206	b 		cs_write
207cs_down:
208	mov.w 	r8, #0x0000
209cs_write:
210	mov.w 	r10, #IO_BASE_LOW
211	movt	r10, #IO_BASE_HIGH
212	str.w 	r8, [r10, #IO_CS_OFFSET]
213	bx 		lr
214error:
215	movs	r0, #0
216	str 	r0, [r2, #4]	/* set rp = 0 on error */
217exit:
218	bl 		cs_up			/* end the command before returning */
219	mov 	r0, r6
220	bkpt 	#0x00
221
222	.end
223