]> git.proxmox.com Git - mirror_edk2.git/blobdiff - MdeModulePkg/Bus/Sd/EmmcDxe/EmmcBlockIo.c
MdeModulePkg/Sd: add Erase Block support on sd/emmc device
[mirror_edk2.git] / MdeModulePkg / Bus / Sd / EmmcDxe / EmmcBlockIo.c
index edb438b09bfb0f95fea1491e0768f3add3b70ac8..5fe710dbb5d682ff1c1a93a6f4690250ec1ae555 100644 (file)
@@ -1589,3 +1589,416 @@ EmmcSecurityProtocolOut (
   return Status;\r
 }\r
 \r
+/**\r
+  Set the erase start address through sync or async I/O request.\r
+\r
+  @param[in]  Partition         A pointer to the EMMC_PARTITION instance.\r
+  @param[in]  StartLba          The starting logical block address to be erased.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EmmcEraseBlockStart (\r
+  IN  EMMC_PARTITION            *Partition,\r
+  IN  EFI_LBA                   StartLba,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EMMC_DEVICE                          *Device;\r
+  EMMC_REQUEST                         *EraseBlockStart;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  EraseBlockStart = NULL;\r
+\r
+  Device   = Partition->Device;\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  EraseBlockStart = AllocateZeroPool (sizeof (EMMC_REQUEST));\r
+  if (EraseBlockStart == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  EraseBlockStart->Signature = EMMC_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Partition->Queue, &EraseBlockStart->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  EraseBlockStart->Packet.SdMmcCmdBlk    = &EraseBlockStart->SdMmcCmdBlk;\r
+  EraseBlockStart->Packet.SdMmcStatusBlk = &EraseBlockStart->SdMmcStatusBlk;\r
+  EraseBlockStart->Packet.Timeout        = EMMC_GENERIC_TIMEOUT;\r
+\r
+  EraseBlockStart->SdMmcCmdBlk.CommandIndex = EMMC_ERASE_GROUP_START;\r
+  EraseBlockStart->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  EraseBlockStart->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+\r
+  if (Device->SectorAddressing) {\r
+    EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)StartLba;\r
+  } else {\r
+    EraseBlockStart->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (StartLba, Partition->BlockMedia.BlockSize);\r
+  }\r
+\r
+  EraseBlockStart->IsEnd = IsEnd;\r
+  EraseBlockStart->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    EraseBlockStart,\r
+                    &EraseBlockStart->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    EraseBlockStart->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockStart->Packet, EraseBlockStart->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (EraseBlockStart != NULL)) {\r
+      RemoveEntryList (&EraseBlockStart->Link);\r
+      if (EraseBlockStart->Event != NULL) {\r
+        gBS->CloseEvent (EraseBlockStart->Event);\r
+      }\r
+      FreePool (EraseBlockStart);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (EraseBlockStart != NULL) {\r
+      RemoveEntryList (&EraseBlockStart->Link);\r
+      FreePool (EraseBlockStart);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Set the erase end address through sync or async I/O request.\r
+\r
+  @param[in]  Partition         A pointer to the EMMC_PARTITION instance.\r
+  @param[in]  EndLba            The ending logical block address to be erased.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EmmcEraseBlockEnd (\r
+  IN  EMMC_PARTITION            *Partition,\r
+  IN  EFI_LBA                   EndLba,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EMMC_DEVICE                          *Device;\r
+  EMMC_REQUEST                         *EraseBlockEnd;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  EraseBlockEnd = NULL;\r
+\r
+  Device   = Partition->Device;\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  EraseBlockEnd = AllocateZeroPool (sizeof (EMMC_REQUEST));\r
+  if (EraseBlockEnd == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  EraseBlockEnd->Signature = EMMC_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Partition->Queue, &EraseBlockEnd->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  EraseBlockEnd->Packet.SdMmcCmdBlk    = &EraseBlockEnd->SdMmcCmdBlk;\r
+  EraseBlockEnd->Packet.SdMmcStatusBlk = &EraseBlockEnd->SdMmcStatusBlk;\r
+  EraseBlockEnd->Packet.Timeout        = EMMC_GENERIC_TIMEOUT;\r
+\r
+  EraseBlockEnd->SdMmcCmdBlk.CommandIndex = EMMC_ERASE_GROUP_END;\r
+  EraseBlockEnd->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  EraseBlockEnd->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1;\r
+\r
+  if (Device->SectorAddressing) {\r
+    EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)EndLba;\r
+  } else {\r
+    EraseBlockEnd->SdMmcCmdBlk.CommandArgument = (UINT32)MultU64x32 (EndLba, Partition->BlockMedia.BlockSize);\r
+  }\r
+\r
+  EraseBlockEnd->IsEnd = IsEnd;\r
+  EraseBlockEnd->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    EraseBlockEnd,\r
+                    &EraseBlockEnd->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    EraseBlockEnd->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlockEnd->Packet, EraseBlockEnd->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (EraseBlockEnd != NULL)) {\r
+      RemoveEntryList (&EraseBlockEnd->Link);\r
+      if (EraseBlockEnd->Event != NULL) {\r
+        gBS->CloseEvent (EraseBlockEnd->Event);\r
+      }\r
+      FreePool (EraseBlockEnd);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (EraseBlockEnd != NULL) {\r
+      RemoveEntryList (&EraseBlockEnd->Link);\r
+      FreePool (EraseBlockEnd);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Erase specified blocks through sync or async I/O request.\r
+\r
+  @param[in]  Partition         A pointer to the EMMC_PARTITION instance.\r
+  @param[in]  Token             A pointer to the token associated with the transaction.\r
+  @param[in]  IsEnd             A boolean to show whether it's the last cmd in a series of cmds.\r
+                                This parameter is only meaningful in async I/O request.\r
+\r
+  @retval EFI_SUCCESS           The request is executed successfully.\r
+  @retval EFI_OUT_OF_RESOURCES  The request could not be executed due to a lack of resources.\r
+  @retval Others                The request could not be executed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EmmcEraseBlock (\r
+  IN  EMMC_PARTITION            *Partition,\r
+  IN  EFI_BLOCK_IO2_TOKEN       *Token,\r
+  IN  BOOLEAN                   IsEnd\r
+  )\r
+{\r
+  EFI_STATUS                           Status;\r
+  EFI_SD_MMC_PASS_THRU_PROTOCOL        *PassThru;\r
+  EMMC_DEVICE                          *Device;\r
+  EMMC_REQUEST                         *EraseBlock;\r
+  EFI_TPL                              OldTpl;\r
+\r
+  EraseBlock = NULL;\r
+\r
+  Device   = Partition->Device;\r
+  PassThru = Device->Private->PassThru;\r
+\r
+  EraseBlock = AllocateZeroPool (sizeof (EMMC_REQUEST));\r
+  if (EraseBlock == NULL) {\r
+    Status = EFI_OUT_OF_RESOURCES;\r
+    goto Error;\r
+  }\r
+\r
+  EraseBlock->Signature = EMMC_REQUEST_SIGNATURE;\r
+  OldTpl = gBS->RaiseTPL (TPL_CALLBACK);\r
+  InsertTailList (&Partition->Queue, &EraseBlock->Link);\r
+  gBS->RestoreTPL (OldTpl);\r
+  EraseBlock->Packet.SdMmcCmdBlk    = &EraseBlock->SdMmcCmdBlk;\r
+  EraseBlock->Packet.SdMmcStatusBlk = &EraseBlock->SdMmcStatusBlk;\r
+  EraseBlock->Packet.Timeout        = EMMC_GENERIC_TIMEOUT;\r
+\r
+  EraseBlock->SdMmcCmdBlk.CommandIndex = EMMC_ERASE;\r
+  EraseBlock->SdMmcCmdBlk.CommandType  = SdMmcCommandTypeAc;\r
+  EraseBlock->SdMmcCmdBlk.ResponseType = SdMmcResponseTypeR1b;\r
+\r
+  EraseBlock->IsEnd = IsEnd;\r
+  EraseBlock->Token = Token;\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Status = gBS->CreateEvent (\r
+                    EVT_NOTIFY_SIGNAL,\r
+                    TPL_CALLBACK,\r
+                    AsyncIoCallback,\r
+                    EraseBlock,\r
+                    &EraseBlock->Event\r
+                    );\r
+    if (EFI_ERROR (Status)) {\r
+      goto Error;\r
+    }\r
+  } else {\r
+    EraseBlock->Event = NULL;\r
+  }\r
+\r
+  Status = PassThru->PassThru (PassThru, Device->Slot, &EraseBlock->Packet, EraseBlock->Event);\r
+\r
+Error:\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    //\r
+    // For asynchronous operation, only free request and event in error case.\r
+    // The request and event will be freed in asynchronous callback for success case.\r
+    //\r
+    if (EFI_ERROR (Status) && (EraseBlock != NULL)) {\r
+      RemoveEntryList (&EraseBlock->Link);\r
+      if (EraseBlock->Event != NULL) {\r
+        gBS->CloseEvent (EraseBlock->Event);\r
+      }\r
+      FreePool (EraseBlock);\r
+    }\r
+  } else {\r
+    //\r
+    // For synchronous operation, free request whatever the execution result is.\r
+    //\r
+    if (EraseBlock != NULL) {\r
+      RemoveEntryList (&EraseBlock->Link);\r
+      FreePool (EraseBlock);\r
+    }\r
+  }\r
+\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Erase a specified number of device blocks.\r
+\r
+  @param[in]       This           Indicates a pointer to the calling context.\r
+  @param[in]       MediaId        The media ID that the erase request is for.\r
+  @param[in]       Lba            The starting logical block address to be\r
+                                  erased. The caller is responsible for erasing\r
+                                  only legitimate locations.\r
+  @param[in, out]  Token          A pointer to the token associated with the\r
+                                  transaction.\r
+  @param[in]       Size           The size in bytes to be erased. This must be\r
+                                  a multiple of the physical block size of the\r
+                                  device.\r
+\r
+  @retval EFI_SUCCESS             The erase request was queued if Event is not\r
+                                  NULL. The data was erased correctly to the\r
+                                  device if the Event is NULL.to the device.\r
+  @retval EFI_WRITE_PROTECTED     The device cannot be erased due to write\r
+                                  protection.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting\r
+                                  to perform the erase operation.\r
+  @retval EFI_INVALID_PARAMETER   The erase request contains LBAs that are not\r
+                                  valid.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_MEDIA_CHANGED       The MediaId is not for the current media.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+EmmcEraseBlocks (\r
+  IN     EFI_ERASE_BLOCK_PROTOCOL      *This,\r
+  IN     UINT32                        MediaId,\r
+  IN     EFI_LBA                       Lba,\r
+  IN OUT EFI_ERASE_BLOCK_TOKEN         *Token,\r
+  IN     UINTN                         Size\r
+  )\r
+{\r
+  EFI_STATUS                            Status;\r
+  EFI_BLOCK_IO_MEDIA                    *Media;\r
+  UINTN                                 BlockSize;\r
+  UINTN                                 BlockNum;\r
+  EFI_LBA                               LastLba;\r
+  UINT8                                 PartitionConfig;\r
+  EMMC_PARTITION                        *Partition;\r
+  EMMC_DEVICE                           *Device;\r
+\r
+  Status    = EFI_SUCCESS;\r
+  Partition = EMMC_PARTITION_DATA_FROM_ERASEBLK (This);\r
+  Device    = Partition->Device;\r
+  Media     = &Partition->BlockMedia;\r
+\r
+  if (MediaId != Media->MediaId) {\r
+    return EFI_MEDIA_CHANGED;\r
+  }\r
+\r
+  if (Media->ReadOnly) {\r
+    return EFI_WRITE_PROTECTED;\r
+  }\r
+\r
+  //\r
+  // Check parameters.\r
+  //\r
+  BlockSize = Media->BlockSize;\r
+  if ((Size % BlockSize) != 0) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  BlockNum  = Size / BlockSize;\r
+  if ((Lba + BlockNum - 1) > Media->LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  if ((Token != NULL) && (Token->Event != NULL)) {\r
+    Token->TransactionStatus = EFI_SUCCESS;\r
+  }\r
+\r
+  LastLba = Lba + BlockNum - 1;\r
+\r
+  //\r
+  // Check if needs to switch partition access.\r
+  //\r
+  PartitionConfig = Device->ExtCsd.PartitionConfig;\r
+  if ((PartitionConfig & 0x7) != Partition->PartitionType) {\r
+    PartitionConfig &= (UINT8)~0x7;\r
+    PartitionConfig |= Partition->PartitionType;\r
+    Status = EmmcSetExtCsd (Partition, OFFSET_OF (EMMC_EXT_CSD, PartitionConfig), PartitionConfig, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);\r
+    if (EFI_ERROR (Status)) {\r
+      return Status;\r
+    }\r
+    Device->ExtCsd.PartitionConfig = PartitionConfig;\r
+  }\r
+\r
+  Status = EmmcEraseBlockStart (Partition, Lba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = EmmcEraseBlockEnd (Partition, LastLba, (EFI_BLOCK_IO2_TOKEN*)Token, FALSE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  Status = EmmcEraseBlock (Partition, (EFI_BLOCK_IO2_TOKEN*)Token, TRUE);\r
+  if (EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  DEBUG ((EFI_D_ERROR, "EmmcEraseBlocks(): Lba 0x%x BlkNo 0x%x Event %p with %r\n", Lba, BlockNum, Token->Event, Status));\r
+\r
+  return Status;\r
+}\r
+\r