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 8 using System; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Logging; 12 using Antmicro.Renode.Core.Structure.Registers; 13 using Antmicro.Renode.Peripherals.Timers; 14 using Antmicro.Renode.Time; 15 16 namespace Antmicro.Renode.Peripherals.Miscellaneous 17 { 18 public class OpenTitan_SystemResetControl : BasicDoubleWordPeripheral, IKnownSize, IGPIOReceiver 19 { OpenTitan_SystemResetControl(IMachine machine, OpenTitan_ResetManager resetManager)20 public OpenTitan_SystemResetControl(IMachine machine, OpenTitan_ResetManager resetManager) : base(machine) 21 { 22 DefineRegisters(); 23 IRQ = new GPIO(); 24 FatalFault = new GPIO(); 25 this.resetManager = resetManager; 26 combosDefinition = new ComboDefinition[NumberOfCombos]; 27 durationTimer = new DurationTimer(machine.ClockSource, DurationTimerFrequency, this, "comboDurationTimer"); 28 Reset(); 29 } 30 Reset()31 public override void Reset() 32 { 33 durationTimer.Cancel(); 34 base.Reset(); 35 // IRQ.Unset is not necessary here as it should happen after registers are set to reset values 36 FatalFault.Unset(); 37 } 38 OnGPIO(int number, bool value)39 public void OnGPIO(int number, bool value) 40 { 41 if(number == 0) 42 { 43 powerButton = value; 44 } 45 durationTimer.Cancel(); 46 CheckCombos(); 47 } 48 49 public long Size => 0x1000; 50 public GPIO IRQ { get; } 51 public GPIO FatalFault { get; } 52 DefineRegisters()53 private void DefineRegisters() 54 { 55 Registers.InterruptState.Define(this) 56 .WithFlag(0, out interruptState, FieldMode.Read | FieldMode.WriteOneToClear, name: "sysrst_ctrl") 57 .WithReservedBits(1, 31) 58 .WithWriteCallback((_, __) => { UpdateInterrupt(); }); 59 60 Registers.InterruptEnable.Define(this) 61 .WithFlag(0, out interruptEnable, name: "sysrst_ctrl") 62 .WithReservedBits(1, 31) 63 .WithWriteCallback((_, __) => { UpdateInterrupt(); }); 64 65 Registers.InterruptTest.Define(this) 66 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) { interruptState.Value = true; } }, name: "sysrst_ctrl") 67 .WithReservedBits(1, 31) 68 .WithWriteCallback((_, __) => { UpdateInterrupt(); }); 69 70 Registers.AlertTest.Define(this) 71 .WithFlag(0, FieldMode.Write, writeCallback: (_, val) => { if(val) FatalFault.Blink(); }, name: "fatal_fault") 72 .WithReservedBits(1, 31); 73 74 Registers.ConfigurationWriteEnable.Define(this, 0x1) 75 .WithTaggedFlag("write_en", 0) 76 .WithReservedBits(1, 31); 77 78 Registers.ECResetControl.Define(this, 0x7d0) 79 .WithTag("ec_rst_pulse", 0, 16) 80 .WithReservedBits(16, 16); 81 82 Registers.UltralowpowerACDebounceControl.Define(this, 0x1f40) 83 .WithTag("ulp_ac_debounce_timer", 0, 16) 84 .WithReservedBits(16, 16); 85 86 Registers.UltralowpowerLidDebounceControl.Define(this, 0x1f40) 87 .WithTag("ulp_lid_debounce_timer", 0, 16) 88 .WithReservedBits(16, 16); 89 90 Registers.UltralowpowerPwrDebounceControl.Define(this, 0x1f40) 91 .WithTag("ulp_pwrb_debounce_timer", 0, 16) 92 .WithReservedBits(16, 16); 93 94 Registers.UltralowpowerControl.Define(this) 95 .WithTaggedFlag("ulp_enable", 0) 96 .WithReservedBits(1, 31); 97 98 Registers.UltralowpowerStatus.Define(this) 99 .WithTaggedFlag("ulp_wakeup", 0) 100 .WithReservedBits(1, 31); 101 102 Registers.WakeupStatus.Define(this) 103 .WithTaggedFlag("wakeup_sts", 0) 104 .WithReservedBits(1, 31); 105 106 Registers.KeyInputOutputInvert.Define(this) 107 .WithFlag(0, out key0Invert, name: "key0_in") 108 .WithFlag(1, name: "key0_out") 109 .WithFlag(2, out key1Invert, name: "key1_in") 110 .WithFlag(3, name: "key1_out") 111 .WithFlag(4, out key2Invert, name: "key2_in") 112 .WithFlag(5, name: "key2_out") 113 .WithFlag(6, out pwrButtonInvert, name: "pwrb_in") 114 .WithTaggedFlag("pwrb_out", 7) 115 .WithFlag(8, out acPresentInvert, name: "ac_present") 116 .WithTaggedFlag("bat_disable", 9) 117 .WithTaggedFlag("lid_open", 10) 118 .WithTaggedFlag("z3_wakeup", 11) 119 .WithReservedBits(12, 20) 120 .DefineWriteCallback((prevVal, val) => 121 { 122 if(prevVal != val) 123 { 124 durationTimer.Cancel(); 125 CheckCombos(); 126 } 127 }); 128 129 Registers.PinAllowedControl.Define(this, 0x82) 130 .WithTaggedFlag("bat_disable_0", 0) 131 .WithTaggedFlag("ec_rst_l_0", 1) 132 .WithTaggedFlag("pwrb_out_0", 2) 133 .WithTaggedFlag("key0_out_0", 3) 134 .WithTaggedFlag("key1_out_0", 4) 135 .WithTaggedFlag("key2_out_0", 5) 136 .WithTaggedFlag("z3_wakeup_0", 6) 137 .WithTaggedFlag("flash_wp_l_0", 7) 138 .WithTaggedFlag("bat_disable_1", 8) 139 .WithTaggedFlag("ec_rst_l_1", 9) 140 .WithTaggedFlag("pwrb_out_1", 10) 141 .WithTaggedFlag("key0_out_1", 11) 142 .WithTaggedFlag("key1_out_1", 12) 143 .WithTaggedFlag("key2_out_1", 13) 144 .WithTaggedFlag("z3_wakeup_1", 14) 145 .WithTaggedFlag("flash_wp_l_1", 15) 146 .WithReservedBits(16, 16); 147 148 Registers.PinOutControl.Define(this, 0x82) 149 .WithTaggedFlag("bat_disable", 0) 150 .WithTaggedFlag("ec_rst_l", 1) 151 .WithTaggedFlag("pwrb_out", 2) 152 .WithTaggedFlag("key0_out", 3) 153 .WithTaggedFlag("key1_out", 4) 154 .WithTaggedFlag("key2_out", 5) 155 .WithTaggedFlag("z3_wakeup", 6) 156 .WithTaggedFlag("flash_wp_l", 7) 157 .WithReservedBits(8, 24); 158 159 Registers.PinOverrideValue.Define(this) 160 .WithTaggedFlag("bat_disable", 0) 161 .WithTaggedFlag("ec_rst_l", 1) 162 .WithTaggedFlag("pwrb_out", 2) 163 .WithTaggedFlag("key0_out", 3) 164 .WithTaggedFlag("key1_out", 4) 165 .WithTaggedFlag("key2_out", 5) 166 .WithTaggedFlag("z3_wakeup", 6) 167 .WithTaggedFlag("flash_wp_l", 7) 168 .WithReservedBits(8, 24); 169 170 Registers.PinInValue.Define(this) 171 .WithFlag(0, FieldMode.Read, valueProviderCallback: (_) => powerButton, name: "pwrb_in") 172 .WithTaggedFlag("key0_in", 1) 173 .WithTaggedFlag("key1_in", 2) 174 .WithTaggedFlag("key2_in", 3) 175 .WithTaggedFlag("lid_open", 4) 176 .WithTaggedFlag("ac_present", 5) 177 .WithTaggedFlag("ec_rst_l", 6) 178 .WithTaggedFlag("flash_wp_l", 7) 179 .WithReservedBits(8, 24); 180 181 Registers.KeyInterruptControl.Define(this) 182 .WithTaggedFlag("pwrb_in_H2L", 0) 183 .WithTaggedFlag("key0_in_H2L", 1) 184 .WithTaggedFlag("key1_in_H2L", 2) 185 .WithTaggedFlag("key2_in_H2L", 3) 186 .WithTaggedFlag("ac_present_H2L", 4) 187 .WithTaggedFlag("ec_rst_l_H2L", 5) 188 .WithTaggedFlag("flash_wp_l_H2L", 6) 189 .WithTaggedFlag("pwrb_in_L2H", 8) 190 .WithTaggedFlag("key0_in_L2H", 9) 191 .WithTaggedFlag("key1_in_L2H", 10) 192 .WithTaggedFlag("key2_in_L2H", 11) 193 .WithTaggedFlag("ac_present_L2H", 12) 194 .WithTaggedFlag("ec_rst_l_L2H", 13) 195 .WithTaggedFlag("flash_wp_l_L2H", 14) 196 .WithReservedBits(15, 17); 197 198 Registers.KeyInterruptDebounceControl.Define(this, 0x7d0) 199 .WithTag("debounce_timer", 0, 16) 200 .WithReservedBits(16, 16); 201 202 Registers.AutoBlockDebounceControl.Define(this, 0x7d0) 203 .WithTag("debounce_timer", 0, 16) 204 .WithTaggedFlag("auto_block_enable", 16) 205 .WithReservedBits(17, 15); 206 207 Registers.AutoBlockOut.Define(this) 208 .WithTaggedFlag("key0_out_sel", 0) 209 .WithTaggedFlag("key1_out_sel", 1) 210 .WithTaggedFlag("key2_out_sel", 2) 211 .WithTaggedFlag("key0_out_value", 4) 212 .WithTaggedFlag("key1_out_value", 5) 213 .WithTaggedFlag("key2_out_value", 6) 214 .WithReservedBits(7, 25); 215 216 Registers.ComboSelectControl0.DefineMany(this, NumberOfCombos, (register, idx) => 217 { 218 register 219 .WithEnumField<DoubleWordRegister, InputsState>(0, 5, 220 valueProviderCallback: _ => combosDefinition[idx].Inputs, 221 writeCallback: (_, val) => { combosDefinition[idx].Inputs = (InputsState)val; }, 222 name: $"sel_{idx}") 223 .WithReservedBits(5, 27); 224 }); 225 226 Registers.ComboDurationControl0.DefineMany(this, NumberOfCombos, (register, idx) => 227 { 228 register 229 .WithValueField(0, 32, 230 writeCallback: (_, val) => { combosDefinition[idx].TriggerDurationInCycles = (uint)val; }, 231 valueProviderCallback: (_) => combosDefinition[idx].TriggerDurationInCycles, 232 name: $"detection_timer_{idx}"); 233 }); 234 235 Registers.ComboOutControl0.DefineMany(this, NumberOfCombos, (register, idx) => 236 { 237 register 238 .WithEnumField<DoubleWordRegister, ComboAction>(0, 4, 239 writeCallback: (_, val) => { combosDefinition[idx].Actions = val; }, 240 valueProviderCallback: _ => combosDefinition[idx].Actions, 241 name: $"combo_out_{idx}") 242 .WithReservedBits(4, 28); 243 }); 244 245 Registers.ComboInterruptStatus.Define(this) 246 .WithTaggedFlag("combo0_H2L", 0) 247 .WithTaggedFlag("combo1_H2L", 1) 248 .WithTaggedFlag("combo2_H2L", 2) 249 .WithTaggedFlag("combo3_H2L", 3) 250 .WithReservedBits(4, 28); 251 252 Registers.KeyInterruptStatus.Define(this) 253 .WithTaggedFlag("pwrb_H2L", 0) 254 .WithTaggedFlag("key0_in_H2L", 1) 255 .WithTaggedFlag("key1_in_H2L", 2) 256 .WithTaggedFlag("key2_in_H2L", 3) 257 .WithTaggedFlag("ac_present_H2L", 4) 258 .WithTaggedFlag("ec_rst_l_H2L", 5) 259 .WithTaggedFlag("flash_wp_l_H2L", 6) 260 .WithTaggedFlag("pwrb_L2H", 7) 261 .WithTaggedFlag("key0_in_L2H", 8) 262 .WithTaggedFlag("key1_in_L2H", 9) 263 .WithTaggedFlag("key2_in_L2H", 10) 264 .WithTaggedFlag("ac_present_L2H", 11) 265 .WithTaggedFlag("ec_rst_l_L2H", 12) 266 .WithTaggedFlag("flash_wp_l_L2H", 13) 267 .WithReservedBits(14, 18); 268 } 269 CheckCombos()270 private void CheckCombos() 271 { 272 var currentState = GetCurrentInputsState(); 273 274 foreach(var combo in combosDefinition.Where(x => x.Inputs == currentState)) 275 { 276 switch(combo.Actions) 277 { 278 case ComboAction.None: 279 this.Log(LogLevel.Warning, "No action set on combo, ignoring"); 280 break; 281 case ComboAction.ResetRequest: 282 // Resets all except POR modules as stated in https://github.com/lowRISC/opentitan/issues/12288 283 durationTimer.ExecOnElapsed(ResetRequest, combo.TriggerDurationInCycles); 284 break; 285 default: 286 this.Log(LogLevel.Error, "The {0} action is not implemented. Currently only the reset request is supported"); 287 break; 288 } 289 return; 290 } 291 } 292 ResetRequest()293 private void ResetRequest() 294 { 295 // Resets all except POR modules as stated in https://github.com/lowRISC/opentitan/issues/12288 296 this.resetManager.LifeCycleReset(); 297 } 298 GetCurrentInputsState()299 private InputsState GetCurrentInputsState() 300 { 301 InputsState currentState = InputsState.None; 302 if(powerButton ^ pwrButtonInvert.Value) 303 { 304 currentState |= InputsState.PowerButton; 305 } 306 if(key0Invert.Value) 307 { 308 currentState |= InputsState.Key0; 309 } 310 if(key1Invert.Value) 311 { 312 currentState |= InputsState.Key0; 313 } 314 if(key2Invert.Value) 315 { 316 currentState |= InputsState.Key2; 317 } 318 if(acPresentInvert.Value) 319 { 320 currentState |= InputsState.AcPresent; 321 } 322 323 return currentState; 324 } 325 UpdateInterrupt()326 private void UpdateInterrupt() 327 { 328 var interrupt = interruptEnable.Value && interruptState.Value; 329 IRQ.Set(interrupt); 330 this.DebugLog("IRQ set to {0}", interrupt); 331 } 332 333 private const uint NumberOfCombos = 4; 334 private const int DurationTimerFrequency = 200000; 335 private readonly OpenTitan_ResetManager resetManager; 336 private readonly ComboDefinition[] combosDefinition; 337 private readonly DurationTimer durationTimer; 338 339 private IFlagRegisterField interruptState; 340 private IFlagRegisterField interruptEnable; 341 private IFlagRegisterField key0Invert; 342 private IFlagRegisterField key1Invert; 343 private IFlagRegisterField key2Invert; 344 private IFlagRegisterField acPresentInvert; 345 private IFlagRegisterField pwrButtonInvert; 346 private bool powerButton; 347 348 public enum Registers 349 { 350 InterruptState = 0x0, 351 InterruptEnable = 0x4, 352 InterruptTest = 0x8, 353 AlertTest = 0xc, 354 ConfigurationWriteEnable = 0x10, 355 ECResetControl = 0x14, 356 UltralowpowerACDebounceControl = 0x18, 357 UltralowpowerLidDebounceControl = 0x1c, 358 UltralowpowerPwrDebounceControl = 0x20, 359 UltralowpowerControl = 0x24, 360 UltralowpowerStatus = 0x28, 361 WakeupStatus = 0x2c, 362 KeyInputOutputInvert = 0x30, 363 PinAllowedControl = 0x34, 364 PinOutControl = 0x38, 365 PinOverrideValue = 0x3c, 366 PinInValue = 0x40, 367 KeyInterruptControl = 0x44, 368 KeyInterruptDebounceControl = 0x48, 369 AutoBlockDebounceControl = 0x4c, 370 AutoBlockOut = 0x50, 371 ComboSelectControl0 = 0x54, 372 ComboSelectControl1 = 0x58, 373 ComboSelectControl2 = 0x5C, 374 ComboSelectControl3 = 0x60, 375 ComboDurationControl0 = 0x64, 376 ComboDurationControl1 = 0x68, 377 ComboDurationControl2 = 0x6C, 378 ComboDurationControl3 = 0x70, 379 ComboOutControl0 = 0x74, 380 ComboOutControl1 = 0x78, 381 ComboOutControl2 = 0x7C, 382 ComboOutControl3 = 0x80, 383 ComboInterruptStatus = 0x84, 384 KeyInterruptStatus = 0x88, 385 } 386 387 [Flags] 388 private enum InputsState 389 { 390 None = 0x0, 391 Key0 = 0x1, 392 Key1 = 0x2, 393 Key2 = 0x4, 394 PowerButton = 0x8, 395 AcPresent = 0x10, 396 } 397 398 [Flags] 399 private enum ComboAction 400 { 401 None = 0x0, 402 BatteryDisable = 0x1, 403 Interrupt = 0x2, 404 EmbeddedControllerReset = 0x4, 405 ResetRequest = 0x8, 406 } 407 408 private struct ComboDefinition 409 { ComboDefinitionAntmicro.Renode.Peripherals.Miscellaneous.OpenTitan_SystemResetControl.ComboDefinition410 public ComboDefinition(InputsState inputs, ComboAction action, uint triggerDuration) 411 { 412 this.Inputs = inputs; 413 this.Actions = action; 414 this.TriggerDurationInCycles = triggerDuration; 415 } 416 417 public InputsState Inputs; 418 public ComboAction Actions; 419 public uint TriggerDurationInCycles; 420 } 421 422 private class DurationTimer: LimitTimer 423 { DurationTimer(IClockSource clockSource, long frequency, IPeripheral owner, string name)424 public DurationTimer(IClockSource clockSource, long frequency, IPeripheral owner, string name) 425 : base(clockSource, frequency, owner, name, limit: uint.MaxValue, direction: Direction.Ascending, 426 workMode: WorkMode.OneShot, enabled:false, eventEnabled: true, autoUpdate: true) 427 { 428 // Intentionally left blank 429 } 430 Cancel()431 public void Cancel() 432 { 433 this.ClearSubscriptions(); 434 this.Enabled = false; 435 } 436 ExecOnElapsed(Action action, uint limitInCycles)437 public void ExecOnElapsed(Action action, uint limitInCycles) 438 { 439 // In case we have something already set up - clean up 440 Cancel(); 441 this.Limit = limitInCycles; 442 this.LimitReached += action; 443 this.Value = 0; 444 this.Enabled = true; 445 } 446 } 447 } // End class OpenTitan_SysrstCtrl 448 } 449