10.MTK充电之mt6358-gauge驱动
1.概述
****mt6358-gauge电量计驱动,包括电量的运算、创建mt6358_sysfs_field_tbl文件节点、创建电池信息服务battery_update_routine实时更新电池状态。
2.分析mt6358_gauge_probe函数
static int mt6358_gauge_probe(struct platform_device *pdev)
{struct mtk_gauge *gauge;int ret;struct iio_channel *chan_bat_temp;/* 通过 IIO(Industrial I/O)子系统获取电池温度 ADC 通道 */chan_bat_temp = devm_iio_channel_get(&pdev->dev, "pmic_battery_temp");if (IS_ERR(chan_bat_temp)) {dev_err(&pdev->dev, "mt6358 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;gauge->regmap_type = REGMAP_TYPE_SPI;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);dev_err(&pdev->dev, "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);dev_err(&pdev->dev, "chan_bat_voltage auxadc get fail, ret=%d\n", ret);}/* 获取电池接口电压(BIF) */gauge->chan_bif = devm_iio_channel_get(&pdev->dev, "pmic_bif_voltage");if (IS_ERR(gauge->chan_bif)) {ret = PTR_ERR(gauge->chan_bif);dev_err(&pdev->dev, "pmic_bif_voltage auxadc get fail, ret=%d\n", ret);}/* 获取PTIM(动态电压测量)电压 */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);dev_err(&pdev->dev, "chan_ptim_bat_voltage auxadc get fail, ret=%d\n",ret);}/* 获取 PTIM 电阻 */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);dev_err(&pdev->dev, "chan_ptim_r auxadc get fail, ret=%d\n",ret);}gauge->hw_status.car_tune_value = 1000; /* 库仑计数器校准值 */gauge->hw_status.r_fg_value = 50; /* 电量计内部电阻值 mΩ */gauge->attr = mt6358_sysfs_field_tbl;if (battery_psy_init(pdev)) {dev_err(&pdev->dev, "battery_psy_init fail\n");return -ENOMEM;}gauge->name = "fgauge";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);mt6358_sysfs_create_group(gauge);initial_set(gauge, 0, 0);battery_init(pdev);adc_cali_cdev_init(gauge->gm, pdev);return 0;
}
2.分析gauge_properties电量计属性
****电量计的属性值,电池是否在位、充电器是否在线、最大充电电流、电池电量、电池温度、当前电流等。
static enum power_supply_property gauge_properties[] = {POWER_SUPPLY_PROP_PRESENT, /* 电池是否在位(插入/移除)*/POWER_SUPPLY_PROP_ONLINE, /* 充电器是否在线(是否供电)*/POWER_SUPPLY_PROP_CURRENT_MAX, /* 最大允许电流(充电/放电)*/POWER_SUPPLY_PROP_ENERGY_EMPTY, /* 电池是否完全耗尽(空电量)*/POWER_SUPPLY_PROP_CURRENT_NOW, /* 当前电流(充电/放电) */POWER_SUPPLY_PROP_TEMP, /* 电池温度 */POWER_SUPPLY_PROP_VOLTAGE_NOW, /* 当前电压 */
};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;int ret = 0, value = 0;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;elseval->intval = gauge->gm->disableGM30;break;case POWER_SUPPLY_PROP_ONLINE:if (gauge == NULL || gauge->gm == NULL)val->intval = 0;elseval->intval = gauge->gm->disableGM30;break;case POWER_SUPPLY_PROP_CURRENT_MAX:val->intval = get_ptim_current(gauge);break;case POWER_SUPPLY_PROP_ENERGY_EMPTY:
#ifdef POWER_MISC_OFFgm = gauge->gm;if (gm != NULL)val->intval = gm->sdc.shutdown_status.is_dlpt_shutdown;
#else /* POWER_MISC_OFF */val->intval = 0;
#endif /* POWER_MISC_OFF */break;case POWER_SUPPLY_PROP_CURRENT_NOW:ret = gauge_get_property(gauge->gm, GAUGE_PROP_BATTERY_CURRENT, &value);if (ret) {bm_err(gauge->gm, "%s, Failed to get CIC1, ret = %d\n", __func__, ret);value = gauge->gm->ibat;}val->intval = value * 100;return 0;case POWER_SUPPLY_PROP_TEMP:gm = gauge->gm;if (gm)val->intval = gm->battery_temp * 10;return 0;case POWER_SUPPLY_PROP_VOLTAGE_NOW:if (!gauge || !gauge->gm || gauge->gm->disableGM30)val->intval = 4000 * 1000;elseval->intval = gauge_get_int_property(gauge->gm,GAUGE_PROP_BATTERY_VOLTAGE) * 1000;return 0;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;case POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN:gm = gauge->gm;if (gm != NULL && val->intval != 0) {gm->imix = val->intval;if (gm->imix > 5500) {gm->imix = 5500;pr_notice("imix check limit 5500:%d\n",val->intval);}}break;default:ret = -EINVAL;break;}return ret;}
3.分析mt6358_sysfs_create_group函数
实现初始化电量计、获取实时电池电流、获取累积充放电电量、设置库仑计数器高阈值中断、设置库仑计数器低阈值中断、检测电池是否在位、获取硬件版本、获取电池电压、获取电池温度文件节点等等,具体函数设计寄存器读写不在说明。
路径:/sys/devices/platform/1000d000.pwrap/1000d000.pwrap:mt6358-pmic/mt6358-gauge/power_supply/mtk-gauge/创建文件节点。
/* Must be in the same order as GAUGE_PROP_* */
static struct mtk_gauge_sysfs_field_info mt6358_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), /* 电池接口电压(BIF)*/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), /* RTC保存的电池百分比(0~100) */GAUGE_SYSFS_FIELD_RO(ptim_battery_voltage_get, /* 获取PTIM模式下电池电压 */GAUGE_PROP_PTIM_BATTERY_VOLTAGE),GAUGE_SYSFS_FIELD_RO(ptim_resist_get,GAUGE_PROP_PTIM_RESIST), /* 获取PTIM模式下电池内阻 */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), /* 通过辅助adc读取电池电压 */GAUGE_SYSFS_FIELD_RO(zcv_current_get,GAUGE_PROP_ZCV_CURRENT), /* 获取电池电流 */GAUGE_SYSFS_FIELD_RO(nafg_cnt_get, /* 获取NAFG(电池未连接)计数 */GAUGE_PROP_NAFG_CNT),GAUGE_SYSFS_FIELD_RO(nafg_dltv_get,GAUGE_PROP_NAFG_DLTV), /* NAFG电压下降阈值 */GAUGE_SYSFS_FIELD_RW(nafg_c_dltv, nafg_c_dltv_set, nafg_c_dltv_get,GAUGE_PROP_NAFG_C_DLTV), /* 设置NAFG(Noise-Aware Fuel Gauge)模块的动态负载瞬态电压值 */GAUGE_SYSFS_FIELD_WO(nafg_en_set,GAUGE_PROP_NAFG_EN), /* NAFG(Noise-Aware Fuel Gauge)模块功能启用 */GAUGE_SYSFS_FIELD_WO(nafg_zcv_set,GAUGE_PROP_NAFG_ZCV), /* NAFG模式下的ZCV值 */GAUGE_SYSFS_FIELD_RO(nafg_vbat_get, /* NAFG模式下的电池电压值 */GAUGE_PROP_NAFG_VBAT),GAUGE_SYSFS_FIELD_WO(reset_fg_rtc_set,GAUGE_PROP_RESET_FG_RTC), /* 电量计重置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), /* 读取平均电流:uA */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), /* zcv中断阈值 */GAUGE_SYSFS_FIELD_WO(zcv_intr_en_set, /* 检测电池的零电流电压使能 */GAUGE_PROP_ZCV_INTR_EN),GAUGE_SYSFS_FIELD_WO(soff_reset_set, GAUGE_PROP_SOFF_RESET), /* SOFF(关机)状态重置 */GAUGE_SYSFS_FIELD_WO(ncar_reset_set,GAUGE_PROP_NCAR_RESET), /* NCAR(噪声校准)重置 */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), /* 2s强制重启 */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_con1_uisoc, GAUGE_PROP_CON1_UISOC), /* 用户界面显示的电池百分比 */GAUGE_SYSFS_INFO_FIELD_RW(info_con1_vaild, GAUGE_PROP_CON1_VAILD), /* SOC数据有效性标志 */GAUGE_SYSFS_INFO_FIELD_RW(info_shutdown_car, GAUGE_PROP_SHUTDOWN_CAR), /* 关机时剩余电量(μAh) 读写 */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), /* 电量计内阻值(mΩ) 读写 */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), /* 温度冻结使能(调试用) 控制 */GAUGE_SYSFS_FIELD_RO(battery_voltage_cali, GAUGE_PROP_BAT_EOC), /* 充电结束电流(EOC)校准 只读 */GAUGE_SYSFS_FIELD_RO(regmap_type_get, GAUGE_PROP_REGMAP_TYPE), /* 寄存器映射类型(SPI/I2C) 只读 */GAUGE_SYSFS_FIELD_RO(battery_cic2_get, GAUGE_PROP_CIC2), /* CIC2寄存器值(调试) 只读 */
};static struct attribute *mt6358_sysfs_attrs[GAUGE_PROP_MAX + 1];static const struct attribute_group mt6358_sysfs_attr_group = {.attrs = mt6358_sysfs_attrs,
};static void mt6358_sysfs_init_attrs(void)
{int i, limit = ARRAY_SIZE(mt6358_sysfs_field_tbl);for (i = 0; i < limit; i++)mt6358_sysfs_attrs[i] = &mt6358_sysfs_field_tbl[i].attr.attr;mt6358_sysfs_attrs[limit] = NULL; /* Has additional entry for this */
}static int mt6358_sysfs_create_group(struct mtk_gauge *gauge)
{mt6358_sysfs_init_attrs();return sysfs_create_group(&gauge->psy->dev.kobj,&mt6358_sysfs_attr_group);
}
kernel/kernel_device_modules-6.6/drivers/power/supply/mtk_gauge.h
enum gauge_property { /* 功能描述 类型 */GAUGE_PROP_INITIAL, /* 电量计初始化标志 控制 */GAUGE_PROP_BATTERY_CURRENT, /* 实时电池电流(μA) 只读 */GAUGE_PROP_COULOMB, /* 累积充放电电量(μAh) 只读 */GAUGE_PROP_COULOMB_HT_INTERRUPT, /* 库仑计数高阈值中断设置 控制 */GAUGE_PROP_COULOMB_LT_INTERRUPT, /* 库仑计数低阈值中断设置 控制 */GAUGE_PROP_BATTERY_EXIST, /* 电池在位检测(0/1) 只读 */GAUGE_PROP_HW_VERSION, /* 硬件版本信息 只读 */GAUGE_PROP_BATTERY_VOLTAGE, /* 实时电池电压(μV) 只读 */GAUGE_PROP_BATTERY_TEMPERATURE_ADC, /* 电池温度(ADC原始值) 只读 */GAUGE_PROP_BIF_VOLTAGE, /* 电池接口电压(BIF) 只读 */GAUGE_PROP_EN_HIGH_VBAT_INTERRUPT, /* 高电压中断使能 控制 */GAUGE_PROP_EN_LOW_VBAT_INTERRUPT, /* 低电压中断使能 控制 */GAUGE_PROP_VBAT_HT_INTR_THRESHOLD, /* 高电压阈值(μV) 控制 */GAUGE_PROP_VBAT_LT_INTR_THRESHOLD, /* 低电压阈值(μV) 控制 */GAUGE_PROP_RTC_UI_SOC, /* RTC保存的电池百分比(0~100)读写 */GAUGE_PROP_PTIM_BATTERY_VOLTAGE, /* PTIM模式下的电池电压 只读 */GAUGE_PROP_PTIM_RESIST, /* PTIM测量的电池内阻(mΩ) 只读 */GAUGE_PROP_RESET, /* 重置电量计 控制 */GAUGE_PROP_BOOT_ZCV, /* 启动时的ZCV值(μV) 只读 */GAUGE_PROP_ZCV, /* 零电流电压(μV) 只读 */GAUGE_PROP_ZCV_CURRENT, /* ZCV测量时的电流(μA) 只读 *//* 电池未连接(NAFG)检测 */GAUGE_PROP_NAFG_CNT, /* NAFG事件计数 只读 */GAUGE_PROP_NAFG_DLTV, /* NAFG电压下降阈值 只读 */GAUGE_PROP_NAFG_C_DLTV, /* NAFG电流下降阈值 读写 */GAUGE_PROP_NAFG_EN, /* NAFG检测使能 控制 */GAUGE_PROP_NAFG_ZCV, /* NAFG模式下的ZCV值 只读 */GAUGE_PROP_NAFG_VBAT, /* NAFG模式下的电池电压 只读 */GAUGE_PROP_RESET_FG_RTC,GAUGE_PROP_GAUGE_INITIALIZED, /* 电量计初始化状态 状态 */GAUGE_PROP_AVERAGE_CURRENT, /* 平均电流(μA) 只读 */GAUGE_PROP_BAT_PLUGOUT_EN, /* 电池拔出检测使能 控制 */GAUGE_PROP_ZCV_INTR_THRESHOLD, /* ZCV中断阈值 控制 */GAUGE_PROP_ZCV_INTR_EN, /* ZCV中断使能 控制 */GAUGE_PROP_SOFF_RESET, /* SOFF(关机)状态重置 控制 */GAUGE_PROP_NCAR_RESET, /* NCAR(噪声校准)重置 控制 */GAUGE_PROP_BAT_CYCLE_INTR_THRESHOLD,GAUGE_PROP_HW_INFO, /* 硬件信息 状态 */GAUGE_PROP_EVENT, /* 触发自定义事件(调试) 控制 */GAUGE_PROP_EN_BAT_TMP_HT, /* 高温中断使能 控制 */GAUGE_PROP_EN_BAT_TMP_LT, /* 低温中断使能 控制 */GAUGE_PROP_BAT_TMP_HT_THRESHOLD, /* 高温阈值(m°C) 控制 */GAUGE_PROP_BAT_TMP_LT_THRESHOLD, /* 低温阈值(m°C) 控制 */GAUGE_PROP_2SEC_REBOOT,//bit info /* 2秒强制重启标志 控制 */GAUGE_PROP_PL_CHARGING_STATUS, /* 充电状态标志 读写 */GAUGE_PROP_MONITER_PLCHG_STATUS,GAUGE_PROP_BAT_PLUG_STATUS, /* 电池插入状态 读写 */GAUGE_PROP_IS_NVRAM_FAIL_MODE,GAUGE_PROP_MONITOR_SOFF_VALIDTIME,GAUGE_PROP_CON0_SOC, /* 实时电池百分比(未校准) 读写 */GAUGE_PROP_CON1_UISOC, /* 用户界面显示的电池百分比 读写 */GAUGE_PROP_CON1_VAILD, /* SOC数据有效性标志 读写 */GAUGE_PROP_SHUTDOWN_CAR, /* 关机时剩余电量(μAh) 读写 */GAUGE_PROP_CAR_TUNE_VALUE, /* 库仑计数器校准值 读写 */GAUGE_PROP_R_FG_VALUE, /* 电量计内阻值(mΩ) 读写 */GAUGE_PROP_VBAT2_DETECT_TIME,GAUGE_PROP_VBAT2_DETECT_COUNTER,GAUGE_PROP_BAT_TEMP_FROZE_EN, /* 温度冻结使能(调试用) 控制 */GAUGE_PROP_BAT_EOC, /* 充电结束电流(EOC)校准 只读 */GAUGE_PROP_REGMAP_TYPE, /* 寄存器映射类型(SPI/I2C) 只读 */GAUGE_PROP_CIC2, /* CIC2寄存器值(调试) 只读 */GAUGE_PROP_MAX,
};
4.分析battery_init函数
****检查启动模式,读取lk阶段记录的电池信息,电量计参数初始化,电量计服务初始化,电池信息更新。
int battery_init(struct platform_device *pdev)
{int ret = 0;bool b_recovery_mode = 0;struct mtk_battery *gm;struct mtk_gauge *gauge;gauge = dev_get_drvdata(&pdev->dev);gm = gauge->gm;gm->fixed_bat_tmp = 0xffff;gm->tmp_table = fg_temp_table;gm->log_level = BMLOG_ERROR_LEVEL;gm->sw_iavg_gap = 3000;gm->in_sleep = false;mutex_init(&gm->fg_update_lock);init_waitqueue_head(&gm->wait_que);/*检查启动模式*/fg_check_bootmode(&pdev->dev, gm);/* 在启动时读取 LK(Little Kernel,Bootloader)阶段 记录的电池信息(如电压、电流、关机时间),用于电池校准或初始化。 */fg_check_lk_swocv(&pdev->dev, gm);/* 属性控制参数初始化 */fg_prop_control_init(gm);/* 检测电池类型 */fg_check_bat_type(pdev, gm);/* 电量计参数初始化 */fg_custom_init_from_header(gm);fg_custom_init_from_dts(pdev, gm);mtk_irq_thread_init(gm);/* 电量计服务初始化 */gauge_coulomb_service_init(gm);gm->coulomb_plus.callback = fg_coulomb_int_h_handler;gauge_coulomb_consumer_init(&gm->coulomb_plus, &pdev->dev, "car+1%");gm->coulomb_minus.callback = fg_coulomb_int_l_handler;gauge_coulomb_consumer_init(&gm->coulomb_minus, &pdev->dev, "car-1%");gauge_coulomb_consumer_init(&gm->uisoc_plus, &pdev->dev, "uisoc+1%");gm->uisoc_plus.callback = fg_bat_int2_h_handler;gauge_coulomb_consumer_init(&gm->uisoc_minus, &pdev->dev, "uisoc-1%");gm->uisoc_minus.callback = fg_bat_int2_l_handler;alarm_init(&gm->tracking_timer, ALARM_BOOTTIME,tracking_timer_callback);INIT_WORK(&gm->tracking_timer_work, tracking_timer_work_handler);alarm_init(&gm->one_percent_timer, ALARM_BOOTTIME,one_percent_timer_callback);INIT_WORK(&gm->one_percent_timer_work, one_percent_timer_work_handler);alarm_init(&gm->sw_uisoc_timer, ALARM_BOOTTIME,sw_uisoc_timer_callback);INIT_WORK(&gm->sw_uisoc_timer_work, sw_uisoc_timer_work_handler);/*电池信息更新*/kthread_run(battery_update_routine, gm, "%s", gm->gauge->name);gm->pm_nb.notifier_call = system_pm_notify;ret = register_pm_notifier(&gm->pm_nb);if (ret)bm_err(gm, "%s failed to register system pm notify\n", __func__);fg_drv_thread_hrtimer_init(gm);battery_sysfs_create_group(gm);gm->battery_sysfs = battery_sysfs_field_tbl;/* for gauge hal hw ocv */gm->battery_temp = force_get_tbat(gm, true);//mtk_power_misc_init(gm);ret = mtk_battery_daemon_init(pdev);b_recovery_mode = is_recovery_mode(gm);gm->is_probe_done = true;if (ret == 0 && b_recovery_mode == 0)bm_err(gm, "[%s]: daemon mode DONE\n", __func__);else {gm->algo.active = true;battery_algo_init(gm);bm_err(gm, "[%s]: enable Kernel mode Gauge\n", __func__);}return 0;
}
4.1 电量计库伦服务初始化 - gauge_coulomb_service_init
void gauge_coulomb_service_init(struct mtk_battery *gm)
{int val = 0;struct mtk_coulomb_service *cs;int ret = 0;bm_debug(gm, "[%s] into\n", __func__);cs = &gm->cs;cs->gm = gm;ret = snprintf(cs->name, 20, "%s gct", gm->gauge->name);if (ret < 0)bm_err(gm, "[%s] something wrong\n", __func__);INIT_LIST_HEAD(&cs->coulomb_head_minus);INIT_LIST_HEAD(&cs->coulomb_head_plus);mutex_init(&cs->coulomb_lock);mutex_init(&cs->hw_coulomb_lock);spin_lock_init(&cs->slock);cs->wlock = wakeup_source_register(NULL, "gauge coulomb wakelock");init_waitqueue_head(&cs->wait_que);atomic_set(&cs->in_sleep, 0);kthread_run(gauge_coulomb_thread, cs, "%s", cs->name); /*函数非常重要*/cs->pm_nb.notifier_call = system_pm_notify;ret = register_pm_notifier(&cs->pm_nb);if (ret)bm_err(gm, "failed to register system pm notify\n");ret = devm_request_threaded_irq(&gm->gauge->pdev->dev,gm->gauge->irq_no[COULOMB_H_IRQ],NULL, coulomb_irq,IRQF_ONESHOT | IRQF_TRIGGER_HIGH,"mtk_gauge_coulomb_high",gm);//disable_irq_nosync(gm->gauge->coulomb_h_irq);if (ret)bm_err(gm, "failed to request coulomb h irq\n");ret = devm_request_threaded_irq(&gm->gauge->pdev->dev,gm->gauge->irq_no[COULOMB_L_IRQ],NULL, coulomb_irq,IRQF_ONESHOT | IRQF_TRIGGER_HIGH,"mtk_gauge_coulomb_low",gm);//disable_irq_nosync(gm->gauge->coulomb_l_irq);if (ret)bm_err(gm, "failed to request coulomb l irq\n");gauge_get_property(gm, GAUGE_PROP_COULOMB, &val);cs->pre_coulomb = val;cs->init = true;
}
分析电量计库伦统计线程 - gauge_coulomb_thread
static int gauge_coulomb_thread(void *arg)
{struct mtk_coulomb_service *cs = (struct mtk_coulomb_service *)arg;unsigned long flags = 0;ktime_t start, end, duraction;int ret = 0;struct mtk_battery *gm =container_of(cs, struct mtk_battery, cs);bm_debug(gm, "[%s]%s:=>\n", cs->name, __func__);while (1) {/*coulomb_thread_timeout为真且不是休眠状态*/ret = wait_event_interruptible(cs->wait_que,cs->coulomb_thread_timeout == true &&!atomic_read(&cs->in_sleep));if (atomic_read(&cs->in_sleep) || ret < 0) {__pm_relax(cs->wlock);continue;}cs->coulomb_thread_timeout = false;start = ktime_get_boottime();mutex_lock(&cs->coulomb_lock);gauge_coulomb_int_handler(cs); //核心处理函数mutex_unlock(&cs->coulomb_lock);spin_lock_irqsave(&cs->slock, flags);__pm_relax(cs->wlock);spin_unlock_irqrestore(&cs->slock, flags);end = ktime_get_boottime();duraction = end - start;bm_debug(gm, "%s time:%d ms\n", __func__,(int)(div_s64(duraction, 1000000)));}return 0;
}
分析gauge_coulomb_int_handler函数
static void gauge_coulomb_int_handler(struct mtk_coulomb_service *cs)
{int car = 0, hw_car;struct list_head *pos;struct list_head *phead;struct gauge_consumer *ptr = NULL;struct mtk_battery *gm =container_of(cs, struct mtk_battery, cs);/* 获取当前库仑计值打印当前值和上次值 */gauge_get_property(cs->gm, GAUGE_PROP_COULOMB, &car);bm_debug(gm, "[%s]%s car:%d preCar:%d\n",cs->name, __func__,car, cs->pre_coulomb);/* 充电时刻 */if (list_empty(&cs->coulomb_head_plus) != true) {pos = cs->coulomb_head_plus.next;phead = &cs->coulomb_head_plus;for (pos = phead->next; pos != phead;) {struct list_head *ptmp;ptr = container_of(pos, struct gauge_consumer, list);/*充电满*/if (ptr->end <= car) {ptmp = pos;pos = pos->next;list_del_init(ptmp);bm_debug(gm,"[%s %s]+ %s s:%ld e:%ld car:%d %d int:%d timeout\n",cs->name,__func__,ptr->name,ptr->start, ptr->end, car,cs->pre_coulomb, ptr->variable);if (ptr->callback) {mutex_unlock(&cs->coulomb_lock);ptr->callback(cs->gm, ptr);mutex_lock(&cs->coulomb_lock);pos = cs->coulomb_head_plus.next;}} elsebreak;}if (list_empty(&cs->coulomb_head_plus) != true) {pos = cs->coulomb_head_plus.next;ptr = container_of(pos, struct gauge_consumer, list);hw_car = ptr->end - car; /*下一次库仑计数高阈值中断值*/bm_debug(gm,"[%s %s]+ %s %ld %ld %d now:%d dif:%d\n",cs->name,__func__,ptr->name,ptr->start, ptr->end,ptr->variable, car, hw_car);mutex_lock(&cs->hw_coulomb_lock);gauge_set_property(cs->gm, GAUGE_PROP_COULOMB_HT_INTERRUPT,hw_car); mutex_unlock(&cs->hw_coulomb_lock);} elsebm_debug(gm, "%s + list is empty\n", cs->name);} elsebm_debug(gm, "%s + list is empty\n", cs->name);/*放电时刻*/if (list_empty(&cs->coulomb_head_minus) != true) {pos = cs->coulomb_head_minus.next;phead = &cs->coulomb_head_minus;for (pos = phead->next; pos != phead;) {struct list_head *ptmp;ptr = container_of(pos, struct gauge_consumer, list);if (ptr->end >= car) {ptmp = pos;pos = pos->next;list_del_init(ptmp);bm_debug(gm,"[%s %s]- %s s:%ld e:%ld car:%d %d int:%d timeout\n",cs->name,__func__,ptr->name,ptr->start, ptr->end,car, cs->pre_coulomb, ptr->variable);if (ptr->callback) {mutex_unlock(&cs->coulomb_lock);ptr->callback(cs->gm, ptr);mutex_lock(&cs->coulomb_lock);pos = cs->coulomb_head_minus.next;}} elsebreak;}if (list_empty(&cs->coulomb_head_minus) != true) {pos = cs->coulomb_head_minus.next;ptr = container_of(pos, struct gauge_consumer, list);hw_car = car - ptr->end;bm_debug(gm,"[%s %s]- %s %ld %ld %d now:%d dif:%d\n",cs->name,__func__,ptr->name,ptr->start, ptr->end,ptr->variable, car, hw_car);mutex_lock(&cs->hw_coulomb_lock);gauge_set_property(cs->gm, GAUGE_PROP_COULOMB_LT_INTERRUPT,hw_car);mutex_unlock(&cs->hw_coulomb_lock);} elsebm_debug(gm, "%s - list is empty\n", cs->name);} elsebm_debug(gm, "%s - list is empty\n", cs->name);cs->pre_coulomb = car;
}
4.2 电池状态信息更新 - battery_update_routine
****电池管理后台线程函数,用于在非睡眠状态下周期性地更新硬件状态(如电压、电流、电量等)
int battery_update_routine(void *arg)
{struct mtk_battery *gm = (struct mtk_battery *)arg;int ret = 0, retry = 0;while (1) {/* 当fg_update_flag > 0 和 非休眠态时触发 */ret = wait_event_interruptible(gm->wait_que,(gm->fg_update_flag > 0) && !gm->in_sleep);if (ret < 0) {retry++;if (retry < 0xFFFFFFFF)continue;else {bm_err(gm, "%s something wrong retry: %d\n", __func__, retry);break;}}retry = 0;mutex_lock(&gm->fg_update_lock);if (gm->in_sleep)goto in_sleep;gm->fg_update_flag = 0;fg_drv_update_hw_status(gm);/* 更新电池硬件状态 */
in_sleep:mutex_unlock(&gm->fg_update_lock);}return 0;
}/* 更新电池硬件状态 */
static void fg_drv_update_hw_status(struct mtk_battery *gm)
{ktime_t ktime;struct property_control *prop_control;char gp_name[MAX_GAUGE_PROP_LEN];char reg_type_name[MAX_REGMAP_TYPE_LEN];int i, regmap_type, is_battery_exist = 0;long car;prop_control = &gm->prop_control;fg_update_porp_control(prop_control);/* 强制读取电池温度、电池电压、电流、电池存在标志 */gm->tbat = force_get_tbat_internal(gm);gm->vbat = gauge_get_int_property(gm,GAUGE_PROP_BATTERY_VOLTAGE);gm->ibat = gauge_get_int_property(gm,GAUGE_PROP_BATTERY_CURRENT);car = gauge_get_int_property(gm, GAUGE_PROP_COULOMB);gauge_get_property_control(gm, GAUGE_PROP_BATTERY_EXIST,&is_battery_exist, 0);bm_err(gm, "[%s] car[%ld,%ld,%ld,%ld,%ld] tmp:%d soc:%d uisoc:%d vbat:%d ibat:%d baton:%d algo:%d gm3:%d %d %d %d %d %d, get_prop:%d %lld %d %d %d %lld %ld %lld, boot:%d rac:%d\n",gm->gauge->name,car,gm->coulomb_plus.end, gm->coulomb_minus.end,gm->uisoc_plus.end, gm->uisoc_minus.end,gm->tbat,gm->soc, gm->ui_soc,gm->vbat,gm->ibat,gm->baton,gm->algo.active,gm->disableGM30, gm->fg_cust_data.disable_nafg,gm->ntc_disable_nafg, gm->cmd_disable_nafg, gm->vbat0_flag, gm->is_evb_board,gm->no_prop_timeout_control, prop_control->last_period.tv_sec,prop_control->last_binder_counter, prop_control->total_fail,prop_control->max_gp, prop_control->max_get_prop_time.tv_sec,prop_control->max_get_prop_time.tv_nsec/1000000,prop_control->last_diff_time.tv_sec, gm->bootmode,gauge_get_int_property(gm, GAUGE_PROP_PTIM_RESIST));fg_drv_update_daemon(gm);/* 硬件访问超时异常处理*/prop_control->max_get_prop_time = ktime_to_timespec64(0);if (prop_control->end_get_prop_time == 0 &&prop_control->last_diff_time.tv_sec > prop_control->i2c_fail_th) {gp_number_to_name(gm, gp_name, prop_control->curr_gp);regmap_type = gauge_get_int_property(gm, GAUGE_PROP_REGMAP_TYPE);reg_type_to_name(gm, reg_type_name, regmap_type);bm_err(gm, "[%s_Error] get %s hang over 3 sec, time:%lld\n",reg_type_name, gp_name, prop_control->last_diff_time.tv_sec);
#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)if (!gm->disableGM30)aee_kernel_warning("BATTERY", "gauge get prop over 3 sec\n");
#endif}if (!gm->disableGM30 && prop_control->total_fail > 20) {regmap_type = gauge_get_int_property(gm,GAUGE_PROP_REGMAP_TYPE);reg_type_to_name(gm, reg_type_name, regmap_type);bm_err(gm, "[%s_Error] Binder last counter: %d, period: %lld", reg_type_name,prop_control->last_binder_counter, prop_control->last_period.tv_sec);for (i = 0; i < GAUGE_PROP_MAX; i++) {gp_number_to_name(gm, gp_name, i);bm_err(gm, "[%s_Error] %s, fail_counter: %d\n",reg_type_name, gp_name, prop_control->i2c_fail_counter[i]);prop_control->i2c_fail_counter[i] = 0;}#if IS_ENABLED(CONFIG_MTK_AEE_FEATURE)aee_kernel_warning("BATTERY", "gauge get prop fail case over 20 times\n");
#endif}gauge_coulomb_dump_list(gm);/*库仑计容错处理*/if ((car < gm->coulomb_minus.end-30 || car > gm->coulomb_plus.end+30) && is_battery_exist) {bm_err(gm, "coulomb service stop %ld %ld %d %d %d",car, gm->coulomb_minus.end, car > gm->coulomb_plus.end,car < gm->coulomb_minus.end, car > gm->coulomb_plus.end);wake_up_gauge_coulomb(gm);}if (gm->algo.active == true)battery_update(gm->bm); // 触发电量算法更新if (gm->log_level >= BMLOG_DEBUG_LEVEL)ktime = ktime_set(10, 0);elsektime = ktime_set(60, 0);hrtimer_start(&gm->fg_hrtimer, ktime, HRTIMER_MODE_REL);
}
5.日志
bm_err(gm, "[%s] car[%ld,%ld,%ld,%ld,%ld] tmp:%d soc:%d uisoc:%d vbat:%d ibat:%d baton:%d algo:%d gm3:%d %d %d %d %d %d, get_prop:%d %lld %d %d %d %lld %ld %lld, boot:%d rac:%d\n",gm->gauge->name, /*电量计名称*/car, /*当前库仑计累计值*//*充电方向阈值(充满电) 放电方向阈值(低电告警)*/gm->coulomb_plus.end, gm->coulomb_minus.end, /*UI显示的充电完成SOC阈值(%) UI显示的低电量SOC阈值(%)*/gm->uisoc_plus.end, gm->uisoc_minus.end,/*电池温度*/ gm->tbat,/*系统内部计算的精确电量 显示给用户的电量(平滑处理)*/gm->soc, gm->ui_soc,gm->vbat, /* 电池电压 */gm->ibat, /* 电池电流(正:充电,负:放电)*/gm->baton, /* 电池在位状态(1=存在)*/gm->algo.active, /* 电量算法是否激活 *//* 是否禁用GM3.0算法 禁用NAFG(噪声抑制)功能 */gm->disableGM30, gm->fg_cust_data.disable_nafg,/* NTC温度导致的NAFG禁用 命令强制禁用NAFG 电池电压为0的特殊标志 是否开发板环境*/gm->ntc_disable_nafg, gm->cmd_disable_nafg, gm->vbat0_flag, gm->is_evb_board,/* 是否禁用属性获取超时控制 上次属性获取周期时长 */gm->no_prop_timeout_control, prop_control->last_period.tv_sec,/* 最近一次binder调用计数 总失败次数 */prop_control->last_binder_counter, prop_control->total_fail,/* 耗时最长的属性ID 单次属性获取最大耗时(秒) */prop_control->max_gp, prop_control->max_get_prop_time.tv_sec,/* 最大耗时的毫秒部分 */prop_control->max_get_prop_time.tv_nsec/1000000,/* 最近一次操作耗时 启动模式 */prop_control->last_diff_time.tv_sec, gm->bootmode,gauge_get_int_property(gm, GAUGE_PROP_PTIM_RESIST));
6736: <5>[ 73.044317][ T170] fgauge: fgauge:[fgauge] car[-250,6,-510,-201,-503] tmp:25 soc:34 uisoc:34 vbat:3754 ibat:-4638 baton:806 algo:0 gm3:0 0 0 0 0 0, get_prop:0 60 626 0 9 0 53 0, boot:0 rac:90