1 //
2 // Copyright (c) 2010-2023 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.Peripherals.Bus;
11 using Antmicro.Renode.Peripherals.Helpers;
12 using Antmicro.Renode.Core;
13 using Antmicro.Renode.Core.Structure;
14 using Antmicro.Renode.Core.Structure.Registers;
15 using Antmicro.Renode.Utilities;
16 using Antmicro.Renode.Logging;
17 using Antmicro.Renode.Peripherals.SPI.Cadence_xSPICommands;
18 
19 namespace Antmicro.Renode.Peripherals.SPI
20 {
21     public class Cadence_xSPI : SimpleContainer<ISPIPeripheral>, IDoubleWordPeripheral, IKnownSize
22     {
Cadence_xSPI(IMachine machine)23         public Cadence_xSPI(IMachine machine) : base(machine)
24         {
25             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
26             auxiliaryRegisters = new DoubleWordRegisterCollection(this);
27 
28             controllerIdle = new CadenceInterruptFlag(() => currentCommand == null || currentCommand.Completed || currentCommand.Failed);
29             commandCompleted = new CadenceInterruptFlag(() => (currentCommand as STIGCommand)?.Completed ?? false);
30             commandIgnored = new CadenceInterruptFlag();
31             dmaTriggered = new CadenceInterruptFlag(() => (currentCommand as IDMACommand)?.DMATriggered ?? false);
32             dmaError = new CadenceInterruptFlag(() => (currentCommand as IDMACommand)?.DMAError ?? false);
33             autoCommandCompleted = new CadenceInterruptFlag(() => (currentCommand as PIOCommand)?.Completed ?? false, initialMask: true);
34         }
35 
WriteDoubleWord(long offset, uint value)36         public void WriteDoubleWord(long offset, uint value)
37         {
38             registers.Write(offset, value);
39         }
40 
ReadDoubleWord(long offset)41         public uint ReadDoubleWord(long offset)
42         {
43             return registers.Read(offset);
44         }
45 
46         [ConnectionRegion("auxiliary")]
WriteDoubleWordToAuxiliary(long offset, uint value)47         public void WriteDoubleWordToAuxiliary(long offset, uint value)
48         {
49             auxiliaryRegisters.Write(offset, value);
50         }
51 
52         [ConnectionRegion("auxiliary")]
ReadDoubleWordFromAuxiliary(long offset)53         public uint ReadDoubleWordFromAuxiliary(long offset)
54         {
55             return auxiliaryRegisters.Read(offset);
56         }
57 
58         // There is no information in the Linux driver about handling offset for a DMA access
59         // The comment above applies to all Write*ToDMA and Read*FromDMA methods
60         [ConnectionRegion("dma")]
WriteByteUsingDMA(long offset, byte value)61         public void WriteByteUsingDMA(long offset, byte value)
62         {
63             WriteUsingDMA(new byte[] { value });
64         }
65 
66         [ConnectionRegion("dma")]
WriteWordUsingDMA(long offset, ushort value)67         public void WriteWordUsingDMA(long offset, ushort value)
68         {
69             WriteUsingDMA(BitHelper.GetBytesFromValue(value, 2));
70         }
71 
72         [ConnectionRegion("dma")]
WriteDoubleWordUsingDMA(long offset, uint value)73         public void WriteDoubleWordUsingDMA(long offset, uint value)
74         {
75             WriteUsingDMA(BitHelper.GetBytesFromValue(value, 4));
76         }
77 
78         [ConnectionRegion("dma")]
ReadByteUsingDMA(long offset)79         public byte ReadByteUsingDMA(long offset)
80         {
81             return ReadUsingDMA(1).First();
82         }
83 
84         [ConnectionRegion("dma")]
ReadWordUsingDMA(long offset)85         public ushort ReadWordUsingDMA(long offset)
86         {
87             return BitHelper.ToUInt16(ReadUsingDMA(2).ToArray(), 0, false);
88         }
89 
90         [ConnectionRegion("dma")]
ReadDoubleWordUsingDMA(long offset)91         public uint ReadDoubleWordUsingDMA(long offset)
92         {
93             return BitHelper.ToUInt32(ReadUsingDMA(4).ToArray(), 0, 4, false);
94         }
95 
Reset()96         public override void Reset()
97         {
98             registers.Reset();
99             Array.Clear(commandPayload, 0, commandPayload.Length);
100             auxiliaryRegisters.Reset();
101             currentCommand = null;
102             foreach(var flag in GetAllInterruptFlags())
103             {
104                 flag.Reset();
105             }
106             UpdateInterrupts();
107         }
108 
109         public ControllerMode Mode => controllerMode.Value;
110 
111         public long Size => 0x1040;
112 
113         public GPIO IRQ { get; } = new GPIO();
114 
TryGetPeripheral(int address, out ISPIPeripheral peripheral)115         internal bool TryGetPeripheral(int address, out ISPIPeripheral peripheral)
116         {
117             return TryGetByAddress(address, out peripheral);
118         }
119 
TriggerCommand()120         private void TriggerCommand()
121         {
122             var previousCommand = currentCommand;
123             currentCommand = Command.CreateCommand(this, new CommandPayload(commandPayload));
124             this.Log(LogLevel.Debug, "New command: {0}", currentCommand);
125 
126             if(currentCommand == null)
127             {
128                 commandIgnored.SetSticky(true);
129             }
130             else if(previousCommand != null && previousCommand.ChipSelect != currentCommand.ChipSelect && !previousCommand.TransmissionFinished)
131             {
132                 this.Log(LogLevel.Error, "Triggering command with chip select different than previous one, when the previous transaction isn't finished.");
133                 previousCommand.FinishTransmission();
134             }
135 
136             currentCommand?.Transmit();
137             UpdateSticky();
138             UpdateInterrupts();
139         }
140 
WriteUsingDMA(IReadOnlyList<byte> data)141         private void WriteUsingDMA(IReadOnlyList<byte> data)
142         {
143             if(TryGetDMACommand(TransmissionDirection.Write, out var command))
144             {
145                 command.WriteData(data);
146                 UpdateSticky();
147                 UpdateInterrupts();
148             }
149         }
150 
ReadUsingDMA(int length)151         private IEnumerable<byte> ReadUsingDMA(int length)
152         {
153             if(TryGetDMACommand(TransmissionDirection.Read, out var command))
154             {
155                 var data = command.ReadData(length);
156                 UpdateSticky();
157                 UpdateInterrupts();
158                 return data;
159             }
160             return new byte[length];
161         }
162 
TryGetDMACommand(TransmissionDirection accessDirection, out IDMACommand command)163         private bool TryGetDMACommand(TransmissionDirection accessDirection, out IDMACommand command)
164         {
165             command = currentCommand as IDMACommand;
166             if(command == null)
167             {
168                 this.Log(LogLevel.Warning, "Trying to access data using DMA, when the latest command isn't a DMA transaction command.");
169                 return false;
170             }
171             if(command.DMADirection != accessDirection)
172             {
173                 this.Log(LogLevel.Warning, "Trying to access data using DMA with the wrong direction ({0}), expected {1}.", accessDirection, command.DMADirection);
174                 return false;
175             }
176             return true;
177         }
178 
BuildRegisterMap()179         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
180         {
181             return new Dictionary<long, DoubleWordRegister>
182             {
183                 {(long)Registers.Command0, new DoubleWordRegister(this)
184                     .WithValueField(0, 32, name: "command0Payload",
185                         valueProviderCallback: _ => commandPayload[0],
186                         writeCallback: (_, val) => commandPayload[0] = (uint)val
187                     )
188                     .WithWriteCallback((_, __) =>
189                         {
190                             if(!IsModeSupported(controllerMode.Value))
191                             {
192                                 this.Log(LogLevel.Warning, "Command trigger ignored, the mode {0} isn't supported.", controllerMode.Value);
193                                 return;
194                             }
195                             // Based on the Linux driver all command registers are written in sequence
196                             // The command0 register are written as the last one, what triggers command execution
197                             TriggerCommand();
198                         }
199                     )
200                 },
201                 {(long)Registers.Command1, new DoubleWordRegister(this)
202                     .WithValueField(0, 32, name: "command1Payload",
203                         valueProviderCallback: _ => commandPayload[1],
204                         writeCallback: (_, val) => commandPayload[1] = (uint)val
205                     )
206                 },
207                 {(long)Registers.Command2, new DoubleWordRegister(this)
208                     .WithValueField(0, 32, name: "command2Payload",
209                         valueProviderCallback: _ => commandPayload[2],
210                         writeCallback: (_, val) => commandPayload[2] = (uint)val
211                     )
212                 },
213                 {(long)Registers.Command3, new DoubleWordRegister(this)
214                     .WithValueField(0, 32, name: "command3Payload",
215                         valueProviderCallback: _ => commandPayload[3],
216                         writeCallback: (_, val) => commandPayload[3] = (uint)val
217                     )
218                 },
219                 {(long)Registers.Command4, new DoubleWordRegister(this)
220                     .WithValueField(0, 32, name: "command4Payload",
221                         valueProviderCallback: _ => commandPayload[4],
222                         writeCallback: (_, val) => commandPayload[4] = (uint)val
223                     )
224                 },
225                 {(long)Registers.Command5, new DoubleWordRegister(this)
226                     .WithValueField(0, 32, name: "command5Payload",
227                         valueProviderCallback: _ => commandPayload[5],
228                         writeCallback: (_, val) => commandPayload[5] = (uint)val
229                     )
230                 },
231                 {(long)Registers.CommandStatus, new DoubleWordRegister(this)
232                     .WithReservedBits(16, 16)
233                     .WithFlag(15, FieldMode.Read, name: "commandCompleted",
234                         valueProviderCallback: _ => currentCommand?.Completed ?? false
235                     )
236                     .WithFlag(14, FieldMode.Read, name: "commandFailed",
237                         valueProviderCallback: _ => currentCommand?.Failed ?? false
238                     )
239                     .WithReservedBits(4, 10)
240                     .WithTaggedFlag("commandDQSError", 3)
241                     .WithFlag(2, FieldMode.Read, name: "commandCRCError",
242                         valueProviderCallback: _ => currentCommand?.CRCError ?? false
243                     )
244                     .WithFlag(1, FieldMode.Read, name: "commandBusError",
245                         valueProviderCallback: _ => currentCommand?.BusError ?? false
246                     )
247                     .WithFlag(0, FieldMode.Read, name: "commandInvalidCommandError",
248                         valueProviderCallback: _ => currentCommand?.InvalidCommandError ?? false
249                     )
250                 },
251                 {(long)Registers.ControllerStatus, new DoubleWordRegister(this)
252                     .WithReservedBits(17, 15)
253                     .WithFlag(16, FieldMode.Read, name: "initializationCompleted",
254                         valueProviderCallback: _ => true
255                     )
256                     .WithReservedBits(10, 6)
257                     .WithTaggedFlag("initializationLegacy", 9)
258                     .WithFlag(8, FieldMode.Read, name: "initializationFail",
259                         valueProviderCallback: _ => false
260                     )
261                     .WithFlag(7, FieldMode.Read, name: "controllerBusy",
262                         valueProviderCallback: _ => !controllerIdle.Status
263                     )
264                     .WithReservedBits(0, 7)
265                 },
266                 {(long)Registers.AutoCommandStatus, new DoubleWordRegister(this)
267                     .WithReservedBits(1, 31)
268                     .WithFlag(0, FieldMode.Read, name: "autoCommandControllerBusy",
269                         valueProviderCallback: _ => !controllerIdle.Status
270                     )
271                 },
272                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
273                     .WithReservedBits(24, 8)
274                     .WithFlag(23, name: "commandCompletedInterruptStatus",
275                         valueProviderCallback: _ => commandCompleted.StickyStatus,
276                         writeCallback: (_, val) => commandCompleted.ClearSticky(val)
277                     )
278                     .WithFlag(22, name: "dmaErrorInterruptStatus",
279                         valueProviderCallback: _ => dmaError.StickyStatus,
280                         writeCallback: (_, val) => dmaError.ClearSticky(val)
281                     )
282                     .WithFlag(21, name: "dmaTriggeredInterruptStatus",
283                         valueProviderCallback: _ => dmaTriggered.StickyStatus,
284                         writeCallback: (_, val) => dmaTriggered.ClearSticky(val)
285                     )
286                     .WithFlag(20, name: "commandIgnoredInterruptStatus",
287                         valueProviderCallback: _ => commandIgnored.StickyStatus,
288                         writeCallback: (_, val) => commandIgnored.ClearSticky(val)
289                     )
290                     .WithReservedBits(19, 1)
291                     .WithTaggedFlag("DDMA_TERR_InterruptStatus", 18)
292                     .WithTaggedFlag("DMA_TREE_InterruptStatus", 17)
293                     .WithFlag(16, name: "controllerIdleInterruptStatus",
294                         valueProviderCallback: _ => controllerIdle.StickyStatus,
295                         writeCallback: (_, val) => controllerIdle.ClearSticky(val)
296                     )
297                     .WithReservedBits(0, 16)
298                     .WithWriteCallback((_, __) => UpdateInterrupts())
299                 },
300                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
301                     .WithFlag(31, out interruptsEnabled, name: "enableInterrupts")
302                     .WithReservedBits(24, 7)
303                     .WithFlag(23, name: "commandCompletedInterruptEnable",
304                         valueProviderCallback: _ => commandCompleted.InterruptMask,
305                         writeCallback: (_, val) => commandCompleted.InterruptEnable(val)
306                     )
307                     .WithFlag(22, name: "dmaErrorInterruptEnable",
308                         valueProviderCallback: _ => dmaError.InterruptMask,
309                         writeCallback: (_, val) => dmaError.InterruptEnable(val)
310                     )
311                     .WithFlag(21, name: "dmaTriggeredInterruptEnable",
312                         valueProviderCallback: _ => dmaTriggered.InterruptMask,
313                         writeCallback: (_, val) => dmaTriggered.InterruptEnable(val)
314                     )
315                     .WithFlag(20, name: "commandIgnoredInterruptEnable",
316                         valueProviderCallback: _ => commandIgnored.InterruptMask,
317                         writeCallback: (_, val) => commandIgnored.InterruptEnable(val)
318                     )
319                     .WithReservedBits(19, 1)
320                     .WithTaggedFlag("DDMA_TERR_InterruptEnable", 18)
321                     .WithTaggedFlag("DMA_TREE_InterruptEnable", 17)
322                     .WithFlag(16, name: "controllerIdleInterruptEnable",
323                         valueProviderCallback: _ => controllerIdle.InterruptMask,
324                         writeCallback: (_, val) => controllerIdle.InterruptEnable(val)
325                     )
326                     .WithReservedBits(0, 16)
327                     .WithWriteCallback((_, __) => UpdateInterrupts())
328                 },
329                 {(long)Registers.AutoCommandCompleteInterruptStatus, new DoubleWordRegister(this)
330                     .WithReservedBits(1, 31)
331                     .WithFlag(0, name: "autoCommandCompletedInterruptStatus",
332                         valueProviderCallback: _ => autoCommandCompleted.StickyStatus,
333                         writeCallback: (_, val) => autoCommandCompleted.ClearSticky(val)
334                     )
335                     .WithWriteCallback((_, __) => UpdateInterrupts())
336                 },
337                 {(long)Registers.ControllerConfig, new DoubleWordRegister(this)
338                     .WithReservedBits(7, 25)
339                     .WithEnumField(5, 2, out controllerMode, name: "controllerMode",
340                         changeCallback: (_, val) =>
341                         {
342                             if(!IsModeSupported(val))
343                             {
344                                 this.Log(LogLevel.Warning, "Setting the controller mode to one which isn't supported ({0}).", val);
345                             }
346                         }
347                     )
348                     .WithReservedBits(0, 5)
349                 },
350                 {(long)Registers.DMASize, new DoubleWordRegister(this)
351                     .WithValueField(0, 32, FieldMode.Read, name: "DMASize",
352                         valueProviderCallback: _ => (currentCommand as IDMACommand)?.DMADataCount ?? 0
353                     )
354                 },
355                 {(long)Registers.DMAStatus, new DoubleWordRegister(this)
356                     .WithReservedBits(9, 23)
357                     .WithEnumField<DoubleWordRegister, TransmissionDirection>(8, 1, FieldMode.Read, name: "DMADirection",
358                         valueProviderCallback: _ => (currentCommand as IDMACommand)?.DMADirection ?? default(TransmissionDirection)
359                     )
360                     .WithReservedBits(0, 8)
361                 },
362                 {(long)Registers.ControllerVersion, new DoubleWordRegister(this)
363                     .WithValueField(0, 8, FieldMode.Read, name: "hardwareRevision",
364                         valueProviderCallback: _ => HardwareRevision
365                     )
366                     .WithReservedBits(8, 8)
367                     .WithValueField(16, 16, FieldMode.Read, name: "hardwareMagicNumber",
368                         valueProviderCallback: _ => HardwareMagicNumber
369                     )
370                 },
371                 {(long)Registers.ControllerFeatures, new DoubleWordRegister(this)
372                     .WithReservedBits(26, 6)
373                     .WithTag("banksCount", 24, 2)
374                     .WithReservedBits(22, 2)
375                     .WithTaggedFlag("dmaDataWidth", 21)
376                     .WithReservedBits(4, 17)
377                     .WithTag("threadsCount", 0, 4)
378                 }
379             };
380         }
381 
IsModeSupported(ControllerMode mode)382         private bool IsModeSupported(ControllerMode mode)
383         {
384             return mode == ControllerMode.SoftwareTriggeredInstructionGenerator || mode == ControllerMode.AutoCommand;
385         }
386 
UpdateSticky()387         private void UpdateSticky()
388         {
389             foreach(var flag in GetAllInterruptFlags())
390             {
391                 flag.UpdateStickyStatus();
392             }
393         }
394 
UpdateInterrupts()395         private void UpdateInterrupts()
396         {
397             IRQ.Set(interruptsEnabled.Value && GetAllInterruptFlags().Any(x => x.InterruptStatus));
398         }
399 
GetControllerInterruptFlags()400         private IEnumerable<CadenceInterruptFlag> GetControllerInterruptFlags()
401         {
402             yield return controllerIdle;
403             yield return commandCompleted;
404             yield return commandIgnored;
405             yield return dmaTriggered;
406             yield return dmaError;
407         }
408 
GetAutoCommandInterruptFlags()409         private IEnumerable<CadenceInterruptFlag> GetAutoCommandInterruptFlags()
410         {
411             yield return autoCommandCompleted;
412         }
413 
GetAllInterruptFlags()414         private IEnumerable<CadenceInterruptFlag> GetAllInterruptFlags()
415         {
416             return GetControllerInterruptFlags().Concat(GetAutoCommandInterruptFlags());
417         }
418 
419         private Command currentCommand;
420 
421         private IFlagRegisterField interruptsEnabled;
422         private IEnumRegisterField<ControllerMode> controllerMode;
423 
424         // Command registers have different fields at same offset depending on the command type
425         // The commandPayload array contains all command registers values
426         // It's passed to the Command class constructor and decoded
427         private readonly uint[] commandPayload = new uint[6];
428 
429         private readonly CadenceInterruptFlag controllerIdle;
430         private readonly CadenceInterruptFlag commandCompleted;
431         private readonly CadenceInterruptFlag commandIgnored;
432         private readonly CadenceInterruptFlag dmaTriggered;
433         private readonly CadenceInterruptFlag dmaError;
434         private readonly CadenceInterruptFlag autoCommandCompleted;
435 
436         private readonly DoubleWordRegisterCollection registers;
437         private readonly DoubleWordRegisterCollection auxiliaryRegisters;
438 
439         private const uint HardwareMagicNumber = 0x6522;
440         private const uint HardwareRevision = 0x0;
441 
442         public enum ControllerMode
443         {
444             Direct = 0x0,
445             SoftwareTriggeredInstructionGenerator = 0x1,
446             // Based on the Linux driver there is no mode for the 0x2 value
447             AutoCommand = 0x3
448         }
449 
450         public enum TransmissionDirection
451         {
452             Read = 0x0,
453             Write = 0x1
454         }
455 
456         private enum Registers : long
457         {
458             Command0 = 0x0000,
459             Command1 = 0x0004,
460             Command2 = 0x0008,
461             Command3 = 0x000c,
462             Command4 = 0x0010,
463             Command5 = 0x0014,
464             CommandStatusPointer = 0x0040,
465             CommandStatus = 0x0044,
466             ControllerStatus = 0x0100,
467             AutoCommandStatus = 0x0104,
468             InterruptStatus = 0x0110,
469             InterruptEnable = 0x0114,
470             AutoCommandCompleteInterruptStatus = 0x0120,
471             AutoCommandErrorInterruptStatus = 0x0130,
472             AutoCommandErrorInterruptEnable = 0x0134,
473             ControllerConfig = 0x0230,
474             DMASize = 0x0240,
475             DMAStatus = 0x0244,
476             ControllerVersion = 0x0f00,
477             ControllerFeatures = 0x0f04,
478             DLLControl = 0x1034,
479         }
480 
481         private enum AuxiliaryRegisters : long
482         {
483             DQTiming = 0x0000,
484             DQSTiming = 0x0004,
485             GateLoopbackControl = 0x0008,
486             DLLSlaveControl = 0x0010,
487         }
488     }
489 }
490