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.Peripherals.GPIOPort; 16 17 namespace Antmicro.Renode.Peripherals.X86 18 { 19 public sealed class Quark_GPIOController : BaseGPIOPort, IDoubleWordPeripheral, IGPIOReceiver, IKnownSize 20 { Quark_GPIOController(IMachine machine)21 public Quark_GPIOController(IMachine machine) : base(machine, NumberOfGPIOS) 22 { 23 internalLock = new object(); 24 previousState = new bool[NumberOfGPIOS]; 25 PortDataDirection = new PinDirection[NumberOfGPIOS]; 26 InterruptEnable = new bool[NumberOfGPIOS]; 27 InterruptMask = new bool[NumberOfGPIOS]; 28 interruptType = new InterruptTrigger[NumberOfGPIOS]; 29 activeInterrupts = new bool[NumberOfGPIOS]; 30 IRQ = new GPIO(); 31 PrepareRegisters(); 32 } 33 ReadDoubleWord(long offset)34 public uint ReadDoubleWord(long offset) 35 { 36 lock(internalLock) 37 { 38 return registers.Read(offset); 39 } 40 } 41 Reset()42 public override void Reset() 43 { 44 lock(internalLock) 45 { 46 base.Reset(); 47 for(int i = 0; i < NumberOfGPIOS; i++) 48 { 49 previousState[i] = false; 50 activeInterrupts[i] = false; 51 PortDataDirection[i] = PinDirection.Input; 52 InterruptEnable[i] = false; 53 InterruptMask[i] = false; 54 interruptType[i] = InterruptTrigger.ActiveLow; 55 } 56 IRQ.Unset(); 57 registers.Reset(); 58 } 59 } 60 WriteDoubleWord(long offset, uint value)61 public void WriteDoubleWord(long offset, uint value) 62 { 63 lock(internalLock) 64 { 65 registers.Write(offset, value); 66 } 67 } 68 OnGPIO(int number, bool value)69 public override void OnGPIO(int number, bool value) 70 { 71 if(number < 0 || number >= NumberOfGPIOS) 72 { 73 throw new ArgumentOutOfRangeException(string.Format("Gpio #{0} called, but only {1} lines are available", number, NumberOfGPIOS)); 74 } 75 76 lock(internalLock) 77 { 78 if(PortDataDirection[number] == PinDirection.Output) 79 { 80 this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number); 81 return; 82 } 83 84 base.OnGPIO(number, value); 85 RefreshInterrupts(); 86 } 87 } 88 SetInterruptType(byte pinId, InterruptTrigger trigger)89 public void SetInterruptType(byte pinId, InterruptTrigger trigger) 90 { 91 lock(internalLock) 92 { 93 interruptType[pinId] = trigger; 94 switch (trigger) 95 { 96 case InterruptTrigger.BothEdges: 97 interruptBothEdgeField.SetBit(pinId, true); 98 // interruptType and interruptPolarity are not considered when this bit is set 99 break; 100 case InterruptTrigger.RisingEdge: 101 interruptBothEdgeField.SetBit(pinId, false); 102 interruptTypeField.SetBit(pinId, true); 103 interruptPolarityField.SetBit(pinId, true); 104 break; 105 case InterruptTrigger.FallingEdge: 106 interruptBothEdgeField.SetBit(pinId, false); 107 interruptTypeField.SetBit(pinId, true); 108 interruptPolarityField.SetBit(pinId, false); 109 break; 110 case InterruptTrigger.ActiveHigh: 111 interruptBothEdgeField.SetBit(pinId, false); 112 interruptTypeField.SetBit(pinId, false); 113 interruptPolarityField.SetBit(pinId, true); 114 break; 115 case InterruptTrigger.ActiveLow: 116 interruptBothEdgeField.SetBit(pinId, false); 117 interruptTypeField.SetBit(pinId, false); 118 interruptPolarityField.SetBit(pinId, false); 119 break; 120 } 121 RefreshInterrupts(); 122 } 123 } 124 125 public GPIO IRQ { get; private set; } 126 public PinDirection[] PortDataDirection { get; private set; } 127 public bool[] InterruptEnable { get; private set; } 128 public IReadOnlyCollection<InterruptTrigger> InterruptType { get { return interruptType; } } 129 public bool[] InterruptMask { get; private set; } 130 // setting state using this array directly will not raise any interrupts! 131 public new bool[] State { get { return base.State; } } 132 public long Size { get { return 0x78; } } 133 PrepareRegisters()134 private void PrepareRegisters() 135 { 136 registers = new DoubleWordRegisterCollection(this, new Dictionary<long, DoubleWordRegister> 137 { 138 {(long)Registers.PortAData, new DoubleWordRegister(this) 139 .WithValueField(0, 32, writeCallback: (_, val) => 140 { 141 var bits = BitHelper.GetBits((uint)val); 142 for(int i = 0; i < bits.Length; i++) 143 { 144 if(PortDataDirection[i] == PinDirection.Output) 145 { 146 Connections[i].Set(bits[i]); 147 State[i] = bits[i]; 148 } 149 } 150 }, valueProviderCallback: _ => { return BitHelper.GetValueFromBitsArray(State); }) 151 }, 152 {(long)Registers.PortADataDirection, new DoubleWordRegister(this) 153 .WithValueField(0, 32, writeCallback: (_, val) => Array.Copy(BitHelper.GetBits((uint)val).Select(x => x ? PinDirection.Output : PinDirection.Input).ToArray() , PortDataDirection, 32), 154 valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(PortDataDirection.Select(x => x == PinDirection.Output))) 155 }, 156 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 157 .WithValueField(0, 32, writeCallback: (_, val) => { 158 Array.Copy(BitHelper.GetBits((uint)val), InterruptEnable, 32); 159 RefreshInterrupts(); 160 }, 161 valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(InterruptEnable)) 162 }, 163 {(long)Registers.InterruptType, new DoubleWordRegister(this) 164 // true = edge sensitive; false = level sensitive 165 .WithValueField(0, 32, out interruptTypeField, writeCallback: (_, val) => CalculateInterruptTypes()) 166 }, 167 {(long)Registers.InterruptPolarity, new DoubleWordRegister(this) 168 // true = rising edge / active high; false = falling edge / active low 169 .WithValueField(0, 32, out interruptPolarityField, writeCallback: (_, val) => CalculateInterruptTypes()) 170 }, 171 {(long)Registers.InterruptBothEdgeType, new DoubleWordRegister(this) 172 .WithValueField(0, 32, out interruptBothEdgeField, writeCallback: (_, val) => CalculateInterruptTypes()) 173 }, 174 {(long)Registers.InterruptMask, new DoubleWordRegister(this) 175 .WithValueField(0, 32, writeCallback: (_, val) => { 176 Array.Copy(BitHelper.GetBits((uint)val), InterruptMask, 32); 177 RefreshInterrupts(); 178 }, 179 valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(InterruptMask)) 180 }, 181 {(long)Registers.PortAExternalPort, new DoubleWordRegister(this) 182 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(State)) 183 }, 184 {(long)Registers.ClearInterrupt, new DoubleWordRegister(this) 185 .WithValueField(0, 32, FieldMode.Write, writeCallback: (_, val) => 186 { 187 foreach(var bit in BitHelper.GetSetBits(val)) 188 { 189 activeInterrupts[bit] = false; 190 } 191 RefreshInterrupts(); 192 }) 193 }, 194 {(long)Registers.InterruptStatus, new DoubleWordRegister(this) 195 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(activeInterrupts.Zip(InterruptMask, (isActive, isMasked) => isActive && !isMasked))) 196 }, 197 {(long)Registers.RawInterruptStatus, new DoubleWordRegister(this) 198 .WithValueField(0, 32, FieldMode.Read, valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(activeInterrupts)) 199 } 200 }); 201 } 202 CalculateInterruptTypes()203 private void CalculateInterruptTypes() 204 { 205 lock(internalLock) 206 { 207 var isBothEdgesSensitive = BitHelper.GetBits((uint)interruptBothEdgeField.Value); 208 var isEdgeSensitive = BitHelper.GetBits((uint)interruptTypeField.Value); 209 var isActiveHighOrRisingEdge = BitHelper.GetBits((uint)interruptPolarityField.Value); 210 for(int i = 0; i < interruptType.Length; i++) 211 { 212 if(isBothEdgesSensitive[i]) 213 { 214 interruptType[i] = InterruptTrigger.BothEdges; 215 } 216 else 217 { 218 if(isEdgeSensitive[i]) 219 { 220 interruptType[i] = isActiveHighOrRisingEdge[i] 221 ? InterruptTrigger.RisingEdge 222 : InterruptTrigger.FallingEdge; 223 } 224 else 225 { 226 interruptType[i] = isActiveHighOrRisingEdge[i] 227 ? InterruptTrigger.ActiveHigh 228 : InterruptTrigger.ActiveLow; 229 } 230 } 231 } 232 RefreshInterrupts(); 233 } 234 } 235 RefreshInterrupts()236 private void RefreshInterrupts() 237 { 238 var irqState = false; 239 for(int i = 0; i < NumberOfGPIOS; i++) 240 { 241 if(!InterruptEnable[i]) 242 { 243 continue; 244 } 245 var isEdge = State[i] != previousState[i]; 246 switch(interruptType[i]) 247 { 248 case InterruptTrigger.ActiveHigh: 249 irqState |= (State[i] && !InterruptMask[i]); 250 break; 251 case InterruptTrigger.ActiveLow: 252 irqState |= (!State[i] && !InterruptMask[i]); 253 break; 254 case InterruptTrigger.RisingEdge: 255 if(isEdge && State[i]) 256 { 257 irqState |= !InterruptMask[i]; 258 activeInterrupts[i] = true; 259 } 260 break; 261 case InterruptTrigger.FallingEdge: 262 if(isEdge && !State[i]) 263 { 264 irqState |= !InterruptMask[i]; 265 activeInterrupts[i] = true; 266 } 267 break; 268 case InterruptTrigger.BothEdges: 269 if(isEdge) 270 { 271 irqState |= !InterruptMask[i]; 272 activeInterrupts[i] = true; 273 } 274 break; 275 } 276 } 277 Array.Copy(State, previousState, State.Length); 278 if(irqState) 279 { 280 IRQ.Set(); 281 } 282 else if(!activeInterrupts.Any(x => x)) 283 { 284 IRQ.Unset(); 285 } 286 } 287 288 private DoubleWordRegisterCollection registers; 289 private InterruptTrigger[] interruptType; 290 private IValueRegisterField interruptPolarityField; 291 private IValueRegisterField interruptTypeField; 292 private IValueRegisterField interruptBothEdgeField; 293 private readonly bool[] activeInterrupts; 294 private readonly bool[] previousState; 295 296 private readonly object internalLock; 297 298 private const int NumberOfGPIOS = 32; 299 300 public enum PinDirection 301 { 302 Input, 303 Output 304 } 305 306 public enum InterruptTrigger 307 { 308 ActiveLow, 309 ActiveHigh, 310 FallingEdge, 311 RisingEdge, 312 BothEdges 313 } 314 315 internal enum Registers : long 316 { 317 PortAData = 0x0, 318 PortADataDirection = 0x4, 319 PortADataSource = 0x8, 320 InterruptEnable = 0x30, 321 InterruptMask = 0x34, 322 InterruptType = 0x38, 323 InterruptPolarity = 0x3C, 324 InterruptStatus = 0x40, 325 RawInterruptStatus = 0x44, 326 DebounceEnable = 0x48, 327 ClearInterrupt = 0x4C, 328 PortAExternalPort = 0x50, 329 SynchronizationLevel = 0x60, 330 InterruptBothEdgeType = 0x68 331 } 332 } 333 } 334