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