1 //
2 // Copyright (c) 2010-2024 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;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Time;
11 using Antmicro.Renode.Logging;
12 
13 namespace Antmicro.Renode.Peripherals.Timers
14 {
15     public class S32K3XX_RealTimeClock : BasicDoubleWordPeripheral, IKnownSize
16     {
S32K3XX_RealTimeClock(IMachine machine, long externalFastCrystalOscillatorFrequency = 2097152, long externalSlowCrystalOscillatorFrequency = 32768)17         public S32K3XX_RealTimeClock(IMachine machine, long externalFastCrystalOscillatorFrequency = 2097152, long externalSlowCrystalOscillatorFrequency = 32768) : base(machine)
18         {
19             this.externalFastCrystalOscillatorFrequency = externalFastCrystalOscillatorFrequency;
20             this.externalSlowCrystalOscillatorFrequency = externalSlowCrystalOscillatorFrequency;
21 
22             IRQ = new GPIO();
23 
24             DefineRegisters();
25 
26             internalClock = new InternalClock(machine.ClockSource, this, GetClockFrequency());
27             internalClock.OnOverflowInterrupt += () =>
28             {
29                 rolloverInterruptPending.Value = true;
30                 UpdateInterrupts();
31             };
32             internalClock.OnAPIInterrupt += () =>
33             {
34                 apiInterruptPending.Value = true;
35                 UpdateInterrupts();
36             };
37             internalClock.OnRTCInterrupt += () =>
38             {
39                 rtcInterruptPending.Value = true;
40                 UpdateInterrupts();
41             };
42         }
43 
UpdateInterrupts()44         private void UpdateInterrupts()
45         {
46             var interrupt = false;
47 
48             interrupt |= rtcInterruptEnabled.Value && rtcInterruptPending.Value;
49             interrupt |= apiInterruptEnabled.Value && apiInterruptPending.Value;
50             interrupt |= rolloverInterruptEnabled.Value && rolloverInterruptPending.Value;
51 
52             IRQ.Set(interrupt);
53         }
54 
Reset()55         public override void Reset()
56         {
57             base.Reset();
58             IRQ.Unset();
59 
60             clockDividerFlags = ClockDivider.None;
61         }
62 
63         public long Size => 0x4000;
64         public GPIO IRQ { get; }
65 
SetClockDivider(bool state, ClockDivider divideBy)66         private void SetClockDivider(bool state, ClockDivider divideBy)
67         {
68             if(internalClock.Enabled)
69             {
70                 this.Log(LogLevel.Warning, "Trying to change clock divider when counter is enabled. Operation ignored");
71                 return;
72             }
73 
74             if(state)
75             {
76                 clockDividerFlags |= divideBy;
77             }
78             else
79             {
80                 clockDividerFlags &= ~divideBy;
81             }
82         }
83 
GetClockFrequency()84         private long GetClockFrequency()
85         {
86             long clockFrequency;
87             switch(clockSource.Value)
88             {
89                 case ClockSource.SXOSC:
90                     clockFrequency = this.externalSlowCrystalOscillatorFrequency;
91                     break;
92                 case ClockSource.SIRC:
93                     clockFrequency = 32000; // 32kHz
94                     break;
95                 case ClockSource.FIRC:
96                     clockFrequency = 48000000; // 48MHz
97                     break;
98                 case ClockSource.FXOSC:
99                     clockFrequency = this.externalFastCrystalOscillatorFrequency;
100                     break;
101                 default:
102                     throw new Exception("unreachable code");
103             }
104 
105             if(clockDividerFlags.HasFlag(ClockDivider.DivideBy32))
106             {
107                 clockFrequency >>= 5;
108             }
109 
110             if(clockDividerFlags.HasFlag(ClockDivider.DivideBy512))
111             {
112                 clockFrequency >>= 9;
113             }
114 
115             return clockFrequency;
116         }
117 
DefineRegisters()118         private void DefineRegisters()
119         {
120             Registers.RTCSupervisorControl.Define(this, 0x80000000)
121                 .WithReservedBits(0, 31)
122                 .WithTaggedFlag("RTCSupervisorBit", 31)
123             ;
124 
125             Registers.RTCControl.Define(this)
126                 .WithTaggedFlag("TriggerEnableForAnalogComparator", 0)
127                 .WithReservedBits(1, 9)
128                 .WithFlag(10, name: "DivideBy32enable",
129                     valueProviderCallback: _ => clockDividerFlags.HasFlag(ClockDivider.DivideBy32),
130                     changeCallback: (_, value) => SetClockDivider(value, ClockDivider.DivideBy32))
131                 .WithFlag(11, name: "DivideBy512enable",
132                     valueProviderCallback: _ => clockDividerFlags.HasFlag(ClockDivider.DivideBy512),
133                     changeCallback: (_, value) => SetClockDivider(value, ClockDivider.DivideBy512))
134                 .WithEnumField(12, 2, out clockSource, name: "ClockSelect")
135                 // NOTE: This flag enables writes to APIInterruptFlag field in RTCStatus
136                 .WithFlag(14, name: "APIInterruptEnable",
137                     valueProviderCallback: _ => internalClock.APIInterruptEnabled,
138                     changeCallback: (_, value) => internalClock.APIInterruptEnabled = value)
139                 // NOTE: This flag 'connects' APIInterruptFlag to IRQ output
140                 .WithFlag(15, out apiInterruptEnabled, name: "AutonomousPeriodicInterruptEnable")
141                 .WithReservedBits(16, 12)
142                 .WithFlag(28, out rolloverInterruptEnabled, name: "CounterRollOverInterruptEnable")
143                 .WithTaggedFlag("FreezeEnableBit", 29)
144                 .WithFlag(30, out rtcInterruptEnabled, name: "RTCInterruptEnable")
145                 .WithFlag(31, name: "CounterEnable",
146                     valueProviderCallback: _ => internalClock.Enabled,
147                     changeCallback: (_, value) => internalClock.Enabled = value)
148                 .WithChangeCallback((_, __) =>
149                 {
150                     UpdateInterrupts();
151                     internalClock.Frequency = GetClockFrequency();
152                 })
153             ;
154 
155             Registers.RTCStatus.Define(this)
156                 .WithReservedBits(0, 9)
157                 .WithFlag(10, out rolloverInterruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "CounterRollOverInterruptFlag")
158                 .WithReservedBits(11, 2)
159                 .WithFlag(13, out apiInterruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "APIInterruptFlag")
160                 .WithReservedBits(14, 2)
161                 .WithReservedBits(16, 1)
162                 .WithTaggedFlag("InvalidAPIVALWrite", 17)
163                 .WithTaggedFlag("InvalidRTCWrite", 18)
164                 .WithReservedBits(19, 10)
165                 .WithFlag(29, out rtcInterruptPending, FieldMode.WriteOneToClear | FieldMode.Read, name: "RTCInterruptFlag")
166                 .WithReservedBits(30, 2)
167                 .WithChangeCallback((_, __) => UpdateInterrupts())
168             ;
169 
170             Registers.RTCCounter.Define(this)
171                 .WithValueField(0, 32, FieldMode.Read, name: "RTCCounterValue",
172                     valueProviderCallback: _ => internalClock.Value)
173             ;
174 
175             Registers.APICompareValue.Define(this)
176                 .WithValueField(0, 32, name: "APICompareValue",
177                     valueProviderCallback: _ => internalClock.APICompareValue,
178                     writeCallback: (_, value) => internalClock.APICompareValue = value)
179             ;
180 
181             Registers.RTCCompareValue.Define(this)
182                 .WithValueField(0, 32, name: "RTCCompareValue",
183                     valueProviderCallback: _ => internalClock.RTCCompareValue,
184                     writeCallback: (_, value) => internalClock.RTCCompareValue = value)
185             ;
186         }
187 
188         private readonly InternalClock internalClock;
189         private readonly long externalSlowCrystalOscillatorFrequency;
190         private readonly long externalFastCrystalOscillatorFrequency;
191 
192         private ClockDivider clockDividerFlags;
193 
194         private IEnumRegisterField<ClockSource> clockSource;
195 
196         private IFlagRegisterField rtcInterruptEnabled;
197         private IFlagRegisterField apiInterruptEnabled;
198         private IFlagRegisterField rolloverInterruptEnabled;
199 
200         private IFlagRegisterField rtcInterruptPending;
201         private IFlagRegisterField apiInterruptPending;
202         private IFlagRegisterField rolloverInterruptPending;
203 
204         private class InternalClock
205         {
InternalClock(IClockSource clockSource, IPeripheral parent, long frequency)206             public InternalClock(IClockSource clockSource, IPeripheral parent, long frequency)
207             {
208                 mainClock = new LimitTimer(clockSource, frequency, parent, "main_clk", limit: uint.MaxValue, direction: Direction.Ascending, eventEnabled: true);
209                 mainClock.LimitReached += HandleOverflow;
210 
211                 apiInterruptClock = new LimitTimer(clockSource, frequency, parent, "api_int_clk", limit: uint.MaxValue, direction: Direction.Ascending, eventEnabled: false);
212                 apiInterruptClock.LimitReached += OnAPIInterrupt;
213 
214                 rtcInterruptClock = new LimitTimer(clockSource, frequency, parent, "rtc_int_clk", limit: uint.MaxValue, direction: Direction.Ascending, eventEnabled: false);
215                 rtcInterruptClock.LimitReached += OnRTCInterrupt;
216             }
217 
HandleOverflow()218             private void HandleOverflow()
219             {
220                 if(APIInterruptEnabled)
221                 {
222                     apiInterruptClock.Value = 0;
223                     apiInterruptClock.Enabled = true;
224                 }
225 
226                 if(RTCInterruptEnabled)
227                 {
228                     rtcInterruptClock.Value = 0;
229                     rtcInterruptClock.Enabled = true;
230                 }
231 
232                 OnOverflowInterrupt?.Invoke();
233             }
234 
235             public ulong Value => mainClock.Value;
236 
237             public long Frequency
238             {
239                 get => mainClock.Frequency;
240                 set
241                 {
242                     if(value == mainClock.Frequency)
243                     {
244                         return;
245                     }
246 
247                     mainClock.Frequency = value;
248                     apiInterruptClock.Frequency = value;
249                     rtcInterruptClock.Frequency = value;
250                 }
251             }
252 
253             public bool AutonomusPeriodicInterruptEnable
254             {
255                 get => apiInterruptClock.Enabled;
256             }
257 
258             public ulong APICompareValue
259             {
260                 get => apiInterruptClock.Limit;
261                 set => apiInterruptClock.Limit = value;
262             }
263 
264             public ulong RTCCompareValue
265             {
266                 get => rtcInterruptClock.Limit;
267                 set => rtcInterruptClock.Limit = value;
268             }
269 
270             public bool Enabled
271             {
272                 get => mainClock.Enabled;
273                 set
274                 {
275                     mainClock.Enabled = value;
276                     if(!value)
277                     {
278                         apiInterruptClock.Enabled = false;
279                         rtcInterruptClock.Enabled = false;
280                     }
281                 }
282             }
283 
284             public bool APIInterruptEnabled
285             {
286                 get => apiInterruptClock.EventEnabled;
287                 set
288                 {
289                     apiInterruptClock.EventEnabled = value;
290                     if(value && APICompareValue > Value)
291                     {
292                         apiInterruptClock.Value = Value;
293                         apiInterruptClock.Enabled = true;
294                     }
295                 }
296             }
297 
298             public bool RTCInterruptEnabled
299             {
300                 get => rtcInterruptClock.EventEnabled;
301                 set
302                 {
303                     rtcInterruptClock.EventEnabled = value;
304                     if(value && RTCCompareValue > Value)
305                     {
306                         rtcInterruptClock.Value = Value;
307                         rtcInterruptClock.Enabled = true;
308                     }
309                 }
310             }
311 
312             public event Action OnOverflowInterrupt;
313             public event Action OnRTCInterrupt;
314             public event Action OnAPIInterrupt;
315 
316             private readonly LimitTimer mainClock;
317             private readonly LimitTimer apiInterruptClock;
318             private readonly LimitTimer rtcInterruptClock;
319         }
320 
321         [Flags]
322         private enum ClockDivider
323         {
324             None = (0 << 0),
325             DivideBy32 = (1 << 0),
326             DivideBy512 = (1 << 1),
327         }
328 
329         private enum ClockSource
330         {
331             SXOSC, // NOTE: Not available on: S32K311; instead SIRC will be used
332             SIRC,
333             FIRC,
334             FXOSC,
335         }
336 
337         private enum Registers
338         {
339             RTCSupervisorControl = 0x0, // RTCSUPV
340             RTCControl = 0x4, // RTCC
341             RTCStatus = 0x8, // RTCS
342             RTCCounter = 0xC, // RTCCNT
343             APICompareValue = 0x10, // APIVAL
344             RTCCompareValue = 0x14 // RTCVAL
345         }
346     }
347 }
348