1 //
2 // Copyright (c) 2010-2018 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
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.Peripherals.Bus;
14 using Antmicro.Renode.Logging;
15 using Antmicro.Renode.Exceptions;
16 
17 namespace Antmicro.Renode.Peripherals.I2C
18 {
19     public class EFM32GGI2CController : SimpleContainer<II2CPeripheral>, IDoubleWordPeripheral
20     {
21 
EFM32GGI2CController(IMachine machine)22         public EFM32GGI2CController(IMachine machine) : base(machine)
23         {
24             IRQ = new GPIO();
25         }
26 
27         // TODO: Implement checks on if the controller is enabled where needed, ie if bit 0 in i2cn_trl is set
28         // TODO: Implment all interrupt handling p 419 EFM32GG Ref Man
29         // TODO: handle and send ack/nack
30 
ReadDoubleWord(long offset)31         public virtual uint ReadDoubleWord(long offset)
32         {
33             this.Log(LogLevel.Noisy, "read {0}", (Registers)offset);
34 
35             switch((Registers)offset)
36             {
37             case Registers.I2Cn_CTRL:
38                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_ctrl);
39                 return i2cn_ctrl;
40             case Registers.I2Cn_CMD:
41                 return 0;
42             case Registers.I2Cn_STATE:
43                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_state);
44                 return i2cn_state;
45             case Registers.I2Cn_STATUS:
46                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_status);
47                 return i2cn_status;
48             case Registers.I2Cn_CLKDIV:
49                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_clkdiv);
50                 return i2cn_clkdiv;
51             case Registers.I2Cn_SADDR:
52                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_saddr);
53                 return i2cn_saddr;
54             case Registers.I2Cn_RXDATA:
55                 // Set underflow flag if buffer is empty
56                 uint i2cn_rxdata = 0;
57                 if(!rxBufferFull)
58                 {
59                     SetInterrupt(1 << (int)Interrupts.RXUF);
60                 }
61                 else
62                 {
63                     i2cn_rxdata = rxBuffer;
64                     rxBuffer = 0;
65                     rxBufferFull = false;
66                     ShiftRxData();
67                 }
68                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_rxdata);
69                 return i2cn_rxdata;
70             case Registers.I2Cn_RXDATAP:
71                 this.Log(LogLevel.Noisy, "register value = {0}", rxBuffer);
72                 return rxBuffer;
73             case Registers.I2Cn_TXDATA:
74                 return 0;
75             case Registers.I2Cn_IF:
76                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_if);
77                 return i2cn_if;
78             case Registers.I2Cn_IFS:
79                 return 0;
80             case Registers.I2Cn_IFC:
81                 return 0;
82             case Registers.I2Cn_IEN:
83                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_ien);
84                 return i2cn_ien;
85             case Registers.I2Cn_ROUTE:
86                 this.Log(LogLevel.Noisy, "register value = {0}", i2cn_route);
87                 return i2cn_route;
88             default:
89                 this.Log(LogLevel.Warning, "Unexpected read at 0x{0:X}.", offset);
90                 return 0;
91             }
92         }
93 
WriteDoubleWord(long offset, uint value)94         public virtual void WriteDoubleWord(long offset, uint value)
95         {
96             this.Log(LogLevel.Noisy, "write {0}, value 0x{1:X}", (Registers)offset, value);
97 
98             switch((Registers)offset)
99             {
100             case Registers.I2Cn_CTRL:
101                 HandleCtrl(value);
102                 break;
103             case Registers.I2Cn_CMD:
104                 HandleCommand(value);
105                 break;
106             case Registers.I2Cn_STATE:
107                 break;
108             case Registers.I2Cn_STATUS:
109                 break;
110             case Registers.I2Cn_CLKDIV:
111                 i2cn_clkdiv = value;
112                 break;
113             case Registers.I2Cn_SADDR:
114                 i2cn_saddr = value;
115                 break;
116             case Registers.I2Cn_RXDATA:
117                 break;
118             case Registers.I2Cn_RXDATAP:
119                 break;
120             case Registers.I2Cn_TXDATA:
121                 LoadTxData(value);
122                 break;
123             case Registers.I2Cn_IF:
124                 break;
125             case Registers.I2Cn_IFS:
126                 SetInterrupt(value);
127                 break;
128             case Registers.I2Cn_IFC:
129                 ClearInterrupt(value);
130                 break;
131             case Registers.I2Cn_IEN:
132                 EnableInterrupt(value);
133                 break;
134             case Registers.I2Cn_ROUTE:
135                 i2cn_route = value;
136                 break;
137             default:
138                 this.Log(LogLevel.Warning, "Unexpected write at 0x{0:X}, value 0x{1:X}.", offset, value);
139                 break;
140             }
141         }
142 
Reset()143         public override void Reset()
144         {
145             this.Log(LogLevel.Noisy, "Reset");
146             i2cn_ctrl = 0;
147             i2cn_cmd = 0;
148             i2cn_state = 0;
149             i2cn_status = 0;
150             i2cn_clkdiv = 0;
151             i2cn_saddr = 0;
152             i2cn_if = 0;
153             i2cn_ien = 0;
154             i2cn_route = 0;
155             rxBuffer = 0;
156             rxBufferFull = false;
157             txBuffer = 0;
158             txBufferFull = false;
159             rxShiftRegister = 0;
160             rxShiftRegisterFull = false;
161             txShiftRegister = 0;
162             txShiftRegisterFull = false;
163             transferState = TransferState.Idle;
164             txpacket.Clear();
165             rxpacket.Clear();
166         }
167 
HandleCtrl(uint value)168         private void HandleCtrl(uint value)
169         {
170             if(i2cn_ctrl == value)
171             {
172                 this.Log(LogLevel.Noisy, "Received Ctrl register update but value is same as current {0}", (Commands)i2cn_ctrl);
173                 return;
174             }
175             // If EN bit in I2Cn_CTRL is cleared then reset internal state and terminate any ongoing transfers
176             if(((i2cn_ctrl & 0x1) == 0x1) && ((~value & 0x1) == 1))
177             {
178                 // TODO: Abort ongoing transfers.
179                 Reset();
180             }
181             // If EN bit is set, i2c controller is to be enabled. Set TXBL flag if tx buffer is empty
182             if(((value & 0x1) == 0x1) && ((~i2cn_ctrl & 0x1) == 1))
183             {
184                 if(!txBufferFull && txShiftRegisterFull)
185                 {
186                     SetStatus(Status.TXBL);
187                     SetInterrupt(1 << (int)Interrupts.TXBL);
188                 }
189             }
190             i2cn_ctrl = value;
191             this.Log(LogLevel.Noisy, "Changed Ctrl register to {0}", (Commands)i2cn_ctrl);
192         }
193 
HandleCommand(uint value)194         private void HandleCommand(uint value)
195         {
196             // I2Cn_CMD register is W1, ie write-only and only write of 1 has any action
197             i2cn_cmd = value & 0xFF; //Only bits 0-7 used in I2Cn_CMD register
198 
199             if((i2cn_cmd & (uint)(Commands.START)) == (uint)(Commands.START))
200             {
201                 // SDA line pulled low while SCL line is high
202                 SetInterrupt(1 << (int)Interrupts.START);
203                 // Since we send a packet list a restart means we should send the present list and start on a new
204                 if(((int)transferState & (int)TransferState.StartTrans) == (int)TransferState.StartTrans)
205                 {
206                     transferState = TransferState.RestartTrans;
207                 }
208                 else
209                 {
210                     transferState = TransferState.StartTrans;
211                 }
212                 this.Log(LogLevel.Noisy, "Received START Command");
213                 HandleTransfer();
214             }
215             if((i2cn_cmd & (uint)(Commands.STOP)) == (uint)(Commands.STOP))
216             {
217                 // SDA line pulled high while SCL line is high
218                 SetInterrupt(1 << (int)Interrupts.MSTOP);
219                 transferState = TransferState.StopTrans;
220                 this.Log(LogLevel.Noisy, "Received STOP Command");
221                 HandleTransfer();
222             }
223             if((i2cn_cmd & (uint)(Commands.ACK)) == (uint)(Commands.ACK))
224             {
225                 // TODO: set an internal variable to track and possibly use this, now slave does not expect ACK/NACK
226                 this.Log(LogLevel.Noisy, "Received ACK Command");
227             }
228             if((i2cn_cmd & (uint)(Commands.NACK)) == (uint)(Commands.NACK))
229             {
230                 // TODO: set an internal variable to track and possibly use this, now slave does not expect ACK/NACK
231                 this.Log(LogLevel.Noisy, "Received NACK Command");
232             }
233             if((i2cn_cmd & (uint)(Commands.CONT)) == (uint)(Commands.CONT))
234             {
235                 // TODO: Not implemented yet, set internal variable
236                 this.Log(LogLevel.Noisy, "Received CONT Command");
237             }
238             if((i2cn_cmd & (uint)(Commands.ABORT)) == (uint)(Commands.ABORT))
239             {
240                 // TODO: Not implemented yet
241                 this.Log(LogLevel.Noisy, "Received ABORT Command");
242             }
243             if((i2cn_cmd & (uint)(Commands.CLEARTX)) == (uint)(Commands.CLEARTX))
244             {
245                 // Clear transmit buffer and shift register, and TXBL flags in status register and interrupt flag register
246                 txBuffer = 0;
247                 txBufferFull = false;
248                 txShiftRegister = 0;
249                 txShiftRegisterFull = false;
250                 txpacket.Clear();
251                 ClearStatus(Status.TXBL);
252                 ClearInterrupt((uint)Interrupts.TXBL);
253                 this.Log(LogLevel.Noisy, "Received CLEARTX Command");
254             }
255             if((i2cn_cmd & (uint)(Commands.CLEARPC)) == (uint)(Commands.CLEARPC))
256             {
257                 // TODO: Not implemented yet
258                 this.Log(LogLevel.Noisy, "Received CLEARPC Command");
259             }
260         }
261 
LoadTxData(uint value)262         private void LoadTxData(uint value)
263         {
264             // Check TXBL flags in status register and interrupt flag register to see if there is room for data in transmit buffer
265             // If not then set TXOF flag in status register and return without loading data.
266             this.Log(LogLevel.Noisy, "LoadTxData - status reg = {0}", i2cn_status);
267             if(CheckStatus(Status.TXBL) || (!txBufferFull && !txShiftRegisterFull))
268             {
269                 this.Log(LogLevel.Noisy, "Loaded transmit buffer with data {0}", value);
270                 txBuffer = value;
271                 txBufferFull = true;
272                 if(CheckStatus(Status.TXBL))
273                 {
274                     ClearStatus(Status.TXBL);
275                     ClearInterrupt((uint)Interrupts.TXBL);
276                 }
277                 ShiftTxData();
278             }
279             else
280             {
281                 this.Log(LogLevel.Noisy, "Transmit buffer is full - overflow interrupt flag set");
282                 SetInterrupt(1 << (int)Interrupts.TXOF);
283             }
284         }
285 
ShiftTxData()286         private void ShiftTxData()
287         {
288             // Only set TXC after transmission, how do we check this? Internal state?
289             if((txBufferFull == false) && (txShiftRegisterFull == false))
290             {
291                 SetStatus(Status.TXC);
292                 SetInterrupt(1 << (int)Interrupts.TXC);
293             }
294             // Check if shift register is empty
295             // If so then move data to shift register and set TXBL flags in status register and interrupt flag register
296             if((txShiftRegisterFull == false) && txBufferFull)
297             {
298                 txShiftRegister = txBuffer;
299                 txShiftRegisterFull = true;
300                 txBuffer = 0;
301                 txBufferFull = false;
302                 ClearStatus(Status.TXC);
303                 // TXC interrupt can only be cleared by software
304                 // Signal that transmit buffer is ready for receiving data
305                 SetStatus(Status.TXBL);
306                 SetInterrupt(1 << (int)Interrupts.TXBL);
307                 HandleTransfer();
308             }
309             // Check if BUSHOLD, then start sending slave address
310             if(txShiftRegisterFull && CheckInterrupt(Interrupts.BUSHOLD))
311             {
312                 HandleTransfer();
313             }
314         }
315 
WriteToSlave(int slaveAddress, List<byte> data)316         private void WriteToSlave(int slaveAddress, List<byte> data)
317         {
318             II2CPeripheral slave;
319             if(TryGetByAddress(slaveAddress, out slave))
320             {
321                 slave.Write(data.ToArray());
322             }
323             else
324             {
325                 this.Log(LogLevel.Warning, "Trying to write to nonexisting slave with address \"{0}\"", slaveAddress);
326             }
327         }
328 
HandleTransfer()329         private void HandleTransfer()
330         {
331             // If transmit shift register contains data, package it and handle TXBL flags in status register and interrupt flag register
332             // TODO: see section 16.3.7.4 in EFM32GG Ref manual for master transmitter
333             // TODO: controller acting as slave
334             this.Log(LogLevel.Noisy, "HandleTransfer - txShiftRegister = {0}", txShiftRegister);
335             switch((TransferState)transferState)
336             {
337             case TransferState.Idle:
338                 break;
339             case TransferState.StartTrans:
340                 // Check if data is available in txShiftRegister, if so then use it, else BUS_HOLD
341                 if(txShiftRegisterFull)
342                 {
343                     if(txBufferFull == false)
344                     {
345                         // Signal that transmit buffer is ready for receiving data
346                         if(CheckStatus(Status.TXBL) == false)
347                         {
348                             this.Log(LogLevel.Noisy, "Setting TXBL status and intr flag");
349                             SetStatus(Status.TXBL);
350                             SetInterrupt(1 << (int)Interrupts.TXBL);
351                         }
352                     }
353                     // Create the packet list with bytes for the save device
354                     // Check the current status of the packet list and add whats needed
355                     // TODO: currently no verification on the validity of the data
356                     switch(txpacket.Count)
357                     {
358                     case 0:
359                         this.Log(LogLevel.Noisy, "HandleTransfer - packet count = 0");
360                         // Add mode as first byte in packet list
361                         // Calculate what the mode should be from the device slave address
362                         // First byte in tx buffer should be the 7-bit slave device address + Read/Write bit
363                         slaveAddressForPacket = (byte)((txShiftRegister >> 1) & 0x7F);
364                         if((txShiftRegister & 0x1) == 1)
365                         {
366                             // Mode is Read data from slave
367                             txpacket.Add((byte)(PacketStates.ReadData));
368                             // Since Read data is a command and not followed by address,
369                             // add the stored address and change to ReadTrans
370                             txpacket.Add((byte)(registerAddress));
371                             transferState = TransferState.ReadTrans;
372                         }
373                         else
374                         {
375                             // Mode is Write data to slave
376                             txpacket.Add((byte)(PacketStates.SendData));
377                         }
378                         // Clear to enable more data
379                         txShiftRegister = 0;
380                         txShiftRegisterFull = false;
381                         // We need to emulate receiving an ACK for the data byte sent
382                         SetInterrupt(1 << (int)Interrupts.ACK);
383                         this.Log(LogLevel.Noisy, "HandleTransfer - packet count = 0 - have set ACK interrupt");
384                         break;
385                     case 1:
386                         this.Log(LogLevel.Noisy, "HandleTransfer - packet count = 1");
387                         // Add register address
388                         // Second byte in tx buffer should be the register address
389                         txpacket.Add((byte)(txShiftRegister & 0xFF));
390                         // Save the address in case of upcoming Read
391                         registerAddress = (byte)txShiftRegister;
392                         // Clear to enable more data
393                         txShiftRegister = 0;
394                         txShiftRegisterFull = false;
395                         // We need to emulate receiving an ACK for the data byte sent
396                         SetInterrupt(1 << (int)Interrupts.ACK);
397                         break;
398                     case 2:
399                         this.Log(LogLevel.Noisy, "HandleTransfer - packet count = 2");
400                         // If read mode of single register then we are done and we can change to EndTransfer state
401                         // If multiple reads then we could continue, how to know if this is the case?
402                         // If mode is write, add value for operation
403                         txpacket.Add((byte)(txShiftRegister & 0xFF));
404                         // Clear to enable more data
405                         txShiftRegister = 0;
406                         txShiftRegisterFull = false;
407                         // We need to emulate receiving an ACK for the data byte sent
408                         SetInterrupt(1 << (int)Interrupts.ACK);
409                         break;
410                     default:
411                         this.Log(LogLevel.Noisy, "HandleTransfer - packet count = ", txpacket.Count);
412                         break;
413                     }
414                     ShiftTxData();
415                 }
416                 else
417                 {
418                     this.Log(LogLevel.Noisy, "Setting state and intr flag BUSHOLD");
419                     SetInterrupt(1 << (int)Interrupts.BUSHOLD);
420                     i2cn_state |= (uint)StateReg.BUSHOLD;
421                 }
422                 // Change state to send packet list if we are finished - how do we know?
423                 // TODO: determine if we are finished
424                 if(((int)transferState & (int)TransferState.ReadTrans) == (int)TransferState.ReadTrans)
425                 {
426                     HandleTransfer();
427                 }
428                 break;
429             case TransferState.RestartTrans:
430                 // Restart was issued - send packet list and change transferState to StartTrans
431                 // Use the registration point - ie memory address - for slave communication
432                 if(txpacket.Count > 0)
433                 {
434                     currentAddress = (int)slaveAddressForPacket << 1;
435                     WriteToSlave(currentAddress, txpacket);
436                     txpacket.Clear();
437                 }
438                 transferState = TransferState.StartTrans;
439                 HandleTransfer();
440                 break;
441             case TransferState.ReadTrans:
442                 // Read was issued - send packet list and then read
443                 // Use the registration point - ie memory address - for slave communication
444                 if(txpacket.Count > 0)
445                 {
446                     currentAddress = (int)slaveAddressForPacket << 1;
447                     WriteToSlave(currentAddress, txpacket);
448                     txpacket.Clear();
449                 }
450                 ReadData();
451                 HandleTransfer();
452                 break;
453             case TransferState.StopTrans:
454                 // STOP was issued - send packet list
455                 if(txpacket.Count > 0)
456                 {
457                     currentAddress = (int)slaveAddressForPacket << 1;
458                     WriteToSlave(currentAddress, txpacket);
459                     txpacket.Clear();
460                 }
461                 // If automatic stop on empty is enabled signal STOP
462                 if((i2cn_ctrl & (uint)(Control.AUTOSE)) == (uint)(Control.AUTOSE))
463                 {
464                     SetInterrupt(1 << (int)Interrupts.MSTOP);
465                     transferState = TransferState.Idle;
466                 }
467                 break;
468             default:
469                 break;
470             }
471         }
472 
ShiftRxData()473         private void ShiftRxData()
474         {
475             // Check if the receive buffer is empty, if not then BUSHOLD
476             if(rxBufferFull && rxShiftRegisterFull)
477             {
478                 SetInterrupt(1 << (int)Interrupts.BUSHOLD);
479                 i2cn_state |= (uint)StateReg.BUSHOLD;
480                 return;
481             }
482             if((rxBufferFull == false) && rxShiftRegisterFull)
483             {
484                 rxBuffer = rxShiftRegister;
485                 rxBufferFull = true;
486                 rxShiftRegister = 0;
487                 rxShiftRegisterFull = false;
488                 SetStatus(Status.RXDATAV);
489                 SetInterrupt(1 << (int)Interrupts.RXDATAV);
490                 this.Log(LogLevel.Noisy, "Data byte available for reading ({0})", rxBuffer);
491             }
492             // Strip away the CRC and do not pass it through
493             // TODO: add CRC verification as well
494             if(rxpacket.Count == 1)
495             {
496                 rxpacket.Clear();
497             }
498             if(rxpacket.Count > 1)
499             {
500                 AddRxData();
501             }
502         }
503 
AddRxData()504         private void AddRxData()
505         {
506             if(rxShiftRegisterFull == false)
507             {
508                 // Copy first item in list to receiver shift buffer and remove it
509                 rxShiftRegister = rxpacket.ElementAt(0);
510                 rxShiftRegisterFull = true;
511                 rxpacket.RemoveAt(0);
512                 this.Log(LogLevel.Noisy, "AddRxData - rxShiftRegister = {0}", rxShiftRegister);
513             }
514             ShiftRxData();
515         }
516 
ReadData()517         private void ReadData()
518         {
519             // Fetch packet list from slave device
520             II2CPeripheral slave;
521             transferState = TransferState.Idle;
522             if(!TryGetByAddress(currentAddress, out slave))
523             {
524                 this.Log(LogLevel.Warning, "Trying to read from nonexisting slave with address \"{0}\"", currentAddress);
525                 return;
526             }
527             byte[] rxArray = slave.Read();
528             // Packet list should have a least one byte plus a CRC byte
529             if(rxArray.Length > 1)
530             {
531                 rxpacket = new List<byte>(rxArray);
532                 this.Log(LogLevel.Noisy, "Read data - packet length = {0}", rxpacket.Count);
533                 AddRxData();
534             }
535         }
536 
CheckStatus(Status status)537         private bool CheckStatus(Status status)
538         {
539             bool result = false;
540             if((i2cn_status & (uint)(1 << (int)status)) == (uint)(1 << (int)status))
541             {
542                 result = true;
543             }
544             return result;
545         }
546 
SetStatus(Status status)547         private void SetStatus(Status status)
548         {
549             i2cn_status |= (uint)(1 << (int)status);
550         }
551 
ClearStatus(Status status)552         private void ClearStatus(Status status)
553         {
554             i2cn_status &= ~((uint)status);
555         }
556 
557         public GPIO IRQ { get; private set; }
558 
CheckInterrupt(Interrupts interrupt)559         private bool CheckInterrupt(Interrupts interrupt)
560         {
561             bool result = false;
562             if((i2cn_if & (uint)(1 << (int)interrupt)) == (uint)(1 << (int)interrupt))
563             {
564                 result = true;
565             }
566             return result;
567         }
568 
UpdateInterrupt()569         private void UpdateInterrupt()
570         {
571             if((i2cn_if & i2cn_ien) > 0)
572             {
573                 this.Log(LogLevel.Noisy, "UpdateInterrupt - Irq set");
574                 IRQ.Set();
575             }
576             else
577             {
578                 this.Log(LogLevel.Noisy, "UpdateInterrupt - Irq cleared");
579                 IRQ.Unset();
580             }
581         }
582 
SetInterrupt(uint interruptMask)583         private void SetInterrupt(uint interruptMask)
584         {
585             // Only act if controller is enabled and on enabled interrupts
586             uint enableInterruptMask = i2cn_ien & interruptMask;
587             this.Log(LogLevel.Noisy, "SetInterrupt - enableInterruptMask = {0}", Convert.ToString(enableInterruptMask));
588             this.Log(LogLevel.Noisy, "SetInterrupt i2cn_ctrl = {0}", Convert.ToString(i2cn_ctrl));
589             // TODO: enable this check again once the Bitbanding.cs issue have been resolved
590             //            if ((i2cn_ctrl & 0x1) == 0x1)
591             //            {
592             i2cn_if |= enableInterruptMask;
593             UpdateInterrupt();
594             //            }
595         }
596 
ClearInterrupt(uint interruptMask)597         private void ClearInterrupt(uint interruptMask)
598         {
599             // Only act if controller is enabled and on enabled interrupts
600             uint enableInterruptMask = i2cn_ien & interruptMask;
601             // TODO: enable this check again once the Bitbanding.cs issue have been resolved
602             //            if ((i2cn_ctrl & 0x1) == 0x1)
603             //            {
604             i2cn_if &= ~enableInterruptMask;
605             this.Log(LogLevel.Noisy, "ClearInterrupt i2cn_if = {0}", Convert.ToString(i2cn_if));
606             UpdateInterrupt();
607             //            }
608         }
609 
EnableInterrupt(uint interruptMask)610         private void EnableInterrupt(uint interruptMask)
611         {
612             i2cn_ien |= interruptMask;
613             // Clear disabled interrupts
614             i2cn_if &= interruptMask;
615             UpdateInterrupt();
616         }
617 
618         private uint i2cn_ctrl;
619         private uint i2cn_cmd;
620         private uint i2cn_state;
621         private uint i2cn_status;
622         private uint i2cn_clkdiv;
623         private uint i2cn_saddr;
624         private uint i2cn_if;
625         private uint i2cn_ien;
626         private uint i2cn_route;
627 
628         private uint txShiftRegister;
629         private uint rxShiftRegister;
630         private bool txShiftRegisterFull;
631         private bool rxShiftRegisterFull;
632         private uint txBuffer;
633         private uint rxBuffer;
634         private bool txBufferFull;
635         private bool rxBufferFull;
636         private TransferState transferState;
637 
638         private byte slaveAddressForPacket;
639         private byte registerAddress;
640         private int currentAddress;
641 
642         private List<byte> txpacket = new List<byte>();
643         private List<byte> rxpacket = new List<byte>();
644 
645         // Source: pages 434-445 in EFM32GG Reference Manual
646         private enum Registers
647         {
648             I2Cn_CTRL = 0x000, // Control Register - Read-Write
649             I2Cn_CMD = 0x004, // Command Register - Write-1-only
650             I2Cn_STATE = 0x008, // State Register - Read-only
651             I2Cn_STATUS = 0x00C, // Status Register - Read-only
652             I2Cn_CLKDIV = 0x010, // Clock Division Register - Read-Write
653             I2Cn_SADDR = 0x014, // Slave Address Register - Read-Write
654             I2Cn_SADDRMASK = 0x018, // Slave Address Mask Register - Read-Write
655             I2Cn_RXDATA = 0x01C, // Receive Buffer Data Register - Read-only
656             I2Cn_RXDATAP = 0x020, // Receive Buffer Data Peek Register - Read-only
657             I2Cn_TXDATA = 0x024, // Transmit Buffer Data Register - Write-only
658             I2Cn_IF = 0x028, // Interrupt Flag Register - Read-only
659             I2Cn_IFS = 0x02C, // Interrupt Flag Set Register - Write-1-only
660             I2Cn_IFC = 0x030, // Interrupt Flag Clear Register - Write-1-only
661             I2Cn_IEN = 0x034, // Interrupt Enable Register - Read-Write
662             I2Cn_ROUTE = 0x038  // I/O Routing Register - Read-Write
663         }
664 
665         // Bits in the Control register (0-31)
666         private enum Control
667         {
668             EN = 0x0,   // I2C Enable
669             SLAVE = 0x1,   // Addressable as slave
670             AUTOACK = 0x2,   // Automatic acknowledge
671             AUTOSE = 0x3,   // Automatic STOP when empty
672             AUTOSN = 0x4,   // Automatic STOP on NACK
673             ARBDIS = 0x5,   // Arbitration disable
674             GCAMEN = 0x6,   // General Call Address Match Enable
675             CLHR_0 = 0x8,   // Clock Low High Ratio, bit 0
676             CLHR_1 = 0x9,   // Clock Low High Ratio, bit 1
677             BITO_0 = 0x12,  // Bus Idle Timeout, bit 0
678             BITO_1 = 0x13,  // Bus Idle Timeout, bit 1
679             GIBITO = 0x15,  // Go Idle on Bus Idle Timeout
680             CLTO_0 = 0x16,  // Clock Low Timeout, bit 0
681             CLTO_1 = 0x17,  // Clock Low Timeout, bit 1
682             CLTO_2 = 0x18   // Clock Low Timeout, bit 2
683 
684         }
685 
686         // Values in the Command register (Only bits 0-7 used, 8-31 reserved)
687         private enum Commands
688         {
689             START = 0x01,  // Send start condition
690             STOP = 0x02,  // Send stop condition
691             ACK = 0x04,  // Send ACK
692             NACK = 0x08,  // Send NACK
693             CONT = 0x10,  // Continue transmission
694             ABORT = 0x20,  // Abort transmission
695             CLEARTX = 0x40,  // Clear TX
696             CLEARPC = 0x80   // Clear pending commands
697         }
698 
699         // Values in the State register (0-31) - Only some states, see pages 423-424 in EFM32GG ref manual
700         private enum StateVal
701         {
702             StartTrans = 0x57,
703             AddrWAck = 0x97,
704             AddrWNack = 0x9F,
705             DataWAck = 0xD7,
706             DataWNack = 0xDF
707         }
708 
709         // Bits in the State register (0-31)
710         private enum StateReg
711         {
712             BUSY = 0x0,  // Bus busy
713             MASTER = 0x1,  // Master
714             TRANSMIT = 0x2,  // Transmitter
715             NACKED = 0x3,  // Nack Received
716             BUSHOLD = 0x4,  // Bus held
717             STATE_0 = 0x5,  // Transmission state, bit 0
718             STATE_1 = 0x6,  // Transmission state, bit 1
719             STATE_2 = 0x7   // Transmission state, bit 2
720         }
721 
722         // Bits in the Status register (0-31)
723         private enum Status
724         {
725             PSTART = 0x0,  // Pending start
726             PSTOP = 0x1,  // Pending stop
727             PACK = 0x2,  // Pending ACK
728             PNACK = 0x3,  // Pending NACK
729             PCONT = 0x4,  // Pending continue transmission
730             PABORT = 0x5,  // Pending abort transmission
731             TXC = 0x6,  // TX complete
732             TXBL = 0x7,  // TX buffer level
733             RXDATAV = 0x8   // RX data valid
734         }
735 
736         // Bits in the Interrupt Flag Register (0-31)
737         private enum Interrupts
738         {
739             START = 0x00, // START condition Interrupt Flag
740             RSTART = 0x01, // Repeated START condition Interrupt Flag
741             ADDR = 0x02, // Address Interrupt Flag
742             TXC = 0x03, // Transfer Completed Interrupt Flag
743             TXBL = 0x04, // Transmit Buffer Level Interrupt Flag
744             RXDATAV = 0x05, // Receive Data Valid Interrupt Flag
745             ACK = 0x06, // Acknowledge Received Interrupt Flag
746             NACK = 0x07, // Not Acknowledge Received Interrupt Flag
747             MSTOP = 0x08, // Master STOP Condition Interrupt Flag
748             ARBLOST = 0x09, // Arbitration Lost Interrupt Flag
749             BUSERR = 0x0A, // Bus Error Interrupt Flag
750             BUSHOLD = 0x0B, // Bus Held Interrupt Flag
751             TXOF = 0x0C, // Transmit Buffer Overflow Interrupt Flag
752             RXUF = 0x0D, // Receive Buffer Underflow Interrupt Flag
753             BITO = 0x0E, // Bus Idle Timeout Interrupt Flag
754             CLTO = 0x0F, // Clock Low Timeout Interrupt Flag
755             SSTOP = 0x10  // Slave STOP condition Interrupt Flag
756         }
757 
758         // Transfer state enumaration
759         private enum TransferState
760         {
761             Idle = 0x0,
762             StartTrans = 0x1,
763             RestartTrans = 0x2,
764             ReadTrans = 0x3,
765             StopTrans = 0x4
766         }
767 
768         private enum PacketStates
769         {
770             ReadData = 0xFC,
771             SendData = 0xFD
772         }
773     }
774 }
775 
776