From cccef3b85c9f63d8fce01c8155fd370cf99f8d46 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 6 May 2019 08:00:22 -0700 Subject: [PATCH] boot: Implement battery driver setup. --- .../boot/source/boot_battery_driver.cpp | 298 ++++++++++++++++++ .../boot/source/boot_battery_driver.hpp | 56 ++++ .../boot/source/boot_battery_parameters.hpp | 286 +++++++++++++++++ 3 files changed, 640 insertions(+) create mode 100644 stratosphere/boot/source/boot_battery_driver.cpp create mode 100644 stratosphere/boot/source/boot_battery_driver.hpp create mode 100644 stratosphere/boot/source/boot_battery_parameters.hpp diff --git a/stratosphere/boot/source/boot_battery_driver.cpp b/stratosphere/boot/source/boot_battery_driver.cpp new file mode 100644 index 000000000..92008927f --- /dev/null +++ b/stratosphere/boot/source/boot_battery_driver.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include "boot_functions.hpp" +#include "boot_battery_driver.hpp" + +const Max17050Parameters *BatteryDriver::GetBatteryParameters() { + const u32 battery_version = Boot::GetBatteryVersion(); + const u32 battery_vendor = Boot::GetBatteryVendor(); + + if (battery_version == 2) { + if (battery_vendor == 'M') { + return &Max17050Params2M; + } else { + return &Max17050Params2; + } + } else if (battery_version == 1) { + return &Max17050Params1; + } else { + switch (battery_vendor) { + case 'M': + return &Max17050ParamsM; + case 'R': + return &Max17050ParamsR; + case 'A': + default: + return &Max17050ParamsA; + } + } +} + +Result BatteryDriver::Read(u8 addr, u16 *out) { + return Boot::ReadI2cRegister(this->i2c_session, reinterpret_cast(out), sizeof(*out), &addr, sizeof(addr)); +} + +Result BatteryDriver::Write(u8 addr, u16 val) { + return Boot::WriteI2cRegister(this->i2c_session, reinterpret_cast(&val), sizeof(val), &addr, sizeof(addr)); +} + +Result BatteryDriver::ReadWrite(u8 addr, u16 mask, u16 val) { + Result rc; + u16 cur_val; + if (R_FAILED((rc = this->Read(addr, &cur_val)))) { + return rc; + } + + const u16 new_val = (cur_val & ~mask) | val; + if (R_FAILED((rc = this->Write(addr, new_val)))) { + return rc; + } + return ResultSuccess; +} + +bool BatteryDriver::WriteValidate(u8 addr, u16 val) { + /* Nintendo doesn't seem to check errors when doing this? */ + /* It's probably okay, since the value does get validated. */ + /* That said, we will validate the read to avoid uninit data problems. */ + this->Write(addr, val); + svcSleepThread(3'000'000ul); + + u16 new_val; + return R_SUCCEEDED(this->Read(addr, &new_val)) && new_val == val; +} + +bool BatteryDriver::IsPowerOnReset() { + /* N doesn't check result... */ + u16 val = 0; + this->Read(Max17050Status, &val); + return (val & 0x0002) == 0x0002; +} + +Result BatteryDriver::LockVfSoc() { + return this->Write(Max17050SocVfAccess, 0x0000); +} + +Result BatteryDriver::UnlockVfSoc() { + return this->Write(Max17050SocVfAccess, 0x0080); +} + +Result BatteryDriver::LockModelTable() { + Result rc; + if (R_FAILED((rc = this->Write(Max17050ModelAccess0, 0x0000)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050ModelAccess1, 0x0000)))) { + return rc; + } + return ResultSuccess; +} + +Result BatteryDriver::UnlockModelTable() { + Result rc; + if (R_FAILED((rc = this->Write(Max17050ModelAccess0, 0x0059)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050ModelAccess1, 0x00C4)))) { + return rc; + } + return ResultSuccess; +} + +Result BatteryDriver::SetModelTable(const u16 *model_table) { + Result rc; + for (size_t i = 0; i < Max17050ModelChrTblSize; i++) { + if (R_FAILED((rc = this->Write(Max17050ModelChrTblStart + i, model_table[i])))) { + return rc; + } + } + + return ResultSuccess; +} + +bool BatteryDriver::IsModelTableLocked() { + bool locked = true; + + u16 cur_val = 0; + for (size_t i = 0; i < Max17050ModelChrTblSize; i++) { + this->Read(Max17050ModelChrTblStart + i, &cur_val); + locked &= (cur_val == 0); + } + + return locked; +} + +bool BatteryDriver::IsModelTableSet(const u16 *model_table) { + bool set = true; + + u16 cur_val = 0; + for (size_t i = 0; i < Max17050ModelChrTblSize; i++) { + this->Read(Max17050ModelChrTblStart + i, &cur_val); + set &= (cur_val == model_table[i]); + } + + return set; +} + +Result BatteryDriver::InitializeBatteryParameters() { + const Max17050Parameters *params = GetBatteryParameters(); + Result rc = ResultSuccess; + + if (IsPowerOnReset()) { + /* Do initial config. */ + if (R_FAILED((rc = this->ReadWrite(Max17050MiscCfg, 0x8000, 0x8000)))) { + return rc; + } + + svcSleepThread(500'000'000ul); + + if (R_FAILED((rc = this->Write(Max17050Config, 0x7210)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050FilterCfg, 0x8784)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050RelaxCfg, params->relaxcfg)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050LearnCfg, 0x2603)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050FullSocThr, params->fullsocthr)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050IAvgEmpty, params->iavgempty)))) { + return rc; + } + + /* Unlock model table, write model table. */ + do { + if (R_FAILED((rc = this->UnlockModelTable()))) { + return rc; + } + if (R_FAILED((rc = this->SetModelTable(params->modeltbl)))) { + return rc; + } + } while (!this->IsModelTableSet(params->modeltbl)); + + /* Lock model table. */ + size_t lock_i = 0; + while (true) { + lock_i++; + if (R_FAILED((rc = this->LockModelTable()))) { + return rc; + } + + if (this->IsModelTableLocked()) { + break; + } + + if (lock_i >= 8) { + /* This is regarded as guaranteed success. */ + return ResultSuccess; + } + } + + /* Write custom parameters. */ + while (!this->WriteValidate(Max17050RComp0, params->rcomp0)) { /* ... */ } + while (!this->WriteValidate(Max17050TempCo, params->tempco)) { /* ... */ } + + if (R_FAILED((rc = this->Write(Max17050IChgTerm, params->ichgterm)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050TGain, params->tgain)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050TOff, params->toff)))) { + return rc; + } + + while (!this->WriteValidate(Max17050VEmpty, params->vempty)) { /* ... */ } + while (!this->WriteValidate(Max17050QResidual00, params->qresidual00)) { /* ... */ } + while (!this->WriteValidate(Max17050QResidual10, params->qresidual10)) { /* ... */ } + while (!this->WriteValidate(Max17050QResidual20, params->qresidual20)) { /* ... */ } + while (!this->WriteValidate(Max17050QResidual30, params->qresidual30)) { /* ... */ } + + + /* Write full capacity parameters. */ + while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ } + if (R_FAILED((rc = this->Write(Max17050DesignCap, params->vffullcap)))) { + return rc; + } + while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ } + + svcSleepThread(350'000'000ul); + + /* Write VFSOC to VFSOC 0. */ + u16 vfsoc, qh; + { + if (R_FAILED((rc = this->Read(Max17050SocVf, &vfsoc)))) { + return rc; + } + if (R_FAILED((rc = this->UnlockVfSoc()))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050SocVf0, vfsoc)))) { + return rc; + } + if (R_FAILED((rc = this->Read(Max17050Qh, &qh)))) { + return rc; + } + if (R_FAILED((rc = this->Write(Max17050Qh0, qh)))) { + return rc; + } + if (R_FAILED((rc = this->LockVfSoc()))) { + return rc; + } + } + + /* Write cycles. */ + while (!this->WriteValidate(Max17050Cycles, 0x0060)) { /* ... */ } + + /* Load new capacity parameters. */ + const u16 remcap = static_cast((vfsoc * params->vffullcap) / 0x6400); + const u16 repcap = static_cast(remcap * (params->fullcap / params->vffullcap)); + const u16 dqacc = params->vffullcap / 0x10; + while (!this->WriteValidate(Max17050RemCapMix, remcap)) { /* ... */ } + while (!this->WriteValidate(Max17050RemCapRep, repcap)) { /* ... */ } + while (!this->WriteValidate(Max17050DPAcc, 0x0C80)) { /* ... */ } + while (!this->WriteValidate(Max17050DQAcc, dqacc)) { /* ... */ } + while (!this->WriteValidate(Max17050FullCap, params->fullcap)) { /* ... */ } + if (R_FAILED((rc = this->Write(Max17050DesignCap, params->vffullcap)))) { + return rc; + } + while (!this->WriteValidate(Max17050FullCapNom, params->vffullcap)) { /* ... */ } + if (R_FAILED((rc = this->Write(Max17050SocRep, vfsoc)))) { + return rc; + } + + /* Finish initialization. */ + { + u16 status; + if (R_FAILED((rc = this->Read(Max17050Status, &status)))) { + return rc; + } + while (!this->WriteValidate(Max17050Status, status & 0xFFFD)) { /* ... */ } + } + if (R_FAILED((rc = this->Write(Max17050CGain, 0x7FFF)))) { + return rc; + } + } + + return ResultSuccess; +} diff --git a/stratosphere/boot/source/boot_battery_driver.hpp b/stratosphere/boot/source/boot_battery_driver.hpp new file mode 100644 index 000000000..2ffbc1238 --- /dev/null +++ b/stratosphere/boot/source/boot_battery_driver.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "i2c_driver/i2c_api.hpp" +#include "boot_battery_parameters.hpp" + +class BatteryDriver { + private: + I2cSessionImpl i2c_session; + public: + BatteryDriver() { + I2cDriver::Initialize(); + I2cDriver::OpenSession(&this->i2c_session, I2cDevice_Max17050); + } + + ~BatteryDriver() { + I2cDriver::CloseSession(this->i2c_session); + I2cDriver::Finalize(); + } + private: + static const Max17050Parameters *GetBatteryParameters(); + + Result Read(u8 addr, u16 *out_data); + Result Write(u8 addr, u16 val); + Result ReadWrite(u8 addr, u16 mask, u16 val); + bool WriteValidate(u8 addr, u16 val); + + bool IsPowerOnReset(); + Result LockVfSoc(); + Result UnlockVfSoc(); + Result LockModelTable(); + Result UnlockModelTable(); + bool IsModelTableLocked(); + Result SetModelTable(const u16 *model_table); + bool IsModelTableSet(const u16 *model_table); + + public: + Result InitializeBatteryParameters(); +}; diff --git a/stratosphere/boot/source/boot_battery_parameters.hpp b/stratosphere/boot/source/boot_battery_parameters.hpp new file mode 100644 index 000000000..b5a644bd2 --- /dev/null +++ b/stratosphere/boot/source/boot_battery_parameters.hpp @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include + +static constexpr u8 Max17050Status = 0x00; +static constexpr u8 Max17050VAlrtThreshold = 0x01; +static constexpr u8 Max17050TAlrtThreshold = 0x02; +static constexpr u8 Max17050SocAlrtThreshold = 0x03; +static constexpr u8 Max17050AtRate = 0x04; +static constexpr u8 Max17050RemCapRep = 0x05; +static constexpr u8 Max17050SocRep = 0x06; +static constexpr u8 Max17050Age = 0x07; +static constexpr u8 Max17050Temperature = 0x08; +static constexpr u8 Max17050VCell = 0x09; +static constexpr u8 Max17050Current = 0x0A; +static constexpr u8 Max17050AverageCurrent = 0x0B; + +static constexpr u8 Max17050SocMix = 0x0D; +static constexpr u8 Max17050SocAv = 0x0E; +static constexpr u8 Max17050RemCapMix = 0x0F; +static constexpr u8 Max17050FullCap = 0x10; +static constexpr u8 Max17050Tte = 0x11; +static constexpr u8 Max17050QResidual00 = 0x12; +static constexpr u8 Max17050FullSocThr = 0x13; + + +static constexpr u8 Max17050AverageTemp = 0x16; +static constexpr u8 Max17050Cycles = 0x17; +static constexpr u8 Max17050DesignCap = 0x18; +static constexpr u8 Max17050AverageVCell = 0x19; +static constexpr u8 Max17050MaxMinTemp = 0x1A; +static constexpr u8 Max17050MaxMinVoltage = 0x1B; +static constexpr u8 Max17050MaxMinCurrent = 0x1C; +static constexpr u8 Max17050Config = 0x1D; +static constexpr u8 Max17050IChgTerm = 0x1E; +static constexpr u8 Max17050RemCapAv = 0x1F; + +static constexpr u8 Max17050Version = 0x21; +static constexpr u8 Max17050QResidual10 = 0x22; +static constexpr u8 Max17050FullCapNom = 0x23; +static constexpr u8 Max17050TempNom = 0x24; +static constexpr u8 Max17050TempLim = 0x25; + +static constexpr u8 Max17050Ain = 0x27; +static constexpr u8 Max17050LearnCfg = 0x28; +static constexpr u8 Max17050FilterCfg = 0x29; +static constexpr u8 Max17050RelaxCfg = 0x2A; +static constexpr u8 Max17050MiscCfg = 0x2B; +static constexpr u8 Max17050TGain = 0x2C; +static constexpr u8 Max17050TOff = 0x2D; +static constexpr u8 Max17050CGain = 0x2E; +static constexpr u8 Max17050COff = 0x2F; + + +static constexpr u8 Max17050QResidual20 = 0x32; + + + +static constexpr u8 Max17050IAvgEmpty = 0x36; +static constexpr u8 Max17050FCtc = 0x37; +static constexpr u8 Max17050RComp0 = 0x38; +static constexpr u8 Max17050TempCo = 0x39; +static constexpr u8 Max17050VEmpty = 0x3A; + + +static constexpr u8 Max17050FStat = 0x3D; +static constexpr u8 Max17050Timer = 0x3E; +static constexpr u8 Max17050ShdnTimer = 0x3F; + + +static constexpr u8 Max17050QResidual30 = 0x42; + + +static constexpr u8 Max17050DQAcc = 0x45; +static constexpr u8 Max17050DPAcc = 0x46; + +static constexpr u8 Max17050SocVf0 = 0x48; + +static constexpr u8 Max17050Qh0 = 0x4C; +static constexpr u8 Max17050Qh = 0x4D; + +static constexpr u8 Max17050SocVfAccess = 0x60; + +static constexpr u8 Max17050ModelAccess0 = 0x62; +static constexpr u8 Max17050ModelAccess1 = 0x63; + +static constexpr u8 Max17050ModelChrTblStart = 0x80; +static constexpr u8 Max17050ModelChrTblEnd = 0xB0; + + +static constexpr u8 Max17050VFocV = 0xFB; +static constexpr u8 Max17050SocVf = 0xFF; + +static constexpr size_t Max17050ModelChrTblSize = Max17050ModelChrTblEnd - Max17050ModelChrTblStart; + +struct Max17050Parameters { + u16 relaxcfg; + u16 rcomp0; + u16 tempco; + u16 ichgterm; + u16 tgain; + u16 toff; + u16 vempty; + u16 qresidual00; + u16 qresidual10; + u16 qresidual20; + u16 qresidual30; + u16 fullcap; + u16 vffullcap; + u16 modeltbl[Max17050ModelChrTblSize]; + u16 fullsocthr; + u16 iavgempty; +}; + +static_assert(sizeof(Max17050Parameters) == 0x7E, "Max17050Parameters definition!"); + +static constexpr Max17050Parameters Max17050ParamsA = { + 0x203B, /* relaxcfg */ + 0x0053, /* rcomp0 */ + 0x1C22, /* tempco */ + 0x0333, /* ichgterm */ + 0xE1F6, /* tgain */ + 0x2BF2, /* toff */ + 0xA05F, /* vempty */ + 0x5786, /* qresidual00 */ + 0x3184, /* qresidual10 */ + 0x1E00, /* qresidual20 */ + 0x1602, /* qresidual30 */ + 0x2476, /* fullcap */ + 0x2476, /* vffullcap */ + { /* modeltbl */ + 0x9FF0, 0xAD30, 0xB5D0, 0xB9C0, 0xBAD0, 0xBBE0, 0xBC30, 0xBC90, + 0xBCE0, 0xBD40, 0xBE70, 0xC0E0, 0xC4E0, 0xC890, 0xCC90, 0xD0F0, + 0x0170, 0x0480, 0x0590, 0x0BE0, 0x0A00, 0x3C00, 0x3810, 0x3A00, + 0x3A30, 0x19F0, 0x0EF0, 0x0AF0, 0x0BD0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + 0x5F00, /* fullsocthr */ + 0x1D2A /* iavgempty */ +}; + +static constexpr Max17050Parameters Max17050ParamsM = { + 0x203B, /* relaxcfg */ + 0x0085, /* rcomp0 */ + 0x1625, /* tempco */ + 0x0333, /* ichgterm */ + 0xE1F6, /* tgain */ + 0x2BF2, /* toff */ + 0xA05F, /* vempty */ + 0x3100, /* qresidual00 */ + 0x1B00, /* qresidual10 */ + 0x1000, /* qresidual20 */ + 0x0C81, /* qresidual30 */ + 0x227A, /* fullcap */ + 0x227A, /* vffullcap */ + { /* modeltbl */ + 0xA340, 0xB840, 0xB900, 0xBB70, 0xBC90, 0xBD20, 0xBDC0, 0xBEA0, + 0xBF70, 0xC030, 0xC210, 0xC3F0, 0xC800, 0xC9E0, 0xCCA0, 0xD090, + 0x0160, 0x3800, 0x0800, 0x1E00, 0x2550, 0x3060, 0x15D0, 0x1810, + 0x1490, 0x0B80, 0x0BF0, 0x0AF0, 0x0CB0, 0x06F0, 0x09D0, 0x09D0, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, + }, + 0x5F00, /* fullsocthr */ + 0x1D2A /* iavgempty */ +}; + +static constexpr Max17050Parameters Max17050ParamsR = { + 0x203B, /* relaxcfg */ + 0x0048, /* rcomp0 */ + 0x2034, /* tempco */ + 0x0333, /* ichgterm */ + 0xE1F6, /* tgain */ + 0x2BF2, /* toff */ + 0xA05F, /* vempty */ + 0x5A00, /* qresidual00 */ + 0x3B00, /* qresidual10 */ + 0x0F80, /* qresidual20 */ + 0x0B02, /* qresidual30 */ + 0x2466, /* fullcap */ + 0x2466, /* vffullcap */ + { /* modeltbl */ + 0x9C50, 0xAD90, 0xB270, 0xB6A0, 0xB8F0, 0xBB10, 0xBC00, 0xBD00, + 0xBD70, 0xBE70, 0xBF50, 0xC1F0, 0xC380, 0xC590, 0xC8E0, 0xD0B0, + 0x00D0, 0x0150, 0x0300, 0x0D00, 0x0E00, 0x1900, 0x2AC0, 0x2830, + 0x1760, 0x18F0, 0x0DF0, 0x0BC0, 0x0DF0, 0x0BF0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + 0x5F00, /* fullsocthr */ + 0x1D2A /* iavgempty */ +}; + +static constexpr Max17050Parameters Max17050Params1 = { + 0x203B, /* relaxcfg */ + 0x0040, /* rcomp0 */ + 0x1624, /* tempco */ + 0x0333, /* ichgterm */ + 0xE1F6, /* tgain */ + 0x2BF2, /* toff */ + 0xA05F, /* vempty */ + 0x4690, /* qresidual00 */ + 0x2605, /* qresidual10 */ + 0x1605, /* qresidual20 */ + 0x0F05, /* qresidual30 */ + 0x1AE4, /* fullcap */ + 0x1AE4, /* vffullcap */ + { /* modeltbl */ + 0x8B50, 0x9C20, 0xACF0, 0xB160, 0xB3A0, 0xB5B0, 0xB950, 0xBBE0, + 0xBDC0, 0xBEF0, 0xC140, 0xC250, 0xC600, 0xC960, 0xCCE0, 0xD060, + 0x0070, 0x00F0, 0x0440, 0x0400, 0x0500, 0x0400, 0x0D00, 0x3270, + 0x0FB0, 0x0AF0, 0x10F0, 0x0CE0, 0x09E0, 0x07F0, 0x06F0, 0x06F0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + 0x5F00, /* fullsocthr */ + 0x1584 /* iavgempty */ +}; + +static constexpr Max17050Parameters Max17050Params2 = { + 0x203B, /* relaxcfg */ + 0x004A, /* rcomp0 */ + 0x1D23, /* tempco */ + 0x0333, /* ichgterm */ + 0xE1F6, /* tgain */ + 0x2BF2, /* toff */ + 0xA05F, /* vempty */ + 0x4000, /* qresidual00 */ + 0x1E80, /* qresidual10 */ + 0x0D83, /* qresidual20 */ + 0x0783, /* qresidual30 */ + 0x1C20, /* fullcap */ + 0x1C20, /* vffullcap */ + { /* modeltbl */ + 0x8040, 0x9A30, 0xB430, 0xB770, 0xBAB0, 0xBBC0, 0xBD00, 0xBE50, + 0xBF70, 0xC0D0, 0xC300, 0xC590, 0xC960, 0xCD40, 0xD1F0, 0xD5C0, + 0x0040, 0x0060, 0x0510, 0x0D30, 0x16C0, 0x2160, 0x1380, 0x1A10, + 0x0EC0, 0x0CE0, 0x08F0, 0x0940, 0x0920, 0x06F0, 0x06C0, 0x06C0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + 0x5500, /* fullsocthr */ + 0x1680 /* iavgempty */ +}; + +static constexpr Max17050Parameters Max17050Params2M = { + 0x203B, /* relaxcfg */ + 0x0049, /* rcomp0 */ + 0x222A, /* tempco */ + 0x0333, /* ichgterm */ + 0xE1F6, /* tgain */ + 0x2BF2, /* toff */ + 0xA05F, /* vempty */ + 0x4F00, /* qresidual00 */ + 0x2680, /* qresidual10 */ + 0x1205, /* qresidual20 */ + 0x0C87, /* qresidual30 */ + 0x1C68, /* fullcap */ + 0x1C68, /* vffullcap */ + { /* modeltbl */ + 0x8E40, 0xB570, 0xB8F0, 0xBB00, 0xBC20, 0xBCC0, 0xBE30, 0xBFE0, + 0xC200, 0xC400, 0xC720, 0xCB50, 0xCF00, 0xD100, 0xD480, 0xD5C0, + 0x00C0, 0x0C00, 0x0A10, 0x1800, 0x2C00, 0x1C10, 0x12D0, 0x09F0, + 0x0AF0, 0x0850, 0x09F0, 0x06F0, 0x06B0, 0x07E0, 0x01D0, 0x01D0, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, + }, + 0x5500, /* fullsocthr */ + 0x16B9 /* iavgempty */ +};