xref: /titanic_52/usr/src/boot/sys/boot/arm/at91/libat91/spi_flash.c (revision 4a5d661a82b942b6538acd26209d959ce98b593a)
1 /******************************************************************************
2  *
3  * Filename: spi_flash.c
4  *
5  * Instantiation of SPI flash control routines supporting AT45DB161B
6  *
7  * Revision information:
8  *
9  * 17JAN2005	kb_admin	initial creation
10  *				adapted from external sources
11  *				tested for basic operation only!!!
12  *
13  * BEGIN_KBDD_BLOCK
14  * No warranty, expressed or implied, is included with this software.  It is
15  * provided "AS IS" and no warranty of any kind including statutory or aspects
16  * relating to merchantability or fitness for any purpose is provided.  All
17  * intellectual property rights of others is maintained with the respective
18  * owners.  This software is not copyrighted and is intended for reference
19  * only.
20  * END_BLOCK
21  *
22  * $FreeBSD$
23  *****************************************************************************/
24 
25 #include "at91rm9200.h"
26 #include "spi_flash.h"
27 #include "lib.h"
28 
29 /*********************** PRIVATE FUNCTIONS/DATA ******************************/
30 
31 
32 static spiCommand_t	spi_command;
33 static char		tx_commandBuffer[8], rx_commandBuffer[8];
34 
35 /*
36  * .KB_C_FN_DEFINITION_START
37  * void SendCommand(spiCommand_t *pCommand)
38  *  Private function sends 8-bit value to the device and returns the 8-bit
39  * value in response.
40  * .KB_C_FN_DEFINITION_END
41  */
42 static void
43 SendCommand(spiCommand_t *pCommand)
44 {
45 	AT91PS_SPI	pSPI = AT91C_BASE_SPI;
46 
47 	pSPI->SPI_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS;
48 
49 	pSPI->SPI_RPR = (unsigned)pCommand->rx_cmd;
50 	pSPI->SPI_RCR = pCommand->rx_cmd_size;
51 	pSPI->SPI_TPR = (unsigned)pCommand->tx_cmd;
52 	pSPI->SPI_TCR = pCommand->tx_cmd_size;
53 
54 	pSPI->SPI_TNPR = (unsigned)pCommand->tx_data;
55 	pSPI->SPI_TNCR = pCommand->tx_data_size;
56 	pSPI->SPI_RNPR = (unsigned)pCommand->rx_data;
57 	pSPI->SPI_RNCR = pCommand->rx_data_size;
58 
59 	pSPI->SPI_PTCR = AT91C_PDC_TXTEN | AT91C_PDC_RXTEN;
60 
61 	// wait for completion
62 	while (!(pSPI->SPI_SR & AT91C_SPI_SPENDRX))
63 		Delay(700);
64 }
65 
66 
67 /*
68  * .KB_C_FN_DEFINITION_START
69  * char GetFlashStatus(void)
70  *  Private function to return device status.
71  * .KB_C_FN_DEFINITION_END
72  */
73 static char
74 GetFlashStatus(void)
75 {
76 	p_memset((char *)&spi_command, 0, sizeof(spi_command));
77 	p_memset(tx_commandBuffer, 0, 8);
78 	tx_commandBuffer[0] = STATUS_REGISTER_READ;
79 	p_memset(rx_commandBuffer, 0, 8);
80 	spi_command.tx_cmd = tx_commandBuffer;
81 	spi_command.rx_cmd = rx_commandBuffer;
82 	spi_command.rx_cmd_size = 2;
83 	spi_command.tx_cmd_size = 2;
84 	SendCommand(&spi_command);
85 	return (rx_commandBuffer[1]);
86 }
87 
88 /*
89  * .KB_C_FN_DEFINITION_START
90  * void WaitForDeviceReady(void)
91  *  Private function to poll until the device is ready for next operation.
92  * .KB_C_FN_DEFINITION_END
93  */
94 static void
95 WaitForDeviceReady(void)
96 {
97 	while (!(GetFlashStatus() & 0x80)) ;
98 }
99 
100 /*************************** GLOBAL FUNCTIONS ********************************/
101 
102 
103 /*
104  * .KB_C_FN_DEFINITION_START
105  * void SPI_ReadFlash(unsigned flash_addr, unsigned dest_addr, unsigned size)
106  *  Global function to read the SPI flash device using the continuous read
107  * array command.
108  * .KB_C_FN_DEFINITION_END
109  */
110 void
111 SPI_ReadFlash(unsigned flash_addr, char *dest_addr, unsigned size)
112 {
113 	unsigned	pageAddress, byteAddress;
114 
115 	// determine page address
116 	pageAddress = flash_addr / FLASH_PAGE_SIZE;
117 
118 	// determine byte address
119 	byteAddress = flash_addr % FLASH_PAGE_SIZE;
120 
121 	p_memset(tx_commandBuffer, 0, 8);
122 #ifdef BOOT_BWCT
123 	tx_commandBuffer[0] = 0xd2;
124 	tx_commandBuffer[1] = ((pageAddress >> 6) & 0xFF);
125 	tx_commandBuffer[2] = ((pageAddress << 2) & 0xFC) |
126 				((byteAddress >> 8) & 0x3);
127 	tx_commandBuffer[3] = byteAddress & 0xFF;
128 	spi_command.tx_cmd = tx_commandBuffer;
129 	spi_command.tx_cmd_size = 8;
130 	spi_command.tx_data_size = size;
131 	spi_command.tx_data = dest_addr;
132 
133 	p_memset(rx_commandBuffer, 0, 8);
134 	spi_command.rx_cmd = rx_commandBuffer;
135 	spi_command.rx_cmd_size = 8;
136 	spi_command.rx_data_size = size;
137 	spi_command.rx_data = dest_addr;
138 #else
139 	tx_commandBuffer[0] = CONTINUOUS_ARRAY_READ_HF;
140 	tx_commandBuffer[1] = ((pageAddress >> 5) & 0xFF);
141 	tx_commandBuffer[2] = ((pageAddress << 3) & 0xF8) |
142 				((byteAddress >> 8) & 0x7);
143 	tx_commandBuffer[3] = byteAddress & 0xFF;
144 	spi_command.tx_cmd = tx_commandBuffer;
145 	spi_command.tx_cmd_size = 5;
146 	spi_command.tx_data_size = size;
147 	spi_command.tx_data = dest_addr;
148 
149 	p_memset(rx_commandBuffer, 0, 8);
150 	spi_command.rx_cmd = rx_commandBuffer;
151 	spi_command.rx_cmd_size = 5;
152 	spi_command.rx_data_size = size;
153 	spi_command.rx_data = dest_addr;
154 #endif
155 
156 	SendCommand(&spi_command);
157 }
158 
159 
160 /*
161  * .KB_C_FN_DEFINITION_START
162  * void SPI_WriteFlash(unsigned flash_addr, unsigned src_addr, unsigned size)
163  *  Global function to program the SPI flash device.  Notice the warning
164  * provided in lower-level functions regarding corruption of data in non-
165  * page aligned write operations.
166  * .KB_C_FN_DEFINITION_END
167  */
168 void
169 SPI_WriteFlash(unsigned flash_addr, char *src_addr, unsigned size)
170 {
171 	unsigned	pageAddress, byteAddress;
172 
173 	// determine page address
174 	pageAddress = flash_addr / FLASH_PAGE_SIZE;
175 
176 	// determine byte address
177 	byteAddress = flash_addr % FLASH_PAGE_SIZE;
178 
179 	p_memset(tx_commandBuffer, 0, 8);
180 #ifdef BOOT_BWCT
181 	tx_commandBuffer[0] = 0x82;
182 	tx_commandBuffer[1] = ((pageAddress >> 6) & 0xFF);
183 	tx_commandBuffer[2] = ((pageAddress << 2) & 0xFC) |
184 				((byteAddress >> 8) & 0x3);
185 	tx_commandBuffer[3] = (byteAddress & 0xFF);
186 #else
187 	tx_commandBuffer[0] = PROGRAM_THROUGH_BUFFER;
188 	tx_commandBuffer[1] = ((pageAddress >> 5) & 0xFF);
189 	tx_commandBuffer[2] = ((pageAddress << 3) & 0xF8) |
190 				((byteAddress >> 8) & 0x7);
191 	tx_commandBuffer[3] = (byteAddress & 0xFF);
192 #endif
193 
194 	p_memset(rx_commandBuffer, 0, 8);
195 
196 	spi_command.tx_cmd = tx_commandBuffer;
197 	spi_command.rx_cmd = rx_commandBuffer;
198 	spi_command.rx_cmd_size = 4;
199 	spi_command.tx_cmd_size = 4;
200 
201 	spi_command.tx_data_size = size;
202 	spi_command.tx_data = src_addr;
203 	spi_command.rx_data_size = size;
204 	spi_command.rx_data = src_addr;
205 
206 	SendCommand(&spi_command);
207 
208 	WaitForDeviceReady();
209 }
210 
211 /*
212  * .KB_C_FN_DEFINITION_START
213  * void SPI_InitFlash(void)
214  *  Global function to initialize the SPI flash device/accessor functions.
215  * .KB_C_FN_DEFINITION_END
216  */
217 void
218 SPI_InitFlash(void)
219 {
220 	AT91PS_PIO	pPio;
221 	AT91PS_SPI	pSPI = AT91C_BASE_SPI;
222 	unsigned	value;
223 
224 	// enable CS0, CLK, MOSI, MISO
225 	pPio = (AT91PS_PIO)AT91C_BASE_PIOA;
226 	pPio->PIO_ASR = AT91C_PIO_PA3 | AT91C_PIO_PA1 | AT91C_PIO_PA0 |
227 	    AT91C_PIO_PA2;
228 	pPio->PIO_PDR = AT91C_PIO_PA3 | AT91C_PIO_PA1 | AT91C_PIO_PA0 |
229 	    AT91C_PIO_PA2;
230 
231 	// enable clocks to SPI
232 	AT91C_BASE_PMC->PMC_PCER = 1u << AT91C_ID_SPI;
233 
234 	// reset the SPI
235 	pSPI->SPI_CR = AT91C_SPI_SWRST;
236 
237 	pSPI->SPI_MR = (0xf << 24) | AT91C_SPI_MSTR | AT91C_SPI_MODFDIS |
238 	    (0xE << 16);
239 
240 	pSPI->SPI_CSR[0] = AT91C_SPI_CPOL | (4 << 16) | (2 << 8);
241 	pSPI->SPI_CR = AT91C_SPI_SPIEN;
242 
243 	pSPI->SPI_PTCR = AT91C_PDC_TXTDIS;
244 	pSPI->SPI_PTCR = AT91C_PDC_RXTDIS;
245 	pSPI->SPI_RNPR = 0;
246 	pSPI->SPI_RNCR = 0;
247 	pSPI->SPI_TNPR = 0;
248 	pSPI->SPI_TNCR = 0;
249 	pSPI->SPI_RPR = 0;
250 	pSPI->SPI_RCR = 0;
251 	pSPI->SPI_TPR = 0;
252 	pSPI->SPI_TCR = 0;
253 	pSPI->SPI_PTCR = AT91C_PDC_RXTEN;
254 	pSPI->SPI_PTCR = AT91C_PDC_TXTEN;
255 
256 	value = pSPI->SPI_RDR;
257 	value = pSPI->SPI_SR;
258 
259 	value = GetFlashStatus() & 0xFC;
260 #ifdef BOOT_BWCT
261 	if (value != 0xB4 && value != 0xAC)
262 		printf(" Bad SPI status: 0x%x\n", value);
263 #else
264 	if (value != 0xBC)
265 		printf(" Bad SPI status: 0x%x\n", value);
266 #endif
267 }
268