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 System.Linq;
9 using System.Collections.Generic;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Logging;
12 using Antmicro.Renode.Peripherals.Bus;
13 using Antmicro.Renode.Core.Structure.Registers;
14 
15 namespace Antmicro.Renode.Peripherals.GPIOPort
16 {
17     abstract public class RenesasRA_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IProvidesRegisterCollection<DoubleWordRegisterCollection>, IKnownSize
18     {
RenesasRA_GPIO(IMachine machine, int portNumber, int numberOfConnections, RenesasRA_GPIOMisc pfsMisc)19         public RenesasRA_GPIO(IMachine machine, int portNumber, int numberOfConnections, RenesasRA_GPIOMisc pfsMisc) : base(machine, numberOfConnections)
20         {
21             RegistersCollection = new DoubleWordRegisterCollection(this);
22             PinConfigurationRegistersCollection = new DoubleWordRegisterCollection(this);
23 
24             pinDirection = new IEnumRegisterField<Direction>[numberOfConnections];
25             usedAsIRQ = new IFlagRegisterField[numberOfConnections];
26 
27             this.pfsMisc = pfsMisc;
28             this.portNumber = portNumber;
29 
30             IRQ0 = new GPIO();
31             IRQ1 = new GPIO();
32             IRQ2 = new GPIO();
33             IRQ3 = new GPIO();
34             IRQ4 = new GPIO();
35             IRQ5 = new GPIO();
36             IRQ6 = new GPIO();
37             IRQ7 = new GPIO();
38             IRQ8 = new GPIO();
39             IRQ9 = new GPIO();
40             IRQ10 = new GPIO();
41             IRQ11 = new GPIO();
42             IRQ12 = new GPIO();
43             IRQ13 = new GPIO();
44             IRQ14 = new GPIO();
45             IRQ15 = new GPIO();
46 
47             DefineRegisters();
48             DefinePinConfigurationRegisters();
49         }
50 
ReadDoubleWord(long offset)51         public uint ReadDoubleWord(long offset)
52         {
53             return RegistersCollection.Read(offset);
54         }
55 
WriteDoubleWord(long offset, uint value)56         public void WriteDoubleWord(long offset, uint value)
57         {
58             RegistersCollection.Write(offset, value);
59         }
60 
Reset()61         public override void Reset()
62         {
63             base.Reset();
64 
65             IRQ0.Unset();
66             IRQ1.Unset();
67             IRQ2.Unset();
68             IRQ3.Unset();
69             IRQ4.Unset();
70             IRQ5.Unset();
71             IRQ6.Unset();
72             IRQ7.Unset();
73             IRQ8.Unset();
74             IRQ9.Unset();
75             IRQ10.Unset();
76             IRQ11.Unset();
77             IRQ12.Unset();
78             IRQ13.Unset();
79             IRQ14.Unset();
80             IRQ15.Unset();
81         }
82 
83         [ConnectionRegion("pinConfiguration")]
ReadDoubleWordFromPinConfiguration(long offset)84         public uint ReadDoubleWordFromPinConfiguration(long offset)
85         {
86             return PinConfigurationRegistersCollection.Read(offset);
87         }
88 
89         [ConnectionRegion("pinConfiguration")]
WriteDoubleWordToPinConfiguration(long offset, uint value)90         public void WriteDoubleWordToPinConfiguration(long offset, uint value)
91         {
92             if(!pfsMisc.PFSWriteEnabled)
93             {
94                 this.Log(LogLevel.Warning, "Trying to write to pin configuration registers (PFS) when PFSWE is deasserted");
95                 return;
96             }
97 
98             PinConfigurationRegistersCollection.Write(offset, value);
99         }
100 
OnGPIO(int number, bool value)101         public override void OnGPIO(int number, bool value)
102         {
103             if(!CheckPinNumber(number))
104             {
105                 return;
106             }
107 
108             base.OnGPIO(number, value);
109 
110             if(pinDirection[number].Value != Direction.Input)
111             {
112                 this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number);
113                 return;
114             }
115 
116             if(TryGetInterruptOutput(number, out var irq))
117             {
118                 irq.Set(value);
119             }
120         }
121 
122         public GPIO IRQ0 { get; }
123         public GPIO IRQ1 { get; }
124         public GPIO IRQ2 { get; }
125         public GPIO IRQ3 { get; }
126         public GPIO IRQ4 { get; }
127         public GPIO IRQ5 { get; }
128         public GPIO IRQ6 { get; }
129         public GPIO IRQ7 { get; }
130         public GPIO IRQ8 { get; }
131         public GPIO IRQ9 { get; }
132         public GPIO IRQ10 { get; }
133         public GPIO IRQ11 { get; }
134         public GPIO IRQ12 { get; }
135         public GPIO IRQ13 { get; }
136         public GPIO IRQ14 { get; }
137         public GPIO IRQ15 { get; }
138 
139         public DoubleWordRegisterCollection RegistersCollection { get; }
140 
141         public DoubleWordRegisterCollection PinConfigurationRegistersCollection { get; }
142 
143         public long Size => 0x20;
144 
UpdateIRQOutput()145         private void UpdateIRQOutput()
146         {
147             for(var i = 0; i < NumberOfConnections; ++i)
148             {
149                 if(pinDirection[i].Value != Direction.Input)
150                 {
151                     continue;
152                 }
153 
154                 if(TryGetInterruptOutput(i, out var irq) && irq.IsSet != State[i])
155                 {
156                     irq.Set(State[i]);
157                 }
158             }
159         }
160 
DefineRegisters()161         private void DefineRegisters()
162         {
163             Registers.DirectionOutput.Define(this)
164                 .WithEnumFields(0, 1, NumberOfConnections, out pinDirection, name: "PDR",
165                     changeCallback: (i, _, value) => { if(value == Direction.Input) UpdateIRQOutput(); })
166                 .WithFlags(16, NumberOfConnections, name: "PODR",
167                     valueProviderCallback: (i, _) => Connections[i].IsSet,
168                     changeCallback: (i, _, value) => SetOutput(i, value))
169             ;
170 
171             Registers.StateEventInput.Define(this)
172                 .WithFlags(0, NumberOfConnections, FieldMode.Read, name: "PIDR",
173                     valueProviderCallback: (i, _) => GetInput(i))
174                 .WithTag("EIDR", 16, NumberOfConnections)
175             ;
176 
177             Registers.Output.Define(this)
178                 .WithFlags(0, NumberOfConnections, FieldMode.Write, name: "POSR",
179                     writeCallback: (i, _, value) => { if(value) SetOutput(i, true); })
180                 .WithFlags(16, NumberOfConnections, FieldMode.Write, name: "PORR",
181                     writeCallback: (i, _, value) => { if(value) SetOutput(i, false); })
182             ;
183         }
184 
DefinePinConfigurationRegisters()185         private void DefinePinConfigurationRegisters()
186         {
187             for(var i = 0; i < NumberOfConnections; ++i)
188             {
189                 var idx = i;
190                 var offset = idx * 0x4;
191                 var register = new DoubleWordRegister(this)
192                     .WithFlag(0, name: "PODR",
193                         valueProviderCallback: _ => Connections[idx].IsSet,
194                         changeCallback: (_, value) => SetOutput(idx, value))
195                     .WithFlag(1, FieldMode.Read, name: "PIDR",
196                         valueProviderCallback: _ => GetInput(idx))
197                     .WithEnumField(2, 1, out pinDirection[idx], name: "PDR",
198                         changeCallback: (_, value) => { if(value == Direction.Input) UpdateIRQOutput(); })
199                     .WithReservedBits(3, 1)
200                     .WithFlag(4, name: "PCR")
201                     .WithReservedBits(5, 1)
202                     .WithFlag(6, name: "NCODR")
203                     .WithReservedBits(7, 3)
204                     .WithTag("DSCR", 10, 2)
205                     .WithTag("EOFR", 12, 2)
206                     .WithFlag(14, out usedAsIRQ[idx], name: "ISEL",
207                         changeCallback: (_, value) => UpdateIRQOutput())
208                     .WithTaggedFlag("ASEL", 15)
209                     .WithTaggedFlag("PMR", 16)
210                     .WithReservedBits(17, 7)
211                     .WithTag("PSEL", 24, 5)
212                     .WithReservedBits(29, 3)
213                 ;
214                 PinConfigurationRegistersCollection.AddRegister(offset, register);
215             }
216         }
217 
GetInput(int index)218         private bool GetInput(int index)
219         {
220             var value = State[index];
221             if(pinDirection[index].Value == Direction.Output)
222             {
223                 value |= Connections[index].IsSet;
224             }
225             return value;
226         }
227 
SetOutput(int index, bool value)228         private void SetOutput(int index, bool value)
229         {
230             if(pinDirection[index].Value != Direction.Output)
231             {
232                 this.Log(LogLevel.Warning, "Trying to set pin level, but pin is not in output mode, ignoring");
233                 return;
234             }
235 
236             Connections[index].Set(value);
237         }
238 
TryGetInterruptOutput(int number, out GPIO irq)239         private bool TryGetInterruptOutput(int number, out GPIO irq)
240         {
241             irq = null;
242             if(!usedAsIRQ[number].Value)
243             {
244                 return false;
245             }
246 
247             var interruptOutput = PinInterruptOutputs[portNumber].SingleOrDefault(e => e.PinNumber == number);
248             if(interruptOutput == null)
249             {
250                 this.Log(LogLevel.Warning, "Trying to use pin#{0} as interrupt, but it's not associated with any IRQn output", number);
251                 return false;
252             }
253 
254             irq = interruptOutput.IRQ;
255             return true;
256         }
257 
258         abstract protected List<InterruptOutput>[] PinInterruptOutputs { get; }
259 
260         private readonly RenesasRA_GPIOMisc pfsMisc;
261         private readonly int portNumber;
262 
263         private IEnumRegisterField<Direction>[] pinDirection;
264         private IFlagRegisterField[] usedAsIRQ;
265 
266         protected class InterruptOutput
267         {
InterruptOutput(int pinNumber, GPIO irq)268             public InterruptOutput(int pinNumber, GPIO irq)
269             {
270                 PinNumber = pinNumber;
271                 IRQ = irq;
272             }
273 
274             public int PinNumber { get; }
275             public GPIO IRQ { get; }
276         }
277 
278         private enum Direction
279         {
280             Input,
281             Output,
282         }
283 
284         private enum Registers
285         {
286             DirectionOutput = 0x00,
287             StateEventInput = 0x04,
288             Output = 0x08,
289             EventOutput = 0x0C,
290         }
291     }
292 }
293