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 }