/** 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; }
/** 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; }