1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Extensions;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Logging;
13 using System;
14 using System.Linq;
15 using System.Collections.Generic;
16 
17 namespace Antmicro.Renode.Peripherals.GPIOPort
18 {
19     public class RenesasRZG_GPIO : BaseGPIOPort, IBytePeripheral, IWordPeripheral, IDoubleWordPeripheral, IKnownSize
20     {
RenesasRZG_GPIO(Machine machine)21         public RenesasRZG_GPIO(Machine machine) : base(machine, NrOfPins)
22         {
23             // Prepare gpio indices offsets per port for easy lookup
24             gpioOffsetPerPort[0] = 0;
25             for(var portIdx = 1; portIdx < NrOfPorts; portIdx++)
26             {
27                 gpioOffsetPerPort[portIdx] = gpioOffsetPerPort[portIdx - 1] + GetNrOfGPIOPinsForPort(portIdx - 1);
28             }
29 
30             DefineRegisters();
31         }
32 
33         // Every register, allows all access widths less than or equal to it's own width.
ReadByte(long offset)34         public byte ReadByte(long offset)
35         {
36             // We align offset to registers width
37             if(doubleWordRegisters.HasRegisterAtOffset(offset & ~0x3))
38             {
39                 return this.ReadByteUsingDoubleWord(offset);
40             }
41             else if(wordRegisters.HasRegisterAtOffset(offset & ~0x1))
42             {
43                 return this.ReadByteUsingWord(offset);
44             }
45             return byteRegisters.Read(offset);
46         }
47 
WriteByte(long offset, byte value)48         public void WriteByte(long offset, byte value)
49         {
50             // We align offset to registers width
51             if(doubleWordRegisters.HasRegisterAtOffset(offset & ~0x3))
52             {
53                 this.WriteByteUsingDoubleWord(offset, value);
54                 return;
55             }
56             if(wordRegisters.HasRegisterAtOffset(offset & ~0x1))
57             {
58                 this.WriteByteUsingWord(offset, value);
59                 return;
60             }
61             byteRegisters.Write(offset, value);
62         }
63 
ReadWord(long offset)64         public ushort ReadWord(long offset)
65         {
66             // We align offset to registers width
67             if(doubleWordRegisters.HasRegisterAtOffset(offset & ~0x3))
68             {
69                 return this.ReadWordUsingDoubleWord(offset);
70             }
71             return wordRegisters.Read(offset);
72         }
73 
WriteWord(long offset, ushort value)74         public void WriteWord(long offset, ushort value)
75         {
76             // We align offset to registers width
77             if(doubleWordRegisters.HasRegisterAtOffset(offset & ~0x3))
78             {
79                 this.WriteWordUsingDoubleWord(offset, value);
80                 return;
81             }
82             wordRegisters.Write(offset, value);
83         }
84 
ReadDoubleWord(long offset)85         public uint ReadDoubleWord(long offset)
86         {
87             return doubleWordRegisters.Read(offset);
88         }
89 
WriteDoubleWord(long offset, uint value)90         public void WriteDoubleWord(long offset, uint value)
91         {
92             doubleWordRegisters.Write(offset, value);
93         }
94 
Reset()95         public override void Reset()
96         {
97             base.Reset();
98             byteRegisters.Reset();
99             wordRegisters.Reset();
100             doubleWordRegisters.Reset();
101             Array.Clear(output, 0, output.Length);
102             Array.Clear(portMode, 0, portMode.Length);
103             Array.Clear(interruptEnabled, 0, interruptEnabled.Length);
104             Array.Clear(pinFunction, 0, pinFunction.Length);
105             Array.Clear(pinFunctionEnabled, 0, pinFunctionEnabled.Length);
106             foreach (var irq in functionInterrupts)
107             {
108                 irq.Unset();
109             }
110         }
111 
OnGPIO(int number, bool value)112         public override void OnGPIO(int number, bool value)
113         {
114             base.OnGPIO(number, value);
115             UpdateGPIO();
116         }
117 
118         public long Size => 0x10000;
119 
120         public GPIO IRQ0 => functionInterrupts[0];
121         public GPIO IRQ1 => functionInterrupts[1];
122         public GPIO IRQ2 => functionInterrupts[2];
123         public GPIO IRQ3 => functionInterrupts[3];
124         public GPIO IRQ4 => functionInterrupts[4];
125         public GPIO IRQ5 => functionInterrupts[5];
126         public GPIO IRQ6 => functionInterrupts[6];
127         public GPIO IRQ7 => functionInterrupts[7];
128 
DefineRegisters()129         private void DefineRegisters()
130         {
131             var byteRegistersMap = new Dictionary <long, ByteRegister>();
132             var wordRegistersMap = new Dictionary <long, WordRegister>();
133             var doubleWordRegistersMap = new Dictionary <long, DoubleWordRegister>();
134 
135             // Interrupt enable registers are not distributed uniformly across it's region
136             // we keep track of their current offset in this variable instead of calculating it every time.
137             var interruptEnableControlOffset = (long)Registers.InterruptEnableControl;
138 
139             // Although different ports have different number of GPIO pins, with maximum of 5 usable pins,
140             // they have the same registers structure.
141             for(var portIdx = 0; portIdx < NrOfPorts; portIdx++)
142             {
143                 var portOffset = (long)Registers.Port + portIdx;
144                 byteRegistersMap[portOffset] = new ByteRegister(this)
145                     .WithFlags(0, NrOfPinsInPortRegister,
146                         writeCallback: CreateGPIOOutputWriteCallback(portIdx),
147                         valueProviderCallback: CreateGPIOOutputValueProviderCallback(portIdx),
148                         name: "P")
149                     .WithWriteCallback((_, __) => UpdateGPIO());
150 
151                 var portModeOffset = (long)Registers.PortMode + portIdx * 0x2;
152                 wordRegistersMap[portModeOffset] = new WordRegister(this)
153                     .WithEnumFields<WordRegister, PortMode>(0, 2, 5,
154                         valueProviderCallback: CreatePortModeValueProviderCallback(portIdx),
155                         writeCallback: CreatePortModeWriteCallback(portIdx),
156                         name: "PM")
157                     .WithWriteCallback((_, __) => UpdateGPIO());
158 
159                 var portModeControlOffset = (long)Registers.PortModeControl + portIdx;
160                 byteRegistersMap[portModeControlOffset] = new ByteRegister(this)
161                     .WithFlags(0, 5,
162                         writeCallback: CreatePortModeControlWriteCallback(portIdx),
163                         valueProviderCallback: CreatePortModeControlValueProviderCallback(portIdx),
164                         name: "PMC")
165                     .WithWriteCallback((_, __) => UpdateGPIO());
166 
167                 var portFunctionControlOffset = (long)Registers.PortFunctionControl + portIdx * 0x4;
168                 doubleWordRegistersMap[portFunctionControlOffset] = new DoubleWordRegister(this)
169                     .WithValueField(0, 3,
170                         writeCallback: CreatePortFunctionControlWriteCallback(portIdx, 0),
171                         valueProviderCallback: CreatePortFunctionControlValueProviderCallback(portIdx, 0),
172                         name: "PFC0")
173                     .WithReservedBits(3, 1)
174                     .WithValueField(4, 3,
175                         writeCallback: CreatePortFunctionControlWriteCallback(portIdx, 1),
176                         valueProviderCallback: CreatePortFunctionControlValueProviderCallback(portIdx, 1),
177                         name: "PFC1")
178                     .WithReservedBits(7, 1)
179                     .WithValueField(8, 3,
180                         writeCallback: CreatePortFunctionControlWriteCallback(portIdx, 2),
181                         valueProviderCallback: CreatePortFunctionControlValueProviderCallback(portIdx, 2),
182                         name: "PFC2")
183                     .WithReservedBits(11, 1)
184                     .WithValueField(12, 3,
185                         writeCallback: CreatePortFunctionControlWriteCallback(portIdx, 3),
186                         valueProviderCallback: CreatePortFunctionControlValueProviderCallback(portIdx, 3),
187                         name: "PFC3")
188                     .WithReservedBits(15, 1)
189                     .WithValueField(16, 3,
190                         writeCallback: CreatePortFunctionControlWriteCallback(portIdx, 4),
191                         valueProviderCallback: CreatePortFunctionControlValueProviderCallback(portIdx, 4),
192                         name: "PFC4")
193                     .WithReservedBits(19, 13)
194                     .WithWriteCallback((_, __) => UpdateGPIO());
195 
196                 var portInputOffset = (long)Registers.PortInput + portIdx;
197                 byteRegistersMap[portInputOffset] = new ByteRegister(this)
198                     .WithFlags(0, NrOfPinsInPortRegister, FieldMode.Read,
199                         valueProviderCallback: CreateGPIOInputValueProviderCallback(portIdx),
200                         name: "PIN");
201 
202                 doubleWordRegistersMap[interruptEnableControlOffset] = new DoubleWordRegister(this)
203                     .WithFlag(0,
204                         writeCallback: CreateInterruptEnableControlWriteCallback(portIdx, 0),
205                         valueProviderCallback: CreateInterruptEnableControlValueProviderCallback(portIdx, 0),
206                         name: "ISEL0")
207                     .WithReservedBits(1, 7)
208                     .WithFlag(8,
209                         writeCallback: CreateInterruptEnableControlWriteCallback(portIdx, 1),
210                         valueProviderCallback: CreateInterruptEnableControlValueProviderCallback(portIdx, 1),
211                         name: "ISEL1")
212                     .WithReservedBits(9, 7)
213                     .WithFlag(16,
214                         writeCallback: CreateInterruptEnableControlWriteCallback(portIdx, 2),
215                         valueProviderCallback: CreateInterruptEnableControlValueProviderCallback(portIdx, 2),
216                         name: "ISEL2")
217                     .WithReservedBits(17, 7)
218                     .WithFlag(24,
219                         writeCallback: CreateInterruptEnableControlWriteCallback(portIdx, 3),
220                         valueProviderCallback: CreateInterruptEnableControlValueProviderCallback(portIdx, 3),
221                         name: "ISEL3")
222                     .WithReservedBits(25, 7)
223                     .WithWriteCallback((_, __) => UpdateGPIO());
224 
225                 interruptEnableControlOffset += 0x4;
226 
227                 if(GetNrOfGPIOPinsForPort(portIdx) > 4)
228                 {
229                     // We have 5 pins per port at most, so we only define here one flag
230                     doubleWordRegistersMap[interruptEnableControlOffset] = new DoubleWordRegister(this)
231                         .WithFlag(0,
232                             writeCallback: CreateInterruptEnableControlWriteCallback(portIdx, 4),
233                             valueProviderCallback: CreateInterruptEnableControlValueProviderCallback(portIdx, 4),
234                             name: "ISEL0")
235                         .WithReservedBits(1, 7)
236                         .WithTaggedFlag("ISEL1", 8)
237                         .WithReservedBits(9, 7)
238                         .WithTaggedFlag("ISEL2", 16)
239                         .WithReservedBits(17, 7)
240                         .WithTaggedFlag("ISEL3", 24)
241                         .WithReservedBits(25, 7)
242                         .WithWriteCallback((_, __) => UpdateGPIO());
243 
244                     interruptEnableControlOffset += 0x4;
245                 }
246             }
247 
248             // Some registers are not uniformly distributed over their regions.
249             // We define them in separate loops.
250             for(var registerIdx = 0; registerIdx < NrOfDrivingAbilityControlRegisters; registerIdx++)
251             {
252                 var drivingAbilityControlOffset = (long)Registers.DrivingAbilityControl + registerIdx * 0x4;
253                 doubleWordRegistersMap[drivingAbilityControlOffset] = new DoubleWordRegister(this)
254                     .WithTag("IOLH0", 0, 2)
255                     .WithReservedBits(2, 6)
256                     .WithTag("IOLH1", 8, 2)
257                     .WithReservedBits(10, 6)
258                     .WithTag("IOLH2", 16, 2)
259                     .WithReservedBits(18, 6)
260                     .WithTag("IOLH3", 24, 2)
261                     .WithReservedBits(26, 6);
262             }
263 
264             for(var registerIdx = 0; registerIdx < NrOfSlewRateSwitchingRegisters; registerIdx++)
265             {
266                 var slewRateSwitchingOffset = (long)Registers.SlewRateSwitching + registerIdx * 0x4;
267                 doubleWordRegistersMap[slewRateSwitchingOffset] = new DoubleWordRegister(this)
268                     .WithTag("SR0", 0, 1)
269                     .WithReservedBits(1, 7)
270                     .WithTag("SR1", 8, 1)
271                     .WithReservedBits(9, 7)
272                     .WithTag("SR2", 16, 1)
273                     .WithReservedBits(17, 7)
274                     .WithTag("SR3", 24, 1)
275                     .WithReservedBits(25, 7);
276             }
277 
278             for(var registerIdx = 0; registerIdx < NrOfPullUpPullDownSwitchingRegisters; registerIdx++)
279             {
280                 var pullUpPullDownSwitchingOffset = (long)Registers.PullUpPullDownSwitching + registerIdx * 0x4;
281                 doubleWordRegistersMap[pullUpPullDownSwitchingOffset] = new DoubleWordRegister(this)
282                     .WithTag("PUPD0", 0, 2)
283                     .WithReservedBits(2, 6)
284                     .WithTag("PUPD1", 8, 2)
285                     .WithReservedBits(10, 6)
286                     .WithTag("PUPD2", 16, 2)
287                     .WithReservedBits(18, 6)
288                     .WithTag("PUPD3", 24, 2)
289                     .WithReservedBits(26, 6);
290             }
291 
292             for(var registerIdx = 0; registerIdx < NrOfDigitalNoiseFilterRegisters; registerIdx++)
293             {
294                 var digitalNoiseFilterSwitchingOffset = (long)Registers.DigitalNoiseFilterSwitching + registerIdx * 0x4;
295                 doubleWordRegistersMap[digitalNoiseFilterSwitchingOffset] = new DoubleWordRegister(this)
296                     .WithTag("FILON0", 0, 1)
297                     .WithReservedBits(1, 7)
298                     .WithTag("FILON1", 8, 1)
299                     .WithReservedBits(9, 7)
300                     .WithTag("FILON2", 16, 1)
301                     .WithReservedBits(17, 7)
302                     .WithTag("FILON3", 24, 1)
303                     .WithReservedBits(25, 7);
304 
305                 var digitalNoiseFilterNumberOffset = (long)Registers.DigitalNoiseFilterNumber + registerIdx * 0x4;
306                 doubleWordRegistersMap[digitalNoiseFilterNumberOffset] = new DoubleWordRegister(this)
307                     .WithTag("FILNUM0", 0, 2)
308                     .WithReservedBits(2, 6)
309                     .WithTag("FILNUM1", 8, 2)
310                     .WithReservedBits(10, 6)
311                     .WithTag("FILNUM2", 16, 2)
312                     .WithReservedBits(18, 6)
313                     .WithTag("FILNUM3", 24, 2)
314                     .WithReservedBits(26, 6);
315 
316                 var digitalNoiseFilterClockSelectionOffset = (long)Registers.DigitalNoiseFilterClockSelection + registerIdx * 0x4;
317                 doubleWordRegistersMap[digitalNoiseFilterClockSelectionOffset] = new DoubleWordRegister(this)
318                     .WithTag("FILCLK0", 0, 2)
319                     .WithReservedBits(2, 6)
320                     .WithTag("FILCLK1", 8, 2)
321                     .WithReservedBits(10, 6)
322                     .WithTag("FILCLK2", 16, 2)
323                     .WithReservedBits(18, 6)
324                     .WithTag("FILCLK3", 24, 2)
325                     .WithReservedBits(26, 6);
326             }
327 
328             byteRegistersMap[(long)Registers.SDChannel0VoltageControlRegister] = new ByteRegister(this)
329                 .WithTaggedFlag("SD0_PVDD", 0)
330                 .WithReservedBits(1, 7);
331 
332             byteRegistersMap[(long)Registers.SDChannel1VoltageControlRegister] = new ByteRegister(this)
333                 .WithTaggedFlag("SD1_PVDD", 0)
334                 .WithReservedBits(1, 7);
335 
336             byteRegistersMap[(long)Registers.QSPIVoltageControlRegister] = new ByteRegister(this)
337                 .WithTaggedFlag("QSPI_PVDD", 0)
338                 .WithReservedBits(1, 7);
339 
340             byteRegistersMap[(long)Registers.EthernetChannel0IOVoltageModeControl] = new ByteRegister(this)
341                 .WithTaggedFlag("ETH0_1.8V_PVDD", 0)
342                 .WithTaggedFlag("ETH0_2.5V_PVDD", 1)
343                 .WithReservedBits(2, 6);
344 
345             byteRegistersMap[(long)Registers.EthernetChannel1IOVoltageModeControl] = new ByteRegister(this)
346                 .WithTaggedFlag("ETH1_1.8V_PVDD", 0)
347                 .WithTaggedFlag("ETH1_2.5V_PVDD", 1)
348                 .WithReservedBits(2, 6);
349 
350             byteRegistersMap[(long)Registers.WriteProtected] = new ByteRegister(this)
351                 .WithReservedBits(0, 6)
352                 .WithTaggedFlag("PFCWE", 6)
353                 .WithTaggedFlag("BOWI", 7);
354 
355             byteRegistersMap[(long)Registers.EthernetMiiRgmiiModeControl] = new ByteRegister(this)
356                 .WithTaggedFlag("ETH0_MODE", 0)
357                 .WithTaggedFlag("ETH1_MODE", 1)
358                 .WithReservedBits(2, 6);
359 
360             byteRegisters = new ByteRegisterCollection(this, byteRegistersMap);
361             wordRegisters = new WordRegisterCollection(this, wordRegistersMap);
362             doubleWordRegisters = new DoubleWordRegisterCollection(this, doubleWordRegistersMap);
363         }
364 
UpdateGPIO()365         private void UpdateGPIO()
366         {
367             for(var gpioIdx = 0; gpioIdx < NrOfPins; gpioIdx++)
368             {
369                 var mode = portMode[gpioIdx];
370                 if(interruptEnabled[gpioIdx])
371                 {
372                     Connections[gpioIdx].Set(State[gpioIdx]);
373                 }
374                 else if(mode == PortMode.Output)
375                 {
376                     Connections[gpioIdx].Set(output[gpioIdx]);
377                 }
378                 else if(mode == PortMode.OutputInput)
379                 {
380                     Connections[gpioIdx].Set(State[gpioIdx] || output[gpioIdx]);
381                 }
382                 else
383                 {
384                     Connections[gpioIdx].Set(false);
385                 }
386                 this.DebugLog("GPIO pin {0}: {1}", gpioIdx, Connections[gpioIdx].IsSet ? "set" : "unset");
387             }
388 
389             foreach(var entry in pinAndFunctionToInterruptMap)
390             {
391                 var gpioIdx = entry.Key.Item1;
392                 var functionIdx = entry.Key.Item2;
393                 var interruptIdx = entry.Value;
394 
395                 if(pinFunctionEnabled[gpioIdx] && pinFunction[gpioIdx] == functionIdx)
396                 {
397                     functionInterrupts[interruptIdx].Set(State[gpioIdx]);
398                     this.DebugLog("IRQ{0}: {1}", interruptIdx, State[gpioIdx] ? "set" : "unset");
399                 }
400                 else
401                 {
402                     functionInterrupts[interruptIdx].Set(false);
403                     this.DebugLog("IRQ{0}: unset", interruptIdx);
404                 }
405             }
406         }
407 
CreateGPIOOutputValueProviderCallback(int portIdx)408         private Func<int, bool, bool> CreateGPIOOutputValueProviderCallback(int portIdx)
409         {
410             return (pinIdx, _) =>
411             {
412                 // Documentation doesn't state what should be done when accessing pins with indices
413                 // greater than real number of pins
414                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
415                 {
416                     switch(portMode[gpioIdx])
417                     {
418                         case PortMode.Output:
419                             return output[gpioIdx];
420                         case PortMode.OutputInput:
421                             return State[gpioIdx] || output[gpioIdx];
422                         default:
423                             return false;
424                     }
425                 }
426                 LogOutOfRangePinWrite(portIdx, pinIdx, Registers.Port);
427                 return false;
428             };
429         }
430 
CreateGPIOOutputWriteCallback(int portIdx)431         private Action<int, bool, bool> CreateGPIOOutputWriteCallback(int portIdx)
432         {
433             return (pinIdx, _, value) =>
434             {
435                 // Documentation doesn't state what should be done when accessing pins with indices
436                 // greater than real number of pins
437                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
438                 {
439                     var mode = portMode[gpioIdx];
440                     if(mode == PortMode.Output || mode == PortMode.OutputInput)
441                     {
442                         output[gpioIdx] = value;
443                     }
444                 }
445                 else
446                 {
447                     LogOutOfRangePinRead(portIdx, pinIdx, Registers.Port);
448                 }
449             };
450         }
451 
CreateGPIOInputValueProviderCallback(int portIdx)452         private Func<int, bool, bool> CreateGPIOInputValueProviderCallback(int portIdx)
453         {
454             return (pinIdx, _) =>
455             {
456                 // Documentation doesn't state what should be done when accessing pins with indices
457                 // greater than real number of pins
458                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
459                 {
460                     switch(portMode[gpioIdx])
461                     {
462                         case PortMode.Input:
463                             return State[gpioIdx];
464                         case PortMode.OutputInput:
465                             return State[gpioIdx] || output[gpioIdx];
466                         default:
467                             return false;
468                     }
469                 }
470                 LogOutOfRangePinRead(portIdx, pinIdx, Registers.PortInput);
471                 return false;
472             };
473         }
474 
CreatePortModeValueProviderCallback(int portIdx)475         private Func<int, PortMode, PortMode> CreatePortModeValueProviderCallback(int portIdx)
476         {
477             return (pinIdx, _) =>
478             {
479                 // Documentation doesn't state what should be done when accessing pins with indices
480                 // greater than real number of pins
481                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
482                 {
483                     return portMode[gpioIdx];
484                 }
485                 LogOutOfRangePinRead(portIdx, pinIdx, Registers.PortMode);
486                 return PortMode.HighImpedance;
487             };
488         }
489 
CreatePortModeWriteCallback(int portIdx)490         private Action<int, PortMode, PortMode> CreatePortModeWriteCallback(int portIdx)
491         {
492             return (pinIdx, _, value) =>
493             {
494                 // Documentation doesn't state what should be done when accessing pins with indices
495                 // greater than real number of pins
496                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
497                 {
498                     portMode[gpioIdx] = value;
499                     switch(value)
500                     {
501                         case PortMode.HighImpedance:
502                             output[gpioIdx] = false;
503                             State[gpioIdx] = false;
504                             break;
505                         case PortMode.Output:
506                             State[gpioIdx] = false;
507                             break;
508                         case PortMode.Input:
509                             output[gpioIdx] = false;
510                             break;
511                         case PortMode.OutputInput:
512                             // Setting this mode doesn't change anything.
513                             break;
514                         default:
515                             throw new Exception("unreachable");
516                     }
517                 }
518                 else
519                 {
520                     LogOutOfRangePinWrite(portIdx, pinIdx, Registers.PortMode);
521                 }
522             };
523         }
524 
CreatePortModeControlValueProviderCallback(int portIdx)525         private Func<int, bool, bool> CreatePortModeControlValueProviderCallback(int portIdx)
526         {
527             return (pinIdx, _) =>
528             {
529                 // Documentation doesn't state what should be done when accessing pins with indices
530                 // greater than real number of pins
531                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
532                 {
533                     return pinFunctionEnabled[gpioIdx];
534                 }
535                 LogOutOfRangePinRead(portIdx, pinIdx, Registers.PortModeControl);
536                 return false;
537             };
538         }
539 
CreatePortModeControlWriteCallback(int portIdx)540         private Action<int, bool, bool> CreatePortModeControlWriteCallback(int portIdx)
541         {
542             return (pinIdx, _, value) =>
543             {
544                 // Documentation doesn't state what should be done when accessing pins with indices
545                 // greater than real number of pins
546                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
547                 {
548                     pinFunctionEnabled[gpioIdx] = value;
549                 }
550                 else
551                 {
552                     LogOutOfRangePinWrite(portIdx, pinIdx, Registers.PortMode);
553                 }
554             };
555         }
556 
CreateInterruptEnableControlValueProviderCallback(int portIdx, int pinIdx)557         private Func<bool, bool> CreateInterruptEnableControlValueProviderCallback(int portIdx, int pinIdx)
558         {
559             return (_) =>
560             {
561                 // Documentation doesn't state what should be done when accessing pins with indices
562                 // greater than real number of pins
563                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
564                 {
565                     return interruptEnabled[gpioIdx];
566                 }
567                 LogOutOfRangePinRead(portIdx, pinIdx, Registers.PortMode);
568                 return false;
569             };
570         }
571 
CreateInterruptEnableControlWriteCallback(int portIdx, int pinIdx)572         private Action<bool, bool> CreateInterruptEnableControlWriteCallback(int portIdx, int pinIdx)
573         {
574             return (_, value) =>
575             {
576                 // Documentation doesn't state what should be done when accessing pins with indices
577                 // greater than real number of pins
578                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
579                 {
580                     interruptEnabled[gpioIdx] = value;
581                 }
582                 else
583                 {
584                     LogOutOfRangePinWrite(portIdx, pinIdx, Registers.InterruptEnableControl);
585                 }
586             };
587         }
588 
CreatePortFunctionControlValueProviderCallback(int portIdx, int pinIdx)589         private Func<ulong, ulong> CreatePortFunctionControlValueProviderCallback(int portIdx, int pinIdx)
590         {
591             return (_) =>
592             {
593                 // Documentation doesn't state what should be done when accessing pins with indices
594                 // greater than real number of pins
595                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
596                 {
597                     return (ulong)pinFunction[gpioIdx];
598                 }
599                 LogOutOfRangePinRead(portIdx, pinIdx, Registers.PortMode);
600                 return 0;
601             };
602         }
603 
CreatePortFunctionControlWriteCallback(int portIdx, int pinIdx)604         private Action<ulong, ulong> CreatePortFunctionControlWriteCallback(int portIdx, int pinIdx)
605         {
606             return (_, value) =>
607             {
608                 // Documentation doesn't state what should be done when accessing pins with indices
609                 // greater than real number of pins
610                 if(TryGetGPIOIdx(portIdx, pinIdx, out var gpioIdx))
611                 {
612                     // We only implement interrupt peripheral functions.
613                     // We also allow selecting default function for pin, which does nothing.
614                     if(value == DefaultPinFunctionIdx || pinAndFunctionToInterruptMap.ContainsKey(Tuple.Create(gpioIdx, (int)value)))
615                     {
616                         pinFunction[gpioIdx] = (int)value;
617                     }
618                     else if(value > MaxPinFunctionIdx)
619                     {
620                         this.WarningLog(
621                             "Trying to set pin function to invalid value {0}. Function value won't change.",
622                             value
623                         );
624                     }
625                     else
626                     {
627                         this.WarningLog(
628                             "Trying to enable function {0} which is not currently supported for port {1}, pin {2}. Function value won't change.",
629                             value,
630                             portIdx,
631                             pinIdx
632                         );
633                     }
634                 }
635                 else
636                 {
637                     LogOutOfRangePinWrite(portIdx, pinIdx, Registers.InterruptEnableControl);
638                 }
639             };
640         }
641 
TryGetGPIOIdx(int portIdx, int pinIdx, out int gpioIdx)642         private bool TryGetGPIOIdx(int portIdx, int pinIdx, out int gpioIdx)
643         {
644             if(pinIdx > GetNrOfGPIOPinsForPort(portIdx))
645             {
646                 gpioIdx = -1;
647                 return false;
648             }
649             gpioIdx = gpioOffsetPerPort[portIdx] + pinIdx;
650             return true;
651         }
652 
GetNrOfGPIOPinsForPort(int portIdx)653         private int GetNrOfGPIOPinsForPort(int portIdx)
654         {
655             // Different ports, have different number of GPIO pins.
656             switch(portIdx)
657             {
658                 case 42:
659                 case 48:
660                     return 5;
661                 case 43:
662                 case 44:
663                 case 45:
664                 case 46:
665                 case 47:
666                     return 4;
667                 case 5:
668                 case 7:
669                 case 8:
670                 case 13:
671                 case 17:
672                 case 20:
673                 case 37:
674                 case 39:
675                 case 40:
676                     return 3;
677                 default:
678                     return 2;
679             }
680         }
681 
LogOutOfRangePinRead(int portIdx, int pinIdx, Registers register)682         private void LogOutOfRangePinRead(int portIdx, int pinIdx, Registers register)
683         {
684             this.WarningLog(
685                 "Trying to read from port {0}, pin {1} in register {2}. This pin doesn't exist. Returning 0x0.",
686                 portIdx,
687                 pinIdx,
688                 register
689             );
690         }
691 
LogOutOfRangePinWrite(int portIdx, int pinIdx, Registers register)692         private void LogOutOfRangePinWrite(int portIdx, int pinIdx, Registers register)
693         {
694             this.WarningLog(
695                 "Trying to write to port {0}, pin {1} in register {2}. This pin doesn't exist. Nothing will happen.",
696                 portIdx,
697                 pinIdx,
698                 register
699             );
700         }
701 
702         private const int NrOfPorts = 49;
703         private const int NrOfPins = 123;
704         private const int NrOfFunctionInterrutps = 8;
705         private const int NrOfPinsInPortRegister = 8;
706         private const int NrOfDrivingAbilityControlRegisters = 46;
707         private const int NrOfSlewRateSwitchingRegisters = 46;
708         private const int NrOfPullUpPullDownSwitchingRegisters = 33;
709         private const int NrOfDigitalNoiseFilterRegisters = 52;
710         private const int MaxPinFunctionIdx = 5;
711         private const int DefaultPinFunctionIdx = 0;
712 
713         private readonly bool[] output = new bool[NrOfPins];
714         private readonly bool[] interruptEnabled = new bool[NrOfPins];
715         private readonly int[] pinFunction = new int[NrOfPins];
716         private readonly bool[] pinFunctionEnabled = new bool[NrOfPins];
717         private readonly PortMode[] portMode = new PortMode[NrOfPins];
718         private readonly int[] gpioOffsetPerPort = new int[NrOfPorts];
719         private readonly GPIO[] functionInterrupts = Enumerable.Range(0, NrOfFunctionInterrutps).Select(_ => new GPIO()).ToArray();
720         private readonly Dictionary<Tuple<int, int>, int> pinAndFunctionToInterruptMap = new Dictionary<Tuple<int, int>, int>
721         {
722             {Tuple.Create(0, 1),    0},
723             {Tuple.Create(1, 1),    1},
724             {Tuple.Create(2, 1),    2},
725             {Tuple.Create(3, 1),    3},
726             {Tuple.Create(4, 1),    4},
727             {Tuple.Create(5, 1),    5},
728             {Tuple.Create(6, 1),    6},
729             {Tuple.Create(7, 1),    7},
730             {Tuple.Create(30, 4),   0},
731             {Tuple.Create(31, 4),   1},
732             {Tuple.Create(32, 4),   2},
733             {Tuple.Create(32, 3),   7},
734             {Tuple.Create(37, 4),   3},
735             {Tuple.Create(38, 4),   4},
736             {Tuple.Create(39, 4),   5},
737             {Tuple.Create(40, 4),   6},
738             {Tuple.Create(41, 4),   7},
739             {Tuple.Create(98, 4),   4},
740             {Tuple.Create(99, 4),   5},
741             {Tuple.Create(100, 4),  6},
742             {Tuple.Create(101, 4),  7},
743             {Tuple.Create(114, 3),  0},
744             {Tuple.Create(115, 3),  1},
745             {Tuple.Create(116, 3),  2},
746             {Tuple.Create(117, 3),  3},
747         };
748 
749         private ByteRegisterCollection byteRegisters;
750         private WordRegisterCollection wordRegisters;
751         private DoubleWordRegisterCollection doubleWordRegisters;
752 
753         private enum PortMode
754         {
755             HighImpedance   = 0x0,
756             Input           = 0x1,
757             Output          = 0x2,
758             OutputInput     = 0x3,
759         }
760 
761         private enum Registers
762         {
763             Port                                    = 0x10,
764             PortMode                                = 0x120,
765             PortModeControl                         = 0x210,
766             PortFunctionControl                     = 0x440,
767             PortInput                               = 0x810,
768             DrivingAbilityControl                   = 0x1010,
769             SlewRateSwitching                       = 0x1410,
770             PullUpPullDownSwitching                 = 0x1C80,
771             DigitalNoiseFilterSwitching             = 0x2008,
772             DigitalNoiseFilterNumber                = 0x2408,
773             DigitalNoiseFilterClockSelection        = 0x2808,
774             InterruptEnableControl                  = 0x2C80,
775             SDChannel0VoltageControlRegister        = 0x3000,
776             SDChannel1VoltageControlRegister        = 0x3004,
777             QSPIVoltageControlRegister              = 0x3008,
778             EthernetChannel0IOVoltageModeControl    = 0x300C,
779             EthernetChannel1IOVoltageModeControl    = 0x3010,
780             WriteProtected                          = 0x3014,
781             EthernetMiiRgmiiModeControl             = 0x3018,
782         }
783     }
784 }
785