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 
8 using System;
9 using System.Linq;
10 using System.Collections.Generic;
11 using Antmicro.Renode.Core;
12 using Antmicro.Renode.Core.Structure;
13 using Antmicro.Renode.Core.Structure.Registers;
14 using Antmicro.Renode.Core.USB;
15 using Antmicro.Renode.Logging;
16 using Antmicro.Renode.Utilities;
17 using Antmicro.Renode.Utilities.Packets;
18 using System.Threading;
19 using Antmicro.Renode.Peripherals.Bus.Wrappers;
20 
21 namespace Antmicro.Renode.Peripherals.SPI
22 {
23     public class MAX3421E : SimpleContainer<IUSBDevice>, IProvidesRegisterCollection<ByteRegisterCollection>, ISPIPeripheral, IDisposable
24     {
MAX3421E(IMachine machine)25         public MAX3421E(IMachine machine) : base(machine)
26         {
27             IRQ = new GPIO();
28             setupQueue = new Queue<byte>();
29             receiveQueue = new Queue<byte>();
30             sendQueue = new Queue<byte>();
31             bumper = machine.ObtainManagedThread(GenerateFrameInterrupt, BumpsPerSecond);
32 
33             RegistersCollection = new ByteRegisterCollection(this);
34 
35             DefineRegisters();
36         }
37 
Register(IUSBDevice peripheral, NumberRegistrationPoint<int> registrationPoint)38         public override void Register(IUSBDevice peripheral, NumberRegistrationPoint<int> registrationPoint)
39         {
40             base.Register(peripheral, registrationPoint);
41 
42             // indicate the K state - full-speed device attached
43             kStatus.Value = true;
44             jStatus.Value = false;
45 
46             connectDisconnectInterruptRequest.Value = true;
47 
48             this.Log(LogLevel.Debug, "USB device connected to port {0}", registrationPoint.Address);
49             UpdateInterrupts();
50 
51             HandleBumper();
52         }
53 
Unregister(IUSBDevice peripheral)54         public override void Unregister(IUSBDevice peripheral)
55         {
56             base.Unregister(peripheral);
57 
58             connectDisconnectInterruptRequest.Value = true;
59             UpdateInterrupts();
60 
61             HandleBumper();
62         }
63 
FinishTransmission()64         public void FinishTransmission()
65         {
66             this.Log(LogLevel.Noisy, "Transmission finished");
67             state = State.Idle;
68         }
69 
Dispose()70         public override void Dispose()
71         {
72             base.Dispose();
73             bumper.Dispose();
74         }
75 
Reset()76         public override void Reset()
77         {
78             RegistersCollection.Reset();
79             UpdateInterrupts();
80 
81             lastRegister = 0;
82             state = State.Idle;
83 
84             setupQueue.Clear();
85             receiveQueue.Clear();
86             sendQueue.Clear();
87 
88             HandleBumper();
89         }
90 
Transmit(byte data)91         public byte Transmit(byte data)
92         {
93             this.Log(LogLevel.Noisy, "Received byte: 0x{0:X} in state {1}", data, state);
94             byte result = 0;
95 
96             switch(state)
97             {
98             case State.Idle:
99                 HandleCommandByte(data);
100                 result = RegistersCollection.Read((long)RegisterType.HostIrqPending);
101                 break;
102 
103             case State.Writing:
104                 this.Log(LogLevel.Noisy, "Writing value 0x{0:X} to register {1} (0x{1:X})", data, lastRegister);
105                 RegistersCollection.Write((long)lastRegister, data);
106                 break;
107 
108             case State.Reading:
109                 this.Log(LogLevel.Noisy, "Reading value from register {0} (0x{0:X})", lastRegister);
110                 result = RegistersCollection.Read((long)lastRegister);
111                 break;
112 
113             default:
114                 this.Log(LogLevel.Error, "Received byte 0x{0:X} in an unexpected state: {1}. Ignoring it...", data, state);
115                 break;
116             }
117 
118             this.Log(LogLevel.Noisy, "Returning byte: 0x{0:X}", result);
119             return result;
120         }
121 
122         public GPIO IRQ { get; }
123 
124         public ByteRegisterCollection RegistersCollection { get; }
125 
HandleBumper()126         private void HandleBumper()
127         {
128             if((hostMode.Value || startOfFramePacketsGenerationEnable.Value) && ChildCollection.Any())
129             {
130                 bumper.Start();
131             }
132             else
133             {
134                 bumper.Stop();
135             }
136         }
137 
UpdateInterrupts()138         private void UpdateInterrupts()
139         {
140             var state = false;
141 
142             state |= (connectDisconnectInterruptRequest.Value && connectDisconnectInterruptEnable.Value);
143             state |= (busEventInterruptRequest.Value && busEventInterruptEnable.Value);
144             state |= (frameGeneratorInterruptRequest.Value && frameGeneratorInterruptEnable.Value);
145             state |= (hostTransferDoneInterruptRequest.Value && hostTransferDoneInterruptEnable.Value);
146             state |= (receiveDataAvailableInterruptRequest.Value && receiveDataAvailableInterruptEnable.Value);
147             state |= (sendDataBufferAvailableInterruptRequest.Value && sendDataBufferAvailableInterruptEnable.Value);
148 
149             state |= (oscillatorOKInterruptRequest.Value && oscillatorOKInterruptEnable.Value);
150 
151             state &= interruptEnable.Value;
152 
153             this.Log(LogLevel.Noisy, "Setting IRQ to {0}", state);
154             IRQ.Set(state);
155         }
156 
DefineRegisters()157         private void DefineRegisters()
158         {
159             RegisterType.ReceiveFifo.Define(this)
160                 .WithValueField(0, 8, FieldMode.Read, name: "data", valueProviderCallback: _ =>
161                 {
162                     if(receiveQueue.Count == 0)
163                     {
164                         this.Log(LogLevel.Warning, "Trying to read from an empty receive queue");
165                         return 0;
166                     }
167                     return receiveQueue.Dequeue();
168                 })
169             ;
170 
171             RegisterType.SendFifo.Define(this)
172                 .WithValueField(0, 8, name: "data",
173                     valueProviderCallback: _ =>
174                     {
175                         if(sendQueue.Count == 0)
176                         {
177                             this.Log(LogLevel.Warning, "Trying to read from an empty send queue");
178                             return 0;
179                         }
180                         return sendQueue.Dequeue();
181 
182                     },
183                     writeCallback: (_, val) =>
184                     {
185                         sendQueue.Enqueue((byte)val);
186                         if(sendQueue.Count > FifoSize)
187                         {
188                             this.Log(LogLevel.Warning, "Too much data put in the send queue. Initial bytes will be dropped");
189                             sendQueue.Dequeue();
190                         }
191                     })
192             ;
193 
194             RegisterType.SetupFifo.Define(this)
195                 .WithValueField(0, 8, name: "setup data", valueProviderCallback: _ =>
196                 {
197                     if(setupQueue.Count == 0)
198                     {
199                         this.Log(LogLevel.Warning, "Trying to read from an empty setup queue");
200                         return 0;
201                     }
202                     return setupQueue.Dequeue();
203 
204                 },
205                 writeCallback: (_, val) =>
206                 {
207                     setupQueue.Enqueue((byte)val);
208                     if(setupQueue.Count > 8)
209                     {
210                         this.Log(LogLevel.Warning, "Too much data put in the setup queue. Initial bytes will be dropped");
211                         setupQueue.Dequeue();
212                     }
213                 })
214             ;
215 
216             RegisterType.ReceiveQueueLength.Define(this)
217                 .WithValueField(0, 7, FieldMode.Read, name: "count", valueProviderCallback: _ => (uint)receiveQueue.Count)
218                 .WithReservedBits(7, 1)
219             ;
220 
221             RegisterType.SendQueueLength.Define(this)
222                 .WithValueField(0, 7, out sendByteCount, name: "count")
223                 .WithReservedBits(7, 1)
224                 .WithWriteCallback((_, __) =>
225                 {
226                     sendDataBufferAvailableInterruptRequest.Value = false;
227                     UpdateInterrupts();
228                 })
229             ;
230 
231             RegisterType.USBIrqPending.Define(this)
232                 .WithFlag(0, out oscillatorOKInterruptRequest, FieldMode.Read | FieldMode.WriteOneToClear, name: "OSCOKIRQ")
233                 .WithReservedBits(1, 4)
234                 .WithTag("NOVBUSIRQ", 5, 1)
235                 .WithTag("VBUSIRQ", 6, 1)
236                 .WithReservedBits(7, 1)
237                 .WithWriteCallback((_, __) => UpdateInterrupts())
238             ;
239 
240             RegisterType.USBIrqEnabled.Define(this)
241                 .WithFlag(0, out oscillatorOKInterruptEnable, name: "OSCOKIE")
242                 .WithReservedBits(1, 4)
243                 .WithTag("NOVBUSIE", 5, 1)
244                 .WithTag("VBUSIE", 6, 1)
245                 .WithReservedBits(7, 1)
246                 .WithWriteCallback((_, __) => UpdateInterrupts())
247             ;
248 
249             RegisterType.USBControl.Define(this)
250                 .WithReservedBits(0, 4)
251                 .WithTag("PWRDOWN", 4, 1)
252                 .WithFlag(5, name: "Chip Reset", changeCallback: (_, v) =>
253                 {
254                     if(!v)
255                     {
256                         // software should wait for the oscillator and PLLS
257                         // to stabilize after setting CHIPRES = 0 which
258                         // is indicated by setting oscillator OK IRQ
259                         oscillatorOKInterruptRequest.Value = true;
260                         UpdateInterrupts();
261                     }
262                 })
263                 .WithReservedBits(6, 2)
264             ;
265 
266             RegisterType.CPUControl.Define(this)
267                 .WithFlag(0, out interruptEnable, name: "IE")
268                 .WithReservedBits(1, 5)
269                 .WithTag("PULSEWID", 6, 2)
270                 .WithWriteCallback((_, __) => UpdateInterrupts())
271             ;
272 
273             RegisterType.PinControl.Define(this)
274                 .WithTag("GPXA", 0, 1)
275                 .WithTag("GPXB", 1, 1)
276                 .WithTag("POSINT", 2, 1)
277                 .WithTag("INTLEVEL", 3, 1)
278                 .WithTag("FDUPSPI", 4, 1)
279                 .WithReservedBits(5, 3)
280             ;
281 
282             RegisterType.Revision.Define(this)
283                 .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => ChipRevision)
284             ;
285 
286             RegisterType.HostIrqPending.Define(this, 0x8) //sndbavirq is set by default
287                 .WithFlag(0, out busEventInterruptRequest, FieldMode.Read | FieldMode.WriteOneToClear, name: "BUSEVENTIRQ")
288                 .WithTag("RWUIRQ - Remote Wakeup Interrupt Request", 1, 1)
289                 .WithFlag(2, out receiveDataAvailableInterruptRequest, FieldMode.Read | FieldMode.WriteOneToClear, name: "RCVDAVIRQ") // this should not go automatically from 1 to 0 when the fifo is empty, but should be explicitly cleared by the cpu
290                 .WithFlag(3, out sendDataBufferAvailableInterruptRequest, FieldMode.Read, name: "SNDAVIRQ") // this bit is cleared by writing to SNDBC register
291                 .WithTag("SUSDNIRQ - Suspend operation Done Interrupt Request", 4, 1)
292                 .WithFlag(5, out connectDisconnectInterruptRequest, FieldMode.Read | FieldMode.WriteOneToClear, name: "CONDETIRQ")
293                 .WithFlag(6, out frameGeneratorInterruptRequest, FieldMode.Read | FieldMode.WriteOneToClear, name: "FRAMEIRQ")
294                 .WithFlag(7, out hostTransferDoneInterruptRequest, FieldMode.Read | FieldMode.WriteOneToClear, name: "HXFRDNIRQ")
295                 .WithWriteCallback((_, __) => UpdateInterrupts())
296             ;
297 
298             RegisterType.HostIrqEnabled.Define(this)
299                 .WithFlag(0, out busEventInterruptEnable, name: "BUSEVENTIE")
300                 .WithTag("RWUIE - Remote Wakeup Interrupt Enable", 1, 1)
301                 .WithFlag(2, out receiveDataAvailableInterruptEnable, name: "RCVDAVIE")
302                 .WithFlag(3, out sendDataBufferAvailableInterruptEnable, name: "SNDAVIE")
303                 .WithTag("SUSDNIE - Suspend operation Done Interrupt Enable", 4, 1)
304                 .WithFlag(5, out connectDisconnectInterruptEnable, name: "CONDETIE")
305                 .WithFlag(6, out frameGeneratorInterruptEnable, name: "FRAMEIE")
306                 .WithFlag(7, out hostTransferDoneInterruptEnable, name: "HXFRDNIE")
307                 .WithWriteCallback((_, __) => UpdateInterrupts())
308             ;
309 
310             RegisterType.Mode.Define(this)
311                 .WithFlag(0, out hostMode, name: "host mode")
312                 .WithTag("LOWSPEED", 1, 1)
313                 .WithTag("HUBPRE - Send the PRE PID to a LS device operating through a USB hub", 2, 1)
314                 .WithFlag(3, out startOfFramePacketsGenerationEnable, name: "SOFKAENAB")
315                 .WithTag("SEPIRQ - Provides the GPIN IRQS on a separate pin (GPX)", 4, 1)
316                 .WithTag("DELAYISO - Delay data transfer to an ISOCHRONOUS endpoint until the next frame", 5, 1)
317                 .WithTag("DMPULLDN - Connect internal 15k resistor from D- to ground", 6, 1)
318                 .WithTag("DPPULLDN - Connect internal 15k resistor from D+ to ground", 7, 1)
319                 .WithWriteCallback((_, __) => HandleBumper())
320             ;
321 
322             RegisterType.PeripheralAddress.Define(this)
323                 .WithValueField(0, 7, out deviceAddress, name: "address")
324                 .WithReservedBits(7, 1)
325             ;
326 
327             RegisterType.HostControl.Define(this)
328                 .WithFlag(0, name: "bus reset",
329                         valueProviderCallback: _ => false,
330                         writeCallback: (_, val) =>
331                         {
332                             if(val)
333                             {
334                                 busEventInterruptRequest.Value = true;
335                                 UpdateInterrupts();
336                             }
337                         })
338                 .WithTag("FRMRST - Reset the SOF frame counter", 1, 1)
339                 .WithTag("SAMPLEBUS - Sample the state of the USB bus", 2, 1)
340                 .WithTag("SIGRSM - Signal a bus resume event", 3, 1)
341                 .WithTag("RCVTOG - Set or clear the data toggle value for a data transfer", 4, 2)
342                 .WithTag("SNDTOG - Set or clear the data toggle value for a data transfer", 6, 2)
343             ;
344 
345             RegisterType.HostTransfer.Define(this)
346                 .WithValueField(0, 4, out var ep, name: "ep")
347                 .WithFlag(4, out var setup, name: "setup")
348                 .WithFlag(5, out var outnin, name: "outnin")
349                 .WithTag("ISO", 6, 1)
350                 .WithFlag(7, out var hs, name: "hs")
351                 .WithWriteCallback((_, v) => { HandleHostTransfer((uint)ep.Value, setup.Value, outnin.Value, hs.Value); })
352             ;
353 
354             RegisterType.HostResult.Define(this)
355                 .WithTag("HRSLT - Host result", 0, 4)
356                 .WithTag("RCVTOGRD - Resulting data toggle value for IN transfers", 4, 1)
357                 .WithTag("SNDTOGRD - Resulting data toggle value for OUT transfers", 5, 1)
358                 .WithFlag(6, out kStatus, name: "KSTATUS - Sample the state of the USB bus")
359                 .WithFlag(7, out jStatus, name: "JSTATUS - Sample the state of the USB bus")
360             ;
361         }
362 
HandleCommandByte(byte data)363         private void HandleCommandByte(byte data)
364         {
365             var dir = (CommandDirection)((data >> 1) & 0x1);
366             lastRegister = (RegisterType)(data >> 3);
367 
368             this.Log(LogLevel.Noisy, "Command byte detected: operation: {0}, register: {1} (0x{1:X})", dir, lastRegister);
369 
370             switch(dir)
371             {
372                 case CommandDirection.Write:
373                     state = State.Writing;
374                     break;
375 
376                 case CommandDirection.Read:
377                     state = State.Reading;
378                     break;
379 
380                 default:
381                     throw new ArgumentException("Unsupported command direction");
382             }
383         }
384 
HandleHostTransfer(uint ep, bool setup, bool outnin, bool hs)385         private void HandleHostTransfer(uint ep, bool setup, bool outnin, bool hs)
386         {
387             if(setup && hs)
388             {
389                 this.Log(LogLevel.Error, "Both SETUP and HS bits set for a host transfer - ignoring it!");
390                 return;
391             }
392 
393             var device = this.ChildCollection.Values.FirstOrDefault(x => x.USBCore.Address == deviceAddress.Value);
394             if(device == null)
395             {
396                 this.Log(LogLevel.Warning, "Tried to send setup packet to a device with address 0x{0:X}, but it's not connected", deviceAddress.Value);
397 
398                 // setting the IRQ is necessary to allow communication right after the usb device address has changed
399                 hostTransferDoneInterruptRequest.Value = true;
400                 UpdateInterrupts();
401 
402                 return;
403             }
404 
405             if(setup)
406             {
407                 this.Log(LogLevel.Noisy, "Setup TX");
408                 if(ep != 0)
409                 {
410                     this.Log(LogLevel.Error, "This model does not support SETUP packets on EP different than 0");
411                     return;
412                 }
413 
414                 HandleSetup(device);
415             }
416             else if(hs)
417             {
418                 this.Log(LogLevel.Noisy, "Handshake {0}", outnin ? "out" : "in");
419 
420                 hostTransferDoneInterruptRequest.Value = true;
421                 UpdateInterrupts();
422             }
423             else
424             {
425                 USBEndpoint endpoint = null;
426                 if(ep != 0)
427                 {
428                     endpoint = device.USBCore.GetEndpoint((int)ep, outnin ? Direction.HostToDevice : Direction.DeviceToHost);
429                     if(endpoint == null)
430                     {
431                         this.Log(LogLevel.Error, "Tried to access a non-existing EP #{0}", ep);
432 
433                         hostTransferDoneInterruptRequest.Value = true;
434                         UpdateInterrupts();
435                         return;
436                     }
437                 }
438 
439                 if(outnin)
440                 {
441                     this.Log(LogLevel.Noisy, "Bulk out");
442                     HandleBulkOut(endpoint);
443                 }
444                 else
445                 {
446                     this.Log(LogLevel.Noisy, "Bulk in");
447                     HandleBulkIn(endpoint);
448                 }
449             }
450         }
451 
GenerateFrameInterrupt()452         private void GenerateFrameInterrupt()
453         {
454             this.Log(LogLevel.Noisy, "Generating frame interrupt");
455 
456             frameGeneratorInterruptRequest.Value = true;
457             UpdateInterrupts();
458         }
459 
HandleBulkOut(USBEndpoint endpoint)460         private void HandleBulkOut(USBEndpoint endpoint)
461         {
462             if(endpoint != null)
463             {
464                 if((int)sendByteCount.Value != sendQueue.Count)
465                 {
466                     this.Log(LogLevel.Warning, "Requested to send BULK out {0} bytes of data, but there are {1} bytes in the queue.", sendByteCount.Value, sendQueue.Count);
467                 }
468 
469                 var bytesToSend = sendQueue.DequeueRange((int)sendByteCount.Value);
470                 this.Log(LogLevel.Noisy, "Writing {0} bytes to the device", bytesToSend.Length);
471                 endpoint.WriteData(bytesToSend);
472 
473                 sendDataBufferAvailableInterruptRequest.Value = true;
474             }
475 
476             hostTransferDoneInterruptRequest.Value = true;
477             UpdateInterrupts();
478         }
479 
HandleBulkIn(USBEndpoint endpoint)480         private void HandleBulkIn(USBEndpoint endpoint)
481         {
482             if(endpoint != null)
483             {
484                 this.Log(LogLevel.Noisy, "Initiated read from the device");
485                 endpoint.SetDataReadCallbackOneShot((_, data) =>
486                 {
487                     this.Log(LogLevel.Noisy, "Received data from the device");
488 #if DEBUG_PACKETS
489                     this.Log(LogLevel.Noisy, Misc.PrettyPrintCollectionHex(data));
490 #endif
491                     EnqueueReceiveData(data);
492 
493                     hostTransferDoneInterruptRequest.Value = true;
494                     UpdateInterrupts();
495                 });
496             }
497             else
498             {
499                 hostTransferDoneInterruptRequest.Value = true;
500                 UpdateInterrupts();
501             }
502         }
503 
HandleSetup(IUSBDevice device)504         private void HandleSetup(IUSBDevice device)
505         {
506             var data = setupQueue.DequeueAll();
507             if(!Packet.TryDecode<SetupPacket>(data, out var setupPacket))
508             {
509                 this.Log(LogLevel.Error, "Could not decode SETUP packet - some data might be lost! Bytes were: {0}", Misc.PrettyPrintCollectionHex(data));
510                 return;
511             }
512 
513             device.USBCore.HandleSetupPacket(setupPacket, response =>
514             {
515                 EnqueueReceiveData(response);
516 
517                 hostTransferDoneInterruptRequest.Value = true;
518                 UpdateInterrupts();
519             });
520         }
521 
EnqueueReceiveData(IEnumerable<byte> data)522         private void EnqueueReceiveData(IEnumerable<byte> data)
523         {
524             if(receiveQueue.EnqueueRange(data) > 0)
525             {
526                 receiveDataAvailableInterruptRequest.Value = true;
527             }
528         }
529 
530         private RegisterType lastRegister;
531         private State state;
532 
533         private IFlagRegisterField connectDisconnectInterruptRequest;
534         private IFlagRegisterField connectDisconnectInterruptEnable;
535         private IFlagRegisterField interruptEnable;
536         private IFlagRegisterField kStatus;
537         private IFlagRegisterField jStatus;
538         private IFlagRegisterField busEventInterruptRequest;
539         private IFlagRegisterField busEventInterruptEnable;
540         private IFlagRegisterField frameGeneratorInterruptRequest;
541         private IFlagRegisterField frameGeneratorInterruptEnable;
542         private IFlagRegisterField hostTransferDoneInterruptRequest;
543         private IFlagRegisterField hostTransferDoneInterruptEnable;
544         private IFlagRegisterField receiveDataAvailableInterruptRequest;
545         private IFlagRegisterField receiveDataAvailableInterruptEnable;
546         private IValueRegisterField deviceAddress;
547         private IValueRegisterField sendByteCount;
548         private IFlagRegisterField sendDataBufferAvailableInterruptRequest;
549         private IFlagRegisterField sendDataBufferAvailableInterruptEnable;
550         private IFlagRegisterField startOfFramePacketsGenerationEnable;
551         private IFlagRegisterField hostMode;
552         private IFlagRegisterField oscillatorOKInterruptEnable;
553         private IFlagRegisterField oscillatorOKInterruptRequest;
554         private readonly Queue<byte> setupQueue;
555         private readonly Queue<byte> receiveQueue;
556         private readonly Queue<byte> sendQueue;
557         private readonly IManagedThread bumper;
558 
559         private const byte ChipRevision = 0x13;
560         private const int BumpsPerSecond = 100;
561         private const int FifoSize = 64;
562 
563         private enum State
564         {
565             Idle,
566             Writing,
567             Reading
568         }
569 
570         private enum CommandDirection
571         {
572             Read = 0,
573             Write = 1
574         }
575 
576         [RegisterMapper.RegistersDescription]
577         private enum RegisterType
578         {
579             // R0 is not available in HOST mode
580             ReceiveFifo = 1,
581             SendFifo = 2,
582             // R3 is not available in HOST mode
583             SetupFifo = 4,
584             // R5 is not available in HOST mode
585             ReceiveQueueLength = 6,
586             SendQueueLength = 7,
587             // R8-R12 are not available in HOST mode
588             USBIrqPending = 13,
589             USBIrqEnabled = 14,
590             USBControl = 15,
591             CPUControl = 16,
592             PinControl = 17,
593             Revision = 18,
594             // R19 is not available in HOST mode
595             IOPins1 = 20,
596             IOPins2 = 21,
597             GeneralPurposeInIrqPending = 22,
598             GeneralPurposeInIrqEnabled = 23,
599             GeneralPurposeInIrqPolarity = 24,
600             HostIrqPending = 25,
601             HostIrqEnabled = 26,
602             Mode = 27,
603             PeripheralAddress = 28,
604             HostControl = 29,
605             HostTransfer = 30,
606             HostResult = 31
607         }
608     }
609 }
610