1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
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.GPIOPort
17 {
18     public class EFR32_GPIOPort : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
19     {
EFR32_GPIOPort(IMachine machine)20         public EFR32_GPIOPort(IMachine machine) : base(machine, NumberOfPins * NumberOfPorts)
21         {
22             EvenIRQ = new GPIO();
23             OddIRQ = new GPIO();
24             CreateRegisters();
25             InnerReset();
26         }
27 
ReadDoubleWord(long offset)28         public uint ReadDoubleWord(long offset)
29         {
30             lock(internalLock)
31             {
32                 return registers.Read(offset);
33             }
34         }
35 
WriteDoubleWord(long offset, uint value)36         public void WriteDoubleWord(long offset, uint value)
37         {
38             lock(internalLock)
39             {
40                 if(configurationLocked)
41                 {
42                     if(offset < (uint)Registers.ExternalInterruptPortSelectLow)
43                     {
44                         //port register, align it to the first port
45                         offset %= PortOffset;
46                     }
47                     var register = (Registers)offset;
48                     if(lockableRegisters.Contains(register))
49                     {
50                         this.Log(LogLevel.Debug, "Not writing to {0} because of configuration lock.", register);
51                         return;
52                     }
53                 }
54                 registers.Write(offset, value);
55             }
56         }
57 
Reset()58         public override void Reset()
59         {
60             lock(internalLock)
61             {
62                 base.Reset();
63                 InnerReset();
64             }
65         }
66 
OnGPIO(int number, bool value)67         public override void OnGPIO(int number, bool value)
68         {
69             if(number < 0 || number >= State.Length)
70             {
71                 throw new ArgumentOutOfRangeException(string.Format("Gpio #{0} called, but only {1} lines are available", number, State.Length));
72             }
73             lock(internalLock)
74             {
75                 if(IsOutput(pinModes[number].Value))
76                 {
77                     this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number);
78                     return;
79                 }
80 
81                 base.OnGPIO(number, value);
82                 UpdateInterrupts();
83             }
84         }
85 
86         public long Size
87         {
88             get
89             {
90                 return 0x1000;
91             }
92         }
93 
94         public GPIO EvenIRQ { get; private set; }
95         public GPIO OddIRQ { get; private set; }
96 
UpdateInterrupts()97         private void UpdateInterrupts()
98         {
99             for(var i = 0; i < State.Length; ++i)
100             {
101                 var externalPin = targetExternalPins[i];
102                 if(!interruptEnable[externalPin])
103                 {
104                     continue;
105                 }
106                 var isEdge = State[i] != previousState[externalPin];
107                 previousState[externalPin] = State[i];
108                 if(isEdge && State[i] == (interruptTriggers[externalPin] == InterruptTrigger.RisingEdge))
109                 {
110                     externalInterrupt[externalPin] = true;
111                 }
112                 //no clear as it must be set manually with InterruptFlagClear
113             }
114 
115             var even = false;
116             var odd = false;
117             for(var i = 0; i < interruptEnable.Length; i += 2)
118             {
119                 even |= externalInterrupt[i];
120             }
121             for(var i = 1; i < interruptEnable.Length; i += 2)
122             {
123                 odd |= externalInterrupt[i];
124             }
125             EvenIRQ.Set(even);
126             OddIRQ.Set(odd);
127         }
128 
InnerReset()129         private void InnerReset()
130         {
131             registers.Reset();
132             configurationLocked = false;
133             EvenIRQ.Unset();
134             OddIRQ.Unset();
135             for(var i = 0; i < NumberOfExternalInterrupts; ++i)
136             {
137                 externalInterrupt[i] = false;
138                 interruptEnable[i] = false;
139                 interruptTriggers[i] = InterruptTrigger.None;
140             }
141             for(var i = 0; i < targetExternalPins.Length; ++i)
142             {
143                 targetExternalPins[i] = 0;
144             }
145             for(var i = 0; i < externalInterruptToPinMapping.Length; ++i)
146             {
147                 //both arrays have the same length
148                 externalInterruptToPinMapping[i] = i % 4;
149                 externalInterruptToPortMapping[i] = 0;
150             }
151         }
152 
CreateRegisters()153         private void CreateRegisters()
154         {
155             var regs = new Dictionary<long, DoubleWordRegister>()
156             {
157                 {(long)Registers.ExternalInterruptPortSelectLow, new DoubleWordRegister(this)
158                     .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePort((uint)oldValue, (uint)newValue, false), name: "EXTIPSEL")
159                 },
160                 {(long)Registers.ExternalInterruptPortSelectHigh, new DoubleWordRegister(this)
161                     .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePort((uint)oldValue, (uint)newValue, true), name: "EXTIPSEL")
162                 },
163                 {(long)Registers.ExternalInterruptPinSelectLow, new DoubleWordRegister(this, 0x32103210)
164                     .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePin((uint)oldValue, (uint)newValue, false), name: "EXTIPINSEL")
165                 },
166                 {(long)Registers.ExternalInterruptPinSelectHigh, new DoubleWordRegister(this, 0x32103210)
167                     .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePin((uint)oldValue, (uint)newValue, true), name: "EXTIPINSEL")
168                 },
169                 {(long)Registers.ExternalInterruptRisingEdgeTrigger, new DoubleWordRegister(this)
170                     .WithValueField(0, 16, changeCallback: (_, value) => SetEdgeSensitivity((uint)value, InterruptTrigger.RisingEdge))
171                 },
172                 {(long)Registers.ExternalInterruptFallingEdgeTrigger, new DoubleWordRegister(this)
173                     .WithValueField(0, 16, changeCallback: (_, value) => SetEdgeSensitivity((uint)value, InterruptTrigger.FallingEdge))
174                 },
175                 {(long)Registers.InterruptFlag, new DoubleWordRegister(this)
176                     .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: (_) => BitHelper.GetValueFromBitsArray(externalInterrupt), name: "EXT")
177                     .WithTag("EM4WU", 16, 16)
178                 },
179                 {(long)Registers.InterruptFlagSet, new DoubleWordRegister(this)
180                     .WithValueField(0, 16, FieldMode.Write, writeCallback: (_, value) => UpdateExternalInterruptBits((uint)value, true), name: "EXT")
181                     .WithTag("EM4WU", 16, 16)
182                 },
183                 {(long)Registers.InterruptFlagClear, new DoubleWordRegister(this)
184                     .WithValueField(0, 16, writeCallback: (_, value) => UpdateExternalInterruptBits((uint)value, false), valueProviderCallback: (_) =>
185                     {
186                         var result = BitHelper.GetValueFromBitsArray(externalInterrupt);
187                         for(var i = 0; i < NumberOfExternalInterrupts; ++i)
188                         {
189                             externalInterrupt[i] = false;
190                         }
191                         UpdateInterrupts();
192                         return result;
193                     }, name: "EXT")
194                     .WithTag("EM4WU", 16, 16)
195                 },
196                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
197                     .WithValueField(0, 16, writeCallback: (_, value) =>
198                     {
199                         Array.Copy(BitHelper.GetBits((uint)value), interruptEnable, NumberOfExternalInterrupts);
200                         UpdateInterrupts();
201                     },
202                                     valueProviderCallback: (_) => BitHelper.GetValueFromBitsArray(interruptEnable), name: "EXT")
203                     .WithTag("EM4WU", 16, 16)
204                 },
205                 {(long)Registers.ConfigurationLock, new DoubleWordRegister(this)
206                     .WithValueField(0, 16, writeCallback: (_, value) => configurationLocked = (value != UnlockCode),
207                                     valueProviderCallback: (_)=> configurationLocked ? 1 : 0u, name: "LOCKKEY")
208                 },
209             };
210             for(var i = 0; i < NumberOfPorts; ++i)
211             {
212                 CreatePortRegisters(regs, i);
213             }
214             registers = new DoubleWordRegisterCollection(this, regs);
215         }
216 
CreatePortRegisters(Dictionary<long, DoubleWordRegister> regs, int portNumber)217         private void CreatePortRegisters(Dictionary<long, DoubleWordRegister> regs, int portNumber)
218         {
219             var regOffset = PortOffset * portNumber;
220             var pinOffset = portNumber * NumberOfPins;
221             regs.Add((long)Registers.PortAControl + regOffset, new DoubleWordRegister(this, 0x700070)
222                      .WithTag("DRIVESTRENGTH", 0, 1)
223                      .WithTag("SLEWRATE", 4, 3)
224                      .WithTag("DINDIS", 12, 1)
225                      .WithTag("DRIVESTRENGTHALT", 16, 1)
226                      .WithTag("SLEWRATEALT", 20, 3)
227                      .WithTag("DINDISALT", 28, 1)
228                     );
229 
230             var gpioModeLow = new DoubleWordRegister(this);
231             var gpioModeHigh = new DoubleWordRegister(this);
232 
233             for(var pinNumber = 0; pinNumber < 8; ++pinNumber)
234             {
235                 pinModes[pinOffset + pinNumber] = gpioModeLow.DefineEnumField<PinMode>(pinNumber * 4, 4, name: "MODEX"); //TODO: pin locking
236             }
237 
238             for(var pinNumber = 8; pinNumber < 16; ++pinNumber)
239             {
240                 pinModes[pinOffset + pinNumber] = gpioModeHigh.DefineEnumField<PinMode>((pinNumber - 8) * 4, 4, name: "MODEX"); //TODO: pin locking
241             }
242 
243             regs.Add((long)Registers.PortAModeLow + regOffset, gpioModeLow);
244             regs.Add((long)Registers.PortAModeHigh + regOffset, gpioModeHigh);
245 
246             regs.Add((long)Registers.PortADataOut + regOffset, new DoubleWordRegister(this)
247                      .WithValueField(0, 16,
248                                      writeCallback: (_, newValue) =>
249                                      {
250                                          var bits = BitHelper.GetBits((uint)newValue);
251                                          for(var i = 0; i < 16; i++)
252                                          {
253                                              var pin = pinOffset + i;
254                                              if(IsOutput(pinModes[pin].Value) && unlockedPins[pin].Value)
255                                              {
256                                                  Connections[pin].Set(bits[i]);
257                                              }
258                                          }
259                                      },
260                                      valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(Connections.Where(x => x.Key >= 0).OrderBy(x => x.Key).Select(x => x.Value.IsSet))));
261 
262             regs.Add((long)Registers.PortADataOutToggle + regOffset, new DoubleWordRegister(this)
263                      .WithValueField(0, 16, FieldMode.Write,
264                                      writeCallback: (_, newValue) =>
265                                      {
266                                          var bits = BitHelper.GetSetBits(newValue);
267                                          foreach(var bit in bits)
268                                          {
269                                              var pin = pinOffset + bit;
270                                              if(IsOutput(pinModes[pin].Value) && unlockedPins[pin].Value)
271                                              {
272                                                  Connections[pin].Toggle();
273                                              }
274                                          }
275                                      }));
276 
277             regs.Add((long)Registers.PortADataIn + regOffset, new DoubleWordRegister(this)
278                      .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: (oldValue) => BitHelper.GetValueFromBitsArray(State.Skip(pinOffset).Take(NumberOfPins))));
279 
280             var unlockedPinsRegister = new DoubleWordRegister(this, 0xFFFF);
281             for(var pinNumber = 0; pinNumber < NumberOfPins; ++pinNumber)
282             {
283                 unlockedPins[pinNumber + pinOffset] = unlockedPinsRegister.DefineFlagField(pinNumber, FieldMode.WriteZeroToClear);
284             }
285             regs.Add((long)Registers.PortAUnlockedPins + regOffset, unlockedPinsRegister);
286         }
287 
SetEdgeSensitivity(uint value, InterruptTrigger trigger)288         private void SetEdgeSensitivity(uint value, InterruptTrigger trigger)
289         {
290             var bits = BitHelper.GetBits(value);
291             for(var i = 0; i < interruptTriggers.Length; ++i)
292             {
293                 if(bits[i])
294                 {
295                     interruptTriggers[i] |= trigger;
296                 }
297                 else
298                 {
299                     interruptTriggers[i] ^= trigger;
300                 }
301             }
302         }
303 
UpdateExternalInterruptBits(uint bits, bool value)304         private void UpdateExternalInterruptBits(uint bits, bool value)
305         {
306             var setBits = BitHelper.GetSetBits(bits);
307             foreach(var bit in setBits)
308             {
309                 externalInterrupt[bit] = value;
310             }
311             UpdateInterrupts();
312         }
313 
ReroutePort(uint oldValue, uint newValue, bool isHighRegister)314         private void ReroutePort(uint oldValue, uint newValue, bool isHighRegister)
315         {
316             var setPins = new HashSet<int>();
317             for(var i = 0; i < 8; ++i)
318             {
319                 var externalIrq = i + (isHighRegister ? 8 : 0);
320                 var portNewValue = (int)(newValue & (0xF << (i * 4))) >> (i * 4);
321                 var portOldValue = (int)(oldValue & (0xF << (i * 4))) >> (i * 4);
322                 if(portOldValue == portNewValue)
323                 {
324                     continue;
325                 }
326                 var pinGroup = externalIrq / 4;
327                 var oldPinNumber = externalInterruptToPinMapping[externalIrq] + pinGroup * 4 + portOldValue * NumberOfPins;
328                 if(!setPins.Contains(oldPinNumber))
329                 {
330                     //if we did not set this pin in this run, let's unset it
331                     targetExternalPins[oldPinNumber] = 0;
332                 }
333                 var newPinNumber = externalInterruptToPinMapping[externalIrq] + pinGroup * 4 + portNewValue * NumberOfPins;
334                 targetExternalPins[newPinNumber] = externalIrq;
335                 setPins.Add(newPinNumber);
336                 //we keep it for the sake of ReroutePin method
337                 externalInterruptToPortMapping[externalIrq] = portNewValue;
338             }
339             UpdateInterrupts();
340         }
341 
ReroutePin(uint oldValue, uint newValue, bool isHighRegister)342         private void ReroutePin(uint oldValue, uint newValue, bool isHighRegister)
343         {
344             var setPins = new HashSet<int>();
345             for(var i = 0; i < 8; ++i)
346             {
347                 var externalIrq = i + (isHighRegister ? 8 : 0);
348                 var pinNewValue = (int)(newValue & (0x3 << (i * 4))) >> (i * 4);
349                 var pinOldValue = (int)(oldValue & (0x3 << (i * 4))) >> (i * 4);
350                 if(pinOldValue == pinNewValue)
351                 {
352                     continue;
353                 }
354                 var pinGroup = externalIrq / 4;
355                 var oldPinNumber = pinOldValue + pinGroup * 4 + externalInterruptToPortMapping[externalIrq] * NumberOfPins;
356                 if(!setPins.Contains(oldPinNumber))
357                 {
358                     //if we did not set this pin in this run, let's unset it
359                     targetExternalPins[oldPinNumber] = 0;
360                 }
361                 var newPinNumber = pinNewValue + pinGroup * 4 + externalInterruptToPortMapping[externalIrq] * NumberOfPins;
362                 targetExternalPins[newPinNumber] = externalIrq;
363                 setPins.Add(newPinNumber);
364                 //we keep it for the sake of ReroutePort method
365                 externalInterruptToPinMapping[externalIrq] = pinNewValue;
366             }
367             UpdateInterrupts();
368         }
369 
IsOutput(PinMode mode)370         private bool IsOutput(PinMode mode)
371         {
372             return mode >= PinMode.PushPull;
373         }
374 
375         private readonly int[] externalInterruptToPortMapping = new int[NumberOfExternalInterrupts];
376         private readonly int[] externalInterruptToPinMapping = new int[NumberOfExternalInterrupts];
377         private readonly bool[] externalInterrupt = new bool[NumberOfExternalInterrupts];
378         private readonly bool[] previousState = new bool[NumberOfExternalInterrupts];
379         private readonly bool[] interruptEnable = new bool[NumberOfExternalInterrupts];
380         private readonly int[] targetExternalPins = new int[NumberOfPins * NumberOfPorts];
381         private readonly InterruptTrigger[] interruptTriggers = new InterruptTrigger[NumberOfExternalInterrupts];
382         private readonly IEnumRegisterField<PinMode>[] pinModes = new IEnumRegisterField<PinMode>[NumberOfPins * NumberOfPorts];
383         private readonly IFlagRegisterField[] unlockedPins = new IFlagRegisterField[NumberOfPins * NumberOfPorts];
384         private readonly object internalLock = new object();
385 
386         private DoubleWordRegisterCollection registers;
387         private bool configurationLocked;
388 
389         private readonly HashSet<Registers> lockableRegisters = new HashSet<Registers>
390         {
391             Registers.PortAControl,
392             Registers.PortAModeLow,
393             Registers.PortAModeHigh,
394             Registers.PortAUnlockedPins,
395             Registers.PortAOverVoltageDisable,
396             Registers.ExternalInterruptPortSelectLow,
397             Registers.ExternalInterruptPortSelectHigh,
398             Registers.ExternalInterruptPinSelectLow,
399             Registers.ExternalInterruptPinSelectHigh,
400             Registers.IORoutingPinEnable,
401             Registers.IORoutingLocation,
402             Registers.InputSense,
403         };
404 
405         private const int NumberOfPorts = 6;
406         private const int NumberOfPins = 16;
407         private const int NumberOfExternalInterrupts = 16;
408         private const int UnlockCode = 0xA534;
409         private const int PortOffset = 0x30;
410 
411         [Flags]
412         private enum InterruptTrigger
413         {
414             None = 0,
415             FallingEdge = 1,
416             RisingEdge = 2
417         }
418 
419         private enum PinMode
420         {
421             //not setting the values explicitly, the implicit values are used. Do not reorder.
422             Disabled,
423             Input,
424             InputPull,
425             InputPullFilter,
426             PushPull,
427             PushPullAlt,
428             WiredOr,
429             WiredOrPullDown,
430             WiredAnd,
431             WiredAndFilter,
432             WiredAndPullUp,
433             WiredAndPullUpFilter,
434             WiredAndAlt,
435             WiredAndAltFilter,
436             WiredAndAltPullUp,
437             WiredAndAltPullUpFilter,
438         }
439 
440         private enum Registers
441         {
442             //port configuration
443             PortAControl                        = 0x0,
444             PortAModeLow                        = 0x4,
445             PortAModeHigh                       = 0x8,
446             PortADataOut                        = 0xC,
447             //reserved x 2
448             PortADataOutToggle                  = 0x18,
449             PortADataIn                         = 0x1C,
450             PortAUnlockedPins                   = 0x20,
451             //reserved x 1
452             PortAOverVoltageDisable             = 0x28,
453             //reserved x 1
454             PortBControl                        = 0x30,
455             PortBModeLow                        = 0x34,
456             PortBModeHigh                       = 0x38,
457             PortBDataOut                        = 0x3C,
458             //reserved x 2
459             PortBDataOutToggle                  = 0x48,
460             PortBDataIn                         = 0x4C,
461             PortBUnlockedPins                   = 0x50,
462             //reserved x 1
463             PortBOverVoltageDisable             = 0x58,
464             //reserved x 1
465             PortCControl                        = 0x60,
466             PortCModeLow                        = 0x64,
467             PortCModeHigh                       = 0x68,
468             PortCDataOut                        = 0x6C,
469             //reserved x 2
470             PortCDataOutToggle                  = 0x78,
471             PortCDataIn                         = 0x7C,
472             PortCUnlockedPins                   = 0x80,
473             //reserved x 1
474             PortCOverVoltageDisable             = 0x88,
475             //reserved x 1
476             PortDControl                        = 0x90,
477             PortDModeLow                        = 0x94,
478             PortDModeHigh                       = 0x98,
479             PortDDataOut                        = 0x9C,
480             //reserved x 2
481             PortDDataOutToggle                  = 0xA8,
482             PortDDataIn                         = 0xAC,
483             PortDUnlockedPins                   = 0xB0,
484             //reserved x 1
485             PortDOverVoltageDisable             = 0xB8,
486             //reserved x 1
487             PortEControl                        = 0xC0,
488             PortEModeLow                        = 0xC4,
489             PortEModeHigh                       = 0xC8,
490             PortEDataOut                        = 0xCC,
491             //reserved x 2
492             PortEDataOutToggle                  = 0xD8,
493             PortEDataIn                         = 0xDC,
494             PortEUnlockedPins                   = 0xE0,
495             //reserved x 1
496             PortEOverVoltageDisable             = 0xE8,
497             //reserved x 1
498             PortFControl                        = 0xF0,
499             PortFModeLow                        = 0xF4,
500             PortFModeHigh                       = 0xF8,
501             PortFDataOut                        = 0xFC,
502             //reserved x 2
503             PortFDataOutToggle                  = 0x108,
504             PortFDataIn                         = 0x10C,
505             PortFUnlockedPins                   = 0x110,
506             //reserved x 1
507             PortFOverVoltageDisable             = 0x118,
508             //reserved x 1
509             //global registers
510             ExternalInterruptPortSelectLow      = 0x400,
511             ExternalInterruptPortSelectHigh     = 0x404,
512             ExternalInterruptPinSelectLow       = 0x408,
513             ExternalInterruptPinSelectHigh      = 0x40C,
514             ExternalInterruptRisingEdgeTrigger  = 0x410,
515             ExternalInterruptFallingEdgeTrigger = 0x414,
516             ExternalInterruptLevel              = 0x418,
517             InterruptFlag                       = 0x41C,
518             InterruptFlagSet                    = 0x420,
519             InterruptFlagClear                  = 0x424,
520             InterruptEnable                     = 0x428,
521             EM4WakeUpEnable                     = 0x42C,
522             IORoutingPinEnable                  = 0x440,
523             IORoutingLocation                   = 0x444,
524             InputSense                          = 0x450,
525             ConfigurationLock                   = 0x454,
526         }
527     }
528 }
529