/**
  Switch the bus width to specified width.

  Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.9 and SD Host Controller
  Simplified Spec 3.0 Figure 3-7 for details.

  @param[in] PciIo          A pointer to the EFI_PCI_IO_PROTOCOL instance.
  @param[in] PassThru       A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] Rca            The relative device address to be assigned.
  @param[in] IsDdr          If TRUE, use dual data rate data simpling method. Otherwise
                            use single data rate data simpling method.
  @param[in] BusWidth       The bus width to be set, it could be 4 or 8.

  @retval EFI_SUCCESS       The operation is done correctly.
  @retval Others            The operation fails.

**/
EFI_STATUS
EmmcSwitchBusWidth (
  IN EFI_PCI_IO_PROTOCOL                *PciIo,
  IN EFI_SD_MMC_PASS_THRU_PROTOCOL      *PassThru,
  IN UINT8                              Slot,
  IN UINT16                             Rca,
  IN BOOLEAN                            IsDdr,
  IN UINT8                              BusWidth
  )
{
  EFI_STATUS          Status;
  UINT8               Access;
  UINT8               Index;
  UINT8               Value;
  UINT8               CmdSet;
  UINT32              DevStatus;

  //
  // Write Byte, the Value field is written into the byte pointed by Index.
  //
  Access = 0x03;
  Index  = OFFSET_OF (EMMC_EXT_CSD, BusWidth);
  if (BusWidth == 4) {
    Value = 1;
  } else if (BusWidth == 8) {
    Value = 2;
  } else {
    return EFI_INVALID_PARAMETER;
  }

  if (IsDdr) {
    Value += 4;
  }

  CmdSet = 0;
  Status = EmmcSwitch (PassThru, Slot, Access, Index, Value, CmdSet);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusWidth: Switch to bus width %d fails with %r\n", BusWidth, Status));
    return Status;
  }

  Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusWidth: Send status fails with %r\n", Status));
    return Status;
  }
  //
  // Check the switch operation is really successful or not.
  //
  if ((DevStatus & BIT7) != 0) {
    DEBUG ((DEBUG_ERROR, "EmmcSwitchBusWidth: The switch operation fails as DevStatus is 0x%08x\n", DevStatus));
    return EFI_DEVICE_ERROR;
  }

  Status = SdMmcHcSetBusWidth (PciIo, Slot, BusWidth);

  return Status;
}
/**
  Switch the clock frequency to the specified value.

  Refer to EMMC Electrical Standard Spec 5.1 Section 6.6 and SD Host Controller
  Simplified Spec 3.0 Figure 3-3 for details.

  @param[in] PciIo          A pointer to the EFI_PCI_IO_PROTOCOL instance.
  @param[in] PassThru       A pointer to the EFI_SD_MMC_PASS_THRU_PROTOCOL instance.
  @param[in] Slot           The slot number of the SD card to send the command to.
  @param[in] Rca            The relative device address to be assigned.
  @param[in] HsTiming       The value to be written to HS_TIMING field of EXT_CSD register.
  @param[in] ClockFreq      The max clock frequency to be set, the unit is MHz.

  @retval EFI_SUCCESS       The operation is done correctly.
  @retval Others            The operation fails.

**/
EFI_STATUS
EmmcSwitchClockFreq (
  IN EFI_PCI_IO_PROTOCOL                *PciIo,
  IN EFI_SD_MMC_PASS_THRU_PROTOCOL      *PassThru,
  IN UINT8                              Slot,
  IN UINT16                             Rca,
  IN UINT8                              HsTiming,
  IN UINT32                             ClockFreq
  )
{
  EFI_STATUS                Status;
  UINT8                     Access;
  UINT8                     Index;
  UINT8                     Value;
  UINT8                     CmdSet;
  UINT32                    DevStatus;
  SD_MMC_HC_PRIVATE_DATA    *Private;

  Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru);
  //
  // Write Byte, the Value field is written into the byte pointed by Index.
  //
  Access = 0x03;
  Index  = OFFSET_OF (EMMC_EXT_CSD, HsTiming);
  Value  = HsTiming;
  CmdSet = 0;

  Status = EmmcSwitch (PassThru, Slot, Access, Index, Value, CmdSet);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSwitchClockFreq: Switch to hstiming %d fails with %r\n", HsTiming, Status));
    return Status;
  }

  Status = EmmcSendStatus (PassThru, Slot, Rca, &DevStatus);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSwitchClockFreq: Send status fails with %r\n", Status));
    return Status;
  }
  //
  // Check the switch operation is really successful or not.
  //
  if ((DevStatus & BIT7) != 0) {
    DEBUG ((DEBUG_ERROR, "EmmcSwitchClockFreq: The switch operation fails as DevStatus is 0x%08x\n", DevStatus));
    return EFI_DEVICE_ERROR;
  }
  //
  // Convert the clock freq unit from MHz to KHz.
  //
  Status = SdMmcHcClockSupply (PciIo, Slot, ClockFreq * 1000, Private->Capability[Slot]);

  return Status;
}
Esempio n. 3
0
/**
  Discover all partitions in the EMMC device.

  @param[in] Device          The pointer to the EMMC_DEVICE data structure.

  @retval EFI_SUCCESS        All the partitions in the device are successfully enumerated.
  @return Others             Some error occurs when enumerating the partitions.

**/
EFI_STATUS
DiscoverAllPartitions (
  IN EMMC_DEVICE             *Device
  )
{
  EFI_STATUS                        Status;
  EMMC_PARTITION                    *Partition;
  EMMC_CSD                          *Csd;
  EMMC_CID                          *Cid;
  EMMC_EXT_CSD                      *ExtCsd;
  UINT8                             Slot;
  UINT64                            Capacity;
  UINT32                            DevStatus;
  UINT8                             Index;
  UINT32                            SecCount;
  UINT32                            GpSizeMult;

  Slot     = Device->Slot;

  Status = EmmcSendStatus (Device, Slot + 1, &DevStatus);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  //
  // Deselect the device to force it enter stby mode before getting CSD
  // register content.
  // Note here we don't judge return status as some EMMC devices return
  // error but the state has been stby.
  //
  EmmcSelect (Device, 0);

  Status = EmmcSendStatus (Device, Slot + 1, &DevStatus);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Csd    = &Device->Csd;
  Status = EmmcGetCsd (Device, Slot + 1, Csd);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  DumpCsd (Csd);

  if ((Csd->CSizeLow | Csd->CSizeHigh << 2) == 0xFFF) {
    Device->SectorAddressing = TRUE;
  } else {
    Device->SectorAddressing = FALSE;
  }

  Cid    = &Device->Cid;
  Status = EmmcGetCid (Device, Slot + 1, Cid);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  Status = EmmcSelect (Device, Slot + 1);
  if (EFI_ERROR (Status)) {
    return Status;
  }

  ExtCsd = &Device->ExtCsd;
  Status = EmmcGetExtCsd (Device, ExtCsd);
  if (EFI_ERROR (Status)) {
    return Status;
  }
  DumpExtCsd (ExtCsd);

  if (ExtCsd->ExtCsdRev < 5) {
    DEBUG ((EFI_D_ERROR, "The EMMC device version is too low, we don't support!!!\n"));
    return EFI_UNSUPPORTED;
  }

  if ((ExtCsd->PartitioningSupport & BIT0) != BIT0) {
    DEBUG ((EFI_D_ERROR, "The EMMC device doesn't support Partition Feature!!!\n"));
    return EFI_UNSUPPORTED;
  }

  for (Index = 0; Index < EMMC_MAX_PARTITIONS; Index++) {
    Partition = &Device->Partition[Index];
    CopyMem (Partition, &mEmmcPartitionTemplate, sizeof (EMMC_PARTITION));
    Partition->Device             = Device;
    InitializeListHead (&Partition->Queue);
    Partition->BlockIo.Media      = &Partition->BlockMedia;
    Partition->BlockIo2.Media     = &Partition->BlockMedia;
    Partition->PartitionType      = Index;
    Partition->BlockMedia.IoAlign = Device->Private->PassThru->IoAlign;
    Partition->BlockMedia.BlockSize      = 0x200;
    Partition->BlockMedia.LastBlock      = 0x00;
    Partition->BlockMedia.RemovableMedia = FALSE;
    Partition->BlockMedia.MediaPresent     = TRUE;
    Partition->BlockMedia.LogicalPartition = FALSE;

    switch (Index) {
      case EmmcPartitionUserData:
        SecCount = *(UINT32*)&ExtCsd->SecCount;
        Capacity = MultU64x32 ((UINT64) SecCount, 0x200);
        break;
      case EmmcPartitionBoot1:
      case EmmcPartitionBoot2:
        Capacity = ExtCsd->BootSizeMult * SIZE_128KB;
        break;
      case EmmcPartitionRPMB:
        Capacity = ExtCsd->RpmbSizeMult * SIZE_128KB;
        break;
      case EmmcPartitionGP1:
        GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[0] | (ExtCsd->GpSizeMult[1] << 8) | (ExtCsd->GpSizeMult[2] << 16));
        Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB);
        break;
      case EmmcPartitionGP2:
        GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[3] | (ExtCsd->GpSizeMult[4] << 8) | (ExtCsd->GpSizeMult[5] << 16));
        Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB);
        break;
      case EmmcPartitionGP3:
        GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[6] | (ExtCsd->GpSizeMult[7] << 8) | (ExtCsd->GpSizeMult[8] << 16));
        Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB);
        break;
      case EmmcPartitionGP4:
        GpSizeMult = (UINT32)(ExtCsd->GpSizeMult[9] | (ExtCsd->GpSizeMult[10] << 8) | (ExtCsd->GpSizeMult[11] << 16));
        Capacity = MultU64x32 (MultU64x32 (MultU64x32 ((UINT64)GpSizeMult, ExtCsd->HcWpGrpSize), ExtCsd->HcEraseGrpSize), SIZE_512KB);
        break;
      default:
        ASSERT (FALSE);
        return EFI_INVALID_PARAMETER;
    }

    if (Capacity != 0) {
      Partition->Enable = TRUE;
      Partition->BlockMedia.LastBlock = DivU64x32 (Capacity, Partition->BlockMedia.BlockSize) - 1;
    }

    if ((ExtCsd->EraseGroupDef & BIT0) == 0) {
      if (Csd->WriteBlLen < 9) {
        Partition->EraseBlock.EraseLengthGranularity = 1;
      } else {
        Partition->EraseBlock.EraseLengthGranularity = (Csd->EraseGrpMult + 1) * (Csd->EraseGrpSize + 1) * (1 << (Csd->WriteBlLen - 9));
      }
    } else {
      Partition->EraseBlock.EraseLengthGranularity = 1024 * ExtCsd->HcEraseGrpSize;
    }
  }

  return EFI_SUCCESS;
}