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