/**
  Switch the high speed timing according to request.

  Refer to EMMC Electrical Standard Spec 5.1 Section 6.6.8 and SD Host Controller
  Simplified Spec 3.0 Figure 2-29 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.

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

**/
EFI_STATUS
EmmcSetBusMode (
  IN EFI_PCI_IO_PROTOCOL                *PciIo,
  IN EFI_SD_MMC_PASS_THRU_PROTOCOL      *PassThru,
  IN UINT8                              Slot,
  IN UINT16                             Rca
  )
{
  EFI_STATUS                    Status;
  EMMC_CSD                      Csd;
  EMMC_EXT_CSD                  ExtCsd;
  UINT8                         HsTiming;
  BOOLEAN                       IsDdr;
  UINT32                        ClockFreq;
  UINT8                         BusWidth;
  SD_MMC_HC_PRIVATE_DATA        *Private;

  Private = SD_MMC_HC_PRIVATE_FROM_THIS (PassThru);

  Status = EmmcGetCsd (PassThru, Slot, Rca, &Csd);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetCsd fails with %r\n", Status));
    return Status;
  }

  Status = EmmcSelect (PassThru, Slot, Rca);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: Select fails with %r\n", Status));
    return Status;
  }

  ASSERT (Private->Capability[Slot].BaseClkFreq != 0);
  //
  // Check if the Host Controller support 8bits bus width.
  //
  if (Private->Capability[Slot].BusWidth8 != 0) {
    BusWidth = 8;
  } else {
    BusWidth = 4;
  }
  //
  // Get Deivce_Type from EXT_CSD register.
  //
  Status = EmmcGetExtCsd (PassThru, Slot, &ExtCsd);
  if (EFI_ERROR (Status)) {
    DEBUG ((DEBUG_ERROR, "EmmcSetBusMode: GetExtCsd fails with %r\n", Status));
    return Status;
  }
  //
  // Calculate supported bus speed/bus width/clock frequency.
  //
  HsTiming  = 0;
  IsDdr     = FALSE;
  ClockFreq = 0;
  if (((ExtCsd.DeviceType & (BIT4 | BIT5))  != 0) && (Private->Capability[Slot].Sdr104 != 0)) {
    HsTiming  = 2;
    IsDdr     = FALSE;
    ClockFreq = 200;
  } else if (((ExtCsd.DeviceType & (BIT2 | BIT3))  != 0) && (Private->Capability[Slot].Ddr50 != 0)) {
    HsTiming  = 1;
    IsDdr     = TRUE;
    ClockFreq = 52;
  } else if (((ExtCsd.DeviceType & BIT1)  != 0) && (Private->Capability[Slot].HighSpeed != 0)) {
    HsTiming  = 1;
    IsDdr     = FALSE;
    ClockFreq = 52;
  } else if (((ExtCsd.DeviceType & BIT0)  != 0) && (Private->Capability[Slot].HighSpeed != 0)) {
    HsTiming  = 1;
    IsDdr     = FALSE;
    ClockFreq = 26;
  }
  //
  // Check if both of the device and the host controller support HS400 DDR mode.
  //
  if (((ExtCsd.DeviceType & (BIT6 | BIT7))  != 0) && (Private->Capability[Slot].Hs400 != 0)) {
    //
    // The host controller supports 8bits bus.
    //
    ASSERT (BusWidth == 8);
    HsTiming  = 3;
    IsDdr     = TRUE;
    ClockFreq = 200;
  }

  if ((ClockFreq == 0) || (HsTiming == 0)) {
    //
    // Continue using default setting.
    //
    return EFI_SUCCESS;
  }

  DEBUG ((DEBUG_INFO, "EmmcSetBusMode: HsTiming %d ClockFreq %d BusWidth %d Ddr %a\n", HsTiming, ClockFreq, BusWidth, IsDdr ? "TRUE":"FALSE"));

  if (HsTiming == 3) {
    //
    // Execute HS400 timing switch procedure
    //
    Status = EmmcSwitchToHS400 (PciIo, PassThru, Slot, Rca, ClockFreq);
  } else if (HsTiming == 2) {
    //
    // Execute HS200 timing switch procedure
    //
    Status = EmmcSwitchToHS200 (PciIo, PassThru, Slot, Rca, ClockFreq, BusWidth);
  } else {
    //
    // Execute High Speed timing switch procedure
    //
    Status = EmmcSwitchToHighSpeed (PciIo, PassThru, Slot, Rca, ClockFreq, IsDdr, BusWidth);
  }

  DEBUG ((DEBUG_INFO, "EmmcSetBusMode: Switch to %a %r\n", (HsTiming == 3) ? "HS400" : ((HsTiming == 2) ? "HS200" : "HighSpeed"), Status));

  return Status;
}
Esempio n. 2
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;
}