| /****************************************************************************** |
| * |
| * Filename: spi_flash.c |
| * |
| * Instantiation of SPI flash control routines supporting AT45DB161B |
| * |
| * Revision information: |
| * |
| * 17JAN2005 kb_admin initial creation |
| * adapted from external sources |
| * tested for basic operation only!!! |
| * |
| * BEGIN_KBDD_BLOCK |
| * No warranty, expressed or implied, is included with this software. It is |
| * provided "AS IS" and no warranty of any kind including statutory or aspects |
| * relating to merchantability or fitness for any purpose is provided. All |
| * intellectual property rights of others is maintained with the respective |
| * owners. This software is not copyrighted and is intended for reference |
| * only. |
| * END_BLOCK |
| * |
| * $FreeBSD$ |
| *****************************************************************************/ |
| |
| #include "at91rm9200.h" |
| #include "spi_flash.h" |
| #include "lib.h" |
| |
| /*********************** PRIVATE FUNCTIONS/DATA ******************************/ |
| |
| |
| static spiCommand_t spi_command; |
| static char tx_commandBuffer[8], rx_commandBuffer[8]; |
| |
| /* |
| * .KB_C_FN_DEFINITION_START |
| * void SendCommand(spiCommand_t *pCommand) |
| * Private function sends 8-bit value to the device and returns the 8-bit |
| * value in response. |
| * .KB_C_FN_DEFINITION_END |
| */ |
| static void |
| SendCommand(spiCommand_t *pCommand) |
| { |
| AT91PS_SPI pSPI = AT91C_BASE_SPI; |
| |
| pSPI->SPI_PTCR = AT91C_PDC_TXTDIS | AT91C_PDC_RXTDIS; |
| |
| pSPI->SPI_RPR = (unsigned)pCommand->rx_cmd; |
| pSPI->SPI_RCR = pCommand->rx_cmd_size; |
| pSPI->SPI_TPR = (unsigned)pCommand->tx_cmd; |
| pSPI->SPI_TCR = pCommand->tx_cmd_size; |
| |
| pSPI->SPI_TNPR = (unsigned)pCommand->tx_data; |
| pSPI->SPI_TNCR = pCommand->tx_data_size; |
| pSPI->SPI_RNPR = (unsigned)pCommand->rx_data; |
| pSPI->SPI_RNCR = pCommand->rx_data_size; |
| |
| pSPI->SPI_PTCR = AT91C_PDC_TXTEN | AT91C_PDC_RXTEN; |
| |
| // wait for completion |
| while (!(pSPI->SPI_SR & AT91C_SPI_SPENDRX)) |
| Delay(700); |
| } |
| |
| |
| /* |
| * .KB_C_FN_DEFINITION_START |
| * char GetFlashStatus(void) |
| * Private function to return device status. |
| * .KB_C_FN_DEFINITION_END |
| */ |
| static char |
| GetFlashStatus(void) |
| { |
| p_memset((char *)&spi_command, 0, sizeof(spi_command)); |
| p_memset(tx_commandBuffer, 0, 8); |
| tx_commandBuffer[0] = STATUS_REGISTER_READ; |
| p_memset(rx_commandBuffer, 0, 8); |
| spi_command.tx_cmd = tx_commandBuffer; |
| spi_command.rx_cmd = rx_commandBuffer; |
| spi_command.rx_cmd_size = 2; |
| spi_command.tx_cmd_size = 2; |
| SendCommand(&spi_command); |
| return (rx_commandBuffer[1]); |
| } |
| |
| /* |
| * .KB_C_FN_DEFINITION_START |
| * void WaitForDeviceReady(void) |
| * Private function to poll until the device is ready for next operation. |
| * .KB_C_FN_DEFINITION_END |
| */ |
| static void |
| WaitForDeviceReady(void) |
| { |
| while (!(GetFlashStatus() & 0x80)) ; |
| } |
| |
| /*************************** GLOBAL FUNCTIONS ********************************/ |
| |
| |
| /* |
| * .KB_C_FN_DEFINITION_START |
| * void SPI_ReadFlash(unsigned flash_addr, unsigned dest_addr, unsigned size) |
| * Global function to read the SPI flash device using the continuous read |
| * array command. |
| * .KB_C_FN_DEFINITION_END |
| */ |
| void |
| SPI_ReadFlash(unsigned flash_addr, char *dest_addr, unsigned size) |
| { |
| unsigned pageAddress, byteAddress; |
| |
| // determine page address |
| pageAddress = flash_addr / FLASH_PAGE_SIZE; |
| |
| // determine byte address |
| byteAddress = flash_addr % FLASH_PAGE_SIZE; |
| |
| p_memset(tx_commandBuffer, 0, 8); |
| #ifdef BOOT_BWCT |
| tx_commandBuffer[0] = 0xd2; |
| tx_commandBuffer[1] = ((pageAddress >> 6) & 0xFF); |
| tx_commandBuffer[2] = ((pageAddress << 2) & 0xFC) | |
| ((byteAddress >> 8) & 0x3); |
| tx_commandBuffer[3] = byteAddress & 0xFF; |
| spi_command.tx_cmd = tx_commandBuffer; |
| spi_command.tx_cmd_size = 8; |
| spi_command.tx_data_size = size; |
| spi_command.tx_data = dest_addr; |
| |
| p_memset(rx_commandBuffer, 0, 8); |
| spi_command.rx_cmd = rx_commandBuffer; |
| spi_command.rx_cmd_size = 8; |
| spi_command.rx_data_size = size; |
| spi_command.rx_data = dest_addr; |
| #else |
| tx_commandBuffer[0] = CONTINUOUS_ARRAY_READ_HF; |
| tx_commandBuffer[1] = ((pageAddress >> 5) & 0xFF); |
| tx_commandBuffer[2] = ((pageAddress << 3) & 0xF8) | |
| ((byteAddress >> 8) & 0x7); |
| tx_commandBuffer[3] = byteAddress & 0xFF; |
| spi_command.tx_cmd = tx_commandBuffer; |
| spi_command.tx_cmd_size = 5; |
| spi_command.tx_data_size = size; |
| spi_command.tx_data = dest_addr; |
| |
| p_memset(rx_commandBuffer, 0, 8); |
| spi_command.rx_cmd = rx_commandBuffer; |
| spi_command.rx_cmd_size = 5; |
| spi_command.rx_data_size = size; |
| spi_command.rx_data = dest_addr; |
| #endif |
| |
| SendCommand(&spi_command); |
| } |
| |
| |
| /* |
| * .KB_C_FN_DEFINITION_START |
| * void SPI_WriteFlash(unsigned flash_addr, unsigned src_addr, unsigned size) |
| * Global function to program the SPI flash device. Notice the warning |
| * provided in lower-level functions regarding corruption of data in non- |
| * page aligned write operations. |
| * .KB_C_FN_DEFINITION_END |
| */ |
| void |
| SPI_WriteFlash(unsigned flash_addr, char *src_addr, unsigned size) |
| { |
| unsigned pageAddress, byteAddress; |
| |
| // determine page address |
| pageAddress = flash_addr / FLASH_PAGE_SIZE; |
| |
| // determine byte address |
| byteAddress = flash_addr % FLASH_PAGE_SIZE; |
| |
| p_memset(tx_commandBuffer, 0, 8); |
| #ifdef BOOT_BWCT |
| tx_commandBuffer[0] = 0x82; |
| tx_commandBuffer[1] = ((pageAddress >> 6) & 0xFF); |
| tx_commandBuffer[2] = ((pageAddress << 2) & 0xFC) | |
| ((byteAddress >> 8) & 0x3); |
| tx_commandBuffer[3] = (byteAddress & 0xFF); |
| #else |
| tx_commandBuffer[0] = PROGRAM_THROUGH_BUFFER; |
| tx_commandBuffer[1] = ((pageAddress >> 5) & 0xFF); |
| tx_commandBuffer[2] = ((pageAddress << 3) & 0xF8) | |
| ((byteAddress >> 8) & 0x7); |
| tx_commandBuffer[3] = (byteAddress & 0xFF); |
| #endif |
| |
| p_memset(rx_commandBuffer, 0, 8); |
| |
| spi_command.tx_cmd = tx_commandBuffer; |
| spi_command.rx_cmd = rx_commandBuffer; |
| spi_command.rx_cmd_size = 4; |
| spi_command.tx_cmd_size = 4; |
| |
| spi_command.tx_data_size = size; |
| spi_command.tx_data = src_addr; |
| spi_command.rx_data_size = size; |
| spi_command.rx_data = src_addr; |
| |
| SendCommand(&spi_command); |
| |
| WaitForDeviceReady(); |
| } |
| |
| /* |
| * .KB_C_FN_DEFINITION_START |
| * void SPI_InitFlash(void) |
| * Global function to initialize the SPI flash device/accessor functions. |
| * .KB_C_FN_DEFINITION_END |
| */ |
| void |
| SPI_InitFlash(void) |
| { |
| AT91PS_PIO pPio; |
| AT91PS_SPI pSPI = AT91C_BASE_SPI; |
| unsigned value; |
| |
| // enable CS0, CLK, MOSI, MISO |
| pPio = (AT91PS_PIO)AT91C_BASE_PIOA; |
| pPio->PIO_ASR = AT91C_PIO_PA3 | AT91C_PIO_PA1 | AT91C_PIO_PA0 | |
| AT91C_PIO_PA2; |
| pPio->PIO_PDR = AT91C_PIO_PA3 | AT91C_PIO_PA1 | AT91C_PIO_PA0 | |
| AT91C_PIO_PA2; |
| |
| // enable clocks to SPI |
| AT91C_BASE_PMC->PMC_PCER = 1u << AT91C_ID_SPI; |
| |
| // reset the SPI |
| pSPI->SPI_CR = AT91C_SPI_SWRST; |
| |
| pSPI->SPI_MR = (0xf << 24) | AT91C_SPI_MSTR | AT91C_SPI_MODFDIS | |
| (0xE << 16); |
| |
| pSPI->SPI_CSR[0] = AT91C_SPI_CPOL | (4 << 16) | (2 << 8); |
| pSPI->SPI_CR = AT91C_SPI_SPIEN; |
| |
| pSPI->SPI_PTCR = AT91C_PDC_TXTDIS; |
| pSPI->SPI_PTCR = AT91C_PDC_RXTDIS; |
| pSPI->SPI_RNPR = 0; |
| pSPI->SPI_RNCR = 0; |
| pSPI->SPI_TNPR = 0; |
| pSPI->SPI_TNCR = 0; |
| pSPI->SPI_RPR = 0; |
| pSPI->SPI_RCR = 0; |
| pSPI->SPI_TPR = 0; |
| pSPI->SPI_TCR = 0; |
| pSPI->SPI_PTCR = AT91C_PDC_RXTEN; |
| pSPI->SPI_PTCR = AT91C_PDC_TXTEN; |
| |
| value = pSPI->SPI_RDR; |
| value = pSPI->SPI_SR; |
| |
| value = GetFlashStatus() & 0xFC; |
| #ifdef BOOT_BWCT |
| if (value != 0xB4 && value != 0xAC) |
| printf(" Bad SPI status: 0x%x\n", value); |
| #else |
| if (value != 0xBC) |
| printf(" Bad SPI status: 0x%x\n", value); |
| #endif |
| } |