/* * bd7181x-power.c * @file ROHM BD71815/BD71817 Charger driver * * Copyright 2014 Embest Technology Co. Ltd. Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ /*#define DEBUG*/ #include #include #include #include #include #include #include #include #define JITTER_DEFAULT 10000 /* hope 3s is enough */ #define JITTER_REPORT_CAP 10000 /* 10 seconds */ #define CALIB_CURRENT_A2A3 0xCE9E #define BD7181X_BATTERY_CAP_MAH 1200 #define BD7181X_BATTERY_CAP mAh_A10s(BD7181X_BATTERY_CAP_MAH) #define MAX_VOLTAGE ocv_table[0] #define MIN_VOLTAGE 3200000 #define THR_VOLTAGE 4100000 #define MAX_CURRENT 1500000 /* uA */ #define AC_NAME "bd7181x_ac" #define BAT_NAME "bd7181x_bat" #define BD7181X_BATTERY_FULL 100 #define BY_BAT_VOLT 0 #define BY_VBATLOAD_REG 1 #define INIT_COULOMB BY_VBATLOAD_REG #define CALIB_CURRENT_A2A3 0xCE9E //VBAT Low voltage detection Threshold #define VBAT_LOW_TH 0x00D4 // 0x00D4*16mV = 212*0.016 = 3.392v #define RS_30mOHM /* This is for 30mOhm sense resistance */ #ifdef RS_30mOHM #define A10s_mAh(s) ((s) * 1000 / (360 * 3)) #define mAh_A10s(m) ((m) * (360 * 3) / 1000) #else #define A10s_mAh(s) ((s) * 1000 / 360) #define mAh_A10s(m) ((m) * 360 / 1000) #endif #define THR_RELAX_CURRENT 5 /* mA */ #define THR_RELAX_TIME ((60 * 60) - 10) /* sec. */ #define BD7181X_DGRD_CYC_CAP 15 /* 1 micro Ah unit */ #define BD7181X_DGRD_TEMP_M 25 /* 1 degrees C unit */ #define BD7181X_DGRD_TEMP_L 5 /* 1 degrees C unit */ #define BD7181X_DGRD_TEMP_CAP_H (0) /* 1 micro Ah unit */ #define BD7181X_DGRD_TEMP_CAP_M (0) /* 1 micro Ah unit */ #define BD7181X_DGRD_TEMP_CAP_L (0) /* 1 micro Ah unit */ #define CANCEL_ADJ_COULOMB_SOC_H_1 700 /* unit 0.1% */ #define CANCEL_ADJ_COULOMB_SOC_L_1 550 /* unit 0.1% */ #define CANCEL_ADJ_COULOMB_SOC_H_2 350 /* unit 0.1% */ #define CANCEL_ADJ_COULOMB_SOC_L_2 0 /* unit 0.1% */ #define FORCE_ADJ_COULOMB_TEMP_H 35 /* 1 degrees C unit */ #define FORCE_ADJ_COULOMB_TEMP_L 15 /* 1 degrees C unit */ unsigned int battery_cycle; static int ocv_table[] = { 4200000, 4160000, 4111638, 4081966, 4016618, 3974931, 3953040, 3925422, 3896285, 3859529, 3835339, 3815914, 3803244, 3793558, 3783886, 3780000, 3769083, 3749738, 3725590, 3705524, 3646077, 3400000, 2682635 }; /* unit 1 micro V */ static int soc_table[] = { 1000, 1000, 950, 900, 850, 800, 750, 700, 650, 600, 550, 500, 450, 400, 350, 300, 250, 200, 150, 100, 50, 0, -50 /* unit 0.1% */ }; /** @brief power deivce */ struct bd7181x_power { struct device *dev; struct bd7181x *mfd; /**< parent for access register */ struct power_supply *ac; /**< alternating current power */ struct power_supply_desc ac_desc; struct power_supply_config ac_cfg; struct power_supply *bat; /**< battery power */ struct power_supply_desc bat_desc; struct power_supply_config bat_cfg; struct delayed_work bd_work; /**< delayed work for timed work */ int reg_index; /**< register address saved for sysfs */ int vbus_status; /**< last vbus status */ int charge_status; /**< last charge status */ int bat_status; /**< last bat status */ int hw_ocv1; /**< HW ocv1 */ int hw_ocv2; /**< HW ocv2 */ int bat_online; /**< battery connect */ int charger_online; /**< charger connect */ int vcell; /**< battery voltage */ int vsys; /**< system voltage */ int vcell_min; /**< minimum battery voltage */ int vsys_min; /**< minimum system voltage */ int rpt_status; /**< battery status report */ int prev_rpt_status; /**< previous battery status report */ int bat_health; /**< battery health */ int designed_cap; /**< battery designed capacity */ int full_cap; /**< battery capacity */ int curr; /**< battery current from DS-ADC */ int curr_sar; /**< battery current from VM_IBAT */ int temp; /**< battery tempature */ u32 coulomb_cnt; /**< Coulomb Counter */ int state_machine; /**< initial-procedure state machine */ u32 soc_org; /**< State Of Charge using designed capacity without by load */ u32 soc_norm; /**< State Of Charge using full capacity without by load */ u32 soc; /**< State Of Charge using full capacity with by load */ u32 clamp_soc; /**< Clamped State Of Charge using full capacity with by load */ int relax_time; /**< Relax Time */ u32 cycle; /**< Charging and Discharging cycle number */ volatile int calib_current; /**< calibration current */ }; #define CALIB_NORM 0 #define CALIB_START 1 #define CALIB_GO 2 enum { STAT_POWER_ON, STAT_INITIALIZED, }; static int bd7181x_calc_soc_org(struct bd7181x_power* pwr); /** @brief read a register group once * @param mfd bd7181x device * @param reg register address of lower register * @return register value */ #ifdef __BD7181X_REGMAP_H__ static u16 bd7181x_reg_read16(struct bd7181x* mfd, int reg) { u16 v; v = (u16)bd7181x_reg_read(mfd, reg) << 8; v |= (u16)bd7181x_reg_read(mfd, reg + 1) << 0; return v; } #else static u16 bd7181x_reg_read16(struct bd7181x* mfd, int reg) { union { u16 long_type; char chars[2]; } u; int r; r = regmap_bulk_read(mfd->regmap, reg, u.chars, sizeof u.chars); if (r) { return -1; } return be16_to_cpu(u.long_type); } #endif /** @brief write a register group once * @param mfd bd7181x device * @param reg register address of lower register * @param val value to write * @retval 0 success * @retval -1 fail */ static int bd7181x_reg_write16(struct bd7181x *mfd, int reg, u16 val) { union { u16 long_type; char chars[2]; } u; int r; u.long_type = cpu_to_be16(val); // printk("write16 0x%.4X 0x%.4X\n", val, u.long_type); #ifdef __BD7181X_REGMAP_H__ r = mfd->write(mfd, reg, sizeof u.chars, u.chars); #else r = regmap_bulk_write(mfd->regmap, reg, u.chars, sizeof u.chars); #endif if (r) { return -1; } return 0; } /** @brief read quad register once * @param mfd bd7181x device * @param reg register address of lower register * @return register value */ static int bd7181x_reg_read32(struct bd7181x *mfd, int reg) { union { u32 long_type; char chars[4]; } u; int r; #ifdef __BD7181X_REGMAP_H__ r = mfd->read(mfd, reg, sizeof u.chars, u.chars); #else r = regmap_bulk_read(mfd->regmap, reg, u.chars, sizeof u.chars); #endif if (r) { return -1; } return be32_to_cpu(u.long_type); } #if 0 /** @brief write quad register once * @param mfd bd7181x device * @param reg register address of lower register * @param val value to write * @retval 0 success * @retval -1 fail */ static int bd7181x_reg_write32(struct bd7181x *mfd, int reg, unsigned val) { union { u32 long_type; char chars[4]; } u; int r; u.long_type = cpu_to_be32(val); r = regmap_bulk_write(mfd->regmap, reg, u.chars, sizeof u.chars); if (r) { return -1; } return 0; } #endif #if INIT_COULOMB == BY_VBATLOAD_REG /** @brief get initial battery voltage and current * @param pwr power device * @return 0 */ static int bd7181x_get_init_bat_stat(struct bd7181x_power *pwr) { struct bd7181x *mfd = pwr->mfd; int vcell; vcell = bd7181x_reg_read16(mfd, BD7181X_REG_VM_OCV_PRE_U) * 1000; dev_dbg(pwr->dev, "VM_OCV_PRE = %d\n", vcell); pwr->hw_ocv1 = vcell; vcell = bd7181x_reg_read16(mfd, BD7181X_REG_VM_OCV_PST_U) * 1000; dev_dbg(pwr->dev, "VM_OCV_PST = %d\n", vcell); pwr->hw_ocv2 = vcell; return 0; } #endif /** @brief get battery average voltage and current * @param pwr power device * @param vcell pointer to return back voltage in unit uV. * @param curr pointer to return back current in unit uA. * @return 0 */ static int bd7181x_get_vbat_curr(struct bd7181x_power *pwr, int *vcell, int *curr) { struct bd7181x* mfd = pwr->mfd; int tmp_vcell, tmp_curr; tmp_vcell = 0; tmp_curr = 0; tmp_vcell = bd7181x_reg_read16(mfd, BD7181X_REG_VM_SA_VBAT_U); tmp_curr = bd7181x_reg_read16(mfd, BD7181X_REG_VM_SA_IBAT_U); if (tmp_curr & IBAT_SA_DIR_Discharging) { tmp_curr = -(tmp_curr & ~IBAT_SA_DIR_Discharging); } *vcell = tmp_vcell * 1000; #ifdef RS_30mOHM *curr = tmp_curr * 1000 / 3; #else *curr = tmp_curr * 1000; #endif return 0; } /** @brief get battery current from DS-ADC * @param pwr power device * @return current in unit uA */ static int bd7181x_get_current_ds_adc(struct bd7181x_power *pwr) { int r; r = bd7181x_reg_read16(pwr->mfd, BD7181X_REG_CC_CURCD_U); if (r < 0) { return 0; } if (r & CURDIR_Discharging) { r = -(r & ~CURDIR_Discharging); } #ifdef RS_30mOHM return r * 1000 / 3; #else return r * 1000; #endif } /** @brief get system average voltage * @param pwr power device * @param vcell pointer to return back voltage in unit uV. * @return 0 */ static int bd7181x_get_vsys(struct bd7181x_power *pwr, int *vsys) { struct bd7181x* mfd = pwr->mfd; int tmp_vsys; tmp_vsys = 0; tmp_vsys = bd7181x_reg_read16(mfd, BD7181X_REG_VM_SA_VSYS_U); *vsys = tmp_vsys * 1000; return 0; } /** @brief get battery minimum average voltage * @param pwr power device * @param vcell pointer to return back voltage in unit uV. * @return 0 */ static int bd7181x_get_vbat_min(struct bd7181x_power *pwr, int *vcell) { struct bd7181x* mfd = pwr->mfd; int tmp_vcell; tmp_vcell = 0; tmp_vcell = bd7181x_reg_read16(mfd, BD7181X_REG_VM_SA_VBAT_MIN_U); bd7181x_set_bits(pwr->mfd, BD7181X_REG_VM_SA_MINMAX_CLR, VBAT_SA_MIN_CLR); *vcell = tmp_vcell * 1000; return 0; } /** @brief get system minimum average voltage * @param pwr power device * @param vcell pointer to return back voltage in unit uV. * @return 0 */ static int bd7181x_get_vsys_min(struct bd7181x_power *pwr, int *vcell) { struct bd7181x* mfd = pwr->mfd; int tmp_vcell; tmp_vcell = 0; tmp_vcell = bd7181x_reg_read16(mfd, BD7181X_REG_VM_SA_VSYS_MIN_U); bd7181x_set_bits(pwr->mfd, BD7181X_REG_VM_SA_MINMAX_CLR, VSYS_SA_MIN_CLR); *vcell = tmp_vcell * 1000; return 0; } /** @brief get battery capacity * @param ocv open circuit voltage * @return capcity in unit 0.1 percent */ static int bd7181x_voltage_to_capacity(int ocv) { int i = 0; int soc; if (ocv > ocv_table[0]) { soc = soc_table[0]; } else { i = 0; while (soc_table[i] != -50) { if ((ocv <= ocv_table[i]) && (ocv > ocv_table[i+1])) { soc = (soc_table[i] - soc_table[i+1]) * (ocv - ocv_table[i+1]) / (ocv_table[i] - ocv_table[i+1]); soc += soc_table[i+1]; break; } i++; } if (soc_table[i] == -50) soc = soc_table[i]; } return soc; } /** @brief get battery temperature * @param pwr power device * @return temperature in unit deg.Celsius */ static int bd7181x_get_temp(struct bd7181x_power *pwr) { struct bd7181x* mfd = pwr->mfd; int t; t = 200 - (int)bd7181x_reg_read(mfd, BD7181X_REG_VM_BTMP); // battery temperature error t = (t > 200)? 200: t; return t; } static int bd7181x_reset_coulomb_count(struct bd7181x_power* pwr); /** @brief get battery charge status * @param pwr power device * @return temperature in unit deg.Celsius */ static int bd7181x_charge_status(struct bd7181x_power *pwr) { u8 state; int ret = 1; state = bd7181x_reg_read(pwr->mfd, BD7181X_REG_CHG_STATE); dev_dbg(pwr->dev, "%s(): CHG_STATE %d\n", __func__, state); switch (state) { case 0x00: ret = 0; pwr->rpt_status = POWER_SUPPLY_STATUS_DISCHARGING; pwr->bat_health = POWER_SUPPLY_HEALTH_GOOD; break; case 0x01: case 0x02: case 0x03: case 0x0E: pwr->rpt_status = POWER_SUPPLY_STATUS_CHARGING; pwr->bat_health = POWER_SUPPLY_HEALTH_GOOD; break; case 0x0F: ret = 0; pwr->rpt_status = POWER_SUPPLY_STATUS_FULL; pwr->bat_health = POWER_SUPPLY_HEALTH_GOOD; break; case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: ret = 0; pwr->rpt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; pwr->bat_health = POWER_SUPPLY_HEALTH_OVERHEAT; break; case 0x30: case 0x31: case 0x32: case 0x40: ret = 0; pwr->rpt_status = POWER_SUPPLY_STATUS_DISCHARGING; pwr->bat_health = POWER_SUPPLY_HEALTH_GOOD; break; case 0x7f: default: ret = 0; pwr->rpt_status = POWER_SUPPLY_STATUS_NOT_CHARGING; pwr->bat_health = POWER_SUPPLY_HEALTH_DEAD; break; } bd7181x_reset_coulomb_count(pwr); pwr->prev_rpt_status = pwr->rpt_status; return ret; } #if INIT_COULOMB == BY_BAT_VOLT static int bd7181x_calib_voltage(struct bd7181x_power* pwr, int* ocv) { int r, curr, volt; bd7181x_get_vbat_curr(pwr, &volt, &curr); r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_CHG_STATE); if (r >= 0 && curr > 0) { // voltage increment caused by battery inner resistor if (r == 3) volt -= 100 * 1000; else if (r == 2) volt -= 50 * 1000; } *ocv = volt; return 0; } #endif /** @brief set initial coulomb counter value from battery voltage * @param pwr power device * @return 0 */ static int calibration_coulomb_counter(struct bd7181x_power* pwr) { u32 bcap; int soc, ocv; #if INIT_COULOMB == BY_VBATLOAD_REG /* Get init OCV by HW */ bd7181x_get_init_bat_stat(pwr); ocv = (pwr->hw_ocv1 >= pwr->hw_ocv2)? pwr->hw_ocv1: pwr->hw_ocv2; dev_dbg(pwr->dev, "ocv %d\n", ocv); #elif INIT_COULOMB == BY_BAT_VOLT bd7181x_calib_voltage(pwr, &ocv); #endif /* Get init soc from ocv/soc table */ soc = bd7181x_voltage_to_capacity(ocv); dev_dbg(pwr->dev, "soc %d[0.1%%]\n", soc); if (soc < 0) soc = 0; bcap = pwr->designed_cap * soc / 1000; bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_1, 0); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_3, ((bcap + pwr->designed_cap / 200) & 0x1FFFUL)); pwr->coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "%s() CC_CCNTD = %d\n", __func__, pwr->coulomb_cnt); /* Start canceling offset of the DS ADC. This needs 1 second at least */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCCALIB); return 0; } /** @brief adjust coulomb counter values at relaxed state * @param pwr power device * @return 0 */ static int bd7181x_adjust_coulomb_count(struct bd7181x_power* pwr) { u32 relaxed_coulomb_cnt; relaxed_coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_REX_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "%s(): relaxed_coulomb_cnt = 0x%x\n", __func__, relaxed_coulomb_cnt); if (relaxed_coulomb_cnt != 0) { u32 bcap; int soc, ocv; int diff_coulomb_cnt; /* Get OCV at relaxed state by HW */ ocv = bd7181x_reg_read16(pwr->mfd, BD7181X_REG_REX_SA_VBAT_U) * 1000; dev_dbg(pwr->dev, "ocv %d\n", ocv); /* Clear Relaxed Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_REX_CTRL_1, REX_CLR); diff_coulomb_cnt = relaxed_coulomb_cnt - (bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL); diff_coulomb_cnt = diff_coulomb_cnt >> 16; dev_dbg(pwr->dev, "diff_coulomb_cnt = %d\n", diff_coulomb_cnt); /* Get soc at relaxed state from ocv/soc table */ soc = bd7181x_voltage_to_capacity(ocv); dev_dbg(pwr->dev, "soc %d[0.1%%]\n", soc); if (soc < 0) soc = 0; if ((soc > CANCEL_ADJ_COULOMB_SOC_H_1) || ((soc < CANCEL_ADJ_COULOMB_SOC_L_1) && (soc > CANCEL_ADJ_COULOMB_SOC_H_2)) || (soc < CANCEL_ADJ_COULOMB_SOC_L_2) || ((pwr->temp <= FORCE_ADJ_COULOMB_TEMP_H) && (pwr->temp >= FORCE_ADJ_COULOMB_TEMP_L))) { bcap = pwr->designed_cap * soc / 1000; /* Stop Coulomb Counter */ bd7181x_clear_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_1, 0); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_3, ((bcap + pwr->designed_cap / 200) & 0x1FFFUL) + diff_coulomb_cnt); pwr->coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "Adjust Coulomb Counter at Relaxed State\n"); dev_dbg(pwr->dev, "CC_CCNTD = %d\n", pwr->coulomb_cnt); /* Start Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); /* If the following commented out code is enabled, the SOC is not clamped at the relax time. */ /* Reset SOCs */ /* bd7181x_calc_soc_org(pwr); */ /* pwr->soc_norm = pwr->soc_org; */ /* pwr->soc = pwr->soc_norm; */ /* pwr->clamp_soc = pwr->soc; */ } } return 0; } /** @brief reset coulomb counter values at full charged state * @param pwr power device * @return 0 */ static int bd7181x_reset_coulomb_count(struct bd7181x_power* pwr) { u32 full_charged_coulomb_cnt; full_charged_coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_FULL_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "%s(): full_charged_coulomb_cnt=0x%x\n", __func__, full_charged_coulomb_cnt); if (full_charged_coulomb_cnt != 0) { int diff_coulomb_cnt; /* Clear Full Charged Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_FULL_CTRL, FULL_CLR); diff_coulomb_cnt = full_charged_coulomb_cnt - (bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL); diff_coulomb_cnt = diff_coulomb_cnt >> 16; if (diff_coulomb_cnt > 0) { diff_coulomb_cnt = 0; } dev_dbg(pwr->dev, "diff_coulomb_cnt = %d\n", diff_coulomb_cnt); /* Stop Coulomb Counter */ bd7181x_clear_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_1, 0); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_3, ((pwr->designed_cap + pwr->designed_cap / 200) & 0x1FFFUL) + diff_coulomb_cnt); pwr->coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "Reset Coulomb Counter at POWER_SUPPLY_STATUS_FULL\n"); dev_dbg(pwr->dev, "CC_CCNTD = %d\n", pwr->coulomb_cnt); /* Start Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); } return 0; } /** @brief get battery parameters, such as voltages, currents, temperatures. * @param pwr power device * @return 0 */ static int bd7181x_get_voltage_current(struct bd7181x_power* pwr) { /* Read detailed vcell and current */ bd7181x_get_vbat_curr(pwr, &pwr->vcell, &pwr->curr_sar); dev_dbg(pwr->dev, "VM_VBAT = %d\n", pwr->vcell); dev_dbg(pwr->dev, "VM_IBAT = %d\n", pwr->curr_sar); pwr->curr = bd7181x_get_current_ds_adc(pwr); dev_dbg(pwr->dev, "CC_CURCD = %d\n", pwr->curr); /* Read detailed vsys */ bd7181x_get_vsys(pwr, &pwr->vsys); dev_dbg(pwr->dev, "VM_VSYS = %d\n", pwr->vsys); /* Read detailed vbat_min */ bd7181x_get_vbat_min(pwr, &pwr->vcell_min); dev_dbg(pwr->dev, "VM_VBAT_MIN = %d\n", pwr->vcell_min); /* Read detailed vsys_min */ bd7181x_get_vsys_min(pwr, &pwr->vsys_min); dev_dbg(pwr->dev, "VM_VSYS_MIN = %d\n", pwr->vsys_min); /* Get tempature */ pwr->temp = bd7181x_get_temp(pwr); // dev_dbg(pwr->dev, "Temperature %d degrees C\n", pwr->temp); return 0; } /** @brief adjust coulomb counter values at relaxed state by SW * @param pwr power device * @return 0 */ static int bd7181x_adjust_coulomb_count_sw(struct bd7181x_power* pwr) { int tmp_curr_mA; tmp_curr_mA = pwr->curr / 1000; if ((tmp_curr_mA * tmp_curr_mA) <= (THR_RELAX_CURRENT * THR_RELAX_CURRENT)) { /* No load */ pwr->relax_time += (JITTER_DEFAULT / 1000); } else { pwr->relax_time = 0; } dev_dbg(pwr->dev, "%s(): pwr->relax_time = 0x%x\n", __func__, pwr->relax_time); if (pwr->relax_time >= THR_RELAX_TIME) { /* Battery is relaxed. */ u32 bcap; int soc, ocv; pwr->relax_time = 0; /* Get OCV */ ocv = pwr->vcell; /* Get soc at relaxed state from ocv/soc table */ soc = bd7181x_voltage_to_capacity(ocv); dev_dbg(pwr->dev, "soc %d[0.1%%]\n", soc); if (soc < 0) soc = 0; if ((soc > CANCEL_ADJ_COULOMB_SOC_H_1) || ((soc < CANCEL_ADJ_COULOMB_SOC_L_1) && (soc > CANCEL_ADJ_COULOMB_SOC_H_2)) || (soc < CANCEL_ADJ_COULOMB_SOC_L_2) || ((pwr->temp <= FORCE_ADJ_COULOMB_TEMP_H) && (pwr->temp >= FORCE_ADJ_COULOMB_TEMP_L))) { bcap = pwr->designed_cap * soc / 1000; /* Stop Coulomb Counter */ bd7181x_clear_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_1, 0); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_3, ((bcap + pwr->designed_cap / 200) & 0x1FFFUL)); pwr->coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "Adjust Coulomb Counter by SW at Relaxed State\n"); dev_dbg(pwr->dev, "CC_CCNTD = %d\n", pwr->coulomb_cnt); /* Start Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); /* If the following commented out code is enabled, the SOC is not clamped at the relax time. */ /* Reset SOCs */ /* bd7181x_calc_soc_org(pwr); */ /* pwr->soc_norm = pwr->soc_org; */ /* pwr->soc = pwr->soc_norm; */ /* pwr->clamp_soc = pwr->soc; */ } } return 0; } /** @brief get coulomb counter values * @param pwr power device * @return 0 */ static int bd7181x_coulomb_count(struct bd7181x_power* pwr) { dev_dbg(pwr->dev, "%s(): pwr->state_machine = 0x%x\n", __func__, pwr->state_machine); if (pwr->state_machine == STAT_POWER_ON) { pwr->state_machine = STAT_INITIALIZED; /* Start Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); } else if (pwr->state_machine == STAT_INITIALIZED) { pwr->coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; // dev_dbg(pwr->dev, "CC_CCNTD = %d\n", pwr->coulomb_cnt); } return 0; } /** @brief calc cycle * @param pwr power device * @return 0 */ static int bd7181x_update_cycle(struct bd7181x_power* pwr) { int charged_coulomb_cnt; charged_coulomb_cnt = bd7181x_reg_read16(pwr->mfd, BD7181X_REG_CCNTD_CHG_3); dev_dbg(pwr->dev, "%s(): charged_coulomb_cnt = 0x%x\n", __func__, charged_coulomb_cnt); if (charged_coulomb_cnt >= pwr->designed_cap) { pwr->cycle++; dev_dbg(pwr->dev, "Update cycle = %d\n", pwr->cycle); battery_cycle = pwr->cycle; charged_coulomb_cnt -= pwr->designed_cap; /* Stop Coulomb Counter */ bd7181x_clear_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CCNTD_CHG_3, charged_coulomb_cnt); /* Start Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); } return 0; } /** @brief calc full capacity value by Cycle and Temperature * @param pwr power device * @return 0 */ static int bd7181x_calc_full_cap(struct bd7181x_power* pwr) { u32 designed_cap_uAh; u32 full_cap_uAh; /* Calculate full capacity by cycle */ designed_cap_uAh = A10s_mAh(pwr->designed_cap) * 1000; full_cap_uAh = designed_cap_uAh - BD7181X_DGRD_CYC_CAP * pwr->cycle; pwr->full_cap = mAh_A10s(full_cap_uAh / 1000); dev_dbg(pwr->dev, "Calculate full capacity by cycle\n"); dev_dbg(pwr->dev, "%s() pwr->full_cap = %d\n", __func__, pwr->full_cap); /* Calculate full capacity by temperature */ dev_dbg(pwr->dev, "Temperature = %d\n", pwr->temp); if (pwr->temp >= BD7181X_DGRD_TEMP_M) { full_cap_uAh += (pwr->temp - BD7181X_DGRD_TEMP_M) * BD7181X_DGRD_TEMP_CAP_H; pwr->full_cap = mAh_A10s(full_cap_uAh / 1000); } else if (pwr->temp >= BD7181X_DGRD_TEMP_L) { full_cap_uAh += (pwr->temp - BD7181X_DGRD_TEMP_M) * BD7181X_DGRD_TEMP_CAP_M; pwr->full_cap = mAh_A10s(full_cap_uAh / 1000); } else { full_cap_uAh += (BD7181X_DGRD_TEMP_L - BD7181X_DGRD_TEMP_M) * BD7181X_DGRD_TEMP_CAP_M; full_cap_uAh += (pwr->temp - BD7181X_DGRD_TEMP_L) * BD7181X_DGRD_TEMP_CAP_L; pwr->full_cap = mAh_A10s(full_cap_uAh / 1000); } dev_dbg(pwr->dev, "Calculate full capacity by cycle and temperature\n"); dev_dbg(pwr->dev, "%s() pwr->full_cap = %d\n", __func__, pwr->full_cap); return 0; } /** @brief calculate SOC values by designed capacity * @param pwr power device * @return 0 */ static int bd7181x_calc_soc_org(struct bd7181x_power* pwr) { pwr->soc_org = (pwr->coulomb_cnt >> 16) * 100 / pwr->designed_cap; if (pwr->soc_org > 100) { pwr->soc_org = 100; /* Stop Coulomb Counter */ bd7181x_clear_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_1, 0); bd7181x_reg_write16(pwr->mfd, BD7181X_REG_CC_CCNTD_3, ((pwr->designed_cap + pwr->designed_cap / 200) & 0x1FFFUL)); pwr->coulomb_cnt = bd7181x_reg_read32(pwr->mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; dev_dbg(pwr->dev, "Limit Coulomb Counter\n"); dev_dbg(pwr->dev, "CC_CCNTD = %d\n", pwr->coulomb_cnt); /* Start Coulomb Counter */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_CC_CTRL, CCNTENB); } dev_dbg(pwr->dev, "%s(): pwr->soc_org = %d\n", __func__, pwr->soc_org); return 0; } /** @brief calculate SOC values by full capacity * @param pwr power device * @return 0 */ static int bd7181x_calc_soc_norm(struct bd7181x_power* pwr) { int lost_cap; int mod_coulomb_cnt; lost_cap = pwr->designed_cap - pwr->full_cap; dev_dbg(pwr->dev, "%s() lost_cap = %d\n", __func__, lost_cap); mod_coulomb_cnt = (pwr->coulomb_cnt >> 16) - lost_cap; if ((mod_coulomb_cnt > 0) && (pwr->full_cap > 0)) { pwr->soc_norm = mod_coulomb_cnt * 100 / pwr->full_cap; } else { pwr->soc_norm = 0; } if (pwr->soc_norm > 100) { pwr->soc_norm = 100; } dev_dbg(pwr->dev, "%s() pwr->soc_norm = %d\n", __func__, pwr->soc_norm); return 0; } /** @brief get OCV value by SOC * @param pwr power device * @return 0 */ int bd7181x_get_ocv(struct bd7181x_power* pwr, int dsoc) { int i = 0; int ocv = 0; if (dsoc > soc_table[0]) { ocv = MAX_VOLTAGE; } else if (dsoc == 0) { ocv = ocv_table[21]; } else { i = 0; while (i < 22) { if ((dsoc <= soc_table[i]) && (dsoc > soc_table[i+1])) { ocv = (ocv_table[i] - ocv_table[i+1]) * (dsoc - soc_table[i+1]) / (soc_table[i] - soc_table[i+1]) + ocv_table[i+1]; break; } i++; } if (i == 22) ocv = ocv_table[22]; } dev_dbg(pwr->dev, "%s() ocv = %d\n", __func__, ocv); return ocv; } /** @brief calculate SOC value by full_capacity and load * @param pwr power device * @return OCV */ static int bd7181x_calc_soc(struct bd7181x_power* pwr) { int ocv_table_load[23]; pwr->soc = pwr->soc_norm; switch (pwr->rpt_status) { /* Adjust for 0% between THR_VOLTAGE and MIN_VOLTAGE */ case POWER_SUPPLY_STATUS_DISCHARGING: case POWER_SUPPLY_STATUS_NOT_CHARGING: if (pwr->vsys_min <= THR_VOLTAGE) { int i; int ocv; int lost_cap; int mod_coulomb_cnt; int dsoc; lost_cap = pwr->designed_cap - pwr->full_cap; mod_coulomb_cnt = (pwr->coulomb_cnt >> 16) - lost_cap; dsoc = mod_coulomb_cnt * 1000 / pwr->full_cap; dev_dbg(pwr->dev, "%s() dsoc = %d\n", __func__, dsoc); ocv = bd7181x_get_ocv(pwr, dsoc); for (i = 1; i < 23; i++) { ocv_table_load[i] = ocv_table[i] - (ocv - pwr->vsys_min); if (ocv_table_load[i] <= MIN_VOLTAGE) { dev_dbg(pwr->dev, "%s() ocv_table_load[%d] = %d\n", __func__, i, ocv_table_load[i]); break; } } if (i < 23) { int j; int dv = (ocv_table_load[i-1] - ocv_table_load[i]) / 5; int lost_cap2; int mod_coulomb_cnt2, mod_full_cap; for (j = 1; j < 5; j++){ if ((ocv_table_load[i] + dv * j) > MIN_VOLTAGE) { break; } } lost_cap2 = ((21 - i) * 5 + (j - 1)) * pwr->full_cap / 100; dev_dbg(pwr->dev, "%s() lost_cap2 = %d\n", __func__, lost_cap2); mod_coulomb_cnt2 = mod_coulomb_cnt - lost_cap2; mod_full_cap = pwr->full_cap - lost_cap2; if ((mod_coulomb_cnt2 > 0) && (mod_full_cap > 0)) { pwr->soc = mod_coulomb_cnt2 * 100 / mod_full_cap; } else { pwr->soc = 0; } dev_dbg(pwr->dev, "%s() pwr->soc(by load) = %d\n", __func__, pwr->soc); } } break; default: break; } switch (pwr->rpt_status) {/* Adjust for 0% and 100% */ case POWER_SUPPLY_STATUS_DISCHARGING: case POWER_SUPPLY_STATUS_NOT_CHARGING: if (pwr->vsys_min <= MIN_VOLTAGE) { pwr->soc = 0; } else { if (pwr->soc == 0) { pwr->soc = 1; } } break; case POWER_SUPPLY_STATUS_CHARGING: if (pwr->soc == 100) { pwr->soc = 99; } break; default: break; } dev_dbg(pwr->dev, "%s() pwr->soc = %d\n", __func__, pwr->soc); return 0; } /** @brief calculate Clamped SOC value by full_capacity and load * @param pwr power device * @return OCV */ static int bd7181x_calc_soc_clamp(struct bd7181x_power* pwr) { switch (pwr->rpt_status) {/* Adjust for 0% and 100% */ case POWER_SUPPLY_STATUS_DISCHARGING: case POWER_SUPPLY_STATUS_NOT_CHARGING: if (pwr->soc <= pwr->clamp_soc) { pwr->clamp_soc = pwr->soc; } break; default: pwr->clamp_soc = pwr->soc; break; } dev_dbg(pwr->dev, "%s() pwr->clamp_soc = %d\n", __func__, pwr->clamp_soc); return 0; } /** @brief get battery and DC online status * @param pwr power device * @return 0 */ static int bd7181x_get_online(struct bd7181x_power* pwr) { int r; #if 0 #define TS_THRESHOLD_VOLT 0xD9 r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_VM_VTH); pwr->bat_online = (r > TS_THRESHOLD_VOLT); #endif #if 0 r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_BAT_STAT); if (r >= 0 && (r & BAT_DET_DONE)) { pwr->bat_online = (r & BAT_DET) != 0; } #endif #if 1 #define BAT_OPEN 0x7 r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_BAT_TEMP); pwr->bat_online = (r != BAT_OPEN); #endif r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_DCIN_STAT); if (r >= 0) { pwr->charger_online = (r & VBUS_DET) != 0; } dev_dbg(pwr->dev, "%s(): pwr->bat_online = %d, pwr->charger_online = %d\n", __func__, pwr->bat_online, pwr->charger_online); return 0; } /** @brief init bd7181x sub module charger * @param pwr power device * @return 0 */ static int bd7181x_init_hardware(struct bd7181x_power *pwr) { struct bd7181x *mfd = pwr->mfd; int r; r = bd7181x_reg_write(mfd, BD7181X_REG_DCIN_CLPS, 0x36); #define XSTB 0x02 r = bd7181x_reg_read(mfd, BD7181X_REG_CONF); #if 0 for (i = 0; i < 300; i++) { r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_BAT_STAT); if (r >= 0 && (r & BAT_DET_DONE)) { break; } msleep(5); } #endif if ((r & XSTB) == 0x00) { //if (r & BAT_DET) { /* Init HW, when the battery is inserted. */ bd7181x_reg_write(mfd, BD7181X_REG_CONF, r | XSTB); #define TEST_SEQ_00 0x00 #define TEST_SEQ_01 0x76 #define TEST_SEQ_02 0x66 #define TEST_SEQ_03 0x56 #if 0 bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_01); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_02); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_03); bd7181x_reg_write16(pwr->mfd, 0xA2, CALIB_CURRENT_A2A3); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_00); #endif /* Stop Coulomb Counter */ bd7181x_clear_bits(mfd, BD7181X_REG_CC_CTRL, CCNTENB); /* Set Coulomb Counter Reset bit*/ bd7181x_set_bits(mfd, BD7181X_REG_CC_CTRL, CCNTRST); /* Clear Coulomb Counter Reset bit*/ bd7181x_clear_bits(mfd, BD7181X_REG_CC_CTRL, CCNTRST); /* Set default Battery Capacity */ pwr->designed_cap = BD7181X_BATTERY_CAP; pwr->full_cap = BD7181X_BATTERY_CAP; /* Set initial Coulomb Counter by HW OCV */ calibration_coulomb_counter(pwr); /* WDT_FST auto set */ bd7181x_set_bits(mfd, BD7181X_REG_CHG_SET1, WDT_AUTO); /* VBAT Low voltage detection Setting, added by John Zhang*/ bd7181x_reg_write16(mfd, BD7181X_REG_ALM_VBAT_TH_U, VBAT_LOW_TH); /* Mask Relax decision by PMU STATE */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_REX_CTRL_1, REX_PMU_STATE_MASK); /* Set Battery Capacity Monitor threshold1 as 90% */ bd7181x_reg_write16(mfd, BD7181X_REG_CC_BATCAP1_TH_U, (BD7181X_BATTERY_CAP * 9 / 10)); dev_dbg(pwr->dev, "BD7181X_REG_CC_BATCAP1_TH = %d\n", (BD7181X_BATTERY_CAP * 9 / 10)); /* Enable LED ON when charging */ bd7181x_set_bits(pwr->mfd, BD7181X_REG_LED_CTRL, CHGDONE_LED_EN); pwr->state_machine = STAT_POWER_ON; } else { pwr->designed_cap = BD7181X_BATTERY_CAP; pwr->full_cap = BD7181X_BATTERY_CAP; // bd7181x_reg_read16(pwr->mfd, BD7181X_REG_CC_BATCAP_U); pwr->state_machine = STAT_INITIALIZED; // STAT_INITIALIZED } pwr->temp = bd7181x_get_temp(pwr); dev_dbg(pwr->dev, "Temperature = %d\n", pwr->temp); bd7181x_adjust_coulomb_count(pwr); bd7181x_reset_coulomb_count(pwr); pwr->coulomb_cnt = bd7181x_reg_read32(mfd, BD7181X_REG_CC_CCNTD_3) & 0x1FFFFFFFUL; bd7181x_calc_soc_org(pwr); pwr->soc_norm = pwr->soc_org; pwr->soc = pwr->soc_norm; pwr->clamp_soc = pwr->soc; dev_dbg(pwr->dev, "%s() CC_CCNTD = %d\n", __func__, pwr->coulomb_cnt); dev_dbg(pwr->dev, "%s() pwr->soc = %d\n", __func__, pwr->soc); dev_dbg(pwr->dev, "%s() pwr->clamp_soc = %d\n", __func__, pwr->clamp_soc); pwr->cycle = battery_cycle; pwr->curr = 0; pwr->curr_sar = 0; pwr->relax_time = 0; return 0; } /**@brief timed work function called by system * read battery capacity, * sense change of charge status, etc. * @param work work struct * @return void */ static void bd_work_callback(struct work_struct *work) { struct bd7181x_power *pwr; struct delayed_work *delayed_work; int status, changed = 0; static int cap_counter = 0; delayed_work = container_of(work, struct delayed_work, work); pwr = container_of(delayed_work, struct bd7181x_power, bd_work); dev_dbg(pwr->dev, "%s(): in\n", __func__); status = bd7181x_reg_read(pwr->mfd, BD7181X_REG_DCIN_STAT); if (status != pwr->vbus_status) { //printk("DCIN_STAT CHANGED from 0x%X to 0x%X\n", pwr->vbus_status, status); pwr->vbus_status = status; changed = 1; } status = bd7181x_reg_read(pwr->mfd, BD7181X_REG_BAT_STAT); status &= ~BAT_DET_DONE; if (status != pwr->bat_status) { dev_dbg(pwr->dev, "BAT_STAT CHANGED from 0x%X to 0x%X\n", pwr->bat_status, status); pwr->bat_status = status; changed = 1; } status = bd7181x_reg_read(pwr->mfd, BD7181X_REG_CHG_STATE); if (status != pwr->charge_status) { dev_dbg(pwr->dev, "CHG_STATE CHANGED from 0x%X to 0x%X\n", pwr->charge_status, status); pwr->charge_status = status; //changed = 1; } bd7181x_get_voltage_current(pwr); bd7181x_adjust_coulomb_count(pwr); bd7181x_reset_coulomb_count(pwr); bd7181x_adjust_coulomb_count_sw(pwr); bd7181x_coulomb_count(pwr); bd7181x_update_cycle(pwr); bd7181x_calc_full_cap(pwr); bd7181x_calc_soc_org(pwr); bd7181x_calc_soc_norm(pwr); bd7181x_calc_soc(pwr); bd7181x_calc_soc_clamp(pwr); bd7181x_get_online(pwr); bd7181x_charge_status(pwr); if (changed || cap_counter++ > JITTER_REPORT_CAP / JITTER_DEFAULT) { power_supply_changed(pwr->ac); power_supply_changed(pwr->bat); cap_counter = 0; } if (pwr->calib_current == CALIB_NORM) { schedule_delayed_work(&pwr->bd_work, msecs_to_jiffies(JITTER_DEFAULT)); } else if (pwr->calib_current == CALIB_START) { pwr->calib_current = CALIB_GO; } } /**@brief bd7181x power interrupt * @param irq system irq * @param pwrsys bd7181x power device of system * @retval IRQ_HANDLED success * @retval IRQ_NONE error */ static irqreturn_t bd7181x_power_interrupt(int irq, void *pwrsys) { struct device *dev = pwrsys; struct bd7181x *mfd = dev_get_drvdata(dev->parent); // struct bd7181x_power *pwr = dev_get_drvdata(dev); int reg, r; dev_info(mfd->dev, "bd7181x_power_interrupt() in.\n"); reg = bd7181x_reg_read(mfd, BD7181X_REG_INT_STAT_03); if (reg < 0) return IRQ_NONE; dev_info(mfd->dev, "INT_STAT_03 = 0x%.2X\n", reg); if(reg & POWERON_PRESS) { kobject_uevent(&(mfd->dev->kobj), KOBJ_ONLINE); dev_info(mfd->dev, "POWERON_PRESS\n"); } if(reg & POWERON_SHORT) { kobject_uevent(&(mfd->dev->kobj), KOBJ_OFFLINE); dev_info(mfd->dev, "POWERON_SHORT\n"); } if(reg & POWERON_MID) { kobject_uevent(&(mfd->dev->kobj), KOBJ_OFFLINE); dev_info(mfd->dev, "POWERON_MID\n"); } if(reg & POWERON_LONG) { kobject_uevent(&(mfd->dev->kobj), KOBJ_OFFLINE); dev_info(mfd->dev, "POWERON_LONG\n"); } r = bd7181x_reg_write(mfd, BD7181X_REG_INT_STAT_03, reg); if (r) return IRQ_NONE; if (reg & DCIN_MON_DET) { dev_info(mfd->dev, "\n~~~DCIN removed\n"); } else if (reg & DCIN_MON_RES) { dev_info(mfd->dev, "\n~~~DCIN inserted\n"); } return IRQ_HANDLED; } /**@brief bd7181x vbat low voltage detection interrupt * @param irq system irq * @param pwrsys bd7181x power device of system * @retval IRQ_HANDLED success * @retval IRQ_NONE error * added by John Zhang at 2015-07-22 */ static irqreturn_t bd7181x_vbat_interrupt(int irq, void *pwrsys) { struct device *dev = pwrsys; struct bd7181x *mfd = dev_get_drvdata(dev->parent); // struct bd7181x_power *pwr = dev_get_drvdata(dev); int reg, r; dev_info(mfd->dev, "bd7181x_vbat_interrupt() in.\n"); reg = bd7181x_reg_read(mfd, BD7181X_REG_INT_STAT_08); if (reg < 0) return IRQ_NONE; dev_info(mfd->dev, "INT_STAT_08 = 0x%.2X\n", reg); r = bd7181x_reg_write(mfd, BD7181X_REG_INT_STAT_08, reg); if (r) return IRQ_NONE; if (reg & VBAT_MON_DET) { dev_info(mfd->dev, "\n~~~ VBAT LOW Detected ... \n"); } else if (reg & VBAT_MON_RES) { dev_info(mfd->dev, "\n~~~ VBAT LOW Resumed ... \n"); } return IRQ_HANDLED; } /**@brief bd7181x int_stat_11 detection interrupt * @param irq system irq * @param pwrsys bd7181x power device of system * @retval IRQ_HANDLED success * @retval IRQ_NONE error * added 2015-12-26 */ static irqreturn_t bd7181x_int_11_interrupt(int irq, void *pwrsys) { struct device *dev = pwrsys; struct bd7181x *mfd = dev_get_drvdata(dev->parent); // struct bd7181x_power *pwr = dev_get_drvdata(dev); int reg, r; dev_info(mfd->dev, "bd7181x_int_11_interrupt() in.\n"); reg = bd7181x_reg_read(mfd, BD7181X_REG_INT_STAT_11); if (reg < 0) return IRQ_NONE; dev_info(mfd->dev, "INT_STAT_11 = 0x%.2X\n", reg); r = bd7181x_reg_write(mfd, BD7181X_REG_INT_STAT_11, reg); if (r) { return IRQ_NONE; } if (reg & INT_STAT_11_VF_DET) { dev_info(mfd->dev, "\n~~~ VF Detected ... \n"); } else if (reg & INT_STAT_11_VF_RES) { dev_info(mfd->dev, "\n~~~ VF Resumed ... \n"); } else if (reg & INT_STAT_11_VF125_DET) { dev_info(mfd->dev, "\n~~~ VF125 Detected ... \n"); } else if (reg & INT_STAT_11_VF125_RES) { dev_info(mfd->dev, "\n~~~ VF125 Resumed ... \n"); } else if (reg & INT_STAT_11_OVTMP_DET) { dev_info(mfd->dev, "\n~~~ Overtemp Detected ... \n"); } else if (reg & INT_STAT_11_OVTMP_RES) { dev_info(mfd->dev, "\n~~~ Overtemp Detected ... \n"); } else if (reg & INT_STAT_11_LOTMP_DET) { dev_info(mfd->dev, "\n~~~ Lowtemp Detected ... \n"); } else if (reg & INT_STAT_11_LOTMP_RES) { dev_info(mfd->dev, "\n~~~ Lowtemp Detected ... \n"); } return IRQ_HANDLED; } /** @brief get property of power supply ac * @param psy power supply deivce * @param psp property to get * @param val property value to return * @retval 0 success * @retval negative fail */ static int bd7181x_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct bd7181x_power *pwr = dev_get_drvdata(psy->dev.parent); u32 vot; switch (psp) { case POWER_SUPPLY_PROP_ONLINE: val->intval = pwr->charger_online; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: vot = bd7181x_reg_read16(pwr->mfd, BD7181X_REG_VM_DCIN_U); val->intval = 5000 * vot; // 5 milli volt steps break; default: return -EINVAL; } return 0; } /** @brief get property of power supply bat * @param psy power supply deivce * @param psp property to get * @param val property value to return * @retval 0 success * @retval negative fail */ static int bd7181x_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct bd7181x_power *pwr = dev_get_drvdata(psy->dev.parent); // u32 cap, vot, r; // u8 ret; switch (psp) { /* case POWER_SUPPLY_PROP_STATUS: r = bd7181x_reg_read(pwr->mfd, BD7181X_REG_CHG_STATE); // printk("CHG_STATE = 0x%.2X\n", r); switch(r) { case CHG_STATE_SUSPEND: val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case CHG_STATE_TRICKLE_CHARGE: case CHG_STATE_PRE_CHARGE: case CHG_STATE_FAST_CHARGE: val->intval = POWER_SUPPLY_STATUS_CHARGING; break; case CHG_STATE_TOP_OFF: case CHG_STATE_DONE: val->intval = POWER_SUPPLY_STATUS_FULL; break; default: val->intval = POWER_SUPPLY_STATUS_UNKNOWN; } break; case POWER_SUPPLY_PROP_PRESENT: break; case POWER_SUPPLY_PROP_HEALTH: ret = bd7181x_reg_read(pwr->mfd, BD7181X_REG_BAT_STAT); if (ret & DBAT_DET) val->intval = POWER_SUPPLY_HEALTH_DEAD; else if (ret & VBAT_OV) val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; else val->intval = POWER_SUPPLY_HEALTH_GOOD; break; case POWER_SUPPLY_PROP_CAPACITY: cap = bd7181x_reg_read16(pwr->mfd, BD7181X_REG_CC_BATCAP_U); // printk("CC_BATCAP = 0x%.4X\n", cap); val->intval = cap * 100 / 0x1FFF; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: vot = bd7181x_reg_read16(pwr->mfd, BD7181X_REG_VM_VBAT_U) * 1000; val->intval = vot; break; */ case POWER_SUPPLY_PROP_STATUS: val->intval = pwr->rpt_status; break; case POWER_SUPPLY_PROP_HEALTH: val->intval = pwr->bat_health; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: if (pwr->rpt_status == POWER_SUPPLY_STATUS_CHARGING) val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; else val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; break; case POWER_SUPPLY_PROP_ONLINE: val->intval = pwr->bat_online; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = pwr->vcell; break; case POWER_SUPPLY_PROP_CAPACITY: val->intval = pwr->clamp_soc; break; case POWER_SUPPLY_PROP_CHARGE_NOW: { u32 t; t = pwr->coulomb_cnt >> 16; t = A10s_mAh(t); if (t > A10s_mAh(pwr->designed_cap)) t = A10s_mAh(pwr->designed_cap); val->intval = t * 1000; /* uA to report */ } break; case POWER_SUPPLY_PROP_PRESENT: val->intval = pwr->bat_online; break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = POWER_SUPPLY_TECHNOLOGY_LION; break; case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: val->intval = BD7181X_BATTERY_FULL * A10s_mAh(pwr->designed_cap) * 10; break; case POWER_SUPPLY_PROP_CHARGE_FULL: val->intval = BD7181X_BATTERY_FULL * A10s_mAh(pwr->full_cap) * 10; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = pwr->curr_sar; break; case POWER_SUPPLY_PROP_CURRENT_AVG: val->intval = pwr->curr; break; case POWER_SUPPLY_PROP_TEMP: val->intval = pwr->temp * 10; /* 0.1 degrees C unit */ break; case POWER_SUPPLY_PROP_VOLTAGE_MAX: val->intval = MAX_VOLTAGE; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN: val->intval = MIN_VOLTAGE; break; case POWER_SUPPLY_PROP_CURRENT_MAX: val->intval = MAX_CURRENT; break; default: return -EINVAL; } return 0; } /** @brief ac properties */ static enum power_supply_property bd7181x_charger_props[] = { POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, }; /** @brief bat properies */ static enum power_supply_property bd7181x_battery_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_CHARGE_NOW, POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, POWER_SUPPLY_PROP_CHARGE_FULL, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CURRENT_AVG, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_MIN, POWER_SUPPLY_PROP_CURRENT_MAX, }; /** @brief directly set raw value to chip register, format: 'register value' */ static ssize_t bd7181x_sysfs_set_registers(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bd7181x_power *pwr = container_of(&psy, struct bd7181x_power, bat); ssize_t ret = 0; unsigned int reg; unsigned int val; ret = sscanf(buf, "%x %x", ®, &val); if (ret < 1) { pwr->reg_index = -1; dev_err(pwr->dev, "registers set: \n"); return count; } if (ret == 1 && reg <= BD7181X_MAX_REGISTER) { pwr->reg_index = reg; dev_info(pwr->dev, "registers set: reg=0x%x\n", reg); return count; } if (reg > BD7181X_MAX_REGISTER || val > 255) return -EINVAL; dev_info(pwr->dev, "registers set: reg=0x%x, val=0x%x\n", reg, val); ret = bd7181x_reg_write(pwr->mfd, reg, val); if (ret < 0) return ret; return count; } /** @brief print value of chip register, format: 'register=value' */ static ssize_t bd7181x_sysfs_print_reg(struct bd7181x_power *pwr, u8 reg, char *buf) { int ret = bd7181x_reg_read(pwr->mfd, reg); if (ret < 0) return sprintf(buf, "%#.2x=error %d\n", reg, ret); return sprintf(buf, "[0x%.2X] = %.2X\n", reg, ret); } /** @brief show all raw values of chip register, format per line: 'register=value' */ static ssize_t bd7181x_sysfs_show_registers(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = dev_get_drvdata(dev); struct bd7181x_power *pwr = container_of(&psy, struct bd7181x_power, bat); ssize_t ret = 0; int i; dev_info(pwr->dev, "register: index[%d]\n", pwr->reg_index); if (pwr->reg_index >= 0) { ret += bd7181x_sysfs_print_reg(pwr, pwr->reg_index, buf + ret); } else { for (i = 0; i <= BD7181X_MAX_REGISTER; i++) { ret += bd7181x_sysfs_print_reg(pwr, i, buf + ret); } } return ret; } static DEVICE_ATTR(registers, S_IWUSR | S_IRUGO, bd7181x_sysfs_show_registers, bd7181x_sysfs_set_registers); static int first_offset(struct bd7181x_power *pwr) { unsigned char ra2, ra3, ra6, ra7; unsigned char ra2_temp; struct bd7181x *mfd = pwr->mfd; bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_01); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_02); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_03); ra2 = bd7181x_reg_read(mfd, 0xA2); // I want to know initial A2 & A3. ra3 = bd7181x_reg_read(mfd, 0xA3); // I want to know initial A2 & A3. ra6 = bd7181x_reg_read(mfd, 0xA6); ra7 = bd7181x_reg_read(mfd, 0xA7); bd7181x_reg_write(mfd, 0xA2, 0x00); bd7181x_reg_write(mfd, 0xA3, 0x00); dev_info(pwr->dev, "TEST[A2] = 0x%.2X\n", ra2); dev_info(pwr->dev, "TEST[A3] = 0x%.2X\n", ra3); dev_info(pwr->dev, "TEST[A6] = 0x%.2X\n", ra6); dev_info(pwr->dev, "TEST[A7] = 0x%.2X\n", ra7); //-------------- First Step ------------------- dev_info(pwr->dev, "Frist Step begginning \n"); // delay some time , Make a state of IBAT=0mA // mdelay(1000 * 10); ra2_temp = ra2; if (ra7 != 0) { //if 0<0xA7<20 decrease the Test register 0xA2[7:3] until 0xA7 becomes 0x00. if ((ra7 > 0) && (ra7 < 20)) { do { ra2 = bd7181x_reg_read(mfd, 0xA2); ra2_temp = ra2 >> 3; ra2_temp -= 1; ra2_temp <<= 3; bd7181x_reg_write(mfd, 0xA2, ra2_temp); dev_info(pwr->dev, "TEST[A2] = 0x%.2X\n", ra2_temp); ra7 = bd7181x_reg_read(mfd, 0xA7); dev_info(pwr->dev, "TEST[A7] = 0x%.2X\n", ra7); mdelay(1000); // 1sec? } while (ra7); dev_info(pwr->dev, "A7 becomes 0 . \n"); } // end if((ra7 > 0)&&(ra7 < 20)) else if ((ra7 > 0xDF) && (ra7 < 0xFF)) //if DF<0xA7> 3; ra2_temp += 1; ra2_temp <<= 3; bd7181x_reg_write(mfd, 0xA2, ra2_temp); dev_info(pwr->dev, "TEST[A2] = 0x%.2X\n", ra2_temp); ra7 = bd7181x_reg_read(mfd, 0xA7); dev_info(pwr->dev, "TEST[A7] = 0x%.2X\n", ra7); mdelay(1000); // 1sec? } while (ra7); dev_info(pwr->dev, "A7 becomes 0 . \n"); } } // please use "ra2_temp" at step2. return ra2_temp; } static int second_step(struct bd7181x_power *pwr, u8 ra2_temp) { u16 ra6, ra7; u8 aft_ra2, aft_ra3; u8 r79, r7a; unsigned int LNRDSA_FUSE; long ADC_SIGN; long DSADGAIN1_INI; struct bd7181x *mfd = pwr->mfd; //-------------- Second Step ------------------- dev_info(pwr->dev, "Second Step begginning \n"); // need to change boad setting ( input 1A tio 10mohm) // delay some time , Make a state of IBAT=1000mA // mdelay(1000 * 10); // rough adjust dev_info(pwr->dev, "ra2_temp = 0x%.2X\n", ra2_temp); ra6 = bd7181x_reg_read(mfd, 0xA6); ra7 = bd7181x_reg_read(mfd, 0xA7); ra6 <<= 8; ra6 |= ra7; // [0xA6 0xA7] dev_info(pwr->dev, "TEST[A6,A7] = 0x%.4X\n", ra6); bd7181x_reg_write(mfd, 0xA2, ra2_temp); // this value from step1 bd7181x_reg_write(mfd, 0xA3, 0x00); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_00); r79 = bd7181x_reg_read(mfd, 0x79); r7a = bd7181x_reg_read(mfd, 0x7A); ADC_SIGN = r79 >> 7; ADC_SIGN = 1 - (2 * ADC_SIGN); DSADGAIN1_INI = r79 << 8; DSADGAIN1_INI = DSADGAIN1_INI + r7a; DSADGAIN1_INI = DSADGAIN1_INI & 0x7FFF; DSADGAIN1_INI = DSADGAIN1_INI * ADC_SIGN; // unit 0.001 // unit 0.000001 DSADGAIN1_INI *= 1000; { if (DSADGAIN1_INI > 1000001) { DSADGAIN1_INI = 2048000000UL - (DSADGAIN1_INI - 1000000) * 8187; } else if (DSADGAIN1_INI < 999999) { DSADGAIN1_INI = -(DSADGAIN1_INI - 1000000) * 8187; } else { DSADGAIN1_INI = 0; } } LNRDSA_FUSE = (int) DSADGAIN1_INI / 1000000; dev_info(pwr->dev, "LNRDSA_FUSE = 0x%.8X\n", LNRDSA_FUSE); aft_ra2 = (LNRDSA_FUSE >> 8) & 255; aft_ra3 = (LNRDSA_FUSE) & 255; aft_ra2 = aft_ra2 + ra2_temp; bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_01); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_02); bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_03); bd7181x_reg_write(mfd, 0xA2, aft_ra2); bd7181x_reg_write(mfd, 0xA3, aft_ra3); return 0; } static int third_step(struct bd7181x_power *pwr, unsigned thr) { u16 ra2_a3, ra6, ra7; u8 ra2, ra3; u8 aft_ra2, aft_ra3; struct bd7181x *mfd = pwr->mfd; // fine adjust ra2 = bd7181x_reg_read(mfd, 0xA2); // ra3 = bd7181x_reg_read(mfd, 0xA3); // ra6 = bd7181x_reg_read(mfd, 0xA6); ra7 = bd7181x_reg_read(mfd, 0xA7); ra6 <<= 8; ra6 |= ra7; // [0xA6 0xA7] dev_info(pwr->dev, "TEST[A6,A7] = 0x%.4X\n", ra6); if (ra6 > thr) { do { ra2_a3 = bd7181x_reg_read(mfd, 0xA2); ra2_a3 <<= 8; ra3 = bd7181x_reg_read(mfd, 0xA3); ra2_a3 |= ra3; //ra2_a3 >>= 3; // ? 0xA3[7:3] , or 0xA3[7:0] ra2_a3 -= 1; //ra2_a3 <<= 3; ra3 = ra2_a3; bd7181x_reg_write(mfd, 0xA3, ra3); ra2_a3 >>= 8; ra2 = ra2_a3; bd7181x_reg_write(mfd, 0xA2, ra2); dev_info(pwr->dev, "TEST[A2] = 0x%.2X , TEST[A3] = 0x%.2X \n", ra2, ra3); mdelay(1000); // 1sec? ra6 = bd7181x_reg_read(mfd, 0xA6); ra7 = bd7181x_reg_read(mfd, 0xA7); ra6 <<= 8; ra6 |= ra7; // [0xA6 0xA7] dev_info(pwr->dev, "TEST[A6,A7] = 0x%.4X\n", ra6); } while (ra6 > thr); } else if (ra6 < thr) { do { ra2_a3 = bd7181x_reg_read(mfd, 0xA2); ra2_a3 <<= 8; ra3 = bd7181x_reg_read(mfd, 0xA3); ra2_a3 |= ra3; //ra2_a3 >>= 3; // ? 0xA3[7:3] , or 0xA3[7:0] ra2_a3 += 1; //ra2_a3 <<= 3; ra3 = ra2_a3; bd7181x_reg_write(mfd, 0xA3, ra3); ra2_a3 >>= 8; ra2 = ra2_a3; bd7181x_reg_write(mfd, 0xA2, ra2); dev_info(pwr->dev, "TEST[A2] = 0x%.2X , TEST[A3] = 0x%.2X \n", ra2, ra3); mdelay(1000); // 1sec? ra6 = bd7181x_reg_read(mfd, 0xA6); ra7 = bd7181x_reg_read(mfd, 0xA7); ra6 <<= 8; ra6 |= ra7; // [0xA6 0xA7] dev_info(pwr->dev, "TEST[A6,A7] = 0x%.4X\n", ra6); } while (ra6 < thr); } dev_info(pwr->dev, "[0xA6 0xA7] becomes [0x%.4X] . \n", thr); dev_info(pwr->dev, " Calibation finished ... \n\n"); aft_ra2 = bd7181x_reg_read(mfd, 0xA2); // aft_ra3 = bd7181x_reg_read(mfd, 0xA3); // I want to know initial A2 & A3. dev_info(pwr->dev, "TEST[A2,A3] = 0x%.2X%.2X\n", aft_ra2, aft_ra3); // bd7181x_reg_write(mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_00); return 0; } static ssize_t bd7181x_sysfs_set_calibrate(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = dev_get_drvdata(dev); struct bd7181x_power *pwr = container_of(&psy, struct bd7181x_power, bat); ssize_t ret = 0; unsigned int val, mA; static u8 rA2; ret = sscanf(buf, "%d %d", &val, &mA); if (ret < 1) { dev_err(pwr->dev, "error: write a integer string"); return count; } if (val == 1) { pwr->calib_current = CALIB_START; while (pwr->calib_current != CALIB_GO) { msleep(500); } rA2 = first_offset(pwr); } if (val == 2) { second_step(pwr, rA2); } if (val == 3) { if (ret <= 1) { dev_err(pwr->dev, "error: Fine adjust need a mA argument!"); } else { unsigned int ra6_thr; ra6_thr = mA * 0xFFFF / 20000; dev_info(pwr->dev, "Fine adjust at %d mA, ra6 threshold %d(0x%X)\n", mA, ra6_thr, ra6_thr); third_step(pwr, ra6_thr); } } if (val == 4) { bd7181x_reg_write(pwr->mfd, BD7181X_REG_TEST_MODE, TEST_SEQ_00); pwr->calib_current = CALIB_NORM; schedule_delayed_work(&pwr->bd_work, msecs_to_jiffies(0)); } return count; } static ssize_t bd7181x_sysfs_show_calibrate(struct device *dev, struct device_attribute *attr, char *buf) { // struct power_supply *psy = dev_get_drvdata(dev); // struct bd7181x_power *pwr = container_of(psy, struct bd7181x_power, bat); ssize_t ret = 0; ret = 0; ret += sprintf(buf + ret, "write string value\n" "\t1 0 mA for step one\n" "\t2 1000 mA for rough adjust\n" "\t3 for fine adjust\n" "\t4 exit current calibration\n"); return ret; } static DEVICE_ATTR(calibrate, S_IWUSR | S_IRUGO, bd7181x_sysfs_show_calibrate, bd7181x_sysfs_set_calibrate); static struct attribute *bd7181x_sysfs_attributes[] = { /* * TODO: some (appropriate) of these attrs should be switched to * use pwr supply class props. */ &dev_attr_registers.attr, &dev_attr_calibrate.attr, NULL, }; static const struct attribute_group bd7181x_sysfs_attr_group = { .attrs = bd7181x_sysfs_attributes, }; /** @brief powers supplied by bd7181x_ac */ static char *bd7181x_ac_supplied_to[] = { BAT_NAME, }; /** @brief probe pwr device * @param pdev platform deivce of bd7181x_power * @retval 0 success * @retval negative fail */ static int __init bd7181x_power_probe(struct platform_device *pdev) { struct bd7181x *bd7181x = dev_get_drvdata(pdev->dev.parent); struct bd7181x_power *pwr; int irq, ret, reg; pwr = kzalloc(sizeof(*pwr), GFP_KERNEL); if (pwr == NULL) return -ENOMEM; pwr->dev = &pdev->dev; pwr->mfd = bd7181x; platform_set_drvdata(pdev, pwr); if (battery_cycle <= 0) { battery_cycle = 0; } dev_err(pwr->dev, "battery_cycle = %d\n", battery_cycle); /* If the product often power up/down and the power down time is long, the Coulomb Counter may have a drift. */ /* If so, it may be better accuracy to enable Coulomb Counter using following commented out code */ /* for counting Coulomb when the product is power up(including sleep). */ /* The condition */ /* (1) Product often power up and down, the power down time is long and there is no power consumed in power down time. */ /* (2) Kernel must call this routin at power up time. */ /* (3) Kernel must call this routin at charging time. */ /* (4) Must use this code with "Stop Coulomb Counter" code in bd7181x_power_remove() function */ /* Start Coulomb Counter */ /* bd7181x_set_bits(pwr->mfd, BD7181x_REG_CC_CTRL, CCNTENB); */ bd7181x_init_hardware(pwr); pwr->bat_desc.name = BAT_NAME; pwr->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY; pwr->bat_desc.properties = bd7181x_battery_props; pwr->bat_desc.num_properties = ARRAY_SIZE(bd7181x_battery_props); pwr->bat_desc.get_property = bd7181x_battery_get_property; pwr->bat = power_supply_register(&pdev->dev, &pwr->bat_desc, &pwr->bat_cfg); if (IS_ERR(pwr->bat)) { dev_err(&pdev->dev, "Failed to register battery, %ld\n", PTR_ERR(pwr->bat)); ret = PTR_ERR(pwr->bat); pwr->bat = NULL; goto fail_register_bat; } pwr->ac_desc.name = AC_NAME; pwr->ac_desc.type = POWER_SUPPLY_TYPE_MAINS; pwr->ac_desc.properties = bd7181x_charger_props; pwr->ac_cfg.supplied_to = bd7181x_ac_supplied_to; pwr->ac_cfg.num_supplicants = ARRAY_SIZE(bd7181x_ac_supplied_to); pwr->ac_desc.num_properties = ARRAY_SIZE(bd7181x_charger_props); pwr->ac_desc.get_property = bd7181x_charger_get_property; pwr->ac = power_supply_register(&pdev->dev, &pwr->ac_desc, &pwr->ac_cfg); if (IS_ERR(pwr->ac)) { dev_err(&pdev->dev, "Failed to register ac, %ld\n", PTR_ERR(pwr->ac)); ret = PTR_ERR(pwr->ac); pwr->ac = NULL; goto fail_register_ac; } /*Add DC_IN Inserted and Remove ISR */ irq = platform_get_irq(pdev, 0); // get irq number #ifdef __BD7181X_REGMAP_H__ irq += bd7181x->irq_base; #endif if (irq <= 0) { dev_warn(&pdev->dev, "platform irq error # %d\n", irq); return -ENXIO; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, bd7181x_power_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME, dev_name(&pdev->dev), &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "IRQ %d is not free.\n", irq); } /*add VBAT Low Voltage detection, John Zhang*/ irq = platform_get_irq(pdev, 1); #ifdef __BD7181X_REGMAP_H__ irq += bd7181x->irq_base; #endif if (irq <= 0) { dev_warn(&pdev->dev, "platform irq error # %d\n", irq); return -ENXIO; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, bd7181x_vbat_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME, dev_name(&pdev->dev), &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "IRQ %d is not free.\n", irq); } /* add INT_STAT_11 */ irq = platform_get_irq(pdev, 2); #ifdef __BD7181X_REGMAP_H__ irq += bd7181x->irq_base; #endif if (irq <= 0) { dev_warn(&pdev->dev, "platform irq error # %d\n", irq); return -ENXIO; } ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, bd7181x_int_11_interrupt, IRQF_TRIGGER_LOW | IRQF_EARLY_RESUME, dev_name(&pdev->dev), &pdev->dev); if (ret < 0) { dev_err(&pdev->dev, "IRQ %d is not free.\n", irq); } /* Enable INT_11 */ ret = bd7181x_reg_write(bd7181x, BD7181X_REG_INT_EN_11, 0xFF); if (ret < 0) { dev_warn(&pdev->dev, "Write BD7181X_REG_INT_EN_11 failed\n"); } reg = bd7181x_reg_read(bd7181x, BD7181X_REG_INT_EN_11); if (reg < 0) { dev_warn(&pdev->dev, "Read BD7181X_REG_INT_EN_11 failed\n"); } dev_info(&pdev->dev, "BD7181X_REG_INT_EN_11=0x%x\n", reg); ret = sysfs_create_group(&pwr->bat->dev.kobj, &bd7181x_sysfs_attr_group); if (ret < 0) { dev_err(&pdev->dev, "failed to register sysfs interface\n"); } pwr->reg_index = -1; INIT_DELAYED_WORK(&pwr->bd_work, bd_work_callback); /* Schedule timer to check current status */ pwr->calib_current = CALIB_NORM; schedule_delayed_work(&pwr->bd_work, msecs_to_jiffies(0)); return 0; //error_exit: power_supply_unregister(pwr->ac); fail_register_ac: power_supply_unregister(pwr->bat); fail_register_bat: platform_set_drvdata(pdev, NULL); kfree(pwr); return ret; } /** @brief remove pwr device * @param pdev platform deivce of bd7181x_power * @return 0 */ static int __exit bd7181x_power_remove(struct platform_device *pdev) { struct bd7181x_power *pwr = platform_get_drvdata(pdev); /* If the product often power up/down and the power down time is long, the Coulomb Counter may have a drift. */ /* If so, it may be better accuracy to disable Coulomb Counter using following commented out code */ /* for stopping counting Coulomb when the product is power down(without sleep). */ /* The condition */ /* (1) Product often power up and down, the power down time is long and there is no power consumed in power down time. */ /* (2) Kernel must call this routin at power down time. */ /* (3) Must use this code with "Start Coulomb Counter" code in bd7181x_power_probe() function */ /* Stop Coulomb Counter */ /* bd7181x_clear_bits(pwr->mfd, BD7181x_REG_CC_CTRL, CCNTENB); */ sysfs_remove_group(&pwr->bat->dev.kobj, &bd7181x_sysfs_attr_group); cancel_delayed_work(&pwr->bd_work); power_supply_unregister(pwr->bat); power_supply_unregister(pwr->ac); platform_set_drvdata(pdev, NULL); kfree(pwr); return 0; } static struct platform_driver bd7181x_power_driver = { .driver = { .name = "bd7181x-power", .owner = THIS_MODULE, }, .remove = __exit_p(bd7181x_power_remove), }; /** @brief module initialize function */ static int __init bd7181x_power_init(void) { return platform_driver_probe(&bd7181x_power_driver, bd7181x_power_probe); } module_init(bd7181x_power_init); /** @brief module deinitialize function */ static void __exit bd7181x_power_exit(void) { platform_driver_unregister(&bd7181x_power_driver); } module_exit(bd7181x_power_exit); module_param(battery_cycle, uint, S_IWUSR | S_IRUGO); MODULE_PARM_DESC(battery_parameters, "battery_cycle:battery charge/discharge cycles"); MODULE_AUTHOR("Tony Luo "); MODULE_AUTHOR("Peter Yang "); MODULE_DESCRIPTION("BD71815/BD71817 Battery Charger Power driver"); MODULE_LICENSE("GPL"); /*-------------------------------------------------------*/ #include #include #include #include #define PROCFS_NAME "bd7181x_rev" #define BD7181X_REV "BD7181x Driver: Rev008\n" #define BD7181X_BUF_SIZE 1024 static char procfs_buffer[BD7181X_BUF_SIZE]; /** * This function is called then the /proc file is read * */ static int onetime = 0; static ssize_t bd7181x_proc_read (struct file *file, char __user *buffer, size_t count, loff_t *data) { int ret = 0, error = 0; if(onetime==0) { onetime = 1; memset( procfs_buffer, 0, BD7181X_BUF_SIZE); sprintf(procfs_buffer, "%s", BD7181X_REV); ret = strlen(procfs_buffer); error = raw_copy_to_user(buffer, procfs_buffer, strlen(procfs_buffer)); } else { //Clear for next time onetime = 0; } return (error!=0)?0:ret; } #if 0 int bd7181x_debug_mask = 0; static ssize_t bd7181x_proc_write (struct file *file, const char __user *buffer, size_t count, loff_t *data) { sscanf(buffer, "0x%x", &bd7181x_debug_mask); printk("BD7181x: bd7181x_debug_mask=0x%08x\n", bd7181x_debug_mask); return count; } #endif static const struct file_operations bd7181x_proc_fops = { .owner = THIS_MODULE, .read = bd7181x_proc_read, //.write = bd7181x_proc_write, }; /** *This function is called when the module is loaded * */ int bd7181x_revision_init(void) { struct proc_dir_entry *bd7181x_proc_entry; /* create the /proc/bd7181x_rev */ bd7181x_proc_entry = proc_create(PROCFS_NAME, 0644, NULL, &bd7181x_proc_fops); if (bd7181x_proc_entry == NULL) { printk("Error: Could not initialize /proc/%s\n", PROCFS_NAME); return -ENOMEM; } return 0; } module_init(bd7181x_revision_init); /*-------------------------------------------------------*/