Home | History | Annotate | Download | only in MmcDxe
      1 /** @file
      2 *
      3 *  Copyright (c) 2011-2015, ARM Limited. All rights reserved.
      4 *
      5 *  This program and the accompanying materials
      6 *  are licensed and made available under the terms and conditions of the BSD License
      7 *  which accompanies this distribution.  The full text of the license may be found at
      8 *  http://opensource.org/licenses/bsd-license.php
      9 *
     10 *  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 *  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 *
     13 **/
     14 
     15 #include <Library/BaseMemoryLib.h>
     16 
     17 #include "Mmc.h"
     18 
     19 EFI_STATUS
     20 MmcNotifyState (
     21   IN MMC_HOST_INSTANCE *MmcHostInstance,
     22   IN MMC_STATE State
     23   )
     24 {
     25   MmcHostInstance->State = State;
     26   return MmcHostInstance->MmcHost->NotifyState (MmcHostInstance->MmcHost, State);
     27 }
     28 
     29 EFI_STATUS
     30 EFIAPI
     31 MmcGetCardStatus (
     32   IN MMC_HOST_INSTANCE     *MmcHostInstance
     33   )
     34 {
     35   EFI_STATUS              Status;
     36   UINT32                  Response[4];
     37   UINTN                   CmdArg;
     38   EFI_MMC_HOST_PROTOCOL   *MmcHost;
     39 
     40   Status = EFI_SUCCESS;
     41   MmcHost = MmcHostInstance->MmcHost;
     42   CmdArg = 0;
     43 
     44   if (MmcHost == NULL) {
     45     return EFI_INVALID_PARAMETER;
     46   }
     47   if (MmcHostInstance->State != MmcHwInitializationState) {
     48     //Get the Status of the card.
     49     CmdArg = MmcHostInstance->CardInfo.RCA << 16;
     50     Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
     51     if (EFI_ERROR (Status)) {
     52       DEBUG ((EFI_D_ERROR, "MmcGetCardStatus(MMC_CMD13): Error and Status = %r\n", Status));
     53       return Status;
     54     }
     55 
     56     //Read Response
     57     MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
     58     PrintResponseR1 (Response[0]);
     59   }
     60 
     61   return Status;
     62 }
     63 
     64 EFI_STATUS
     65 EFIAPI
     66 MmcReset (
     67   IN EFI_BLOCK_IO_PROTOCOL    *This,
     68   IN BOOLEAN                  ExtendedVerification
     69   )
     70 {
     71   MMC_HOST_INSTANCE       *MmcHostInstance;
     72 
     73   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
     74 
     75   if (MmcHostInstance->MmcHost == NULL) {
     76     // Nothing to do
     77     return EFI_SUCCESS;
     78   }
     79 
     80   // If a card is not present then clear all media settings
     81   if (!MmcHostInstance->MmcHost->IsCardPresent (MmcHostInstance->MmcHost)) {
     82     MmcHostInstance->BlockIo.Media->MediaPresent = FALSE;
     83     MmcHostInstance->BlockIo.Media->LastBlock    = 0;
     84     MmcHostInstance->BlockIo.Media->BlockSize    = 512;  // Should be zero but there is a bug in DiskIo
     85     MmcHostInstance->BlockIo.Media->ReadOnly     = FALSE;
     86 
     87     // Indicate that the driver requires initialization
     88     MmcHostInstance->State = MmcHwInitializationState;
     89 
     90     return EFI_SUCCESS;
     91   }
     92 
     93   // Implement me. Either send a CMD0 (could not work for some MMC host) or just turn off/turn
     94   //      on power and restart Identification mode
     95   return EFI_SUCCESS;
     96 }
     97 
     98 EFI_STATUS
     99 MmcDetectCard (
    100   EFI_MMC_HOST_PROTOCOL     *MmcHost
    101   )
    102 {
    103   if (!MmcHost->IsCardPresent (MmcHost)) {
    104     return EFI_NO_MEDIA;
    105   } else {
    106     return EFI_SUCCESS;
    107   }
    108 }
    109 
    110 EFI_STATUS
    111 MmcStopTransmission (
    112   EFI_MMC_HOST_PROTOCOL     *MmcHost
    113   )
    114 {
    115   EFI_STATUS              Status;
    116   UINT32                  Response[4];
    117   // Command 12 - Stop transmission (ends read or write)
    118   // Normally only needed for streaming transfers or after error.
    119   Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
    120   if (!EFI_ERROR (Status)) {
    121     MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
    122   }
    123   return Status;
    124 }
    125 
    126 #define MMCI0_BLOCKLEN 512
    127 #define MMCI0_TIMEOUT  10000
    128 
    129 STATIC EFI_STATUS
    130 MmcTransferBlock (
    131   IN EFI_BLOCK_IO_PROTOCOL    *This,
    132   IN UINTN                    Cmd,
    133   IN UINTN                    Transfer,
    134   IN UINT32                   MediaId,
    135   IN EFI_LBA                  Lba,
    136   IN UINTN                    BufferSize,
    137   OUT VOID                    *Buffer
    138   )
    139 {
    140   EFI_STATUS              Status;
    141   UINTN                   CmdArg;
    142   INTN                    Timeout;
    143   UINT32                  Response[4];
    144   MMC_HOST_INSTANCE       *MmcHostInstance;
    145   EFI_MMC_HOST_PROTOCOL   *MmcHost;
    146 
    147   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
    148   MmcHost = MmcHostInstance->MmcHost;
    149 
    150   //Set command argument based on the card access mode (Byte mode or Block mode)
    151   if (MmcHostInstance->CardInfo.OCRData.AccessMode & BIT1) {
    152     CmdArg = Lba;
    153   } else {
    154     CmdArg = Lba * This->Media->BlockSize;
    155   }
    156 
    157   Status = MmcHost->SendCommand (MmcHost, Cmd, CmdArg);
    158   if (EFI_ERROR (Status)) {
    159     DEBUG ((EFI_D_ERROR, "%a(MMC_CMD%d): Error %r\n", __func__, Cmd, Status));
    160     return Status;
    161   }
    162 
    163   if (Transfer == MMC_IOBLOCKS_READ) {
    164     // Read Data
    165     Status = MmcHost->ReadBlockData (MmcHost, Lba, BufferSize, Buffer);
    166     if (EFI_ERROR (Status)) {
    167       DEBUG ((EFI_D_BLKIO, "%a(): Error Read Block Data and Status = %r\n", __func__, Status));
    168       MmcStopTransmission (MmcHost);
    169       return Status;
    170     }
    171     Status = MmcNotifyState (MmcHostInstance, MmcProgrammingState);
    172     if (EFI_ERROR (Status)) {
    173       DEBUG ((EFI_D_ERROR, "%a() : Error MmcProgrammingState\n", __func__));
    174       return Status;
    175     }
    176   } else {
    177     // Write Data
    178     Status = MmcHost->WriteBlockData (MmcHost, Lba, BufferSize, Buffer);
    179     if (EFI_ERROR (Status)) {
    180       DEBUG ((EFI_D_BLKIO, "%a(): Error Write Block Data and Status = %r\n", __func__, Status));
    181       MmcStopTransmission (MmcHost);
    182       return Status;
    183     }
    184   }
    185 
    186   // Command 13 - Read status and wait for programming to complete (return to tran)
    187   Timeout = MMCI0_TIMEOUT;
    188   CmdArg = MmcHostInstance->CardInfo.RCA << 16;
    189   Response[0] = 0;
    190   while(   (!(Response[0] & MMC_R0_READY_FOR_DATA))
    191         && (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
    192         && Timeout--) {
    193     Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
    194     if (!EFI_ERROR (Status)) {
    195       MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
    196       if ((Response[0] & MMC_R0_READY_FOR_DATA)) {
    197         break;  // Prevents delay once finished
    198       }
    199     }
    200     gBS->Stall (1);
    201   }
    202 
    203   if (BufferSize > This->Media->BlockSize) {
    204     Status = MmcHost->SendCommand (MmcHost, MMC_CMD12, 0);
    205     if (EFI_ERROR (Status)) {
    206       DEBUG ((EFI_D_BLKIO, "%a(): Error and Status:%r\n", __func__, Status));
    207     }
    208   }
    209 
    210   Status = MmcNotifyState (MmcHostInstance, MmcTransferState);
    211   if (EFI_ERROR (Status)) {
    212     DEBUG ((EFI_D_ERROR, "MmcIoBlocks() : Error MmcTransferState\n"));
    213     return Status;
    214   }
    215   return Status;
    216 }
    217 
    218 EFI_STATUS
    219 MmcIoBlocks (
    220   IN EFI_BLOCK_IO_PROTOCOL    *This,
    221   IN UINTN                    Transfer,
    222   IN UINT32                   MediaId,
    223   IN EFI_LBA                  Lba,
    224   IN UINTN                    BufferSize,
    225   OUT VOID                    *Buffer
    226   )
    227 {
    228   UINT32                  Response[4];
    229   EFI_STATUS              Status;
    230   UINTN                   CmdArg;
    231   INTN                    Timeout;
    232   UINTN                   Cmd;
    233   MMC_HOST_INSTANCE       *MmcHostInstance;
    234   EFI_MMC_HOST_PROTOCOL   *MmcHost;
    235   UINTN                   BytesRemainingToBeTransfered;
    236   UINTN                   BlockCount;
    237   UINTN                   ConsumeSize;
    238 
    239   BlockCount = 1;
    240   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
    241   ASSERT (MmcHostInstance != NULL);
    242   MmcHost = MmcHostInstance->MmcHost;
    243   ASSERT (MmcHost);
    244 
    245   if (This->Media->MediaId != MediaId) {
    246     return EFI_MEDIA_CHANGED;
    247   }
    248 
    249   if ((MmcHost == NULL) || (Buffer == NULL)) {
    250     return EFI_INVALID_PARAMETER;
    251   }
    252 
    253   // Check if a Card is Present
    254   if (!MmcHostInstance->BlockIo.Media->MediaPresent) {
    255     return EFI_NO_MEDIA;
    256   }
    257 
    258   if (MmcHost->IsMultiBlock && MmcHost->IsMultiBlock(MmcHost)) {
    259     BlockCount = (BufferSize + This->Media->BlockSize - 1) / This->Media->BlockSize;
    260   }
    261 
    262   // All blocks must be within the device
    263   if ((Lba + (BufferSize / This->Media->BlockSize)) > (This->Media->LastBlock + 1)) {
    264     return EFI_INVALID_PARAMETER;
    265   }
    266 
    267   if ((Transfer == MMC_IOBLOCKS_WRITE) && (This->Media->ReadOnly == TRUE)) {
    268     return EFI_WRITE_PROTECTED;
    269   }
    270 
    271   // Reading 0 Byte is valid
    272   if (BufferSize == 0) {
    273     return EFI_SUCCESS;
    274   }
    275 
    276   // The buffer size must be an exact multiple of the block size
    277   if ((BufferSize % This->Media->BlockSize) != 0) {
    278     return EFI_BAD_BUFFER_SIZE;
    279   }
    280 
    281   // Check the alignment
    282   if ((This->Media->IoAlign > 2) && (((UINTN)Buffer & (This->Media->IoAlign - 1)) != 0)) {
    283     return EFI_INVALID_PARAMETER;
    284   }
    285 
    286   BytesRemainingToBeTransfered = BufferSize;
    287   while (BytesRemainingToBeTransfered > 0) {
    288 
    289     // Check if the Card is in Ready status
    290     CmdArg = MmcHostInstance->CardInfo.RCA << 16;
    291     Response[0] = 0;
    292     Timeout = 20;
    293     while(   (!(Response[0] & MMC_R0_READY_FOR_DATA))
    294           && (MMC_R0_CURRENTSTATE (Response) != MMC_R0_STATE_TRAN)
    295           && Timeout--) {
    296       Status = MmcHost->SendCommand (MmcHost, MMC_CMD13, CmdArg);
    297       if (!EFI_ERROR (Status)) {
    298         MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
    299       }
    300     }
    301 
    302     if (0 == Timeout) {
    303       DEBUG ((EFI_D_ERROR, "The Card is busy\n"));
    304       return EFI_NOT_READY;
    305     }
    306 
    307     if (Transfer == MMC_IOBLOCKS_READ) {
    308       if (BlockCount == 1) {
    309         // Read a single block
    310         Cmd = MMC_CMD17;
    311       } else {
    312 	// Read multiple blocks
    313 	Cmd = MMC_CMD18;
    314       }
    315     } else {
    316       if (BlockCount == 1) {
    317         // Write a single block
    318         Cmd = MMC_CMD24;
    319       } else {
    320 	// Write multiple blocks
    321 	Cmd = MMC_CMD25;
    322       }
    323     }
    324 
    325     ConsumeSize = BlockCount * This->Media->BlockSize;
    326     if (BytesRemainingToBeTransfered < ConsumeSize) {
    327       ConsumeSize = BytesRemainingToBeTransfered;
    328     }
    329     Status = MmcTransferBlock (This, Cmd, Transfer, MediaId, Lba, ConsumeSize, Buffer);
    330     if (EFI_ERROR (Status)) {
    331       DEBUG ((EFI_D_ERROR, "%a(): Failed to transfer block and Status:%r\n", __func__, Status));
    332     }
    333 
    334     BytesRemainingToBeTransfered -= ConsumeSize;
    335     if (BytesRemainingToBeTransfered > 0) {
    336       Lba    += BlockCount;
    337       Buffer = (UINT8 *)Buffer + ConsumeSize;
    338     }
    339   }
    340 
    341   return EFI_SUCCESS;
    342 }
    343 
    344 EFI_STATUS
    345 EFIAPI
    346 MmcReadBlocks (
    347   IN EFI_BLOCK_IO_PROTOCOL    *This,
    348   IN UINT32                   MediaId,
    349   IN EFI_LBA                  Lba,
    350   IN UINTN                    BufferSize,
    351   OUT VOID                    *Buffer
    352   )
    353 {
    354   return MmcIoBlocks (This, MMC_IOBLOCKS_READ, MediaId, Lba, BufferSize, Buffer);
    355 }
    356 
    357 EFI_STATUS
    358 EFIAPI
    359 MmcWriteBlocks (
    360   IN EFI_BLOCK_IO_PROTOCOL    *This,
    361   IN UINT32                   MediaId,
    362   IN EFI_LBA                  Lba,
    363   IN UINTN                    BufferSize,
    364   IN VOID                     *Buffer
    365   )
    366 {
    367   return MmcIoBlocks (This, MMC_IOBLOCKS_WRITE, MediaId, Lba, BufferSize, Buffer);
    368 }
    369 
    370 EFI_STATUS
    371 EFIAPI
    372 MmcFlushBlocks (
    373   IN EFI_BLOCK_IO_PROTOCOL  *This
    374   )
    375 {
    376   return EFI_SUCCESS;
    377 }
    378 
    379 EFI_STATUS
    380 EFIAPI
    381 MmcEraseBlocks (
    382   IN EFI_BLOCK_IO_PROTOCOL          *This,
    383   IN UINT32                         MediaId,
    384   IN EFI_LBA                        Lba,
    385   IN OUT EFI_ERASE_BLOCK_TOKEN      *Token,
    386   IN UINTN                          Size
    387   )
    388 {
    389   EFI_STATUS             Status;
    390   MMC_HOST_INSTANCE      *MmcHostInstance;
    391   EFI_MMC_HOST_PROTOCOL  *MmcHost;
    392   UINT32                 Response[4];
    393   UINTN                  CmdArg;
    394 
    395   MmcHostInstance = MMC_HOST_INSTANCE_FROM_BLOCK_IO_THIS (This);
    396   ASSERT (MmcHostInstance != NULL);
    397   MmcHost = MmcHostInstance->MmcHost;
    398   ASSERT (MmcHost);
    399 
    400   if (This->Media->ReadOnly == TRUE)
    401      return EFI_WRITE_PROTECTED;
    402 
    403 // EFI_DEVICE_ERROR, EFI_INVALID_PARAMETER
    404 
    405   if (!MmcHostInstance->BlockIo.Media->MediaPresent)
    406      return EFI_NO_MEDIA;
    407 
    408   if (This->Media->MediaId != MediaId)
    409      return EFI_MEDIA_CHANGED;
    410 
    411   CmdArg = Lba;
    412   Status = MmcHost->SendCommand (MmcHost, MMC_CMD35, CmdArg);
    413   if (!EFI_ERROR (Status)) {
    414      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
    415   }
    416 
    417   CmdArg = Lba + Size;
    418   Status = MmcHost->SendCommand (MmcHost, MMC_CMD36, CmdArg);
    419   if (!EFI_ERROR (Status)) {
    420      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1, Response);
    421   }
    422 
    423   CmdArg = 0;
    424   Status = MmcHost->SendCommand (MmcHost, MMC_CMD38, CmdArg);
    425   if (!EFI_ERROR (Status)) {
    426      MmcHost->ReceiveResponse (MmcHost, MMC_RESPONSE_TYPE_R1b, Response);
    427   }
    428 
    429   return EFI_SUCCESS;
    430 }
    431