1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 using Antmicro.Renode.Peripherals.Bus;
14 using Antmicro.Renode.Utilities;
15 using Antmicro.Renode.Peripherals.GPIOPort;
16 
17 namespace Antmicro.Renode.Peripherals.X86
18 {
19     public sealed class Quark_GPIOController : BaseGPIOPort, IDoubleWordPeripheral, IGPIOReceiver, IKnownSize
20     {
Quark_GPIOController(IMachine machine)21         public Quark_GPIOController(IMachine machine) : base(machine, NumberOfGPIOS)
22         {
23             internalLock = new object();
24             previousState = new bool[NumberOfGPIOS];
25             PortDataDirection = new PinDirection[NumberOfGPIOS];
26             InterruptEnable = new bool[NumberOfGPIOS];
27             InterruptMask = new bool[NumberOfGPIOS];
28             interruptType = new InterruptTrigger[NumberOfGPIOS];
29             activeInterrupts = new bool[NumberOfGPIOS];
30             IRQ = new GPIO();
31             PrepareRegisters();
32         }
33 
ReadDoubleWord(long offset)34         public uint ReadDoubleWord(long offset)
35         {
36             lock(internalLock)
37             {
38                 return registers.Read(offset);
39             }
40         }
41 
Reset()42         public override void Reset()
43         {
44             lock(internalLock)
45             {
46                 base.Reset();
47                 for(int i = 0; i < NumberOfGPIOS; i++)
48                 {
49                     previousState[i] = false;
50                     activeInterrupts[i] = false;
51                     PortDataDirection[i] = PinDirection.Input;
52                     InterruptEnable[i] = false;
53                     InterruptMask[i] = false;
54                     interruptType[i] = InterruptTrigger.ActiveLow;
55                 }
56                 IRQ.Unset();
57                 registers.Reset();
58             }
59         }
60 
WriteDoubleWord(long offset, uint value)61         public void WriteDoubleWord(long offset, uint value)
62         {
63             lock(internalLock)
64             {
65                 registers.Write(offset, value);
66             }
67         }
68 
OnGPIO(int number, bool value)69         public override void OnGPIO(int number, bool value)
70         {
71             if(number < 0 || number >= NumberOfGPIOS)
72             {
73                 throw new ArgumentOutOfRangeException(string.Format("Gpio #{0} called, but only {1} lines are available", number, NumberOfGPIOS));
74             }
75 
76             lock(internalLock)
77             {
78                 if(PortDataDirection[number] == PinDirection.Output)
79                 {
80                     this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number);
81                     return;
82                 }
83 
84                 base.OnGPIO(number, value);
85                 RefreshInterrupts();
86             }
87         }
88 
SetInterruptType(byte pinId, InterruptTrigger trigger)89         public void SetInterruptType(byte pinId, InterruptTrigger trigger)
90         {
91             lock(internalLock)
92             {
93                 interruptType[pinId] = trigger;
94                 switch (trigger)
95                 {
96                     case InterruptTrigger.BothEdges:
97                         interruptBothEdgeField.SetBit(pinId, true);
98                         // interruptType and interruptPolarity are not considered when this bit is set
99                         break;
100                     case InterruptTrigger.RisingEdge:
101                         interruptBothEdgeField.SetBit(pinId, false);
102                         interruptTypeField.SetBit(pinId, true);
103                         interruptPolarityField.SetBit(pinId, true);
104                         break;
105                     case InterruptTrigger.FallingEdge:
106                         interruptBothEdgeField.SetBit(pinId, false);
107                         interruptTypeField.SetBit(pinId, true);
108                         interruptPolarityField.SetBit(pinId, false);
109                         break;
110                     case InterruptTrigger.ActiveHigh:
111                         interruptBothEdgeField.SetBit(pinId, false);
112                         interruptTypeField.SetBit(pinId, false);
113                         interruptPolarityField.SetBit(pinId, true);
114                         break;
115                     case InterruptTrigger.ActiveLow:
116                         interruptBothEdgeField.SetBit(pinId, false);
117                         interruptTypeField.SetBit(pinId, false);
118                         interruptPolarityField.SetBit(pinId, false);
119                         break;
120                 }
121                 RefreshInterrupts();
122             }
123         }
124 
125         public GPIO IRQ { get; private set; }
126         public PinDirection[] PortDataDirection { get; private set; }
127         public bool[] InterruptEnable { get; private set; }
128         public IReadOnlyCollection<InterruptTrigger> InterruptType { get { return interruptType; } }
129         public bool[] InterruptMask { get; private set; }
130         // setting state using this array directly will not raise any interrupts!
131         public new bool[] State { get { return base.State; } }
132         public long Size { get { return 0x78; } }
133 
PrepareRegisters()134         private void PrepareRegisters()
135         {
136             registers = new DoubleWordRegisterCollection(this, new Dictionary<long, DoubleWordRegister>
137             {
138                 {(long)Registers.PortAData, new DoubleWordRegister(this)
139                                 .WithValueField(0, 32, writeCallback: (_, val) =>
140                                 {
141                                     var bits = BitHelper.GetBits((uint)val);
142                                     for(int i = 0; i < bits.Length; i++)
143                                     {
144                                         if(PortDataDirection[i] == PinDirection.Output)
145                                         {
146                                             Connections[i].Set(bits[i]);
147                                             State[i] = bits[i];
148                                         }
149                                     }
150                     }, valueProviderCallback: _ => { return BitHelper.GetValueFromBitsArray(State); })
151                 },
152                 {(long)Registers.PortADataDirection, new DoubleWordRegister(this)
153                                 .WithValueField(0, 32, writeCallback: (_, val) => Array.Copy(BitHelper.GetBits((uint)val).Select(x => x ? PinDirection.Output : PinDirection.Input).ToArray() , PortDataDirection, 32),
154                                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(PortDataDirection.Select(x => x == PinDirection.Output)))
155                 },
156                 {(long)Registers.InterruptEnable, new DoubleWordRegister(this)
157                                 .WithValueField(0, 32, writeCallback: (_, val) => {
158                                             Array.Copy(BitHelper.GetBits((uint)val), InterruptEnable, 32);
159                                             RefreshInterrupts();
160                                         },
161                                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(InterruptEnable))
162                 },
163                 {(long)Registers.InterruptType, new DoubleWordRegister(this)
164                                 // true = edge sensitive; false = level sensitive
165                                 .WithValueField(0, 32, out interruptTypeField, writeCallback: (_, val) => CalculateInterruptTypes())
166                 },
167                 {(long)Registers.InterruptPolarity, new DoubleWordRegister(this)
168                                 // true = rising edge / active high; false = falling edge / active low
169                                 .WithValueField(0, 32, out interruptPolarityField, writeCallback: (_, val) => CalculateInterruptTypes())
170                 },
171                 {(long)Registers.InterruptBothEdgeType, new DoubleWordRegister(this)
172                                 .WithValueField(0, 32, out interruptBothEdgeField, writeCallback: (_, val) => CalculateInterruptTypes())
173                 },
174                 {(long)Registers.InterruptMask, new DoubleWordRegister(this)
175                                 .WithValueField(0, 32, writeCallback: (_, val) => {
176                                         Array.Copy(BitHelper.GetBits((uint)val), InterruptMask, 32);
177                                         RefreshInterrupts();
178                                     },
179                                     valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(InterruptMask))
180                 },
181                 {(long)Registers.PortAExternalPort, new DoubleWordRegister(this)
182                                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(State))
183                 },
184                 {(long)Registers.ClearInterrupt, new DoubleWordRegister(this)
185                                 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) =>
186                                 {
187                                     foreach(var bit in BitHelper.GetSetBits(val))
188                                     {
189                                         activeInterrupts[bit] = false;
190                                     }
191                                     RefreshInterrupts();
192                                 })
193                 },
194                 {(long)Registers.InterruptStatus, new DoubleWordRegister(this)
195                                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(activeInterrupts.Zip(InterruptMask, (isActive, isMasked) => isActive && !isMasked)))
196                 },
197                 {(long)Registers.RawInterruptStatus, new DoubleWordRegister(this)
198                                 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(activeInterrupts))
199                 }
200             });
201         }
202 
CalculateInterruptTypes()203         private void CalculateInterruptTypes()
204         {
205             lock(internalLock)
206             {
207                 var isBothEdgesSensitive = BitHelper.GetBits((uint)interruptBothEdgeField.Value);
208                 var isEdgeSensitive = BitHelper.GetBits((uint)interruptTypeField.Value);
209                 var isActiveHighOrRisingEdge = BitHelper.GetBits((uint)interruptPolarityField.Value);
210                 for(int i = 0; i < interruptType.Length; i++)
211                 {
212                     if(isBothEdgesSensitive[i])
213                     {
214                         interruptType[i] = InterruptTrigger.BothEdges;
215                     }
216                     else
217                     {
218                         if(isEdgeSensitive[i])
219                         {
220                             interruptType[i] = isActiveHighOrRisingEdge[i]
221                                 ? InterruptTrigger.RisingEdge
222                                 : InterruptTrigger.FallingEdge;
223                         }
224                         else
225                         {
226                             interruptType[i] = isActiveHighOrRisingEdge[i]
227                                 ? InterruptTrigger.ActiveHigh
228                                 : InterruptTrigger.ActiveLow;
229                         }
230                     }
231                 }
232                 RefreshInterrupts();
233             }
234         }
235 
RefreshInterrupts()236         private void RefreshInterrupts()
237         {
238             var irqState = false;
239             for(int i = 0; i < NumberOfGPIOS; i++)
240             {
241                 if(!InterruptEnable[i])
242                 {
243                     continue;
244                 }
245                 var isEdge = State[i] != previousState[i];
246                 switch(interruptType[i])
247                 {
248                     case InterruptTrigger.ActiveHigh:
249                         irqState |= (State[i] && !InterruptMask[i]);
250                         break;
251                     case InterruptTrigger.ActiveLow:
252                         irqState |= (!State[i] && !InterruptMask[i]);
253                         break;
254                     case InterruptTrigger.RisingEdge:
255                         if(isEdge && State[i])
256                         {
257                             irqState |= !InterruptMask[i];
258                             activeInterrupts[i] = true;
259                         }
260                     break;
261                     case InterruptTrigger.FallingEdge:
262                         if(isEdge && !State[i])
263                         {
264                             irqState |= !InterruptMask[i];
265                             activeInterrupts[i] = true;
266                         }
267                     break;
268                     case InterruptTrigger.BothEdges:
269                         if(isEdge)
270                         {
271                             irqState |= !InterruptMask[i];
272                             activeInterrupts[i] = true;
273                         }
274                     break;
275                 }
276             }
277             Array.Copy(State, previousState, State.Length);
278             if(irqState)
279             {
280                 IRQ.Set();
281             }
282             else if(!activeInterrupts.Any(x => x))
283             {
284                 IRQ.Unset();
285             }
286         }
287 
288         private DoubleWordRegisterCollection registers;
289         private InterruptTrigger[] interruptType;
290         private IValueRegisterField interruptPolarityField;
291         private IValueRegisterField interruptTypeField;
292         private IValueRegisterField interruptBothEdgeField;
293         private readonly bool[] activeInterrupts;
294         private readonly bool[] previousState;
295 
296         private readonly object internalLock;
297 
298         private const int NumberOfGPIOS = 32;
299 
300         public enum PinDirection
301         {
302             Input,
303             Output
304         }
305 
306         public enum InterruptTrigger
307         {
308             ActiveLow,
309             ActiveHigh,
310             FallingEdge,
311             RisingEdge,
312             BothEdges
313         }
314 
315         internal enum Registers : long
316         {
317             PortAData = 0x0,
318             PortADataDirection = 0x4,
319             PortADataSource = 0x8,
320             InterruptEnable = 0x30,
321             InterruptMask = 0x34,
322             InterruptType = 0x38,
323             InterruptPolarity = 0x3C,
324             InterruptStatus = 0x40,
325             RawInterruptStatus = 0x44,
326             DebounceEnable = 0x48,
327             ClearInterrupt = 0x4C,
328             PortAExternalPort = 0x50,
329             SynchronizationLevel = 0x60,
330             InterruptBothEdgeType = 0x68
331         }
332     }
333 }
334