blob: bc79522524963145960100f094eb07ac88cfcf5b [file] [log] [blame]
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright 2019 Google, LLC
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that 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.
*/
#ifndef MAX77779_FG_MODEL_H_
#define MAX77779_FG_MODEL_H_
#include "maxfg_common.h"
#include "max77779.h"
/* change to 1 or 0 to load FG model with default parameters on startup */
#define MAX77779_FG_LOAD_MODEL_DISABLED -1
#define MAX77779_FG_LOAD_MODEL_IDLE 0
#define MAX77779_FG_LOAD_MODEL_REQUEST 1
#define MAX77779_FG_MODEL_START MAX77779_FG_OCV0
#define MAX77779_FG_MODEL_SIZE 32
/* model version */
#define MAX77779_FG_INVALID_VERSION -1
/* Config2: must not enable TAlert */
#define MAX77779_FG_MODEL_VERSION_REG MAX77779_FG_TAlrtTh
#define MAX77779_FG_NDGB_ADDRESS 0x37
#define MAX77779_FG_MAX_LOG_REGS 30
static const struct maxfg_reg max77779_fg[] = {
[MAXFG_TAG_avgc] = { ATOM_INIT_REG16(MAX77779_FG_AvgCurrent)},
[MAXFG_TAG_cnfg] = { ATOM_INIT_REG16(MAX77779_FG_Config)},
[MAXFG_TAG_mmdv] = { ATOM_INIT_REG16(MAX77779_FG_MaxMinVolt)},
[MAXFG_TAG_vcel] = { ATOM_INIT_REG16(MAX77779_FG_VCell)},
[MAXFG_TAG_temp] = { ATOM_INIT_REG16(MAX77779_FG_Temp)},
[MAXFG_TAG_curr] = { ATOM_INIT_REG16(MAX77779_FG_Current)},
[MAXFG_TAG_mcap] = { ATOM_INIT_REG16(MAX77779_FG_MixCap)},
[MAXFG_TAG_vfsoc] = { ATOM_INIT_REG16(MAX77779_FG_VFSOC)},
[MAXFG_TAG_tempco] = { ATOM_INIT_REG16(MAX77779_FG_NVM_nTempCo)},
[MAXFG_TAG_rcomp0] = { ATOM_INIT_REG16(MAX77779_FG_NVM_nRComp0)},
[MAXFG_TAG_timerh] = { ATOM_INIT_REG16(MAX77779_FG_TimerH)},
[MAXFG_TAG_descap] = { ATOM_INIT_REG16(MAX77779_FG_DesignCap)},
[MAXFG_TAG_fcnom] = { ATOM_INIT_REG16(MAX77779_FG_FullCapNom)},
[MAXFG_TAG_fcrep] = { ATOM_INIT_REG16(MAX77779_FG_FullCapRep)},
[MAXFG_TAG_msoc] = { ATOM_INIT_REG16(MAX77779_FG_MixSOC)},
[MAXFG_TAG_mmdt] = { ATOM_INIT_REG16(MAX77779_FG_MaxMinTemp)},
[MAXFG_TAG_mmdc] = { ATOM_INIT_REG16(MAX77779_FG_MaxMinCurr)},
[MAXFG_TAG_repsoc] = { ATOM_INIT_REG16(MAX77779_FG_RepSOC)},
[MAXFG_TAG_avcap] = { ATOM_INIT_REG16(MAX77779_FG_AvCap)},
[MAXFG_TAG_repcap] = { ATOM_INIT_REG16(MAX77779_FG_RepCap)},
[MAXFG_TAG_fulcap] = { ATOM_INIT_REG16(MAX77779_FG_FullCap)},
[MAXFG_TAG_qh0] = { ATOM_INIT_REG16(MAX77779_FG_QH0)},
[MAXFG_TAG_qh] = { ATOM_INIT_REG16(MAX77779_FG_QH)},
[MAXFG_TAG_dqacc] = { ATOM_INIT_REG16(MAX77779_FG_dQAcc)},
[MAXFG_TAG_dpacc] = { ATOM_INIT_REG16(MAX77779_FG_dPAcc)},
[MAXFG_TAG_qresd] = { ATOM_INIT_REG16(MAX77779_FG_QResidual)},
[MAXFG_TAG_fstat] = { ATOM_INIT_REG16(MAX77779_FG_FStat)},
[MAXFG_TAG_learn] = { ATOM_INIT_REG16(MAX77779_FG_LearnCfg)},
[MAXFG_TAG_filcfg] = { ATOM_INIT_REG16(MAX77779_FG_NVM_nFilterCfg)},
[MAXFG_TAG_vfcap] = { ATOM_INIT_REG16(MAX77779_FG_VFRemCap)},
[MAXFG_TAG_cycles] = { ATOM_INIT_REG16(MAX77779_FG_Cycles)},
[MAXFG_TAG_rslow] = { ATOM_INIT_REG16(MAX77779_FG_RSlow)},
[MAXFG_TAG_vfocv] = { ATOM_INIT_REG16(MAX77779_FG_VFOCV)},
[MAXFG_TAG_avgt] = { ATOM_INIT_REG16(MAX77779_FG_AvgTA)},
[MAXFG_TAG_avgv] = { ATOM_INIT_REG16(MAX77779_FG_AvgVCell)},
[MAXFG_TAG_mixcap] = { ATOM_INIT_REG16(MAX77779_FG_MixCap)},
[MAXFG_TAG_vfremcap] = { ATOM_INIT_REG16(MAX77779_FG_VFRemCap)},
[MAXFG_TAG_vfsoc0] = { ATOM_INIT_REG16(MAX77779_FG_VFSOC0)},
[MAXFG_TAG_qrtable00] = { ATOM_INIT_REG16(MAX77779_FG_QRTable00)},
[MAXFG_TAG_qrtable10] = { ATOM_INIT_REG16(MAX77779_FG_QRTable10)},
[MAXFG_TAG_qrtable20] = { ATOM_INIT_REG16(MAX77779_FG_QRTable20)},
[MAXFG_TAG_qrtable30] = { ATOM_INIT_REG16(MAX77779_FG_QRTable30)},
[MAXFG_TAG_status] = { ATOM_INIT_REG16(MAX77779_FG_Status)},
};
static const struct maxfg_reg max77779_debug_fg[] = {
[MAXFG_TAG_tempco] = { ATOM_INIT_REG16(MAX77779_FG_NVM_nTempCo)},
[MAXFG_TAG_rcomp0] = { ATOM_INIT_REG16(MAX77779_FG_NVM_nRComp0)},
[MAXFG_TAG_filcfg] = { ATOM_INIT_REG16(MAX77779_FG_NVM_nFilterCfg)},
[MAXFG_TAG_relaxcfg] = { ATOM_INIT_REG16(MAX77779_FG_NVM_RelaxCFG)},
};
struct max77779_fg_chip {
struct device *dev;
struct i2c_client *secondary;
struct device *pmic_dev;
int irq;
struct maxfg_regmap regmap;
struct maxfg_regmap regmap_debug;
struct power_supply *psy;
struct delayed_work init_work;
struct device_node *batt_node;
u16 devname;
/* config */
void *model_data;
struct mutex model_lock;
struct delayed_work model_work;
int model_next_update;
/* also used to restore model state from permanent storage */
u16 reg_prop_capacity_raw;
int model_reload;
bool model_ok; /* model is running */
int fake_battery;
u16 RSense;
u16 RConfig;
int batt_id;
int batt_id_defer_cnt;
int cycle_count;
u16 eeprom_cycle;
u16 designcap;
bool init_complete;
bool resume_complete;
bool irq_disabled;
u16 health_status;
int fake_capacity;
int previous_qh;
int current_capacity;
int prev_charge_status;
char serial_number[30];
bool offmode_charger;
bool por;
unsigned int debug_irq_none_cnt;
/* Capacity Estimation */
struct gbatt_capacity_estimation cap_estimate;
struct logbuffer *ce_log;
/* Dynamic Relax */
struct maxfg_dynrel_state dynrel_state;
/* debug interface, register to read or write */
u32 debug_reg_address;
u32 debug_dbg_reg_address;
/* dump data to logbuffer periodically */
struct logbuffer *monitor_log;
u16 pre_repsoc;
struct gbms_desc max77779_fg_psy_desc;
int bhi_fcn_count;
int bhi_acim;
/* battery current criteria for report status charge */
u32 status_charge_threshold_ma;
bool current_offset_check_done;
bool fw_update_mode;
/* in-field logging */
unsigned int abnormal_event_bits;
u16 last_fullcapnom;
struct mutex check_event_lock;
/* firmware revision */
int fw_rev;
int fw_sub_rev;
/* total number of model loading attempts counter since boot */
int ml_cnt;
/* total number of model loading failures since boot */
int ml_fails;
/* buffer for recording learning history */
struct maxfg_capture_buf cb_lh;
/* get suspend/resume notification */
struct mutex save_data_lock;
struct wakeup_source *fg_wake_lock;
struct delayed_work stuck_monitor_work;
int fg_stuck_count;
u16 timer;
/* mutex lock to access FG USR reg */
struct mutex usr_lock;
/* AAFV: Aged Adjusted Float Voltage */
int aafv;
};
/** ------------------------------------------------------------------------ */
/*
* Custom parameters are updated while the device is running.
* NOTE: a subset (model_state_save) is saved to permanent storage every "n"
* cycles and restored when the model is reloaded (usually on POR).
* TODO: handle switching between RC1 and RC2 model types.
*/
struct max77779_custom_parameters {
u16 nvcfg0;
u16 relaxcfg;
u16 learncfg;
u16 config;
u16 config2;
u16 fullsocthr;
u16 fullcaprep; /* WV */
u16 designcap;
u16 dpacc; /* WV */
u16 fullcapnom; /* WV */
u16 v_empty;
u16 qresidual00; /* WV */
u16 qresidual10; /* WV */
u16 qresidual20; /* WV */
u16 qresidual30; /* WV */
u16 rcomp0; /* WV */
u16 tempco; /* WV */
u16 ichgterm;
u16 misccfg; /* 0x9d0 for internal current sense, 0x8d0 external */
u16 modelcfg;
u16 thermcfg;
u16 filtercfg;
} __attribute__((packed));
/* this is what is saved and restored to/from GMSR */
struct model_state_save {
u16 qrtable00;
u16 qrtable10;
u16 qrtable20;
u16 qrtable30;
u16 fullcapnom;
u16 fullcaprep;
u16 rcomp0;
u16 tempco;
u16 cycles;
u8 padding[4]; /* keep the same size as 59 for consistency GBMS_GMSR_LEN */
u8 crc;
} __attribute__((packed));
struct max77779_model_data {
struct device *dev;
struct maxfg_regmap *regmap;
struct maxfg_regmap *debug_regmap;
/* initial parameters are in device tree they are also learned */
struct max77779_custom_parameters parameters;
u16 cycles;
u16 cv_mixcap;
u16 hibcfg;
int custom_model_size;
u16 *custom_model;
u32 model_version;
bool force_reset_model_data;
/* to/from GMSR */
struct model_state_save model_save;
};
/** ------------------------------------------------------------------------ */
int max77779_model_read_version(const struct max77779_model_data *model_data);
int max77779_model_write_version(const struct max77779_model_data *model_data, int version);
int max77779_model_get_cap_lsb(const struct max77779_model_data *model_data);
int max77779_reset_state_data(struct max77779_model_data *model_data);
int max77779_needs_reset_model_data(const struct max77779_model_data *model_data);
u16 max77779_get_designcap(const struct max77779_model_data *model_data);
u16 max77779_get_relaxcfg(const struct max77779_model_data *model_data);
/*
* max77779 might use the low 8 bits of devname to keep the model version number
* - 0 not M5, !=0 M5
*/
static inline int max77779_check_devname(u16 devname)
{
const u16 radix = devname >> 8;
return radix == 0x62 || radix == 0x63 || radix == 0x51;
}
static inline int max77779_fg_model_version(const struct max77779_model_data *model_data)
{
return model_data ? model_data->model_version : MAX77779_FG_INVALID_VERSION;
}
/*
* 0 reload, != 0 no reload
* always reload when the model version is not specified
*/
static inline int max77779_fg_model_check_version(const struct max77779_model_data *model_data)
{
/* if model version is invalid, no reload */
if (!model_data || model_data->model_version == MAX77779_FG_INVALID_VERSION)
return 1;
return max77779_model_read_version(model_data) == model_data->model_version;
}
enum max77779_fg_reg_sections {
MAX77779_FG_RAM_SECTION,
MAX77779_FG_FUNC_SECTION,
MAX77779_FG_NVM_SECTION,
MAX77779_FG_ALL_SECTION,
MAX77779_FG_UNKNOWN_SECTION,
};
/* TODO: b/325642439 add protection during model loading and firmware update */
#define MAX77779_FG_REGMAP_WRITE(regmap, what, value) \
max77779_fg_register_write(regmap, what, value, false)
#define MAX77779_FG_REGMAP_WRITE_VERIFY(regmap, what, value) \
max77779_fg_register_write(regmap, what, value, true)
#define MAX77779_FG_N_REGMAP_WRITE(regmap, nregmap, what, value) \
max77779_fg_nregister_write(regmap, nregmap, what, value, false)
#define MAX77779_FG_N_REGMAP_WRITE_VERIFY(regmap, nregmap, what, value) \
max77779_fg_nregister_write(regmap, nregmap, what, value, true)
/** ------------------------------------------------------------------------ */
int max77779_fg_usr_lock_section(const struct maxfg_regmap *map, enum max77779_fg_reg_sections section, bool enabled);
int max77779_fg_register_write(const struct maxfg_regmap *regmap, unsigned int reg,
u16 value, bool verify);
int max77779_fg_nregister_write(const struct maxfg_regmap *map,
const struct maxfg_regmap *debug_map,
unsigned int reg, u16 value, bool verify);
void *max77779_init_data(struct device *dev, struct device_node *batt_node,
struct maxfg_regmap *regmap, struct maxfg_regmap *debug_regmap);
void max77779_free_data(struct max77779_model_data *model_data);
int max77779_load_state_data(struct max77779_model_data *model_data);
int max77779_save_state_data(struct max77779_model_data *model_data);
/* read state from the gauge */
int max77779_model_read_state(struct max77779_model_data *model_data);
int max77779_model_check_state(struct max77779_model_data *model_data);
/* load model to gauge */
int max77779_load_gauge_model(struct max77779_model_data *model_data, int fw_rev, int fw_sub_rev);
ssize_t max77779_model_state_cstr(char *buf, int max, struct max77779_model_data *model_data);
int max77779_fg_param_cstr(char *buf, int max, const struct max77779_model_data *model_data);
int max77779_fg_param_sscan(struct max77779_model_data *model_data, const char *buf, int max);
int max77779_fg_model_cstr(char *buf, int max, const struct max77779_model_data *model_data);
int max77779_fg_model_sscan(struct max77779_model_data *model_data, const char *buf, int max);
bool max77779_fg_check_state(struct max77779_model_data *model_data);
/* read saved value */
ssize_t max77779_gmsr_state_cstr(char *buf, int max);
/** ------------------------------------------------------------------------ */
void *max77779_get_model_data(struct device *dev);
int max77779_fg_init(struct max77779_fg_chip *chip);
bool max77779_fg_dbg_is_reg(struct device *dev, unsigned int reg);
bool max77779_fg_is_reg(struct device *dev, unsigned int reg);
void max77779_fg_remove(struct max77779_fg_chip *chip);
#if IS_ENABLED(CONFIG_PM)
int max77779_fg_pm_suspend(struct device *dev);
int max77779_fg_pm_resume(struct device *dev);
#endif
#endif