1 // 2 // Copyright (c) 2010-2022 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.Linq; 9 using Antmicro.Renode.Core; 10 using Antmicro.Renode.Core.Structure; 11 using Antmicro.Renode.Core.Structure.Registers; 12 using Antmicro.Renode.Logging; 13 using Antmicro.Renode.Utilities; 14 15 namespace Antmicro.Renode.Peripherals.I2C 16 { 17 public class LC709205F : II2CPeripheral, IProvidesRegisterCollection<WordRegisterCollection> 18 { LC709205F(IMachine machine)19 public LC709205F(IMachine machine) 20 { 21 this.machine = machine; 22 RegistersCollection = new WordRegisterCollection(this); 23 DefineRegisters(); 24 } 25 Write(byte[] data)26 public void Write(byte[] data) 27 { 28 if(data.Length == 0) 29 { 30 this.Log(LogLevel.Warning, "Unexpected write with no data"); 31 return; 32 } 33 34 var offset = registerAddress == null ? 1 : 0; 35 registerAddress = (Registers)data[0]; 36 if(data.Length > offset) 37 { 38 foreach(var item in data.Skip(offset).Select((value, index) => new { index, value })) 39 { 40 WriteWithCRC(registerAddress.Value, item.index, item.value); 41 } 42 } 43 } 44 Read(int count = 1)45 public byte[] Read(int count = 1) 46 { 47 if(!registerAddress.HasValue) 48 { 49 this.Log(LogLevel.Error, "Trying to read without setting address"); 50 return new byte[] {}; 51 } 52 53 var result = new byte[count]; 54 for(var i = 0; i < count; ++i) 55 { 56 result[i] = ReadWithCRC(registerAddress.Value, i); 57 } 58 return result; 59 } 60 FinishTransmission()61 public void FinishTransmission() 62 { 63 registerAddress = null; 64 } 65 Reset()66 public void Reset() 67 { 68 RegistersCollection.Reset(); 69 registerAddress = null; 70 } 71 72 public uint TimeToEmpty { get; set; } 73 public uint TimeToFull { get; set; } 74 public uint CellVoltage { get; set; } 75 public uint CycleCount { get; set; } 76 77 public decimal CellTemperature 78 { 79 get => (cellTemperature * TemperatureSensitivity) - CelciusToKelvin; 80 set => cellTemperature = (uint)((value + CelciusToKelvin) / TemperatureSensitivity).Clamp(0m, uint.MaxValue); 81 } 82 83 public decimal AmbientTemperature 84 { 85 get => (ambientTemperature * TemperatureSensitivity) - CelciusToKelvin; 86 set => ambientTemperature = (uint)((value + CelciusToKelvin) / TemperatureSensitivity).Clamp(0m, uint.MaxValue); 87 } 88 89 public decimal RelativeStateOfCharge { get; set; } 90 91 public decimal FullChargeCapacity 92 { 93 get => fullChargeCapacity * CapacitySensitivity; 94 set => fullChargeCapacity = (uint)(value / CapacitySensitivity).Clamp(0, uint.MaxValue); 95 } 96 97 public decimal DesignCapacity 98 { 99 get => designCapacity * CapacitySensitivity; 100 set => designCapacity = (uint)(value / CapacitySensitivity).Clamp(0, uint.MaxValue); 101 } 102 103 public decimal RemainingCapacity 104 { 105 get => remainingCapacity * CapacitySensitivity; 106 set => remainingCapacity = (uint)(value / CapacitySensitivity).Clamp(0, uint.MaxValue); 107 } 108 109 public WordRegisterCollection RegistersCollection { get; } 110 ReadWithCRC(Registers register, int offset)111 private byte ReadWithCRC(Registers register, int offset) 112 { 113 var addressOffset = offset / 3; 114 var result = RegistersCollection.Read((byte)((int)register + addressOffset)); 115 var step = (TransactionStep)(offset % 3); 116 switch(step) 117 { 118 // First two bytes are the actual value 119 case TransactionStep.LSB: 120 case TransactionStep.MSB: 121 return step == TransactionStep.MSB ? (byte)(result >> 8) : (byte)result; 122 // Third byte is CRC of the value 123 case TransactionStep.CRC: 124 var crc = CalculateCrc8(new byte[] {(byte)(SlaveAddress << 1), (byte)register, (byte)((SlaveAddress << 1) + 1), (byte)result, (byte)(result >> 8)}); 125 return (byte)crc; 126 default: 127 throw new Exception("unreachable state"); 128 } 129 } 130 WriteWithCRC(Registers register, int offset, byte value)131 private void WriteWithCRC(Registers register, int offset, byte value) 132 { 133 var step = (TransactionStep)(offset % 3); 134 if(step == TransactionStep.CRC) 135 { 136 // We are deliberately omitting checking CRC, as the only possibility for 137 // it to be invalid, is when software generate wrong checksum for written data. 138 return; 139 } 140 var addressOffset = offset / 3; 141 var realAddress = (byte)((int)register + addressOffset); 142 var underlyingValue = RegistersCollection.Read(realAddress); 143 if(step == TransactionStep.MSB) 144 { 145 underlyingValue &= 0x00FF; 146 underlyingValue |= (ushort)((short)value << 8); 147 } 148 else 149 { 150 underlyingValue &= 0xFF00; 151 underlyingValue |= (ushort)value; 152 } 153 RegistersCollection.Write(realAddress, underlyingValue); 154 } 155 CalculateCrc8(byte[] data)156 private byte CalculateCrc8(byte[] data) 157 { 158 var result = 0x00; 159 foreach(var b in data) 160 { 161 result = (byte)(result ^ b); 162 for(var i = 0; i < 8; ++i) 163 { 164 if((result & 0x80) != 0x00) 165 { 166 // Polynomial x^8+x^2+x+1 167 result = (byte)((result << 1) ^ 0x07); 168 } 169 else 170 { 171 result = (byte)(result << 1); 172 } 173 } 174 } 175 return (byte)result; 176 } 177 DefineRegisters()178 private void DefineRegisters() 179 { 180 Registers.TimeToEmpty.Define(this) 181 .WithValueField(0, 16, FieldMode.Read, name: "TimeToEmpty", 182 valueProviderCallback: _ => TimeToEmpty) 183 ; 184 185 Registers.TimeToFull.Define(this) 186 .WithValueField(0, 16, FieldMode.Read, name: "TimeToFull", 187 valueProviderCallback: _ => TimeToFull) 188 ; 189 190 Registers.CellTemperature.Define(this) 191 .WithValueField(0, 16, FieldMode.Read, name: "CellTemperature", 192 valueProviderCallback: _ => cellTemperature) 193 ; 194 195 Registers.CellVoltage.Define(this) 196 .WithValueField(0, 16, FieldMode.Read, name: "CellVoltage", 197 valueProviderCallback: _ => CellVoltage) 198 ; 199 200 Registers.RelativeStateOfCharge.Define(this) 201 .WithValueField(0, 16, FieldMode.Read, name: "RSOC", 202 valueProviderCallback: _ => (uint)RelativeStateOfCharge.Clamp(0, 100)) 203 ; 204 205 Registers.IndicatorToEmpty.Define(this) 206 .WithValueField(0, 16, FieldMode.Read, name: "ITE", 207 valueProviderCallback: _ => (uint)(RelativeStateOfCharge * 10.0m).Clamp(0, 1000)) 208 ; 209 210 Registers.FullChargeCapacity.Define(this) 211 .WithValueField(0, 16, FieldMode.Read, name: "FullChargeCapacity", 212 valueProviderCallback: _ => fullChargeCapacity) 213 ; 214 215 Registers.ICPowerMode.Define(this, 0x01) 216 .WithValueField(0, 16, name: "ICPowerMode") 217 ; 218 219 Registers.CycleCount.Define(this) 220 .WithValueField(0, 16, FieldMode.Read, name: "CycleCount", 221 valueProviderCallback: _ => CycleCount) 222 ; 223 224 Registers.DesignCapacity.Define(this) 225 .WithValueField(0, 16, FieldMode.Read, name: "DesignCapacity", 226 valueProviderCallback: _ => designCapacity) 227 ; 228 229 Registers.NumberOfTheParameters.Define(this, 0x1001) 230 .WithTag("NumberOfTheParameters", 0, 16) 231 ; 232 233 Registers.AmbientTemperature.Define(this) 234 .WithValueField(0, 16, FieldMode.Read, name: "AmbientTemperature", 235 valueProviderCallback: _ => ambientTemperature) 236 ; 237 238 Registers.RemainingCapacity.Define(this) 239 .WithValueField(0, 16, FieldMode.Read, name: "RemainingCapacity", 240 valueProviderCallback: _ => remainingCapacity) 241 ; 242 } 243 244 private int SlaveAddress 245 { 246 get 247 { 248 if(!slaveAddress.HasValue) 249 { 250 var parentPeripheral = machine.GetParentPeripherals(this).FirstOrDefault(); 251 if(parentPeripheral == null) 252 { 253 this.Log(LogLevel.Warning, "Peripheral hasn't been connected to any I2C controller"); 254 return DefaultSlaveAddress; 255 } 256 var registrationPoint = machine.GetPeripheralRegistrationPoints(parentPeripheral, this).FirstOrDefault(); 257 if(registrationPoint == null) 258 { 259 throw new Exception("could not find registration point for this peripheral"); 260 } 261 262 if(registrationPoint is NumberRegistrationPoint<int> numberRegistrationPoint) 263 { 264 slaveAddress = numberRegistrationPoint.Address; 265 } 266 else if(registrationPoint is TypedNumberRegistrationPoint<int> typedNumberRegistrationPoint) 267 { 268 slaveAddress = typedNumberRegistrationPoint.Address; 269 } 270 else 271 { 272 throw new Exception($"SlaveAddress is unimplemented for {registrationPoint.GetType().ToString()}"); 273 } 274 } 275 return slaveAddress.Value; 276 } 277 } 278 279 private const decimal CelciusToKelvin = 273.15m; 280 private const decimal CapacitySensitivity = 0.1m; 281 private const decimal TemperatureSensitivity = 0.1m; 282 private const int DefaultSlaveAddress = 0x0B; 283 284 private readonly IMachine machine; 285 286 private Registers? registerAddress; 287 288 private int? slaveAddress; 289 290 private uint fullChargeCapacity; 291 private uint designCapacity; 292 private uint remainingCapacity; 293 private uint cellTemperature; 294 private uint ambientTemperature; 295 296 private enum TransactionStep 297 { 298 LSB, 299 MSB, 300 CRC 301 } 302 303 private enum Registers 304 { 305 Prohibited1 = 0x00, 306 Prohibited2 = 0x01, 307 // Reserved (0x02) 308 TimeToEmpty = 0x03, 309 BeforeRelativeStateOfCharge = 0x04, 310 TimeToFull = 0x05, 311 TSense1TherimistorB = 0x06, 312 InitialRelativeStateOfCharge = 0x07, 313 CellTemperature = 0x08, 314 CellVoltage = 0x09, 315 CurrentDirection = 0x0A, 316 AdjustmentPackApplication = 0x0B, 317 AdjustmentPackThermistor = 0x0C, 318 RelativeStateOfCharge = 0x0D, 319 TSense2ThermistorB = 0x0E, 320 IndicatorToEmpty = 0x0F, 321 FullChargeCapacity = 0x10, 322 ICVersion = 0x11, 323 ChangeOfTheParameter = 0x12, 324 AlarmLowRelativeStateOfCharge = 0x13, 325 AlarmLowCellVoltage = 0x14, 326 ICPowerMode = 0x15, 327 StatusBit = 0x16, 328 CycleCount = 0x17, 329 DesignCapacity = 0x18, 330 BatteryStatus = 0x19, 331 NumberOfTheParameters = 0x1A, 332 // Reserved (0x1B) 333 TerminationCurrentRate = 0x1C, 334 EmptyCellVoltage = 0x1D, 335 ITEOffset = 0x1E, 336 AlarmHighCellVoltage = 0x1F, 337 AlarmLowTemperature = 0x20, 338 AlarmHighTemperature = 0x21, 339 AlarmOverChargingCurrent = 0x22, 340 AlarmOverDischargingCurrent = 0x23, 341 TotalRuntimeLow = 0x24, 342 TotalRuntimeHigh = 0x25, 343 AccumulatedTemperatureLow = 0x26, 344 AccumulatedTemperatureHigh = 0x27, 345 AccumulatedRelativeStateOfChargeLow = 0x28, 346 AccumulatedRelativeStateOfChargeHigh = 0x29, 347 MaximumCellVoltage = 0x2A, 348 MinimumCellVoltage = 0x2B, 349 MaximumCellTemperature = 0x2C, 350 MinimumCellTemperature = 0x2D, 351 MaximumCellCurrent = 0x2E, 352 MinimumCellCurrent = 0x2F, 353 AmbientTemperature = 0x30, 354 SenseResistance = 0x31, 355 StateOfHealth = 0x32, 356 DynamicCellCurrent = 0x33, 357 AverageCellCurrent = 0x34, 358 RemainingCapacity = 0x35, 359 UserIDLow = 0x36, 360 UserIDHigh = 0x37 361 } 362 } 363 } 364