1 //
2 // Copyright (c) 2010-2021 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.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Utilities;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Core.Structure.Registers;
12 using Antmicro.Renode.Logging;
13 
14 namespace Antmicro.Renode.Peripherals.GPIOPort
15 {
16     public class IMXRT_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize
17     {
IMXRT_GPIO(IMachine machine)18         public IMXRT_GPIO(IMachine machine) : base(machine, NumberOfPins)
19         {
20             locker = new object();
21             IRQ = new GPIO();
22             registers = new DoubleWordRegisterCollection(this, BuildRegisterMap());
23             data = new bool[NumberOfPins];
24             directionOutNotIn = new bool[NumberOfPins];
25             interruptEnabled = new bool[NumberOfPins];
26             interruptRequest = new bool[NumberOfPins];
27             edgeSelect = new bool[NumberOfPins];
28             interruptConfig = new InterruptConfig[NumberOfPins];
29         }
30 
Reset()31         public override void Reset()
32         {
33             lock(locker)
34             {
35                 base.Reset();
36                 IRQ.Unset();
37                 registers.Reset();
38                 for(var i = 0; i < NumberOfPins; ++i)
39                 {
40                     data[i] = false;
41                     directionOutNotIn[i] = false;
42                     interruptEnabled[i] = false;
43                     interruptRequest[i] = false;
44                     edgeSelect[i] = false;
45                     interruptConfig[i] = InterruptConfig.Low;
46                 }
47             }
48         }
49 
ReadDoubleWord(long offset)50         public uint ReadDoubleWord(long offset)
51         {
52             lock(locker)
53             {
54                 return registers.Read(offset);
55             }
56         }
57 
WriteDoubleWord(long offset, uint value)58         public void WriteDoubleWord(long offset, uint value)
59         {
60             lock(locker)
61             {
62                 registers.Write(offset, value);
63             }
64         }
65 
OnGPIO(int number, bool value)66         public override void OnGPIO(int number, bool value)
67         {
68             if(!CheckPinNumber(number))
69             {
70                 return;
71             }
72 
73             if(directionOutNotIn[number])
74             {
75                 this.Log(LogLevel.Warning, "gpio {0} is set to output, signal ignored.", number);
76                 return;
77             }
78 
79             lock(locker)
80             {
81                 var previousState = State[number];
82                 base.OnGPIO(number, value);
83 
84                 UpdateSingleInterruptRequest(number, value, previousState != value);
85                 UpdateIRQ();
86             }
87         }
88 
89         public long Size => 0x90;
90 
91         public GPIO IRQ { get; }
92 
BuildRegisterMap()93         private Dictionary<long, DoubleWordRegister> BuildRegisterMap()
94         {
95             var registersDictionary = new Dictionary<long, DoubleWordRegister>
96             {
97                 {(long)Registers.Data, new DoubleWordRegister(this)
98                     .WithFlags(0, NumberOfPins, name: "GR / GPIO data register",
99                         writeCallback: (id, _, val) => { data[id] = val; },
100                         valueProviderCallback: (id, _) =>
101                         {
102                             return (directionOutNotIn[id])
103                                 ? data[id]
104                                 : State[id];
105                         })
106                     .WithWriteCallback((_, __) => UpdateConnections())
107                 },
108                 {(long)Registers.Direction, new DoubleWordRegister(this)
109                     .WithFlags(0, NumberOfPins, name: "GDIR / GPIO direction register",
110                         writeCallback: (id, _, val) => { directionOutNotIn[id] = val; },
111                         valueProviderCallback: (id, _) => directionOutNotIn[id])
112                     .WithWriteCallback((_, __) => UpdateConnections())
113                 },
114                 {(long)Registers.PadStatus, new DoubleWordRegister(this)
115                     .WithFlags(0, NumberOfPins, FieldMode.Read, name: "PSR / GPIO pad status register",
116                         valueProviderCallback: (id, _) => State[id])
117                 },
118                 {(long)Registers.Mask, new DoubleWordRegister(this)
119                     .WithFlags(0, NumberOfPins, name: "IMR / GPIO interrupt mask register",
120                         writeCallback: (id, _, val) => { interruptEnabled[id] = val; },
121                         valueProviderCallback: (id, _) => interruptEnabled[id])
122                     .WithWriteCallback((_, __) => UpdateIRQ())
123                 },
124                 {(long)Registers.Status, new DoubleWordRegister(this)
125                     .WithFlags(0, NumberOfPins, FieldMode.Read | FieldMode.WriteOneToClear, name: "ISR / GPIO interrupt status register",
126                         writeCallback: (id, _, val) =>
127                         {
128                             if(val)
129                             {
130                                 interruptRequest[id] = false;
131                             }
132                         },
133                         valueProviderCallback: (id, _) => interruptRequest[id])
134                     .WithWriteCallback((_, __) => UpdateIRQ())
135                 },
136                 {(long)Registers.EdgeSelect, new DoubleWordRegister(this)
137                     .WithFlags(0, NumberOfPins, name: "EDGE_SEL / GPIO edge select register",
138                         writeCallback: (id, _, val) => { edgeSelect[id] = val; },
139                         valueProviderCallback: (id, _) => edgeSelect[id])
140                 },
141                 {(long)Registers.DataSet, new DoubleWordRegister(this)
142                     .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_SET / GPIO data register SET",
143                         writeCallback: (id, _, __)  => { data[id] = true; })
144                     .WithWriteCallback((_, __) => UpdateConnections())
145                 },
146                 {(long)Registers.DataClear, new DoubleWordRegister(this)
147                     .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_CLEAR / GPIO data register CLEAR",
148                         writeCallback: (id, _, __)  => { data[id] = false; })
149                     .WithWriteCallback((_, __) => UpdateConnections())
150                 },
151                 {(long)Registers.DataToggle, new DoubleWordRegister(this)
152                     .WithFlags(0, NumberOfPins, FieldMode.Write, name: "DR_TOGGLE / GPIO data register TOGGLE",
153                         writeCallback: (id, _, __)  => { data[id] ^= true; })
154                     .WithWriteCallback((_, __) => UpdateConnections())
155                 },
156             };
157 
158             var config1 = new DoubleWordRegister(this);
159             var config2 = new DoubleWordRegister(this);
160             var half = NumberOfPins / 2;
161             for(var i = 0; i < half; ++i)
162             {
163                 var j = i;
164                 config1.WithEnumField<DoubleWordRegister, InterruptConfig>(j * 2, 2,
165                     name: $"ICR{j} / Interrupt configuration {j}",
166                     writeCallback: (_, val) => { interruptConfig[j] = val; },
167                     valueProviderCallback: _ => interruptConfig[j]);
168                 config2.WithEnumField<DoubleWordRegister, InterruptConfig>(j * 2, 2,
169                     name: $"ICR{half + j} / Interrupt configuration {half + j}",
170                     writeCallback: (_, val) => { interruptConfig[half + j] = val; },
171                     valueProviderCallback: _ => interruptConfig[half + j]);
172             }
173             config1.WithWriteCallback((_, __) => UpdateAllInterruptRequests());
174             config2.WithWriteCallback((_, __) => UpdateAllInterruptRequests());
175             registersDictionary.Add((long)Registers.Config1, config1);
176             registersDictionary.Add((long)Registers.Config2, config2);
177             return registersDictionary;
178         }
179 
UpdateIRQ()180         private void UpdateIRQ()
181         {
182             var flag = false;
183             for(var i = 0; i < NumberOfPins; ++i)
184             {
185                 flag |= interruptEnabled[i] && interruptRequest[i];
186             }
187             IRQ.Set(flag);
188         }
189 
UpdateConnections()190         private void UpdateConnections()
191         {
192             for(var i = 0; i < NumberOfPins; ++i)
193             {
194                 Connections[i].Set(directionOutNotIn[i] && data[i]);
195             }
196             UpdateIRQ();
197         }
198 
UpdateAllInterruptRequests()199         private void UpdateAllInterruptRequests()
200         {
201             for(var i = 0; i < NumberOfPins; ++i)
202             {
203                 UpdateSingleInterruptRequest(i, State[i]);
204             }
205             UpdateIRQ();
206         }
207 
UpdateSingleInterruptRequest(int i, bool currentState, bool stateChanged = false)208         private void UpdateSingleInterruptRequest(int i, bool currentState, bool stateChanged = false)
209         {
210             if(edgeSelect[i])
211             {
212                 interruptRequest[i] |= stateChanged;
213             }
214             else
215             {
216                 switch(interruptConfig[i])
217                 {
218                     case InterruptConfig.Low:
219                         interruptRequest[i] |= !currentState;
220                         break;
221                     case InterruptConfig.High:
222                         interruptRequest[i] |= currentState;
223                         break;
224                     case InterruptConfig.Rising:
225                         interruptRequest[i] |= stateChanged && currentState;
226                         break;
227                     case InterruptConfig.Falling:
228                         interruptRequest[i] |= stateChanged && !currentState;
229                         break;
230                     default:
231                         this.Log(LogLevel.Error, "Invalid state (interruptConfig[{0}]: 0x{1:X}).", i, interruptConfig[i]);
232                         break;
233                 }
234             }
235         }
236 
237         private readonly DoubleWordRegisterCollection registers;
238         private readonly object locker;
239         private readonly bool[] data;
240         private readonly bool[] directionOutNotIn;
241         private readonly bool[] interruptEnabled;
242         private readonly bool[] interruptRequest;
243         private readonly bool[] edgeSelect;
244         private readonly InterruptConfig[] interruptConfig;
245 
246         private const int NumberOfPins = 32;
247 
248         private enum InterruptConfig
249         {
250             Low = 0b00,
251             High = 0b01,
252             Rising = 0b10,
253             Falling = 0b11,
254         }
255 
256         private enum Registers : long
257         {
258             Data = 0x0,
259             Direction = 0x4,
260             PadStatus = 0x8,
261             Config1 = 0xc,
262             Config2 = 0x10,
263             Mask = 0x14,
264             Status = 0x18,
265             EdgeSelect = 0x1c,
266             DataSet = 0x84,
267             DataClear = 0x88,
268             DataToggle = 0x8c,
269         }
270     }
271 }
272