initial commit
[freebsd-arm:freebsd-arm.git] / boot / arm / at91 / libat91 / spi_flash.c
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_PA3_NPCS0 | AT91C_PA1_MOSI | AT91C_PA0_MISO |
227             AT91C_PA2_SPCK;
228         pPio->PIO_PDR = AT91C_PA3_NPCS0 | AT91C_PA1_MOSI | AT91C_PA0_MISO |
229             AT91C_PA2_SPCK;
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 }