1 // 2 // Copyright (c) 2010-2023 Antmicro 3 // Copyright (c) 2011-2015 Realtime Embedded 4 // 5 // This file is licensed under the MIT License. 6 // Full license text is available in 'licenses/MIT.txt'. 7 // 8 using System; 9 using Antmicro.Renode.Peripherals.Bus; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Peripherals.DMA; 13 using System.Collections.Generic; 14 15 namespace Antmicro.Renode.Peripherals.SD 16 { 17 public class SunxiMMC : MMCController, IDoubleWordPeripheral, IKnownSize 18 { SunxiMMC(IMachine machine)19 public SunxiMMC(IMachine machine) : base(machine) 20 { 21 SetupRegisters(); 22 IRQ = new GPIO(); 23 dmaEngine = new DmaEngine(machine.GetSystemBus(this)); 24 } 25 ReadDoubleWord(long offset)26 public uint ReadDoubleWord(long offset) 27 { 28 switch((Registers)offset) 29 { 30 case Registers.InterruptMaskRegister: 31 return interruptMask; 32 case Registers.RawInterruptStatusRegister: 33 return rawInterruptStatus; 34 case Registers.DescriptorListBaseAddress: 35 return descriptorListBaseAddress; 36 case Registers.CommandArgumentRegister: 37 return commandArgument; 38 case Registers.ClockControlRegister: 39 case Registers.MaskedInterruptStatusRegister: 40 return rawInterruptStatus & interruptMask; 41 case Registers.ResponseRegister0: 42 return responseRegisters[0]; 43 case Registers.ResponseRegister1: 44 return responseRegisters[1]; 45 case Registers.ResponseRegister2: 46 return responseRegisters[2]; 47 case Registers.ResponseRegister3: 48 return responseRegisters[3]; 49 case Registers.ByteCountRegister: 50 return (uint)ByteCount; 51 case Registers.CommandRegister: 52 return commandRegister.Read(); 53 case Registers.DmacStatus: 54 return dmacStatusRegister.Read(); 55 default: 56 return generalRegisters.Read(offset); 57 } 58 } 59 WriteDoubleWord(long offset, uint value)60 public void WriteDoubleWord(long offset, uint value) 61 { 62 switch((Registers)offset) 63 { 64 case Registers.InterruptMaskRegister: 65 interruptMask = value; 66 break; 67 case Registers.RawInterruptStatusRegister: 68 rawInterruptStatus &= ~value; // write one to clear 69 Update(); 70 break; 71 case Registers.DescriptorListBaseAddress: 72 descriptorListBaseAddress = value; 73 break; 74 case Registers.CommandArgumentRegister: 75 commandArgument = value; 76 break; 77 case Registers.ByteCountRegister: 78 ByteCount = (int)value; 79 break; 80 case Registers.CommandRegister: 81 commandRegister.Write(offset, value); 82 break; 83 case Registers.DmacStatus: 84 dmacStatusRegister.Write(offset, value); 85 break; 86 default: 87 generalRegisters.Write(offset, value); 88 break; 89 } 90 } 91 Reset()92 public override void Reset() 93 { 94 generalRegisters.Reset(); 95 interruptMask = 0; 96 descriptorListBaseAddress = 0; 97 commandArgument = 0; 98 commandRegister.Reset(); 99 dmacStatusRegister.Reset(); 100 } 101 102 public long Size 103 { 104 get 105 { 106 return 0x1000; 107 } 108 } 109 110 public GPIO IRQ 111 { 112 get; 113 private set; 114 } 115 TransferDataFromCard(uint cardOffset, int bytes)116 protected override void TransferDataFromCard(uint cardOffset, int bytes) 117 { 118 byte[] data = ReadFromCard(cardOffset, bytes); 119 DmaTransfer(data, bytes, DataDirection.ReadFromSD); 120 } 121 TransferDataToCard(uint cardOffset, int bytes)122 protected override void TransferDataToCard(uint cardOffset, int bytes) 123 { 124 var data = new byte[bytes]; 125 DmaTransfer(data, bytes, DataDirection.WriteToSD); 126 WriteToCard(cardOffset, data); 127 } 128 SendSdConfigurationValue()129 protected override void SendSdConfigurationValue() 130 { 131 byte[] data = BitConverter.GetBytes(RegisteredPeripheral.SendSdConfigurationValue()); 132 DmaTransfer(data, 8, DataDirection.ReadFromSD); 133 } 134 SetupRegisters()135 private void SetupRegisters() 136 { 137 commandRegister = new DoubleWordRegister(this); 138 dmacStatusRegister = new DoubleWordRegister(this); 139 startCommandFlag = commandRegister.DefineFlagField(31, changeCallback: OnStartCommand); 140 sendInitSequence = commandRegister.DefineFlagField(15); 141 transferDirection = commandRegister.DefineFlagField(10); 142 dataTransfer = commandRegister.DefineFlagField(9); 143 receiveResponse = commandRegister.DefineFlagField(6); 144 commandIndex = commandRegister.DefineValueField(0, 6); 145 146 responseRegisters = new uint[4]; 147 148 receiveInterrupt = dmacStatusRegister.DefineFlagField(1, FieldMode.WriteOneToClear | FieldMode.Read); 149 transmitInterrupt = dmacStatusRegister.DefineFlagField(0, FieldMode.WriteOneToClear | FieldMode.Read); 150 generalRegisters = new DoubleWordRegisterCollection(this, new Dictionary<long, DoubleWordRegister>() { 151 {(long)Registers.ControlRegister, new DoubleWordRegister(this).WithFlag(0, changeCallback: (oldValue, newValue) => {if(newValue) Reset();}).WithFlag(2).WithFlag(4).WithFlag(5)}, 152 {(long)Registers.BlockSizeRegister, new DoubleWordRegister(this, 0x200).WithValueField(0, 16, changeCallback: (oldValue, newValue) => BlockSize = (int)newValue)}, 153 {(long)Registers.DmacInterruptEnable, DoubleWordRegister.CreateRWRegister()}, 154 }); 155 } 156 DmaTransfer(byte[] data, int bytes, DataDirection direction)157 private void DmaTransfer(byte[] data, int bytes, DataDirection direction) 158 { 159 Place source, destination; 160 uint currentDescriptorAddress = descriptorListBaseAddress; 161 int bytesTransferred = 0; 162 163 while(bytesTransferred < bytes) 164 { 165 166 int bytesLeft = bytes - bytesTransferred; 167 var currentDescriptor = new SunxiDMADescriptor(currentDescriptorAddress, dmaEngine); 168 int bytesToTransfer = currentDescriptor.BufferSize > bytesLeft ? bytesLeft : (int) currentDescriptor.BufferSize; 169 170 if(direction == DataDirection.ReadFromSD) 171 { 172 destination = currentDescriptor.BufferAddress; 173 source = new Place(data, bytesTransferred); 174 } 175 else 176 { 177 destination = new Place(data, bytesTransferred); 178 source = currentDescriptor.BufferAddress; 179 } 180 181 var request = new Request(source, destination, bytesToTransfer, TransferType.DoubleWord, TransferType.DoubleWord); 182 dmaEngine.IssueCopy(request); 183 currentDescriptor.Release(); 184 bytesTransferred += bytesToTransfer; 185 currentDescriptorAddress = currentDescriptor.NextDescriptor; 186 } 187 } 188 Update()189 private void Update() 190 { 191 if((rawInterruptStatus & interruptMask) != 0) 192 { 193 IRQ.Set(); 194 } 195 else 196 { 197 IRQ.Unset(); 198 } 199 } 200 OnStartCommand(bool oldValue, bool newValue)201 private void OnStartCommand(bool oldValue, bool newValue) 202 { 203 if(newValue) 204 { 205 responseRegisters = ExecuteCommand((Commands)commandIndex.Value, commandArgument, sendInitSequence.Value, dataTransfer.Value); 206 startCommandFlag.Value = false; 207 208 if(dataTransfer.Value) 209 { 210 if(transferDirection.Value) 211 { 212 transmitInterrupt.Value = true; 213 } 214 else 215 { 216 receiveInterrupt.Value = true; 217 } 218 if((interruptMask & (int)Interrupts.DataTransferComplete) != 0) 219 { 220 rawInterruptStatus |= (int)Interrupts.DataTransferComplete; 221 } 222 else if((interruptMask & (int)Interrupts.AutoCommandDone) != 0) 223 { 224 rawInterruptStatus |= (int)Interrupts.AutoCommandDone; 225 } 226 } 227 else 228 { 229 if(receiveResponse.Value || sendInitSequence.Value) 230 { 231 var cmd = (Commands)commandIndex.Value; 232 if(cmd == Commands.IoSendOpCond || cmd == Commands.SendOpCond || cmd == Commands.IoRwDirect) 233 { 234 rawInterruptStatus |= (int)Interrupts.BootAck; 235 } 236 rawInterruptStatus |= (int)Interrupts.CommandComplete; 237 } 238 } 239 Update(); 240 } 241 } 242 243 private DoubleWordRegisterCollection generalRegisters; 244 private DoubleWordRegister commandRegister, dmacStatusRegister; 245 private IFlagRegisterField startCommandFlag, receiveResponse, sendInitSequence, receiveInterrupt, transmitInterrupt, dataTransfer, transferDirection; 246 private IValueRegisterField commandIndex; 247 private uint commandArgument, descriptorListBaseAddress, rawInterruptStatus, interruptMask; 248 private uint[] responseRegisters; 249 250 private readonly DmaEngine dmaEngine; 251 252 private enum Registers 253 { 254 ControlRegister = 0x00, 255 ClockControlRegister = 0x04, 256 TimeOutRegister = 0x08, 257 BusWidthRegister = 0x0c, 258 BlockSizeRegister = 0x10, 259 ByteCountRegister = 0x14, 260 CommandRegister = 0x18, 261 CommandArgumentRegister = 0x1c, 262 ResponseRegister0 = 0x20, 263 ResponseRegister1 = 0x24, 264 ResponseRegister2 = 0x28, 265 ResponseRegister3 = 0x2c, 266 InterruptMaskRegister = 0x30, 267 MaskedInterruptStatusRegister = 0x34, 268 RawInterruptStatusRegister = 0x38, 269 StatusRegister = 0x3c, 270 FifoWaterLevelRegister = 0x40, 271 FifoFunctionSelectRegister = 0x44, 272 DebugEnableRegister = 0x50, 273 BusModeControl = 0x80, 274 DescriptorListBaseAddress = 0x84, 275 DmacStatus = 0x88, 276 DmacInterruptEnable = 0x8c, 277 ReadWriteFifo = 0x100 278 } 279 280 [Flags] 281 private enum Interrupts 282 { 283 ResponseError = (1 << 1), 284 CommandComplete = (1 << 2), 285 DataTransferComplete = (1 << 3), 286 DataransmitRequest = (1 << 4), 287 DataReceiveRequest = (1 << 5), 288 ResponseCrcError = (1 << 6), 289 DataCrcError = (1 << 7), 290 ResponseTimeout = (1 << 8), 291 BootAck = (1 << 8), 292 DataTimeout = (1 << 9), 293 BootDataStart = (1 << 9), 294 DataStarvationTimeout = (1 << 10), 295 VoltageSwitchDone = (1 << 10), 296 FifoUnderrun = (1 << 11), 297 FifoOverflow = (1 << 11), 298 CommandBusy = (1 << 12), 299 IllegalWrite = (1 << 12), 300 DataStartError = (1 << 13), 301 AutoCommandDone = (1 << 14), 302 DataEndBitError = (1 << 15), 303 SdioInterrupt = (1 << 16), 304 CardInserted = (1 << 30), 305 CardRemoved = (1 << 31) 306 } 307 308 private enum DataDirection 309 { 310 WriteToSD, 311 ReadFromSD 312 } 313 314 private class SunxiDMADescriptor 315 { SunxiDMADescriptor(ulong address, DmaEngine dmaEngine)316 public SunxiDMADescriptor(ulong address, DmaEngine dmaEngine) 317 { 318 Address = address; 319 320 byte[] descriptorData = new byte[16]; 321 Request getDescriptorData = new Request(Address, new Place(descriptorData, 0), 16, 322 TransferType.DoubleWord, TransferType.DoubleWord); 323 324 dmaEngine.IssueCopy(getDescriptorData); 325 Status = BitConverter.ToUInt32(descriptorData, 0); 326 BufferSize = BitConverter.ToUInt32(descriptorData, 4); 327 BufferAddress = BitConverter.ToUInt32(descriptorData, 8); 328 NextDescriptor = BitConverter.ToUInt32(descriptorData, 12); 329 330 if(BufferSize == 0) // the driver assumes 0-sized blocks to be 64kB, which is inconsistent with the Allwinner user manual. 331 { 332 BufferSize = 0x10000; 333 } 334 } 335 Release()336 public void Release() 337 { 338 Status &= ~(1 << 31); 339 } 340 341 public uint BufferAddress 342 { 343 get; 344 private set; 345 } 346 public uint BufferSize 347 { 348 get; 349 private set; 350 } 351 public uint NextDescriptor 352 { 353 get; 354 private set; 355 } 356 public ulong Address 357 { 358 get; 359 private set; 360 } 361 public uint Status 362 { 363 get; 364 private set; 365 } 366 } 367 } 368 } 369