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.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Exceptions; 14 using Antmicro.Renode.Logging; 15 using Antmicro.Renode.Peripherals.Bus; 16 using Antmicro.Renode.Peripherals.DMA; 17 using Antmicro.Renode.Peripherals.Sensors; 18 using Antmicro.Renode.Storage.SCSI.Commands; 19 using Antmicro.Renode.Utilities; 20 21 namespace Antmicro.Renode.Peripherals.SD 22 { 23 /* 24 * minimal SDMMC model working with zephyr driver and STM32 HAL for FAT and EXT2: 25 * 26 * references: 27 * - https://www.st.com/resource/en/reference_manual/dm00124865.pdf 28 * Zephyr 29 * - https://github.com/zephyrproject-rtos/zephyr/blob/c008cbab1a05316139de191b0553ab6ccc0073ad/drivers/disk/sdmmc_stm32.c 30 * STM HAL 31 * - https://github.com/STMicroelectronics/stm32f7xx_hal_driver/blob/master/Src/stm32f7xx_hal_sd.c 32 * - https://github.com/STMicroelectronics/stm32f7xx_hal_driver/blob/master/Src/stm32f7xx_ll_sdmmc.c 33 */ 34 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord | AllowedTranslation.WordToDoubleWord)] 35 public class STM32SDMMC : NullRegistrationPointPeripheralContainer<SDCard>, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize 36 { STM32SDMMC(IMachine machine)37 public STM32SDMMC(IMachine machine) : base(machine) 38 { 39 IRQ = new GPIO(); 40 DMAReceive = new GPIO(); 41 RegistersCollection = new DoubleWordRegisterCollection(this); 42 responseFields = new IValueRegisterField[4]; 43 dataEndFlag = false; 44 internalBuffer = new Queue<byte>(); 45 InitializeRegisters(); 46 } 47 ReadDoubleWord(long offset)48 public uint ReadDoubleWord(long offset) 49 { 50 return RegistersCollection.Read(offset);; 51 } 52 WriteDoubleWord(long offset, uint value)53 public void WriteDoubleWord(long offset, uint value) 54 { 55 RegistersCollection.Write(offset, value); 56 } 57 Reset()58 public override void Reset() 59 { 60 RegistersCollection.Reset(); 61 responseFields[0].Value = 0; 62 responseFields[1].Value = 0; 63 responseFields[2].Value = 0; 64 responseFields[3].Value = 0; 65 RegisteredPeripheral?.Reset(); 66 internalBuffer.Clear(); 67 dataEndFlag = false; 68 rxFifoHalfFullFlag = false; 69 txFifoHalfEmptyFlag = false; 70 UpdateInterrupts(); 71 } 72 73 [IrqProvider] 74 public GPIO IRQ { get; private set; } 75 public GPIO DMAReceive { get; } 76 77 public long Size => 0x400; 78 79 public DoubleWordRegisterCollection RegistersCollection { get; } 80 InitializeRegisters()81 private void InitializeRegisters() 82 { 83 Registers.Power.Define(this) 84 .WithValueField(0, 2, name: "Power supply control bits (PWRCTRL)", valueProviderCallback: _ => 3) 85 .WithReservedBits(2, 30); 86 87 Registers.Arg.Define(this) 88 .WithValueField(0, 32, out commandArgument, name: "Command Argument (CMDARG)"); 89 90 Registers.Cmd.Define(this) 91 .WithEnumField(0, 6, out commandIndex, name: "Command Index (CMDINDEX)") 92 .WithEnumField<DoubleWordRegister, ResponseWidth>(6, 2, out var responseWidth, name: "Wait for response bits (WAITRESP)") 93 .WithTaggedFlag("CPSM waits for interrupt request (WAITINIT)", 8) 94 .WithTaggedFlag("CPSM Waits for ends of data transfer (WAITPEND)", 9) 95 .WithTaggedFlag("Command path state machine enable (CPSM)", 10) 96 .WithTaggedFlag("SD I/O suspend command (SDIOSuspend)", 11) 97 .WithReservedBits(12, 20) 98 .WithWriteCallback((_, value) => 99 { 100 var sdCard = RegisteredPeripheral; 101 if(sdCard == null) 102 { 103 this.Log(LogLevel.Warning, "Tried to send a command with index {0}, but no SD card is currently attached", commandIndex); 104 return; 105 } 106 var commandResult = sdCard.HandleCommand((uint) commandIndex.Value, (uint) commandArgument.Value); 107 108 /* in case of no response there's no need to do anything extra */ 109 switch(responseWidth.Value) 110 { 111 case ResponseWidth.LongResponse: 112 responseFields[3].Value = commandResult.AsUInt32(0); 113 responseFields[3].Value &= 0xFFFFFFFE; /* lsb is always = 0 */ 114 responseFields[2].Value = commandResult.AsUInt32(32); 115 responseFields[1].Value = commandResult.AsUInt32(64); 116 responseFields[0].Value = commandResult.AsUInt32(96); 117 break; 118 case ResponseWidth.ShortResponse: 119 responseFields[0].Value = commandResult.AsUInt32(0); 120 break; 121 } 122 ProcessCommand(sdCard, commandIndex.Value); 123 }); 124 125 Registers.Fifo.Define(this) 126 .WithValueField(0, 32, valueProviderCallback: _=> ReadBuffer()) 127 .WithReadCallback((_, __) => UpdateInterrupts()) 128 .WithWriteCallback((_, value) => 129 { 130 var sdCard = RegisteredPeripheral; 131 if(sdCard == null) 132 { 133 this.Log(LogLevel.Warning, "Tried to write to SD card, but no SD card is currently attached"); 134 return; 135 } 136 WriteCard(sdCard, value); 137 writeDataAmount -= 4; 138 this.Log(LogLevel.Debug, "Remaining data to write {0}", writeDataAmount); 139 if(writeDataAmount == 0) 140 { 141 txFifoHalfEmptyFlag = false; 142 dataEndFlag = true; 143 } 144 UpdateInterrupts(); 145 }); 146 147 Registers.Resp1.Define(this) 148 .WithValueField(0, 32, out responseFields[0], FieldMode.Read, name: "(CARDSTATUS1)"); 149 Registers.Resp2.Define(this) 150 .WithValueField(0, 32, out responseFields[1], FieldMode.Read, name: "(CARDSTATUS2)"); 151 Registers.Resp3.Define(this) 152 .WithValueField(0, 32, out responseFields[2], FieldMode.Read, name: "(CARDSTATUS3)"); 153 Registers.Resp4.Define(this) 154 .WithValueField(0, 32, out responseFields[3], FieldMode.Read, name: "(CARDSTATUS4)"); 155 156 Registers.RespCmd.Define(this) 157 .WithValueField(0, 6, name: "Response command index (RESPCMD)", valueProviderCallback: _ => (ulong)commandIndex.Value) 158 .WithReservedBits(6, 26) 159 .WithReadCallback((_, __) => UpdateInterrupts()); 160 161 Registers.Sta.Define(this) 162 .WithFlag(0, FieldMode.Read, name: "Command CRC check failed (CCRCFAIL)", valueProviderCallback: _ => RegisteredPeripheral == null) 163 .WithFlag(1, FieldMode.Read, name: "Data CRC check failed (DCRCFAIL)", valueProviderCallback: _ => RegisteredPeripheral == null) 164 .WithFlag(2, FieldMode.Read, name: "Command response timeout (CTIMEOUT)", valueProviderCallback: _ => RegisteredPeripheral == null) 165 .WithTaggedFlag("Data timeout (DTIMEOUT)", 3) 166 .WithTaggedFlag("Transmit FIFO underrun error (TXUNDERR)", 4) 167 .WithTaggedFlag("Receiverd FIFO overrun error (RXOVERR)", 5) 168 .WithFlag(6, FieldMode.Read, name: "Command response received (CMDREND)", valueProviderCallback: _ => RegisteredPeripheral != null) 169 .WithFlag(7, FieldMode.Read, name: "Command sent (CMDSENT)", valueProviderCallback: _ => RegisteredPeripheral != null) 170 .WithFlag(8, FieldMode.Read, name: "Data end (DATAEND)", valueProviderCallback: _ => dataEndFlag) 171 .WithReservedBits(9, 1) 172 .WithFlag(10, FieldMode.Read, name: "Data block sent/received (DBCKEND)", valueProviderCallback: _ => RegisteredPeripheral != null) 173 .WithFlag(11, FieldMode.Read, name: "Command transfer in progress (CMDACT)", valueProviderCallback: _ => false) 174 .WithTaggedFlag("Data transmit in progress (TXACT)", 12) 175 .WithTaggedFlag("Data receive in progress (RXACT)", 13) 176 .WithFlag(14, FieldMode.Read, name: "Transmit FIFO half empty (TXFIFOHE)", valueProviderCallback: _ => txFifoHalfEmptyFlag) 177 .WithFlag(15, FieldMode.Read, name: "Receive FIFO half full (RXFIFOHF)", valueProviderCallback: _ => rxFifoHalfFullFlag) 178 .WithTaggedFlag("Transmit FIFO full (TXFIFOF)", 16) 179 .WithTaggedFlag("Receive FIFO full (RXFIFOF)", 17) 180 .WithTaggedFlag("Transmit FIFO empty (TXFIFOE)", 18) 181 .WithTaggedFlag("Receive FIFO empty (RXFIFOE)", 19) 182 .WithTaggedFlag("Data available in transmit FIFO (TXDAVL)", 20) 183 .WithTaggedFlag("Data available in receive FIFO (RXDAVL)", 21) 184 .WithTaggedFlag("SDIO interrupt received (SDIOT)", 22) 185 .WithReservedBits(23, 9); 186 187 Registers.DLen.Define(this) 188 .WithValueField(0, 25, out dataLength, name: "Data length value (DATALENGTH)") 189 .WithReservedBits(25, 7); 190 191 Registers.Icr.Define(this) 192 .WithTaggedFlag("CCRCFAIL flag clear bit (CCRCFAILC)", 0) 193 .WithTaggedFlag("DCRCFAIL flag clear bit (DCRCFAILC)", 1) 194 .WithTaggedFlag("CTIMEOUT flag clear bit (CTIMEOUTC)", 2) 195 .WithTaggedFlag("DTIMEOUT flag clear bit (DTIMEOUTC)", 3) 196 .WithTaggedFlag("TXUNDERR flag clear bit (TXUNDERRC)", 4) 197 .WithTaggedFlag("RXOVERR flag clear bit (RXOVERRC)", 5) 198 .WithTaggedFlag("CMDREND flag clear bit (CMDRENDC)", 6) 199 .WithTaggedFlag("DCRCFAIL flag clear bit (DCRCFAILC)", 7) 200 .WithFlag(8, FieldMode.Write, name: "DATAEND flag clear bit (DATAENDC)", writeCallback: (_, value) => 201 { 202 if(!value) 203 { 204 return; 205 } 206 207 dataEndFlag = false; 208 UpdateInterrupts(); 209 }) 210 .WithReservedBits(9, 1) 211 .WithTaggedFlag("DBCKEND flag clear bit (DBCKENDC)", 10) 212 .WithReservedBits(11, 11) 213 .WithTaggedFlag("SDIOIT flag clear bit (SDIOITC)", 22) 214 .WithReservedBits(23, 9); 215 216 Registers.DCtrl.Define(this) 217 .WithTaggedFlag("Data transfer enabled bit (DTEN)", 0) 218 .WithTaggedFlag("Data transfer direction selection (DTDIR)", 1) 219 .WithTaggedFlag("Data transfer mode selection (DTMODE)", 2) 220 .WithFlag(3, out isDmaEnabled, name: "DMA enable bit") 221 .WithTag("Data block size (DBLOCKSIZE)", 4, 4) 222 .WithTaggedFlag("Read wait start (RWSTART)", 8) 223 .WithTaggedFlag("Read wait stop (RWSTOP)", 9) 224 .WithTaggedFlag("Read wait mode (RWMOD)", 10) 225 .WithTaggedFlag("SD I/O enable funtions (SDIOEN)", 11) 226 .WithReservedBits(12, 20); 227 228 Registers.Mask.Define(this) 229 .WithTaggedFlag("Command CRC fail interrupt enable (CCRCFAILIE)", 0) 230 .WithTaggedFlag("Data CRC fail interrupt enable (DCRCFAILIE)", 1) 231 .WithTaggedFlag("Command timeout interrupt enable (CTIMEOUTIE)", 2) 232 .WithTaggedFlag("Data transfer enabled bit (DTIMEOUTIE)", 3) 233 .WithTaggedFlag("Tx FIFO underrun error interrupt enable (TXUNDERRIE)", 4) 234 .WithTaggedFlag("Rx FIFO overrun error interrupt enable (RXOVERRIE)", 5) 235 .WithTaggedFlag("Command response received interrupt enable (CMDRENDIE)", 6) 236 .WithTaggedFlag("Command sent interrupt enable (CMDSENTIE)", 7) 237 .WithFlag(8, out dataEndItEnabled, name: "Data end interrupt enable (DATAENDIE)") 238 .WithReservedBits(9, 1) 239 .WithTaggedFlag("Data block end interrupt enable (DBCKENDIE)", 10) 240 .WithTaggedFlag("Command acting interrupt enable (CMDACTIE)", 11) 241 .WithTaggedFlag("Data transmit acting interrupt enable (TXACTIE)", 12) 242 .WithTaggedFlag("Data receive acting interrupt enable (RXACTIE)", 13) 243 .WithFlag(14, out txFifoHalfEmptyItEnabled, name: "Tx FIFO half empty interrupt enable (TXFIFOHEIE)") 244 .WithFlag(15, out rxFifoHalfFullItEnabled, name: "Rx FIFO half full interrupt enable (RXFIFOHFIE)") 245 .WithTaggedFlag("Tx FIFO full interrupt enable (TXFIFOFIE)", 16) 246 .WithTaggedFlag("Rx FIFO full interrupt enable (RXFIFOFIE)", 17) 247 .WithTaggedFlag("Tx FIFO empty interrupt enable (TXFIFOEIE)", 18) 248 .WithTaggedFlag("Rx FIFO empty interrupt enable (RXFIFOEIE)", 19) 249 .WithTaggedFlag("Data available in Tx FIFO interrupt enable (TXDAVLIE)", 20) 250 .WithTaggedFlag("Data available in Rx FIFO interrupt enable (RXDAVLIE)", 21) 251 .WithTaggedFlag("SDIO mode interrupt receied interrupt enable (SDIOITIE)", 22) 252 .WithReservedBits(23, 9) 253 .WithWriteCallback((_, __) => UpdateInterrupts()); 254 } 255 ProcessCommand(SDCard sdCard, SDCardCommand command)256 private void ProcessCommand(SDCard sdCard, SDCardCommand command) 257 { 258 switch(command) 259 { 260 case SDCardCommand.GoIdleState: 261 Reset(); 262 break; 263 case SDCardCommand.ReadMultBlock: 264 ReadCard(sdCard, (uint) dataLength.Value); 265 rxFifoHalfFullFlag = true; 266 break; 267 case SDCardCommand.WriteSingleBlock: 268 writeDataAmount = 512U; 269 txFifoHalfEmptyFlag = true; 270 break; 271 case SDCardCommand.ReadSingleBlock: 272 ReadCard(sdCard, 512U); 273 rxFifoHalfFullFlag = true; 274 break; 275 case SDCardCommand.WriteMultBlock: 276 writeDataAmount = (uint) dataLength.Value; 277 txFifoHalfEmptyFlag = true; 278 break; 279 case SDCardCommand.SendStatus: 280 responseFields[0].Value = CardState; 281 break; 282 default: 283 this.Log(LogLevel.Warning, "Calling command without explicit handling with index: {0}", command); 284 break; 285 } 286 } 287 ReadBuffer()288 private uint ReadBuffer() 289 { 290 var internalBytes = internalBuffer.DequeueRange(4); 291 if(internalBuffer.Count == 0) 292 { 293 dataEndFlag = true; 294 rxFifoHalfFullFlag = false; 295 } 296 return internalBytes.ToUInt32Smart(); 297 } 298 WriteCard(SDCard sdCard, uint data)299 private void WriteCard(SDCard sdCard, uint data) 300 { 301 sdCard.WriteData(data.AsRawBytes()); 302 } 303 ReadCard(SDCard sdCard, uint size)304 private void ReadCard(SDCard sdCard, uint size) 305 { 306 var data = sdCard.ReadData(size); 307 internalBuffer.EnqueueRange(data); 308 if(!isDmaEnabled.Value) 309 { 310 return; 311 } 312 313 /* DMA reads data from FIFO in bursts of 4 bytes when this pin blinks */ 314 for(ulong i = 0; i < dataLength.Value / DmaReadChunk; i++) 315 { 316 DMAReceive.Blink(); 317 } 318 } 319 UpdateInterrupts()320 private void UpdateInterrupts() 321 { 322 IRQ.Set( 323 (dataEndFlag && dataEndItEnabled.Value) || 324 (rxFifoHalfFullFlag && rxFifoHalfFullItEnabled.Value) || 325 (txFifoHalfEmptyFlag && txFifoHalfEmptyItEnabled.Value) 326 ); 327 } 328 329 private IValueRegisterField commandArgument; 330 private IValueRegisterField dataLength; 331 332 private IEnumRegisterField<SDCardCommand> commandIndex; 333 private IValueRegisterField[] responseFields; 334 private IFlagRegisterField isDmaEnabled; 335 private bool dataEndFlag; 336 private bool rxFifoHalfFullFlag; 337 private bool txFifoHalfEmptyFlag; 338 private IFlagRegisterField dataEndItEnabled; 339 private IFlagRegisterField rxFifoHalfFullItEnabled; 340 private IFlagRegisterField txFifoHalfEmptyItEnabled; 341 private ulong writeDataAmount; 342 343 private const ulong CardState = 4 << 9; /* HACK: card state: transfer (otherwise it stucks here: https://github.com/zephyrproject-rtos/zephyr/blob/c008cbab1a05316139de191b0553ab6ccc0073ad/drivers/disk/sdmmc_stm32.c#L386) */ 344 private const ulong DmaReadChunk = 4; 345 346 private readonly Queue<byte> internalBuffer; 347 private enum Registers 348 { 349 Power = 0x00, 350 Arg = 0x08, 351 Cmd = 0x0C, 352 RespCmd = 0x10, 353 Resp1 = 0x14, 354 Resp2 = 0x18, 355 Resp3 = 0x1c, 356 Resp4 = 0x20, 357 DLen = 0x28, 358 DCtrl = 0x2C, 359 Sta = 0x34, 360 Icr = 0x38, 361 Mask = 0x3C, 362 Fifo = 0x80 363 } 364 365 /* command ids: https://github.com/STMicroelectronics/stm32f7xx_hal_driver/blob/52bfa97ba66afc08481f6fd7631322593bd89691/Inc/stm32f7xx_ll_sdmmc.h#L171 */ 366 private enum SDCardCommand 367 { 368 GoIdleState = 0, 369 SendStatus = 13, 370 SetBlockLen = 16, 371 ReadSingleBlock = 17, 372 ReadMultBlock = 18, 373 WriteSingleBlock = 24, 374 WriteMultBlock = 25 375 } 376 377 private enum ResponseWidth 378 { 379 ShortResponse = 1, 380 LongResponse = 3 381 } 382 } 383 }