1 // 2 // Copyright (c) 2010-2024 Antmicro 3 // 4 // This file is licensed under the MIT License. 5 // Full license text is available in 'licenses/MIT.txt'. 6 // 7 using System; 8 using System.Linq; 9 using Antmicro.Renode.Debugging; 10 using Antmicro.Renode.Utilities; 11 12 namespace Antmicro.Renode.Peripherals.SD 13 { 14 public static class SDHelpers 15 { TypeFromCapacity(ulong capacity)16 public static CardType TypeFromCapacity(ulong capacity) 17 { 18 if(capacity <= 2.GB()) 19 { 20 return CardType.StandardCapacity_SC; 21 } 22 else if(capacity <= 32.GB()) 23 { 24 return CardType.HighCapacity_HC; 25 } 26 else if(capacity <= 2.TB()) 27 { 28 return CardType.ExtendedCapacity_XC; 29 } 30 else 31 { 32 throw new ArgumentException($"Unexpected capacity: {capacity}"); 33 } 34 } 35 BlockLengthInBytes(BlockLength blockSize)36 public static uint BlockLengthInBytes(BlockLength blockSize) 37 { 38 return blockSize == BlockLength.Undefined 39 ? 0 40 : (uint)(1 << (int)blockSize); 41 } 42 SeekForCapacityParameters(long capacity, BlockLength blockSize = BlockLength.Undefined)43 public static SDCapacityParameters SeekForCapacityParameters(long capacity, BlockLength blockSize = BlockLength.Undefined) 44 { 45 DebugHelper.Assert((blockSize == BlockLength.Undefined) || (capacity % BlockLengthInBytes(blockSize) == 0)); 46 47 if(blockSize != BlockLength.Undefined) 48 { 49 if(TryFindParameters(capacity, blockSize, out var result)) 50 { 51 return result; 52 } 53 } 54 else 55 { 56 foreach(BlockLength possibleBlockSize in Enum.GetValues(typeof(BlockLength))) 57 { 58 if(possibleBlockSize == BlockLength.Undefined) 59 { 60 continue; 61 } 62 63 if(TryFindParameters(capacity, possibleBlockSize, out var result)) 64 { 65 return result; 66 } 67 } 68 } 69 70 // We should never reach here. 71 throw new InvalidOperationException($"Could not calculate capacity parameters for given arguments: capacity={capacity}, blockSize={blockSize}"); 72 } 73 TryFindParameters(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)74 private static bool TryFindParameters(long capacity, BlockLength blockSize, out SDCapacityParameters parameters) 75 { 76 switch(TypeFromCapacity((ulong)capacity)) 77 { 78 case CardType.StandardCapacity_SC: 79 return TryFindParametersStandardCapacity(capacity, blockSize, out parameters); 80 81 case CardType.HighCapacity_HC: 82 case CardType.ExtendedCapacity_XC: 83 return TryFindParametersHighCapacity(capacity, blockSize, out parameters); 84 85 default: 86 // unsupported type 87 parameters = default(SDCapacityParameters); 88 return false; 89 } 90 } 91 TryFindParametersStandardCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)92 private static bool TryFindParametersStandardCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters) 93 { 94 // cSize has only 12 bits, hence the limit 95 const int MaxCSizeValue = (1 << 12) - 1; 96 97 var numberOfBlocks = (int)(capacity >> (int)blockSize); 98 99 foreach(var multiplierEncoded in Enum.GetValues(typeof(SizeMultiplier)) 100 .Cast<SizeMultiplier>() 101 .Where(x => x != SizeMultiplier.Undefined) 102 .OrderByDescending(x => (int)x)) 103 { 104 var multiplierValue = 1 << ((int)multiplierEncoded + 2); 105 if(multiplierValue > numberOfBlocks) 106 { 107 // we are looking for the highest possible multiplier that is lower-or-equal than numberOfBlocks 108 continue; 109 } 110 111 var cSize = (long)(numberOfBlocks / multiplierValue) - 1; 112 if(cSize > MaxCSizeValue) 113 { 114 // if for the highest possible multiplier cSize is still too high, 115 // checking smaller multipliers won't help; we can back-off immediately 116 break; 117 } 118 119 parameters = new SDCapacityParameters(blockSize, multiplierEncoded, cSize); 120 return true; 121 } 122 123 parameters = default(SDCapacityParameters); 124 return false; 125 } 126 TryFindParametersHighCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters)127 private static bool TryFindParametersHighCapacity(long capacity, BlockLength blockSize, out SDCapacityParameters parameters) 128 { 129 // block sizes other than 512 are not allowed in the HC/XC modes 130 if(blockSize != BlockLength.Block512) 131 { 132 parameters = default(SDCapacityParameters); 133 return false; 134 } 135 136 var cSize = (capacity / 1024 / 512) - 1; 137 138 // for HC/XC cards multiplier is not used 139 parameters = new SDCapacityParameters(BlockLength.Block512, SizeMultiplier.Undefined, cSize); 140 return true; 141 } 142 } 143 144 public enum SizeMultiplier 145 { 146 Multiplier4 = 0, 147 Multiplier8 = 1, 148 Multiplier16 = 2, 149 Multiplier32 = 3, 150 Multiplier64 = 4, 151 Multiplier128 = 5, 152 Multiplier256 = 6, 153 Multiplier512 = 7, 154 // other values are reserved 155 Undefined = int.MaxValue 156 } 157 158 public enum CardType 159 { 160 StandardCapacity_SC, 161 HighCapacity_HC, 162 ExtendedCapacity_XC 163 } 164 165 public enum BlockLength 166 { 167 Block512 = 9, 168 Block1024 = 10, 169 Block2048 = 11, 170 // other values are reserved 171 Undefined = int.MaxValue 172 } 173 174 public struct SDCapacityParameters 175 { SDCapacityParametersAntmicro.Renode.Peripherals.SD.SDCapacityParameters176 public SDCapacityParameters(BlockLength blockSize, SizeMultiplier multiplier, long deviceSize) 177 { 178 this.BlockSize = blockSize; 179 this.Multiplier = multiplier; 180 this.DeviceSize = deviceSize; 181 } 182 ToStringAntmicro.Renode.Peripherals.SD.SDCapacityParameters183 public override string ToString() 184 { 185 return $"[BlockSize={BlockSize}, Multiplier={Multiplier}, DeviceSize={DeviceSize}, MemoryCapacity={MemoryCapacity}]"; 186 } 187 188 public BlockLength BlockSize { get; } 189 public SizeMultiplier Multiplier { get; } 190 public long DeviceSize { get; } 191 192 public long MemoryCapacity 193 { 194 get 195 { 196 // HC/XC SD cards do not use Multiplier to describe size 197 // but calculate the it by simply multiplying `DeviceSize + 1` by 512Kb; 198 // we internally mark this by setting Multipler to Undefined 199 if(Multiplier == SizeMultiplier.Undefined) 200 { 201 DebugHelper.Assert(BlockSize == BlockLength.Block512); 202 return (DeviceSize + 1) * 512.KB(); 203 } 204 else 205 { 206 return (DeviceSize + 1) * (1L << ((int)Multiplier + (int)BlockSize + 2)); 207 } 208 } 209 } 210 211 212 } 213 } 214