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 using System.Collections.Generic;
8 using Antmicro.Renode.Core;
9 using Antmicro.Renode.Core.Structure.Registers;
10 using Antmicro.Renode.Peripherals.Bus;
11 using Antmicro.Renode.Peripherals.Timers;
12 using Antmicro.Renode.Peripherals.X86;
13 
14 namespace Antmicro.Renode.Peripherals.Miscellaneous
15 {
16     public class Quark_SystemControlSubsystem : IDoubleWordPeripheral
17     {
Quark_SystemControlSubsystem(IMachine machine, Quark_GPIOController gpioPort)18         public Quark_SystemControlSubsystem(IMachine machine, Quark_GPIOController gpioPort)
19         {
20             this.gpioPort = gpioPort;
21             this.alwaysOnCounter = new LimitTimer(machine.ClockSource, 32000, this, nameof(alwaysOnCounter), direction: Time.Direction.Ascending, enabled: true);
22 
23             var registerMap = new Dictionary<long, DoubleWordRegister>
24             {
25                 {(long)Registers.HybridOscillatorStatus1, new DoubleWordRegister(this, 3)}, //use only reset value - means that oscillators are enabled
26                 {(long)Registers.AlwaysOnCounter, new DoubleWordRegister(this, 0).WithValueField(0, 32, FieldMode.Read, valueProviderCallback: (_) => (uint)alwaysOnCounter.Value)},
27 
28                 // These registers map directly to GPIO port. Only 6 LSBits are important. Offsets in SCSS are generally the same as in the gpio port + 0xB00,
29                 // but we keep it here for clarity, logging purposes and ease of defining field modes.
30                 {(long)Registers.PortAGPIOAlwaysOn, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.PortAData)},
31                 {(long)Registers.PortAGPIOAlwaysOnDirection, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.PortADataDirection)},
32                 {(long)Registers.InterruptEnable, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.InterruptEnable)},
33                 {(long)Registers.InterruptMask, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.InterruptMask)},
34                 {(long)Registers.InterruptType, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.InterruptType)},
35                 {(long)Registers.InterruptPolarity, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.InterruptPolarity)},
36                 {(long)Registers.InterruptStatus, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.InterruptStatus, FieldMode.Read)},
37                 {(long)Registers.RawInterruptStatus, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.RawInterruptStatus, FieldMode.Read)},
38                 {(long)Registers.ClearInterrupt, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.ClearInterrupt)},
39                 {(long)Registers.PortAExternalPort, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.PortAExternalPort, FieldMode.Read)},
40                 {(long)Registers.InterruptBothEdgeType, CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers.InterruptBothEdgeType)},
41             };
42             registers = new DoubleWordRegisterCollection(this, registerMap);
43             Reset();
44         }
45 
ReadDoubleWord(long offset)46         public uint ReadDoubleWord(long offset)
47         {
48             return registers.Read(offset);
49         }
50 
Reset()51         public void Reset()
52         {
53             alwaysOnCounter.Reset();
54             registers.Reset();
55         }
56 
WriteDoubleWord(long offset, uint value)57         public void WriteDoubleWord(long offset, uint value)
58         {
59             registers.Write(offset, value);
60         }
61 
CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers register, FieldMode fieldMode = FieldMode.Read | FieldMode.Write)62         private DoubleWordRegister CreateAlwaysOnGPIORegister(Quark_GPIOController.Registers register, FieldMode fieldMode = FieldMode.Read | FieldMode.Write)
63         {
64             if(fieldMode.IsWritable())
65             {
66                 return new DoubleWordRegister(this, 0)
67                     .WithValueField(0, NumberOfGPIOs, fieldMode, writeCallback: (_, value) =>
68                         {
69                             var currentState = gpioPort.ReadDoubleWord((uint)register);
70                             // no need to filter value with & AlwaysOnGPIOMask, as it is already filtered
71                             gpioPort.WriteDoubleWord((uint)register, (currentState & ~AlwaysOnGPIOMask) | (uint)value);
72                         }, valueProviderCallback: _ =>
73                         {
74                             return gpioPort.ReadDoubleWord((uint)register);
75                         });
76             }
77             else
78             {
79                 return new DoubleWordRegister(this, 0)
80                     .WithValueField(0, NumberOfGPIOs, fieldMode, valueProviderCallback: _ =>
81                         {
82                             return gpioPort.ReadDoubleWord((uint)register);
83                         });
84             }
85         }
86 
87         private readonly DoubleWordRegisterCollection registers;
88         private readonly LimitTimer alwaysOnCounter;
89         private readonly Quark_GPIOController gpioPort;
90 
91         private const int NumberOfGPIOs = 6;
92         private const uint AlwaysOnGPIOMask = (1 << NumberOfGPIOs) - 1;
93 
94         private enum Registers
95         {
96             HybridOscillatorStatus1 = 0x4,
97             AlwaysOnCounter = 0x700,
98             PortAGPIOAlwaysOn = 0xB00,
99             PortAGPIOAlwaysOnDirection = 0xB04,
100             InterruptEnable = 0xB30,
101             InterruptMask = 0xB34,
102             InterruptType = 0xB38,
103             InterruptPolarity = 0xB3C,
104             InterruptStatus = 0xB40,
105             RawInterruptStatus = 0xB44,
106             DebounceEnable = 0xB48,
107             ClearInterrupt = 0xB4C,
108             PortAExternalPort = 0xB50,
109             SynchronizationLevel = 0xB60,
110             InterruptBothEdgeType = 0xB68,
111         }
112     }
113 }
114