1 //
2 // Copyright (c) 2010-2024 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
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.Peripherals;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Logging;
15 
16 namespace Antmicro.Renode.Peripherals.GPIOPort
17 {
18     public class SAM4S_PIO : BaseGPIOPort, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
19     {
20         // Status register reset value is defined at product level, thus there's an appropriate variable in the constructor
SAM4S_PIO(IMachine machine, uint statusRegisterResetVal = 0xFFFFFFFF)21         public SAM4S_PIO(IMachine machine, uint statusRegisterResetVal = 0xFFFFFFFF) : base(machine, NumberOfPins)
22         {
23             innerLock = new object();
24             IRQ = new GPIO();
25             irqManager = new GPIOInterruptManager(IRQ, State);
26             enabled = new IFlagRegisterField[NumberOfPins];
27             useAdditionalIrqMode = new IFlagRegisterField[NumberOfPins];
28             selectEdgeLevel= new IFlagRegisterField[NumberOfPins];
29             selectFallRiseLowHigh = new IFlagRegisterField[NumberOfPins];
30             outputData = new IFlagRegisterField[NumberOfPins];
31             outputDataWriteEnabled = new IFlagRegisterField[NumberOfPins];
32 
33             irqManager = new GPIOInterruptManager(IRQ, State);
34             ResetDirection();
35             RegistersCollection = new DoubleWordRegisterCollection(this);
36             DefineRegisters(statusRegisterResetVal);
37         }
38 
ReadDoubleWord(long offset)39         public uint ReadDoubleWord(long offset)
40         {
41             lock(innerLock)
42             {
43                 return RegistersCollection.Read(offset);
44             }
45         }
46 
WriteDoubleWord(long offset, uint value)47         public void WriteDoubleWord(long offset, uint value)
48         {
49             lock(innerLock)
50             {
51                 RegistersCollection.Write(offset, value);
52             }
53         }
54 
OnGPIO(int number, bool value)55         public override void OnGPIO(int number, bool value)
56         {
57             if(!CheckPinNumber(number))
58             {
59                 return;
60             }
61 
62             lock(innerLock)
63             {
64                 if((irqManager.PinDirection[number] & GPIOInterruptManager.Direction.Input) == 0)
65                 {
66                     this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number);
67                     return;
68                 }
69 
70                 base.OnGPIO(number, value);
71                 irqManager.RefreshInterrupts();
72             }
73         }
74 
Reset()75         public override void Reset()
76         {
77             lock(innerLock)
78             {
79                 base.Reset();
80                 irqManager.Reset();
81                 ResetDirection();
82                 RegistersCollection.Reset();
83             }
84         }
85 
86         public DoubleWordRegisterCollection RegistersCollection { get; }
87 
88         public GPIO IRQ { get; }
89         public long Size => 0x168;
90 
ResetDirection()91         private void ResetDirection()
92         {
93             for(int i = 0; i < NumberOfPins; i++)
94             {
95                 irqManager.PinDirection[i] = GPIOInterruptManager.Direction.Input;
96             }
97         }
98 
DefineRegisters(uint statusRegisterResetVal)99         private void DefineRegisters(uint statusRegisterResetVal)
100         {
101             Registers.PioEnable.Define(this)
102                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
103                         writeCallback: (i, _, value) =>
104                         {
105                             if(value)
106                             {
107                                 enabled[i].Value = true;
108                                 UpdateIOLine(i);
109                             }
110                         },
111                         name: "PER")
112             ;
113 
114             Registers.PioDisable.Define(this)
115                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
116                         writeCallback: (i, _, value) =>
117                         {
118                             if(value)
119                             {
120                                 enabled[i].Value = false;
121                                 UpdateIOLine(i);
122                             }
123                         },
124                         name: "PDR")
125             ;
126 
127             Registers.PioStatus.Define(this, statusRegisterResetVal)
128                 .WithFlags(0, 32, out enabled, FieldMode.Read, name: "PSR")
129             ;
130 
131             Registers.OutputEnable.Define(this)
132                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
133                         writeCallback: (i, _, value) =>
134                         {
135                             if(value)
136                             {
137                                 irqManager.PinDirection[i] |= GPIOInterruptManager.Direction.Output;
138                                 UpdateIOLine(i);
139                             }
140                         },
141                         name: "OER")
142             ;
143 
144             Registers.OutputDisable.Define(this)
145                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
146                         writeCallback: (i, _, value) =>
147                         {
148                             if(value)
149                             {
150                                 irqManager.PinDirection[i] &= ~GPIOInterruptManager.Direction.Output;
151                                 UpdateIOLine(i);
152                             }
153                         },
154                         name: "ODR")
155             ;
156 
157             Registers.OutputStatus.Define(this)
158                 .WithFlags(0, 32, FieldMode.Read,
159                         valueProviderCallback: (i, _) =>
160                         {
161                             return (irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Output) != 0;
162                         },
163                         name: "OSR")
164             ;
165 
166             Registers.GlitchInputFilterEnable.Define(this)
167                 .WithTaggedFlags("IFER", 0, 32)
168             ;
169 
170             Registers.GlitchInputFilterDisable.Define(this)
171                 .WithTaggedFlags("IFDR", 0, 32)
172             ;
173 
174             Registers.GlitchInputFilterStatus.Define(this)
175                 .WithTaggedFlags("IFSR", 0, 32)
176             ;
177 
178             Registers.SetOutputData.Define(this)
179                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
180                         writeCallback: (i, _, value) =>
181                         {
182                             if(value)
183                             {
184                                 outputData[i].Value = true;
185                                 UpdateIOLine(i);
186                             }
187                         },
188                         name: "SODR")
189             ;
190 
191             Registers.ClearOutputData.Define(this)
192                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
193                         writeCallback: (i, _, value) =>
194                         {
195                             if(value)
196                             {
197                                 outputData[i].Value = false;
198                                 UpdateIOLine(i);
199                             }
200                         },
201                         name: "CODR")
202             ;
203 
204             Registers.OutputDataStatus.Define(this)
205                 .WithFlags(0, 32, out outputData,
206                         writeCallback: (i, _, value) =>
207                         {
208                             // Bits in this register can be Read/Write if 1 is set in OutputWriteEnable
209                             if(outputDataWriteEnabled[i].Value)
210                             {
211                                 outputData[i].Value = value;
212                                 UpdateIOLine(i);
213                             }
214                         },
215                         name: "ODSR")
216             ;
217 
218             Registers.PinDataStatus.Define(this)
219                 .WithFlags(0, 32, FieldMode.Read, valueProviderCallback: (i, _) => State[i], name: "PDSR")
220             ;
221 
222             Registers.InterruptEnable.Define(this)
223                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
224                         writeCallback: (i, _, value) =>
225                         {
226                             if(value)
227                             {
228                                 irqManager.InterruptEnable[i] = true;
229                             }
230                         },
231                         name: "IER")
232             ;
233 
234             Registers.InterruptDisable.Define(this)
235                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
236                         writeCallback: (i, _, value) =>
237                         {
238                             if(value)
239                             {
240                                 irqManager.InterruptEnable[i] = false;
241                             }
242                         },
243                         name: "IDR")
244             ;
245 
246             Registers.InterruptMask.Define(this)
247                 .WithFlags(0, 32, FieldMode.Read, valueProviderCallback: (i, _) => irqManager.InterruptEnable[i], name: "IMR")
248             ;
249 
250             Registers.InterruptStatus.Define(this)
251                 .WithFlags(0, 32, FieldMode.Read,
252                         valueProviderCallback: (i, _) =>
253                         {
254                             var result = irqManager.ActiveInterrupts.ElementAt(i);
255                             irqManager.ClearInterrupt(i);
256                             return result;
257                         },
258                         name: "ISR")
259             ;
260 
261             Registers.MultiDriverEnable.Define(this)
262                 .WithTaggedFlags("MDER", 0, 32)
263             ;
264 
265             Registers.MultiDriverDisable.Define(this)
266                 .WithTaggedFlags("MDDR", 0, 32)
267             ;
268 
269             Registers.MultiDriverStatus.Define(this)
270                 .WithTaggedFlags("MDSR", 0, 32)
271             ;
272 
273             Registers.PullUpDisable.Define(this)
274                 .WithTaggedFlags("PUDR", 0, 32)
275             ;
276 
277             Registers.PullUpEnable.Define(this)
278                 .WithTaggedFlags("PUER", 0, 32)
279             ;
280 
281             Registers.PadPullUpStatus.Define(this)
282                 .WithTaggedFlags("PUSR", 0, 32)
283             ;
284 
285             Registers.PeripheralSelect1.Define(this)
286                 .WithTaggedFlags("ABCDSR1", 0, 32)
287             ;
288 
289             Registers.PeripheralSelect2.Define(this)
290                 .WithTaggedFlags("ABCDSR2", 0, 32)
291             ;
292 
293             Registers.InputFilterSlowClockDisable.Define(this)
294                 .WithTaggedFlags("IFSCDR", 0, 32)
295             ;
296 
297             Registers.InputFilterSlowClockEnable.Define(this)
298                 .WithTaggedFlags("IFSCER", 0, 32)
299             ;
300 
301             Registers.InputFilterSlowClockStatus.Define(this)
302                 .WithTaggedFlags("IFSCSR", 0, 32)
303             ;
304 
305             Registers.SlowClockDividerDebouncing.Define(this)
306                 .WithTag("DIV", 0, 14)
307                 .WithReservedBits(14, 18)
308             ;
309 
310             Registers.PadPullDownDisable.Define(this)
311                 .WithTaggedFlags("PPDDR", 0, 32)
312             ;
313 
314             Registers.PadPullDownEnable.Define(this)
315                 .WithTaggedFlags("PPDER", 0, 32)
316             ;
317 
318             Registers.PadPullDownStatus.Define(this)
319                 .WithTaggedFlags("PPDSR", 0, 32)
320             ;
321 
322             Registers.OutputWriteEnable.Define(this)
323                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
324                         writeCallback: (i, _, value) =>
325                         {
326                             if(value)
327                             {
328                                 outputDataWriteEnabled[i].Value = true;
329                             }
330                         },
331                         name: "OWER")
332             ;
333 
334             Registers.OutputWriteDisable.Define(this)
335                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
336                         writeCallback: (i, _, value) =>
337                         {
338                             if(value)
339                             {
340                                 outputDataWriteEnabled[i].Value = false;
341                             }
342                         },
343                         name: "OWDR")
344             ;
345 
346             Registers.OutputWriteStatus.Define(this)
347                 .WithFlags(0, 32, out outputDataWriteEnabled, FieldMode.Read, name: "OWSR")
348             ;
349 
350             Registers.AdditionalInterruptModesEnable.Define(this)
351                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
352                         writeCallback: (i, _, value) =>
353                         {
354                             if(value)
355                             {
356                                 useAdditionalIrqMode[i].Value = true;
357                                 UpdateInterruptType(i);
358                             }
359                         },
360                         name: "AIMER")
361             ;
362 
363             Registers.AdditionalInterruptModesDisable.Define(this)
364                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
365                         writeCallback: (i, _, value) =>
366                         {
367                             if(value)
368                             {
369                                 useAdditionalIrqMode[i].Value = false;
370                                 UpdateInterruptType(i);
371                             }
372                         },
373                         name: "AIMDR")
374             ;
375 
376             Registers.AdditionalInterruptModesMask.Define(this)
377                 .WithFlags(0, 32, out useAdditionalIrqMode, FieldMode.Read, name: "AIMMR")
378             ;
379 
380             Registers.EdgeSelect.Define(this)
381                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
382                         writeCallback: (i, _, value) =>
383                         {
384                             if(value)
385                             {
386                                 selectEdgeLevel[i].Value = false;
387                                 UpdateInterruptType(i);
388                             }
389                         },
390                         name: "ESR")
391             ;
392 
393             Registers.LevelSelect.Define(this)
394                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
395                         writeCallback: (i, _, value) =>
396                         {
397                             if(value)
398                             {
399                                 selectEdgeLevel[i].Value = true;
400                                 UpdateInterruptType(i);
401                             }
402                         },
403                         name: "LSR")
404             ;
405 
406             Registers.EdgeLevelStatus.Define(this)
407                 .WithFlags(0, 32, out selectEdgeLevel, FieldMode.Read, name: "ELSR")
408             ;
409 
410             Registers.FallingEdgeLowLevelSelect.Define(this)
411                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
412                         writeCallback: (i, _, value) =>
413                         {
414                             if(value)
415                             {
416                                 selectFallRiseLowHigh[i].Value = false;
417                                 UpdateInterruptType(i);
418                             }
419                         },
420                         name: "FELLSR")
421             ;
422 
423             Registers.RisingEdgeHighLevelSelect.Define(this)
424                 .WithFlags(0, 32, FieldMode.WriteOneToClear,
425                         writeCallback: (i, _, value) =>
426                         {
427                             if(value)
428                             {
429                                 selectFallRiseLowHigh[i].Value = true;
430                                 UpdateInterruptType(i);
431                             }
432                         },
433                         name: "REHLSR")
434             ;
435 
436             Registers.FallRiseLowHighStatus.Define(this)
437                 .WithFlags(0, 32, out selectFallRiseLowHigh, FieldMode.Read, name: "FRLHSR")
438             ;
439 
440             Registers.LockStatus.Define(this)
441                 .WithTaggedFlags("LOCKSR", 0, 32)
442             ;
443 
444             Registers.WriteProtectionMode.Define(this)
445                 .WithTaggedFlag("WPEN", 0)
446                 .WithReservedBits(1, 7)
447                 .WithTag("WPKEY", 8, 24)
448             ;
449 
450             Registers.WriteProtectionStatus.Define(this)
451                 .WithTaggedFlag("WPVS", 0)
452                 .WithReservedBits(1, 7)
453                 .WithTag("WPVSRC", 8, 16)
454                 .WithReservedBits(24, 8)
455             ;
456 
457             Registers.SchmittTrigger.Define(this)
458                 .WithTaggedFlags("SCHMITT", 0, 32)
459             ;
460 
461             Registers.ParallelCaptureMode.Define(this)
462                 .WithTaggedFlag("PCEN", 0)
463                 .WithReservedBits(1, 3)
464                 .WithTag("DSIZE", 4, 2)
465                 .WithReservedBits(6, 3)
466                 .WithTaggedFlag("ALWYS", 9)
467                 .WithTaggedFlag("HALFS", 10)
468                 .WithTaggedFlag("FRSTS", 11)
469                 .WithReservedBits(12, 20)
470             ;
471 
472             Registers.ParallelCaptureInterruptEnable.Define(this)
473                 .WithTaggedFlag("DRDY", 0)
474                 .WithTaggedFlag("OVRE", 1)
475                 .WithTaggedFlag("ENDRX", 2)
476                 .WithTaggedFlag("RXBUFF", 3)
477                 .WithReservedBits(4, 28)
478             ;
479 
480             Registers.ParallelCaptureInterruptDisable.Define(this)
481                 .WithTaggedFlag("DRDY", 0)
482                 .WithTaggedFlag("OVRE", 1)
483                 .WithTaggedFlag("ENDRX", 2)
484                 .WithTaggedFlag("RXBUFF", 3)
485                 .WithReservedBits(4, 28)
486             ;
487 
488             Registers.ParallelCaptureInterruptMask.Define(this)
489                 .WithTaggedFlag("DRDY", 0)
490                 .WithTaggedFlag("OVRE", 1)
491                 .WithTaggedFlag("ENDRX", 2)
492                 .WithTaggedFlag("RXBUFF", 3)
493                 .WithReservedBits(4, 28)
494             ;
495 
496             Registers.ParallelCaptureInterruptStatus.Define(this)
497                 .WithTaggedFlag("DRDY", 0)
498                 .WithTaggedFlag("OVRE", 1)
499                 .WithTaggedFlag("ENDRX", 2)
500                 .WithTaggedFlag("RXBUFF", 3)
501                 .WithReservedBits(4, 28)
502             ;
503 
504             Registers.ParallelCaptureReceptionHolding.Define(this)
505                 .WithTag("RDATA", 0, 32)
506             ;
507         }
508 
UpdateIOLine(int number)509         private void UpdateIOLine(int number)
510         {
511             if((irqManager.PinDirection[number] & GPIOInterruptManager.Direction.Output) != 0 && enabled[number].Value)
512             {
513                 this.Log(LogLevel.Debug, "Setting connection on pin {0} to {1}", number, outputData[number].Value);
514                 Connections[number].Set(outputData[number].Value);
515             }
516         }
517 
UpdateInterruptType(int number)518         private void UpdateInterruptType(int number)
519         {
520             if(!useAdditionalIrqMode[number].Value)
521             {
522                 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.BothEdges;
523             }
524             else if(selectEdgeLevel[number].Value && selectFallRiseLowHigh[number].Value)
525             {
526                 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.ActiveHigh;
527             }
528             else if(!selectEdgeLevel[number].Value && selectFallRiseLowHigh[number].Value)
529             {
530                 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.RisingEdge;
531             }
532             else if(selectEdgeLevel[number].Value && !selectFallRiseLowHigh[number].Value)
533             {
534                 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.ActiveLow;
535             }
536             else
537             {
538                 irqManager.InterruptType[number] = GPIOInterruptManager.InterruptTrigger.FallingEdge;
539             }
540             this.Log(LogLevel.Debug, "Setting interrupt trigger type to {0}", irqManager.InterruptType[number]);
541         }
542 
543         private IFlagRegisterField[] enabled;
544         private IFlagRegisterField[] outputData;
545         private IFlagRegisterField[] outputDataWriteEnabled;
546         private IFlagRegisterField[] useAdditionalIrqMode;
547         private IFlagRegisterField[] selectEdgeLevel;
548         private IFlagRegisterField[] selectFallRiseLowHigh;
549         private readonly GPIOInterruptManager irqManager;
550         private readonly object innerLock;
551 
552         private const int NumberOfPins = 32;
553 
554         public enum Registers
555         {
556             PioEnable = 0x0000, // WO
557             PioDisable = 0x0004, // WO
558             PioStatus = 0x0008, // RO
559             // Reserved = 0x000C
560             OutputEnable = 0x0010, // WO
561             OutputDisable = 0x0014, // WO
562             OutputStatus = 0x0018, // RO
563             // Reserved = 0x001C
564             GlitchInputFilterEnable = 0x0020, // WO
565             GlitchInputFilterDisable = 0x0024, // WO
566             GlitchInputFilterStatus = 0x0028, // RO
567             // Reserved = 0x002C
568             SetOutputData = 0x0030, // WO
569             ClearOutputData = 0x0034, // WO
570             OutputDataStatus = 0x0038, // (RO or RW)
571             PinDataStatus = 0x003C, // RO
572             InterruptEnable = 0x0040, // WO
573             InterruptDisable = 0x0044, // WO
574             InterruptMask = 0x0048, // RO
575             InterruptStatus = 0x004C, //RO
576             MultiDriverEnable = 0x0050, // WO
577             MultiDriverDisable = 0x0054, // WO
578             MultiDriverStatus = 0x0058, // RO
579             // Reserved = 0x005C
580             PullUpDisable = 0x0060, // WO
581             PullUpEnable = 0x0064, // WO
582             PadPullUpStatus = 0x0068, // RO
583             // Reserved = 0x006C
584             PeripheralSelect1 = 0x0070, // RW
585             PeripheralSelect2 = 0x0074, // RW
586             // Reserved = 0x0078–0x007C
587             InputFilterSlowClockDisable = 0x0080, // WO
588             InputFilterSlowClockEnable = 0x0084, // WO
589             InputFilterSlowClockStatus = 0x0088, // RO
590             SlowClockDividerDebouncing = 0x008C, // RW
591             PadPullDownDisable = 0x0090, // WO
592             PadPullDownEnable = 0x0094, // WO
593             PadPullDownStatus = 0x0098, // RO
594             // Reserved = 0x009C
595             OutputWriteEnable = 0x00A0, // WO
596             OutputWriteDisable = 0x00A4, // WO
597             OutputWriteStatus = 0x00A8, // RO
598             // Reserved = 0x00AC
599             AdditionalInterruptModesEnable = 0x00B0, // WO
600             AdditionalInterruptModesDisable = 0x00B4, // WO
601             AdditionalInterruptModesMask = 0x00B8, // RO
602             // Reserved = 0x00BC
603             EdgeSelect = 0x00C0, // WO
604             LevelSelect = 0x00C4, // WO
605             EdgeLevelStatus = 0x00C8, // RO
606             // Reserved = 0x00CC
607             FallingEdgeLowLevelSelect = 0x00D0, // WO
608             RisingEdgeHighLevelSelect = 0x00D4, // WO
609             FallRiseLowHighStatus = 0x00D8, // RO
610             // Reserved = 0x00DC
611             LockStatus = 0x00E0, // RO
612             WriteProtectionMode = 0x00E4, // RW
613             WriteProtectionStatus = 0x00E8, // RO
614             // Reserved = 0x00EC–0x00FC
615             SchmittTrigger = 0x0100, // RW
616             // Reserved = 0x0104–0x010C
617             // Reserved = 0x0110
618             // Reserved = 0x0114–0x011C
619             // Reserved = 0x0120–0x014C
620             ParallelCaptureMode = 0x0150, // RW
621             ParallelCaptureInterruptEnable = 0x0154, // WO
622             ParallelCaptureInterruptDisable = 0x0158, // WO
623             ParallelCaptureInterruptMask = 0x015C, // RO
624             ParallelCaptureInterruptStatus = 0x0160, // RO
625             ParallelCaptureReceptionHolding = 0x0164, // RO
626         }
627     }
628 }
629