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