text: //*****************************************************************************
  // sd_diskio.c
  //
  // Low level SD Card access hookup for FatFS
  //
  // Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/
  //
  //
  //  Redistribution and use in source and binary forms, with or without
  //  modification, are permitted provided that the following conditions
  //  are met:
  //
  //    Redistributions of source code must retain the above copyright
  //    notice, this list of conditions and the following disclaimer.
  //
  //    Redistributions in binary form must reproduce the above copyright
  //    notice, this list of conditions and the following disclaimer in the
  //    documentation and/or other materials provided with the
  //    distribution.
  //
  //    Neither the name of Texas Instruments Incorporated nor the names of
  //    its contributors may be used to endorse or promote products derived
  //    from this software without specific prior written permission.
  //
  //  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  //  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  //  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  //  A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  //  OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  //  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  //  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  //  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  //  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  //  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  //  OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  //
  //*****************************************************************************
  #include <stdbool.h>
  
  #include "py/mpconfig.h"
  #include MICROPY_HAL_H
  #include "hw_types.h"
  #include "hw_memmap.h"
  #include "hw_ints.h"
  #include "rom.h"
  #include "rom_map.h"
  #include "diskio.h"
  #include "sd_diskio.h"
  #include "sdhost.h"
  #include "pin.h"
  #include "prcm.h"
  #include "stdcmd.h"
  #include "utils.h"
  
  //*****************************************************************************
  // Macros
  //*****************************************************************************
  #define DISKIO_RETRY_TIMEOUT        0xFFFFFFFF
  
  #define CARD_TYPE_UNKNOWN           0
  #define CARD_TYPE_MMC               1
  #define CARD_TYPE_SDCARD            2
  
  #define CARD_CAP_CLASS_SDSC         0
  #define CARD_CAP_CLASS_SDHC         1
  
  #define CARD_VERSION_1              0
  #define CARD_VERSION_2              1
  
  //*****************************************************************************
  // Disk Info for attached disk
  //*****************************************************************************
  DiskInfo_t sd_disk_info =  {CARD_TYPE_UNKNOWN, CARD_VERSION_1, CARD_CAP_CLASS_SDSC, 0, 0, STA_NOINIT, 0};
  
  //*****************************************************************************
  //
  //! Send Command to card
  //!
  //! \param ulCmd is the command to be send
  //! \paran ulArg is the command argument
  //!
  //! This function sends command to attached card and check the response status
  //! if any.
  //!
  //! \return Returns 0 on success, 1 otherwise
  //
  //*****************************************************************************
  static unsigned int CardSendCmd (unsigned int ulCmd, unsigned int ulArg) {
      unsigned long ulStatus;
  
      // Clear the interrupt status
      MAP_SDHostIntClear(SDHOST_BASE,0xFFFFFFFF);
  
      // Send command
      MAP_SDHostCmdSend(SDHOST_BASE,ulCmd,ulArg);
  
      // Wait for command complete or error
      do {
          ulStatus = MAP_SDHostIntStatus(SDHOST_BASE);
          ulStatus = (ulStatus & (SDHOST_INT_CC | SDHOST_INT_ERRI));
      } while (!ulStatus);
  
      // Check error status
      if (ulStatus & SDHOST_INT_ERRI) {
          // Reset the command line
          MAP_SDHostCmdReset(SDHOST_BASE);
          return 1;
      }
      else {
          return 0;
      }
  }
  
  //*****************************************************************************
  //
  //! Get the capacity of specified card
  //!
  //! \param ulRCA is the Relative Card Address (RCA)
  //!
  //! This function gets the capacity of card addressed by \e ulRCA paramaeter.
  //!
  //! \return Returns 0 on success, 1 otherwise.
  //
  //*****************************************************************************
  static unsigned int CardCapacityGet(DiskInfo_t *psDiskInfo) {
      unsigned long ulRet;
      unsigned long ulResp[4];
      unsigned long ulBlockSize;
      unsigned long ulBlockCount;
      unsigned long ulCSizeMult;
      unsigned long ulCSize;
  
      // Read the CSD register
      ulRet = CardSendCmd(CMD_SEND_CSD, (psDiskInfo->usRCA << 16));
  
      if(ulRet == 0) {
          // Read the response
          MAP_SDHostRespGet(SDHOST_BASE,ulResp);
  
          // 136 bit CSD register is read into an array of 4 words.
          // ulResp[0] = CSD[31:0]
          // ulResp[1] = CSD[63:32]
          // ulResp[2] = CSD[95:64]
          // ulResp[3] = CSD[127:96]
          if(ulResp[3] >> 30) {
              ulBlockSize = SD_SECTOR_SIZE * 1024;
              ulBlockCount = (ulResp[1] >> 16 | ((ulResp[2] & 0x3F) << 16)) + 1;
          }
          else {
              ulBlockSize  = 1 << ((ulResp[2] >> 16) & 0xF);
              ulCSizeMult  = ((ulResp[1] >> 15) & 0x7);
              ulCSize      = ((ulResp[1] >> 30) | (ulResp[2] & 0x3FF) << 2);
              ulBlockCount = (ulCSize + 1) * (1 << (ulCSizeMult + 2));
          }
  
          // Calculate the card capacity in bytes
          psDiskInfo->ulBlockSize = ulBlockSize;
          psDiskInfo->ulNofBlock  = ulBlockCount;
      }
  
      return ulRet;
  }
  
  //*****************************************************************************
  //
  //! Select a card for reading or writing
  //!
  //! \param Card is the pointer to card attribute structure.
  //!
  //! This function selects a card for reading or writing using its RCA from
  //! \e Card parameter.
  //!
  //! \return Returns 0 success, 1 otherwise.
  //
  //*****************************************************************************
  static unsigned int CardSelect (DiskInfo_t *sDiskInfo) {
      unsigned long ulRCA;
      unsigned long ulRet;
  
      ulRCA = sDiskInfo->usRCA;
  
      // Send select command with card's RCA.
      ulRet = CardSendCmd(CMD_SELECT_CARD, (ulRCA << 16));
  
      if (ulRet == 0) {
          while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC));
      }
  
      // Delay 250ms for the card to become ready
      HAL_Delay (250);
  
      return ulRet;
  }
  
  //*****************************************************************************
  //
  //! Initializes physical drive
  //!
  //! This function initializes the physical drive
  //!
  //! \return Returns 0 on succeeded.
  //*****************************************************************************
  DSTATUS sd_disk_init (void) {
      unsigned long ulRet;
      unsigned long ulResp[4];
  
      if (sd_disk_info.bStatus != 0) {
          sd_disk_info.bStatus = STA_NODISK;
          // Send std GO IDLE command
          if (CardSendCmd(CMD_GO_IDLE_STATE, 0) == 0) {
              // Get interface operating condition for the card
              ulRet = CardSendCmd(CMD_SEND_IF_COND,0x000001A5);
              MAP_SDHostRespGet(SDHOST_BASE,ulResp);
  
              // It's a SD ver 2.0 or higher card
              if (ulRet == 0 && ((ulResp[0] & 0xFF) == 0xA5)) {
                  // Version 1 card do not respond to this command
                  sd_disk_info.ulVersion = CARD_VERSION_2;
                  sd_disk_info.ucCardType = CARD_TYPE_SDCARD;
  
                  // Wait for card to become ready.
                  do {
                      // Send ACMD41
                      CardSendCmd(CMD_APP_CMD, 0);
                      ulRet = CardSendCmd(CMD_SD_SEND_OP_COND, 0x40E00000);
  
                      // Response contains 32-bit OCR register
                      MAP_SDHostRespGet(SDHOST_BASE, ulResp);
  
                  } while (((ulResp[0] >> 31) == 0));
  
                  if (ulResp[0] & (1UL<<30)) {
                      sd_disk_info.ulCapClass = CARD_CAP_CLASS_SDHC;
                  }
                  sd_disk_info.bStatus = 0;
              }
              //It's a MMC or SD 1.x card
              else {
                  // Wait for card to become ready.
                  do {
                      CardSendCmd(CMD_APP_CMD, 0);
                      ulRet = CardSendCmd(CMD_SD_SEND_OP_COND,0x00E00000);
                      if (ulRet == 0) {
                          // Response contains 32-bit OCR register
                          MAP_SDHostRespGet(SDHOST_BASE, ulResp);
                      }
                  } while (((ulRet == 0) && (ulResp[0] >> 31) == 0));
  
                  if (ulRet == 0) {
                      sd_disk_info.ucCardType = CARD_TYPE_SDCARD;
                      sd_disk_info.bStatus = 0;
                  }
                  else {
                      if (CardSendCmd(CMD_SEND_OP_COND, 0) == 0) {
                          // MMC not supported by the controller
                          sd_disk_info.ucCardType = CARD_TYPE_MMC;
                      }
                  }
              }
          }
  
          // Get the RCA of the attached card
          if (sd_disk_info.bStatus == 0) {
              ulRet = CardSendCmd(CMD_ALL_SEND_CID, 0);
              if (ulRet == 0) {
                  CardSendCmd(CMD_SEND_REL_ADDR,0);
                  MAP_SDHostRespGet(SDHOST_BASE, ulResp);
  
                  //  Fill in the RCA
                  sd_disk_info.usRCA = (ulResp[0] >> 16);
  
                  // Get tha card capacity
                  CardCapacityGet(&sd_disk_info);
              }
  
              // Select the card.
              ulRet = CardSelect(&sd_disk_info);
              if (ulRet == 0) {
                  sd_disk_info.bStatus = 0;
              }
          }
      }
  
      return sd_disk_info.bStatus;
  }
  
  //*****************************************************************************
  //
  //! De-initializes the physical drive
  //!
  //! This function de-initializes the physical drive
  //*****************************************************************************
  void sd_disk_deinit (void) {
      sd_disk_info.ucCardType = CARD_TYPE_UNKNOWN;
      sd_disk_info.ulVersion = CARD_VERSION_1;
      sd_disk_info.ulCapClass = CARD_CAP_CLASS_SDSC;
      sd_disk_info.ulNofBlock = 0;
      sd_disk_info.ulBlockSize = 0;
      sd_disk_info.bStatus = STA_NOINIT;
      sd_disk_info.usRCA = 0;
  }
  
  //*****************************************************************************
  //
  //! Reads sector(s) from the disk drive.
  //!
  //!
  //! This function reads specified number of sectors from the drive
  //!
  //! \return Returns RES_OK on success.
  //
  //*****************************************************************************
  DRESULT sd_disk_read (BYTE* pBuffer, DWORD ulSectorNumber, UINT SectorCount) {
      DRESULT Res = RES_ERROR;
      unsigned long ulSize;
  
      if (SectorCount > 0) {
          // Return if disk not initialized
          if (sd_disk_info.bStatus & STA_NOINIT) {
              return RES_NOTRDY;
          }
  
          // SDSC uses linear address, SDHC uses block address
          if (sd_disk_info.ulCapClass == CARD_CAP_CLASS_SDSC) {
              ulSectorNumber = ulSectorNumber * SD_SECTOR_SIZE;
          }
  
          // Set the block count
          MAP_SDHostBlockCountSet(SDHOST_BASE, SectorCount);
  
          // Compute the number of words
          ulSize = (SD_SECTOR_SIZE * SectorCount) / 4;
  
          // Check if 1 block or multi block transfer
          if (SectorCount == 1) {
              // Send single block read command
              if (CardSendCmd(CMD_READ_SINGLE_BLK, ulSectorNumber) == 0) {
                  // Read the block of data
                  while (ulSize--) {
                      MAP_SDHostDataRead(SDHOST_BASE, (unsigned long *)pBuffer);
                      pBuffer += 4;
                  }
                  Res = RES_OK;
              }
          }
          else {
              // Send multi block read command
              if (CardSendCmd(CMD_READ_MULTI_BLK, ulSectorNumber) == 0) {
                  // Read the data
                  while (ulSize--) {
                      MAP_SDHostDataRead(SDHOST_BASE, (unsigned long *)pBuffer);
                      pBuffer += 4;
                  }
                  CardSendCmd(CMD_STOP_TRANS, 0);
                  while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC));
                  Res = RES_OK;
              }
          }
      }
  
      return Res;
  }
  
  //*****************************************************************************
  //
  //! Wrties sector(s) to the disk drive.
  //!
  //!
  //! This function writes specified number of sectors to the drive
  //!
  //! \return Returns RES_OK on success.
  //
  //*****************************************************************************
  DRESULT sd_disk_write (const BYTE* pBuffer, DWORD ulSectorNumber, UINT SectorCount) {
      DRESULT Res = RES_ERROR;
      unsigned long ulSize;
  
      if (SectorCount > 0) {
          // Return if disk not initialized
          if (sd_disk_info.bStatus & STA_NOINIT) {
              return RES_NOTRDY;
          }
  
          // SDSC uses linear address, SDHC uses block address
          if (sd_disk_info.ulCapClass == CARD_CAP_CLASS_SDSC) {
              ulSectorNumber = ulSectorNumber * SD_SECTOR_SIZE;
          }
  
          // Set the block count
          MAP_SDHostBlockCountSet(SDHOST_BASE, SectorCount);
  
          // Compute the number of words
          ulSize = (SD_SECTOR_SIZE * SectorCount) / 4;
  
          // Check if 1 block or multi block transfer
          if (SectorCount == 1) {
              // Send single block write command
              if (CardSendCmd(CMD_WRITE_SINGLE_BLK, ulSectorNumber) == 0) {
                  // Write the data
                  while (ulSize--) {
                      MAP_SDHostDataWrite (SDHOST_BASE, (*(unsigned long *)pBuffer));
                      pBuffer += 4;
                  }
                  // Wait for data transfer complete
                  while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC));
                  Res = RES_OK;
              }
          }
          else {
              // Set the card write block count
              if (sd_disk_info.ucCardType == CARD_TYPE_SDCARD) {
                  CardSendCmd(CMD_APP_CMD,sd_disk_info.usRCA << 16);
                  CardSendCmd(CMD_SET_BLK_CNT, SectorCount);
              }
  
              // Send multi block write command
              if (CardSendCmd(CMD_WRITE_MULTI_BLK, ulSectorNumber) == 0) {
                  // Write the data buffer
                  while (ulSize--) {
                      MAP_SDHostDataWrite(SDHOST_BASE, (*(unsigned long *)pBuffer));
                      pBuffer += 4;
                  }
                  // Wait for transfer complete
                  while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC));
                  CardSendCmd(CMD_STOP_TRANS, 0);
                  while (!(MAP_SDHostIntStatus(SDHOST_BASE) & SDHOST_INT_TC));
                  Res = RES_OK;
              }
          }
      }
  
      return Res;
  }
  