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