// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. * Author Wy Chuang */ #include #include #include #include #include #include #include /* PMIC MFD core header */ #include #include #include #include #include #include "mtk_battery.h" #include "mtk_gauge.h" /* ============================================================ */ /* pmic control start*/ /* ============================================================ */ #define MT6359_FGADC_CON1 0xd0a #define PMIC_HWCID_ADDR 0x8 #define PMIC_HWCID_MASK 0xFFFF #define PMIC_HWCID_SHIFT 0 #define PMIC_AUXADC_NAG_PRD_SEL_ADDR 0x11be #define PMIC_AUXADC_NAG_PRD_SEL_MASK 0x3 #define PMIC_AUXADC_NAG_PRD_SEL_SHIFT 3 #define PMIC_FG_LATCHDATA_ST_ADDR 0xd0a #define PMIC_FG_LATCHDATA_ST_MASK 0x1 #define PMIC_FG_LATCHDATA_ST_SHIFT 15 #define PMIC_FG_SW_CLEAR_ADDR 0xd0a #define PMIC_FG_SW_CLEAR_MASK 0x1 #define PMIC_FG_SW_CLEAR_SHIFT 3 #define PMIC_FG_SW_READ_PRE_ADDR 0xd0a #define PMIC_FG_SW_READ_PRE_MASK 0x1 #define PMIC_FG_SW_READ_PRE_SHIFT 0 #define PMIC_FG_CURRENT_OUT_ADDR 0xd8a #define PMIC_FG_CURRENT_OUT_MASK 0xFFFF #define PMIC_FG_CURRENT_OUT_SHIFT 0 #define PMIC_FG_R_CURR_ADDR 0xd88 #define PMIC_FG_R_CURR_MASK 0xFFFF #define PMIC_FG_R_CURR_SHIFT 0 #define PMIC_FG_CAR_15_00_ADDR 0xd16 #define PMIC_FG_CAR_15_00_MASK 0xFFFF #define PMIC_FG_CAR_15_00_SHIFT 0 #define PMIC_FG_CAR_31_16_ADDR 0xd18 #define PMIC_FG_CAR_31_16_MASK 0xFFFF #define PMIC_FG_CAR_31_16_SHIFT 0 #define PMIC_FG_BAT_HTH_15_00_ADDR 0xd20 #define PMIC_FG_BAT_HTH_15_00_MASK 0xFFFF #define PMIC_FG_BAT_HTH_15_00_SHIFT 0 #define PMIC_FG_BAT_HTH_31_16_ADDR 0xd22 #define PMIC_FG_BAT_HTH_31_16_MASK 0xFFFF #define PMIC_FG_BAT_HTH_31_16_SHIFT 0 #define PMIC_FG_BAT_LTH_15_00_ADDR 0xd1c #define PMIC_FG_BAT_LTH_15_00_MASK 0xFFFF #define PMIC_FG_BAT_LTH_15_00_SHIFT 0 #define PMIC_FG_BAT_LTH_31_16_ADDR 0xd1e #define PMIC_FG_BAT_LTH_31_16_MASK 0xFFFF #define PMIC_FG_BAT_LTH_31_16_SHIFT 0 #define PMIC_AD_BATON_UNDET_ADDR 0xe0a #define PMIC_AD_BATON_UNDET_MASK 0x1 #define PMIC_AD_BATON_UNDET_SHIFT 1 #define PMIC_AUXADC_ADC_RDY_PWRON_CLR_ADDR 0x11b2 #define PMIC_AUXADC_ADC_RDY_PWRON_CLR_MASK 0x1 #define PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT 3 #define PMIC_AUXADC_LBAT2_IRQ_EN_MIN_ADDR 0x1240 #define PMIC_AUXADC_LBAT2_IRQ_EN_MIN_MASK 0x1 #define PMIC_AUXADC_LBAT2_IRQ_EN_MIN_SHIFT 12 #define PMIC_AUXADC_LBAT2_DET_MIN_ADDR 0x1240 #define PMIC_AUXADC_LBAT2_DET_MIN_MASK 0x1 #define PMIC_AUXADC_LBAT2_DET_MIN_SHIFT 13 #define PMIC_AUXADC_LBAT2_IRQ_EN_MAX_ADDR 0x123e #define PMIC_AUXADC_LBAT2_IRQ_EN_MAX_MASK 0x1 #define PMIC_AUXADC_LBAT2_IRQ_EN_MAX_SHIFT 12 #define PMIC_AUXADC_LBAT2_DET_MAX_ADDR 0x123e #define PMIC_AUXADC_LBAT2_DET_MAX_MASK 0x1 #define PMIC_AUXADC_LBAT2_DET_MAX_SHIFT 13 #define PMIC_AUXADC_LBAT2_EN_ADDR 0x123a #define PMIC_AUXADC_LBAT2_EN_MASK 0x1 #define PMIC_AUXADC_LBAT2_EN_SHIFT 0 #define PMIC_AUXADC_LBAT2_VOLT_MIN_ADDR 0x1240 #define PMIC_AUXADC_LBAT2_VOLT_MIN_MASK 0xFFF #define PMIC_AUXADC_LBAT2_VOLT_MIN_SHIFT 0 #define PMIC_AUXADC_LBAT2_DET_PRD_SEL_ADDR 0x123c #define PMIC_AUXADC_LBAT2_DET_PRD_SEL_MASK 0x3 #define PMIC_AUXADC_LBAT2_DET_PRD_SEL_SHIFT 0 #define PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_ADDR 0x123c #define PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_MASK 0x3 #define PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_SHIFT 4 #define PMIC_AUXADC_LBAT2_VOLT_MAX_ADDR 0x123e #define PMIC_AUXADC_LBAT2_VOLT_MAX_MASK 0xFFF #define PMIC_AUXADC_LBAT2_VOLT_MAX_SHIFT 0 #define PMIC_AUXADC_LBAT2_DET_PRD_SEL_ADDR 0x123c #define PMIC_AUXADC_LBAT2_DET_PRD_SEL_MASK 0x3 #define PMIC_AUXADC_LBAT2_DET_PRD_SEL_SHIFT 0 #define PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_ADDR 0x123c #define PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_MASK 0x3 #define PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_SHIFT 2 #define PMIC_AUXADC_NAG_CNT_15_0_ADDR 0x11c6 #define PMIC_AUXADC_NAG_CNT_15_0_MASK 0xFFFF #define PMIC_AUXADC_NAG_CNT_15_0_SHIFT 0 #define PMIC_AUXADC_NAG_CNT_25_16_ADDR 0x11c8 #define PMIC_AUXADC_NAG_CNT_25_16_MASK 0x3FF #define PMIC_AUXADC_NAG_CNT_25_16_SHIFT 0 #define PMIC_AUXADC_NAG_DLTV_ADDR 0x11ca #define PMIC_AUXADC_NAG_DLTV_MASK 0xFFFF #define PMIC_AUXADC_NAG_DLTV_SHIFT 0 #define PMIC_AUXADC_NAG_C_DLTV_15_0_ADDR 0x11cc #define PMIC_AUXADC_NAG_C_DLTV_15_0_MASK 0xFFFF #define PMIC_AUXADC_NAG_C_DLTV_15_0_SHIFT 0 #define PMIC_AUXADC_NAG_C_DLTV_26_16_ADDR 0x11ce #define PMIC_AUXADC_NAG_C_DLTV_26_16_MASK 0x7FF #define PMIC_AUXADC_NAG_C_DLTV_26_16_SHIFT 0 #define PMIC_FG_RSTB_STATUS_ADDR 0xd14 #define PMIC_FG_RSTB_STATUS_MASK 0x1 #define PMIC_FG_RSTB_STATUS_SHIFT 0 #define PMIC_FG_IAVG_VLD_ADDR 0xd2e #define PMIC_FG_IAVG_VLD_MASK 0x1 #define PMIC_FG_IAVG_VLD_SHIFT 15 #define PMIC_FG_IAVG_15_00_ADDR 0xd2c #define PMIC_FG_IAVG_15_00_MASK 0xFFFF #define PMIC_FG_IAVG_15_00_SHIFT 0 #define PMIC_FG_IAVG_27_16_ADDR 0xd2e #define PMIC_FG_IAVG_27_16_MASK 0xFFF #define PMIC_FG_IAVG_27_16_SHIFT 0 #define PMIC_FG_IAVG_VLD_ADDR 0xd2e #define PMIC_FG_IAVG_VLD_MASK 0x1 #define PMIC_FG_IAVG_VLD_SHIFT 15 #define PMIC_AUXADC_ADC_OUT_FGADC_PCHR_ADDR 0x10b6 #define PMIC_AUXADC_ADC_OUT_FGADC_PCHR_MASK 0x7FFF #define PMIC_AUXADC_ADC_OUT_FGADC_PCHR_SHIFT 0 #define PMIC_AUXADC_NAG_IRQ_EN_ADDR 0x11be #define PMIC_AUXADC_NAG_IRQ_EN_MASK 0x1 #define PMIC_AUXADC_NAG_IRQ_EN_SHIFT 10 #define PMIC_AUXADC_NAG_EN_ADDR 0x11be #define PMIC_AUXADC_NAG_EN_MASK 0x1 #define PMIC_AUXADC_NAG_EN_SHIFT 0 #define PMIC_AUXADC_NAG_ZCV_ADDR 0x11c0 #define PMIC_AUXADC_NAG_ZCV_MASK 0x7FFF #define PMIC_AUXADC_NAG_ZCV_SHIFT 0 #define PMIC_AUXADC_NAG_C_DLTV_TH_15_0_ADDR 0x11c2 #define PMIC_AUXADC_NAG_C_DLTV_TH_15_0_MASK 0xFFFF #define PMIC_AUXADC_NAG_C_DLTV_TH_15_0_SHIFT 0 #define PMIC_AUXADC_NAG_C_DLTV_TH_26_16_ADDR 0x11c4 #define PMIC_AUXADC_NAG_C_DLTV_TH_26_16_MASK 0x7FF #define PMIC_AUXADC_NAG_C_DLTV_TH_26_16_SHIFT 0 #define PMIC_AUXADC_NAG_VBAT1_SEL_ADDR 0x11be #define PMIC_AUXADC_NAG_VBAT1_SEL_MASK 0x1 #define PMIC_AUXADC_NAG_VBAT1_SEL_SHIFT 2 #define PMIC_FG_ZCV_DET_IV_ADDR 0xd4e #define PMIC_FG_ZCV_DET_IV_MASK 0xF #define PMIC_FG_ZCV_DET_IV_SHIFT 0 #define PMIC_FG_ZCV_CAR_TH_15_00_ADDR 0xd58 #define PMIC_FG_ZCV_CAR_TH_15_00_MASK 0xFFFF #define PMIC_FG_ZCV_CAR_TH_15_00_SHIFT 0 #define PMIC_FG_ZCV_CAR_TH_30_16_ADDR 0xd5a #define PMIC_FG_ZCV_CAR_TH_30_16_MASK 0x7FFF #define PMIC_FG_ZCV_CAR_TH_30_16_SHIFT 0 #define PMIC_FG_ZCV_DET_EN_ADDR 0xd08 #define PMIC_FG_ZCV_DET_EN_MASK 0x1 #define PMIC_FG_ZCV_DET_EN_SHIFT 10 #define PMIC_FG_N_CHARGE_RST_ADDR 0xd0a #define PMIC_FG_N_CHARGE_RST_MASK 0x1 #define PMIC_FG_N_CHARGE_RST_SHIFT 11 #define PMIC_AUXADC_ADC_OUT_NAG_ADDR 0x11d2 #define PMIC_AUXADC_ADC_OUT_NAG_MASK 0x7FFF #define PMIC_AUXADC_ADC_OUT_NAG_SHIFT 0 #define PMIC_AUXADC_ADC_RDY_PWRON_PCHR_ADDR 0x10aa #define PMIC_AUXADC_ADC_RDY_PWRON_PCHR_MASK 0x1 #define PMIC_AUXADC_ADC_RDY_PWRON_PCHR_SHIFT 15 #define PMIC_AUXADC_ADC_OUT_PWRON_PCHR_ADDR 0x10aa #define PMIC_AUXADC_ADC_OUT_PWRON_PCHR_MASK 0x7FFF #define PMIC_AUXADC_ADC_OUT_PWRON_PCHR_SHIFT 0 #define PMIC_RG_HK_STRUP_AUXADC_START_SEL_ADDR 0xfae #define PMIC_RG_HK_STRUP_AUXADC_START_SEL_MASK 0x1 #define PMIC_RG_HK_STRUP_AUXADC_START_SEL_SHIFT 2 #define PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_ADDR 0x10b8 #define PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_MASK 0x1 #define PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_SHIFT 15 #define PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_ADDR 0x10b8 #define PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_MASK 0x7FFF #define PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_SHIFT 0 #define PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_ADDR 0x11b2 #define PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_MASK 0x1 #define PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_SHIFT 2 #define PMIC_FG_N_CHARGE_LTH_15_00_ADDR 0xd28 #define PMIC_FG_N_CHARGE_LTH_15_00_MASK 0xFFFF #define PMIC_FG_N_CHARGE_LTH_15_00_SHIFT 0 #define PMIC_FG_N_CHARGE_LTH_31_16_ADDR 0xd2a #define PMIC_FG_N_CHARGE_LTH_31_16_MASK 0xFFFF #define PMIC_FG_N_CHARGE_LTH_31_16_SHIFT 0 #define PMIC_FG_NTER_15_00_ADDR 0xd38 #define PMIC_FG_NTER_15_00_MASK 0xFFFF #define PMIC_FG_NTER_15_00_SHIFT 0 #define PMIC_FG_NTER_29_16_ADDR 0xd3a #define PMIC_FG_NTER_29_16_MASK 0x3FFF #define PMIC_FG_NTER_29_16_SHIFT 0 #define PMIC_MT6359_FG_CIC2_ADDR 0xd90 #define PMIC_MT6359_FG_CIC2_MASK 0xFFFF #define PMIC_MT6359_FG_CIC2_SHIFT 0 #define PMIC_FG_NCAR_15_00_ADDR 0xd24 #define PMIC_FG_NCAR_15_00_MASK 0xFFFF #define PMIC_FG_NCAR_15_00_SHIFT 0 #define PMIC_FG_NCAR_31_16_ADDR 0xd26 #define PMIC_FG_NCAR_31_16_MASK 0xFFFF #define PMIC_FG_NCAR_31_16_SHIFT 0 #define PMIC_FG_IAVG_LTH_15_00_ADDR 0xd30 #define PMIC_FG_IAVG_LTH_15_00_MASK 0xFFFF #define PMIC_FG_IAVG_LTH_15_00_SHIFT 0 #define PMIC_FG_IAVG_LTH_28_16_ADDR 0xd32 #define PMIC_FG_IAVG_LTH_28_16_MASK 0x1FFF #define PMIC_FG_IAVG_LTH_28_16_SHIFT 0 #define PMIC_FG_IAVG_HTH_15_00_ADDR 0xd34 #define PMIC_FG_IAVG_HTH_15_00_MASK 0xFFFF #define PMIC_FG_IAVG_HTH_15_00_SHIFT 0 #define PMIC_FG_IAVG_HTH_28_16_ADDR 0xd36 #define PMIC_FG_IAVG_HTH_28_16_MASK 0x1FFF #define PMIC_FG_IAVG_HTH_28_16_SHIFT 0 #define PMIC_FG_ZCV_CURR_ADDR 0xd50 #define PMIC_FG_ZCV_CURR_MASK 0xFFFF #define PMIC_FG_ZCV_CURR_SHIFT 0 #define PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_ADDR 0x122c #define PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_MASK 0x1 #define PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_SHIFT 12 #define PMIC_AUXADC_BAT_TEMP_EN_ADDR 0x1226 #define PMIC_AUXADC_BAT_TEMP_EN_MASK 0x1 #define PMIC_AUXADC_BAT_TEMP_EN_SHIFT 0 #define PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_ADDR 0x122a #define PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_MASK 0x3 #define PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_SHIFT 2 #define PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_ADDR 0x122a #define PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_MASK 0x3 #define PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_SHIFT 0 #define PMIC_AUXADC_BAT_TEMP_VOLT_MAX_ADDR 0x122c #define PMIC_AUXADC_BAT_TEMP_VOLT_MAX_MASK 0xFFF #define PMIC_AUXADC_BAT_TEMP_VOLT_MAX_SHIFT 0 #define PMIC_RG_INT_MASK_BAT_TEMP_L_ADDR 0xf98 #define PMIC_RG_INT_MASK_BAT_TEMP_L_MASK 0x1 #define PMIC_RG_INT_MASK_BAT_TEMP_L_SHIFT 5 #define PMIC_AUXADC_BAT_TEMP_DET_MAX_ADDR 0x122c #define PMIC_AUXADC_BAT_TEMP_DET_MAX_MASK 0x1 #define PMIC_AUXADC_BAT_TEMP_DET_MAX_SHIFT 13 #define PMIC_AUXADC_BAT_TEMP_VOLT_MIN_ADDR 0x122e #define PMIC_AUXADC_BAT_TEMP_VOLT_MIN_MASK 0xFFF #define PMIC_AUXADC_BAT_TEMP_VOLT_MIN_SHIFT 0 #define PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_ADDR 0x122e #define PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_MASK 0x1 #define PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_SHIFT 12 #define PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_ADDR 0x122a #define PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_MASK 0x3 #define PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_SHIFT 4 #define PMIC_RG_INT_MASK_BAT_TEMP_H_ADDR 0xf98 #define PMIC_RG_INT_MASK_BAT_TEMP_H_MASK 0x1 #define PMIC_RG_INT_MASK_BAT_TEMP_H_SHIFT 4 #define PMIC_AUXADC_BAT_TEMP_DET_MIN_ADDR 0x122e #define PMIC_AUXADC_BAT_TEMP_DET_MIN_MASK 0x1 #define PMIC_AUXADC_BAT_TEMP_DET_MIN_SHIFT 13 #define PMIC_RG_SYSTEM_INFO_CON0_ADDR 0xd9a #define UNIT_FGCURRENT (610352) /* mt6359 610.352 uA */ #define UNIT_CHARGE (85) /* CHARGE_LSB 0.085 uAh*/ /* AUXADC */ #define R_VAL_TEMP_2 (25) #define R_VAL_TEMP_3 (35) #define UNIT_TIME (50) #define UNIT_FG_IAVG (305176) /* IAVG LSB: 305.176 uA */ #define DEFAULT_R_FG (50) /* 5mm ohm */ #define UNIT_FGCAR_ZCV (85) /* CHARGE_LSB = 0.085 uAh */ #define VOLTAGE_FULL_RANGES 1800 #define ADC_PRECISE 32768 /* 15 bits */ #define CAR_TO_REG_SHIFT (5) /*coulomb interrupt lsb might be different with coulomb lsb */ #define CAR_TO_REG_FACTOR (0x2E14) /* 1000 * 1000 / CHARGE_LSB */ #define UNIT_FGCAR (174080) /* CHARGE_LSB 0.085 * 2^11 */ /************ bat_cali *******************/ #define BAT_CALI_DEVNAME "MT_pmic_adc_cali" /* add for meta tool----------------------------------------- */ #define Get_META_BAT_VOL _IOW('k', 10, int) #define Get_META_BAT_SOC _IOW('k', 11, int) #define Get_META_BAT_CAR_TUNE_VALUE _IOW('k', 12, int) #define Set_META_BAT_CAR_TUNE_VALUE _IOW('k', 13, int) #define Set_BAT_DISABLE_NAFG _IOW('k', 14, int) #define Set_CARTUNE_TO_KERNEL _IOW('k', 15, int) #define MT6359_AUXADC_BAT_TEMP_1 0x1228 #define PMIC_AUXADC_BAT_TEMP_FROZE_EN_ADDR \ MT6359_AUXADC_BAT_TEMP_1 #define PMIC_AUXADC_BAT_TEMP_FROZE_EN_MASK 0x1 #define PMIC_AUXADC_BAT_TEMP_FROZE_EN_SHIFT 0 static struct class *bat_cali_class; static int bat_cali_major; static dev_t bat_cali_devno; static struct cdev *bat_cali_cdev; void __attribute__ ((weak)) mtk_battery_netlink_handler(struct sk_buff *skb) { } static signed int reg_to_mv_value(signed int _reg) { long long _reg64 = _reg; int ret; #if defined(__LP64__) || defined(_LP64) _reg64 = (_reg64 * VOLTAGE_FULL_RANGES * R_VAL_TEMP_3) / ADC_PRECISE; #else _reg64 = div_s64(_reg64 * VOLTAGE_FULL_RANGES * R_VAL_TEMP_3, ADC_PRECISE); #endif ret = _reg64; bm_debug("[%s] %lld => %d\n", __func__, _reg64, ret); return ret; } static signed int mv_to_reg_value(signed int _mv) { int ret; long long _reg64 = _mv; #if defined(__LP64__) || defined(_LP64) _reg64 = (_reg64 * ADC_PRECISE) / (VOLTAGE_FULL_RANGES * R_VAL_TEMP_3); #else _reg64 = div_s64((_reg64 * ADC_PRECISE), (VOLTAGE_FULL_RANGES * R_VAL_TEMP_3)); #endif ret = _reg64; if (ret <= 0) { bm_err( "[fg_bat_nafg][%s] mv=%d,%lld => %d,\n", __func__, _mv, _reg64, ret); return ret; } bm_debug("[%s] mv=%d,%lld => %d,\n", __func__, _mv, _reg64, ret); return ret; } static int mv_to_reg_12_temp_value(signed int _reg) { int ret = (_reg * 4096) / (VOLTAGE_FULL_RANGES * R_VAL_TEMP_2); bm_debug("[%s] %d => %d\n", __func__, _reg, ret); return ret; } static void pre_gauge_update(struct mtk_gauge *gauge) { int m = 0; unsigned int reg_val = 0; regmap_update_bits(gauge->regmap, PMIC_FG_SW_READ_PRE_ADDR, PMIC_FG_SW_READ_PRE_MASK << PMIC_FG_SW_READ_PRE_SHIFT, 1 << PMIC_FG_SW_READ_PRE_SHIFT); do { m++; if (m > 1000) { bm_err("[%s] gauge_update_polling timeout 1!\r\n", __func__); break; } regmap_read(gauge->regmap, PMIC_FG_LATCHDATA_ST_ADDR, ®_val); reg_val = (reg_val & (PMIC_FG_LATCHDATA_ST_MASK << PMIC_FG_LATCHDATA_ST_SHIFT)) >> PMIC_FG_LATCHDATA_ST_SHIFT; } while (reg_val == 0); } static void post_gauge_update(struct mtk_gauge *gauge) { int m = 0; unsigned int reg_val; regmap_update_bits(gauge->regmap, PMIC_FG_SW_CLEAR_ADDR, PMIC_FG_SW_CLEAR_MASK << PMIC_FG_SW_CLEAR_SHIFT, 1 << PMIC_FG_SW_CLEAR_SHIFT); regmap_update_bits(gauge->regmap, PMIC_FG_SW_READ_PRE_ADDR, PMIC_FG_SW_READ_PRE_MASK << PMIC_FG_SW_READ_PRE_SHIFT, 0 << PMIC_FG_SW_READ_PRE_SHIFT); do { m++; if (m > 1000) { bm_err("[%s] gauge_update_polling timeout 2!\r\n", __func__); break; } regmap_read(gauge->regmap, PMIC_FG_LATCHDATA_ST_ADDR, ®_val); reg_val = (reg_val & (PMIC_FG_LATCHDATA_ST_MASK << PMIC_FG_LATCHDATA_ST_SHIFT)) >> PMIC_FG_LATCHDATA_ST_SHIFT; } while (reg_val != 0); regmap_update_bits(gauge->regmap, PMIC_FG_SW_CLEAR_ADDR, PMIC_FG_SW_CLEAR_MASK << PMIC_FG_SW_CLEAR_SHIFT, 0 << PMIC_FG_SW_CLEAR_SHIFT); } static int mv_to_reg_12_value(struct mtk_gauge *gauge, signed int _reg) { int ret = (_reg * 4096) / (VOLTAGE_FULL_RANGES * R_VAL_TEMP_3); bm_debug("[%s] %d => %d\n", __func__, _reg, ret); return ret; } static int reg_to_current(struct mtk_gauge *gauge, unsigned int regval) { unsigned short uvalue16 = 0; int dvalue, retval; long long temp_value = 0; bool is_charging = true; uvalue16 = (unsigned short) regval; dvalue = (unsigned int) uvalue16; if (dvalue == 0) { temp_value = (long long) dvalue; is_charging = false; } else if (dvalue > 32767) { /* > 0x8000 */ temp_value = (long long) (dvalue - 65535); temp_value = temp_value - (temp_value * 2); is_charging = false; } else { temp_value = (long long) dvalue; } temp_value = temp_value * UNIT_FGCURRENT; #if defined(__LP64__) || defined(_LP64) do_div(temp_value, 100000); #else temp_value = div_s64(temp_value, 100000); #endif retval = (unsigned int) temp_value; bm_debug("[%s] 0x%x 0x%x 0x%x 0x%x 0x%x %d\n", __func__, regval, uvalue16, dvalue, (int)temp_value, retval, is_charging); if (is_charging == false) return -retval; return retval; } /* ============================================================ */ /* pmic control end*/ /* ============================================================ */ u8 get_rtc_spare0_fg_value(struct mtk_gauge *gauge) { struct nvmem_cell *cell; u8 *buf, data; cell = nvmem_cell_get(&gauge->pdev->dev, "initialization"); if (IS_ERR(cell)) { bm_err("[%s]get rtc cell fail\n", __func__); return 0; } buf = nvmem_cell_read(cell, NULL); nvmem_cell_put(cell); if (IS_ERR(buf)) { bm_err("[%s]read rtc cell fail\n", __func__); return 0; } bm_debug("[%s] val=0x%x, %d\n", __func__, *buf, *buf); data = *buf; kfree(buf); return data; } void set_rtc_spare0_fg_value(struct mtk_gauge *gauge, u8 val) { struct nvmem_cell *cell; u32 length = 1; int ret; cell = nvmem_cell_get(&gauge->pdev->dev, "initialization"); if (IS_ERR(cell)) { bm_err("[%s]get rtc cell fail\n", __func__); return; } ret = nvmem_cell_write(cell, &val, length); nvmem_cell_put(cell); if (ret != length) bm_err("[%s] write rtc cell fail\n", __func__); } u8 get_rtc_spare_fg_value(struct mtk_gauge *gauge) { struct nvmem_cell *cell; u8 *buf, data; cell = nvmem_cell_get(&gauge->pdev->dev, "state-of-charge"); if (IS_ERR(cell)) { bm_err("[%s]get rtc cell fail\n", __func__); return 0; } buf = nvmem_cell_read(cell, NULL); nvmem_cell_put(cell); if (IS_ERR(buf)) { bm_err("[%s]read rtc cell fail\n", __func__); return 0; } bm_debug("[%s] val=%d\n", __func__, *buf); data = *buf; kfree(buf); return data; } void set_rtc_spare_fg_value(struct mtk_gauge *gauge, u8 val) { struct nvmem_cell *cell; u32 length = 1; int ret; cell = nvmem_cell_get(&gauge->pdev->dev, "state-of-charge"); if (IS_ERR(cell)) { bm_err("[%s]get rtc cell fail\n", __func__); return; } ret = nvmem_cell_write(cell, &val, length); nvmem_cell_put(cell); if (ret != length) bm_err("[%s] write rtc cell fail\n", __func__); bm_debug("[%s] val=%d\n", __func__, val); } static int fgauge_set_info(struct mtk_gauge *gauge, enum gauge_property ginfo, unsigned int value) { bm_debug("[%s]info:%d v:%d\n", __func__, ginfo, value); if (ginfo == GAUGE_PROP_2SEC_REBOOT) regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x0001, value); else if (ginfo == GAUGE_PROP_PL_CHARGING_STATUS) regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x0001 << 0x1, value << 0x1); else if (ginfo == GAUGE_PROP_MONITER_PLCHG_STATUS) regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x0001 << 0x2, value << 0x2); else if (ginfo == GAUGE_PROP_BAT_PLUG_STATUS) regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x0001 << 0x3, value << 0x3); else if (ginfo == GAUGE_PROP_IS_NVRAM_FAIL_MODE) regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x0001 << 0x4, value << 0x4); else if (ginfo == GAUGE_PROP_MONITOR_SOFF_VALIDTIME) regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x0001 << 0x5, value << 0x5); else if (ginfo == GAUGE_PROP_CON0_SOC) { value = value / 100; regmap_update_bits(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, 0x007f << 0x9, value << 0x9); } return 0; } static int fgauge_get_info(struct mtk_gauge *gauge, enum gauge_property ginfo, int *value) { int reg_val = 0; regmap_read(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, ®_val); if (ginfo == GAUGE_PROP_2SEC_REBOOT) *value = reg_val & 0x0001; else if (ginfo == GAUGE_PROP_PL_CHARGING_STATUS) *value = (reg_val & (0x0001 << 0x1)) >> 0x1; else if (ginfo == GAUGE_PROP_MONITER_PLCHG_STATUS) *value = (reg_val & (0x0001 << 0x2)) >> 0x2; else if (ginfo == GAUGE_PROP_BAT_PLUG_STATUS) *value = (reg_val & (0x0001 << 0x3)) >> 0x3; else if (ginfo == GAUGE_PROP_IS_NVRAM_FAIL_MODE) *value = (reg_val & (0x0001 << 0x4)) >> 0x4; else if (ginfo == GAUGE_PROP_MONITOR_SOFF_VALIDTIME) *value = (reg_val & (0x0001 << 0x5)) >> 0x5; else if (ginfo == GAUGE_PROP_CON0_SOC) *value = (reg_val & (0x007F << 0x9)) >> 0x9; bm_debug("[%s]info:%d v:%d\n", __func__, ginfo, *value); return 0; } static void switch_nafg_period(int _prd, int *value) { if (_prd >= 1 && _prd < 5) *value = 0; else if (_prd >= 5 && _prd < 10) *value = 1; else if (_prd >= 10 && _prd < 20) *value = 2; else if (_prd >= 20) *value = 3; } static void fgauge_set_nafg_intr_internal(struct mtk_gauge *gauge, int _prd, int _zcv_mv, int _thr_mv) { int NAG_C_DLTV_Threashold_26_16; int NAG_C_DLTV_Threashold_15_0; int period = 0; gauge->zcv_reg = mv_to_reg_value(_zcv_mv); gauge->thr_reg = mv_to_reg_value(_thr_mv); NAG_C_DLTV_Threashold_26_16 = (gauge->thr_reg & 0xffff0000) >> 16; NAG_C_DLTV_Threashold_15_0 = (gauge->thr_reg & 0x0000ffff); regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_ZCV_ADDR, PMIC_AUXADC_NAG_ZCV_MASK << PMIC_AUXADC_NAG_ZCV_SHIFT, gauge->zcv_reg << PMIC_AUXADC_NAG_ZCV_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_C_DLTV_TH_26_16_ADDR, PMIC_AUXADC_NAG_C_DLTV_TH_26_16_MASK << PMIC_AUXADC_NAG_C_DLTV_TH_26_16_SHIFT, NAG_C_DLTV_Threashold_26_16 << PMIC_AUXADC_NAG_C_DLTV_TH_26_16_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_C_DLTV_TH_15_0_ADDR, PMIC_AUXADC_NAG_C_DLTV_TH_15_0_MASK << PMIC_AUXADC_NAG_C_DLTV_TH_15_0_SHIFT, NAG_C_DLTV_Threashold_15_0 << PMIC_AUXADC_NAG_C_DLTV_TH_15_0_SHIFT); /* AUXADC_NAG_PRD_SEL change to 0x10 means 10s detect*/ switch_nafg_period(_prd, &period); regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_PRD_SEL_ADDR, PMIC_AUXADC_NAG_PRD_SEL_MASK << PMIC_AUXADC_NAG_PRD_SEL_SHIFT, period << PMIC_AUXADC_NAG_PRD_SEL_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_VBAT1_SEL_ADDR, PMIC_AUXADC_NAG_VBAT1_SEL_MASK << PMIC_AUXADC_NAG_VBAT1_SEL_SHIFT, 0 << PMIC_AUXADC_NAG_VBAT1_SEL_SHIFT); bm_debug("[fg_bat_nafg][fgauge_set_nafg_interrupt_internal] time[%d] zcv[%d:%d] thr[%d:%d] 26_16[0x%x] 15_00[0x%x]\n", _prd, _zcv_mv, gauge->zcv_reg, _thr_mv, gauge->thr_reg, NAG_C_DLTV_Threashold_26_16, NAG_C_DLTV_Threashold_15_0); } int nafg_zcv_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int zcv) { gauge->nafg_zcv_mv = zcv; /* 0.1 mv*/ return 0; } int zcv_current_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *zcv_current) { unsigned int uvalue16 = 0; signed int dvalue = 0; long long Temp_Value = 0; regmap_read(gauge->regmap, PMIC_FG_ZCV_CURR_ADDR, &uvalue16); uvalue16 = (uvalue16 & (PMIC_FG_ZCV_CURR_MASK << PMIC_FG_ZCV_CURR_SHIFT)) >> PMIC_FG_ZCV_CURR_SHIFT; dvalue = uvalue16; if (dvalue == 0) { Temp_Value = (long long) dvalue; } else if (dvalue > 32767) { /* > 0x8000 */ Temp_Value = (long long) (dvalue - 65535); Temp_Value = Temp_Value - (Temp_Value * 2); } else { Temp_Value = (long long) dvalue; } Temp_Value = Temp_Value * UNIT_FGCURRENT; #if defined(__LP64__) || defined(_LP64) do_div(Temp_Value, 100000); #else Temp_Value = div_s64(Temp_Value, 100000); #endif dvalue = (unsigned int) Temp_Value; /* Auto adjust value */ if (gauge->gm->fg_cust_data.r_fg_value != DEFAULT_R_FG) { bm_debug( "[fgauge_read_current] Auto adjust value due to the Rfg is %d\n Ori curr=%d", gauge->gm->fg_cust_data.r_fg_value, dvalue); dvalue = (dvalue * DEFAULT_R_FG) / gauge->gm->fg_cust_data.r_fg_value; bm_debug("[fgauge_read_current] new current=%d\n", dvalue); } bm_debug("[fgauge_read_current] ori current=%d\n", dvalue); dvalue = ((dvalue * gauge->gm->fg_cust_data.car_tune_value) / 1000); bm_debug("[fgauge_read_current] final current=%d (ratio=%d)\n", dvalue, gauge->gm->fg_cust_data.car_tune_value); *zcv_current = dvalue; return 0; } int nafg_c_dltv_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int c_dltv_mv) { gauge->nafg_c_dltv_mv = c_dltv_mv; /* 0.1 mv*/ fgauge_set_nafg_intr_internal( gauge, gauge->gm->fg_cust_data.nafg_time_setting, gauge->nafg_zcv_mv, gauge->nafg_c_dltv_mv); return 0; } static int get_nafg_vbat(struct mtk_gauge *gauge) { unsigned int nag_vbat_reg, vbat_val; int nag_vbat_mv, i = 0; do { regmap_read(gauge->regmap, PMIC_AUXADC_ADC_OUT_NAG_ADDR, &nag_vbat_reg); nag_vbat_reg = (nag_vbat_reg & (PMIC_AUXADC_ADC_OUT_NAG_MASK << PMIC_AUXADC_ADC_OUT_NAG_SHIFT)) >> PMIC_AUXADC_ADC_OUT_NAG_SHIFT; if ((nag_vbat_reg & 0x8000) != 0) break; msleep(30); i++; } while (i <= 5); vbat_val = nag_vbat_reg & 0x7fff; nag_vbat_mv = reg_to_mv_value(vbat_val); return nag_vbat_mv; } int nafg_vbat_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *vbat) { *vbat = get_nafg_vbat(gauge); return 0; } int bat_plugout_en_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { if (val != 0) { val = 1; enable_gauge_irq(gauge, BAT_PLUGOUT_IRQ); } else disable_gauge_irq(gauge, BAT_PLUGOUT_IRQ); return 0; } static void fgauge_set_zcv_intr_internal( struct mtk_gauge *gauge_dev, int fg_zcv_det_time, int fg_zcv_car_th) { int fg_zcv_car_thr_h_reg, fg_zcv_car_thr_l_reg; long long fg_zcv_car_th_reg = fg_zcv_car_th; fg_zcv_car_th_reg = (fg_zcv_car_th_reg * 100 * 1000); #if defined(__LP64__) || defined(_LP64) do_div(fg_zcv_car_th_reg, UNIT_FGCAR_ZCV); #else fg_zcv_car_th_reg = div_s64(fg_zcv_car_th_reg, UNIT_FGCAR_ZCV); #endif if (gauge_dev->hw_status.r_fg_value != DEFAULT_R_FG) #if defined(__LP64__) || defined(_LP64) fg_zcv_car_th_reg = (fg_zcv_car_th_reg * gauge_dev->hw_status.r_fg_value) / DEFAULT_R_FG; #else fg_zcv_car_th_reg = div_s64(fg_zcv_car_th_reg * gauge_dev->hw_status.r_fg_value, DEFAULT_R_FG); #endif #if defined(__LP64__) || defined(_LP64) fg_zcv_car_th_reg = ((fg_zcv_car_th_reg * 1000) / gauge_dev->hw_status.car_tune_value); #else fg_zcv_car_th_reg = div_s64((fg_zcv_car_th_reg * 1000), gauge_dev->hw_status.car_tune_value); #endif fg_zcv_car_thr_h_reg = (fg_zcv_car_th_reg & 0xffff0000) >> 16; fg_zcv_car_thr_l_reg = fg_zcv_car_th_reg & 0x0000ffff; regmap_update_bits(gauge_dev->regmap, PMIC_FG_ZCV_DET_IV_ADDR, PMIC_FG_ZCV_DET_IV_MASK << PMIC_FG_ZCV_DET_IV_SHIFT, fg_zcv_det_time << PMIC_FG_ZCV_DET_IV_SHIFT); regmap_update_bits(gauge_dev->regmap, PMIC_FG_ZCV_CAR_TH_15_00_ADDR, PMIC_FG_ZCV_CAR_TH_15_00_MASK << PMIC_FG_ZCV_CAR_TH_15_00_SHIFT, fg_zcv_car_thr_l_reg << PMIC_FG_ZCV_CAR_TH_15_00_SHIFT); regmap_update_bits(gauge_dev->regmap, PMIC_FG_ZCV_CAR_TH_30_16_ADDR, PMIC_FG_ZCV_CAR_TH_30_16_MASK << PMIC_FG_ZCV_CAR_TH_30_16_SHIFT, fg_zcv_car_thr_h_reg << PMIC_FG_ZCV_CAR_TH_30_16_SHIFT); bm_debug("[FG_ZCV_INT][%s] det_time %d mv %d reg %lld 30_16 0x%x 15_00 0x%x\n", __func__, fg_zcv_det_time, fg_zcv_car_th, fg_zcv_car_th_reg, fg_zcv_car_thr_h_reg, fg_zcv_car_thr_l_reg); } int zcv_intr_threshold_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int zcv_avg_current) { int fg_zcv_det_time; int fg_zcv_car_th = 0; fg_zcv_det_time = gauge->gm->fg_cust_data.zcv_suspend_time; fg_zcv_car_th = (fg_zcv_det_time + 1) * 4 * zcv_avg_current / 60; bm_debug("[%s] current:%d, fg_zcv_det_time:%d, fg_zcv_car_th:%d\n", __func__, zcv_avg_current, fg_zcv_det_time, fg_zcv_car_th); fgauge_set_zcv_intr_internal( gauge, fg_zcv_det_time, fg_zcv_car_th); return 0; } int zcv_intr_en_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int en) { static int cnt; bm_debug("%s %d %d\n", __func__, cnt, en); if (en != 0) cnt++; else cnt--; if (en == 0) { disable_gauge_irq(gauge, ZCV_IRQ); regmap_update_bits(gauge->regmap, PMIC_FG_ZCV_DET_EN_ADDR, PMIC_FG_ZCV_DET_EN_MASK << PMIC_FG_ZCV_DET_EN_SHIFT, en << PMIC_FG_ZCV_DET_EN_SHIFT); mdelay(1); } if (en == 1) { enable_gauge_irq(gauge, ZCV_IRQ); regmap_update_bits(gauge->regmap, PMIC_FG_ZCV_DET_EN_ADDR, PMIC_FG_ZCV_DET_EN_MASK << PMIC_FG_ZCV_DET_EN_SHIFT, en << PMIC_FG_ZCV_DET_EN_SHIFT); } bm_debug("[FG_ZCV_INT][fg_set_zcv_intr_en] En %d\n", en); return 0; } int soff_reset_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int en) { return 0; } int ncar_reset_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { regmap_update_bits(gauge->regmap, PMIC_FG_N_CHARGE_RST_ADDR, PMIC_FG_N_CHARGE_RST_MASK << PMIC_FG_N_CHARGE_RST_SHIFT, 1 << PMIC_FG_N_CHARGE_RST_SHIFT); udelay(200); regmap_update_bits(gauge->regmap, PMIC_FG_N_CHARGE_RST_ADDR, PMIC_FG_N_CHARGE_RST_MASK << PMIC_FG_N_CHARGE_RST_SHIFT, 0 << PMIC_FG_N_CHARGE_RST_SHIFT); return 0; } int nafg_check_corner(struct mtk_gauge *gauge) { int nag_vbat = 0; int setto_cdltv_thr_mv = 0; int get_c_dltv_mv = 0; int diff = 0; signed int nag_c_dltv_value; signed int nag_c_dltv_value_h; signed int nag_c_dltv_reg_value; bool bcheckbit10; int nag_zcv = gauge->nafg_zcv_mv; setto_cdltv_thr_mv = gauge->nafg_c_dltv_mv; /*AUXADC_NAG_7*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_C_DLTV_15_0_ADDR, &nag_c_dltv_value); nag_c_dltv_value = (nag_c_dltv_value & (PMIC_AUXADC_NAG_C_DLTV_15_0_MASK << PMIC_AUXADC_NAG_C_DLTV_15_0_SHIFT)) >> PMIC_AUXADC_NAG_C_DLTV_15_0_SHIFT; /*AUXADC_NAG_8*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_C_DLTV_26_16_ADDR, &nag_c_dltv_value_h); nag_c_dltv_value_h = (nag_c_dltv_value_h & (PMIC_AUXADC_NAG_C_DLTV_26_16_MASK << PMIC_AUXADC_NAG_C_DLTV_26_16_SHIFT)) >> PMIC_AUXADC_NAG_C_DLTV_26_16_SHIFT; bcheckbit10 = nag_c_dltv_value_h & 0x0400; if (bcheckbit10 == 0) nag_c_dltv_reg_value = (nag_c_dltv_value & 0xffff) + ((nag_c_dltv_value_h & 0x07ff) << 16); else nag_c_dltv_reg_value = (nag_c_dltv_value & 0xffff) + (((nag_c_dltv_value_h | 0xf800) & 0xffff) << 16); get_c_dltv_mv = reg_to_mv_value(nag_c_dltv_reg_value); nag_vbat = get_nafg_vbat(gauge); if (nag_vbat < 31500 && nag_zcv > 31500) gauge->nafg_corner = 1; else if (nag_zcv < 31500 && nag_vbat > 31500) gauge->nafg_corner = 2; else gauge->nafg_corner = 0; bm_debug("%s:corner:%d nag_vbat:%d nag_zcv:%d get_c_dltv_mv:%d setto_cdltv_thr_mv:%d, diff:%d, RG[0x%x,0x%x]\n", __func__, gauge->nafg_corner, nag_vbat, nag_zcv, get_c_dltv_mv, setto_cdltv_thr_mv, diff, nag_c_dltv_value_h, nag_c_dltv_value); return 0; } int event_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int event) { if (event == EVT_INT_NAFG_CHECK) nafg_check_corner(gauge); return 0; } int bat_tmp_ht_threshold_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int threshold) { int tmp_int_lt = mv_to_reg_12_temp_value(threshold); /* min is high temp */ regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_VOLT_MIN_ADDR, PMIC_AUXADC_BAT_TEMP_VOLT_MIN_MASK << PMIC_AUXADC_BAT_TEMP_VOLT_MIN_SHIFT, tmp_int_lt << PMIC_AUXADC_BAT_TEMP_VOLT_MIN_SHIFT); bm_debug("[%s]mv:%d reg:%d\n", __func__, threshold, tmp_int_lt); return 0; } int en_bat_tmp_ht_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int en) { if (en == 0) { disable_gauge_irq(gauge, BAT_TMP_H_IRQ); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_ADDR, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_MASK << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_SHIFT, 0 << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_EN_ADDR, PMIC_AUXADC_BAT_TEMP_EN_MASK << PMIC_AUXADC_BAT_TEMP_EN_SHIFT, 0 << PMIC_AUXADC_BAT_TEMP_EN_SHIFT); } else { regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_ADDR, PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_MASK << PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_SHIFT, 2 << PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_SHIFT); /* unit: 0x10 = 2, means 5 second */ regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_ADDR, PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_MASK << PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_SHIFT, 2 << PMIC_AUXADC_BAT_TEMP_DEBT_MIN_SEL_SHIFT); /* debounce 0x10 = 2 , means 4 times*/ /* 5s * 4 times = 20s to issue bat_temp interrupt */ regmap_update_bits(gauge->regmap, PMIC_RG_INT_MASK_BAT_TEMP_H_ADDR, PMIC_RG_INT_MASK_BAT_TEMP_H_MASK << PMIC_RG_INT_MASK_BAT_TEMP_H_SHIFT, 0 << PMIC_RG_INT_MASK_BAT_TEMP_H_SHIFT); enable_gauge_irq(gauge, BAT_TMP_H_IRQ); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_ADDR, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_MASK << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_SHIFT, 1 << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MIN_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_DET_MIN_ADDR, PMIC_AUXADC_BAT_TEMP_DET_MIN_MASK << PMIC_AUXADC_BAT_TEMP_DET_MIN_SHIFT, 1 << PMIC_AUXADC_BAT_TEMP_DET_MIN_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_EN_ADDR, PMIC_AUXADC_BAT_TEMP_EN_MASK << PMIC_AUXADC_BAT_TEMP_EN_SHIFT, 1 << PMIC_AUXADC_BAT_TEMP_EN_SHIFT); } bm_debug("[%s]en:%d\n", __func__, en); return 0; } int bat_tmp_lt_threshold_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int threshold) { int tmp_int_lt = mv_to_reg_12_temp_value(threshold); /* max is low temp */ regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_VOLT_MAX_ADDR, PMIC_AUXADC_BAT_TEMP_VOLT_MAX_MASK << PMIC_AUXADC_BAT_TEMP_VOLT_MAX_SHIFT, tmp_int_lt << PMIC_AUXADC_BAT_TEMP_VOLT_MAX_SHIFT); bm_debug("[%s]mv:%d reg:%d\n", __func__, threshold, tmp_int_lt); return 0; } int en_bat_tmp_lt_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int en) { if (en == 0) { disable_gauge_irq(gauge, BAT_TMP_L_IRQ); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_ADDR, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_MASK << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_SHIFT, 0 << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_EN_ADDR, PMIC_AUXADC_BAT_TEMP_EN_MASK << PMIC_AUXADC_BAT_TEMP_EN_SHIFT, 0 << PMIC_AUXADC_BAT_TEMP_EN_SHIFT); } else { regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_ADDR, PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_MASK << PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_SHIFT, 2 << PMIC_AUXADC_BAT_TEMP_DET_PRD_SEL_SHIFT); /* unit: 0x10 = 2, means 5 second */ regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_ADDR, PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_MASK << PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_SHIFT, 2 << PMIC_AUXADC_BAT_TEMP_DEBT_MAX_SEL_SHIFT); /* debounce 0x10 = 2 , means 4 times*/ /* 5s * 4 times = 20s to issue bat_temp interrupt */ regmap_update_bits(gauge->regmap, PMIC_RG_INT_MASK_BAT_TEMP_L_ADDR, PMIC_RG_INT_MASK_BAT_TEMP_L_MASK << PMIC_RG_INT_MASK_BAT_TEMP_L_SHIFT, 0 << PMIC_RG_INT_MASK_BAT_TEMP_L_SHIFT); enable_gauge_irq(gauge, BAT_TMP_L_IRQ); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_ADDR, PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_MASK << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_SHIFT, 1 << PMIC_AUXADC_BAT_TEMP_IRQ_EN_MAX_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_DET_MAX_ADDR, PMIC_AUXADC_BAT_TEMP_DET_MAX_MASK << PMIC_AUXADC_BAT_TEMP_DET_MAX_SHIFT, 1 << PMIC_AUXADC_BAT_TEMP_DET_MAX_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_EN_ADDR, PMIC_AUXADC_BAT_TEMP_EN_MASK << PMIC_AUXADC_BAT_TEMP_EN_SHIFT, 1 << PMIC_AUXADC_BAT_TEMP_EN_SHIFT); } bm_debug("[%s]en:%d\n", __func__, en); return 0; } int bat_cycle_intr_threshold_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int threshold) { long long car = threshold; long long carReg; disable_gauge_irq(gauge, FG_N_CHARGE_L_IRQ); #if defined(__LP64__) || defined(_LP64) car = car * 100000 / UNIT_CHARGE; /* 1000 * 100 */ #else car = div_s64(car * 100000, UNIT_CHARGE); #endif if (gauge->hw_status.r_fg_value != DEFAULT_R_FG) { car = (car * gauge->hw_status.r_fg_value); #if defined(__LP64__) || defined(_LP64) do_div(car, DEFAULT_R_FG); #else car = div_s64(car, DEFAULT_R_FG); #endif } car = car * 1000; #if defined(__LP64__) || defined(_LP64) do_div(car, gauge->hw_status.car_tune_value); #else car = div_s64(car, gauge->hw_status.car_tune_value); #endif carReg = car; carReg = 0 - carReg; regmap_update_bits(gauge->regmap, PMIC_FG_N_CHARGE_LTH_15_00_ADDR, PMIC_FG_N_CHARGE_LTH_15_00_MASK << PMIC_FG_N_CHARGE_LTH_15_00_SHIFT, (carReg & 0xffff) << PMIC_FG_N_CHARGE_LTH_15_00_SHIFT); regmap_update_bits(gauge->regmap, PMIC_FG_N_CHARGE_LTH_31_16_ADDR, PMIC_FG_N_CHARGE_LTH_31_16_MASK << PMIC_FG_N_CHARGE_LTH_31_16_SHIFT, ((carReg & 0xffff0000) >> 16) << PMIC_FG_N_CHARGE_LTH_31_16_SHIFT); bm_err("car:%d carR:%lld r:%lld\n", threshold, car, carReg); enable_gauge_irq(gauge, FG_N_CHARGE_L_IRQ); return 0; } int fgauge_get_time(struct mtk_gauge *gauge_dev, unsigned int *ptime) { unsigned int time_29_16, time_15_00, ret_time; long long time = 0; pre_gauge_update(gauge_dev); regmap_read(gauge_dev->regmap, PMIC_FG_NTER_15_00_ADDR, &time_15_00); time_15_00 = (time_15_00 & (PMIC_FG_NTER_15_00_MASK << PMIC_FG_NTER_15_00_SHIFT)) >> PMIC_FG_NTER_15_00_SHIFT; regmap_read(gauge_dev->regmap, PMIC_FG_NTER_29_16_ADDR, &time_29_16); time_29_16 = (time_29_16 & (PMIC_FG_NTER_29_16_MASK << PMIC_FG_NTER_29_16_SHIFT)) >> PMIC_FG_NTER_29_16_SHIFT; time = time_15_00; time |= time_29_16 << 16; #if defined(__LP64__) || defined(_LP64) time = time * UNIT_TIME / 100; #else time = div_s64(time * UNIT_TIME, 100); #endif ret_time = time; bm_debug( "[%s] low:0x%x high:0x%x rtime:0x%llx 0x%x!\r\n", __func__, time_15_00, time_29_16, time, ret_time); post_gauge_update(gauge_dev); *ptime = ret_time; return 0; } static unsigned int instant_current_for_car_tune(struct mtk_gauge *gauge) { unsigned int reg_value = 0; pre_gauge_update(gauge); regmap_read(gauge->regmap, PMIC_FG_CURRENT_OUT_ADDR, ®_value); reg_value = (reg_value & (PMIC_FG_CURRENT_OUT_MASK << PMIC_FG_CURRENT_OUT_SHIFT)) >> PMIC_FG_CURRENT_OUT_SHIFT; post_gauge_update(gauge); bm_err("%s, reg_value=%d\n", __func__, reg_value); return reg_value; } static int instant_current(struct mtk_gauge *gauge) { unsigned int reg_value; int dvalue; int r_fg_value; int car_tune_value; r_fg_value = gauge->hw_status.r_fg_value; car_tune_value = gauge->hw_status.car_tune_value; pre_gauge_update(gauge); regmap_read(gauge->regmap, PMIC_FG_CURRENT_OUT_ADDR, ®_value); reg_value = (reg_value & (PMIC_FG_CURRENT_OUT_MASK << PMIC_FG_CURRENT_OUT_SHIFT)) >> PMIC_FG_CURRENT_OUT_SHIFT; post_gauge_update(gauge); dvalue = reg_to_current(gauge, reg_value); /* Auto adjust value */ if (r_fg_value != DEFAULT_R_FG) { dvalue = (dvalue * DEFAULT_R_FG) / r_fg_value; } dvalue = ((dvalue * car_tune_value) / 1000); return dvalue; } void read_fg_hw_info_current_1(struct mtk_gauge *gauge_dev) { gauge_dev->fg_hw_info.current_1 = instant_current(gauge_dev); } void read_fg_hw_info_current_2(struct mtk_gauge *gauge_dev) { long long fg_current_2_reg; int cic2_reg; signed int dvalue; long long Temp_Value; int sign_bit = 0; regmap_read(gauge_dev->regmap, PMIC_MT6359_FG_CIC2_ADDR, &cic2_reg); cic2_reg = (cic2_reg & (PMIC_MT6359_FG_CIC2_MASK << PMIC_MT6359_FG_CIC2_SHIFT)) >> PMIC_MT6359_FG_CIC2_SHIFT; fg_current_2_reg = cic2_reg; /*calculate the real world data */ dvalue = (unsigned int) fg_current_2_reg; if (dvalue == 0) { Temp_Value = (long long) dvalue; sign_bit = 0; } else if (dvalue > 32767) { /* > 0x8000 */ Temp_Value = (long long) (dvalue - 65535); Temp_Value = Temp_Value - (Temp_Value * 2); sign_bit = 1; } else { Temp_Value = (long long) dvalue; sign_bit = 0; } Temp_Value = Temp_Value * UNIT_FGCURRENT; #if defined(__LP64__) || defined(_LP64) do_div(Temp_Value, 100000); #else Temp_Value = div_s64(Temp_Value, 100000); #endif dvalue = (unsigned int) Temp_Value; if (gauge_dev->hw_status.r_fg_value != DEFAULT_R_FG) dvalue = (dvalue * DEFAULT_R_FG) / gauge_dev->hw_status.r_fg_value; if (sign_bit == 1) dvalue = dvalue - (dvalue * 2); gauge_dev->fg_hw_info.current_2 = ((dvalue * gauge_dev->hw_status.car_tune_value) / 1000); } static int average_current_get(struct mtk_gauge *gauge_dev, struct mtk_gauge_sysfs_field_info *attr, int *data) { long long fg_iavg_reg = 0; long long fg_iavg_reg_tmp = 0; long long fg_iavg_ma = 0; int fg_iavg_reg_27_16 = 0; int fg_iavg_reg_15_00 = 0; int sign_bit = 0; int is_bat_charging; int iavg_vld; int r_fg_value, car_tune_value; r_fg_value = gauge_dev->hw_status.r_fg_value; car_tune_value = gauge_dev->hw_status.car_tune_value; pre_gauge_update(gauge_dev); regmap_read(gauge_dev->regmap, PMIC_FG_IAVG_VLD_ADDR, &iavg_vld); iavg_vld = (iavg_vld & (PMIC_FG_IAVG_VLD_MASK << PMIC_FG_IAVG_VLD_SHIFT)) >> PMIC_FG_IAVG_VLD_SHIFT; if (iavg_vld == 1) { regmap_read(gauge_dev->regmap, PMIC_FG_IAVG_27_16_ADDR, &fg_iavg_reg_27_16); fg_iavg_reg_27_16 = (fg_iavg_reg_27_16 & (PMIC_FG_IAVG_27_16_MASK << PMIC_FG_IAVG_27_16_SHIFT)) >> PMIC_FG_IAVG_27_16_SHIFT; regmap_read(gauge_dev->regmap, PMIC_FG_IAVG_15_00_ADDR, &fg_iavg_reg_15_00); fg_iavg_reg_15_00 = (fg_iavg_reg_15_00 & (PMIC_FG_IAVG_15_00_MASK << PMIC_FG_IAVG_15_00_SHIFT)) >> PMIC_FG_IAVG_15_00_SHIFT; fg_iavg_reg = fg_iavg_reg_27_16; fg_iavg_reg = ((long long)fg_iavg_reg << 16) + fg_iavg_reg_15_00; sign_bit = (fg_iavg_reg_27_16 & 0x800) >> 11; if (sign_bit) { fg_iavg_reg_tmp = fg_iavg_reg; /*fg_iavg_reg = fg_iavg_reg_tmp - 0xfffffff - 1;*/ fg_iavg_reg = 0xfffffff - fg_iavg_reg_tmp + 1; } if (sign_bit == 1) is_bat_charging = 0; /* discharge */ else is_bat_charging = 1; /* charge */ fg_iavg_ma = fg_iavg_reg * UNIT_FG_IAVG * car_tune_value; bm_debug( "[fg_get_current_iavg] fg_iavg_ma %lld fg_iavg_reg %lld fg_iavg_reg_tmp %lld\n", fg_iavg_ma, fg_iavg_reg, fg_iavg_reg_tmp); #if defined(__LP64__) || defined(_LP64) do_div(fg_iavg_ma, 1000000); #else fg_iavg_ma = div_s64(fg_iavg_ma, 1000000); #endif if (r_fg_value != DEFAULT_R_FG) { #if defined(__LP64__) || defined(_LP64) fg_iavg_ma = (fg_iavg_ma * DEFAULT_R_FG / r_fg_value); #else fg_iavg_ma = div_s64(fg_iavg_ma * DEFAULT_R_FG, r_fg_value); #endif } #if defined(__LP64__) || defined(_LP64) do_div(fg_iavg_ma, 100); #else fg_iavg_ma = div_s64(fg_iavg_ma, 100); #endif bm_debug("[fg_get_current_iavg] fg_iavg_ma %lld\n", fg_iavg_ma); if (sign_bit == 1) fg_iavg_ma = 0 - fg_iavg_ma; bm_debug( "[fg_get_current_iavg] fg_iavg_ma %lld fg_iavg_reg %lld r_fg_value %d 27_16 0x%x 15_00 0x%x\n", fg_iavg_ma, fg_iavg_reg, r_fg_value, fg_iavg_reg_27_16, fg_iavg_reg_15_00); gauge_dev->fg_hw_info.current_avg = fg_iavg_ma; gauge_dev->fg_hw_info.current_avg_sign = sign_bit; bm_debug("[fg_get_current_iavg] PMIC_FG_IAVG_VLD == 1\n"); } else { read_fg_hw_info_current_1(gauge_dev); gauge_dev->fg_hw_info.current_avg = gauge_dev->fg_hw_info.current_1; if (gauge_dev->fg_hw_info.current_1 < 0) gauge_dev->fg_hw_info.current_avg_sign = 1; bm_debug("[fg_get_current_iavg] PMIC_FG_IAVG_VLD != 1, avg %d, current_1 %d\n", gauge_dev->fg_hw_info.current_avg, gauge_dev->fg_hw_info.current_1); } post_gauge_update(gauge_dev); *data = gauge_dev->fg_hw_info.current_avg; gauge_dev->fg_hw_info.current_avg_valid = iavg_vld; bm_debug("[fg_get_current_iavg] %d %d\n", *data, iavg_vld); return 0; } static signed int fg_set_iavg_intr(struct mtk_gauge *gauge_dev, void *data) { int iavg_gap = *(unsigned int *) (data); int iavg; long long iavg_ht, iavg_lt; long long fg_iavg_reg_ht, fg_iavg_reg_lt; int fg_iavg_lth_28_16, fg_iavg_lth_15_00; int fg_iavg_hth_28_16, fg_iavg_hth_15_00; average_current_get(gauge_dev, NULL, &iavg); iavg_ht = abs(iavg) + iavg_gap; iavg_lt = abs(iavg) - iavg_gap; if (iavg_lt <= 0) iavg_lt = 0; gauge_dev->hw_status.iavg_ht = iavg_ht; gauge_dev->hw_status.iavg_lt = iavg_lt; /* reverse for IAVG */ /* fg_iavg_ma * 100 * fg_cust_data.r_fg_value / DEFAULT_RFG * 1000 * 1000 */ /* / fg_cust_data.car_tune_value / UNIT_FG_IAVG = fg_iavg_reg */ fg_iavg_reg_ht = iavg_ht * 100; if (gauge_dev->hw_status.r_fg_value != DEFAULT_R_FG) { fg_iavg_reg_ht = fg_iavg_reg_ht * gauge_dev->hw_status.r_fg_value; #if defined(__LP64__) || defined(_LP64) do_div(fg_iavg_reg_ht, DEFAULT_R_FG); #else fg_iavg_reg_ht = div_s64(fg_iavg_reg_ht, DEFAULT_R_FG); #endif } fg_iavg_reg_ht = fg_iavg_reg_ht * 1000000; #if defined(__LP64__) || defined(_LP64) do_div(fg_iavg_reg_ht, UNIT_FG_IAVG); do_div(fg_iavg_reg_ht, gauge_dev->hw_status.car_tune_value); #else fg_iavg_reg_ht = div_s64(fg_iavg_reg_ht, UNIT_FG_IAVG); fg_iavg_reg_ht = div_s64(fg_iavg_reg_ht, gauge_dev->hw_status.car_tune_value); #endif fg_iavg_reg_lt = iavg_lt * 100; if (gauge_dev->hw_status.r_fg_value != DEFAULT_R_FG) { fg_iavg_reg_lt = fg_iavg_reg_lt * gauge_dev->hw_status.r_fg_value; #if defined(__LP64__) || defined(_LP64) do_div(fg_iavg_reg_lt, DEFAULT_R_FG); #else fg_iavg_reg_lt = div_s64(fg_iavg_reg_lt, DEFAULT_R_FG); #endif } fg_iavg_reg_lt = fg_iavg_reg_lt * 1000000; #if defined(__LP64__) || defined(_LP64) do_div(fg_iavg_reg_lt, UNIT_FG_IAVG); do_div(fg_iavg_reg_lt, gauge_dev->hw_status.car_tune_value); #else fg_iavg_reg_lt = div_s64(fg_iavg_reg_lt, UNIT_FG_IAVG); fg_iavg_reg_lt = div_s64(fg_iavg_reg_lt, gauge_dev->hw_status.car_tune_value); #endif fg_iavg_lth_28_16 = (fg_iavg_reg_lt & 0x1fff0000) >> 16; fg_iavg_lth_15_00 = fg_iavg_reg_lt & 0xffff; fg_iavg_hth_28_16 = (fg_iavg_reg_ht & 0x1fff0000) >> 16; fg_iavg_hth_15_00 = fg_iavg_reg_ht & 0xffff; disable_gauge_irq(gauge_dev, FG_IAVG_H_IRQ); disable_gauge_irq(gauge_dev, FG_IAVG_L_IRQ); regmap_update_bits(gauge_dev->regmap, PMIC_FG_IAVG_LTH_28_16_ADDR, PMIC_FG_IAVG_LTH_28_16_MASK << PMIC_FG_IAVG_LTH_28_16_SHIFT, fg_iavg_lth_28_16 << PMIC_FG_IAVG_LTH_28_16_SHIFT); regmap_update_bits(gauge_dev->regmap, PMIC_FG_IAVG_LTH_15_00_ADDR, PMIC_FG_IAVG_LTH_15_00_MASK << PMIC_FG_IAVG_LTH_15_00_SHIFT, fg_iavg_lth_15_00 << PMIC_FG_IAVG_LTH_15_00_SHIFT); regmap_update_bits(gauge_dev->regmap, PMIC_FG_IAVG_HTH_28_16_ADDR, PMIC_FG_IAVG_HTH_28_16_MASK << PMIC_FG_IAVG_HTH_28_16_SHIFT, fg_iavg_hth_28_16 << PMIC_FG_IAVG_HTH_28_16_SHIFT); regmap_update_bits(gauge_dev->regmap, PMIC_FG_IAVG_HTH_15_00_ADDR, PMIC_FG_IAVG_HTH_15_00_MASK << PMIC_FG_IAVG_HTH_15_00_SHIFT, fg_iavg_hth_15_00 << PMIC_FG_IAVG_HTH_15_00_SHIFT); enable_gauge_irq(gauge_dev, FG_IAVG_H_IRQ); if (iavg_lt > 0) enable_gauge_irq(gauge_dev, FG_IAVG_L_IRQ); else disable_gauge_irq(gauge_dev, FG_IAVG_L_IRQ); bm_debug("[FG_IAVG_INT][%s] iavg %d iavg_gap %d iavg_ht %lld iavg_lt %lld fg_iavg_reg_ht %lld fg_iavg_reg_lt %lld\n", __func__, iavg, iavg_gap, iavg_ht, iavg_lt, fg_iavg_reg_ht, fg_iavg_reg_lt); bm_debug("[FG_IAVG_INT][%s] lt_28_16 0x%x lt_15_00 0x%x ht_28_16 0x%x ht_15_00 0x%x\n", __func__, fg_iavg_lth_28_16, fg_iavg_lth_15_00, fg_iavg_hth_28_16, fg_iavg_hth_15_00); enable_gauge_irq(gauge_dev, FG_IAVG_H_IRQ); if (iavg_lt > 0) enable_gauge_irq(gauge_dev, FG_IAVG_L_IRQ); else disable_gauge_irq(gauge_dev, FG_IAVG_L_IRQ); return 0; } void read_fg_hw_info_ncar(struct mtk_gauge *gauge_dev) { unsigned int uvalue32_NCAR = 0; unsigned int uvalue32_NCAR_MSB = 0; unsigned int temp_NCAR_15_0 = 0; unsigned int temp_NCAR_31_16 = 0; signed int dvalue_NCAR = 0; long long Temp_Value = 0; regmap_read(gauge_dev->regmap, PMIC_FG_NCAR_15_00_ADDR, &temp_NCAR_15_0); temp_NCAR_15_0 = (temp_NCAR_15_0 & (PMIC_FG_NCAR_15_00_MASK << PMIC_FG_NCAR_15_00_SHIFT)) >> PMIC_FG_NCAR_15_00_SHIFT; regmap_read(gauge_dev->regmap, PMIC_FG_NCAR_31_16_ADDR, &temp_NCAR_31_16); temp_NCAR_31_16 = (temp_NCAR_31_16 & (PMIC_FG_NCAR_31_16_MASK << PMIC_FG_NCAR_31_16_SHIFT)) >> PMIC_FG_NCAR_31_16_SHIFT; uvalue32_NCAR = temp_NCAR_15_0 & 0xffff; uvalue32_NCAR |= (temp_NCAR_31_16 & 0x7fff) << 16; uvalue32_NCAR_MSB = (temp_NCAR_31_16 & 0x8000) >> 15; /*calculate the real world data */ dvalue_NCAR = (signed int) uvalue32_NCAR; if (uvalue32_NCAR == 0) { Temp_Value = 0; } else if (uvalue32_NCAR_MSB == 0x1) { /* dis-charging */ Temp_Value = (long long) (dvalue_NCAR - 0x7fffffff); /* keep negative value */ Temp_Value = Temp_Value - (Temp_Value * 2); } else { /*charging */ Temp_Value = (long long) dvalue_NCAR; } /* 0.1 mAh */ #if defined(__LP64__) || defined(_LP64) Temp_Value = Temp_Value * UNIT_CHARGE / 1000; #else Temp_Value = div_s64(Temp_Value * UNIT_CHARGE, 1000); #endif #if defined(__LP64__) || defined(_LP64) do_div(Temp_Value, 10); Temp_Value = Temp_Value + 5; do_div(Temp_Value, 10); #else Temp_Value = div_s64(Temp_Value, 10); Temp_Value = Temp_Value + 5; Temp_Value = div_s64(Temp_Value, 10); #endif if (uvalue32_NCAR_MSB == 0x1) dvalue_NCAR = (signed int) (Temp_Value - (Temp_Value * 2)); else dvalue_NCAR = (signed int) Temp_Value; /*Auto adjust value*/ if (gauge_dev->hw_status.r_fg_value != DEFAULT_R_FG) dvalue_NCAR = (dvalue_NCAR * DEFAULT_R_FG) / gauge_dev->hw_status.r_fg_value; gauge_dev->fg_hw_info.ncar = ((dvalue_NCAR * gauge_dev->hw_status.car_tune_value) / 1000); } static int coulomb_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { unsigned int uvalue32_car = 0; unsigned int uvalue32_car_msb = 0; unsigned int temp_car_15_0 = 0; unsigned int temp_car_31_16 = 0; signed int dvalue_CAR = 0; long long temp_value = 0; int r_fg_value; int car_tune_value; r_fg_value = gauge->hw_status.r_fg_value; car_tune_value = gauge->hw_status.car_tune_value; pre_gauge_update(gauge); regmap_read(gauge->regmap, PMIC_FG_CAR_15_00_ADDR, &temp_car_15_0); temp_car_15_0 = (temp_car_15_0 & (PMIC_FG_CAR_15_00_MASK << PMIC_FG_CAR_15_00_SHIFT)) >> PMIC_FG_CAR_15_00_SHIFT; regmap_read(gauge->regmap, PMIC_FG_CAR_31_16_ADDR, &temp_car_31_16); temp_car_31_16 = (temp_car_31_16 & (PMIC_FG_CAR_31_16_MASK << PMIC_FG_CAR_31_16_SHIFT)) >> PMIC_FG_CAR_31_16_SHIFT; post_gauge_update(gauge); uvalue32_car = temp_car_15_0 & 0xffff; uvalue32_car |= (temp_car_31_16 & 0x7fff) << 16; uvalue32_car_msb = (temp_car_31_16 & 0x8000) >> 15; /*calculate the real world data */ dvalue_CAR = (signed int) uvalue32_car; if (uvalue32_car == 0) { temp_value = 0; } else if (uvalue32_car_msb == 0x1) { /* dis-charging */ temp_value = (long long) (dvalue_CAR - 0x7fffffff); /* keep negative value */ temp_value = temp_value - (temp_value * 2); } else { /*charging */ temp_value = (long long) dvalue_CAR; } #if defined(__LP64__) || defined(_LP64) temp_value = temp_value * UNIT_CHARGE / 1000; #else temp_value = div_s64(temp_value * UNIT_CHARGE, 1000); #endif #if defined(__LP64__) || defined(_LP64) do_div(temp_value, 10); temp_value = temp_value + 5; do_div(temp_value, 10); #else temp_value = div_s64(temp_value, 10); temp_value = temp_value + 5; temp_value = div_s64(temp_value, 10); #endif if (uvalue32_car_msb == 0x1) dvalue_CAR = (signed int) (temp_value - (temp_value * 2)); /* keep negative value */ else dvalue_CAR = (signed int) temp_value; bm_debug("[%s]l:0x%x h:0x%x val:%d msb:%d car:%d\n", __func__, temp_car_15_0, temp_car_31_16, uvalue32_car, uvalue32_car_msb, dvalue_CAR); /*Auto adjust value*/ if (r_fg_value != DEFAULT_R_FG) { bm_debug("[%s] Auto adjust value deu to the Rfg is %d\n Ori CAR=%d", __func__, r_fg_value, dvalue_CAR); dvalue_CAR = (dvalue_CAR * DEFAULT_R_FG) / r_fg_value; bm_debug("[%s] new CAR=%d\n", __func__, dvalue_CAR); } dvalue_CAR = ((dvalue_CAR * car_tune_value) / 1000); bm_debug("[%s] CAR=%d r_fg_value=%d car_tune_value=%d\n", __func__, dvalue_CAR, r_fg_value, car_tune_value); *val = dvalue_CAR; return 0; } int hw_info_set(struct mtk_gauge *gauge_dev, struct mtk_gauge_sysfs_field_info *attr, int en) { int ret; int is_iavg_valid; int avg_current; int iavg_th; unsigned int time; struct gauge_hw_status *gauge_status; gauge_status = &gauge_dev->hw_status; /* Set Read Latchdata */ post_gauge_update(gauge_dev); /* Current_1 */ read_fg_hw_info_current_1(gauge_dev); /* Current_2 */ read_fg_hw_info_current_2(gauge_dev); /* curr_out = pmic_get_register_value(PMIC_FG_CURRENT_OUT); */ /* fg_offset = pmic_get_register_value(PMIC_FG_OFFSET); */ /* Iavg */ average_current_get(gauge_dev, NULL, &avg_current); is_iavg_valid = gauge_dev->fg_hw_info.current_avg_valid; if ((is_iavg_valid == 1) && (gauge_status->iavg_intr_flag == 0)) { bm_debug("[read_fg_hw_info]set first fg_set_iavg_intr %d %d\n", is_iavg_valid, gauge_status->iavg_intr_flag); gauge_status->iavg_intr_flag = 1; iavg_th = gauge_dev->gm->fg_cust_data.diff_iavg_th; ret = fg_set_iavg_intr(gauge_dev, &iavg_th); } else if (is_iavg_valid == 0) { gauge_status->iavg_intr_flag = 0; disable_gauge_irq(gauge_dev, FG_IAVG_H_IRQ); disable_gauge_irq(gauge_dev, FG_IAVG_L_IRQ); bm_debug( "[read_fg_hw_info] doublecheck first fg_set_iavg_intr %d %d\n", is_iavg_valid, gauge_status->iavg_intr_flag); } bm_debug("[read_fg_hw_info] thirdcheck first fg_set_iavg_intr %d %d\n", is_iavg_valid, gauge_status->iavg_intr_flag); /* Ncar */ read_fg_hw_info_ncar(gauge_dev); /* recover read */ post_gauge_update(gauge_dev); coulomb_get(gauge_dev, NULL, &gauge_dev->fg_hw_info.car); fgauge_get_time(gauge_dev, &time); gauge_dev->fg_hw_info.time = time; bm_debug("[FGADC_intr_end][read_fg_hw_info] curr_1 %d curr_2 %d Iavg %d sign %d car %d ncar %d time %d\n", gauge_dev->fg_hw_info.current_1, gauge_dev->fg_hw_info.current_2, gauge_dev->fg_hw_info.current_avg, gauge_dev->fg_hw_info.current_avg_sign, gauge_dev->fg_hw_info.car, gauge_dev->fg_hw_info.ncar, gauge_dev->fg_hw_info.time); return 0; } int nafg_en_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { static int cnt; bm_debug("%s %d %d\n", __func__, cnt, val); if (val != 0) cnt++; else cnt--; if (val != 0) { val = 1; enable_gauge_irq(gauge, NAFG_IRQ); bm_debug("[%s]enable:%d\n", __func__, val); } else { disable_gauge_irq(gauge, NAFG_IRQ); bm_debug("[%s]disable:%d\n", __func__, val); } regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_IRQ_EN_ADDR, PMIC_AUXADC_NAG_IRQ_EN_MASK << PMIC_AUXADC_NAG_IRQ_EN_SHIFT, val << PMIC_AUXADC_NAG_IRQ_EN_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_EN_ADDR, PMIC_AUXADC_NAG_EN_MASK << PMIC_AUXADC_NAG_EN_SHIFT, val << PMIC_AUXADC_NAG_EN_SHIFT); return 0; } static int calculate_car_tune(struct mtk_gauge *gauge) { int cali_car_tune; long long sum_all = 0; unsigned long long temp_sum = 0; int avg_cnt = 0; int i; unsigned int uvalue32 = 0; signed int dvalue = 0; long long Temp_Value1 = 0; unsigned long long Temp_Value2 = 0; long long current_from_ADC = 0; bm_err("%s, meta_current=%d,\n", __func__, gauge->hw_status.meta_current); if (gauge->hw_status.meta_current != 0) { for (i = 0; i < CALI_CAR_TUNE_AVG_NUM; i++) { uvalue32 = instant_current_for_car_tune(gauge); if (uvalue32 != 0) { if (uvalue32 <= 0x8000) { Temp_Value1 = (long long)uvalue32; bm_err("[111]uvalue32 %d Temp_Value1 %lld\n", uvalue32, Temp_Value1); } else if (uvalue32 > 0x8000) { Temp_Value1 = (long long) (65535 - uvalue32); bm_err("[222]uvalue32 %d Temp_Value1 %lld\n", uvalue32, Temp_Value1); } sum_all += Temp_Value1; avg_cnt++; /*****************/ bm_err("[333]uvalue32 %d Temp_Value1 %lld sum_all %lld\n", uvalue32, Temp_Value1, sum_all); /*****************/ } mdelay(30); } /*calculate the real world data */ /*current_from_ADC = sum_all / avg_cnt;*/ temp_sum = sum_all; bm_err("[444]sum_all %lld temp_sum %lld avg_cnt %d current_from_ADC %lld\n", sum_all, temp_sum, avg_cnt, current_from_ADC); if (avg_cnt != 0) do_div(temp_sum, avg_cnt); current_from_ADC = temp_sum; bm_err("[555]sum_all %lld temp_sum %lld avg_cnt %d current_from_ADC %lld\n", sum_all, temp_sum, avg_cnt, current_from_ADC); Temp_Value2 = current_from_ADC * UNIT_FGCURRENT; bm_err("[555]Temp_Value2 %lld current_from_ADC %lld UNIT_FGCURRENT %d\n", Temp_Value2, current_from_ADC, UNIT_FGCURRENT); /* Move 100 from denominator to cali_car_tune's numerator */ /*do_div(Temp_Value2, 1000000);*/ do_div(Temp_Value2, 10000); bm_err("[666]Temp_Value2 %lld current_from_ADC %lld UNIT_FGCURRENT %d\n", Temp_Value2, current_from_ADC, UNIT_FGCURRENT); dvalue = (unsigned int) Temp_Value2; /* Auto adjust value */ if (gauge->hw_status.r_fg_value != 100) dvalue = (dvalue * 100) / gauge->hw_status.r_fg_value; bm_err("[666]dvalue %d fg_cust_data.r_fg_value %d\n", dvalue, gauge->hw_status.r_fg_value); /* Move 100 from denominator to cali_car_tune's numerator */ /*cali_car_tune = meta_input_cali_current * 1000 / dvalue;*/ if (dvalue != 0) { cali_car_tune = gauge->hw_status.meta_current * 1000 * 100 / dvalue; bm_err("[777]dvalue %d fg_cust_data.r_fg_value %d cali_car_tune %d\n", dvalue, gauge->hw_status.r_fg_value, cali_car_tune); gauge->hw_status.tmp_car_tune = cali_car_tune; bm_err( "[fgauge_meta_cali_car_tune_value][%d] meta:%d, adc:%lld, UNI_FGCUR:%d, r_fg_value:%d\n", cali_car_tune, gauge->hw_status.meta_current, current_from_ADC, UNIT_FGCURRENT, gauge->hw_status.r_fg_value); } return 0; } return 0; } int info_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { int ret = 0; if (attr->prop == GAUGE_PROP_CAR_TUNE_VALUE && (val > 500 && val < 1500)) { /* send external_current for calculate_car_tune */ gauge->hw_status.meta_current = val; calculate_car_tune(gauge); } else if (attr->prop == GAUGE_PROP_R_FG_VALUE && val != 0) gauge->hw_status.r_fg_value = val; else if (attr->prop == GAUGE_PROP_VBAT2_DETECT_TIME) gauge->hw_status.vbat2_det_time = val; else if (attr->prop == GAUGE_PROP_VBAT2_DETECT_COUNTER) gauge->hw_status.vbat2_det_counter = val; else ret = fgauge_set_info(gauge, attr->prop, val); return ret; } int info_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int ret = 0; if (attr->prop == GAUGE_PROP_CAR_TUNE_VALUE) *val = gauge->hw_status.tmp_car_tune; else if (attr->prop == GAUGE_PROP_R_FG_VALUE) *val = gauge->hw_status.r_fg_value; else if (attr->prop == GAUGE_PROP_VBAT2_DETECT_TIME) *val = gauge->hw_status.vbat2_det_time; else if (attr->prop == GAUGE_PROP_VBAT2_DETECT_COUNTER) *val = gauge->hw_status.vbat2_det_counter; else ret = fgauge_get_info(gauge, attr->prop, val); return ret; } static int get_ptim_current(struct mtk_gauge *gauge) { unsigned int reg_value; int dvalue; int r_fg_value; int car_tune_value; r_fg_value = gauge->hw_status.r_fg_value; car_tune_value = gauge->hw_status.car_tune_value; regmap_read(gauge->regmap, PMIC_FG_R_CURR_ADDR, ®_value); reg_value = (reg_value & (PMIC_FG_R_CURR_MASK << PMIC_FG_R_CURR_SHIFT)) >> PMIC_FG_R_CURR_SHIFT; dvalue = reg_to_current(gauge, reg_value); /* Auto adjust value */ if (r_fg_value != DEFAULT_R_FG) dvalue = (dvalue * DEFAULT_R_FG) / r_fg_value; dvalue = ((dvalue * car_tune_value) / 1000); /* ptim current >0 means discharge, different to bat_current */ dvalue = dvalue * -1; bm_debug("[%s]ptim current:%d\n", __func__, dvalue); return dvalue; } static enum power_supply_property gauge_properties[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_ENERGY_EMPTY, }; static int psy_gauge_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct mtk_gauge *gauge; struct mtk_battery *gm; gauge = (struct mtk_gauge *)power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_PRESENT: /* store disableGM30 status to mtk-gauge psy for DLPT */ if (gauge == NULL || gauge->gm == NULL) val->intval = 0; else val->intval = gauge->gm->disableGM30; break; case POWER_SUPPLY_PROP_ONLINE: if (gauge == NULL || gauge->gm == NULL) val->intval = 0; else val->intval = gauge->gm->disableGM30; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = get_ptim_current(gauge); break; case POWER_SUPPLY_PROP_ENERGY_EMPTY: gm = gauge->gm; if (gm != NULL) val->intval = gm->sdc.shutdown_status.is_dlpt_shutdown; break; default: return -EINVAL; } return 0; } static int psy_gauge_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { int ret = 0; struct mtk_gauge *gauge; struct mtk_battery *gm; gauge = (struct mtk_gauge *)power_supply_get_drvdata(psy); switch (psp) { case POWER_SUPPLY_PROP_ONLINE: pr_notice("%s: %d %d\n", __func__, psp, val->intval); break; case POWER_SUPPLY_PROP_ENERGY_EMPTY: gm = gauge->gm; if (gm != NULL && val->intval == 1) set_shutdown_cond(gm, DLPT_SHUTDOWN); break; default: ret = -EINVAL; break; } return ret; } static void fgauge_read_RTC_boot_status(struct mtk_gauge *gauge) { unsigned int hw_id; u8 spare0_reg = 0; unsigned int spare0_reg_b13 = 0; u8 spare3_reg = 0; int spare3_reg_valid = 0; regmap_read(gauge->regmap, PMIC_HWCID_ADDR, &hw_id); hw_id = (hw_id & (PMIC_HWCID_MASK << PMIC_HWCID_SHIFT)) >> PMIC_HWCID_SHIFT; spare0_reg = get_rtc_spare0_fg_value(gauge); spare3_reg = get_rtc_spare_fg_value(gauge); gauge->hw_status.gspare0_reg = spare0_reg; gauge->hw_status.gspare3_reg = spare3_reg; spare3_reg_valid = (spare3_reg & 0x80) >> 7; if (spare3_reg_valid == 0) gauge->hw_status.rtc_invalid = 1; else gauge->hw_status.rtc_invalid = 0; if (gauge->hw_status.rtc_invalid == 0) { spare0_reg_b13 = (spare0_reg & 0x20) >> 5; if ((hw_id & 0xff00) == 0x3500) gauge->hw_status.is_bat_plugout = spare0_reg_b13; else gauge->hw_status.is_bat_plugout = !spare0_reg_b13; gauge->hw_status.bat_plug_out_time = spare0_reg & 0x1f; } else { gauge->hw_status.is_bat_plugout = 1; gauge->hw_status.bat_plug_out_time = 31; } bm_err("[%s]rtc_invalid %d plugout %d plugout_time %d spare3 0x%x spare0 0x%x hw_id 0x%x\n", __func__, gauge->hw_status.rtc_invalid, gauge->hw_status.is_bat_plugout, gauge->hw_status.bat_plug_out_time, spare3_reg, spare0_reg, hw_id); } static int reset_fg_rtc_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { int hw_id; int temp_value; u8 spare0_reg, after_rst_spare0_reg; u8 spare3_reg, after_rst_spare3_reg; regmap_read(gauge->regmap, PMIC_HWCID_ADDR, &hw_id); hw_id = (hw_id & (PMIC_HWCID_MASK << PMIC_HWCID_SHIFT)) >> PMIC_HWCID_SHIFT; fgauge_read_RTC_boot_status(gauge); /* read spare0 */ spare0_reg = get_rtc_spare0_fg_value(gauge); /* raise 15b to reset */ if ((hw_id & 0xff00) == 0x3500) { temp_value = 0x80; set_rtc_spare0_fg_value(gauge, temp_value); mdelay(1); temp_value = 0x00; set_rtc_spare0_fg_value(gauge, temp_value); } else { temp_value = 0x80; set_rtc_spare0_fg_value(gauge, temp_value); mdelay(1); temp_value = 0x20; set_rtc_spare0_fg_value(gauge, temp_value); } /* read spare0 again */ after_rst_spare0_reg = get_rtc_spare0_fg_value(gauge); /* read spare3 */ spare3_reg = get_rtc_spare_fg_value(gauge); /* set spare3 0x7f */ set_rtc_spare_fg_value(gauge, spare3_reg | 0x80); /* read spare3 again */ after_rst_spare3_reg = get_rtc_spare_fg_value(gauge); bm_err("[fgauge_read_RTC_boot_status] spare0 0x%x 0x%x, spare3 0x%x 0x%x\n", spare0_reg, after_rst_spare0_reg, spare3_reg, after_rst_spare3_reg); return 0; } static int read_hw_ocv_6359_plug_in(struct mtk_gauge *gauge) { signed int adc_rdy = 0; signed int adc_result_reg = 0; signed int adc_result = 0; int sel; /* 6359 no need to switch SWCHR_POWER_PATH, only 56 57 */ regmap_read(gauge->regmap, PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_ADDR, &adc_rdy); adc_rdy = (adc_rdy & (PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_MASK << PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_SHIFT)) >> PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_PCHR_SHIFT; regmap_read(gauge->regmap, PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_ADDR, &adc_result_reg); adc_result_reg = (adc_result_reg & (PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_MASK << PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_SHIFT)) >> PMIC_AUXADC_ADC_OUT_BAT_PLUGIN_PCHR_SHIFT; regmap_read(gauge->regmap, PMIC_RG_HK_STRUP_AUXADC_START_SEL_ADDR, &sel); sel = (sel & (PMIC_RG_HK_STRUP_AUXADC_START_SEL_MASK << PMIC_RG_HK_STRUP_AUXADC_START_SEL_SHIFT)) >> PMIC_RG_HK_STRUP_AUXADC_START_SEL_SHIFT; adc_result = reg_to_mv_value(adc_result_reg); bm_err("[oam] %s (pchr): adc_result_reg=%d, adc_result=%d, start_sel=%d, rdy=%d\n", __func__, adc_result_reg, adc_result, sel, adc_rdy); if (adc_rdy == 1) { regmap_update_bits(gauge->regmap, PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_ADDR, PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_MASK << PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_SHIFT, 1 << PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_SHIFT); mdelay(1); regmap_update_bits(gauge->regmap, PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_ADDR, PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_MASK << PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_SHIFT, 0 << PMIC_AUXADC_ADC_RDY_BAT_PLUGIN_CLR_SHIFT); } return adc_result; } static int read_hw_ocv_6359_power_on(struct mtk_gauge *gauge) { signed int adc_result_rdy = 0; signed int adc_result_reg = 0; signed int adc_result = 0; int sel; regmap_read(gauge->regmap, PMIC_AUXADC_ADC_RDY_PWRON_PCHR_ADDR, &adc_result_rdy); adc_result_rdy = (adc_result_rdy & (PMIC_AUXADC_ADC_RDY_PWRON_PCHR_MASK << PMIC_AUXADC_ADC_RDY_PWRON_PCHR_SHIFT)) >> PMIC_AUXADC_ADC_RDY_PWRON_PCHR_SHIFT; regmap_read(gauge->regmap, PMIC_AUXADC_ADC_OUT_PWRON_PCHR_ADDR, &adc_result_reg); adc_result_reg = (adc_result_reg & (PMIC_AUXADC_ADC_OUT_PWRON_PCHR_MASK << PMIC_AUXADC_ADC_OUT_PWRON_PCHR_SHIFT)) >> PMIC_AUXADC_ADC_OUT_PWRON_PCHR_SHIFT; regmap_read(gauge->regmap, PMIC_RG_HK_STRUP_AUXADC_START_SEL_ADDR, &sel); sel = (sel & (PMIC_RG_HK_STRUP_AUXADC_START_SEL_MASK << PMIC_RG_HK_STRUP_AUXADC_START_SEL_SHIFT)) >> PMIC_RG_HK_STRUP_AUXADC_START_SEL_SHIFT; adc_result = reg_to_mv_value(adc_result_reg); bm_err("[oam] %s (pchr) : adc_result_reg=%d, adc_result=%d, start_sel=%d, rdy=%d\n", __func__, adc_result_reg, adc_result, sel, adc_result_rdy); if (adc_result_rdy == 1) { regmap_update_bits(gauge->regmap, PMIC_AUXADC_ADC_RDY_PWRON_CLR_ADDR, PMIC_AUXADC_ADC_RDY_PWRON_CLR_MASK << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT, 1 << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT); mdelay(1); regmap_update_bits(gauge->regmap, PMIC_AUXADC_ADC_RDY_PWRON_CLR_ADDR, PMIC_AUXADC_ADC_RDY_PWRON_CLR_MASK << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT, 0 << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT); } return adc_result; } static int read_hw_ocv_6359_power_on_rdy(struct mtk_gauge *gauge) { int pon_rdy = 0; regmap_read(gauge->regmap, PMIC_AUXADC_ADC_RDY_PWRON_PCHR_ADDR, &pon_rdy); pon_rdy = (pon_rdy & (PMIC_AUXADC_ADC_RDY_PWRON_PCHR_MASK << PMIC_AUXADC_ADC_RDY_PWRON_PCHR_SHIFT)) >> PMIC_AUXADC_ADC_RDY_PWRON_PCHR_SHIFT; bm_err("[%s] pwron_PCHR_rdy %d\n", __func__, pon_rdy); return pon_rdy; } static int nafg_cnt_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *nag_cnt) { signed int NAG_C_DLTV_CNT; signed int NAG_C_DLTV_CNT_H; /*AUXADC_NAG_4*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_CNT_15_0_ADDR, &NAG_C_DLTV_CNT); /*AUXADC_NAG_5*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_CNT_25_16_ADDR, &NAG_C_DLTV_CNT_H); *nag_cnt = (NAG_C_DLTV_CNT & PMIC_AUXADC_NAG_CNT_15_0_MASK) + ((NAG_C_DLTV_CNT_H & PMIC_AUXADC_NAG_CNT_25_16_MASK) << 16); bm_debug("[fg_bat_nafg][%s] %d [25_16 %d 15_0 %d]\n", __func__, *nag_cnt, NAG_C_DLTV_CNT_H, NAG_C_DLTV_CNT); return 0; } static int nafg_dltv_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *nag_dltv) { signed int nag_dltv_reg_value; signed int nag_dltv_mv_value; short reg_value; /*AUXADC_NAG_4*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_DLTV_ADDR, &nag_dltv_reg_value); reg_value = nag_dltv_reg_value & 0xffff; nag_dltv_mv_value = reg_to_mv_value(nag_dltv_reg_value); *nag_dltv = nag_dltv_mv_value; bm_debug("[fg_bat_nafg][%s] mV:Reg [%d:%d] [%d:%d]\n", __func__, nag_dltv_mv_value, nag_dltv_reg_value, reg_to_mv_value(reg_value), reg_value); return 0; } static int nafg_c_dltv_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *nafg_c_dltv) { signed int nag_c_dltv_value; signed int nag_c_dltv_value_h; signed int nag_c_dltv_reg_value; signed int nag_c_dltv_mv_value; bool bcheckbit10; /*AUXADC_NAG_7*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_C_DLTV_15_0_ADDR, &nag_c_dltv_value); /*AUXADC_NAG_8*/ regmap_read(gauge->regmap, PMIC_AUXADC_NAG_C_DLTV_26_16_ADDR, &nag_c_dltv_value_h); nag_c_dltv_value_h = (nag_c_dltv_value_h & PMIC_AUXADC_NAG_C_DLTV_26_16_MASK); bcheckbit10 = nag_c_dltv_value_h & 0x0400; if (gauge->nafg_corner == 1) { nag_c_dltv_reg_value = (nag_c_dltv_value & 0x7fff); nag_c_dltv_mv_value = reg_to_mv_value(nag_c_dltv_reg_value); *nafg_c_dltv = nag_c_dltv_mv_value; bm_debug("[fg_bat_nafg][%s] mV:Reg[%d:%d] [b10:%d][26_16(0x%04x) 15_00(0x%04x)] corner:%d\n", __func__, nag_c_dltv_mv_value, nag_c_dltv_reg_value, bcheckbit10, nag_c_dltv_value_h, nag_c_dltv_value, gauge->nafg_corner); return 0; } else if (gauge->nafg_corner == 2) { nag_c_dltv_reg_value = (nag_c_dltv_value - 32768); nag_c_dltv_mv_value = reg_to_mv_value(nag_c_dltv_reg_value); *nafg_c_dltv = nag_c_dltv_mv_value; bm_debug("[fg_bat_nafg][%s] mV:Reg[%d:%d] [b10:%d][26_16(0x%04x) 15_00(0x%04x)] corner:%d\n", __func__, nag_c_dltv_mv_value, nag_c_dltv_reg_value, bcheckbit10, nag_c_dltv_value_h, nag_c_dltv_value, gauge->nafg_corner); return 0; } if (bcheckbit10 == 0) nag_c_dltv_reg_value = (nag_c_dltv_value & 0xffff) + ((nag_c_dltv_value_h & 0x07ff) << 16); else nag_c_dltv_reg_value = (nag_c_dltv_value & 0xffff) + (((nag_c_dltv_value_h | 0xf800) & 0xffff) << 16); nag_c_dltv_mv_value = reg_to_mv_value(nag_c_dltv_reg_value); *nafg_c_dltv = nag_c_dltv_mv_value; bm_debug("[fg_bat_nafg][%s] mV:Reg[%d:%d] [b10:%d][26_16(0x%04x) 15_00(0x%04x)] corner:%d\n", __func__, nag_c_dltv_mv_value, nag_c_dltv_reg_value, bcheckbit10, nag_c_dltv_value_h, nag_c_dltv_value, gauge->nafg_corner); return 0; } static int zcv_get(struct mtk_gauge *gauge_dev, struct mtk_gauge_sysfs_field_info *attr, int *zcv) { signed int adc_result_reg = 0; signed int adc_result = 0; regmap_read(gauge_dev->regmap, PMIC_AUXADC_ADC_OUT_FGADC_PCHR_ADDR, &adc_result_reg); adc_result_reg = (adc_result_reg & (PMIC_AUXADC_ADC_OUT_FGADC_PCHR_MASK << PMIC_AUXADC_ADC_OUT_FGADC_PCHR_SHIFT)) >> PMIC_AUXADC_ADC_OUT_FGADC_PCHR_SHIFT; adc_result = reg_to_mv_value(adc_result_reg); bm_err("[oam] %s BATSNS (pchr):adc_result_reg=%d, adc_result=%d\n", __func__, adc_result_reg, adc_result); *zcv = adc_result; return 0; } static int get_charger_zcv(struct mtk_gauge *gauge_dev) { struct power_supply *chg_psy; union power_supply_propval val; int ret = 0; chg_psy = power_supply_get_by_name("mtk-master-charger"); if (chg_psy == NULL) { bm_err("[%s] can get charger psy\n", __func__); return -ENODEV; } ret = power_supply_get_property(chg_psy, POWER_SUPPLY_PROP_VOLTAGE_BOOT, &val); bm_err("[%s]_hw_ocv_chgin=%d, ret=%d\n", __func__, val.intval, ret); return val.intval; } static int boot_zcv_get(struct mtk_gauge *gauge_dev, struct mtk_gauge_sysfs_field_info *attr, int *val) { int _hw_ocv, _sw_ocv; int _hw_ocv_src; int _prev_hw_ocv, _prev_hw_ocv_src; int _hw_ocv_rdy; int _flag_unreliable; int _hw_ocv_59_pon; int _hw_ocv_59_plugin; int _hw_ocv_59_pon_rdy; int _hw_ocv_chgin; int _hw_ocv_chgin_rdy; int now_temp; int now_thr; int tmp_hwocv_chgin = 0; bool fg_is_charger_exist; struct mtk_battery *gm; struct zcv_data *zcvinfo; struct gauge_hw_status *p; gm = gauge_dev->gm; p = &gauge_dev->hw_status; zcvinfo = &gauge_dev->zcv_info; _hw_ocv_59_pon_rdy = read_hw_ocv_6359_power_on_rdy(gauge_dev); _hw_ocv_59_pon = read_hw_ocv_6359_power_on(gauge_dev); _hw_ocv_59_plugin = read_hw_ocv_6359_plug_in(gauge_dev); tmp_hwocv_chgin = get_charger_zcv(gauge_dev); if (tmp_hwocv_chgin != -ENODEV) _hw_ocv_chgin = tmp_hwocv_chgin / 100; else _hw_ocv_chgin = 0; now_temp = gm->bs_data.bat_batt_temp; if (gm == NULL) now_thr = 300; else { if (now_temp > gm->ext_hwocv_swocv_lt_temp) now_thr = gm->ext_hwocv_swocv; else now_thr = gm->ext_hwocv_swocv_lt; } if (_hw_ocv_chgin < 25000) _hw_ocv_chgin_rdy = 0; else _hw_ocv_chgin_rdy = 1; /* if preloader records charge in, need to using subpmic as hwocv */ fgauge_get_info( gauge_dev, GAUGE_PROP_PL_CHARGING_STATUS, &zcvinfo->pl_charging_status); fgauge_set_info( gauge_dev, GAUGE_PROP_PL_CHARGING_STATUS, 0); fgauge_get_info( gauge_dev, GAUGE_PROP_MONITER_PLCHG_STATUS, &zcvinfo->moniter_plchg_bit); fgauge_set_info( gauge_dev, GAUGE_PROP_MONITER_PLCHG_STATUS, 0); if (zcvinfo->pl_charging_status == 1) fg_is_charger_exist = 1; else fg_is_charger_exist = 0; _hw_ocv = _hw_ocv_59_pon; _sw_ocv = gauge_dev->hw_status.sw_ocv; /* _sw_ocv = get_sw_ocv();*/ _hw_ocv_src = FROM_PMIC_PON_ON; _prev_hw_ocv = _hw_ocv; _prev_hw_ocv_src = FROM_PMIC_PON_ON; _flag_unreliable = 0; if (fg_is_charger_exist) { _hw_ocv_rdy = _hw_ocv_59_pon_rdy; if (_hw_ocv_rdy == 1) { if (_hw_ocv_chgin_rdy == 1) { _hw_ocv = _hw_ocv_chgin; _hw_ocv_src = FROM_CHR_IN; } else { _hw_ocv = _hw_ocv_59_pon; _hw_ocv_src = FROM_PMIC_PON_ON; } if (abs(_hw_ocv - _sw_ocv) > now_thr) { _prev_hw_ocv = _hw_ocv; _prev_hw_ocv_src = _hw_ocv_src; _hw_ocv = _sw_ocv; _hw_ocv_src = FROM_SW_OCV; p->flag_hw_ocv_unreliable = true; _flag_unreliable = 1; } } else { /* fixme: swocv is workaround */ /* plug charger poweron but charger not ready */ /* should use swocv to workaround */ _hw_ocv = _sw_ocv; _hw_ocv_src = FROM_SW_OCV; if (_hw_ocv_chgin_rdy != 1) { if (abs(_hw_ocv - _sw_ocv) > now_thr) { _prev_hw_ocv = _hw_ocv; _prev_hw_ocv_src = _hw_ocv_src; _hw_ocv = _sw_ocv; _hw_ocv_src = FROM_SW_OCV; p->flag_hw_ocv_unreliable = true; _flag_unreliable = 1; } } } } else { if (_hw_ocv_59_pon_rdy == 0) { _hw_ocv = _sw_ocv; _hw_ocv_src = FROM_SW_OCV; } } /* final chance to check hwocv */ if (gm != NULL) if (_hw_ocv < 28000 && (gm->disableGM30 == 0)) { bm_err("[%s] ERROR, _hw_ocv=%d src:%d, force use swocv\n", __func__, _hw_ocv, _hw_ocv_src); _hw_ocv = _sw_ocv; _hw_ocv_src = FROM_SW_OCV; } *val = _hw_ocv; zcvinfo->charger_zcv = _hw_ocv_chgin; zcvinfo->pmic_rdy = _hw_ocv_59_pon_rdy; zcvinfo->pmic_zcv = _hw_ocv_59_pon; zcvinfo->pmic_in_zcv = _hw_ocv_59_plugin; zcvinfo->swocv = _sw_ocv; zcvinfo->zcv_from = _hw_ocv_src; zcvinfo->zcv_tmp = now_temp; if (zcvinfo->zcv_1st_read == false) { zcvinfo->charger_zcv_1st = zcvinfo->charger_zcv; zcvinfo->pmic_rdy_1st = zcvinfo->pmic_rdy; zcvinfo->pmic_zcv_1st = zcvinfo->pmic_zcv; zcvinfo->pmic_in_zcv_1st = zcvinfo->pmic_in_zcv; zcvinfo->swocv_1st = zcvinfo->swocv; zcvinfo->zcv_from_1st = zcvinfo->zcv_from; zcvinfo->zcv_tmp_1st = zcvinfo->zcv_tmp; zcvinfo->zcv_1st_read = true; } gauge_dev->fg_hw_info.pmic_zcv = _hw_ocv_59_pon; gauge_dev->fg_hw_info.pmic_zcv_rdy = _hw_ocv_59_pon_rdy; gauge_dev->fg_hw_info.charger_zcv = _hw_ocv_chgin; gauge_dev->fg_hw_info.hw_zcv = _hw_ocv; bm_err("[%s] g_fg_is_charger_exist %d _hw_ocv_chgin_rdy %d pl:%d %d\n", __func__, fg_is_charger_exist, _hw_ocv_chgin_rdy, zcvinfo->pl_charging_status, zcvinfo->moniter_plchg_bit); bm_err("[%s] _hw_ocv %d _sw_ocv %d now_thr %d\n", __func__, _prev_hw_ocv, _sw_ocv, now_thr); bm_err("[%s] _hw_ocv %d _hw_ocv_src %d _prev_hw_ocv %d _prev_hw_ocv_src %d _flag_unreliable %d\n", __func__, _hw_ocv, _hw_ocv_src, _prev_hw_ocv, _prev_hw_ocv_src, _flag_unreliable); bm_err("[%s] _hw_ocv_59_pon_rdy %d _hw_ocv_59_pon %d _hw_ocv_59_plugin %d _hw_ocv_chgin %d _sw_ocv %d now_temp %d now_thr %d\n", __func__, _hw_ocv_59_pon_rdy, _hw_ocv_59_pon, _hw_ocv_59_plugin, _hw_ocv_chgin, _sw_ocv, now_temp, now_thr); return 0; } static int initial_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { int bat_flag = 0; int is_charger_exist; int rev_val = 0; regmap_update_bits(gauge->regmap, PMIC_AUXADC_NAG_PRD_SEL_ADDR, PMIC_AUXADC_NAG_PRD_SEL_MASK << PMIC_AUXADC_NAG_PRD_SEL_SHIFT, 2 << PMIC_AUXADC_NAG_PRD_SEL_SHIFT); fgauge_get_info(gauge, GAUGE_PROP_BAT_PLUG_STATUS, &bat_flag); fgauge_get_info(gauge, GAUGE_PROP_PL_CHARGING_STATUS, &is_charger_exist); regmap_read(gauge->regmap, PMIC_RG_SYSTEM_INFO_CON0_ADDR, &rev_val); bm_err("bat_plug:%d chr:%d info:0x%x\n", bat_flag, is_charger_exist, rev_val); gauge->hw_status.pl_charger_status = is_charger_exist; if (is_charger_exist == 1) { gauge->hw_status.is_bat_plugout = 1; fgauge_set_info(gauge, GAUGE_PROP_2SEC_REBOOT, 0); } else { if (bat_flag == 0) gauge->hw_status.is_bat_plugout = 1; else gauge->hw_status.is_bat_plugout = 0; } fgauge_set_info(gauge, GAUGE_PROP_BAT_PLUG_STATUS, 1); /*[12:8], 5 bits*/ gauge->hw_status.bat_plug_out_time = 31; fgauge_read_RTC_boot_status(gauge); return 1; } static int battery_current_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { *val = instant_current(gauge); return 0; } static int hw_version_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { *val = GAUGE_HW_V2000; return 0; } static int rtc_ui_soc_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { u8 rtc_value; int rtc_ui_soc = 0; rtc_value = get_rtc_spare_fg_value(gauge); rtc_ui_soc = (rtc_value & 0x7f); *val = rtc_ui_soc; if (rtc_ui_soc > 100 || rtc_ui_soc < 0) bm_err("[%s]ERR!rtc=0x%x,ui_soc=%d\n", rtc_value, rtc_ui_soc); else bm_debug("[%s]rtc=0x%x,ui_soc=%d\n", rtc_value, rtc_ui_soc); return 0; } static int rtc_ui_soc_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { u8 spare3_reg = get_rtc_spare_fg_value(gauge); int spare3_reg_valid = 0; int new_spare3_reg = 0; spare3_reg_valid = (spare3_reg & 0x80); new_spare3_reg = spare3_reg_valid + val; set_rtc_spare_fg_value(gauge, new_spare3_reg); bm_debug("[%s] ui_soc=%d, spare3_reg=0x%x, valid:%d, new_spare3_reg:0x%x\n", __func__, val, spare3_reg, spare3_reg_valid, new_spare3_reg); return 1; } static int gauge_initialized_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int fg_reset_status; regmap_read(gauge->regmap, PMIC_FG_RSTB_STATUS_ADDR, &fg_reset_status); *val = (fg_reset_status & (PMIC_FG_RSTB_STATUS_MASK << PMIC_FG_RSTB_STATUS_SHIFT)) >> PMIC_FG_RSTB_STATUS_SHIFT; return 0; } static int gauge_initialized_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { regmap_update_bits(gauge->regmap, PMIC_FG_RSTB_STATUS_ADDR, PMIC_FG_RSTB_STATUS_MASK << PMIC_FG_RSTB_STATUS_SHIFT, val << PMIC_FG_RSTB_STATUS_SHIFT); return 0; } static int battery_exist_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { unsigned int regval; #if defined(CONFIG_FPGA_EARLY_PORTING) *val = 0; return 0; #endif regmap_read(gauge->regmap, PMIC_AD_BATON_UNDET_ADDR, ®val); regval = (regval & (PMIC_AD_BATON_UNDET_MASK << PMIC_AD_BATON_UNDET_SHIFT)) >> PMIC_AD_BATON_UNDET_SHIFT; if (regval == 0) *val = 1; else { *val = 0; regmap_update_bits(gauge->regmap, PMIC_AUXADC_ADC_RDY_PWRON_CLR_ADDR, PMIC_AUXADC_ADC_RDY_PWRON_CLR_MASK << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT, 1 << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT); mdelay(1); regmap_update_bits(gauge->regmap, PMIC_AUXADC_ADC_RDY_PWRON_CLR_ADDR, PMIC_AUXADC_ADC_RDY_PWRON_CLR_MASK << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT, 0 << PMIC_AUXADC_ADC_RDY_PWRON_CLR_SHIFT); } return 0; } static int bat_vol_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int ret; if (!IS_ERR(gauge->chan_bat_voltage)) { ret = iio_read_channel_processed(gauge->chan_bat_voltage, val); if (ret < 0) bm_err("[%s]read fail,ret=%d\n", __func__, ret); } else { bm_err("[%s]chan error\n", __func__); ret = -ENOTSUPP; } return ret; } static int battery_temperature_adc_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int ret; if (!IS_ERR(gauge->chan_bat_temp)) { ret = iio_read_channel_processed(gauge->chan_bat_temp, val); if (ret < 0) bm_err("[%s]read fail,ret=%d\n", __func__, ret); } else { bm_err("[%s]chan error\n", __func__); ret = -ENOTSUPP; } return ret; } static int bif_voltage_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int ret; if (!IS_ERR(gauge->chan_bif)) { ret = iio_read_channel_processed(gauge->chan_bif, val); if (ret < 0) bm_err("[%s]read fail,ret=%d\n", __func__, ret); } else { bm_err("[%s]chan error\n", __func__); ret = -ENOTSUPP; } return ret; } static int ptim_battery_voltage_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int ret; if (!IS_ERR(gauge->chan_ptim_bat_voltage)) { ret = iio_read_channel_processed( gauge->chan_ptim_bat_voltage, val); if (ret < 0) bm_err("[%s]read fail,ret=%d\n", __func__, ret); } else { bm_err("[%s]chan error\n", __func__); ret = -ENOTSUPP; } return ret; } static int ptim_resist_get(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int *val) { int ret; if (!IS_ERR(gauge->chan_ptim_r)) { ret = iio_read_channel_processed( gauge->chan_ptim_r, val); if (ret < 0) bm_err("[%s]read fail,ret=%d\n", __func__, ret); } else { bm_err("[%s]chan error\n", __func__); ret = -ENOTSUPP; } return ret; } static int bat_temp_froze_en_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { if (val != 0) val = 1; regmap_update_bits(gauge->regmap, PMIC_AUXADC_BAT_TEMP_FROZE_EN_ADDR, PMIC_AUXADC_BAT_TEMP_FROZE_EN_MASK << PMIC_AUXADC_BAT_TEMP_FROZE_EN_SHIFT, val << PMIC_AUXADC_BAT_TEMP_FROZE_EN_SHIFT); return 0; } static int coulomb_interrupt_ht_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { unsigned int temp_car_15_0 = 0; unsigned int temp_car_31_16 = 0; unsigned int uvalue32_car_msb = 0; signed int upperbound = 0; signed int upperbound_31_16 = 0, upperbound_15_00 = 0; signed int value32_car; long long car = val; int r_fg_value; int car_tune_value; r_fg_value = gauge->hw_status.r_fg_value; car_tune_value = gauge->hw_status.car_tune_value; bm_debug("%s car=%d\n", __func__, val); if (car == 0) { disable_gauge_irq(gauge, COULOMB_H_IRQ); return 0; } pre_gauge_update(gauge); regmap_read(gauge->regmap, PMIC_FG_CAR_15_00_ADDR, &temp_car_15_0); temp_car_15_0 = (temp_car_15_0 & (PMIC_FG_CAR_15_00_MASK << PMIC_FG_CAR_15_00_SHIFT)) >> PMIC_FG_CAR_15_00_SHIFT; regmap_read(gauge->regmap, PMIC_FG_CAR_31_16_ADDR, &temp_car_31_16); temp_car_31_16 = (temp_car_31_16 & (PMIC_FG_CAR_31_16_MASK << PMIC_FG_CAR_31_16_SHIFT)) >> PMIC_FG_CAR_31_16_SHIFT; post_gauge_update(gauge); uvalue32_car_msb = (temp_car_31_16 & 0x8000) >> 15; value32_car = temp_car_15_0 & 0xffff; value32_car |= (temp_car_31_16 & 0xffff) << 16; bm_debug("[%s] FG_CAR = 0x%x:%d uvalue32_car_msb:0x%x 0x%x 0x%x\r\n", __func__, value32_car, value32_car, uvalue32_car_msb, temp_car_15_0, temp_car_31_16); #if defined(__LP64__) || defined(_LP64) car = car * 100000 / UNIT_CHARGE; #else car = div_s64(car * 100000, UNIT_CHARGE); #endif if (r_fg_value != DEFAULT_R_FG) #if defined(__LP64__) || defined(_LP64) car = (car * r_fg_value) / DEFAULT_R_FG; #else car = div_s64(car * r_fg_value, DEFAULT_R_FG); #endif #if defined(__LP64__) || defined(_LP64) car = ((car * 1000) / car_tune_value); #else car = div_s64((car * 1000), car_tune_value); #endif upperbound = value32_car; bm_debug("[%s] upper = 0x%x:%d diff_car=0x%llx:%lld\r\n", __func__, upperbound, upperbound, car, car); upperbound = upperbound + car; upperbound_31_16 = (upperbound & 0xffff0000) >> 16; upperbound_15_00 = (upperbound & 0xffff); bm_debug("[%s] final upper = 0x%x:%d car=0x%llx:%lld\r\n", __func__, upperbound, upperbound, car, car); bm_debug("[%s] final upper 0x%x 0x%x 0x%x car=0x%llx\n", __func__, upperbound, upperbound_31_16, upperbound_15_00, car); disable_gauge_irq(gauge, COULOMB_H_IRQ); regmap_update_bits(gauge->regmap, PMIC_FG_BAT_HTH_15_00_ADDR, PMIC_FG_BAT_HTH_15_00_MASK << PMIC_FG_BAT_HTH_15_00_SHIFT, upperbound_15_00 << PMIC_FG_BAT_HTH_15_00_SHIFT); regmap_update_bits(gauge->regmap, PMIC_FG_BAT_HTH_31_16_ADDR, PMIC_FG_BAT_HTH_31_16_MASK << PMIC_FG_BAT_HTH_31_16_SHIFT, upperbound_31_16 << PMIC_FG_BAT_HTH_31_16_SHIFT); mdelay(1); enable_gauge_irq(gauge, COULOMB_H_IRQ); bm_debug("[%s] high:0x%x 0x%x car_value:%d car:%d irq:%d\r\n", __func__, upperbound_15_00, upperbound_31_16, val, value32_car, gauge->irq_no[COULOMB_H_IRQ]); return 0; } static int coulomb_interrupt_lt_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { unsigned int temp_car_15_0 = 0; unsigned int temp_car_31_16 = 0; unsigned int uvalue32_car_msb = 0; signed int lowbound = 0; signed int lowbound_31_16 = 0, lowbound_15_00 = 0; signed int value32_car; long long car = val; int r_fg_value; int car_tune_value; r_fg_value = gauge->hw_status.r_fg_value; car_tune_value = gauge->hw_status.car_tune_value; bm_debug("%s car=%d\n", __func__, val); if (car == 0) { disable_gauge_irq(gauge, COULOMB_L_IRQ); return 0; } pre_gauge_update(gauge); regmap_read(gauge->regmap, PMIC_FG_CAR_15_00_ADDR, &temp_car_15_0); temp_car_15_0 = (temp_car_15_0 & (PMIC_FG_CAR_15_00_MASK << PMIC_FG_CAR_15_00_SHIFT)) >> PMIC_FG_CAR_15_00_SHIFT; regmap_read(gauge->regmap, PMIC_FG_CAR_31_16_ADDR, &temp_car_31_16); temp_car_31_16 = (temp_car_31_16 & (PMIC_FG_CAR_31_16_MASK << PMIC_FG_CAR_31_16_SHIFT)) >> PMIC_FG_CAR_31_16_SHIFT; post_gauge_update(gauge); uvalue32_car_msb = (temp_car_31_16 & 0x8000) >> 15; value32_car = temp_car_15_0 & 0xffff; value32_car |= (temp_car_31_16 & 0xffff) << 16; bm_debug("[%s] FG_CAR = 0x%x:%d uvalue32_car_msb:0x%x 0x%x 0x%x\r\n", __func__, value32_car, value32_car, uvalue32_car_msb, temp_car_15_0, temp_car_31_16); /* gap to register-base */ #if defined(__LP64__) || defined(_LP64) car = car * 100000 / UNIT_CHARGE; /* car * 1000 * 100 */ #else car = div_s64(car * 100000, UNIT_CHARGE); #endif if (r_fg_value != DEFAULT_R_FG) #if defined(__LP64__) || defined(_LP64) car = (car * r_fg_value) / DEFAULT_R_FG; #else car = div_s64(car * r_fg_value, DEFAULT_R_FG); #endif #if defined(__LP64__) || defined(_LP64) car = ((car * 1000) / car_tune_value); #else car = div_s64((car * 1000), car_tune_value); #endif lowbound = value32_car; bm_debug("[%s]low=0x%x:%d diff_car=0x%llx:%lld\r\n", __func__, lowbound, lowbound, car, car); lowbound = lowbound - car; lowbound_31_16 = (lowbound & 0xffff0000) >> 16; lowbound_15_00 = (lowbound & 0xffff); bm_debug("[%s]final low=0x%x:%d car=0x%llx:%lld\r\n", __func__, lowbound, lowbound, car, car); bm_debug("[%s] final low 0x%x 0x%x 0x%x car=0x%llx\n", __func__, lowbound, lowbound_31_16, lowbound_15_00, car); disable_gauge_irq(gauge, COULOMB_L_IRQ); regmap_update_bits(gauge->regmap, PMIC_FG_BAT_LTH_15_00_ADDR, PMIC_FG_BAT_LTH_15_00_MASK << PMIC_FG_BAT_LTH_15_00_SHIFT, lowbound_15_00 << PMIC_FG_BAT_LTH_15_00_SHIFT); regmap_update_bits(gauge->regmap, PMIC_FG_BAT_LTH_31_16_ADDR, PMIC_FG_BAT_LTH_31_16_MASK << PMIC_FG_BAT_LTH_31_16_SHIFT, lowbound_31_16 << PMIC_FG_BAT_LTH_31_16_SHIFT); mdelay(1); enable_gauge_irq(gauge, COULOMB_L_IRQ); bm_debug("[%s] low:0x%x 0x%x car_value:%d car:%d irq:%d\r\n", __func__, lowbound_15_00, lowbound_31_16, val, value32_car, gauge->irq_no[COULOMB_L_IRQ]); return 0; } static void enable_lbat2_en(struct mtk_gauge *gauge) { if (gauge->vbat_l_en == true || gauge->vbat_h_en == true) regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_EN_ADDR, PMIC_AUXADC_LBAT2_EN_MASK << PMIC_AUXADC_LBAT2_EN_SHIFT, 1 << PMIC_AUXADC_LBAT2_EN_SHIFT); if (gauge->vbat_l_en == false && gauge->vbat_h_en == false) regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_EN_ADDR, PMIC_AUXADC_LBAT2_EN_MASK << PMIC_AUXADC_LBAT2_EN_SHIFT, 0 << PMIC_AUXADC_LBAT2_EN_SHIFT); } static int en_h_vbat_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { if (val != 0) { val = 1; enable_gauge_irq(gauge, VBAT_H_IRQ); } else disable_gauge_irq(gauge, VBAT_H_IRQ); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_IRQ_EN_MAX_ADDR, PMIC_AUXADC_LBAT2_IRQ_EN_MAX_MASK << PMIC_AUXADC_LBAT2_IRQ_EN_MAX_SHIFT, val << PMIC_AUXADC_LBAT2_IRQ_EN_MAX_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_DET_MAX_ADDR, PMIC_AUXADC_LBAT2_DET_MAX_MASK << PMIC_AUXADC_LBAT2_DET_MAX_SHIFT, val << PMIC_AUXADC_LBAT2_DET_MAX_SHIFT); gauge->vbat_h_en = val; enable_lbat2_en(gauge); return 0; } static int en_l_vbat_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int val) { static int cnt; bm_debug("%s %d %d\n", __func__, cnt, val); if (val != 0) cnt++; else cnt--; if (val != 0) { val = 1; enable_gauge_irq(gauge, VBAT_L_IRQ); } else disable_gauge_irq(gauge, VBAT_L_IRQ); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_IRQ_EN_MIN_ADDR, PMIC_AUXADC_LBAT2_IRQ_EN_MIN_MASK << PMIC_AUXADC_LBAT2_IRQ_EN_MIN_SHIFT, val << PMIC_AUXADC_LBAT2_IRQ_EN_MIN_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_DET_MIN_ADDR, PMIC_AUXADC_LBAT2_DET_MIN_MASK << PMIC_AUXADC_LBAT2_IRQ_EN_MIN_SHIFT, val << PMIC_AUXADC_LBAT2_IRQ_EN_MIN_SHIFT); gauge->vbat_l_en = val; enable_lbat2_en(gauge); return 0; } static void switch_vbat2_det_time(int _prd, int *value) { if (_prd >= 1 && _prd < 3) *value = 0; else if (_prd >= 3 && _prd < 5) *value = 1; else if (_prd >= 5 && _prd < 10) *value = 2; else if (_prd >= 10) *value = 3; } static void switch_vbat2_debt_counter(int _prd, int *value) { if (_prd >= 1 && _prd < 2) *value = 0; else if (_prd >= 2 && _prd < 4) *value = 1; else if (_prd >= 4 && _prd < 8) *value = 2; else if (_prd >= 8) *value = 3; } static int vbat_ht_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int threshold) { int vbat2_h_th_mv = threshold; int vbat2_h_th_reg = mv_to_reg_12_value(gauge, vbat2_h_th_mv); int vbat2_det_counter = 0; int vbat2_det_time = 0; switch_vbat2_det_time( gauge->hw_status.vbat2_det_time, &vbat2_det_time); switch_vbat2_debt_counter( gauge->hw_status.vbat2_det_counter, &vbat2_det_counter); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_VOLT_MAX_ADDR, PMIC_AUXADC_LBAT2_VOLT_MAX_MASK << PMIC_AUXADC_LBAT2_VOLT_MAX_SHIFT, vbat2_h_th_reg << PMIC_AUXADC_LBAT2_VOLT_MAX_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_DET_PRD_SEL_ADDR, PMIC_AUXADC_LBAT2_DET_PRD_SEL_MASK << PMIC_AUXADC_LBAT2_DET_PRD_SEL_SHIFT, vbat2_det_time << PMIC_AUXADC_LBAT2_DET_PRD_SEL_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_ADDR, PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_MASK << PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_SHIFT, vbat2_det_counter << PMIC_AUXADC_LBAT2_DEBT_MAX_SEL_SHIFT); bm_debug("[fg_set_vbat2_h_th] thr:%d [0x%x %d 0x%x %d 0x%x]\n", threshold, vbat2_h_th_reg, gauge->hw_status.vbat2_det_time, vbat2_det_time, gauge->hw_status.vbat2_det_counter, vbat2_det_counter); return 0; } static int reset_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int threshold) { unsigned int ret = 0; bm_err("[fgauge_hw_reset]\n"); regmap_update_bits(gauge->regmap, MT6359_FGADC_CON1, 0x0F00, 0x0630); bm_err("[fgauge_hw_reset] reset fgadc car ret =%d\n", ret); mdelay(1); regmap_update_bits(gauge->regmap, MT6359_FGADC_CON1, 0x0F00, 0x0030); return 0; } static int vbat_lt_set(struct mtk_gauge *gauge, struct mtk_gauge_sysfs_field_info *attr, int threshold) { int vbat2_l_th_mv = threshold; int vbat2_l_th_reg = mv_to_reg_12_value(gauge, vbat2_l_th_mv); int vbat2_det_counter = 0; int vbat2_det_time = 0; switch_vbat2_det_time( gauge->hw_status.vbat2_det_time, &vbat2_det_time); switch_vbat2_debt_counter( gauge->hw_status.vbat2_det_counter, &vbat2_det_counter); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_VOLT_MIN_ADDR, PMIC_AUXADC_LBAT2_VOLT_MIN_MASK << PMIC_AUXADC_LBAT2_VOLT_MIN_SHIFT, vbat2_l_th_reg << PMIC_AUXADC_LBAT2_VOLT_MIN_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_DET_PRD_SEL_ADDR, PMIC_AUXADC_LBAT2_DET_PRD_SEL_MASK << PMIC_AUXADC_LBAT2_DET_PRD_SEL_SHIFT, vbat2_det_time << PMIC_AUXADC_LBAT2_DET_PRD_SEL_SHIFT); regmap_update_bits(gauge->regmap, PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_ADDR, PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_MASK << PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_SHIFT, vbat2_det_counter << PMIC_AUXADC_LBAT2_DEBT_MIN_SEL_SHIFT); bm_debug("[fg_set_vbat2_l_th] thr:%d [0x%x %d 0x%x %d 0x%x]\n", threshold, vbat2_l_th_reg, gauge->hw_status.vbat2_det_time, vbat2_det_time, gauge->hw_status.vbat2_det_counter, vbat2_det_counter); return 0; } void dump_nag(struct mtk_gauge *gauge) { int nag[12]; regmap_read(gauge->regmap, 0x11be, &nag[0]); regmap_read(gauge->regmap, 0x11c0, &nag[1]); regmap_read(gauge->regmap, 0x11c2, &nag[2]); regmap_read(gauge->regmap, 0x11c4, &nag[3]); regmap_read(gauge->regmap, 0x11c6, &nag[4]); regmap_read(gauge->regmap, 0x11c8, &nag[5]); regmap_read(gauge->regmap, 0x11ca, &nag[6]); regmap_read(gauge->regmap, 0x11cc, &nag[7]); regmap_read(gauge->regmap, 0x11ce, &nag[8]); regmap_read(gauge->regmap, 0x11d0, &nag[9]); regmap_read(gauge->regmap, 0x11d2, &nag[10]); regmap_read(gauge->regmap, 0x11d4, &nag[11]); bm_err("nag %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", nag[0], nag[1], nag[2], nag[3], nag[4], nag[5], nag[6], nag[7], nag[8], nag[9], nag[10], nag[11], reg_to_mv_value(nag[1]), reg_to_mv_value(nag[2]), reg_to_mv_value(nag[6]) ); } static ssize_t gauge_sysfs_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy; struct mtk_gauge *gauge; struct mtk_gauge_sysfs_field_info *gauge_attr; int val; ssize_t ret; ret = kstrtoint(buf, 0, &val); if (ret < 0) return ret; psy = dev_get_drvdata(dev); gauge = (struct mtk_gauge *)power_supply_get_drvdata(psy); gauge_attr = container_of(attr, struct mtk_gauge_sysfs_field_info, attr); if (gauge_attr->set != NULL) { mutex_lock(&gauge->ops_lock); gauge_attr->set(gauge, gauge_attr, val); mutex_unlock(&gauge->ops_lock); } return count; } static ssize_t gauge_sysfs_show(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy; struct mtk_gauge *gauge; struct mtk_gauge_sysfs_field_info *gauge_attr; int val = 0; ssize_t count; psy = dev_get_drvdata(dev); gauge = (struct mtk_gauge *)power_supply_get_drvdata(psy); gauge_attr = container_of(attr, struct mtk_gauge_sysfs_field_info, attr); if (gauge_attr->get != NULL) { mutex_lock(&gauge->ops_lock); gauge_attr->get(gauge, gauge_attr, &val); mutex_unlock(&gauge->ops_lock); } count = scnprintf(buf, PAGE_SIZE, "%d\n", val); return count; } /* Must be in the same order as GAUGE_PROP_* */ static struct mtk_gauge_sysfs_field_info mt6359_sysfs_field_tbl[] = { GAUGE_SYSFS_FIELD_WO(initial_set, GAUGE_PROP_INITIAL), GAUGE_SYSFS_FIELD_RO(battery_current_get, GAUGE_PROP_BATTERY_CURRENT), GAUGE_SYSFS_FIELD_RO(coulomb_get, GAUGE_PROP_COULOMB), GAUGE_SYSFS_FIELD_WO(coulomb_interrupt_ht_set, GAUGE_PROP_COULOMB_HT_INTERRUPT), GAUGE_SYSFS_FIELD_WO(coulomb_interrupt_lt_set, GAUGE_PROP_COULOMB_LT_INTERRUPT), GAUGE_SYSFS_FIELD_RO(battery_exist_get, GAUGE_PROP_BATTERY_EXIST), GAUGE_SYSFS_FIELD_RO(hw_version_get, GAUGE_PROP_HW_VERSION), GAUGE_SYSFS_FIELD_RO(bat_vol_get, GAUGE_PROP_BATTERY_VOLTAGE), GAUGE_SYSFS_FIELD_RO(battery_temperature_adc_get, GAUGE_PROP_BATTERY_TEMPERATURE_ADC), GAUGE_SYSFS_FIELD_RO(bif_voltage_get, GAUGE_PROP_BIF_VOLTAGE), GAUGE_SYSFS_FIELD_WO(en_h_vbat_set, GAUGE_PROP_EN_HIGH_VBAT_INTERRUPT), GAUGE_SYSFS_FIELD_WO(en_l_vbat_set, GAUGE_PROP_EN_LOW_VBAT_INTERRUPT), GAUGE_SYSFS_FIELD_WO(vbat_ht_set, GAUGE_PROP_VBAT_HT_INTR_THRESHOLD), GAUGE_SYSFS_FIELD_WO(vbat_lt_set, GAUGE_PROP_VBAT_LT_INTR_THRESHOLD), GAUGE_SYSFS_FIELD_RW(rtc_ui_soc, rtc_ui_soc_set, rtc_ui_soc_get, GAUGE_PROP_RTC_UI_SOC), GAUGE_SYSFS_FIELD_RO(ptim_battery_voltage_get, GAUGE_PROP_PTIM_BATTERY_VOLTAGE), GAUGE_SYSFS_FIELD_RO(ptim_resist_get, GAUGE_PROP_PTIM_RESIST), GAUGE_SYSFS_FIELD_WO(reset_set, GAUGE_PROP_RESET), GAUGE_SYSFS_FIELD_RO(boot_zcv_get, GAUGE_PROP_BOOT_ZCV), GAUGE_SYSFS_FIELD_RO(zcv_get, GAUGE_PROP_ZCV), GAUGE_SYSFS_FIELD_RO(zcv_current_get, GAUGE_PROP_ZCV_CURRENT), GAUGE_SYSFS_FIELD_RO(nafg_cnt_get, GAUGE_PROP_NAFG_CNT), GAUGE_SYSFS_FIELD_RO(nafg_dltv_get, GAUGE_PROP_NAFG_DLTV), GAUGE_SYSFS_FIELD_RW(nafg_c_dltv, nafg_c_dltv_set, nafg_c_dltv_get, GAUGE_PROP_NAFG_C_DLTV), GAUGE_SYSFS_FIELD_WO(nafg_en_set, GAUGE_PROP_NAFG_EN), GAUGE_SYSFS_FIELD_WO(nafg_zcv_set, GAUGE_PROP_NAFG_ZCV), GAUGE_SYSFS_FIELD_RO(nafg_vbat_get, GAUGE_PROP_NAFG_VBAT), GAUGE_SYSFS_FIELD_WO(reset_fg_rtc_set, GAUGE_PROP_RESET_FG_RTC), GAUGE_SYSFS_FIELD_RW(gauge_initialized, gauge_initialized_set, gauge_initialized_get, GAUGE_PROP_GAUGE_INITIALIZED), GAUGE_SYSFS_FIELD_RO(average_current_get, GAUGE_PROP_AVERAGE_CURRENT), GAUGE_SYSFS_FIELD_WO(bat_plugout_en_set, GAUGE_PROP_BAT_PLUGOUT_EN), GAUGE_SYSFS_FIELD_WO(zcv_intr_threshold_set, GAUGE_PROP_ZCV_INTR_THRESHOLD), GAUGE_SYSFS_FIELD_WO(zcv_intr_en_set, GAUGE_PROP_ZCV_INTR_EN), GAUGE_SYSFS_FIELD_WO(soff_reset_set, GAUGE_PROP_SOFF_RESET), GAUGE_SYSFS_FIELD_WO(ncar_reset_set, GAUGE_PROP_NCAR_RESET), GAUGE_SYSFS_FIELD_WO(bat_cycle_intr_threshold_set, GAUGE_PROP_BAT_CYCLE_INTR_THRESHOLD), GAUGE_SYSFS_FIELD_WO(hw_info_set, GAUGE_PROP_HW_INFO), GAUGE_SYSFS_FIELD_WO(event_set, GAUGE_PROP_EVENT), GAUGE_SYSFS_FIELD_WO(en_bat_tmp_ht_set, GAUGE_PROP_EN_BAT_TMP_HT), GAUGE_SYSFS_FIELD_WO(en_bat_tmp_lt_set, GAUGE_PROP_EN_BAT_TMP_LT), GAUGE_SYSFS_FIELD_WO(bat_tmp_ht_threshold_set, GAUGE_PROP_BAT_TMP_HT_THRESHOLD), GAUGE_SYSFS_FIELD_WO(bat_tmp_lt_threshold_set, GAUGE_PROP_BAT_TMP_LT_THRESHOLD), GAUGE_SYSFS_INFO_FIELD_RW( info_2sec_reboot, GAUGE_PROP_2SEC_REBOOT), GAUGE_SYSFS_INFO_FIELD_RW( info_pl_charging_status, GAUGE_PROP_PL_CHARGING_STATUS), GAUGE_SYSFS_INFO_FIELD_RW( info_monitor_plchg_status, GAUGE_PROP_MONITER_PLCHG_STATUS), GAUGE_SYSFS_INFO_FIELD_RW( info_bat_plug_status, GAUGE_PROP_BAT_PLUG_STATUS), GAUGE_SYSFS_INFO_FIELD_RW( info_is_nvram_fail_mode, GAUGE_PROP_IS_NVRAM_FAIL_MODE), GAUGE_SYSFS_INFO_FIELD_RW( info_monitor_soff_validtime, GAUGE_PROP_MONITOR_SOFF_VALIDTIME), GAUGE_SYSFS_INFO_FIELD_RW( info_con0_soc, GAUGE_PROP_CON0_SOC), GAUGE_SYSFS_INFO_FIELD_RW( info_shutdown_car, GAUGE_PROP_SHUTDOWN_CAR), GAUGE_SYSFS_INFO_FIELD_RW( car_tune_value, GAUGE_PROP_CAR_TUNE_VALUE), GAUGE_SYSFS_INFO_FIELD_RW( r_fg_value, GAUGE_PROP_R_FG_VALUE), GAUGE_SYSFS_INFO_FIELD_RW( vbat2_detect_time, GAUGE_PROP_VBAT2_DETECT_TIME), GAUGE_SYSFS_INFO_FIELD_RW( vbat2_detect_counter, GAUGE_PROP_VBAT2_DETECT_COUNTER), GAUGE_SYSFS_FIELD_WO( bat_temp_froze_en_set, GAUGE_PROP_BAT_TEMP_FROZE_EN), }; static struct attribute * mt6359_sysfs_attrs[ARRAY_SIZE(mt6359_sysfs_field_tbl) + 1]; static const struct attribute_group mt6359_sysfs_attr_group = { .attrs = mt6359_sysfs_attrs, }; static void mt6359_sysfs_init_attrs(void) { int i, limit = ARRAY_SIZE(mt6359_sysfs_field_tbl); for (i = 0; i < limit; i++) mt6359_sysfs_attrs[i] = &mt6359_sysfs_field_tbl[i].attr.attr; mt6359_sysfs_attrs[limit] = NULL; /* Has additional entry for this */ } static int mt6359_sysfs_create_group(struct mtk_gauge *gauge) { mt6359_sysfs_init_attrs(); return sysfs_create_group(&gauge->psy->dev.kobj, &mt6359_sysfs_attr_group); } static void mt6359_gauge_shutdown(struct platform_device *pdev) { struct mtk_battery *gm; struct mtk_gauge *gauge; gauge = dev_get_drvdata(&pdev->dev); gm = gauge->gm; gm->shutdown(gm); } static int mt6359_gauge_suspend(struct platform_device *pdev, pm_message_t state) { struct mtk_battery *gm; struct mtk_gauge *gauge; gauge = dev_get_drvdata(&pdev->dev); gm = gauge->gm; gm->suspend(gm, state); return 0; } static int mt6359_gauge_resume(struct platform_device *pdev) { struct mtk_battery *gm; struct mtk_gauge *gauge; gauge = dev_get_drvdata(&pdev->dev); gm = gauge->gm; gm->resume(gm); return 0; } signed int battery_meter_meta_tool_cali_car_tune(struct mtk_battery *gm, int meta_current) { int cali_car_tune = 0; if (meta_current == 0) return gm->fg_cust_data.car_tune_value * 10; gm->gauge->hw_status.meta_current = meta_current; bm_err("%s meta_current=%d\n", __func__, meta_current); calculate_car_tune(gm->gauge); cali_car_tune = gm->gauge->hw_status.tmp_car_tune; bm_err("%s cali_car_tune=%d\n", __func__, cali_car_tune); return cali_car_tune; /* 1000 base */ } #if IS_ENABLED(CONFIG_COMPAT) static long compat_adc_cali_ioctl( struct file *filp, unsigned int cmd, unsigned long arg) { int adc_out_datas[2] = { 1, 1 }; bm_notice("%s 32bit IOCTL, cmd=0x%08x\n", __func__, cmd); if (!filp->f_op || !filp->f_op->unlocked_ioctl) { bm_err("%s file has no f_op or no f_op->unlocked_ioctl.\n", __func__); return -ENOTTY; } if (sizeof(arg) != sizeof(adc_out_datas)) return -EFAULT; switch (cmd) { case Get_META_BAT_VOL: case Get_META_BAT_SOC: case Get_META_BAT_CAR_TUNE_VALUE: case Set_META_BAT_CAR_TUNE_VALUE: case Set_BAT_DISABLE_NAFG: case Set_CARTUNE_TO_KERNEL: { bm_notice( "%s send to unlocked_ioctl cmd=0x%08x\n", __func__, cmd); return filp->f_op->unlocked_ioctl( filp, cmd, (unsigned long)compat_ptr(arg)); } break; default: bm_err("%s unknown IOCTL: 0x%08x, %d\n", __func__, cmd, adc_out_datas[0]); break; } return 0; } #endif static long adc_cali_ioctl( struct file *file, unsigned int cmd, unsigned long arg) { int *user_data_addr; int ret = 0; int adc_in_data[2] = { 1, 1 }; int adc_out_data[2] = { 1, 1 }; int temp_car_tune; int isdisNAFG = 0; struct mtk_battery *gm; bm_notice("%s enter\n", __func__); gm = get_mtk_battery(); mutex_lock(&gm->gauge->fg_mutex); user_data_addr = (int *)arg; ret = copy_from_user(adc_in_data, user_data_addr, sizeof(adc_in_data)); if (adc_in_data[1] < 0) { bm_err("%s unknown data: %d\n", __func__, adc_in_data[1]); mutex_unlock(&gm->gauge->fg_mutex); return -EFAULT; } switch (cmd) { /* add for meta tool------------------------------- */ case Get_META_BAT_VOL: adc_out_data[0] = gauge_get_int_property(GAUGE_PROP_BATTERY_VOLTAGE); if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm->gauge->fg_mutex); return -EFAULT; } bm_notice("**** unlocked_ioctl :Get_META_BAT_VOL Done!\n"); break; case Get_META_BAT_SOC: adc_out_data[0] = gm->ui_soc; if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm->gauge->fg_mutex); return -EFAULT; } bm_notice("**** unlocked_ioctl :Get_META_BAT_SOC Done!\n"); break; case Get_META_BAT_CAR_TUNE_VALUE: adc_out_data[0] = gm->fg_cust_data.car_tune_value; bm_err("Get_BAT_CAR_TUNE_VALUE, res=%d\n", adc_out_data[0]); if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm->gauge->fg_mutex); return -EFAULT; } bm_notice("**** unlocked_ioctl :Get_META_BAT_CAR_TUNE_VALUE Done!\n"); break; case Set_META_BAT_CAR_TUNE_VALUE: /* meta tool input: adc_in_data[1] (mA)*/ /* Send cali_current to hal to calculate car_tune_value*/ temp_car_tune = battery_meter_meta_tool_cali_car_tune(gm, adc_in_data[1]); /* return car_tune_value to meta tool in adc_out_data[0] */ if (temp_car_tune >= 900 && temp_car_tune <= 1100) gm->fg_cust_data.car_tune_value = temp_car_tune; else bm_err("car_tune_value invalid:%d\n", temp_car_tune); adc_out_data[0] = temp_car_tune; if (copy_to_user(user_data_addr, adc_out_data, sizeof(adc_out_data))) { mutex_unlock(&gm->gauge->fg_mutex); return -EFAULT; } bm_err("**** unlocked_ioctl Set_BAT_CAR_TUNE_VALUE[%d], tmp_car_tune=%d result=%d, ret=%d\n", adc_in_data[1], adc_out_data[0], temp_car_tune, ret); break; case Set_BAT_DISABLE_NAFG: isdisNAFG = adc_in_data[1]; if (isdisNAFG == 1) { gm->cmd_disable_nafg = true; wakeup_fg_algo_cmd( gm, FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_DISABLE_NAFG, 1); } else if (isdisNAFG == 0) { gm->cmd_disable_nafg = false; wakeup_fg_algo_cmd( gm, FG_INTR_KERNEL_CMD, FG_KERNEL_CMD_DISABLE_NAFG, 0); } bm_debug("unlocked_ioctl Set_BAT_DISABLE_NAFG,isdisNAFG=%d [%d]\n", isdisNAFG, adc_in_data[1]); break; /* add bing meta tool------------------------------- */ case Set_CARTUNE_TO_KERNEL: temp_car_tune = adc_in_data[1]; if (temp_car_tune > 500 && temp_car_tune < 1500) gm->fg_cust_data.car_tune_value = temp_car_tune; bm_err("**** unlocked_ioctl Set_CARTUNE_TO_KERNEL[%d,%d], ret=%d\n", adc_in_data[0], adc_in_data[1], ret); break; default: bm_err("**** unlocked_ioctl unknown IOCTL: 0x%08x\n", cmd); mutex_unlock(&gm->gauge->fg_mutex); return -EINVAL; } mutex_unlock(&gm->gauge->fg_mutex); return 0; } static int adc_cali_open(struct inode *inode, struct file *file) { return 0; } static int adc_cali_release(struct inode *inode, struct file *file) { return 0; } static const struct file_operations adc_cali_fops = { .owner = THIS_MODULE, .unlocked_ioctl = adc_cali_ioctl, #if IS_ENABLED(CONFIG_COMPAT) .compat_ioctl = compat_adc_cali_ioctl, #endif .open = adc_cali_open, .release = adc_cali_release, }; static int adc_cali_cdev_init(struct platform_device *pdev) { int ret = 0; struct class_device *class_dev = NULL; struct mtk_battery *gm; gm = get_mtk_battery(); if (gm != NULL) mutex_init(&gm->gauge->fg_mutex); ret = alloc_chrdev_region(&bat_cali_devno, 0, 1, BAT_CALI_DEVNAME); if (ret) bm_err("Error: Can't Get Major number for adc_cali\n"); bat_cali_cdev = cdev_alloc(); bat_cali_cdev->owner = THIS_MODULE; bat_cali_cdev->ops = &adc_cali_fops; ret = cdev_add(bat_cali_cdev, bat_cali_devno, 1); if (ret) bm_err("adc_cali Error: cdev_add\n"); bat_cali_major = MAJOR(bat_cali_devno); bat_cali_class = class_create(THIS_MODULE, BAT_CALI_DEVNAME); class_dev = (struct class_device *)device_create(bat_cali_class, NULL, bat_cali_devno, NULL, BAT_CALI_DEVNAME); return 0; } static void mtk_gauge_netlink_handler(struct sk_buff *skb) { mtk_battery_netlink_handler(skb); } int bat_create_netlink(struct platform_device *pdev) { struct mtk_gauge *gauge; struct netlink_kernel_cfg cfg = { .input = mtk_gauge_netlink_handler, }; gauge = dev_get_drvdata(&pdev->dev); gauge->gm->mtk_battery_sk = netlink_kernel_create(&init_net, NETLINK_FGD, &cfg); if (gauge->gm->mtk_battery_sk == NULL) { bm_err("netlink_kernel_create error\n"); return -EIO; } bm_err("[%s]netlink_kernel_create protol= %d\n", __func__, NETLINK_FGD); return 0; } static int mt6359_gauge_probe(struct platform_device *pdev) { struct mtk_gauge *gauge; int ret; struct iio_channel *chan_bat_temp; bm_err("%s: starts\n", __func__); chan_bat_temp = devm_iio_channel_get( &pdev->dev, "pmic_battery_temp"); if (IS_ERR(chan_bat_temp)) { bm_err("mt6359 requests probe deferral\n"); return -EPROBE_DEFER; } gauge = devm_kzalloc(&pdev->dev, sizeof(*gauge), GFP_KERNEL); if (!gauge) return -ENOMEM; gauge->chip = (struct mt6397_chip *)dev_get_drvdata( pdev->dev.parent); gauge->regmap = gauge->chip->regmap; dev_set_drvdata(&pdev->dev, gauge); gauge->pdev = pdev; mutex_init(&gauge->ops_lock); gauge->irq_no[COULOMB_H_IRQ] = platform_get_irq_byname(pdev, "COULOMB_H"); gauge->irq_no[COULOMB_L_IRQ] = platform_get_irq_byname(pdev, "COULOMB_L"); gauge->irq_no[VBAT_H_IRQ] = platform_get_irq_byname(pdev, "VBAT_H"); gauge->irq_no[VBAT_L_IRQ] = platform_get_irq_byname(pdev, "VBAT_L"); gauge->irq_no[NAFG_IRQ] = platform_get_irq_byname(pdev, "NAFG"); gauge->irq_no[BAT_PLUGOUT_IRQ] = platform_get_irq_byname(pdev, "BAT_OUT"); gauge->irq_no[ZCV_IRQ] = platform_get_irq_byname(pdev, "ZCV"); gauge->irq_no[FG_N_CHARGE_L_IRQ] = platform_get_irq_byname(pdev, "FG_N_CHARGE_L"); gauge->irq_no[FG_IAVG_H_IRQ] = platform_get_irq_byname(pdev, "FG_IAVG_H"); gauge->irq_no[FG_IAVG_L_IRQ] = platform_get_irq_byname(pdev, "FG_IAVG_L"); gauge->irq_no[BAT_TMP_H_IRQ] = platform_get_irq_byname(pdev, "BAT_TMP_H"); gauge->irq_no[BAT_TMP_L_IRQ] = platform_get_irq_byname(pdev, "BAT_TMP_L"); gauge->chan_bat_temp = devm_iio_channel_get( &pdev->dev, "pmic_battery_temp"); if (IS_ERR(gauge->chan_bat_temp)) { ret = PTR_ERR(gauge->chan_bat_temp); bm_err("pmic_battery_temp auxadc get fail, ret=%d\n", ret); } gauge->chan_bat_voltage = devm_iio_channel_get( &pdev->dev, "pmic_battery_voltage"); if (IS_ERR(gauge->chan_bat_voltage)) { ret = PTR_ERR(gauge->chan_bat_voltage); bm_err("chan_bat_voltage auxadc get fail, ret=%d\n", ret); } gauge->chan_bif = devm_iio_channel_get( &pdev->dev, "pmic_bif_voltage"); if (IS_ERR(gauge->chan_bif)) { ret = PTR_ERR(gauge->chan_bif); bm_err("pmic_bif_voltage auxadc get fail, ret=%d\n", ret); } gauge->chan_ptim_bat_voltage = devm_iio_channel_get( &pdev->dev, "pmic_ptim_voltage"); if (IS_ERR(gauge->chan_ptim_bat_voltage)) { ret = PTR_ERR(gauge->chan_ptim_bat_voltage); bm_err("chan_ptim_bat_voltage auxadc get fail, ret=%d\n", ret); } gauge->chan_ptim_r = devm_iio_channel_get( &pdev->dev, "pmic_ptim_r"); if (IS_ERR(gauge->chan_ptim_r)) { ret = PTR_ERR(gauge->chan_ptim_r); bm_err("chan_ptim_r auxadc get fail, ret=%d\n", ret); } gauge->hw_status.car_tune_value = 1000; gauge->hw_status.r_fg_value = 50; gauge->attr = mt6359_sysfs_field_tbl; if (battery_psy_init(pdev)) return -ENOMEM; gauge->psy_desc.name = "mtk-gauge"; gauge->psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN; gauge->psy_desc.properties = gauge_properties; gauge->psy_desc.num_properties = ARRAY_SIZE(gauge_properties); gauge->psy_desc.get_property = psy_gauge_get_property; gauge->psy_desc.set_property = psy_gauge_set_property; gauge->psy_cfg.drv_data = gauge; gauge->psy = power_supply_register(&pdev->dev, &gauge->psy_desc, &gauge->psy_cfg); mt6359_sysfs_create_group(gauge); initial_set(gauge, 0, 0); bat_create_netlink(pdev); battery_init(pdev); adc_cali_cdev_init(pdev); bm_err("%s: done\n", __func__); return 0; } static const struct of_device_id mt6359_gauge_of_match[] = { {.compatible = "mediatek,mt6359-gauge",}, {}, }; static int mt6359_gauge_remove(struct platform_device *pdev) { struct mtk_gauge *gauge = platform_get_drvdata(pdev); if (gauge) devm_kfree(&pdev->dev, gauge); return 0; } MODULE_DEVICE_TABLE(of, mt6359_gauge_of_match); static struct platform_driver mt6359_gauge_driver = { .probe = mt6359_gauge_probe, .remove = mt6359_gauge_remove, .shutdown = mt6359_gauge_shutdown, .suspend = mt6359_gauge_suspend, .resume = mt6359_gauge_resume, .driver = { .name = "mt6359_gauge", .of_match_table = mt6359_gauge_of_match, }, }; static int __init mt6359_gauge_init(void) { return platform_driver_register(&mt6359_gauge_driver); } module_init(mt6359_gauge_init); static void __exit mt6359_gauge_exit(void) { platform_driver_unregister(&mt6359_gauge_driver); } module_exit(mt6359_gauge_exit); MODULE_AUTHOR("wy.chuang "); MODULE_DESCRIPTION("MTK Gauge Device Driver"); MODULE_LICENSE("GPL");