| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Google Battery Management System |
| * |
| * Copyright 2020 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 MAX1720X_BATTERY_H_ |
| #define MAX1720X_BATTERY_H_ |
| |
| #include <linux/device.h> |
| #include <linux/regmap.h> |
| #include <linux/math64.h> |
| |
| #define MAX1720X_GAUGE_TYPE 0 |
| #define MAX1730X_GAUGE_TYPE 1 |
| #define MAX_M5_GAUGE_TYPE 2 |
| |
| #define EEPROM_SN 0 |
| #define MAX1720X_SN 1 |
| |
| /* multiply by 2 when task period = 351 ms */ |
| static inline int reg_to_micro_amp_h(s16 val, u16 rsense, int lsb) |
| { |
| /* LSB: 5.0μVh/RSENSE ; Rsense LSB is 10μΩ */ |
| return div_s64((s64) val * 500000, rsense) * lsb; |
| } |
| |
| /* divide by 2 when task period = 351 ms */ |
| static inline s16 micro_amp_h_to_reg(int val, u16 rsense, int lsb) |
| { |
| /* LSB: 5.0μVh/RSENSE ; Rsense LSB is 10μΩ */ |
| return div_s64((s64)(val / lsb) * rsense, 500000); |
| } |
| |
| static inline int reg_to_micro_volt(u16 val) |
| { |
| /* LSB: 0.078125mV */ |
| return div_u64((u64) val * 78125, 1000); |
| } |
| |
| enum max17x0x_reg_tags { |
| MAX17X0X_TAG_avgc, |
| MAX17X0X_TAG_cnfg, |
| MAX17X0X_TAG_mmdv, |
| MAX17X0X_TAG_vcel, |
| MAX17X0X_TAG_temp, |
| MAX17X0X_TAG_curr, |
| MAX17X0X_TAG_mcap, |
| MAX17X0X_TAG_avgr, |
| MAX17X0X_TAG_vfsoc, |
| MAX17X0X_TAG_vfocv, |
| |
| MAX17X0X_TAG_BCNT, |
| MAX17X0X_TAG_SNUM, |
| MAX17X0X_TAG_HSTY, |
| MAX17X0X_TAG_BCEA, |
| MAX17X0X_TAG_rset, |
| MAX17X0X_TAG_BRES, |
| |
| MAXFG_TAG_fcnom, |
| MAXFG_TAG_dpacc, |
| MAXFG_TAG_dqacc, |
| MAXFG_TAG_fcrep, |
| MAXFG_TAG_repsoc, |
| MAXFG_TAG_msoc, |
| MAXFG_TAG_learn, |
| MAXFG_TAG_rcomp0, |
| MAXFG_TAG_tempco, |
| MAXFG_TAG_fstat, |
| MAXFG_TAG_avgt, |
| MAXFG_TAG_temp, |
| MAXFG_TAG_qh, |
| MAXFG_TAG_vcel, |
| MAXFG_TAG_avgv, |
| MAXFG_TAG_vfocv, |
| }; |
| |
| enum max17x0x_reg_types { |
| GBMS_ATOM_TYPE_MAP = 0, |
| GBMS_ATOM_TYPE_REG = 1, |
| GBMS_ATOM_TYPE_ZONE = 2, |
| GBMS_ATOM_TYPE_SET = 3, |
| }; |
| |
| /* this is a map for u16 registers */ |
| #define ATOM_INIT_MAP(...) \ |
| .type = GBMS_ATOM_TYPE_MAP, \ |
| .size = 2 * sizeof((u8[]){__VA_ARGS__}),\ |
| .map = (u8[]){__VA_ARGS__} |
| |
| #define ATOM_INIT_REG16(r) \ |
| .type = GBMS_ATOM_TYPE_REG, \ |
| .size = 2, \ |
| .reg = r |
| |
| #define ATOM_INIT_ZONE(start, sz) \ |
| .type = GBMS_ATOM_TYPE_ZONE, \ |
| .size = sz, \ |
| .base = start |
| |
| /* a set has no storage and cannot be used in load/store */ |
| #define ATOM_INIT_SET(...) \ |
| .type = GBMS_ATOM_TYPE_SET, \ |
| .size = 0, \ |
| .map = (u8[]){__VA_ARGS__} |
| |
| #define ATOM_INIT_SET16(...) \ |
| .type = GBMS_ATOM_TYPE_SET, \ |
| .size = 0, \ |
| .map16 = (u16[]){__VA_ARGS__} |
| |
| struct max17x0x_reg { |
| int type; |
| int size; |
| union { |
| unsigned int base; |
| unsigned int reg; |
| const u16 *map16; |
| const u8 *map; |
| }; |
| }; |
| |
| struct max17x0x_cache_data { |
| struct max17x0x_reg atom; |
| u16 *cache_data; |
| }; |
| |
| #define NB_REGMAP_MAX 256 |
| |
| struct max17x0x_reglog { |
| u16 data[NB_REGMAP_MAX]; |
| DECLARE_BITMAP(valid, NB_REGMAP_MAX); |
| int errors[NB_REGMAP_MAX]; |
| int count[NB_REGMAP_MAX]; |
| }; |
| |
| struct max17x0x_regtags { |
| const struct max17x0x_reg *map; |
| unsigned int max; |
| }; |
| |
| struct max17x0x_regmap { |
| struct regmap *regmap; |
| struct max17x0x_regtags regtags; |
| struct max17x0x_reglog *reglog; |
| }; |
| |
| static inline const struct max17x0x_reg *max17x0x_find_by_index(struct max17x0x_regtags *tags, |
| int index) |
| { |
| if (index < 0 || !tags || index >= tags->max) |
| return NULL; |
| |
| return &tags->map[index]; |
| } |
| |
| static inline const struct max17x0x_reg *max17x0x_find_by_tag(struct max17x0x_regmap *map, |
| enum max17x0x_reg_tags tag) |
| { |
| return max17x0x_find_by_index(&map->regtags, tag); |
| } |
| |
| static inline int max17x0x_reg_read(struct max17x0x_regmap *map, |
| enum max17x0x_reg_tags tag, |
| u16 *val) |
| { |
| const struct max17x0x_reg *reg; |
| unsigned int tmp; |
| int rtn; |
| |
| reg = max17x0x_find_by_tag(map, tag); |
| if (!reg) |
| return -EINVAL; |
| |
| rtn = regmap_read(map->regmap, reg->reg, &tmp); |
| if (rtn == 0) |
| *val = tmp; |
| |
| return rtn; |
| } |
| |
| |
| int max1720x_get_capacity(struct i2c_client *client, int *iic_raw); |
| int max1720x_get_voltage_now(struct i2c_client *client, int *iic_raw); |
| int max17x0x_sw_reset(struct i2c_client *client); |
| |
| /* */ |
| #ifdef CONFIG_MAX1720X_REGLOG_LOG |
| static inline void max17x0x_reglog_log(struct max17x0x_reglog *reglog, |
| unsigned int reg, u16 data, int rtn) |
| { |
| if (!reglog) |
| return; |
| |
| reglog->count[reg] += 1; |
| if (rtn != 0) { |
| reglog->errors[reg] += 1; |
| } else { |
| __set_bit(reg, reglog->valid); |
| reglog->data[reg] = data; |
| } |
| |
| } |
| |
| #else |
| static inline void max17x0x_reglog_log(struct max17x0x_reglog *reglog, |
| unsigned int reg, u16 data, int rtn) |
| { |
| |
| } |
| #endif |
| |
| static inline int max17x0x_regmap_read(const struct max17x0x_regmap *map, |
| unsigned int reg, |
| u16 *val, |
| const char *name) |
| { |
| int rtn; |
| unsigned int tmp; |
| |
| if (!map->regmap) { |
| pr_err("Failed to read %s, no regmap\n", name); |
| return -EIO; |
| } |
| |
| rtn = regmap_read(map->regmap, reg, &tmp); |
| if (rtn) |
| pr_err("Failed to read %s\n", name); |
| else |
| *val = tmp; |
| |
| return rtn; |
| } |
| |
| #define REGMAP_READ(regmap, what, dst) \ |
| max17x0x_regmap_read(regmap, what, dst, #what) |
| |
| static inline int max17x0x_regmap_write(const struct max17x0x_regmap *map, |
| unsigned int reg, |
| u16 data, |
| const char *name) |
| { |
| int rtn; |
| |
| if (!map->regmap) { |
| pr_err("Failed to write %s, no regmap\n", name); |
| return -EIO; |
| } |
| |
| rtn = regmap_write(map->regmap, reg, data); |
| if (rtn) |
| pr_err("Failed to write %s\n", name); |
| |
| max17x0x_reglog_log(map->reglog, reg, data, rtn); |
| |
| return rtn; |
| } |
| |
| #define REGMAP_WRITE(regmap, what, value) \ |
| max17x0x_regmap_write(regmap, what, value, #what) |
| |
| #define WAIT_VERIFY (10 * USEC_PER_MSEC) /* 10 msec */ |
| static inline int max1720x_regmap_writeverify(const struct max17x0x_regmap *map, |
| unsigned int reg, |
| u16 data, |
| const char *name) |
| { |
| int tmp, ret, retries; |
| |
| if (!map->regmap) { |
| pr_err("Failed to write %s, no regmap\n", name); |
| return -EINVAL; |
| } |
| |
| for (retries = 3; retries > 0; retries--) { |
| ret = regmap_write(map->regmap, reg, data); |
| if (ret < 0) |
| continue; |
| |
| usleep_range(WAIT_VERIFY, WAIT_VERIFY + 100); |
| |
| ret = regmap_read(map->regmap, reg, &tmp); |
| if (ret < 0) |
| continue; |
| |
| if (tmp == data) |
| return 0; |
| } |
| |
| return -EIO; |
| } |
| |
| #define REGMAP_WRITE_VERIFY(regmap, what, value) \ |
| max1720x_regmap_writeverify(regmap, what, value, #what) |
| |
| enum max1720x_drift_algo_version { |
| MAX1720X_DA_VER_NONE = -1, /* MW RC2 */ |
| MAX1720X_DA_VER_ORIG = 0, /* MW A0, max1720x */ |
| MAX1720X_DA_VER_MWA1 = 1, /* MW A1 RC1 */ |
| MAX1720X_DA_VER_MWA2 = 2, /* MW A2 RC1 */ |
| }; |
| |
| #define max1720x_check_drift_enabled(dd) \ |
| ((dd)->algo_ver >= MAX1720X_DA_VER_ORIG) |
| #define max1720x_check_drift_on_soc(dd) \ |
| ((dd)->algo_ver == MAX1720X_DA_VER_MWA1) |
| #define max1720x_check_drift_delay(dd) \ |
| ((dd)->algo_ver == MAX1720X_DA_VER_MWA1 ? 351 : 0) |
| |
| /* fix to capacity estimation */ |
| struct max1720x_drift_data { |
| u16 rsense; |
| enum max1720x_drift_algo_version algo_ver; |
| |
| u16 design_capacity; |
| int cycle_band; |
| int cycle_fade; |
| int cycle_stable; |
| int ini_rcomp0; |
| int ini_tempco; |
| int ini_filtercfg; |
| }; |
| |
| struct max1720x_dyn_filtercfg { |
| s32 temp; |
| s32 hysteresis; |
| u16 curr_val; |
| u16 default_val; |
| u16 adjust_val; |
| struct mutex lock; |
| bool disable_dynamic_filtercfg; |
| }; |
| |
| extern int max1720x_fixup_comp(struct max1720x_drift_data *ddata, |
| struct max17x0x_regmap *map, |
| int plugged); |
| extern int max1720x_fixup_dxacc(struct max1720x_drift_data *ddata, |
| struct max17x0x_regmap *map, |
| int cycle_count, |
| int plugged, |
| int lsb); |
| |
| #endif |