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.Utilities.Collections;
16 
17 namespace Antmicro.Renode.Peripherals.GPIOPort
18 {
19     [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)]
20     public class MPFS_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
21     {
MPFS_GPIO(IMachine machine)22         public MPFS_GPIO(IMachine machine) : base(machine, 32)
23         {
24             locker = new object();
25             IRQ = new GPIO();
26             irqManager = new GPIOInterruptManager(IRQ, State);
27             irqManager.DeassertActiveInterruptTrigger = true;
28 
29             var registersMap = new Dictionary<long, DoubleWordRegister>
30             {
31                 {(long)Registers.InterruptRegister, new DoubleWordRegister(this)
32                     .WithValueField(0, 32,
33                         writeCallback: (_, val) =>
34                         {
35                             foreach(var i in BitHelper.GetSetBits(val))
36                             {
37                                 irqManager.ClearInterrupt(i);
38                                 if((irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Input) != 0)
39                                 {
40                                     Connections[i].Set(false);
41                                 }
42                             }
43                         },
44                         valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(irqManager.ActiveInterrupts), name: "INTR")
45                 },
46 
47                 {(long)Registers.InputRegister, new DoubleWordRegister(this)
48                     .WithValueField(0, 32, FieldMode.Read,
49                         valueProviderCallback: val =>
50                         {
51                             var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Input) != 0);
52                             var result = pins.Zip(State, (pin, state) => pin && state);
53                             return BitHelper.GetValueFromBitsArray(result);
54                         }, name: "GPIN")
55                 },
56 
57                 {(long)Registers.OutputRegister, new DoubleWordRegister(this)
58                     .WithValueField(0, 32,
59                         valueProviderCallback: val =>
60                         {
61                             var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Output) != 0);
62                             var result = pins.Zip(Connections.Values, (pin, state) => pin && state.IsSet);
63                             return BitHelper.GetValueFromBitsArray(result);
64                         },
65                         writeCallback: (_, val) =>
66                         {
67                             // Potentially we should raise an exception, as GPIO is bidirectional,
68                             // but we do not have such infrastructure.
69                             var bits = BitHelper.GetBits((uint)val);
70                             for(var i = 0; i < bits.Length; i++)
71                             {
72                                 if((irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Output) != 0)
73                                 {
74                                     Connections[i].Set(bits[i]);
75                                 }
76                             }
77                         }, name: "GPOUT")
78                 },
79 
80                 {(long)Registers.ClearRegister, new DoubleWordRegister(this)
81                     .WithValueField(0, 32, writeCallback: (_, val) => SetRegisterBits((uint)val, false), name: "CLEAR_BITS")
82                 },
83 
84                 {(long)Registers.SetRegister, new DoubleWordRegister(this)
85                     .WithValueField(0, 32, writeCallback: (_, val) => SetRegisterBits((uint)val, true), name: "SET_BITS")
86                 },
87             };
88 
89             var intTypeToVal = new TwoWayDictionary<GPIOInterruptManager.InterruptTrigger, uint>();
90             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveHigh, 0);
91             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveLow, 1);
92             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.RisingEdge, 2);
93             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.FallingEdge, 3);
94             intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.BothEdges, 4);
95 
96             for(var i = 0; i < RegisterLength; i++)
97             {
98                 var j = i;
99                 registersMap.Add(i * RegisterOffset, new DoubleWordRegister(this)
100                     .WithFlag(0,
101                         writeCallback: (_, val) =>
102                         {
103                             if(val)
104                             {
105                                 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Output;
106                             }
107                             else
108                             {
109                                 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Output;
110                             }
111                         },
112                         valueProviderCallback: _ => (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Output) != 0, name: "OutputRegEnable")
113                     .WithFlag(1,
114                         writeCallback: (_, value) =>
115                         {
116                             if(value)
117                             {
118                                 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Input;
119                             }
120                             else
121                             {
122                                 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Input;
123                             }
124                         },
125                         valueProviderCallback: _ => (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Input) != 0, name: "InputRegEnable")
126                     .WithTag("OutputBufferEnable", 2, 1)
127                     .WithFlag(3, writeCallback: (_, v) => { irqManager.InterruptEnable[j] = v; }, valueProviderCallback: _ => irqManager.InterruptEnable[j], name: "InterruptEnable")
128                     .WithReservedBits(4, 1)
129                     .WithValueField(5, 3,
130                         writeCallback: (_, value) =>
131                         {
132                             if(!intTypeToVal.TryGetValue((uint)value, out var type))
133                             {
134                                 this.Log(LogLevel.Warning, "Invalid interrupt type for pin #{0}: {1}", j, value);
135                                 return;
136                             }
137                             irqManager.InterruptType[j] = type;
138                         },
139                         valueProviderCallback: _ => intTypeToVal[irqManager.InterruptType[j]], name: "InterruptType"));
140             }
141             registers = new DoubleWordRegisterCollection(this, registersMap);
142         }
143 
ReadDoubleWord(long offset)144         public uint ReadDoubleWord(long offset)
145         {
146             lock(locker)
147             {
148                 return registers.Read(offset);
149             }
150         }
151 
WriteDoubleWord(long offset, uint value)152         public void WriteDoubleWord(long offset, uint value)
153         {
154             lock(locker)
155             {
156                 registers.Write(offset, value);
157             }
158         }
159 
OnGPIO(int number, bool value)160         public override void OnGPIO(int number, bool value)
161         {
162             lock(locker)
163             {
164                 var isInput = irqManager.PinDirection[number].HasFlag(GPIOInterruptManager.Direction.Input);
165                 var isOutput = irqManager.PinDirection[number].HasFlag(GPIOInterruptManager.Direction.Output);
166                 if(isOutput && !isInput)
167                 {
168                     this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number);
169                     return;
170                 }
171                 base.OnGPIO(number, value);
172                 irqManager.RefreshInterrupts();
173 
174                 // RefreshInterrupts will update the main IRQ, but it will not update the connection.
175                 // We have to do it manually, as connection reflects if there is an active interrupt for the given pin.
176                 var isIrqActive = irqManager.ActiveInterrupts.ElementAt(number);
177                 if(isInput)
178                 {
179                     Connections[number].Set(isIrqActive);
180                 }
181             }
182         }
183 
Reset()184         public override void Reset()
185         {
186             lock(locker)
187             {
188                 base.Reset();
189                 irqManager.Reset();
190                 registers.Reset();
191             }
192         }
193 
194         public GPIO IRQ { get; private set; }
195 
196         public long Size => 0x1000;
197 
SetRegisterBits(uint regVal, bool state)198         private void SetRegisterBits(uint regVal, bool state)
199         {
200             lock(locker)
201             {
202                 var setBits = BitHelper.GetSetBits(regVal);
203                 foreach (var i in setBits)
204                 {
205                     Connections[i].Set(state);
206                 }
207             }
208         }
209 
210         private readonly GPIOInterruptManager irqManager;
211         private readonly DoubleWordRegisterCollection registers;
212         private readonly object locker;
213 
214         private const int RegisterLength = 32;
215         private const int RegisterOffset = 0x4;
216 
217         private enum Registers
218         {
219             InterruptRegister = 0x80,
220             InputRegister = 0x84,
221             OutputRegister = 0x88,
222             ConfigurationRegister = 0x8c,
223             ConfigurationRegisterByte0 = 0x90,
224             ConfigurationRegisterByte1 = 0x94,
225             ConfigurationRegisterByte2 = 0x98,
226             ConfigurationRegisterByte3 = 0x9c,
227             ClearRegister = 0xa0,
228             SetRegister = 0xa4
229         }
230     }
231 }
232