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