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 using System.Collections.Generic;
7 using Antmicro.Renode.Core;
8 using Antmicro.Renode.Core.Structure.Registers;
9 using Antmicro.Renode.Logging;
10 using Antmicro.Renode.Peripherals.Bus;
11 
12 namespace Antmicro.Renode.Peripherals.GPIOPort
13 {
14     public class MAX32650_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
15     {
MAX32650_GPIO(IMachine machine, int numberOfPins)16         public MAX32650_GPIO(IMachine machine, int numberOfPins) : base(machine, numberOfPins)
17         {
18             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
19             IRQ = new GPIO();
20             WakeUpIRQ = new GPIO();
21         }
22 
Reset()23         public override void Reset()
24         {
25             base.Reset();
26             registers.Reset();
27             UpdateInterrupts();
28         }
29 
ReadDoubleWord(long offset)30         public uint ReadDoubleWord(long offset)
31         {
32             return registers.Read(offset);
33         }
34 
WriteDoubleWord(long offset, uint value)35         public void WriteDoubleWord(long offset, uint value)
36         {
37             registers.Write(offset, value);
38         }
39 
OnGPIO(int number, bool value)40         public override void OnGPIO(int number, bool value)
41         {
42             var previousValue = State[number];
43             base.OnGPIO(number, value);
44             OnPinStateChanged(number, previousValue, value);
45         }
46 
47         public long Size => 0x400;
48 
49         public GPIO IRQ { get; }
50         public GPIO WakeUpIRQ { get; }
51 
UpdatePinOutput(int idx, bool value)52         private void UpdatePinOutput(int idx, bool value)
53         {
54             if(!pinOutputEnabled[idx].Value)
55             {
56                 this.Log(LogLevel.Warning, "Attempted to set pin{0} to {1}, but it's not set to OUTPUT; ignoring", idx, value);
57                 return;
58             }
59 
60             if(!pinEnabled[idx].Value)
61             {
62                 this.Log(LogLevel.Warning, "Attempted to set pin{0} to {1}, but it's not enabled; ignoring", idx, value);
63                 return;
64             }
65 
66             if(State[idx] == value)
67             {
68                 return;
69             }
70 
71             Connections[idx].Set(value);
72             State[idx] = value;
73         }
74 
OnPinStateChanged(int idx, bool previous, bool current)75         private void OnPinStateChanged(int idx, bool previous, bool current)
76         {
77             var interruptPending = false;
78             switch(interruptPolarity[idx].Value)
79             {
80                 case InterruptPolarity.LowFalling:
81                     if(interruptMode[idx].Value == InterruptMode.EdgeTriggered)
82                     {
83                         interruptPending = !current && (previous != current);
84                     }
85                     if(interruptMode[idx].Value == InterruptMode.LevelTriggered)
86                     {
87                         interruptPending = !current;
88                     }
89                     break;
90                 case InterruptPolarity.HighRising:
91                     if(interruptMode[idx].Value == InterruptMode.EdgeTriggered)
92                     {
93                         interruptPending = current && (previous != current);
94                     }
95                     if(interruptMode[idx].Value == InterruptMode.LevelTriggered)
96                     {
97                         interruptPending = current;
98                     }
99                     break;
100             }
101 
102             interruptStatus[idx].Value |= interruptPending;
103             UpdateInterrupts();
104         }
105 
UpdateInterrupts()106         private void UpdateInterrupts()
107         {
108             var pendingInterrupt = false;
109             var pendingWakeUp = false;
110             for(var i = 0; i < NumberOfConnections; ++i)
111             {
112                 pendingInterrupt |= interruptEnabled[i].Value && interruptStatus[i].Value;
113                 pendingWakeUp |= wakeupEnabled[i].Value && interruptStatus[i].Value;
114             }
115             IRQ.Set(pendingInterrupt);
116             WakeUpIRQ.Set(pendingWakeUp);
117         }
118 
BuildRegisterMap()119         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
120         {
121             var registersMap = new Dictionary<long, DoubleWordRegister>
122             {
123                 {(long)Registers.Enable, new DoubleWordRegister(this, 0xffffffff)
124                     .WithFlags(0, 32, out pinEnabled, name: "EN.gpio_en")
125                 },
126                 {(long)Registers.EnableSet, new DoubleWordRegister(this, 0xffffffff)
127                     .WithFlags(0, 32, name: "EN_SET.all",
128                         valueProviderCallback: (i, _) => pinEnabled[i].Value,
129                         writeCallback: (i, _, val) => { if(val) pinEnabled[i].Value = true; })
130                 },
131                 {(long)Registers.EnableClear, new DoubleWordRegister(this, 0xffffffff)
132                     .WithFlags(0, 32, name: "EN_CLR.all",
133                         valueProviderCallback: (i, _) => pinEnabled[i].Value,
134                         writeCallback: (i, _, val) => { if(val) pinEnabled[i].Value = false; })
135                 },
136                 {(long)Registers.OutputEnable, new DoubleWordRegister(this, 0x8000000)
137                     .WithFlags(0, 32, out pinOutputEnabled, name: "OUT_EN.gpio_out_en")
138                 },
139                 {(long)Registers.OutputEnableSet, new DoubleWordRegister(this)
140                     .WithFlags(0, 32, name: "OUT_EN_SET.all",
141                         valueProviderCallback: (i, _) => pinOutputEnabled[i].Value,
142                         writeCallback: (i, _, val) => { if(val) pinOutputEnabled[i].Value = true; })
143                 },
144                 {(long)Registers.OutputEnableClear, new DoubleWordRegister(this)
145                     .WithFlags(0, 32, out pinOutputEnabled, name: "OUT_EN_CLR.all",
146                         valueProviderCallback: (i, _) => pinOutputEnabled[i].Value,
147                         writeCallback: (i, _, val) => { if(val) pinOutputEnabled[i].Value = false; })
148                 },
149                 {(long)Registers.Output, new DoubleWordRegister(this)
150                     .WithFlags(0, 32, name: "OUT.gpio_out",
151                         valueProviderCallback: (i, _) => State[i] && pinOutputEnabled[i].Value,
152                         writeCallback: (i, _, val) => UpdatePinOutput(i, val))
153                 },
154                 {(long)Registers.OutputSet, new DoubleWordRegister(this)
155                     .WithFlags(0, 32, name: "OUT_SET.gpio_out_set",
156                         valueProviderCallback: (i, _) => State[i] && pinOutputEnabled[i].Value,
157                         writeCallback: (i, _, val) => { if(val) UpdatePinOutput(i, true) ;})
158                 },
159                 {(long)Registers.OutputClear, new DoubleWordRegister(this)
160                     .WithFlags(0, 32, name: "OUT_CLR.gpio_out_clr",
161                         valueProviderCallback: (i, _) => State[i] && pinOutputEnabled[i].Value,
162                         writeCallback: (i, _, val) => { if(val) UpdatePinOutput(i, false); })
163                 },
164                 {(long)Registers.Input, new DoubleWordRegister(this)
165                     .WithFlags(0, 32, name: "IN.gpio_in",
166                         valueProviderCallback: (i, _) =>
167                         {
168                             if(!pinInputEnabled[i].Value)
169                             {
170                                 this.Log(LogLevel.Noisy, "Trying to get value from pin {0} which doesn't have input mode enabled");
171                                 return false;
172                             }
173                             return State[i];
174                         })
175                 },
176                 {(long)Registers.InterruptMode, new DoubleWordRegister(this)
177                     .WithEnumFields<DoubleWordRegister, InterruptMode>(0, 1, 32, out interruptMode, name: "INT_MOD.gpio_int_mod")
178                     .WithChangeCallback((_, __) => UpdateInterrupts())
179                 },
180                 {(long)Registers.InterruptPolarity, new DoubleWordRegister(this)
181                     .WithEnumFields<DoubleWordRegister, InterruptPolarity>(0, 1, 32, out interruptPolarity, name: "INT_POL.gpio_int_pol")
182                     .WithChangeCallback((_, __) => UpdateInterrupts())
183                 },
184                 {(long)Registers.InputEnabled, new DoubleWordRegister(this, 0xffffffff)
185                     .WithFlags(0, 32, out pinInputEnabled, name: "IN_EN.gpio_in_en")
186                 },
187                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
188                     .WithFlags(0, 32, out interruptEnabled, name: "INT_EN.gpio_int_en")
189                     .WithChangeCallback((_, __) => UpdateInterrupts())
190                 },
191                 {(long)Registers.InterruptEnableSet, new DoubleWordRegister(this)
192                     .WithFlags(0, 32, name: "INT_EN_SET.gpio_int_en_set",
193                         valueProviderCallback: (i, _) => interruptEnabled[i].Value,
194                         writeCallback: (i, _, value) =>
195                         {
196                             if(value)
197                             {
198                                 interruptEnabled[i].Value = true;
199                             }
200                         })
201                     .WithWriteCallback((_, __) => UpdateInterrupts())
202                 },
203                 {(long)Registers.InterruptEnableClear, new DoubleWordRegister(this)
204                     .WithFlags(0, 32,  name: "INT_EN_CLR.gpio_int_en_clr",
205                         valueProviderCallback: (i, _) => interruptEnabled[i].Value,
206                         writeCallback: (i, _, value) =>
207                         {
208                             if(value)
209                             {
210                                 interruptEnabled[i].Value = false;
211                             }
212                         })
213                     .WithWriteCallback((_, __) => UpdateInterrupts())
214                 },
215                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
216                     .WithFlags(0, 32, out interruptStatus, FieldMode.Read, name: "INT_STAT.gpio_int_stat")
217                 },
218                 {(long)Registers.InterruptStatusClear, new DoubleWordRegister(this)
219                     .WithFlags(0, 32, name: "INT_CLR.all",
220                         valueProviderCallback: (i, _) => interruptStatus[i].Value,
221                         writeCallback: (i, _, value) =>
222                         {
223                             if(value)
224                             {
225                                 interruptStatus[i].Value = false;
226                             }
227                         })
228                     .WithWriteCallback((_, __) => UpdateInterrupts())
229                 },
230                 {(long)Registers.WakeEnable, new DoubleWordRegister(this)
231                     .WithFlags(0, 32, out wakeupEnabled, name: "WAKE_EN.gpio_wake_en")
232                     .WithChangeCallback((_, __) => UpdateInterrupts())
233                 },
234                 {(long)Registers.WakeEnableSet, new DoubleWordRegister(this)
235                     .WithFlags(0, 32, name: "WAKE_EN_SET.all",
236                         valueProviderCallback: (i, _) => wakeupEnabled[i].Value,
237                         writeCallback: (i, _, value) =>
238                         {
239                             if(value)
240                             {
241                                 wakeupEnabled[i].Value = true;
242                             }
243                         })
244                     .WithWriteCallback((_, __) => UpdateInterrupts())
245                 },
246                 {(long)Registers.WakeEnableClear, new DoubleWordRegister(this)
247                     .WithFlags(0, 32, name: "WAKE_EN_CLR.all",
248                         valueProviderCallback: (i, _) => wakeupEnabled[i].Value,
249                         writeCallback: (i, _, value) =>
250                         {
251                             if(value)
252                             {
253                                 wakeupEnabled[i].Value = false;
254                             }
255                         })
256                     .WithWriteCallback((_, __) => UpdateInterrupts())
257                 },
258                 {(long)Registers.InterruptDualEdge, new DoubleWordRegister(this)
259                     .WithTag("INT_DUAL_EDGE.gpio_int_dual_edge", 0, 32)
260                 },
261                 {(long)Registers.InputMode1, new DoubleWordRegister(this)
262                     .WithTag("PAD_CFG1.gpio_pad_cfg1", 0, 32)
263                 },
264                 {(long)Registers.InputMode2, new DoubleWordRegister(this)
265                     .WithTag("PAD_CFG2.gpio_pad_cfg2", 0, 32)
266                 },
267                 {(long)Registers.AlternateFunction1, new DoubleWordRegister(this)
268                     .WithTag("EN1.gpio_en1", 0, 32)
269                 },
270                 {(long)Registers.AlternateFunction1Set, new DoubleWordRegister(this)
271                     .WithTag("EN1_SET.all", 0, 32)
272                 },
273                 {(long)Registers.AlternateFunction1Clear, new DoubleWordRegister(this)
274                     .WithTag("EN1_CLR.all", 0, 32)
275                 },
276                 {(long)Registers.AlternateFunction2, new DoubleWordRegister(this)
277                     .WithTag("EN2.gpio_en2", 0, 32)
278                 },
279                 {(long)Registers.AlternateFunction2Set, new DoubleWordRegister(this)
280                     .WithTag("EN2_SET.all", 0, 32)
281                 },
282                 {(long)Registers.AlternateFunction2Clear, new DoubleWordRegister(this)
283                     .WithTag("EN2_CLR.all", 0, 32)
284                 },
285                 {(long)Registers.DriveStrength1, new DoubleWordRegister(this)
286                     .WithTag("DS.ds", 0, 32)
287                 },
288                 {(long)Registers.DriveStrength2, new DoubleWordRegister(this)
289                     .WithTag("DS1.all", 0, 32)
290                 },
291                 {(long)Registers.PullUpPullDown, new DoubleWordRegister(this)
292                     .WithTag("PS.all", 0, 32)
293                 },
294                 {(long)Registers.Voltage, new DoubleWordRegister(this)
295                     .WithTag("VSSEL.all", 0, 32)
296                 }
297             };
298             return registersMap;
299         }
300 
301         private IFlagRegisterField[] pinEnabled;
302         private IFlagRegisterField[] pinOutputEnabled;
303         private IFlagRegisterField[] pinInputEnabled;
304         private IFlagRegisterField[] interruptEnabled;
305         private IFlagRegisterField[] interruptStatus;
306         private IFlagRegisterField[] wakeupEnabled;
307 
308         private IEnumRegisterField<InterruptMode>[] interruptMode;
309         private IEnumRegisterField<InterruptPolarity>[] interruptPolarity;
310 
311         private readonly DoubleWordRegisterCollection registers;
312 
313         enum InterruptMode
314         {
315             LevelTriggered = 0,
316             EdgeTriggered,
317         }
318 
319         enum InterruptPolarity
320         {
321             LowFalling = 0,
322             HighRising,
323         }
324 
325         private enum Registers
326         {
327             Enable = 0x00,
328             EnableSet = 0x04,
329             EnableClear = 0x08,
330             OutputEnable = 0x0C,
331             OutputEnableSet = 0x10,
332             OutputEnableClear = 0x14,
333             Output = 0x18,
334             OutputSet = 0x1C,
335             OutputClear = 0x20,
336             Input = 0x24,
337             InterruptMode = 0x28,
338             InterruptPolarity = 0x2C,
339             InputEnabled = 0x30,
340             InterruptEnable = 0x34,
341             InterruptEnableSet = 0x38,
342             InterruptEnableClear = 0x3C,
343             InterruptStatus = 0x40,
344             InterruptStatusClear = 0x48,
345             WakeEnable = 0x4C,
346             WakeEnableSet = 0x50,
347             WakeEnableClear = 0x54,
348             InterruptDualEdge = 0x5C,
349             InputMode1 = 0x60,
350             InputMode2 = 0x64,
351             AlternateFunction1 = 0x68,
352             AlternateFunction1Set = 0x6C,
353             AlternateFunction1Clear = 0x70,
354             AlternateFunction2 = 0x74,
355             AlternateFunction2Set = 0x78,
356             AlternateFunction2Clear = 0x7C,
357             DriveStrength1 = 0xB0,
358             DriveStrength2 = 0xB4,
359             PullUpPullDown = 0xB8,
360             Voltage = 0xC0,
361         }
362     }
363 }
364