1 //
2 // Copyright (c) 2010-2018 Antmicro
3 // Copyright (c) 2011-2015 Realtime Embedded
4 //
5 // This file is licensed under the MIT License.
6 // Full license text is available in 'licenses/MIT.txt'.
7 //
8 
9 using Antmicro.Renode.Core;
10 using Antmicro.Renode.Logging;
11 using Antmicro.Renode.Peripherals.Bus;
12 using System;
13 
14 namespace Antmicro.Renode.Peripherals.Timers
15 {
16     public class PeriodicInterruptTimer : IDoubleWordPeripheral
17     {
18 
PeriodicInterruptTimer(IMachine machine)19         public PeriodicInterruptTimer(IMachine machine)
20         {
21             this.machine = machine;
22             IRQ = new GPIO();
23             Reset();
24         }
25 
26         public GPIO IRQ { get; private set; }
27 
ReadDoubleWord(long offset)28         public uint ReadDoubleWord(long offset)
29         {
30             if (offset < 0x100) {
31                 return 0;
32             }
33             var returnValue = 0u;
34             var timer_no = (int) ((offset - 0x100) / 0x10);
35             var noffset = (long) (offset - 0x100 - 0x10*timer_no);
36             if (timer_no < 8) {
37                 returnValue = ReadTimer(timer_no, noffset);
38             } else {
39                 this.LogUnhandledRead(offset);
40             }
41             return returnValue;
42         }
43 
WriteDoubleWord(long offset, uint value)44         public void WriteDoubleWord(long offset, uint value)
45         {
46             if (offset < 0x100) {
47                 return;
48             }
49             var timer_no = (int) ((offset - 0x100) / 0x10);
50             var noffset = (long) (offset - 0x100 - 0x10*timer_no);
51             if (timer_no < 8) {
52                 WriteTimer(timer_no, noffset, value);
53             }
54             else
55             {
56                 this.LogUnhandledWrite(offset, value);
57             }
58         }
59 
Reset()60         public void Reset()
61         {
62             timers = new InnerTimer[8];
63             for(var i = 0; i < timers.Length; i++)
64             {
65                 var j = i;
66                 timers[i].CoreTimer = new LimitTimer(machine.ClockSource, TimerFrequency, this, i.ToString()) { AutoUpdate = true };
67                 timers[i].Control = InnerTimer.ControlRegister.InterruptEnable;
68                 timers[i].CoreTimer.Limit = 0xFFFFFFFF;
69                 timers[i].CoreTimer.LimitReached += () => UpdateInterrupt(j, true);
70             }
71         }
72 
ReadTimer(int timerNo, long offset)73         private uint ReadTimer(int timerNo, long offset)
74         {
75             var value = 0u;
76             switch((Offset)offset)
77             {
78             case Offset.Load:
79                 value = (uint)timers[timerNo].CoreTimer.Limit;
80                 break;
81             case Offset.Value:
82                 value = (uint)timers[timerNo].CoreTimer.Value;
83                 break;
84             case Offset.Control:
85                 value = (uint)timers[timerNo].Control;
86                 break;
87             case Offset.Flag:
88                 value = timers[timerNo].CoreTimer.RawInterrupt ? 1u : 0u;
89                 break;
90             default:
91                 this.Log(LogLevel.Warning, "Unhandled read from 0x{0:X} (timer {1}).", offset, timerNo);
92                 value = 0;
93                 break;
94             }
95             return value;
96         }
97 
WriteTimer(int timerNo, long offset, uint value)98         private void WriteTimer(int timerNo, long offset, uint value)
99         {
100             switch((Offset)offset)
101             {
102             case Offset.Load:
103                 timers[timerNo].CoreTimer.Limit = value;
104                 break;
105             case Offset.Value:
106                 // linux writes here
107                 break;
108             case Offset.Control:
109                 timers[timerNo].Control = (InnerTimer.ControlRegister)value;
110                 break;
111             case Offset.Flag:
112                 timers[timerNo].CoreTimer.ClearInterrupt();
113                 UpdateInterrupt(timerNo, false);
114                 break;
115             default:
116                 this.Log(LogLevel.Warning, "Unhandled write to 0x{0:X}, value 0x{1:X} (timer {2}).", offset, value, timerNo);
117                 break;
118             }
119         }
120 
UpdateInterrupt(int timerNo, bool value)121         private void UpdateInterrupt(int timerNo, bool value)
122         {
123             // this method's code is rather good despite looking strange
124             if(value)
125             {
126                 IRQ.Set(true);
127                 return;
128             }
129             IRQ.Set(timers[timerNo].CoreTimer.Interrupt);
130         }
131 
132         private InnerTimer[] timers;
133         private readonly IMachine machine;
134 
135         private struct InnerTimer
136         {
137             public LimitTimer CoreTimer;
138 
139             public ControlRegister Control
140             {
141                 get
142                 {
143                     lock(CoreTimer)
144                     {
145                         return control;
146                     }
147                 }
148                 set
149                 {
150                     lock(CoreTimer)
151                     {
152                         if((value & ControlRegister.Enable) != (control & ControlRegister.Enable))
153                         {
154                             CoreTimer.Enabled = (value & ControlRegister.Enable) != 0;
155                         }
156                         CoreTimer.EventEnabled = (value & ControlRegister.InterruptEnable) != 0;
157                         control = value;
158                     }
159                 }
160             }
161 
162             private ControlRegister control;
163 
164             [Flags]
165             public enum ControlRegister
166             {
167                 ChainMode       = (1 << 2),
168                 InterruptEnable = (1 << 1),
169                 Enable          = (1 << 0)
170             }
171         }
172 
173         private enum Offset : uint
174         {
175             Load                =   0x000,
176             Value               =   0x004,
177             Control             =   0x008,
178             Flag                =   0x00C,
179         }
180 
181         private const int TimerFrequency = 66000000;
182     }
183 }
184