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 16 namespace Antmicro.Renode.Peripherals.GPIOPort 17 { 18 public class EFR32_GPIOPort : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 19 { EFR32_GPIOPort(IMachine machine)20 public EFR32_GPIOPort(IMachine machine) : base(machine, NumberOfPins * NumberOfPorts) 21 { 22 EvenIRQ = new GPIO(); 23 OddIRQ = new GPIO(); 24 CreateRegisters(); 25 InnerReset(); 26 } 27 ReadDoubleWord(long offset)28 public uint ReadDoubleWord(long offset) 29 { 30 lock(internalLock) 31 { 32 return registers.Read(offset); 33 } 34 } 35 WriteDoubleWord(long offset, uint value)36 public void WriteDoubleWord(long offset, uint value) 37 { 38 lock(internalLock) 39 { 40 if(configurationLocked) 41 { 42 if(offset < (uint)Registers.ExternalInterruptPortSelectLow) 43 { 44 //port register, align it to the first port 45 offset %= PortOffset; 46 } 47 var register = (Registers)offset; 48 if(lockableRegisters.Contains(register)) 49 { 50 this.Log(LogLevel.Debug, "Not writing to {0} because of configuration lock.", register); 51 return; 52 } 53 } 54 registers.Write(offset, value); 55 } 56 } 57 Reset()58 public override void Reset() 59 { 60 lock(internalLock) 61 { 62 base.Reset(); 63 InnerReset(); 64 } 65 } 66 OnGPIO(int number, bool value)67 public override void OnGPIO(int number, bool value) 68 { 69 if(number < 0 || number >= State.Length) 70 { 71 throw new ArgumentOutOfRangeException(string.Format("Gpio #{0} called, but only {1} lines are available", number, State.Length)); 72 } 73 lock(internalLock) 74 { 75 if(IsOutput(pinModes[number].Value)) 76 { 77 this.Log(LogLevel.Warning, "Writing to an output GPIO pin #{0}", number); 78 return; 79 } 80 81 base.OnGPIO(number, value); 82 UpdateInterrupts(); 83 } 84 } 85 86 public long Size 87 { 88 get 89 { 90 return 0x1000; 91 } 92 } 93 94 public GPIO EvenIRQ { get; private set; } 95 public GPIO OddIRQ { get; private set; } 96 UpdateInterrupts()97 private void UpdateInterrupts() 98 { 99 for(var i = 0; i < State.Length; ++i) 100 { 101 var externalPin = targetExternalPins[i]; 102 if(!interruptEnable[externalPin]) 103 { 104 continue; 105 } 106 var isEdge = State[i] != previousState[externalPin]; 107 previousState[externalPin] = State[i]; 108 if(isEdge && State[i] == (interruptTriggers[externalPin] == InterruptTrigger.RisingEdge)) 109 { 110 externalInterrupt[externalPin] = true; 111 } 112 //no clear as it must be set manually with InterruptFlagClear 113 } 114 115 var even = false; 116 var odd = false; 117 for(var i = 0; i < interruptEnable.Length; i += 2) 118 { 119 even |= externalInterrupt[i]; 120 } 121 for(var i = 1; i < interruptEnable.Length; i += 2) 122 { 123 odd |= externalInterrupt[i]; 124 } 125 EvenIRQ.Set(even); 126 OddIRQ.Set(odd); 127 } 128 InnerReset()129 private void InnerReset() 130 { 131 registers.Reset(); 132 configurationLocked = false; 133 EvenIRQ.Unset(); 134 OddIRQ.Unset(); 135 for(var i = 0; i < NumberOfExternalInterrupts; ++i) 136 { 137 externalInterrupt[i] = false; 138 interruptEnable[i] = false; 139 interruptTriggers[i] = InterruptTrigger.None; 140 } 141 for(var i = 0; i < targetExternalPins.Length; ++i) 142 { 143 targetExternalPins[i] = 0; 144 } 145 for(var i = 0; i < externalInterruptToPinMapping.Length; ++i) 146 { 147 //both arrays have the same length 148 externalInterruptToPinMapping[i] = i % 4; 149 externalInterruptToPortMapping[i] = 0; 150 } 151 } 152 CreateRegisters()153 private void CreateRegisters() 154 { 155 var regs = new Dictionary<long, DoubleWordRegister>() 156 { 157 {(long)Registers.ExternalInterruptPortSelectLow, new DoubleWordRegister(this) 158 .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePort((uint)oldValue, (uint)newValue, false), name: "EXTIPSEL") 159 }, 160 {(long)Registers.ExternalInterruptPortSelectHigh, new DoubleWordRegister(this) 161 .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePort((uint)oldValue, (uint)newValue, true), name: "EXTIPSEL") 162 }, 163 {(long)Registers.ExternalInterruptPinSelectLow, new DoubleWordRegister(this, 0x32103210) 164 .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePin((uint)oldValue, (uint)newValue, false), name: "EXTIPINSEL") 165 }, 166 {(long)Registers.ExternalInterruptPinSelectHigh, new DoubleWordRegister(this, 0x32103210) 167 .WithValueField(0, 32, changeCallback: (oldValue, newValue) => ReroutePin((uint)oldValue, (uint)newValue, true), name: "EXTIPINSEL") 168 }, 169 {(long)Registers.ExternalInterruptRisingEdgeTrigger, new DoubleWordRegister(this) 170 .WithValueField(0, 16, changeCallback: (_, value) => SetEdgeSensitivity((uint)value, InterruptTrigger.RisingEdge)) 171 }, 172 {(long)Registers.ExternalInterruptFallingEdgeTrigger, new DoubleWordRegister(this) 173 .WithValueField(0, 16, changeCallback: (_, value) => SetEdgeSensitivity((uint)value, InterruptTrigger.FallingEdge)) 174 }, 175 {(long)Registers.InterruptFlag, new DoubleWordRegister(this) 176 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: (_) => BitHelper.GetValueFromBitsArray(externalInterrupt), name: "EXT") 177 .WithTag("EM4WU", 16, 16) 178 }, 179 {(long)Registers.InterruptFlagSet, new DoubleWordRegister(this) 180 .WithValueField(0, 16, FieldMode.Write, writeCallback: (_, value) => UpdateExternalInterruptBits((uint)value, true), name: "EXT") 181 .WithTag("EM4WU", 16, 16) 182 }, 183 {(long)Registers.InterruptFlagClear, new DoubleWordRegister(this) 184 .WithValueField(0, 16, writeCallback: (_, value) => UpdateExternalInterruptBits((uint)value, false), valueProviderCallback: (_) => 185 { 186 var result = BitHelper.GetValueFromBitsArray(externalInterrupt); 187 for(var i = 0; i < NumberOfExternalInterrupts; ++i) 188 { 189 externalInterrupt[i] = false; 190 } 191 UpdateInterrupts(); 192 return result; 193 }, name: "EXT") 194 .WithTag("EM4WU", 16, 16) 195 }, 196 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 197 .WithValueField(0, 16, writeCallback: (_, value) => 198 { 199 Array.Copy(BitHelper.GetBits((uint)value), interruptEnable, NumberOfExternalInterrupts); 200 UpdateInterrupts(); 201 }, 202 valueProviderCallback: (_) => BitHelper.GetValueFromBitsArray(interruptEnable), name: "EXT") 203 .WithTag("EM4WU", 16, 16) 204 }, 205 {(long)Registers.ConfigurationLock, new DoubleWordRegister(this) 206 .WithValueField(0, 16, writeCallback: (_, value) => configurationLocked = (value != UnlockCode), 207 valueProviderCallback: (_)=> configurationLocked ? 1 : 0u, name: "LOCKKEY") 208 }, 209 }; 210 for(var i = 0; i < NumberOfPorts; ++i) 211 { 212 CreatePortRegisters(regs, i); 213 } 214 registers = new DoubleWordRegisterCollection(this, regs); 215 } 216 CreatePortRegisters(Dictionary<long, DoubleWordRegister> regs, int portNumber)217 private void CreatePortRegisters(Dictionary<long, DoubleWordRegister> regs, int portNumber) 218 { 219 var regOffset = PortOffset * portNumber; 220 var pinOffset = portNumber * NumberOfPins; 221 regs.Add((long)Registers.PortAControl + regOffset, new DoubleWordRegister(this, 0x700070) 222 .WithTag("DRIVESTRENGTH", 0, 1) 223 .WithTag("SLEWRATE", 4, 3) 224 .WithTag("DINDIS", 12, 1) 225 .WithTag("DRIVESTRENGTHALT", 16, 1) 226 .WithTag("SLEWRATEALT", 20, 3) 227 .WithTag("DINDISALT", 28, 1) 228 ); 229 230 var gpioModeLow = new DoubleWordRegister(this); 231 var gpioModeHigh = new DoubleWordRegister(this); 232 233 for(var pinNumber = 0; pinNumber < 8; ++pinNumber) 234 { 235 pinModes[pinOffset + pinNumber] = gpioModeLow.DefineEnumField<PinMode>(pinNumber * 4, 4, name: "MODEX"); //TODO: pin locking 236 } 237 238 for(var pinNumber = 8; pinNumber < 16; ++pinNumber) 239 { 240 pinModes[pinOffset + pinNumber] = gpioModeHigh.DefineEnumField<PinMode>((pinNumber - 8) * 4, 4, name: "MODEX"); //TODO: pin locking 241 } 242 243 regs.Add((long)Registers.PortAModeLow + regOffset, gpioModeLow); 244 regs.Add((long)Registers.PortAModeHigh + regOffset, gpioModeHigh); 245 246 regs.Add((long)Registers.PortADataOut + regOffset, new DoubleWordRegister(this) 247 .WithValueField(0, 16, 248 writeCallback: (_, newValue) => 249 { 250 var bits = BitHelper.GetBits((uint)newValue); 251 for(var i = 0; i < 16; i++) 252 { 253 var pin = pinOffset + i; 254 if(IsOutput(pinModes[pin].Value) && unlockedPins[pin].Value) 255 { 256 Connections[pin].Set(bits[i]); 257 } 258 } 259 }, 260 valueProviderCallback: _ => BitHelper.GetValueFromBitsArray(Connections.Where(x => x.Key >= 0).OrderBy(x => x.Key).Select(x => x.Value.IsSet)))); 261 262 regs.Add((long)Registers.PortADataOutToggle + regOffset, new DoubleWordRegister(this) 263 .WithValueField(0, 16, FieldMode.Write, 264 writeCallback: (_, newValue) => 265 { 266 var bits = BitHelper.GetSetBits(newValue); 267 foreach(var bit in bits) 268 { 269 var pin = pinOffset + bit; 270 if(IsOutput(pinModes[pin].Value) && unlockedPins[pin].Value) 271 { 272 Connections[pin].Toggle(); 273 } 274 } 275 })); 276 277 regs.Add((long)Registers.PortADataIn + regOffset, new DoubleWordRegister(this) 278 .WithValueField(0, 16, FieldMode.Read, valueProviderCallback: (oldValue) => BitHelper.GetValueFromBitsArray(State.Skip(pinOffset).Take(NumberOfPins)))); 279 280 var unlockedPinsRegister = new DoubleWordRegister(this, 0xFFFF); 281 for(var pinNumber = 0; pinNumber < NumberOfPins; ++pinNumber) 282 { 283 unlockedPins[pinNumber + pinOffset] = unlockedPinsRegister.DefineFlagField(pinNumber, FieldMode.WriteZeroToClear); 284 } 285 regs.Add((long)Registers.PortAUnlockedPins + regOffset, unlockedPinsRegister); 286 } 287 SetEdgeSensitivity(uint value, InterruptTrigger trigger)288 private void SetEdgeSensitivity(uint value, InterruptTrigger trigger) 289 { 290 var bits = BitHelper.GetBits(value); 291 for(var i = 0; i < interruptTriggers.Length; ++i) 292 { 293 if(bits[i]) 294 { 295 interruptTriggers[i] |= trigger; 296 } 297 else 298 { 299 interruptTriggers[i] ^= trigger; 300 } 301 } 302 } 303 UpdateExternalInterruptBits(uint bits, bool value)304 private void UpdateExternalInterruptBits(uint bits, bool value) 305 { 306 var setBits = BitHelper.GetSetBits(bits); 307 foreach(var bit in setBits) 308 { 309 externalInterrupt[bit] = value; 310 } 311 UpdateInterrupts(); 312 } 313 ReroutePort(uint oldValue, uint newValue, bool isHighRegister)314 private void ReroutePort(uint oldValue, uint newValue, bool isHighRegister) 315 { 316 var setPins = new HashSet<int>(); 317 for(var i = 0; i < 8; ++i) 318 { 319 var externalIrq = i + (isHighRegister ? 8 : 0); 320 var portNewValue = (int)(newValue & (0xF << (i * 4))) >> (i * 4); 321 var portOldValue = (int)(oldValue & (0xF << (i * 4))) >> (i * 4); 322 if(portOldValue == portNewValue) 323 { 324 continue; 325 } 326 var pinGroup = externalIrq / 4; 327 var oldPinNumber = externalInterruptToPinMapping[externalIrq] + pinGroup * 4 + portOldValue * NumberOfPins; 328 if(!setPins.Contains(oldPinNumber)) 329 { 330 //if we did not set this pin in this run, let's unset it 331 targetExternalPins[oldPinNumber] = 0; 332 } 333 var newPinNumber = externalInterruptToPinMapping[externalIrq] + pinGroup * 4 + portNewValue * NumberOfPins; 334 targetExternalPins[newPinNumber] = externalIrq; 335 setPins.Add(newPinNumber); 336 //we keep it for the sake of ReroutePin method 337 externalInterruptToPortMapping[externalIrq] = portNewValue; 338 } 339 UpdateInterrupts(); 340 } 341 ReroutePin(uint oldValue, uint newValue, bool isHighRegister)342 private void ReroutePin(uint oldValue, uint newValue, bool isHighRegister) 343 { 344 var setPins = new HashSet<int>(); 345 for(var i = 0; i < 8; ++i) 346 { 347 var externalIrq = i + (isHighRegister ? 8 : 0); 348 var pinNewValue = (int)(newValue & (0x3 << (i * 4))) >> (i * 4); 349 var pinOldValue = (int)(oldValue & (0x3 << (i * 4))) >> (i * 4); 350 if(pinOldValue == pinNewValue) 351 { 352 continue; 353 } 354 var pinGroup = externalIrq / 4; 355 var oldPinNumber = pinOldValue + pinGroup * 4 + externalInterruptToPortMapping[externalIrq] * NumberOfPins; 356 if(!setPins.Contains(oldPinNumber)) 357 { 358 //if we did not set this pin in this run, let's unset it 359 targetExternalPins[oldPinNumber] = 0; 360 } 361 var newPinNumber = pinNewValue + pinGroup * 4 + externalInterruptToPortMapping[externalIrq] * NumberOfPins; 362 targetExternalPins[newPinNumber] = externalIrq; 363 setPins.Add(newPinNumber); 364 //we keep it for the sake of ReroutePort method 365 externalInterruptToPinMapping[externalIrq] = pinNewValue; 366 } 367 UpdateInterrupts(); 368 } 369 IsOutput(PinMode mode)370 private bool IsOutput(PinMode mode) 371 { 372 return mode >= PinMode.PushPull; 373 } 374 375 private readonly int[] externalInterruptToPortMapping = new int[NumberOfExternalInterrupts]; 376 private readonly int[] externalInterruptToPinMapping = new int[NumberOfExternalInterrupts]; 377 private readonly bool[] externalInterrupt = new bool[NumberOfExternalInterrupts]; 378 private readonly bool[] previousState = new bool[NumberOfExternalInterrupts]; 379 private readonly bool[] interruptEnable = new bool[NumberOfExternalInterrupts]; 380 private readonly int[] targetExternalPins = new int[NumberOfPins * NumberOfPorts]; 381 private readonly InterruptTrigger[] interruptTriggers = new InterruptTrigger[NumberOfExternalInterrupts]; 382 private readonly IEnumRegisterField<PinMode>[] pinModes = new IEnumRegisterField<PinMode>[NumberOfPins * NumberOfPorts]; 383 private readonly IFlagRegisterField[] unlockedPins = new IFlagRegisterField[NumberOfPins * NumberOfPorts]; 384 private readonly object internalLock = new object(); 385 386 private DoubleWordRegisterCollection registers; 387 private bool configurationLocked; 388 389 private readonly HashSet<Registers> lockableRegisters = new HashSet<Registers> 390 { 391 Registers.PortAControl, 392 Registers.PortAModeLow, 393 Registers.PortAModeHigh, 394 Registers.PortAUnlockedPins, 395 Registers.PortAOverVoltageDisable, 396 Registers.ExternalInterruptPortSelectLow, 397 Registers.ExternalInterruptPortSelectHigh, 398 Registers.ExternalInterruptPinSelectLow, 399 Registers.ExternalInterruptPinSelectHigh, 400 Registers.IORoutingPinEnable, 401 Registers.IORoutingLocation, 402 Registers.InputSense, 403 }; 404 405 private const int NumberOfPorts = 6; 406 private const int NumberOfPins = 16; 407 private const int NumberOfExternalInterrupts = 16; 408 private const int UnlockCode = 0xA534; 409 private const int PortOffset = 0x30; 410 411 [Flags] 412 private enum InterruptTrigger 413 { 414 None = 0, 415 FallingEdge = 1, 416 RisingEdge = 2 417 } 418 419 private enum PinMode 420 { 421 //not setting the values explicitly, the implicit values are used. Do not reorder. 422 Disabled, 423 Input, 424 InputPull, 425 InputPullFilter, 426 PushPull, 427 PushPullAlt, 428 WiredOr, 429 WiredOrPullDown, 430 WiredAnd, 431 WiredAndFilter, 432 WiredAndPullUp, 433 WiredAndPullUpFilter, 434 WiredAndAlt, 435 WiredAndAltFilter, 436 WiredAndAltPullUp, 437 WiredAndAltPullUpFilter, 438 } 439 440 private enum Registers 441 { 442 //port configuration 443 PortAControl = 0x0, 444 PortAModeLow = 0x4, 445 PortAModeHigh = 0x8, 446 PortADataOut = 0xC, 447 //reserved x 2 448 PortADataOutToggle = 0x18, 449 PortADataIn = 0x1C, 450 PortAUnlockedPins = 0x20, 451 //reserved x 1 452 PortAOverVoltageDisable = 0x28, 453 //reserved x 1 454 PortBControl = 0x30, 455 PortBModeLow = 0x34, 456 PortBModeHigh = 0x38, 457 PortBDataOut = 0x3C, 458 //reserved x 2 459 PortBDataOutToggle = 0x48, 460 PortBDataIn = 0x4C, 461 PortBUnlockedPins = 0x50, 462 //reserved x 1 463 PortBOverVoltageDisable = 0x58, 464 //reserved x 1 465 PortCControl = 0x60, 466 PortCModeLow = 0x64, 467 PortCModeHigh = 0x68, 468 PortCDataOut = 0x6C, 469 //reserved x 2 470 PortCDataOutToggle = 0x78, 471 PortCDataIn = 0x7C, 472 PortCUnlockedPins = 0x80, 473 //reserved x 1 474 PortCOverVoltageDisable = 0x88, 475 //reserved x 1 476 PortDControl = 0x90, 477 PortDModeLow = 0x94, 478 PortDModeHigh = 0x98, 479 PortDDataOut = 0x9C, 480 //reserved x 2 481 PortDDataOutToggle = 0xA8, 482 PortDDataIn = 0xAC, 483 PortDUnlockedPins = 0xB0, 484 //reserved x 1 485 PortDOverVoltageDisable = 0xB8, 486 //reserved x 1 487 PortEControl = 0xC0, 488 PortEModeLow = 0xC4, 489 PortEModeHigh = 0xC8, 490 PortEDataOut = 0xCC, 491 //reserved x 2 492 PortEDataOutToggle = 0xD8, 493 PortEDataIn = 0xDC, 494 PortEUnlockedPins = 0xE0, 495 //reserved x 1 496 PortEOverVoltageDisable = 0xE8, 497 //reserved x 1 498 PortFControl = 0xF0, 499 PortFModeLow = 0xF4, 500 PortFModeHigh = 0xF8, 501 PortFDataOut = 0xFC, 502 //reserved x 2 503 PortFDataOutToggle = 0x108, 504 PortFDataIn = 0x10C, 505 PortFUnlockedPins = 0x110, 506 //reserved x 1 507 PortFOverVoltageDisable = 0x118, 508 //reserved x 1 509 //global registers 510 ExternalInterruptPortSelectLow = 0x400, 511 ExternalInterruptPortSelectHigh = 0x404, 512 ExternalInterruptPinSelectLow = 0x408, 513 ExternalInterruptPinSelectHigh = 0x40C, 514 ExternalInterruptRisingEdgeTrigger = 0x410, 515 ExternalInterruptFallingEdgeTrigger = 0x414, 516 ExternalInterruptLevel = 0x418, 517 InterruptFlag = 0x41C, 518 InterruptFlagSet = 0x420, 519 InterruptFlagClear = 0x424, 520 InterruptEnable = 0x428, 521 EM4WakeUpEnable = 0x42C, 522 IORoutingPinEnable = 0x440, 523 IORoutingLocation = 0x444, 524 InputSense = 0x450, 525 ConfigurationLock = 0x454, 526 } 527 } 528 } 529