1 //
2 // Copyright (c) 2010-2020 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.Collections.Generic;
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Core.Structure.Registers;
11 using Antmicro.Renode.Peripherals.Bus;
12 using Antmicro.Renode.Peripherals.GPIOPort;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Utilities;
15 using System;
16 using System.Linq;
17 using Antmicro.Renode.Debugging;
18 
19 namespace Antmicro.Renode.Peripherals.IRQControllers
20 {
21     /*
22      * This peripherals serves a combined function of being both a
23      * GPIO controller and a generic interrupt controller/router.
24      * For most of the inputs it only forwards the interrupt.
25      */
26     public sealed class EOSS3_IntrCtrl : BaseGPIOPort, IIRQController, INumberedGPIOOutput, IKnownSize, IDoubleWordPeripheral
27     {
EOSS3_IntrCtrl(IMachine machine)28         public EOSS3_IntrCtrl(IMachine machine) : base(machine, NumberOfGPIOs)
29         {
30             gpioManager = new GPIOInterruptManager(GPIOIrq, State);
31             gpioManager.DeassertActiveInterruptTrigger = true;
32 
33             externalIrqConfig = new [] { SRAMIrq, UARTIrq, TimerIrq, WatchdogIrq,
34                 WatchdogResetIrq, BusTimeoutIrq, FPUIrq, PacketFIFOIrq, ReservedI2SIrq, ReservedAudioIrq,
35                 SPIMasterIrq, ConfigDMAIrq, PMUTimerIrq, ADCIrq, RTCIrq, ResetIrq, FFE0Irq, WatchdogFFEIrq,
36                 ApBootIrq, LDO30Irq, LDO50Irq, ReservedSRAMIrq, LPSDIrq, DMicIrq }.Select((x, i) => new InterruptConfig(x, this, i)).ToArray();
37 
38             DebugHelper.Assert(externalIrqConfig.Length == NumberOfOtherInterrupts);
39 
40             // SoftwareIrq2 is connected to nvic0, SoftwareIrq1 is connected to nvic1
41             softwareInterrupt2Config = new InterruptConfig(SoftwareIrq2, this, 0);
42             softwareInterrupt1Config = new InterruptConfig(SoftwareIrq1, this, 1);
43 
44             var gpioReg = new DoubleWordRegister(this);
45             var gpioRawReg = new DoubleWordRegister(this);
46             var gpioTypeReg = new DoubleWordRegister(this);
47             var gpioPolarityReg = new DoubleWordRegister(this);
48             var gpioEnableM4Reg = new DoubleWordRegister(this);
49 
50             for(var j = 0; j < NumberOfGPIOs; j++)
51             {
52                 var i = j;
53                 gpioManager.PinDirection[i] = GPIOInterruptManager.Direction.Input | GPIOInterruptManager.Direction.Output;
54                 gpioReg.DefineFlagField(i, writeCallback: (_, value) => { if(value) { gpioManager.ClearInterrupt(i); }},
55                     valueProviderCallback: _ => gpioManager.ActiveInterrupts.ElementAt(i),
56                     name: $"GPIO_{i}_INTR");
57                 gpioRawReg.DefineFlagField(i, FieldMode.Read, valueProviderCallback: _ => State[i], name: $"GPIO_{i}_INTR_RAW");
58                 gpioTypeReg.DefineFlagField(i, writeCallback: (_, value) => gpioManager.InterruptType[i] = UpdateGPIOSettings(gpioManager.InterruptType[i], value, null),
59                     valueProviderCallback: _ =>
60                         gpioManager.InterruptType[i] == GPIOInterruptManager.InterruptTrigger.RisingEdge
61                         || gpioManager.InterruptType[i] == GPIOInterruptManager.InterruptTrigger.FallingEdge,
62                     name: $"GPIO_{i}_INTR_TYPE");
63                 gpioPolarityReg.DefineFlagField(i, writeCallback: (_, value) => gpioManager.InterruptType[i] = UpdateGPIOSettings(gpioManager.InterruptType[i], null, value),
64                     valueProviderCallback: _ =>
65                         gpioManager.InterruptType[i] == GPIOInterruptManager.InterruptTrigger.RisingEdge
66                         || gpioManager.InterruptType[i] == GPIOInterruptManager.InterruptTrigger.ActiveHigh,
67                     name: $"GPIO_{i}_INTR_POL");
68                 gpioEnableM4Reg.DefineFlagField(i, writeCallback: (_, value) => gpioManager.InterruptEnable[i] = value, valueProviderCallback: _ => gpioManager.InterruptEnable[i], name: $"GPIO_{i}_INTR_EN_M4");
69             }
70 
71             var otherInterruptsReg = new DoubleWordRegister(this);
72             var otherInterruptsEnabledM4Reg = new DoubleWordRegister(this);
73             for(var j = 0; j < NumberOfOtherInterrupts; ++j)
74             {
75                 var i = j;
76                 otherInterruptsReg.DefineFlagField(i, valueProviderCallback: _ => externalIrqConfig[i].Active,
77                     writeCallback: (_, value) => { if(value) { externalIrqConfig[i].Active = false; } }, name: $"OTHER_INTR[{i}]");
78 
79                 externalIrqConfig[i].EnabledField = otherInterruptsEnabledM4Reg.DefineFlagField(i, changeCallback: (_, __) => externalIrqConfig[i].Update(), name: $"OTHER_INTR_EN_M4[{i}]");
80             }
81             var regs = new Dictionary<long, DoubleWordRegister>
82             {
83                 {(long)Registers.GPIOInterrupt, gpioReg},
84                 {(long)Registers.GPIOInterruptRaw, gpioRawReg},
85                 {(long)Registers.GPIOInterruptType, gpioTypeReg},
86                 {(long)Registers.GPIOInterruptPolarity, gpioPolarityReg},
87                 {(long)Registers.GPIOInterruptEnableM4, gpioEnableM4Reg},
88 
89                 {(long)Registers.OtherInterrupts, otherInterruptsReg},
90                 {(long)Registers.OtherInterruptsEnableM4, otherInterruptsEnabledM4Reg},
91 
92                 {(long)Registers.SoftwareInterrupt1, new DoubleWordRegister(this)
93                     .WithFlag(0, writeCallback: (_, value) => softwareInterrupt1Config.Active = value,
94                         valueProviderCallback: _ => softwareInterrupt1Config.Active,
95                         name: "SW_INTR_1")},
96                 {(long)Registers.SoftwareInterrupt1EnableM4, new DoubleWordRegister(this)
97                     .WithFlag(0, out var software1Enabled, changeCallback: (_, __) => softwareInterrupt1Config.Update(), name: "SW_INTR_1_EN_M4")
98                 },
99                 {(long)Registers.SoftwareInterrupt2, new DoubleWordRegister(this)
100                     .WithFlag(0, writeCallback: (_, value) => softwareInterrupt2Config.Active = value,
101                         valueProviderCallback: _ => softwareInterrupt2Config.Active,
102                         name: "SW_INTR_2")
103                 },
104                 {(long)Registers.SoftwareInterrupt2EnableM4, new DoubleWordRegister(this)
105                     .WithFlag(0, out var software2Enabled, changeCallback: (_, __) => softwareInterrupt2Config.Update(), name: "SW_INTR_2_EN_M4")
106                 },
107             };
108             softwareInterrupt1Config.EnabledField = software1Enabled;
109             softwareInterrupt2Config.EnabledField = software2Enabled;
110             registers = new DoubleWordRegisterCollection(this, regs);
111 
112             var miscRegs = new Dictionary<long, DoubleWordRegister>
113             {
114                 {(long)MiscRegisters.IOInput, new DoubleWordRegister(this)
115                     .WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(State), name: "IO_IN")
116                     .WithReservedBits(8, 24)
117                 },
118                 {(long)MiscRegisters.IOOutput, new DoubleWordRegister(this)
119                     .WithValueField(0, 8, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(Connections.Select(x => x.Value.IsSet)), writeCallback: (_, value) => UpdateOutputBits((byte)value), name: "IO_OUT")
120                     .WithReservedBits(8, 24)
121                 },
122             };
123             miscRegisters = new DoubleWordRegisterCollection(this, miscRegs);
124         }
125 
OnGPIO(int number, bool value)126         public override void OnGPIO(int number, bool value)
127         {
128             // Input mapping:
129             // 0-5 - reserved
130             // 6-29 - peripheral interrupts, ordered according to the externalIrqConfig collection
131             // 30-37 - GPIOs 0-7
132             this.Log(LogLevel.Noisy, "Received interrupt {0}, value {1}", number, value);
133             //"Other", non-gpio interrupt
134             if(number >= NumberOfOtherInterrupts + FirstExternalInterrupt + NumberOfGPIOs || number < FirstExternalInterrupt)
135             {
136                 this.Log(LogLevel.Error, "Received an out-of-range interrupt/GPIO. Number: {0}, value: {1}. External interrupts must be connected to pins between {2} and {3}, inclusive.",
137                     number, value, FirstExternalInterrupt, NumberOfOtherInterrupts + FirstExternalInterrupt + NumberOfGPIOs);
138                 return;
139             }
140             if(number >= NumberOfOtherInterrupts + FirstExternalInterrupt)
141             {
142                 base.OnGPIO(number - NumberOfOtherInterrupts - FirstExternalInterrupt, value);
143                 gpioManager.RefreshInterrupts();
144                 return;
145             }
146             var config = externalIrqConfig[number - FirstExternalInterrupt];
147             config.Detected = value;
148         }
149 
Reset()150         public override void Reset()
151         {
152             base.Reset();
153             registers.Reset();
154             miscRegisters.Reset();
155             foreach(var interruptConfig in externalIrqConfig)
156             {
157                 interruptConfig.Reset();
158             }
159             softwareInterrupt1Config.Reset();
160             softwareInterrupt2Config.Reset();
161             gpioManager.Reset();
162         }
163 
ReadDoubleWord(long offset)164         public uint ReadDoubleWord(long offset)
165         {
166             return registers.Read(offset);
167         }
168 
WriteDoubleWord(long offset, uint value)169         public void WriteDoubleWord(long offset, uint value)
170         {
171             registers.Write(offset, value);
172         }
173 
174         [ConnectionRegionAttribute("misc")]
ReadDoubleWordFromMisc(long offset)175         public uint ReadDoubleWordFromMisc(long offset)
176         {
177             return miscRegisters.Read(offset);
178         }
179 
180         [ConnectionRegionAttribute("misc")]
WriteDoubleWordToMisc(long offset, uint value)181         public void WriteDoubleWordToMisc(long offset, uint value)
182         {
183             miscRegisters.Write(offset, value);
184         }
185 
186         [ConnectionRegionAttribute("iomux")]
ReadDoubleWordFromIOMux(long offset)187         public uint ReadDoubleWordFromIOMux(long offset)
188         {
189             this.Log(LogLevel.Warning, "Read from unsupported iomux, offset 0x{0:X}", offset);
190             return 0;
191         }
192 
193         [ConnectionRegionAttribute("iomux")]
WriteDoubleWordToIOMux(long offset, uint value)194         public void WriteDoubleWordToIOMux(long offset, uint value)
195         {
196             this.Log(LogLevel.Warning, "Write to unsupported iomux, offset 0x{0:X} value 0x{1:X}", offset, value);
197         }
198 
199         public long Size => 0x400;
200 
201         public GPIO SoftwareIrq2 { get; } = new GPIO();
202         public GPIO SoftwareIrq1 { get; } = new GPIO();
203         public GPIO FFE0MessageIrq { get; } = new GPIO();
204         public GPIO FabricIrq { get; } = new GPIO();
205         public GPIO GPIOIrq { get; } = new GPIO();
206         public GPIO SRAMIrq { get; } = new GPIO();
207         public GPIO UARTIrq { get; } = new GPIO();
208         public GPIO TimerIrq { get; } = new GPIO();
209         public GPIO WatchdogIrq { get; } = new GPIO();
210         public GPIO WatchdogResetIrq { get; } = new GPIO();
211         public GPIO BusTimeoutIrq { get; } = new GPIO();
212         public GPIO FPUIrq { get; } = new GPIO();
213         public GPIO PacketFIFOIrq { get; } = new GPIO();
214         public GPIO ReservedI2SIrq { get; } = new GPIO();
215         public GPIO ReservedAudioIrq { get; } = new GPIO();
216         public GPIO SPIMasterIrq { get; } = new GPIO();
217         public GPIO ConfigDMAIrq { get; } = new GPIO();
218         public GPIO PMUTimerIrq { get; } = new GPIO();
219         public GPIO ADCIrq { get; } = new GPIO();
220         public GPIO RTCIrq { get; } = new GPIO();
221         public GPIO ResetIrq { get; } = new GPIO();
222         public GPIO FFE0Irq { get; } = new GPIO();
223         public GPIO WatchdogFFEIrq { get; } = new GPIO();
224         public GPIO ApBootIrq { get; } = new GPIO();
225         public GPIO LDO30Irq { get; } = new GPIO();
226         public GPIO LDO50Irq { get; } = new GPIO();
227         public GPIO ReservedSRAMIrq { get; } = new GPIO();
228         public GPIO LPSDIrq { get; } = new GPIO();
229         public GPIO DMicIrq { get; } = new GPIO();
230 
UpdateOutputBits(byte value)231         private void UpdateOutputBits(byte value)
232         {
233             for(byte i = 0; i < NumberOfGPIOs; ++i)
234             {
235                 Connections[i].Set(BitHelper.IsBitSet(value, i));
236             }
237         }
238 
UpdateGPIOSettings(GPIOInterruptManager.InterruptTrigger oldTrigger, bool? type, bool? polarity)239         private GPIOInterruptManager.InterruptTrigger UpdateGPIOSettings(GPIOInterruptManager.InterruptTrigger oldTrigger, bool? type, bool? polarity)
240         {
241             if(!(type.HasValue ^ polarity.HasValue))
242             {
243                 throw new ArgumentException($"Either {nameof(type)} or {nameof(polarity)} must be null. The other must not be null.");
244             }
245             var isEdge = oldTrigger != GPIOInterruptManager.InterruptTrigger.ActiveHigh && oldTrigger != GPIOInterruptManager.InterruptTrigger.ActiveLow;
246             var isHigh = oldTrigger != GPIOInterruptManager.InterruptTrigger.ActiveLow && oldTrigger != GPIOInterruptManager.InterruptTrigger.FallingEdge; //both edges is not an option in this controller
247 
248             if(type.HasValue)
249             {
250                 if(isHigh)
251                 {
252                     return type.Value ? GPIOInterruptManager.InterruptTrigger.RisingEdge : GPIOInterruptManager.InterruptTrigger.ActiveHigh;
253                 }
254                 else
255                 {
256                     return type.Value ? GPIOInterruptManager.InterruptTrigger.FallingEdge : GPIOInterruptManager.InterruptTrigger.ActiveLow;
257                 }
258             }
259             else //polarity has value
260             {
261                 if(isEdge)
262                 {
263                     return polarity.Value ? GPIOInterruptManager.InterruptTrigger.RisingEdge : GPIOInterruptManager.InterruptTrigger.FallingEdge;
264                 }
265                 else
266                 {
267                     return polarity.Value ? GPIOInterruptManager.InterruptTrigger.ActiveHigh : GPIOInterruptManager.InterruptTrigger.ActiveLow;
268                 }
269             }
270         }
271 
272         private readonly InterruptConfig[] externalIrqConfig;
273         private readonly InterruptConfig softwareInterrupt1Config;
274         private readonly InterruptConfig softwareInterrupt2Config;
275         private readonly DoubleWordRegisterCollection registers;
276         private readonly DoubleWordRegisterCollection miscRegisters;
277         private readonly GPIOInterruptManager gpioManager;
278 
279         private const int NumberOfGPIOs = 8;
280         private const int NumberOfOtherInterrupts = 24;
281         private const int FirstExternalInterrupt = 6;
282 
283         private class InterruptConfig
284         {
InterruptConfig(GPIO irq, EOSS3_IntrCtrl parent, int number)285             public InterruptConfig(GPIO irq, EOSS3_IntrCtrl parent, int number)
286             {
287                 Interrupt = irq;
288                 this.parent = parent;
289                 this.number = number;
290             }
291 
Update()292             public void Update()
293             {
294                 Interrupt.Set(Active && EnabledField.Value);
295                 parent.Log(LogLevel.Noisy, "Interrupt {3}: active {0}, enabled {1}, interrupt set to {2}", Active, EnabledField.Value, Interrupt.IsSet, number);
296             }
297 
Reset()298             public void Reset()
299             {
300                 active = false;
301                 detected = false;
302                 Interrupt.Unset();
303             }
304 
305             public GPIO Interrupt { get; }
306 
307             public bool Detected
308             {
309                 get => detected;
310                 set
311                 {
312                     parent.Log(LogLevel.Noisy, "Interrupt {1}: setting detected to {0}", value, number);
313                     detected = value;
314                     if(value)
315                     {
316                         Active = true;
317                     }
318                 }
319             }
320 
321             public bool Active
322             {
323                 get => active;
324                 set
325                 {
326                     parent.Log(LogLevel.Noisy, "Interrupt {1}: setting active to {0}", value, number);
327                     if(!value && !Detected)
328                     {
329                         active = false;
330                     }
331                     else
332                     {
333                         active = true;
334                     }
335                     Update();
336                 }
337             }
338 
339             public IFlagRegisterField EnabledField
340             {
341                 private get; set;
342             }
343             private bool detected;
344             private bool active;
345             private readonly EOSS3_IntrCtrl parent;
346             private readonly int number;
347         }
348 
349         private enum Registers
350         {
351             GPIOInterrupt = 0x0,
352             GPIOInterruptRaw = 0x4,
353             GPIOInterruptType = 0x8,
354             GPIOInterruptPolarity = 0xC,
355             GPIOInterruptEnableAP = 0x10,
356             GPIOInterruptEnableM4 = 0x14,
357             GPIOInterruptEnableFFE0 = 0x18,
358             GPIOInterruptEnableFFE1 = 0x1C,
359             /* 0x20-0x2c not defined */
360             OtherInterrupts = 0x30,
361             OtherInterruptsEnableAP = 0x34,
362             OtherInterruptsEnableM4 = 0x38,
363             /* 0x3c not defined */
364             SoftwareInterrupt1 = 0x40,
365             SoftwareInterrupt1EnableAP = 0x44,
366             SoftwareInterrupt1EnableM4 = 0x48,
367             /* 0x4c not defined */
368             SoftwareInterrupt2 = 0x50,
369             SoftwareInterrupt2EnableAP = 0x54,
370             SoftwareInterrupt2EnableM4 = 0x58,
371             /* 0x5c not defined */
372             FFEInterrupt = 0x60,
373             FFEInterruptEnableAP = 0x64,
374             FFEInterruptEnableM4 = 0x68,
375             /* 0x6c-0x7c not defined */
376             FabricInterrupt = 0x80,
377             FabricInterruptRaw = 0x84,
378             FabricInterruptType = 0x88,
379             FabricInterruptPolarity = 0x8C,
380             FabricInterruptEnableAP = 0x90,
381             FabricInterruptEnableM4 = 0x94,
382             /* 0x94-0x9C not defined */
383             M4MemoryAlwaysOnInterrupt = 0xA0,
384             M4MemoryAlwaysOnInterruptEnable = 0xA4,
385         }
386 
387         private enum MiscRegisters
388         {
389             IOInput = 0,
390             IOOutput = 4,
391         }
392     }
393 }
394