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