diff options
Diffstat (limited to 'backends/platform/ds/arm9/source/fat/io_nmmc.c')
-rw-r--r-- | backends/platform/ds/arm9/source/fat/io_nmmc.c | 716 |
1 files changed, 716 insertions, 0 deletions
diff --git a/backends/platform/ds/arm9/source/fat/io_nmmc.c b/backends/platform/ds/arm9/source/fat/io_nmmc.c new file mode 100644 index 0000000000..2e6b99de24 --- /dev/null +++ b/backends/platform/ds/arm9/source/fat/io_nmmc.c @@ -0,0 +1,716 @@ +/* + io_nmmc.c + + Hardware Routines for reading an SD or MMC card using + a Neoflash MK2 or MK3. + + Written by www.neoflash.com + + Submit bug reports for this device to the NeoFlash forums + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. + + 2006-02-09 - www.neoflash.com: + * First stable release + + 2006-02-13 - Chishm + * Added ReadMK2Config function + * Added read config test to init function so no unnecessary card commands are sent + * Changed data read and write functions to use multiple block commands +*/ + +#include "io_nmmc.h" + +#ifdef SUPPORT_NMMC + +#include <nds/card.h> + +int spi_freq = 3; + +#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5) +#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0)) +//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1)) +//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4) + +#define MMC_READ_MULTIPLE_BLOCK 18 +#define MMC_READ_BLOCK 17 +#define MMC_WRITE_MULTIPLE_BLOCK 25 +#define MMC_WRITE_BLOCK 24 +#define MMC_STOP_TRANSMISSION 12 +#define MMC_SET_BLOCKLEN 16 +#define MMC_SET_BLOCK_COUNT 23 +#define MMC_SEND_CSD 9 + +// SPI functions + +static inline void Neo_OpenSPI( u8 frequency ) +{ + CARD_CR1 = 0x0000A040 | frequency; +} + +static inline u8 Neo_SPI( u8 dataByte ) +{ + CARD_EEPDATA = dataByte; + while (CARD_CR1 & 0x80); // card busy + return CARD_EEPDATA; +} + +static inline void Neo_CloseSPI ( void ) +{ + CARD_CR1 = 0; +} + +static inline void Neo_MK2GameMode() { + Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port + Neo_SPI(0xF1); // Switch to game mode + Neo_CloseSPI(); // Disable DS Card's SPI port +} + +static inline void Neo_EnableEEPROM( bool enable ) { + Neo_OpenSPI(spi_freq); + if(enable) Neo_SPI(0x06); + else Neo_SPI(0x0E); + Neo_CloseSPI(); +} + +void Neo_WriteMK2Config(u8 config) { + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xFA); // Send mem conf write command + Neo_SPI(0x01); // Send high byte (0x01) + Neo_SPI(config); // Send low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); +} + +u8 Neo_ReadMK2Config(void) +{ + u8 config; + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xf8); // Send mem conf read command + Neo_SPI(0x01); // Send high byte + config = Neo_SPI(0x00); // Get low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); + return config; +} + +// Low level functions + +u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3}; + +void Neo_SelectMMC (u8 dataByte) +{ + selectMMC_command[1] = dataByte; // Set enable / disable byte + cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card" + CARD_CR2 = CARD_ACTIVATE | CARD_nRESET; + while (CARD_CR2 & CARD_BUSY); + return; +} + +void Neo_EnableMMC( bool enable ) +{ + if ( enable == false) { + Neo_CloseSPI (); + Neo_SelectMMC (0); + Neo_SelectMMC (0); + } else { + Neo_SelectMMC (1); + Neo_SelectMMC (1); + Neo_OpenSPI (spi_freq); + } + return; +} + +void Neo_SendMMCCommand( u8 command, u32 argument ) +{ + Neo_SPI (0xFF); + Neo_SPI (command | 0x40); + Neo_SPI ((argument >> 24) & 0xff); + Neo_SPI ((argument >> 16) & 0xff); + Neo_SPI ((argument >> 8) & 0xff) ; + Neo_SPI (argument & 0xff); + Neo_SPI (0x95); + Neo_SPI (0xFF); + return; +} + +bool Neo_CheckMMCResponse( u8 response, u8 mask ) { + u32 i; + for(i=0;i<256;i++) { + if( ( Neo_SPI( 0xFF ) & mask ) == response ) + return true; + } + return false; +} + +// Neo MMC functions + +bool Neo_InitMMC() { + Neo_MK2GameMode(); + Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE); + + // Make sure the configuration was accepted + if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) { + return false; // If not, then it wasn't initialised properly + } + + return true; +} + +// Neo MMC driver functions + +bool NMMC_IsInserted(void) { + int i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + + // consume data from card, and send clocks. + for (i = 0; i < 28; i++) { + Neo_SPI(0xff); + } + + return true; +} + +bool NMMC_ClearStatus (void) { + u32 i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + for (i = 0; i < 10; i++) { + Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card + } + Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command + if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error) + Neo_EnableMMC( false ); + return false; + } + for(i=0;i<256;i++) { + Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND + if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; // Card is now idle + } + } + Neo_EnableMMC( false ); + return false; +} + +bool NMMC_Shutdown(void) { + return NMMC_ClearStatus(); +} + +bool NMMC_StartUp(void) { + int i; + int transSpeed; + if (Neo_InitMMC() == false) { + return false; + } + if (NMMC_ClearStatus() == false) { + return false; + } + Neo_EnableMMC( true ); // Open SPI port to MMC card + + // Set block length + Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + // Check if we can use a higher SPI frequency + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for (i = 0; i < 3; i++) { + Neo_SPI(0xFF); + } + transSpeed = Neo_SPI (0xFF); + for (i = 0; i < 24; i++) { + Neo_SPI(0xFF); + } + if ((transSpeed & 0xf0) >= 0x30) { + spi_freq = 0; + } + + Neo_EnableMMC( false ); + return true; +} + + +bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand( 25, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + while (totalSecs--) { + Neo_SPI( 0xFC ); // Send Start Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data + Neo_SPI( *p++ ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted + Neo_EnableMMC( false ); + return false; + } + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written + } + + // Stop transmission block + Neo_SPI( 0xFD ); // Send Stop Transmission Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data + Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + Neo_SPI (0xFF); // Send 8 clocks + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear + + + for ( i = 0; i < 0x10; i++) { + Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + +bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + + while (totalSecs--) { + Neo_SendMMCCommand(MMC_READ_BLOCK, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data + *p++ = Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Ignore CRC16 + Neo_SPI( 0xFF ); // Ignore CRC16 + sector += BYTE_PER_READ; + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + + +IO_INTERFACE io_nmmc = { + DEVICE_TYPE_NMMC, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, + (FN_MEDIUM_STARTUP)&NMMC_StartUp, + (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted, + (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown +} ; + + +LPIO_INTERFACE NMMC_GetInterface(void) { + return &io_nmmc ; +} + +#endif // #ifdef SUPPORT_NMMC +/* + io_nmmc.c + + Hardware Routines for reading an SD or MMC card using + a Neoflash MK2 or MK3. + + Written by www.neoflash.com + + Submit bug reports for this device to the NeoFlash forums + + This software is completely free. No warranty is provided. + If you use it, please give me credit and email me about your + project at chishm@hotmail.com + + See gba_nds_fat.txt for help and license details. + + 2006-02-09 - www.neoflash.com: + * First stable release + + 2006-02-13 - Chishm + * Added ReadMK2Config function + * Added read config test to init function so no unnecessary card commands are sent + * Changed data read and write functions to use multiple block commands +*/ + +#include "io_nmmc.h" + +#ifdef SUPPORT_NMMC + +#include <nds/card.h> + +int spi_freq = 3; + +#define MK2_CONFIG_ZIP_RAM_CLOSE (1 << 5) +#define MK2_CONFIG_GAME_FLASH_CLOSE ((1 << 4) | (1 << 0)) +//#define MK2_CONFIG_ZIP_RAM_CLOSE ((1 << 5) | (1 << 1)) +//#define MK2_CONFIG_GAME_FLASH_CLOSE (1 << 4) + +#define MMC_READ_MULTIPLE_BLOCK 18 +#define MMC_READ_BLOCK 17 +#define MMC_WRITE_MULTIPLE_BLOCK 25 +#define MMC_WRITE_BLOCK 24 +#define MMC_STOP_TRANSMISSION 12 +#define MMC_SET_BLOCKLEN 16 +#define MMC_SET_BLOCK_COUNT 23 +#define MMC_SEND_CSD 9 + +// SPI functions + +static inline void Neo_OpenSPI( u8 frequency ) +{ + CARD_CR1 = 0x0000A040 | frequency; +} + +static inline u8 Neo_SPI( u8 dataByte ) +{ + CARD_EEPDATA = dataByte; + while (CARD_CR1 & 0x80); // card busy + return CARD_EEPDATA; +} + +static inline void Neo_CloseSPI ( void ) +{ + CARD_CR1 = 0; +} + +static inline void Neo_MK2GameMode() { + Neo_OpenSPI(spi_freq); // Enable DS Card's SPI port + Neo_SPI(0xF1); // Switch to game mode + Neo_CloseSPI(); // Disable DS Card's SPI port +} + +static inline void Neo_EnableEEPROM( bool enable ) { + Neo_OpenSPI(spi_freq); + if(enable) Neo_SPI(0x06); + else Neo_SPI(0x0E); + Neo_CloseSPI(); +} + +void Neo_WriteMK2Config(u8 config) { + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xFA); // Send mem conf write command + Neo_SPI(0x01); // Send high byte (0x01) + Neo_SPI(config); // Send low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); +} + +u8 Neo_ReadMK2Config(void) +{ + u8 config; + Neo_EnableEEPROM(true); + Neo_OpenSPI(spi_freq); + Neo_SPI(0xf8); // Send mem conf read command + Neo_SPI(0x01); // Send high byte + config = Neo_SPI(0x00); // Get low byte + Neo_CloseSPI(); + Neo_EnableEEPROM(false); + return config; +} + +// Low level functions + +u8 selectMMC_command [8] = {0xFF, 0x00, 0x6A, 0xDF, 0x37, 0x59, 0x33, 0xA3}; + +void Neo_SelectMMC (u8 dataByte) +{ + selectMMC_command[1] = dataByte; // Set enable / disable byte + cardWriteCommand (selectMMC_command); // Send "5. Use the EEPROM CS to access the MK2 MMC/SD card" + CARD_CR2 = CARD_ACTIVATE | CARD_nRESET; + while (CARD_CR2 & CARD_BUSY); + return; +} + +void Neo_EnableMMC( bool enable ) +{ + if ( enable == false) { + Neo_CloseSPI (); + Neo_SelectMMC (0); + Neo_SelectMMC (0); + } else { + Neo_SelectMMC (1); + Neo_SelectMMC (1); + Neo_OpenSPI (spi_freq); + } + return; +} + +void Neo_SendMMCCommand( u8 command, u32 argument ) +{ + Neo_SPI (0xFF); + Neo_SPI (command | 0x40); + Neo_SPI ((argument >> 24) & 0xff); + Neo_SPI ((argument >> 16) & 0xff); + Neo_SPI ((argument >> 8) & 0xff) ; + Neo_SPI (argument & 0xff); + Neo_SPI (0x95); + Neo_SPI (0xFF); + return; +} + +bool Neo_CheckMMCResponse( u8 response, u8 mask ) { + u32 i; + for(i=0;i<256;i++) { + if( ( Neo_SPI( 0xFF ) & mask ) == response ) + return true; + } + return false; +} + +// Neo MMC functions + +bool Neo_InitMMC() { + Neo_MK2GameMode(); + Neo_WriteMK2Config( MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE); + + // Make sure the configuration was accepted + if (Neo_ReadMK2Config() != (MK2_CONFIG_ZIP_RAM_CLOSE | MK2_CONFIG_GAME_FLASH_CLOSE)) { + return false; // If not, then it wasn't initialised properly + } + + return true; +} + +// Neo MMC driver functions + +bool NMMC_IsInserted(void) { + int i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + + // consume data from card, and send clocks. + for (i = 0; i < 28; i++) { + Neo_SPI(0xff); + } + + return true; +} + +bool NMMC_ClearStatus (void) { + u32 i; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + for (i = 0; i < 10; i++) { + Neo_SPI(0xFF); // Send 10 0xFF bytes to MMC card + } + Neo_SendMMCCommand(0, 0); // Send GO_IDLE_STATE command + if( Neo_CheckMMCResponse( 0x01, 0xFF ) == false ) { // Check that it replied with 0x01 (not idle, no other error) + Neo_EnableMMC( false ); + return false; + } + for(i=0;i<256;i++) { + Neo_SendMMCCommand(1, 0); // Poll with SEND_OP_COND + if( Neo_CheckMMCResponse( 0x00, 0x01 ) == true ) { // Check for idle state + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; // Card is now idle + } + } + Neo_EnableMMC( false ); + return false; +} + +bool NMMC_Shutdown(void) { + return NMMC_ClearStatus(); +} + +bool NMMC_StartUp(void) { + int i; + int transSpeed; + if (Neo_InitMMC() == false) { + return false; + } + if (NMMC_ClearStatus() == false) { + return false; + } + Neo_EnableMMC( true ); // Open SPI port to MMC card + + // Set block length + Neo_SendMMCCommand(MMC_SET_BLOCKLEN, BYTE_PER_READ ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + // Check if we can use a higher SPI frequency + Neo_SendMMCCommand(MMC_SEND_CSD, 0); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for (i = 0; i < 3; i++) { + Neo_SPI(0xFF); + } + transSpeed = Neo_SPI (0xFF); + for (i = 0; i < 24; i++) { + Neo_SPI(0xFF); + } + if ((transSpeed & 0xf0) >= 0x30) { + spi_freq = 0; + } + + Neo_EnableMMC( false ); + return true; +} + + +bool NMMC_WriteSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + Neo_SendMMCCommand( 25, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + while (totalSecs--) { + Neo_SPI( 0xFC ); // Send Start Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of data + Neo_SPI( *p++ ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + if( ( Neo_SPI( 0xFF ) & 0x0F ) != 0x05 ) { // Make sure the block was accepted + Neo_EnableMMC( false ); + return false; + } + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the block to be written + } + + // Stop transmission block + Neo_SPI( 0xFD ); // Send Stop Transmission Block token + for( i = 0; i < BYTE_PER_READ; i++ ) // Send a block of fake data + Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Send fake CRC16 + Neo_SPI( 0xFF ); // Send fake CRC16 + + Neo_SPI (0xFF); // Send 8 clocks + while( Neo_SPI( 0xFF ) == 0x00 ); // Wait for the busy signal to clear + + + for ( i = 0; i < 0x10; i++) { + Neo_SPI (0xFF); // Send clocks for the MMC card to finish what it's doing + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + +bool NMMC_ReadSectors (u32 sector, u8 numSecs, void* buffer) +{ + u32 i; + u8 *p=buffer; + + int totalSecs = (numSecs == 0) ? 256 : numSecs; + sector *= BYTE_PER_READ; + + Neo_EnableMMC( true ); // Open SPI port to MMC card + + while (totalSecs--) { + Neo_SendMMCCommand(MMC_READ_BLOCK, sector ); + if( Neo_CheckMMCResponse( 0x00, 0xFF ) == false ) { // Make sure no errors occured + Neo_EnableMMC( false ); + return false; + } + + if( Neo_CheckMMCResponse( 0xFE, 0xFF ) == false ) { // Check for Start Block token + Neo_EnableMMC( false ); + return false; + } + for( i = 0; i < BYTE_PER_READ; i++ ) // Read in a block of data + *p++ = Neo_SPI( 0xFF ); + Neo_SPI( 0xFF ); // Ignore CRC16 + Neo_SPI( 0xFF ); // Ignore CRC16 + sector += BYTE_PER_READ; + } + + Neo_EnableMMC( false ); // Close SPI port to MMC card + return true; +} + + +IO_INTERFACE io_nmmc = { + DEVICE_TYPE_NMMC, + FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE, + (FN_MEDIUM_STARTUP)&NMMC_StartUp, + (FN_MEDIUM_ISINSERTED)&NMMC_IsInserted, + (FN_MEDIUM_READSECTORS)&NMMC_ReadSectors, + (FN_MEDIUM_WRITESECTORS)&NMMC_WriteSectors, + (FN_MEDIUM_CLEARSTATUS)&NMMC_ClearStatus, + (FN_MEDIUM_SHUTDOWN)&NMMC_Shutdown +} ; + + +LPIO_INTERFACE NMMC_GetInterface(void) { + return &io_nmmc ; +} + +#endif // #ifdef SUPPORT_NMMC |