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