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 using System.Collections.Generic;
8 
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Utilities;
15 
16 namespace Antmicro.Renode.Peripherals.I2C
17 {
18     public class NPCX_SMBus : SimpleContainer<II2CPeripheral>, IBytePeripheral, IProvidesRegisterCollection<ByteRegisterCollection>, IKnownSize
19     {
NPCX_SMBus(IMachine machine)20         public NPCX_SMBus(IMachine machine) : base(machine)
21         {
22             RegistersCollection = new ByteRegisterCollection(this);
23             IRQ = new GPIO();
24 
25             txQueue = new Queue<byte>();
26             rxQueue = new Queue<byte>();
27 
28             DefineRegisters();
29             DefineFIFORegisters();
30         }
31 
ReadByte(long offset)32         public byte ReadByte(long offset)
33         {
34             return RegistersCollection.Read(offset);
35         }
36 
WriteByte(long offset, byte value)37         public void WriteByte(long offset, byte value)
38         {
39             RegistersCollection.Write(offset, value);
40         }
41 
Reset()42         public override void Reset()
43         {
44             RegistersCollection.Reset();
45 
46             CurrentState = State.Idle;
47             activePeripheral = null;
48 
49             IRQ.Unset();
50             rxQueue.Clear();
51             txQueue.Clear();
52         }
53 
54         public long Size => 0x20;
55 
56         public ByteRegisterCollection RegistersCollection { get; }
57 
58         public GPIO IRQ { get; }
59 
HandleWrite(byte value)60         private void HandleWrite(byte value)
61         {
62             switch(CurrentState)
63             {
64                 case State.Idle:
65                     this.Log(LogLevel.Warning, "Tried writing to SMB_DAT while in idle state");
66                     break;
67 
68                 case State.Start:
69                     // NOTE: On Repeated Start we have to first send all the data
70                     HandleStop();
71 
72                     var address = value >> 1;
73                     var read = (value & 1) > 0;
74 
75                     if(!TryGetByAddress(address, out activePeripheral))
76                     {
77                         this.Log(LogLevel.Warning, "No I2C device with address {0} is connected", address);
78                         negativeAcknowledge.Value = true;
79                         CurrentState = State.Idle;
80                         UpdateInterrupts();
81                         break;
82                     }
83 
84                     CurrentState = read ? State.Reading : State.Writing;
85                     if(CurrentState == State.Reading)
86                     {
87                         TryReadFromPeripheral();
88                     }
89                     else
90                     {
91                         readyForTransaction.Value = true;
92                         UpdateInterrupts();
93                     }
94                     break;
95 
96                 case State.Writing:
97                     txQueue.Enqueue(value);
98 
99                     readyForTransaction.Value |= true;
100                     rxFullTxEmptyStatus.Value |= true;
101                     UpdateInterrupts();
102                     break;
103 
104                 default:
105                     this.Log(LogLevel.Warning, "Trying to write data in wrong state: {0}, ignoring", CurrentState);
106                     return;
107             }
108         }
109 
HandleStop()110         private void HandleStop()
111         {
112             if(CurrentState == State.Start || CurrentState == State.Writing)
113             {
114                 if(txQueue.Count > 0)
115                 {
116                     activePeripheral?.Write(txQueue.ToArray());
117                     txQueue.Clear();
118                 }
119                 rxFullTxEmptyStatus.Value = true;
120             }
121 
122             if(CurrentState != State.Start)
123             {
124                 this.Log(LogLevel.Debug, "Finishing transmission");
125 
126                 CurrentState = State.Idle;
127                 activePeripheral?.FinishTransmission();
128             }
129 
130             activePeripheral = null;
131             readyForTransaction.Value = false;
132             UpdateInterrupts();
133         }
134 
TryReadFromPeripheral()135         private void TryReadFromPeripheral()
136         {
137             if(!fifoMode.Value)
138             {
139                 var data = activePeripheral.Read(1);
140                 rxQueue.Enqueue(data.Length > 0 ? data[0] : (byte)0);
141 
142                 readyForTransaction.Value = true;
143                 UpdateInterrupts();
144                 return;
145             }
146 
147             rxQueue.EnqueueRange(activePeripheral.Read((int)rxFIFOThreshold.Value));
148 
149             rxFullTxEmptyStatus.Value |= rxQueue.Count == FIFOLength;
150             rxFIFOThresholdStatus.Value |= fifoMode.Value && rxQueue.Count > 0 && rxQueue.Count == (int)rxFIFOThreshold.Value;
151 
152             if((rxQueue.Count > 0) ||
153                (rxFIFOThresholdStatus.Value && !rxFIFOThresholdInterrupt.Value))
154             {
155                 // NOTE: This should be set to true if:
156                 //  * there is data to read from the FIFO
157                 //  * we hit RX FIFO threshold and respective interrupt is _not_ enabled
158                 readyForTransaction.Value = true;
159             }
160 
161             UpdateInterrupts();
162         }
163 
UpdateInterrupts()164         private void UpdateInterrupts()
165         {
166             var interrupt = false;
167             if(interruptsEnabled.Value)
168             {
169                 interrupt |= negativeAcknowledge.Value;
170                 interrupt |= readyForTransaction.Value;
171                 interrupt |= rxFIFOThresholdInterrupt.Value && rxFIFOThresholdStatus.Value;
172                 interrupt |= rxFullTxEmptyInterrupt.Value && rxFullTxEmptyStatus.Value;
173             }
174 
175             this.Log(LogLevel.Debug, "Setting IRQ to {0} (interruptsEnabled={1}, negativeAcknowledge={2}, readyForTransaction={3} rxFIFOThreshold={4}, rxFullTxEmpty={5})",
176                 interrupt,
177                 interruptsEnabled.Value,
178                 negativeAcknowledge.Value,
179                 readyForTransaction.Value,
180                 rxFIFOThresholdInterrupt.Value && rxFIFOThresholdStatus.Value,
181                 rxFullTxEmptyInterrupt.Value && rxFullTxEmptyStatus.Value);
182 
183             IRQ.Set(interrupt);
184         }
185 
DefineRegisters()186         private void DefineRegisters()
187         {
188             Registers.SerialData.Define(this)
189                 .WithValueField(0, 8, name: "SMB_DAT (SMBus Data)",
190                     valueProviderCallback: _ =>
191                     {
192                         if(rxQueue.TryDequeue(out var val))
193                         {
194                             UpdateInterrupts();
195                             return val;
196                         }
197 
198                         readyForTransaction.Value = false;
199                         if(lastPacket.Value)
200                         {
201                             HandleStop();
202                         }
203                         return (byte)0;
204                     },
205                     writeCallback: (_, value) => HandleWrite((byte)value))
206             ;
207 
208             Registers.Status.Define(this)
209                 .WithFlag(0, name: "XMIT (Transmit Mode)",
210                     valueProviderCallback: _ => CurrentState == State.Writing)
211                 .WithFlag(1, FieldMode.Read, name: "MASTER (Master Mode)",
212                     valueProviderCallback: _ => CurrentState != State.Idle)
213                 .WithTaggedFlag("NMATCH (New Match)", 2)
214                 .WithTaggedFlag("STASTR (Stall After Start)", 3)
215                 .WithFlag(4, out negativeAcknowledge, FieldMode.Read | FieldMode.WriteOneToClear, name: "NEGACK (Negative Acknowledge)")
216                 .WithTaggedFlag("BER (Bus Error)", 5)
217                 // NOTE: This field should be set to true when:
218                 // * there is data to read from the RX buffer in receive mode
219                 // * TX buffer is empty in transmit mode
220                 // * in FIFO mode, either RX or TX threshold happened, and their respective interrupts aren't enabled
221                 .WithFlag(6, out readyForTransaction, name: "SDAST (SDA Status)")
222                 .WithTaggedFlag("SLVSTP (Slave Stop)", 7)
223                 .WithWriteCallback((_, __) => UpdateInterrupts());
224             ;
225 
226             Registers.ControlStatus.Define(this)
227                 .WithFlag(0, FieldMode.Read, name: "BUSY (Module Busy)",
228                     valueProviderCallback: _ => CurrentState != State.Idle)
229                 .WithTaggedFlag("BB (Bus Ready)", 1)
230                 .WithTaggedFlag("MATCH (Address Match)", 2)
231                 .WithTaggedFlag("GCMATCH (Global Call Match)", 3)
232                 .WithTaggedFlag("TSDA (Test SDA Line)", 4)
233                 .WithTaggedFlag("TGSCL (Toggle SCL Line)", 5)
234                 .WithTaggedFlag("MATCHAF (Match Address Field)", 6)
235                 .WithTaggedFlag("ARPMATCH (ARP Address Match)", 7)
236             ;
237 
238             Registers.Control1.Define(this)
239                 .WithFlag(0, FieldMode.Read | FieldMode.WriteOneToClear, name: "START",
240                     writeCallback: (_, value) =>
241                     {
242                         if(value)
243                         {
244                             CurrentState = State.Start;
245                             readyForTransaction.Value = true;
246 
247                             UpdateInterrupts();
248                         }
249                     }
250                 )
251                 .WithFlag(1, FieldMode.Read | FieldMode.WriteOneToClear, name: "STOP",
252                     writeCallback: (_, value) => { if(value) HandleStop(); })
253                 .WithFlag(2, out interruptsEnabled, name: "INTEN (Interrupt Enable)")
254                 .WithTaggedFlag("EOBINTE (End of 'Busy' Interrupt Enable)", 3)
255                 .WithTaggedFlag("ACK (Acknowledge)", 4)
256                 .WithTaggedFlag("GCMEN (Global Call Match Enable)", 5)
257                 .WithTaggedFlag("NMINTE (New Match Interrupt Enable)", 6)
258                 .WithTaggedFlag("STASTRE (Stall After Start Enable)", 7)
259                 .WithChangeCallback((_, __) => UpdateInterrupts())
260             ;
261 
262             var ownAddressRegisters = new[]
263             {
264                 Registers.OwnAddress1,
265                 Registers.OwnAddress2,
266                 Registers.OwnAddress3,
267                 Registers.OwnAddress4,
268                 Registers.OwnAddress5,
269                 Registers.OwnAddress6,
270                 Registers.OwnAddress7,
271                 Registers.OwnAddress8,
272             };
273 
274             foreach(var registerAddress in ownAddressRegisters)
275             {
276                 var register = (long)registerAddress < CommonRegisterSize ?
277                     registerAddress.Define(this) :
278                     registerAddress.DefineConditional(this, () => FirstBankSelected)
279                 ;
280 
281                 register
282                     .WithTag("ADDR (Address)", 0, 7)
283                     .WithTaggedFlag("SAEN (Slave Address Enable)", 7)
284                 ;
285             }
286 
287             Registers.Control2.Define(this)
288                 .WithFlag(0, out moduleEnabled, name: "ENABLE")
289                 .WithTag("SCLFRQ6-0 (SCL Frequency bits 6 through 0)", 1, 7)
290             ;
291 
292             Registers.Control3.Define(this)
293                 .WithTag("SCLFRQ8-7 (SCL Frequency bits 8 and 7)", 0, 2)
294                 .WithTaggedFlag("ARPMEN (ARP Match Enable)", 2)
295                 .WithTaggedFlag("SLP_START (Start Detect in Sleep Enable)", 3)
296                 // XXX: Don't allow for Slave mode when this field is set
297                 .WithFlag(4, name: "400K_MODE (400 kHz Master Enable)")
298                 .WithFlag(5, out bankSelect, name: "BNK_SEL (Bank Select)")
299                 .WithFlag(6, name: "SDA_LVL (SDA Level)",
300                     valueProviderCallback: _ => CurrentState == State.Idle)
301                 .WithFlag(7, name: "SCL_LVL (SCL Level)",
302                     valueProviderCallback: _ => CurrentState == State.Idle)
303             ;
304 
305             var busTimeoutRegister = new ByteRegister(this)
306                 .WithTag("TO_CKDIV (Timeout Clock Divisor)", 0, 6)
307                 .WithTaggedFlag("T_OUTIE (Timeout Interrupt Enable)", 6)
308                 .WithTaggedFlag("T_OUTST (Timeout Status)", 7)
309             ;
310 
311             RegistersCollection.AddRegister((long)Registers.BusTimeout, busTimeoutRegister);
312             RegistersCollection.AddConditionalRegister((long)Registers.BusTimeout2, busTimeoutRegister, () => !FirstBankSelected);
313 
314             Registers.ControlStatus2.DefineConditional(this, () => FirstBankSelected)
315                 .WithTaggedFlag("MATCHA1F (Match Address 1 Field)", 0)
316                 .WithTaggedFlag("MATCHA2F (Match Address 2 Field)", 1)
317                 .WithTaggedFlag("MATCHA3F (Match Address 3 Field)", 2)
318                 .WithTaggedFlag("MATCHA4F (Match Address 4 Field)", 3)
319                 .WithTaggedFlag("MATCHA5F (Match Address 5 Field)", 4)
320                 .WithTaggedFlag("MATCHA6F (Match Address 6 Field)", 5)
321                 .WithTaggedFlag("MATCHA7F (Match Address 7 Field)", 6)
322                 .WithTaggedFlag("INTSTS (Interrupt Status)", 7)
323             ;
324 
325             Registers.ControlStatus3.DefineConditional(this, () => FirstBankSelected)
326                 .WithTaggedFlag("MATCHA8F (Match Address 8 Field)", 0)
327                 .WithReservedBits(1, 6)
328                 .WithTaggedFlag("EO_BUSY (End of 'Busy')", 7)
329             ;
330 
331             Registers.Control4.DefineConditional(this, () => FirstBankSelected)
332                 .WithTag("GLDT (SDA Hold Time)", 0, 6)
333                 .WithReservedBits(6, 1)
334                 .WithTaggedFlag("LVL_WE (Level Control Write Enable)", 7)
335             ;
336 
337             Registers.SCLLowTime.DefineConditional(this, () => FirstBankSelected)
338                 .WithTag("SCLLT7-0 (SCL Low Time)", 0, 8)
339             ;
340 
341             Registers.FIFOControl.DefineConditional(this, () => FirstBankSelected)
342                 .WithReservedBits(0, 4)
343                 .WithFlag(4, out fifoMode, name: "FIFO_EN (Enable FIFO Mode)")
344                 .WithReservedBits(5, 3)
345             ;
346 
347             Registers.SCLHighTime.DefineConditional(this, () => FirstBankSelected)
348                 .WithTag("SCLHT7-0 (SCL High Time)", 0, 8)
349             ;
350         }
351 
DefineFIFORegisters()352         private void DefineFIFORegisters()
353         {
354             Registers.FIFOControlAndStatus.DefineConditional(this, () => !FirstBankSelected)
355                 .WithReservedBits(0, 1)
356                 .WithFlag(1, out rxFullTxEmptyStatus, FieldMode.Read | FieldMode.WriteOneToClear, name: "RXF_TXE (Rx-FIFO Full, Tx-FIFO Empty Status)")
357                 .WithReservedBits(2, 1)
358                 .WithFlag(3, out rxFullTxEmptyInterrupt, name: "RFTE_IE (Rx-FIFO Full, Tx-FIFO Empty Interrupt Enable)")
359                 .WithReservedBits(4, 2)
360                 .WithFlag(6, FieldMode.WriteOneToClear, name: "CLR_FIFO (Clear FIFOs)",
361                     writeCallback: (_, value) =>
362                     {
363                         if(value)
364                         {
365                             rxQueue.Clear();
366                             txQueue.Clear();
367                         }
368                     })
369                 .WithTaggedFlag("SLVRSTR (Slave Start or Restart)", 7)
370                 .WithChangeCallback((_, __) => UpdateInterrupts())
371             ;
372 
373             Registers.TxFIFOControl.DefineConditional(this, () => !FirstBankSelected)
374                 .WithTag("TX_THR (Tx-FIFO Threshold)", 0, 6)
375                 .WithTaggedFlag("THR_TXIE (Threshold Tx-FIFO Interrupt Enable)", 6)
376                 .WithReservedBits(7, 1)
377             ;
378 
379             Registers.FrameTimeout.DefineConditional(this, () => !FirstBankSelected)
380                 .WithTag("FR_LEN_TO (Frame Length Timeout)", 0, 6)
381                 .WithTaggedFlag("FRTOIE (Frame Timeout Interrupt Enable)", 6)
382                 .WithTaggedFlag("FRTOST (Frame Timeout Status)", 7)
383             ;
384 
385             Registers.PECData.DefineConditional(this, () => !FirstBankSelected)
386                 .WithTag("PEC_DATA (PEC Data)", 0, 7)
387             ;
388 
389             Registers.TxFIFOStatus.DefineConditional(this, () => !FirstBankSelected)
390                 .WithTag("TX_BYTES (Tx-FIFO Number of Bytes)", 0, 6)
391                 .WithTaggedFlag("TX_THST (Tx-FIFO Threshold Status)", 6)
392                 .WithReservedBits(7, 1)
393             ;
394 
395             Registers.RxFIFOStatus.DefineConditional(this, () => !FirstBankSelected)
396                 .WithValueField(0, 6, name: "RX_BYTES (Rx-FIFO Number of Bytes)",
397                     valueProviderCallback: _ => (uint)rxQueue.Count)
398                 .WithFlag(6, out rxFIFOThresholdStatus, FieldMode.Read | FieldMode.WriteOneToClear, name: "RX_THST (Rx-FIFO Threshold Status)")
399                 .WithReservedBits(7, 1)
400                 .WithChangeCallback((_, __) => UpdateInterrupts())
401             ;
402 
403             Registers.RxFIFOControl.DefineConditional(this, () => !FirstBankSelected)
404                 .WithValueField(0, 6, out rxFIFOThreshold, name: "RX_THR (Rx-FIFO Threshold)")
405                 .WithFlag(6, out rxFIFOThresholdInterrupt, name: "THR_RXIE (Threshold Rx-FIFO Interrupt Enable)")
406                 .WithFlag(7, out lastPacket, name: "LAST_PEC (Last Byte or PEC Byte)")
407                 .WithWriteCallback((_, __) => UpdateInterrupts());
408             ;
409         }
410 
411         private bool FirstBankSelected => !fifoMode.Value || !bankSelect.Value;
412         private State CurrentState
413         {
414             get => currentState;
415             set
416             {
417                 if(!moduleEnabled.Value)
418                 {
419                     this.Log(LogLevel.Warning, "Tried to change state to {0}, but module is disabled", value);
420                     return;
421                 }
422                 this.Log(LogLevel.Debug, "Changing state from {0} to {1}", currentState, value);
423                 currentState = value;
424             }
425         }
426 
427         private readonly Queue<byte> txQueue;
428         private readonly Queue<byte> rxQueue;
429 
430         private State currentState;
431         private II2CPeripheral activePeripheral;
432 
433         private IFlagRegisterField interruptsEnabled;
434         private IFlagRegisterField rxFIFOThresholdInterrupt;
435         private IFlagRegisterField negativeAcknowledge;
436         private IFlagRegisterField rxFullTxEmptyStatus;
437         private IFlagRegisterField rxFullTxEmptyInterrupt;
438         private IFlagRegisterField rxFIFOThresholdStatus;
439 
440         private IFlagRegisterField fifoMode;
441         private IFlagRegisterField bankSelect;
442         private IFlagRegisterField moduleEnabled;
443         private IFlagRegisterField lastPacket;
444 
445         private IValueRegisterField rxFIFOThreshold;
446         private IFlagRegisterField readyForTransaction;
447         private const long CommonRegisterSize = 0x10;
448         private const long FIFOLength = 32;
449 
450         private enum State
451         {
452             Idle,
453             Start,
454             Reading,
455             Writing,
456         }
457 
458         private enum Registers
459         {
460             // Common registers
461             SerialData = 0x00,
462             Status = 0x02,
463             ControlStatus = 0x04,
464             Control1 = 0x06,
465             OwnAddress1 = 0x08,
466             Control2 = 0x0A,
467             OwnAddress2 = 0x0C,
468             Control3 = 0x0E,
469             BusTimeout = 0x0F,
470 
471             // Bank0 registers
472             OwnAddress3 = 0x10,
473             OwnAddress7 = 0x11,
474             OwnAddress4 = 0x12,
475             OwnAddress8 = 0x13,
476             OwnAddress5 = 0x14,
477             OwnAddress6 = 0x16,
478             ControlStatus2 = 0x18,
479             ControlStatus3 = 0x19,
480             Control4 = 0x1A,
481             SCLLowTime = 0x1C,
482             FIFOControl = 0x1D,
483             SCLHighTime = 0x1E,
484 
485             // Bank1 registers
486             FIFOControlAndStatus = 0x10,
487             TxFIFOControl = 0x12,
488             FrameTimeout = 0x13,
489             BusTimeout2 = 0x14,
490             PECData = 0x16,
491             TxFIFOStatus = 0x1A,
492             RxFIFOStatus = 0x1C,
493             RxFIFOControl = 0x1E,
494         }
495     }
496 }
497