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; 8 using System.Collections.Generic; 9 using System.Linq; 10 using Antmicro.Renode.Core; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Peripherals.Bus; 14 using Antmicro.Renode.Utilities; 15 using Antmicro.Renode.Utilities.Collections; 16 17 namespace Antmicro.Renode.Peripherals.GPIOPort 18 { 19 [AllowedTranslations(AllowedTranslation.ByteToDoubleWord)] 20 public class MPFS_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 21 { MPFS_GPIO(IMachine machine)22 public MPFS_GPIO(IMachine machine) : base(machine, 32) 23 { 24 locker = new object(); 25 IRQ = new GPIO(); 26 irqManager = new GPIOInterruptManager(IRQ, State); 27 irqManager.DeassertActiveInterruptTrigger = true; 28 29 var registersMap = new Dictionary<long, DoubleWordRegister> 30 { 31 {(long)Registers.InterruptRegister, new DoubleWordRegister(this) 32 .WithValueField(0, 32, 33 writeCallback: (_, val) => 34 { 35 foreach(var i in BitHelper.GetSetBits(val)) 36 { 37 irqManager.ClearInterrupt(i); 38 if((irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Input) != 0) 39 { 40 Connections[i].Set(false); 41 } 42 } 43 }, 44 valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(irqManager.ActiveInterrupts), name: "INTR") 45 }, 46 47 {(long)Registers.InputRegister, new DoubleWordRegister(this) 48 .WithValueField(0, 32, FieldMode.Read, 49 valueProviderCallback: val => 50 { 51 var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Input) != 0); 52 var result = pins.Zip(State, (pin, state) => pin && state); 53 return BitHelper.GetValueFromBitsArray(result); 54 }, name: "GPIN") 55 }, 56 57 {(long)Registers.OutputRegister, new DoubleWordRegister(this) 58 .WithValueField(0, 32, 59 valueProviderCallback: val => 60 { 61 var pins = irqManager.PinDirection.Select(x => (x & GPIOInterruptManager.Direction.Output) != 0); 62 var result = pins.Zip(Connections.Values, (pin, state) => pin && state.IsSet); 63 return BitHelper.GetValueFromBitsArray(result); 64 }, 65 writeCallback: (_, val) => 66 { 67 // Potentially we should raise an exception, as GPIO is bidirectional, 68 // but we do not have such infrastructure. 69 var bits = BitHelper.GetBits((uint)val); 70 for(var i = 0; i < bits.Length; i++) 71 { 72 if((irqManager.PinDirection[i] & GPIOInterruptManager.Direction.Output) != 0) 73 { 74 Connections[i].Set(bits[i]); 75 } 76 } 77 }, name: "GPOUT") 78 }, 79 80 {(long)Registers.ClearRegister, new DoubleWordRegister(this) 81 .WithValueField(0, 32, writeCallback: (_, val) => SetRegisterBits((uint)val, false), name: "CLEAR_BITS") 82 }, 83 84 {(long)Registers.SetRegister, new DoubleWordRegister(this) 85 .WithValueField(0, 32, writeCallback: (_, val) => SetRegisterBits((uint)val, true), name: "SET_BITS") 86 }, 87 }; 88 89 var intTypeToVal = new TwoWayDictionary<GPIOInterruptManager.InterruptTrigger, uint>(); 90 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveHigh, 0); 91 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.ActiveLow, 1); 92 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.RisingEdge, 2); 93 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.FallingEdge, 3); 94 intTypeToVal.Add(GPIOInterruptManager.InterruptTrigger.BothEdges, 4); 95 96 for(var i = 0; i < RegisterLength; i++) 97 { 98 var j = i; 99 registersMap.Add(i * RegisterOffset, new DoubleWordRegister(this) 100 .WithFlag(0, 101 writeCallback: (_, val) => 102 { 103 if(val) 104 { 105 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Output; 106 } 107 else 108 { 109 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Output; 110 } 111 }, 112 valueProviderCallback: _ => (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Output) != 0, name: "OutputRegEnable") 113 .WithFlag(1, 114 writeCallback: (_, value) => 115 { 116 if(value) 117 { 118 irqManager.PinDirection[j] |= GPIOInterruptManager.Direction.Input; 119 } 120 else 121 { 122 irqManager.PinDirection[j] &= ~GPIOInterruptManager.Direction.Input; 123 } 124 }, 125 valueProviderCallback: _ => (irqManager.PinDirection[j] & GPIOInterruptManager.Direction.Input) != 0, name: "InputRegEnable") 126 .WithTag("OutputBufferEnable", 2, 1) 127 .WithFlag(3, writeCallback: (_, v) => { irqManager.InterruptEnable[j] = v; }, valueProviderCallback: _ => irqManager.InterruptEnable[j], name: "InterruptEnable") 128 .WithReservedBits(4, 1) 129 .WithValueField(5, 3, 130 writeCallback: (_, value) => 131 { 132 if(!intTypeToVal.TryGetValue((uint)value, out var type)) 133 { 134 this.Log(LogLevel.Warning, "Invalid interrupt type for pin #{0}: {1}", j, value); 135 return; 136 } 137 irqManager.InterruptType[j] = type; 138 }, 139 valueProviderCallback: _ => intTypeToVal[irqManager.InterruptType[j]], name: "InterruptType")); 140 } 141 registers = new DoubleWordRegisterCollection(this, registersMap); 142 } 143 ReadDoubleWord(long offset)144 public uint ReadDoubleWord(long offset) 145 { 146 lock(locker) 147 { 148 return registers.Read(offset); 149 } 150 } 151 WriteDoubleWord(long offset, uint value)152 public void WriteDoubleWord(long offset, uint value) 153 { 154 lock(locker) 155 { 156 registers.Write(offset, value); 157 } 158 } 159 OnGPIO(int number, bool value)160 public override void OnGPIO(int number, bool value) 161 { 162 lock(locker) 163 { 164 var isInput = irqManager.PinDirection[number].HasFlag(GPIOInterruptManager.Direction.Input); 165 var isOutput = irqManager.PinDirection[number].HasFlag(GPIOInterruptManager.Direction.Output); 166 if(isOutput && !isInput) 167 { 168 this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number); 169 return; 170 } 171 base.OnGPIO(number, value); 172 irqManager.RefreshInterrupts(); 173 174 // RefreshInterrupts will update the main IRQ, but it will not update the connection. 175 // We have to do it manually, as connection reflects if there is an active interrupt for the given pin. 176 var isIrqActive = irqManager.ActiveInterrupts.ElementAt(number); 177 if(isInput) 178 { 179 Connections[number].Set(isIrqActive); 180 } 181 } 182 } 183 Reset()184 public override void Reset() 185 { 186 lock(locker) 187 { 188 base.Reset(); 189 irqManager.Reset(); 190 registers.Reset(); 191 } 192 } 193 194 public GPIO IRQ { get; private set; } 195 196 public long Size => 0x1000; 197 SetRegisterBits(uint regVal, bool state)198 private void SetRegisterBits(uint regVal, bool state) 199 { 200 lock(locker) 201 { 202 var setBits = BitHelper.GetSetBits(regVal); 203 foreach (var i in setBits) 204 { 205 Connections[i].Set(state); 206 } 207 } 208 } 209 210 private readonly GPIOInterruptManager irqManager; 211 private readonly DoubleWordRegisterCollection registers; 212 private readonly object locker; 213 214 private const int RegisterLength = 32; 215 private const int RegisterOffset = 0x4; 216 217 private enum Registers 218 { 219 InterruptRegister = 0x80, 220 InputRegister = 0x84, 221 OutputRegister = 0x88, 222 ConfigurationRegister = 0x8c, 223 ConfigurationRegisterByte0 = 0x90, 224 ConfigurationRegisterByte1 = 0x94, 225 ConfigurationRegisterByte2 = 0x98, 226 ConfigurationRegisterByte3 = 0x9c, 227 ClearRegister = 0xa0, 228 SetRegister = 0xa4 229 } 230 } 231 } 232