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.Utilities;
11 using Antmicro.Renode.Logging;
12 using static Antmicro.Renode.Peripherals.SPI.Cadence_xSPI;
13 
14 namespace Antmicro.Renode.Peripherals.SPI.Cadence_xSPICommands
15 {
16     internal abstract class PIOCommand : AutoCommand
17     {
CreatePIOCommand(Cadence_xSPI controller, CommandPayload payload)18         public static PIOCommand CreatePIOCommand(Cadence_xSPI controller, CommandPayload payload)
19         {
20             // It isn't clear what is a purpose of this bit in the Linux driver.
21             var intBit = BitHelper.GetValue(payload[0], 18, 1);
22             if(intBit != 1)
23             {
24                 controller.Log(LogLevel.Warning, "There is a support only for PIO commands with the 18th bit set to 1.");
25                 return null;
26             }
27 
28             var commandType = DecodeCommandType(payload);
29             switch(commandType)
30             {
31                 case CommandType.Reset:
32                 case CommandType.SectorErase:
33                 case CommandType.ChipErase:
34                     return new PIOOperationCommand(controller, payload);
35                 case CommandType.Program:
36                 case CommandType.Read:
37                     var dmaRole = (DMARoleType)BitHelper.GetValue(payload[0], 19, 1);
38                     if(dmaRole != DMARoleType.Peripheral)
39                     {
40                         controller.Log(LogLevel.Warning, "There is a support only for PIO commands with a DMA in a peripheral role.");
41                         return null;
42                     }
43                     return new PIODataCommand(controller, payload);
44                 default:
45                     controller.Log(LogLevel.Warning, "Unable to create a PIO command, unknown command type 0x{0:x}", commandType);
46                     return null;
47             }
48         }
49 
PIOCommand(Cadence_xSPI controller, CommandPayload payload)50         public PIOCommand(Cadence_xSPI controller, CommandPayload payload) : base(controller, payload)
51         {
52             type = DecodeCommandType(payload);
53             address = payload[1];
54             length = (ulong)payload[4] + 1;
55             if(length > uint.MaxValue)
56             {
57                 Log(LogLevel.Warning, "The length of the PIOCommand doesn't fit in 32 bits. Value of the DMA transation size register is undefined.");
58             }
59         }
60 
ToString()61         public override string ToString()
62         {
63             return $"{base.ToString()}, commandType = {type}, address = 0x{address:x}, length = {length}";
64         }
65 
TransmitAddress()66         protected void TransmitAddress()
67         {
68             foreach(var addressByte in BitHelper.GetBytesFromValue(address, 4))
69             {
70                 Peripheral.Transmit(addressByte);
71             }
72         }
73 
74         protected readonly CommandType type;
75         protected readonly uint address;
76         protected readonly ulong length;
77 
DecodeCommandType(CommandPayload payload)78         private static CommandType DecodeCommandType(CommandPayload payload)
79         {
80             return (CommandType)BitHelper.GetValue(payload[0], 0, 16);
81         }
82 
83         protected enum CommandType
84         {
85             Reset = 0x1100,
86             SectorErase = 0x1000,
87             ChipErase = 0x1001,
88             Read = 0x2200,
89             Program = 0x2100
90         }
91 
92         private enum DMARoleType
93         {
94             Peripheral = 0,
95             Controller = 1
96         }
97     }
98 
99     internal class PIOOperationCommand : PIOCommand
100     {
PIOOperationCommand(Cadence_xSPI controller, CommandPayload payload)101         public PIOOperationCommand(Cadence_xSPI controller, CommandPayload payload) : base(controller, payload) { }
102 
Transmit()103         public override void Transmit()
104         {
105             if(Peripheral != null)
106             {
107                 switch(type)
108                 {
109                     case CommandType.Reset:
110                         Peripheral.Transmit(ResetEnableOperation);
111                         Peripheral.Transmit(ResetMemoryOperation);
112                         break;
113                     case CommandType.ChipErase:
114                         Peripheral.Transmit(ChipEraseOperation);
115                         break;
116                     case CommandType.SectorErase:
117                         Peripheral.Transmit(SectorErase4ByteOperation);
118                         TransmitAddress();
119                         break;
120                     default:
121                         throw new ArgumentException($"Unknown PIO Command type: {type}.");
122                 }
123             }
124             Completed = true;
125             FinishTransmission();
126         }
127 
128         private const byte ResetEnableOperation = 0x66;
129         private const byte ResetMemoryOperation = 0x99;
130         private const byte ChipEraseOperation = 0xC7;
131         private const byte SectorErase4ByteOperation = 0xDC;
132     }
133 
134     internal class PIODataCommand : PIOCommand, IDMACommand
135     {
PIODataCommand(Cadence_xSPI controller, CommandPayload payload)136         public PIODataCommand(Cadence_xSPI controller, CommandPayload payload) : base(controller, payload)
137         {
138             switch(type)
139             {
140                 case CommandType.Program:
141                     DMADirection = TransmissionDirection.Write;
142                     break;
143                 case CommandType.Read:
144                     DMADirection = TransmissionDirection.Read;
145                     break;
146                 default:
147                     throw new ArgumentException($"Unknown PIO Command type: {type}.");
148             }
149         }
150 
Transmit()151         public override void Transmit()
152         {
153             if(Peripheral == null)
154             {
155                 DMAError = true;
156                 FinishTransmission();
157                 return;
158             }
159             DMATriggered = true;
160         }
161 
WriteData(IReadOnlyList<byte> data)162         public void WriteData(IReadOnlyList<byte> data)
163         {
164             if(DMADirection != TransmissionDirection.Write)
165             {
166                 throw new InvalidOperationException($"Trying to write data using the command with wrong direction ({DMADirection}).");
167             }
168 
169             if(Peripheral != null)
170             {
171                 if(dataTransmittedCount == 0)
172                 {
173                     Peripheral.Transmit(PageProgram4ByteOperation);
174                     TransmitAddress();
175                 }
176                 foreach(var dataByte in data)
177                 {
178                     Peripheral.Transmit(dataByte);
179                 }
180             }
181             dataTransmittedCount += (ulong)data.Count;
182             FinishIfDone();
183         }
184 
ReadData(int length)185         public IList<byte> ReadData(int length)
186         {
187             if(DMADirection != TransmissionDirection.Read)
188             {
189                 throw new InvalidOperationException($"Trying to read data using the command with wrong direction ({DMADirection}).");
190             }
191 
192             var data = Enumerable.Repeat(default(byte), length);
193             if(Peripheral != null)
194             {
195                 if(dataTransmittedCount == 0)
196                 {
197                     Peripheral.Transmit(Read4ByteOperation);
198                     TransmitAddress();
199                 }
200                 data = data.Select(
201                     dataByte => Peripheral.Transmit(dataByte)
202                 );
203             }
204             var dataList = data.ToList();
205             dataTransmittedCount += (ulong)dataList.Count;
206             FinishIfDone();
207 
208             return dataList;
209         }
210 
ToString()211         public override string ToString()
212         {
213             return $"{base.ToString()}, DMADirection = {DMADirection}";
214         }
215 
216         public TransmissionDirection DMADirection { get; }
217         public uint DMADataCount => (uint)length;
218         public bool DMATriggered { get; private set; }
219         public bool DMAError { get; private set; }
220 
FinishIfDone()221         private void FinishIfDone()
222         {
223             if(dataTransmittedCount < DMADataCount)
224             {
225                 return;
226             }
227             if(dataTransmittedCount > DMADataCount)
228             {
229                 Log(LogLevel.Warning, "Accessed more data than command data count.");
230             }
231             Completed = true;
232             FinishTransmission();
233         }
234 
235         private ulong dataTransmittedCount;
236 
237         private const byte PageProgram4ByteOperation = 0x12;
238         private const byte Read4ByteOperation = 0x13;
239     }
240 }
241