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 using System.Collections.Generic; 7 using Antmicro.Renode.Core; 8 using Antmicro.Renode.Core.Structure.Registers; 9 using Antmicro.Renode.Logging; 10 using Antmicro.Renode.Peripherals.Bus; 11 12 namespace Antmicro.Renode.Peripherals.GPIOPort 13 { 14 public class MAX32650_GPIO : BaseGPIOPort, IDoubleWordPeripheral, IKnownSize 15 { MAX32650_GPIO(IMachine machine, int numberOfPins)16 public MAX32650_GPIO(IMachine machine, int numberOfPins) : base(machine, numberOfPins) 17 { 18 registers = new DoubleWordRegisterCollection(this, BuildRegisterMap()); 19 IRQ = new GPIO(); 20 WakeUpIRQ = new GPIO(); 21 } 22 Reset()23 public override void Reset() 24 { 25 base.Reset(); 26 registers.Reset(); 27 UpdateInterrupts(); 28 } 29 ReadDoubleWord(long offset)30 public uint ReadDoubleWord(long offset) 31 { 32 return registers.Read(offset); 33 } 34 WriteDoubleWord(long offset, uint value)35 public void WriteDoubleWord(long offset, uint value) 36 { 37 registers.Write(offset, value); 38 } 39 OnGPIO(int number, bool value)40 public override void OnGPIO(int number, bool value) 41 { 42 var previousValue = State[number]; 43 base.OnGPIO(number, value); 44 OnPinStateChanged(number, previousValue, value); 45 } 46 47 public long Size => 0x400; 48 49 public GPIO IRQ { get; } 50 public GPIO WakeUpIRQ { get; } 51 UpdatePinOutput(int idx, bool value)52 private void UpdatePinOutput(int idx, bool value) 53 { 54 if(!pinOutputEnabled[idx].Value) 55 { 56 this.Log(LogLevel.Warning, "Attempted to set pin{0} to {1}, but it's not set to OUTPUT; ignoring", idx, value); 57 return; 58 } 59 60 if(!pinEnabled[idx].Value) 61 { 62 this.Log(LogLevel.Warning, "Attempted to set pin{0} to {1}, but it's not enabled; ignoring", idx, value); 63 return; 64 } 65 66 if(State[idx] == value) 67 { 68 return; 69 } 70 71 Connections[idx].Set(value); 72 State[idx] = value; 73 } 74 OnPinStateChanged(int idx, bool previous, bool current)75 private void OnPinStateChanged(int idx, bool previous, bool current) 76 { 77 var interruptPending = false; 78 switch(interruptPolarity[idx].Value) 79 { 80 case InterruptPolarity.LowFalling: 81 if(interruptMode[idx].Value == InterruptMode.EdgeTriggered) 82 { 83 interruptPending = !current && (previous != current); 84 } 85 if(interruptMode[idx].Value == InterruptMode.LevelTriggered) 86 { 87 interruptPending = !current; 88 } 89 break; 90 case InterruptPolarity.HighRising: 91 if(interruptMode[idx].Value == InterruptMode.EdgeTriggered) 92 { 93 interruptPending = current && (previous != current); 94 } 95 if(interruptMode[idx].Value == InterruptMode.LevelTriggered) 96 { 97 interruptPending = current; 98 } 99 break; 100 } 101 102 interruptStatus[idx].Value |= interruptPending; 103 UpdateInterrupts(); 104 } 105 UpdateInterrupts()106 private void UpdateInterrupts() 107 { 108 var pendingInterrupt = false; 109 var pendingWakeUp = false; 110 for(var i = 0; i < NumberOfConnections; ++i) 111 { 112 pendingInterrupt |= interruptEnabled[i].Value && interruptStatus[i].Value; 113 pendingWakeUp |= wakeupEnabled[i].Value && interruptStatus[i].Value; 114 } 115 IRQ.Set(pendingInterrupt); 116 WakeUpIRQ.Set(pendingWakeUp); 117 } 118 BuildRegisterMap()119 private Dictionary<long, DoubleWordRegister> BuildRegisterMap() 120 { 121 var registersMap = new Dictionary<long, DoubleWordRegister> 122 { 123 {(long)Registers.Enable, new DoubleWordRegister(this, 0xffffffff) 124 .WithFlags(0, 32, out pinEnabled, name: "EN.gpio_en") 125 }, 126 {(long)Registers.EnableSet, new DoubleWordRegister(this, 0xffffffff) 127 .WithFlags(0, 32, name: "EN_SET.all", 128 valueProviderCallback: (i, _) => pinEnabled[i].Value, 129 writeCallback: (i, _, val) => { if(val) pinEnabled[i].Value = true; }) 130 }, 131 {(long)Registers.EnableClear, new DoubleWordRegister(this, 0xffffffff) 132 .WithFlags(0, 32, name: "EN_CLR.all", 133 valueProviderCallback: (i, _) => pinEnabled[i].Value, 134 writeCallback: (i, _, val) => { if(val) pinEnabled[i].Value = false; }) 135 }, 136 {(long)Registers.OutputEnable, new DoubleWordRegister(this, 0x8000000) 137 .WithFlags(0, 32, out pinOutputEnabled, name: "OUT_EN.gpio_out_en") 138 }, 139 {(long)Registers.OutputEnableSet, new DoubleWordRegister(this) 140 .WithFlags(0, 32, name: "OUT_EN_SET.all", 141 valueProviderCallback: (i, _) => pinOutputEnabled[i].Value, 142 writeCallback: (i, _, val) => { if(val) pinOutputEnabled[i].Value = true; }) 143 }, 144 {(long)Registers.OutputEnableClear, new DoubleWordRegister(this) 145 .WithFlags(0, 32, out pinOutputEnabled, name: "OUT_EN_CLR.all", 146 valueProviderCallback: (i, _) => pinOutputEnabled[i].Value, 147 writeCallback: (i, _, val) => { if(val) pinOutputEnabled[i].Value = false; }) 148 }, 149 {(long)Registers.Output, new DoubleWordRegister(this) 150 .WithFlags(0, 32, name: "OUT.gpio_out", 151 valueProviderCallback: (i, _) => State[i] && pinOutputEnabled[i].Value, 152 writeCallback: (i, _, val) => UpdatePinOutput(i, val)) 153 }, 154 {(long)Registers.OutputSet, new DoubleWordRegister(this) 155 .WithFlags(0, 32, name: "OUT_SET.gpio_out_set", 156 valueProviderCallback: (i, _) => State[i] && pinOutputEnabled[i].Value, 157 writeCallback: (i, _, val) => { if(val) UpdatePinOutput(i, true) ;}) 158 }, 159 {(long)Registers.OutputClear, new DoubleWordRegister(this) 160 .WithFlags(0, 32, name: "OUT_CLR.gpio_out_clr", 161 valueProviderCallback: (i, _) => State[i] && pinOutputEnabled[i].Value, 162 writeCallback: (i, _, val) => { if(val) UpdatePinOutput(i, false); }) 163 }, 164 {(long)Registers.Input, new DoubleWordRegister(this) 165 .WithFlags(0, 32, name: "IN.gpio_in", 166 valueProviderCallback: (i, _) => 167 { 168 if(!pinInputEnabled[i].Value) 169 { 170 this.Log(LogLevel.Noisy, "Trying to get value from pin {0} which doesn't have input mode enabled"); 171 return false; 172 } 173 return State[i]; 174 }) 175 }, 176 {(long)Registers.InterruptMode, new DoubleWordRegister(this) 177 .WithEnumFields<DoubleWordRegister, InterruptMode>(0, 1, 32, out interruptMode, name: "INT_MOD.gpio_int_mod") 178 .WithChangeCallback((_, __) => UpdateInterrupts()) 179 }, 180 {(long)Registers.InterruptPolarity, new DoubleWordRegister(this) 181 .WithEnumFields<DoubleWordRegister, InterruptPolarity>(0, 1, 32, out interruptPolarity, name: "INT_POL.gpio_int_pol") 182 .WithChangeCallback((_, __) => UpdateInterrupts()) 183 }, 184 {(long)Registers.InputEnabled, new DoubleWordRegister(this, 0xffffffff) 185 .WithFlags(0, 32, out pinInputEnabled, name: "IN_EN.gpio_in_en") 186 }, 187 {(long)Registers.InterruptEnable, new DoubleWordRegister(this) 188 .WithFlags(0, 32, out interruptEnabled, name: "INT_EN.gpio_int_en") 189 .WithChangeCallback((_, __) => UpdateInterrupts()) 190 }, 191 {(long)Registers.InterruptEnableSet, new DoubleWordRegister(this) 192 .WithFlags(0, 32, name: "INT_EN_SET.gpio_int_en_set", 193 valueProviderCallback: (i, _) => interruptEnabled[i].Value, 194 writeCallback: (i, _, value) => 195 { 196 if(value) 197 { 198 interruptEnabled[i].Value = true; 199 } 200 }) 201 .WithWriteCallback((_, __) => UpdateInterrupts()) 202 }, 203 {(long)Registers.InterruptEnableClear, new DoubleWordRegister(this) 204 .WithFlags(0, 32, name: "INT_EN_CLR.gpio_int_en_clr", 205 valueProviderCallback: (i, _) => interruptEnabled[i].Value, 206 writeCallback: (i, _, value) => 207 { 208 if(value) 209 { 210 interruptEnabled[i].Value = false; 211 } 212 }) 213 .WithWriteCallback((_, __) => UpdateInterrupts()) 214 }, 215 {(long)Registers.InterruptStatus, new DoubleWordRegister(this) 216 .WithFlags(0, 32, out interruptStatus, FieldMode.Read, name: "INT_STAT.gpio_int_stat") 217 }, 218 {(long)Registers.InterruptStatusClear, new DoubleWordRegister(this) 219 .WithFlags(0, 32, name: "INT_CLR.all", 220 valueProviderCallback: (i, _) => interruptStatus[i].Value, 221 writeCallback: (i, _, value) => 222 { 223 if(value) 224 { 225 interruptStatus[i].Value = false; 226 } 227 }) 228 .WithWriteCallback((_, __) => UpdateInterrupts()) 229 }, 230 {(long)Registers.WakeEnable, new DoubleWordRegister(this) 231 .WithFlags(0, 32, out wakeupEnabled, name: "WAKE_EN.gpio_wake_en") 232 .WithChangeCallback((_, __) => UpdateInterrupts()) 233 }, 234 {(long)Registers.WakeEnableSet, new DoubleWordRegister(this) 235 .WithFlags(0, 32, name: "WAKE_EN_SET.all", 236 valueProviderCallback: (i, _) => wakeupEnabled[i].Value, 237 writeCallback: (i, _, value) => 238 { 239 if(value) 240 { 241 wakeupEnabled[i].Value = true; 242 } 243 }) 244 .WithWriteCallback((_, __) => UpdateInterrupts()) 245 }, 246 {(long)Registers.WakeEnableClear, new DoubleWordRegister(this) 247 .WithFlags(0, 32, name: "WAKE_EN_CLR.all", 248 valueProviderCallback: (i, _) => wakeupEnabled[i].Value, 249 writeCallback: (i, _, value) => 250 { 251 if(value) 252 { 253 wakeupEnabled[i].Value = false; 254 } 255 }) 256 .WithWriteCallback((_, __) => UpdateInterrupts()) 257 }, 258 {(long)Registers.InterruptDualEdge, new DoubleWordRegister(this) 259 .WithTag("INT_DUAL_EDGE.gpio_int_dual_edge", 0, 32) 260 }, 261 {(long)Registers.InputMode1, new DoubleWordRegister(this) 262 .WithTag("PAD_CFG1.gpio_pad_cfg1", 0, 32) 263 }, 264 {(long)Registers.InputMode2, new DoubleWordRegister(this) 265 .WithTag("PAD_CFG2.gpio_pad_cfg2", 0, 32) 266 }, 267 {(long)Registers.AlternateFunction1, new DoubleWordRegister(this) 268 .WithTag("EN1.gpio_en1", 0, 32) 269 }, 270 {(long)Registers.AlternateFunction1Set, new DoubleWordRegister(this) 271 .WithTag("EN1_SET.all", 0, 32) 272 }, 273 {(long)Registers.AlternateFunction1Clear, new DoubleWordRegister(this) 274 .WithTag("EN1_CLR.all", 0, 32) 275 }, 276 {(long)Registers.AlternateFunction2, new DoubleWordRegister(this) 277 .WithTag("EN2.gpio_en2", 0, 32) 278 }, 279 {(long)Registers.AlternateFunction2Set, new DoubleWordRegister(this) 280 .WithTag("EN2_SET.all", 0, 32) 281 }, 282 {(long)Registers.AlternateFunction2Clear, new DoubleWordRegister(this) 283 .WithTag("EN2_CLR.all", 0, 32) 284 }, 285 {(long)Registers.DriveStrength1, new DoubleWordRegister(this) 286 .WithTag("DS.ds", 0, 32) 287 }, 288 {(long)Registers.DriveStrength2, new DoubleWordRegister(this) 289 .WithTag("DS1.all", 0, 32) 290 }, 291 {(long)Registers.PullUpPullDown, new DoubleWordRegister(this) 292 .WithTag("PS.all", 0, 32) 293 }, 294 {(long)Registers.Voltage, new DoubleWordRegister(this) 295 .WithTag("VSSEL.all", 0, 32) 296 } 297 }; 298 return registersMap; 299 } 300 301 private IFlagRegisterField[] pinEnabled; 302 private IFlagRegisterField[] pinOutputEnabled; 303 private IFlagRegisterField[] pinInputEnabled; 304 private IFlagRegisterField[] interruptEnabled; 305 private IFlagRegisterField[] interruptStatus; 306 private IFlagRegisterField[] wakeupEnabled; 307 308 private IEnumRegisterField<InterruptMode>[] interruptMode; 309 private IEnumRegisterField<InterruptPolarity>[] interruptPolarity; 310 311 private readonly DoubleWordRegisterCollection registers; 312 313 enum InterruptMode 314 { 315 LevelTriggered = 0, 316 EdgeTriggered, 317 } 318 319 enum InterruptPolarity 320 { 321 LowFalling = 0, 322 HighRising, 323 } 324 325 private enum Registers 326 { 327 Enable = 0x00, 328 EnableSet = 0x04, 329 EnableClear = 0x08, 330 OutputEnable = 0x0C, 331 OutputEnableSet = 0x10, 332 OutputEnableClear = 0x14, 333 Output = 0x18, 334 OutputSet = 0x1C, 335 OutputClear = 0x20, 336 Input = 0x24, 337 InterruptMode = 0x28, 338 InterruptPolarity = 0x2C, 339 InputEnabled = 0x30, 340 InterruptEnable = 0x34, 341 InterruptEnableSet = 0x38, 342 InterruptEnableClear = 0x3C, 343 InterruptStatus = 0x40, 344 InterruptStatusClear = 0x48, 345 WakeEnable = 0x4C, 346 WakeEnableSet = 0x50, 347 WakeEnableClear = 0x54, 348 InterruptDualEdge = 0x5C, 349 InputMode1 = 0x60, 350 InputMode2 = 0x64, 351 AlternateFunction1 = 0x68, 352 AlternateFunction1Set = 0x6C, 353 AlternateFunction1Clear = 0x70, 354 AlternateFunction2 = 0x74, 355 AlternateFunction2Set = 0x78, 356 AlternateFunction2Clear = 0x7C, 357 DriveStrength1 = 0xB0, 358 DriveStrength2 = 0xB4, 359 PullUpPullDown = 0xB8, 360 Voltage = 0xC0, 361 } 362 } 363 } 364