blob: d55468538235477c7dab349a8e7f67b36131265a [file] [log] [blame]
/* Copyright (c) 2016-2017, HUAWEI TECHNOLOGIES CO., LTD. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/i2c.h>
#include <linux/debugfs.h>
#include <linux/gpio.h>
#include <linux/errno.h>
#include <linux/delay.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
#include <linux/regulator/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/qpnp/qpnp-adc.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/wakelock.h>
/* Mask/Bit helpers */
#define _MP2661_MASK(BITS, POS) \
((unsigned char)(((1 << (BITS)) - 1) << (POS)))
#define MP2661_MASK(LEFT_BIT_POS, RIGHT_BIT_POS) \
_MP2661_MASK((LEFT_BIT_POS) - (RIGHT_BIT_POS) + 1, \
(RIGHT_BIT_POS))
/*Input Source Control Register*/
#define INPUT_SOURCE_CTRL_REG 0x0
#define EN_HIZ_MASK MP2661_MASK(7, 7)
#define EN_HIZ_MASK_SHIFT 7
#define INPUT_SOURCE_VOLTAGE_LIMIT_MASK MP2661_MASK(6, 3)
#define INPUT_SOURCE_VOLTAGE_LIMIT_MASK_SHIFT 3
#define INPUT_SOURCE_CURRENT_LIMIT_MASK MP2661_MASK(2, 0)
#define INPUT_SOURCE_CURRENT_LIMIT_MASK_SHIFT 0
/*Power-On Configuration register*/
#define POWER_ON_CFG_REG 0x1
#define CHG_ENABLE_MASK MP2661_MASK(3, 3)
#define CHG_ENABLE_MASK_SHIFT 3
#define UVLO_THRESHOLD_MASK MP2661_MASK(2, 0)
#define UVLO_THRESHOLD_MASK_SHIFT 0
/* Config/Control register */
#define CHG_CURRENT_CTRL_REG 0x2
#define BATT_CHARGING_CURRENT_MASK MP2661_MASK(4, 0)
#define BATT_CHARGING_CURRENT_MASK_SHIFT 0
/*Discharge/termination current limit register*/
#define DISCHG_TERM_CURRENT_REG 0x3
#define DISCHG_CURRENT_MASK MP2661_MASK(6, 3)
#define DISCHG_CURRENT_MASK_SHIFT 3
#define TRCIKE_PCB_OTP_DISABLE_MASK MP2661_MASK(2, 2)
#define TRCIKE_PCB_OTP_DISABLE_MASK_SHIFT 2
#define TRCIKE_CHARGING_CURRENT_MASK MP2661_MASK(1, 0)
#define TRCIKE_CHARGING_CURRENT_MASK_SHIFT 0
/*Charge Voltage Control Register*/
#define CHG_VOLTAGE_CTRL_REG 0x4
#define FULL_CHG_VOLTAGE_MASK MP2661_MASK(7, 2)
#define FULL_CHG_VOLTAGE_MASK_SHIFT 2
#define TRICKLE_CHARGE_THESHOLD_MASK MP2661_MASK(1, 1)
#define TRICKLE_CHARGE_THESHOLD_MASK_SHIFT 1
#define BATTERY_RECHARGE_THRESHOLD_MASK MP2661_MASK(0, 0)
#define BATTERY_RECHARGE_THRESHOLD_MASK_SHIFT 0
/*Charge Termination/Timer Control Register */
#define CHG_TERMINATION_TIMER_CTRL_REG 0x5
#define TERMINATION_EN_MASK MP2661_MASK(6, 6)
#define TERMINATION_EN_MASK_SHIFT 6
#define I2C_WATCHDOG_TIMER_LIMIT_MASK MP2661_MASK(5, 4)
#define I2C_WATCHDOG_TIMER_LIMIT_MASK_SHIFT 4
#define SAFETY_TIMER_MASK MP2661_MASK(3, 3)
#define SAFETY_TIMER_MASK_SHIFT 3
#define CC_CHG_TIMER_MASK MP2661_MASK(2, 1)
#define CC_CHG_TIMER_MASK_SHIFT 1
/*Miscellaneous Operation Control Register*/
#define MISCELLANEOUS_OPER_CTRL_REG 0x6
#define THERMAL_REGULATION_THRESHOLD_MASK MP2661_MASK(1, 0)
#define THERMAL_REGULATION_THRESHOLD_MASK_SHIFT 0
#define NTC_EN_MASK MP2661_MASK(3, 3)
#define NTC_EN_MASK_SHIFT 3
#define BAT_FET_DIS_MASK MP2661_MASK(5, 5)
#define BAT_FET_DIS_MASK_SHIFT 5
#define TMR2X_EN_MASK MP2661_MASK(6, 6)
#define TMR2X_EN_MASK_SHIFT 6
/* System Status Register */
#define SYSTEM_STATUS_REG 0x07
#define CHG_STAT_MASK MP2661_MASK(4, 3)
#define CHG_STAT_SHIFT 3
#define CHAG_IN_VALID_IRQ BIT(1)
/* Fault Register */
#define FAULT_REG 0x08
#define WATCHDOG_FAULT_MASK MP2661_MASK(6, 6)
#define WATCHDOG_FAULT_MASK_SHIFT 6
#define VIN_FAULT_MASK MP2661_MASK(5, 5)
#define VIN_FAULT_MASK_SHIFT 5
#define THEM_SD_MASK MP2661_MASK(4, 4)
#define THEM_SD_MASK_SHIFT 4
#define BAT_FAULT_MASK MP2661_MASK(3, 3)
#define BAT_FAULT_MASK_SHIFT 3
#define STMR_FAULT_MASK MP2661_MASK(2, 2)
#define STMR_FAULT_MASK_SHIFT 2
#define STMR_FAULT 1
#define FAULT_FLAG 1
#define RECHARGE_CAPACITY_THRESHOLD 95
#define CLEAR_RECHARGE_CAPACITY_THRESHOLD 90
extern bool get_global_max17055_intialized_flag(void);
enum {
CHG_STAT_NOT_CHARGING =0,
CHG_STAT_TRICKE_CHARGE,
CHG_STAT_CONSTANT_CHARGE,
CHG_STAT_CHARGE_DONE,
};
enum {
BAT_TEMP_STATUS_COLD = 0,
BAT_TEMP_STATUS_NORMAL_STATE1,
BAT_TEMP_STATUS_NORMAL_STATE2,
BAT_TEMP_STATUS_NORMAL_STATE3,
BAT_TEMP_STATUS_NORMAL_STATE4,
BAT_TEMP_STATUS_HOT,
BAT_TEMP_STATUS_MAX,
};
static char *pm_batt_supplied_to[] = {
"bms",
};
struct mp2661_chg {
struct i2c_client *client;
struct device *dev;
struct mutex read_write_lock;
bool usb_present;
int charging_status;
int fake_battery_soc;
struct dentry *debug_root;
/* psy */
struct power_supply *usb_psy;
struct power_supply batt_psy;
struct power_supply *bms_psy;
const char *bms_psy_name;
struct workqueue_struct *charger_int_work_queue;
struct work_struct process_interrupt_work;
struct wake_lock chg_wake_lock;
/* adc_tm parameters */
struct qpnp_vadc_chip *vadc_dev;
struct qpnp_adc_tm_chip *adc_tm_dev;
bool using_pmic_therm;
/* batt temp states decidegc */
int cold_batt_decidegc;
int normal_state1_batt_decidegc;
int normal_state2_batt_decidegc;
int normal_state3_batt_decidegc;
int hot_batt_decidegc;
/* charge parameters */
int batt_full_mv;
int batt_full_terminate_ma;
int usb_input_ma;
int usb_input_regulation_mv;
int batt_charging_ma_max;
int batt_temp_status;
int batt_chaging_ma_mitigation[BAT_TEMP_STATUS_MAX];
int batt_trickle_charging_ma;
int batt_trickle_to_cc_theshold_mv;
int batt_uvlo_theshold_mv;
int batt_auto_recharge_delta_mv;
int batt_discharging_ma;
int thermal_regulation_threshold;
int batt_cc_chg_timer;
/* monitor temp task */
struct task_struct *monitor_temp_task;
struct semaphore monitor_temp_sem;
struct timespec resume_time;
struct timespec last_monitor_time;
int last_temp;
/* step charging */
int step_charging_batt_full_mv;
int step_charging_current_ma;
int step_charging_delta_voltage_mv;
int batt_full_now_mv;
int batt_charging_current_now_ma;
bool repeat_charging_detect_flag;
int stmr_expiration_count;
int batt_temp_in_normal_state1_count;
int batt_cv_chg_current_delta_ma;
int batt_cv_chg_current_ma;
int repeat_charging_detect_threshold_mv;
bool enable_charging_flag;
int retail_mode;
int notify_user_paired;
/* continue check battery current */
struct timer_list usb_in_timer;
bool ibat_continue_check_flag;
};
struct mp2661_chg *global_mp2661 = NULL;
extern int max17055_global_get_real_capacity(void);
#define RETRY_COUNT 5
int retry_sleep_ms[RETRY_COUNT] = {
10, 20, 30, 40, 50
};
static int __mp2661_read(struct mp2661_chg *chip, int reg,
u8 *val)
{
s32 ret;
int retry_count = 0;
retry:
ret = i2c_smbus_read_byte_data(chip->client, reg);
if (ret < 0 && retry_count < RETRY_COUNT)
{
/* sleep for few ms before retrying */
msleep(retry_sleep_ms[retry_count++]);
goto retry;
}
if (ret < 0)
{
pr_err("i2c read fail: can't read from %02x: %d\n", reg, ret);
return ret;
}
else
{
*val = ret;
}
return 0;
}
static int __mp2661_write(struct mp2661_chg *chip, int reg,
u8 val)
{
s32 ret;
int retry_count = 0;
retry:
ret = i2c_smbus_write_byte_data(chip->client, reg, val);
if (ret < 0 && retry_count < RETRY_COUNT)
{
/* sleep for few ms before retrying */
msleep(retry_sleep_ms[retry_count++]);
goto retry;
}
if (ret < 0)
{
pr_err("i2c write fail: can't write %02x to %02x: %d\n",
val, reg, ret);
return ret;
}
pr_debug("Writing 0x%02x=0x%02x\n", reg, val);
return 0;
}
static int mp2661_read(struct mp2661_chg *chip, int reg,
u8 *val)
{
int rc;
mutex_lock(&chip->read_write_lock);
pm_stay_awake(chip->dev);
rc = __mp2661_read(chip, reg, val);
pm_relax(chip->dev);
mutex_unlock(&chip->read_write_lock);
return rc;
}
static int mp2661_masked_write(struct mp2661_chg *chip, int reg,
u8 mask, u8 val)
{
s32 rc;
u8 temp;
mutex_lock(&chip->read_write_lock);
rc = __mp2661_read(chip, reg, &temp);
if (rc < 0)
{
pr_err("read failed: reg=%03X, rc=%d\n", reg, rc);
goto out;
}
temp &= ~mask;
temp |= val & mask;
rc = __mp2661_write(chip, reg, temp);
if (rc < 0)
{
pr_err("write failed: reg=%03X, rc=%d\n", reg, rc);
}
out:
mutex_unlock(&chip->read_write_lock);
return rc;
}
static bool mp2661_reg_and_write_is_equal(struct mp2661_chg *chip, int reg,
u8 mask, u8 val)
{
s32 rc = -1;
u8 tmp_value = 0;
u8 read_value = 0;
rc = mp2661_read(chip, reg, &read_value);
if (rc < 0)
{
return false;
}
/* compare val and reg */
tmp_value = (read_value & mask);
if (val == tmp_value)
{
return true;
}
else
{
return false;
}
}
static int mp2661_masked_write_verify(struct mp2661_chg *chip, int reg,
u8 mask, u8 val)
{
s32 rc = -1;
bool ret = false;
int retries = 3;
/* compare default value */
ret = mp2661_reg_and_write_is_equal(chip, reg,
mask, val);
if (ret)
{
return 0;
}
/* retry three times when consistent */
do
{
rc = mp2661_masked_write(chip, reg,
mask, val);
if (rc < 0)
{
/* I2C busy, write fail */
return -EBUSY;
}
ret = mp2661_reg_and_write_is_equal(chip, reg,
mask, val);
if (ret)
{
return 0;
}
else
{
rc = -EAGAIN;
retries--;
}
}while (retries > 0);
if (retries <= 0)
{
pr_err("retry three times failed: reg=%02x, rc=%d\n", reg, rc);
}
return rc;
}
static enum power_supply_property mp2661_battery_properties[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_CHARGING_ENABLED,
POWER_SUPPLY_PROP_CHARGE_TYPE,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_TEMP,
POWER_SUPPLY_PROP_TEMP_AMBIENT,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_USB_INPUT_CURRENT,
POWER_SUPPLY_PROP_BATTERY_ID,
POWER_SUPPLY_PROP_RETAIL_MODE,
POWER_SUPPLY_PROP_NOTIFY_USER_PARIED,
};
static int mp2661_get_prop_current_now(struct mp2661_chg *chip);
static int mp2661_is_chg_plugged_in(struct mp2661_chg *chip);
/* The max check continue current time is IBAT_SLEEP_MS * CONTINUE_IBAT_MAX_COUNT */
#define CONTINUE_IBAT_MAX_COUNT 80
#define IBAT_SLEEP_MS 30
#define CONSECUTIVE_CHARGING_COUNT 3
static int mp2661_fix_charging_status(struct mp2661_chg *chip)
{
int usb_present, current_now_ma, retry_times;
int status = POWER_SUPPLY_STATUS_CHARGING;
int consective_charging_count = 0;
if(!chip->ibat_continue_check_flag)
{
current_now_ma = mp2661_get_prop_current_now(chip) / 1000;
if(current_now_ma < 0)
{
pr_info("fix charging status");
status = POWER_SUPPLY_STATUS_DISCHARGING;
}
}
else
{
/* need check some times to wait usb plugin stable */
for(retry_times = 0; retry_times < CONTINUE_IBAT_MAX_COUNT; retry_times++)
{
current_now_ma = mp2661_get_prop_current_now(chip) / 1000;
if(current_now_ma >= 0)
{
consective_charging_count++;
}
else
{
consective_charging_count = 0;
}
if((CONSECUTIVE_CHARGING_COUNT == consective_charging_count)
|| (!chip->ibat_continue_check_flag))
{
break;
}
msleep(IBAT_SLEEP_MS);
/* consider unplugin usb during the continue check time */
usb_present = mp2661_is_chg_plugged_in(chip);
if(!usb_present)
{
pr_info("unplugin usb in continue check");
status = POWER_SUPPLY_STATUS_DISCHARGING;
break;
}
}
if(retry_times >= CONTINUE_IBAT_MAX_COUNT)
{
pr_info("fix charging status after some times check");
status = POWER_SUPPLY_STATUS_DISCHARGING;
}
else if(!chip->ibat_continue_check_flag && (0 == consective_charging_count))
{
pr_info("fix charging status after some time");
status = POWER_SUPPLY_STATUS_DISCHARGING;
}
}
return status;
}
static irqreturn_t mp2661_chg_stat_handler(int irq, void *dev_id);
static int mp2661_get_prop_batt_status(struct mp2661_chg *chip)
{
int rc, usb_present;
u8 reg;
int status = POWER_SUPPLY_STATUS_DISCHARGING;
u8 chgr_sts = 0;
rc = mp2661_read(chip, SYSTEM_STATUS_REG, &reg);
if (rc < 0)
{
pr_err("Unable to read system status reg rc = %d\n", rc);
return POWER_SUPPLY_STATUS_UNKNOWN;
}
chgr_sts = (reg & CHG_STAT_MASK) >> CHG_STAT_SHIFT;
usb_present = (reg & CHAG_IN_VALID_IRQ) ? 1 : 0;
pr_info("reg = %x, chgr_sts = %d, usb_present = %d\n", reg, chgr_sts, usb_present);
if(usb_present)
{
if (chip->repeat_charging_detect_flag)
{
status = POWER_SUPPLY_STATUS_FULL;
}
else if (CHG_STAT_CHARGE_DONE == chgr_sts)
{
status = POWER_SUPPLY_STATUS_FULL;
}
else if (CHG_STAT_NOT_CHARGING == chgr_sts)
{
status = POWER_SUPPLY_STATUS_DISCHARGING;
}
else
{
/* fix charging status according to battery current in healthd*/
if(strncmp(current->comm, "healthd", 7))
{
status = POWER_SUPPLY_STATUS_CHARGING;
}
else
{
status = mp2661_fix_charging_status(chip);
}
}
}
else
{
if (chip->repeat_charging_detect_flag)
{
pr_info("usb has gone, need to fix it\n");
mp2661_chg_stat_handler(chip->client->irq, chip);
}
status = POWER_SUPPLY_STATUS_DISCHARGING;
}
return status;
}
#define BATT_ID_VOLTAGE_REFERENCE_UV 1800000
#define BATT_ID_VOLTAGE_REFERENCE_DELTA_UV 50000
static int mp2661_get_prop_batt_present(struct mp2661_chg *chip)
{
int rc = 0;
struct qpnp_vadc_result results;
rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &results);
if (rc)
{
pr_debug("Unable to read batt_id rc=%d\n", rc);
return 0;
}
if((results.physical > BATT_ID_VOLTAGE_REFERENCE_UV - BATT_ID_VOLTAGE_REFERENCE_DELTA_UV )
&& (results.physical < BATT_ID_VOLTAGE_REFERENCE_UV + BATT_ID_VOLTAGE_REFERENCE_DELTA_UV))
{
return 0;
}
return 1;
}
enum {
POWER_SUPPLY_BATTERY_ID_UNKNOWN = 0,
POWER_SUPPLY_BATTERY_ID_GUANGYU,
POWER_SUPPLY_BATTERY_ID_DESAY,
};
#define BATT_ID_VOL_MIN_UV 0
#define BATT_ID_VOL_AVG_UV 548038
#define BATT_ID_VOL_MAX_UV 1600000
static int mp2661_get_prop_batt_id(struct mp2661_chg *chip)
{
int rc = 0;
struct qpnp_vadc_result results;
union power_supply_propval ret = {0, };
rc = qpnp_vadc_read(chip->vadc_dev, LR_MUX2_BAT_ID, &results);
if (rc)
{
pr_debug("Unable to read batt_id rc=%d\n", rc);
return POWER_SUPPLY_BATTERY_ID_UNKNOWN;
}
if((results.physical > BATT_ID_VOL_MIN_UV) && (results.physical <= BATT_ID_VOL_AVG_UV))
{
ret.intval = POWER_SUPPLY_BATTERY_ID_GUANGYU;
}
else if((results.physical > BATT_ID_VOL_AVG_UV) && (results.physical <= BATT_ID_VOL_MAX_UV))
{
ret.intval = POWER_SUPPLY_BATTERY_ID_DESAY;
}
else
{
ret.intval = POWER_SUPPLY_BATTERY_ID_UNKNOWN;
}
return ret.intval;
}
static int mp2661_get_prop_charge_type(struct mp2661_chg *chip)
{
return POWER_SUPPLY_CHARGE_TYPE_FAST;
}
#define DEFAULT_BATT_CAPACITY 50
static int mp2661_get_prop_batt_capacity(struct mp2661_chg *chip)
{
union power_supply_propval ret = {0, };
if (chip->fake_battery_soc >= 0)
{
return chip->fake_battery_soc;
}
if (!chip->bms_psy && chip->bms_psy_name)
{
pr_info("get bms power supply\n");
chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name);
}
if (chip->bms_psy)
{
chip->bms_psy->get_property(chip->bms_psy,
POWER_SUPPLY_PROP_CAPACITY, &ret);
return ret.intval;
}
return DEFAULT_BATT_CAPACITY;
}
static int mp2661_get_prop_batt_health(struct mp2661_chg *chip)
{
union power_supply_propval ret = {0, };
if (BAT_TEMP_STATUS_HOT == chip->batt_temp_status)
{
ret.intval = POWER_SUPPLY_HEALTH_OVERHEAT;
}
else if (BAT_TEMP_STATUS_COLD == chip->batt_temp_status)
{
ret.intval = POWER_SUPPLY_HEALTH_COLD;
}
else
{
ret.intval = POWER_SUPPLY_HEALTH_GOOD;
}
return ret.intval;
}
#define DEFAULT_TEMP 250
static int mp2661_get_prop_batt_temp(struct mp2661_chg *chip)
{
int rc = 0;
struct qpnp_vadc_result results;
rc = qpnp_vadc_read(chip->vadc_dev, P_MUX2_1_1, &results);
if (rc)
{
pr_err("Unable to read batt temperature rc=%d\n", rc);
return DEFAULT_TEMP;
}
pr_debug("get_batt_temp %d, %lld\n",
results.adc_code, results.physical);
return (int)results.physical;
}
static int mp2661_get_prop_ambient_temp(struct mp2661_chg *chip)
{
int rc = 0;
struct qpnp_vadc_result results;
rc = qpnp_vadc_read(chip->vadc_dev, P_MUX3_1_1, &results);
if (rc)
{
pr_err("Unable to read ambient temperature rc=%d\n", rc);
return DEFAULT_TEMP;
}
pr_debug("get_ambient_temp %d, %lld\n",
results.adc_code, results.physical);
return (int)results.physical;
}
#define DEFAULT_BATT_VOLTAGE 2800000
static int mp2661_get_prop_battery_voltage_now(struct mp2661_chg *chip)
{
union power_supply_propval ret = {0, };
if (chip->fake_battery_soc >= 0)
{
return chip->fake_battery_soc;
}
if (!chip->bms_psy && chip->bms_psy_name)
{
pr_info("get bms power supply\n");
chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name);
}
if (chip->bms_psy)
{
chip->bms_psy->get_property(chip->bms_psy,
POWER_SUPPLY_PROP_VOLTAGE_NOW, &ret);
return ret.intval;
}
return DEFAULT_BATT_VOLTAGE;
}
static int mp2661_get_prop_current_now(struct mp2661_chg *chip)
{
union power_supply_propval ret = {0,};
if (!chip->bms_psy && chip->bms_psy_name)
{
pr_info("get bms power supply\n");
chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name);
}
if (chip->bms_psy)
{
chip->bms_psy->get_property(chip->bms_psy,
POWER_SUPPLY_PROP_CURRENT_NOW, &ret);
return ret.intval;
}
else
{
pr_debug("No BMS supply registered return 0\n");
}
return 0;
}
static int mp2661_get_prop_current_avg(struct mp2661_chg *chip)
{
union power_supply_propval ret = {0,};
if (!chip->bms_psy && chip->bms_psy_name)
{
pr_info("get bms power supply\n");
chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name);
}
if (chip->bms_psy)
{
chip->bms_psy->get_property(chip->bms_psy,
POWER_SUPPLY_PROP_CURRENT_AVG, &ret);
return ret.intval;
}
else
{
pr_debug("No BMS supply registered return 0\n");
}
return 0;
}
#define MP2661_FULL_VOLTAGE_STEP_MV 15
#define MP2661_FULL_VOLTAGE_MIN_MV 3600
#define MP2661_FULL_VOLTAGE_MAX_MV 4545
static int mp2661_set_batt_full_voltage(struct mp2661_chg *chip,
int voltage)
{
int rc,i;
if ((voltage < MP2661_FULL_VOLTAGE_MIN_MV) ||
(voltage > MP2661_FULL_VOLTAGE_MAX_MV))
{
pr_err( "bad charge full voltage %d asked to set\n",
voltage);
return -EINVAL;
}
i = (voltage - MP2661_FULL_VOLTAGE_MIN_MV) / MP2661_FULL_VOLTAGE_STEP_MV;
i = i << FULL_CHG_VOLTAGE_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_VOLTAGE_CTRL_REG,
FULL_CHG_VOLTAGE_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt full voltage to %dmv rc = %d\n",
voltage, rc);
}
else
{
chip->batt_full_now_mv = voltage;
}
return rc;
}
static int mp2661_set_batt_full_terminate_current(struct mp2661_chg *chip,
int current_limit)
{
return 0;
}
#define MP2661_USB_INPUT_CURRENT_MIN_MA 85
#define MP2661_USB_INPUT_CURRENT_MAX_MA 455
static int usb_input_current_limit[] = {
85, 130, 175, 220, 265, 310, 355, 455,
};
static int mp2661_set_usb_input_current(struct mp2661_chg *chip,
int current_limit)
{
int rc, i;
if ((current_limit < MP2661_USB_INPUT_CURRENT_MIN_MA) ||
(current_limit > MP2661_USB_INPUT_CURRENT_MAX_MA))
{
pr_err( "bad usb input current mA=%d asked to set\n",
current_limit);
return -EINVAL;
}
for (i = ARRAY_SIZE(usb_input_current_limit) - 1; i >= 0; i--)
{
if (usb_input_current_limit[i] <= current_limit)
break;
}
if (i < 0)
{
pr_err("Invalid current setting %dmA\n",
current_limit);
i = 0;
}
i = i << INPUT_SOURCE_CURRENT_LIMIT_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, INPUT_SOURCE_CTRL_REG,
INPUT_SOURCE_CURRENT_LIMIT_MASK, i);
if (rc < 0)
{
pr_err("cannot set usb input current to %dma rc = %d\n",
current_limit, rc);
}
return rc;
}
void mp2661_global_set_usb_input_current_default(void)
{
int rc;
if(!global_mp2661)
{
pr_err("mp2661 chip can not register\n");
return;
}
rc = mp2661_set_usb_input_current(global_mp2661, global_mp2661->usb_input_ma);
if(rc)
{
pr_err("Couldn't set usb input current rc = %d\n", rc);
}
}
static int mp2661_get_usb_input_current(struct mp2661_chg *chip)
{
union power_supply_propval ret = {0,};
int rc;
u8 reg;
rc = mp2661_read(chip, INPUT_SOURCE_CTRL_REG, &reg);
if (rc < 0)
{
pr_err("Couldn't read INPUT_SOURCE_CTRL_REG rc = %d\n", rc);
return rc;
}
reg = (reg & INPUT_SOURCE_CURRENT_LIMIT_MASK) >> INPUT_SOURCE_CURRENT_LIMIT_MASK_SHIFT;
ret.intval = usb_input_current_limit[reg];
return ret.intval;
}
#define MP2661_USB_INPUT_VOLTAGE_STEP_MV 80
#define MP2661_USB_INPUT_VOLTAGE_MAX_MV 5080
#define MP2661_USB_INPUT_VOLTAGE_MIN_MV 3880
static int mp2661_set_usb_input_voltage_regulation(struct mp2661_chg *chip,
int voltage)
{
int rc, i;
if ((voltage < MP2661_USB_INPUT_VOLTAGE_MIN_MV) ||
(voltage > MP2661_USB_INPUT_VOLTAGE_MAX_MV))
{
pr_err( "bad input current mA=%d asked to set\n",
voltage);
return -EINVAL;
}
i = (voltage - MP2661_USB_INPUT_VOLTAGE_MIN_MV) / MP2661_USB_INPUT_VOLTAGE_STEP_MV;
i = i << INPUT_SOURCE_VOLTAGE_LIMIT_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, INPUT_SOURCE_CTRL_REG,
INPUT_SOURCE_VOLTAGE_LIMIT_MASK, i);
if (rc < 0)
{
pr_err("cannot set usb input voltage regulation to %dmv rc = %d\n",
voltage, rc);
}
return rc;
}
#define MP2661_BATT_CHARGING_STEP_MA 17
#define MP2661_BATT_CHARGING_MIN_MA 8
#define MP2661_BATT_CHARGING_MAX_MA 535
static int mp2661_set_batt_charging_current(struct mp2661_chg *chip,
int current_ma)
{
int rc, i;
if ((current_ma < MP2661_BATT_CHARGING_MIN_MA) ||
(current_ma > MP2661_BATT_CHARGING_MAX_MA))
{
pr_err( "bad bat charging current mA=%d asked to set\n",
current_ma);
return -EINVAL;
}
i = (current_ma - MP2661_BATT_CHARGING_MIN_MA) / MP2661_BATT_CHARGING_STEP_MA;
i = i << BATT_CHARGING_CURRENT_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_CURRENT_CTRL_REG,
BATT_CHARGING_CURRENT_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt charging current to %dma rc = %d\n",
current_ma, rc);
}
else
{
chip->batt_charging_current_now_ma = current_ma;
}
return rc;
}
static void mp2661_set_appropriate_batt_charging_current(
struct mp2661_chg *chip)
{
int rc;
int current_max = chip->batt_charging_ma_max;
int index = chip->batt_temp_status;
current_max =
min(current_max, chip->batt_chaging_ma_mitigation[index]);
pr_info("setting %dma", current_max);
rc = mp2661_set_batt_charging_current(chip, current_max);
if (rc)
{
pr_err("Couldn't set batt appopriate charging current rc = %d\n", rc);
}
}
static int mp2661_set_pcb_otp_disable(struct mp2661_chg *chip,
bool disable)
{
int rc, i;
i = disable << TRCIKE_PCB_OTP_DISABLE_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, DISCHG_TERM_CURRENT_REG,
TRCIKE_PCB_OTP_DISABLE_MASK, i);
if (rc < 0)
{
pr_err("cannot set pcb otp disable to %d rc = %d\n", disable, rc);
}
return rc;
}
#define MP2661_TRICKE_CHARGING_STEP_MA 7
#define MP2661_TRICKLE_CHARGING_MIN_MA 6
#define MP2661_TRICKLE_CHARGING_MAX_MA 27
static int mp2661_set_batt_trickle_charging_current(struct mp2661_chg *chip,
int current_limit)
{
int rc,i;
if ((current_limit < MP2661_TRICKLE_CHARGING_MIN_MA) ||
(current_limit > MP2661_TRICKLE_CHARGING_MAX_MA))
{
pr_err( "bad trickle charging current limit mA=%d asked to set\n",
current_limit);
return -EINVAL;
}
i = (current_limit - MP2661_TRICKLE_CHARGING_MIN_MA) / MP2661_TRICKE_CHARGING_STEP_MA;
i = i << TRCIKE_CHARGING_CURRENT_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, DISCHG_TERM_CURRENT_REG,
TRCIKE_CHARGING_CURRENT_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt trickle charging current to %dma rc = %d\n",
current_limit, rc);
}
return rc;
}
#define MP2661_BATT_TRICKLE_TO_CC_THRESHOLD_MIN_MV 2800
#define MP2661_BATT_TRICKLE_TO_CC_THRESHOLD_MAX_MV 3000
static int mp2661_set_batt_trickle_to_cc_threshold(struct mp2661_chg *chip,
int voltage)
{
int rc,i;
if(MP2661_BATT_TRICKLE_TO_CC_THRESHOLD_MIN_MV == voltage)
{
i = 0;
}
else if(MP2661_BATT_TRICKLE_TO_CC_THRESHOLD_MAX_MV == voltage)
{
i = 1;
}
else
{
pr_err( "bad batt trickle to cc mv=%d asked to set\n",
voltage);
return -EINVAL;
}
i = i << TRICKLE_CHARGE_THESHOLD_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_VOLTAGE_CTRL_REG,
TRICKLE_CHARGE_THESHOLD_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt trickle to cc threshold to %dmv rc = %d\n",
voltage, rc);
}
return rc;
}
#define MP2661_UVLO_THRESHOLD_STEP_MV 100
#define MP2661_UVLO_THRESHOLD_MIN_MV 2400
#define MP2661_UVLO_THRESHOLD_MAX_MV 3100
static int mp2661_set_batt_uvlo_threshold(struct mp2661_chg *chip,
int voltage)
{
int rc, i;
if ((voltage < MP2661_UVLO_THRESHOLD_MIN_MV) ||
(voltage > MP2661_UVLO_THRESHOLD_MAX_MV))
{
pr_err( "bad batt uvlo threshold mv=%d asked to set\n",
voltage);
return -EINVAL;
}
i = (voltage - MP2661_UVLO_THRESHOLD_MIN_MV) / MP2661_UVLO_THRESHOLD_STEP_MV;
i = i << UVLO_THRESHOLD_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, POWER_ON_CFG_REG,
UVLO_THRESHOLD_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt uvlo threshold to %dmv rc = %d\n", voltage, rc);
}
return rc;
}
#define MP2661_AUTO_RECHARGE_BELOW_FULL_MIN_MV 150
#define MP2661_AUTO_RECHARGE_BELOW_FULL_MAX_MV 300
static int mp2661_set_batt_auto_recharge(struct mp2661_chg *chip,
int voltage_below_full)
{
int rc,i;
if(MP2661_AUTO_RECHARGE_BELOW_FULL_MIN_MV == voltage_below_full)
{
i = 0;
}
else if(MP2661_AUTO_RECHARGE_BELOW_FULL_MAX_MV == voltage_below_full)
{
i = 1;
}
else
{
pr_err( "bad auto recharge current below full mv=%d asked to set\n",
voltage_below_full);
return -EINVAL;
}
i = i << BATTERY_RECHARGE_THRESHOLD_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_VOLTAGE_CTRL_REG,
BATTERY_RECHARGE_THRESHOLD_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt auto recharge below full to %dmv rc = %d\n",
voltage_below_full, rc);
}
return rc;
}
#define MP2661_BATT_DISCHARGE_CURRENT_STEP_MA 200
#define MP2661_BATT_DISCHARGE_MIN_MA 200
#define MP2661_BATT_DISCHARGE_MAX_MA 3200
static int mp2661_set_batt_discharging_current(struct mp2661_chg *chip,
int current_limit)
{
int rc, i;
if ((current_limit < MP2661_BATT_DISCHARGE_MIN_MA) ||
(current_limit > MP2661_BATT_DISCHARGE_MAX_MA))
{
pr_err( "bad batt dischargge current limit ma=%d asked to set\n",
current_limit);
return -EINVAL;
}
i = (current_limit - MP2661_BATT_DISCHARGE_MIN_MA) / MP2661_BATT_DISCHARGE_CURRENT_STEP_MA;
i = i << DISCHG_CURRENT_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, DISCHG_TERM_CURRENT_REG,
DISCHG_CURRENT_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt discharging current to %dma rc = %d\n", current_limit, rc);
}
return rc;
}
#define MP2661_THERMAL_TEMP_STEP 20
#define MP2661_THERMAL_TEMP_MAX 120
#define MP2661_THERMAL_TEMP_MIN 60
static int mp2661_set_thermal_regulation_threshold(struct mp2661_chg *chip,
int temp)
{
int rc, i;
if ((temp < MP2661_THERMAL_TEMP_MIN) ||
(temp > MP2661_THERMAL_TEMP_MAX))
{
pr_err("bad input temp = %d asked to set\n",
temp);
return -EINVAL;
}
i = (temp - MP2661_THERMAL_TEMP_MIN) / MP2661_THERMAL_TEMP_STEP;
i = i << THERMAL_REGULATION_THRESHOLD_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, MISCELLANEOUS_OPER_CTRL_REG,
THERMAL_REGULATION_THRESHOLD_MASK, i);
if (rc < 0)
{
pr_err("cannot set thermal regulation threshold to %d, rc = %d\n",
temp, rc);
}
return rc;
}
static int mp2661_en_bf_enable(struct mp2661_chg *chip,
bool enable)
{
int rc,i;
i = enable << TERMINATION_EN_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_TERMINATION_TIMER_CTRL_REG,
TERMINATION_EN_MASK, i);
if (rc < 0)
{
pr_err("cannot set bf enable to %d rc = %d\n", enable, rc);
}
return rc;
}
static int mp2661_set_ldo_fet_disconnect(struct mp2661_chg *chip,
bool disconnect)
{
int rc,i;
i = disconnect << EN_HIZ_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, INPUT_SOURCE_CTRL_REG,
EN_HIZ_MASK, i);
if (rc < 0)
{
pr_err("cannot set ldo fet disconnect to %d rc = %d\n",
disconnect, rc);
}
return rc;
}
static int mp2661_set_batt_fet_disconnect(struct mp2661_chg *chip,
bool disconnect)
{
int rc,i;
i = disconnect << BAT_FET_DIS_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, MISCELLANEOUS_OPER_CTRL_REG,
BAT_FET_DIS_MASK, i);
if (rc < 0)
{
pr_err("cannot set batt fet disconnect to %d rc = %d\n",
disconnect, rc);
}
return rc;
}
static int mp2661_tmr2x_enable(struct mp2661_chg *chip,
bool enable)
{
int rc,i;
i = enable << TMR2X_EN_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, MISCELLANEOUS_OPER_CTRL_REG,
TMR2X_EN_MASK, i);
if (rc < 0)
{
pr_err("cannot set tmr2x enable to %d rc = %d\n", enable, rc);
}
return rc;
}
static int mp2661_ntc_enable(struct mp2661_chg *chip,
bool enable)
{
int rc,i;
i = enable << NTC_EN_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, MISCELLANEOUS_OPER_CTRL_REG,
NTC_EN_MASK, i);
if (rc < 0)
{
pr_err("cannot set ntc enable to %d rc = %d\n", enable, rc);
}
return rc;
}
#define MP2661_I2C_WATCHDOG_TIMER_STEP_SEC 40
#define MP2661_I2C_WATCHDOG_TIMER_MIN_SEC 0
#define MP2661_I2C_WATCHDOG_TIMER_MAX_SEC 160
static int mp2661_set_i2c_watchdog_timer(struct mp2661_chg *chip,
int time)
{
int rc,i;
if((time > MP2661_I2C_WATCHDOG_TIMER_MAX_SEC) ||
(time < MP2661_I2C_WATCHDOG_TIMER_MIN_SEC))
{
pr_err("bad i2c watchdog timer sec=%d asked to set\n",
time);
return -EINVAL;
}
i = time / MP2661_I2C_WATCHDOG_TIMER_STEP_SEC;
i = i << I2C_WATCHDOG_TIMER_LIMIT_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_TERMINATION_TIMER_CTRL_REG,
I2C_WATCHDOG_TIMER_LIMIT_MASK, i);
if (rc < 0)
{
pr_err("cannot set i2c watchdog timer to %ds rc = %d\n",
time, rc);
}
return rc;
}
static int mp2661_safety_timer_enable(struct mp2661_chg *chip,
bool enable)
{
int rc,i;
i = enable << SAFETY_TIMER_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_TERMINATION_TIMER_CTRL_REG,
SAFETY_TIMER_MASK, i);
if (rc < 0)
{
pr_err("cannot set safety timer enable to %d rc = %d\n",
enable, rc);
}
return rc;
}
static int cc_chg_timer[4] = {
3, 5, 8, 12
};
static int mp2661_set_cc_chg_timer(struct mp2661_chg *chip,
int time_limit)
{
int rc,i;
if ((time_limit < cc_chg_timer[0]) ||
(time_limit > cc_chg_timer[3]))
{
pr_err( "bad cc chg timer hours=%d asked to set\n",
time_limit);
return -EINVAL;
}
for (i = ARRAY_SIZE(cc_chg_timer) - 1; i >= 0; i--)
{
if (cc_chg_timer[i] <= time_limit)
{
break;
}
}
if (i < 0)
{
pr_err("Invalid cc chg timer, setting default timer to %d hours\n",
cc_chg_timer[0]);
i = 0;
}
i = i << CC_CHG_TIMER_MASK_SHIFT;
rc = mp2661_masked_write_verify(chip, CHG_TERMINATION_TIMER_CTRL_REG,
CC_CHG_TIMER_MASK, i);
if (rc < 0)
{
pr_err("cannot set cc chg timer to %dh rc = %d\n", time_limit, rc);
}
return rc;
}
static int mp2661_set_charging_enable(struct mp2661_chg *chip, bool enable)
{
int rc,i;
i = enable ? 0 : (1 << CHG_ENABLE_MASK_SHIFT);
rc = mp2661_masked_write_verify(chip, POWER_ON_CFG_REG,
CHG_ENABLE_MASK, i);
if (rc < 0)
{
pr_err("Couldn't set chg enable = %d rc = %d\n", enable, rc);
return rc;
}
return 0;
}
static int mp2661_is_charging_enable(struct mp2661_chg *chip)
{
int rc;
u8 reg;
rc = mp2661_read(chip, POWER_ON_CFG_REG, &reg);
if (rc < 0)
{
pr_err("Couldn't read power-on config reg rc = %d\n", rc);
return rc;
}
return (reg & CHG_ENABLE_MASK) ? 0 : 1;
}
static int mp2661_is_chg_plugged_in(struct mp2661_chg *chip)
{
int rc;
u8 reg;
rc = mp2661_read(chip, SYSTEM_STATUS_REG, &reg);
if (rc < 0)
{
pr_err("Couldn't read system status reg rc = %d\n", rc);
return POWER_SUPPLY_STATUS_UNKNOWN;
}
return (reg & CHAG_IN_VALID_IRQ) ? 1 : 0;
}
bool mp2661_global_is_chg_plugged_in(void)
{
int rc;
if (!global_mp2661)
{
pr_err("mp2661 chip can not register\n");
return false;
}
rc = (bool)mp2661_is_chg_plugged_in(global_mp2661);
return rc;
}
static int mp2661_battery_set_property(struct power_supply *psy,
enum power_supply_property prop,
const union power_supply_propval *val)
{
int rc = 0, update_psy = 0;
struct mp2661_chg *chip = container_of(psy,
struct mp2661_chg, batt_psy);
switch (prop)
{
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
rc = mp2661_set_charging_enable(chip, val->intval);
update_psy = 1;
break;
case POWER_SUPPLY_PROP_USB_INPUT_CURRENT:
rc = mp2661_set_usb_input_current(chip, val->intval);
update_psy = 1;
break;
case POWER_SUPPLY_PROP_RETAIL_MODE:
chip->retail_mode = val->intval;
break;
case POWER_SUPPLY_PROP_NOTIFY_USER_PARIED:
chip->notify_user_paired = val->intval;
break;
default:
rc = -EINVAL;
}
if (!rc && update_psy)
{
power_supply_changed(&chip->batt_psy);
}
return rc;
}
static int mp2661_battery_get_property(struct power_supply *psy,
enum power_supply_property prop,
union power_supply_propval *val)
{
struct mp2661_chg *chip = container_of(psy,
struct mp2661_chg, batt_psy);
switch (prop)
{
case POWER_SUPPLY_PROP_STATUS:
val->intval = mp2661_get_prop_batt_status(chip);
break;
case POWER_SUPPLY_PROP_PRESENT:
val->intval = mp2661_get_prop_batt_present(chip);
break;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
val->intval = mp2661_is_charging_enable(chip);
break;
case POWER_SUPPLY_PROP_CHARGE_TYPE:
val->intval = mp2661_get_prop_charge_type(chip);
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = mp2661_get_prop_batt_capacity(chip);
break;
case POWER_SUPPLY_PROP_HEALTH:
val->intval = mp2661_get_prop_batt_health(chip);
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
break;
case POWER_SUPPLY_PROP_TEMP:
val->intval = mp2661_get_prop_batt_temp(chip);
break;
case POWER_SUPPLY_PROP_TEMP_AMBIENT:
val->intval = mp2661_get_prop_ambient_temp(chip);
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = mp2661_get_prop_battery_voltage_now(chip);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = mp2661_get_prop_current_now(chip);
break;
case POWER_SUPPLY_PROP_USB_INPUT_CURRENT:
val->intval = mp2661_get_usb_input_current(chip);
break;
case POWER_SUPPLY_PROP_BATTERY_ID:
val->intval = mp2661_get_prop_batt_id(chip);
break;
case POWER_SUPPLY_PROP_RETAIL_MODE:
val->intval = chip->retail_mode;
break;
case POWER_SUPPLY_PROP_NOTIFY_USER_PARIED:
val->intval = chip->notify_user_paired;
break;
default:
return -EINVAL;
}
return 0;
}
static void mp2661_external_power_changed(struct power_supply *psy)
{
struct mp2661_chg *chip = container_of(psy,
struct mp2661_chg, batt_psy);
if (!chip->bms_psy && chip->bms_psy_name)
{
chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name);
}
}
static void mp2661_disable_and_enable_charging(struct mp2661_chg *chip)
{
int rc;
rc = mp2661_set_charging_enable(chip, false);
if (rc)
{
pr_err("Couldn't set charging disable rc=%d\n", rc);
}
mdelay(200);
rc = mp2661_set_charging_enable(chip, true);
if (rc)
{
pr_err("Couldn't set charging enable rc=%d\n", rc);
}
}
static void usb_in_timer_func(unsigned long arg)
{
struct mp2661_chg *chip = (struct mp2661_chg *) arg;
pr_info("clear continue check flag\n");
chip->ibat_continue_check_flag = false;
}
#define STMR_EXPIRATION_COUNT_MAX 3
#define BATT_TEMP_IN_NORMAL_STATE1_COUNT_MAX 9
#define IBAT_CONTINUE_CHECK_TIME (5 * HZ)
static void mp2661_process_interrupt_work(struct work_struct *work)
{
int status, usb_present;
struct mp2661_chg *chip = container_of(work, struct mp2661_chg, process_interrupt_work);
u8 reg = 0;
int rc = -1;
int capacity = -1;
int vbatt_mv = 0;
/* check charging status */
status = mp2661_get_prop_batt_status(chip);
if(chip->charging_status != status)
{
pr_info("charing status change from %d to %d\n",
chip->charging_status, status);
chip->charging_status = status;
if(POWER_SUPPLY_STATUS_FULL == status)
{
pr_info("battery is full\n");
/* set repeat_charging_detect_flag to true and disable charge
* when charge terminate due to batt full.
*/
vbatt_mv = mp2661_get_prop_battery_voltage_now(chip) / 1000;
capacity = max17055_global_get_real_capacity();
if ((vbatt_mv >= chip->repeat_charging_detect_threshold_mv)
&& (capacity > RECHARGE_CAPACITY_THRESHOLD))
{
chip->repeat_charging_detect_flag = true;
rc = mp2661_set_charging_enable(chip, false);
if (rc)
{
pr_err("Couldn't set charging disable rc=%d\n", rc);
}
}
pr_info("clear stmr count when batt full!");
chip->stmr_expiration_count = 0;
chip->batt_temp_in_normal_state1_count = 0;
if (!chip->bms_psy && chip->bms_psy_name)
{
pr_info("get bms power supply\n");
chip->bms_psy = power_supply_get_by_name((char *)chip->bms_psy_name);
}
}
if(chip->bms_psy)
{
power_supply_changed(chip->bms_psy);
}
else
{
pr_err("bms_psy is NULL\n");
}
}
/* check usb status */
usb_present = mp2661_is_chg_plugged_in(chip);
pr_info("usb_present = %d, chip->usb_present = %d\n", usb_present, chip->usb_present);
if (chip->usb_present != usb_present)
{
chip->usb_present = usb_present;
if(chip->usb_present)
{
wake_lock(&chip->chg_wake_lock);
/* set battery current continue check time */
chip->ibat_continue_check_flag = true;
del_timer_sync(&chip->usb_in_timer);
chip->usb_in_timer.expires = jiffies + IBAT_CONTINUE_CHECK_TIME;
add_timer(&chip->usb_in_timer);
}
else
{
capacity = max17055_global_get_real_capacity();
if (chip->repeat_charging_detect_flag)
{
/* set fullcap to repcap when charger is not present and auto recharge is true */
const union power_supply_propval ret = {capacity,};
pr_info("real capacity = %d, update fullcap when remove charger!\n", capacity);
if ((chip->bms_psy) && (chip->bms_psy->set_property))
{
chip->bms_psy->set_property(chip->bms_psy, POWER_SUPPLY_PROP_CAPACITY,
&ret);
}
chip->repeat_charging_detect_flag = false;
/* hold charging status in retail mode and abnoraml temp status */
if ((0 == chip->retail_mode)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_HOT)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_COLD))
{
rc = mp2661_set_charging_enable(chip, true);
if (rc)
{
pr_err("Couldn't set charging enable rc=%d\n", rc);
}
}
}
pr_info("clear stmr count and enable charge when remove charger!");
chip->stmr_expiration_count = 0;
chip->batt_temp_in_normal_state1_count = 0;
/* clear enable charging flag */
chip->enable_charging_flag = false;
wake_unlock(&chip->chg_wake_lock);
}
power_supply_set_present(chip->usb_psy, chip->usb_present);
power_supply_changed(chip->usb_psy);
}
/* check safetytimer expiration */
rc = mp2661_read(chip, FAULT_REG, &reg);
if (rc < 0)
{
pr_err("Couldn't read fault reg rc = %d\n", rc);
}
reg = (reg & STMR_FAULT_MASK) >> STMR_FAULT_MASK_SHIFT;
if (STMR_FAULT == reg)
{
pr_info("stmr expiration!\n");
chip->stmr_expiration_count++;
if (chip->stmr_expiration_count < STMR_EXPIRATION_COUNT_MAX)
{
pr_info("stmr expiration count is %d less than %d!\n",
chip->stmr_expiration_count, STMR_EXPIRATION_COUNT_MAX);
if ((BAT_TEMP_STATUS_NORMAL_STATE1 == chip->batt_temp_status)
|| ((chip->batt_temp_in_normal_state1_count >= BATT_TEMP_IN_NORMAL_STATE1_COUNT_MAX)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_HOT)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_COLD)))
{
pr_info("re-enable charge when batt temp is in 0~10 or not full!\n");
mp2661_disable_and_enable_charging(chip);
/* clear stmr expiration flag */
rc = mp2661_read(chip, FAULT_REG, &reg);
if (rc < 0)
{
pr_err("Couldn't read fault reg rc = %d\n", rc);
}
chip->batt_temp_in_normal_state1_count = 0;
}
}
else
{
pr_info("stmr disable charge when expiration count is %d greater or equal to %d!\n",
chip->stmr_expiration_count, STMR_EXPIRATION_COUNT_MAX);
}
}
/* list mp2661 interrupt events */
rc = mp2661_read(chip, FAULT_REG, &reg);
if (rc < 0)
{
pr_err("Couldn't read fault reg rc = %d\n", rc);
}
rc = (reg & WATCHDOG_FAULT_MASK) >> WATCHDOG_FAULT_MASK_SHIFT;
if (FAULT_FLAG == rc)
{
pr_info("watchdog expiration!\n");
}
rc = (reg & VIN_FAULT_MASK) >> VIN_FAULT_MASK_SHIFT;
if (FAULT_FLAG == rc)
{
pr_info("input fault(ovp or bad source)!\n");
}
rc = (reg & THEM_SD_MASK) >> THEM_SD_MASK_SHIFT;
if (FAULT_FLAG == rc)
{
pr_info("thermal shutdown!\n");
}
rc = (reg & BAT_FAULT_MASK) >> BAT_FAULT_MASK_SHIFT;
if (FAULT_FLAG == rc)
{
pr_info("battery ovp!\n");
}
}
static irqreturn_t mp2661_chg_stat_handler(int irq, void *dev_id)
{
struct mp2661_chg *chip = dev_id;
pr_info("interrupt happens\n");
queue_work(chip->charger_int_work_queue, &chip->process_interrupt_work);
return IRQ_HANDLED;
}
static struct of_device_id mp2661_match_table[] = {
{
.compatible = "qcom,mp2661-charger",
},
{ },
};
static int mp2661_parse_dt(struct mp2661_chg *chip)
{
int rc;
struct device_node *node = chip->dev->of_node;
rc = of_property_read_string(node, "qcom,bms-psy-name",
&chip->bms_psy_name);
if (rc)
{
chip->bms_psy_name = NULL;
}
chip->using_pmic_therm = of_property_read_bool(node,
"qcom,using-pmic-therm");
rc = of_property_read_u32(node, "qcom,cold-batt-decidegc",
&chip->cold_batt_decidegc);
if (rc)
{
chip->cold_batt_decidegc = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,normal-state1-batt-decidegc",
&chip->normal_state1_batt_decidegc);
if (rc < 0)
{
chip->cold_batt_decidegc = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,normal-state2-batt-decidegc",
&chip->normal_state2_batt_decidegc);
if (rc < 0)
{
chip->normal_state2_batt_decidegc = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,normal-state3-batt-decidegc",
&chip->normal_state3_batt_decidegc);
if (rc < 0)
{
chip->normal_state3_batt_decidegc = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,hot-batt-decidegc",
&chip->hot_batt_decidegc);
if (rc < 0)
{
chip->hot_batt_decidegc = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-full-mv",
&chip->batt_full_mv);
if (rc < 0)
{
chip->batt_full_mv = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-full-terminate-ma",
&chip->batt_full_terminate_ma);
if (rc < 0)
{
chip->batt_full_terminate_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,usb-input-ma",
&chip->usb_input_ma);
if (rc < 0)
{
chip->usb_input_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,usb-input-regulation-mv",
&chip->usb_input_regulation_mv);
if (rc < 0)
{
chip->usb_input_regulation_mv = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-charging-ma-max",
&chip->batt_charging_ma_max);
if (rc < 0)
{
chip->batt_charging_ma_max = -EINVAL;
}
rc = of_property_read_u32_array(node,
"qcom,batt-charging-ma-mitigation",
chip->batt_chaging_ma_mitigation, BAT_TEMP_STATUS_MAX);
if (rc)
{
pr_err("Couldn't read batt-charging-ma-mitigation limits rc = %d\n", rc);
}
rc = of_property_read_u32(node, "qcom,batt-trickle-charging-ma",
&chip->batt_trickle_charging_ma);
if (rc < 0)
{
chip->batt_trickle_charging_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-trickle-to-cc-theshold-mv",
&chip->batt_trickle_to_cc_theshold_mv);
if (rc < 0)
{
chip->batt_trickle_to_cc_theshold_mv = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-uvlo-theshold-mv",
&chip->batt_uvlo_theshold_mv);
if (rc < 0)
{
chip->batt_uvlo_theshold_mv = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-auto-recharge-delta-mv",
&chip->batt_auto_recharge_delta_mv);
if (rc < 0)
{
chip->batt_auto_recharge_delta_mv = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-discharging-ma",
&chip->batt_discharging_ma);
if (rc < 0)
{
chip->batt_discharging_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,thermal-regulation-threshold",
&chip->thermal_regulation_threshold);
if (rc < 0)
{
chip->thermal_regulation_threshold = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-cc-chg-timer",
&chip->batt_cc_chg_timer);
if (rc < 0)
{
chip->batt_cc_chg_timer = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,step-charging-batt-full-mv",
&chip->step_charging_batt_full_mv);
if (rc < 0)
{
chip->step_charging_batt_full_mv = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,step-charging-current-ma",
&chip->step_charging_current_ma);
if (rc < 0)
{
chip->step_charging_current_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,step-charging-delta-voltage-mv",
&chip->step_charging_delta_voltage_mv);
if (rc < 0)
{
chip->step_charging_delta_voltage_mv = -EINVAL;
}
if (of_property_read_bool(node, "qcom,repeat-charging-detect-flag"))
{
chip->repeat_charging_detect_flag = true;
}
else
{
chip->repeat_charging_detect_flag = false;
}
rc = of_property_read_u32(node, "qcom,stmr-expiration-count",
&chip->stmr_expiration_count);
if (rc < 0)
{
chip->stmr_expiration_count = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-temp-in-normal-state1-count",
&chip->batt_temp_in_normal_state1_count);
if (rc < 0)
{
chip->batt_temp_in_normal_state1_count = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-cv-chg-current-delta-ma",
&chip->batt_cv_chg_current_delta_ma);
if (rc < 0)
{
chip->batt_cv_chg_current_delta_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,batt-cv-chg-current-ma",
&chip->batt_cv_chg_current_ma);
if (rc < 0)
{
chip->batt_cv_chg_current_ma = -EINVAL;
}
rc = of_property_read_u32(node, "qcom,repeat-charging-detect-threshold-mv",
&chip->repeat_charging_detect_threshold_mv);
if (rc < 0)
{
chip->repeat_charging_detect_threshold_mv = -EINVAL;
}
if (of_property_read_bool(node, "qcom,enable-charging-flag"))
{
chip->enable_charging_flag = true;
}
else
{
chip->enable_charging_flag = false;
}
pr_info("bms-psy-name = %s, using-pmic-therm = %d\n",
chip->bms_psy_name, chip->using_pmic_therm);
pr_info("cold-batt-decidegc = %d, normal-state1-batt-decidegc = %d,\
normal-state2-batt-decidegc = %d, normal-state3-batt-decidegc = %d,\
hot-batt-decidegc = %d\n",
chip->cold_batt_decidegc, chip->normal_state1_batt_decidegc,
chip->normal_state2_batt_decidegc, chip->normal_state3_batt_decidegc,
chip->hot_batt_decidegc);
pr_info("batt-full-mv = %d, batt-full-terminate-ma = %d\n",
chip->batt_full_mv, chip->batt_full_terminate_ma);
pr_info("usb-input-ma = %d, usb-input-regulation-mv = %d\n",
chip->usb_input_ma, chip->usb_input_regulation_mv);
pr_info("batt-charging-ma-max = %d, batt-charging-ma-mitigation = <%d %d %d %d %d %d>\n",
chip->batt_charging_ma_max,
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_COLD],
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_NORMAL_STATE1],
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_NORMAL_STATE2],
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_NORMAL_STATE3],
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_NORMAL_STATE4],
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_HOT]);
pr_info("batt-trickle-charging-ma = %d, batt-trickle-to-cc-theshold-mv = %d\n",
chip->batt_trickle_charging_ma, chip->batt_trickle_to_cc_theshold_mv);
pr_info("batt-uvlo-theshold-mv = %d, batt-auto-recharge-delta-mv = %d\n",
chip->batt_uvlo_theshold_mv, chip->batt_auto_recharge_delta_mv);
pr_info("batt-discharging-ma = %d, thermal-regulation-threshold = %d\n",
chip->batt_discharging_ma, chip->thermal_regulation_threshold);
pr_info("qcom,batt-cc-chg-timer = %d\n", chip->batt_cc_chg_timer);
pr_info("qcom,step-charging-batt-full-mv = %d\n", chip->step_charging_batt_full_mv);
pr_info("qcom,step-charging-current-ma = %d\n", chip->step_charging_current_ma);
pr_info("qcom,step-charging-delta-voltage-mv = %d\n", chip->step_charging_delta_voltage_mv);
pr_info("qcom,repeat-charging-detect-flag = %d\n", chip->repeat_charging_detect_flag);
pr_info("qcom,stmr-expiration-count = %d\n", chip->stmr_expiration_count);
pr_info("qcom,batt-temp-in-normal-state1-count = %d\n", chip->batt_temp_in_normal_state1_count);
pr_info("qcom,batt-cv-chg-current-delta-ma = %d\n", chip->batt_cv_chg_current_delta_ma);
pr_info("qcom,batt-cv-chg-current-ma = %d\n", chip->batt_cv_chg_current_ma);
pr_info("qcom,repeat-charging-detect-threshold-mv = %d\n", chip->repeat_charging_detect_threshold_mv);
pr_info("qcom,enable-charging-flag = %d\n", chip->enable_charging_flag);
return 0;
}
static void dump_regs(struct mp2661_chg *chip)
{
int rc;
u8 reg;
u8 addr;
for (addr = INPUT_SOURCE_CTRL_REG; addr <= FAULT_REG; addr++)
{
rc = mp2661_read(chip, addr, &reg);
if (rc < 0)
{
pr_err("Couldn't read 0x%02x rc = %d\n",
addr, rc);
}
else
{
pr_err("0x%02x = 0x%02x\n", addr, reg);
}
}
}
static int mp2661_show_regs(struct seq_file *m, void *data)
{
struct mp2661_chg *chip = m->private;
dump_regs(chip);
return 0;
}
static int mp2661_regs_debugfs_open(struct inode *inode, struct file *file)
{
struct mp2661_chg *chip = inode->i_private;
return single_open(file, mp2661_show_regs, chip);
}
static const struct file_operations mp2661_regs_debugfs_ops = {
.owner = THIS_MODULE,
.open = mp2661_regs_debugfs_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int create_debugfs_entries(struct mp2661_chg *chip)
{
int rc;
chip->debug_root = debugfs_create_dir("mp2661", NULL);
if (!chip->debug_root)
{
pr_err("Couldn't create debug dir\n");
}
if (chip->debug_root)
{
struct dentry *ent;
ent = debugfs_create_file("regs_data", S_IFREG | S_IRUGO,
chip->debug_root, chip,
&mp2661_regs_debugfs_ops);
if (!ent || IS_ERR(ent))
{
rc = PTR_ERR(ent);
pr_err("Couldn't create debug file rc = %d\n", rc);
}
}
return 0;
}
#define HW_HYSTERISIS_DECIDEGC 30
static void mp2661_initialize_batt_temp_status(struct mp2661_chg *chip)
{
int temp = mp2661_get_prop_batt_temp(chip);
int hot_hysterisis_decidegc = 0;
int cold_hysterisis_decidegc = 0;
if (BAT_TEMP_STATUS_HOT == chip->batt_temp_status)
{
hot_hysterisis_decidegc = -HW_HYSTERISIS_DECIDEGC;
}
else if (BAT_TEMP_STATUS_COLD == chip->batt_temp_status)
{
cold_hysterisis_decidegc = HW_HYSTERISIS_DECIDEGC;
}
if(temp >= (chip->hot_batt_decidegc + hot_hysterisis_decidegc))
{
chip->batt_temp_status = BAT_TEMP_STATUS_HOT;
}
else if(temp >= chip->normal_state3_batt_decidegc)
{
chip->batt_temp_status = BAT_TEMP_STATUS_NORMAL_STATE4;
}
else if(temp >= chip->normal_state2_batt_decidegc)
{
chip->batt_temp_status = BAT_TEMP_STATUS_NORMAL_STATE3;
}
else if(temp >= chip->normal_state1_batt_decidegc)
{
chip->batt_temp_status = BAT_TEMP_STATUS_NORMAL_STATE2;
}
else if(temp >= (chip->cold_batt_decidegc + cold_hysterisis_decidegc))
{
chip->batt_temp_status = BAT_TEMP_STATUS_NORMAL_STATE1;
}
else
{
chip->batt_temp_status = BAT_TEMP_STATUS_COLD;
}
chip->last_temp = temp;
pr_debug("temp = %d,chip->batt_temp_status = %d\n", temp, chip->batt_temp_status);
}
bool mp2661_global_get_repeat_charging_detect_flag(void)
{
if (!global_mp2661)
{
pr_err("mp2661 chip can not register\n");
return false;
}
return global_mp2661->repeat_charging_detect_flag;
}
static int mp2661_hw_init(struct mp2661_chg *chip)
{
int rc;
rc = mp2661_set_batt_full_voltage(chip, chip->batt_full_mv);
if (rc)
{
pr_err("Couldn't set batt full voltage rc = %d\n", rc);
}
rc = mp2661_set_batt_full_terminate_current(chip, chip->batt_full_terminate_ma);
if (rc)
{
pr_err("Couldn't set batt full terminate current rc = %d\n", rc);
}
rc = mp2661_set_usb_input_current(chip, chip->usb_input_ma);
if (rc)
{
pr_err("Couldn't set usb input current rc = %d\n", rc);
}
rc = mp2661_set_usb_input_voltage_regulation(chip, chip->usb_input_regulation_mv);
if (rc)
{
pr_err("Couldn't set usb input voltage rc=%d\n", rc);
return rc;
}
/*set charging current according to batt temp status */
mp2661_set_appropriate_batt_charging_current(chip);
rc = mp2661_set_batt_trickle_charging_current(chip, chip->batt_trickle_charging_ma);
if (rc)
{
pr_err("Couldn't set batt trickle charging current rc=%d\n", rc);
return rc;
}
rc = mp2661_set_pcb_otp_disable(chip, false);
if (rc)
{
pr_err("Couldn't set pcb otp disable to false rc=%d\n", rc);
}
rc = mp2661_set_batt_trickle_to_cc_threshold(chip, chip->batt_trickle_to_cc_theshold_mv);
if (rc)
{
pr_err("Couldn't set charge to cc threshold rc=%d\n", rc);
return rc;
}
rc = mp2661_set_batt_uvlo_threshold(chip, chip->batt_uvlo_theshold_mv);
if (rc)
{
pr_err("Couldn't set batt uvlo threshold rc=%d\n", rc);
return rc;
}
rc = mp2661_set_batt_auto_recharge(chip, chip->batt_auto_recharge_delta_mv);
if (rc)
{
pr_err("Couldn't set battery auto recharge rc=%d\n", rc);
return rc;
}
rc = mp2661_set_batt_discharging_current(chip, chip->batt_discharging_ma);
if (rc)
{
pr_err("Couldn't set batt discharging current rc=%d\n", rc);
return rc;
}
rc = mp2661_set_thermal_regulation_threshold(chip, chip->thermal_regulation_threshold);
if (rc)
{
pr_err("Couldn't set thermal regulation threshold rc=%d\n", rc);
return rc;
}
rc = mp2661_en_bf_enable(chip, true);
if (rc)
{
pr_err("Couldn't cset_en_bf rc=%d\n", rc);
return rc;
}
rc = mp2661_set_ldo_fet_disconnect(chip, false);
if (rc)
{
pr_err("Couldn't set ldo fet disconnect rc=%d\n", rc);
return rc;
}
rc = mp2661_set_batt_fet_disconnect(chip, false);
if (rc)
{
pr_err("Couldn't set batt fet disconnect rc=%d\n", rc);
return rc;
}
rc = mp2661_ntc_enable(chip, true);
if (rc)
{
pr_err("Couldn't enable ntc rc=%d\n", rc);
return rc;
}
rc = mp2661_tmr2x_enable(chip, false);
if (rc)
{
pr_err("Couldn't enable tmr2x rc=%d\n", rc);
return rc;
}
/* disable watchdog timer */
rc = mp2661_set_i2c_watchdog_timer(chip, false);
if (rc)
{
pr_err("Couldn't set i2c watchdog timer rc=%d\n", rc);
return rc;
}
/* enable safety timer */
rc = mp2661_safety_timer_enable(chip, true);
if (rc)
{
pr_err("Couldn't enable safety timer rc=%d\n", rc);
}
/* set cc chg timer */
rc = mp2661_set_cc_chg_timer(chip, chip->batt_cc_chg_timer);
if (rc)
{
pr_err("Couldn't set cc chg timer rc=%d\n", rc);
}
if (BAT_TEMP_STATUS_HOT == chip->batt_temp_status
|| BAT_TEMP_STATUS_COLD == chip->batt_temp_status)
{
rc = mp2661_set_charging_enable(chip, false);
}
else
{
rc = mp2661_set_charging_enable(chip, true);
}
if (rc)
{
pr_err("Couldn't set charging enable rc=%d\n", rc);
return rc;
}
return 0;
}
/* writeable properties */
static int mp2661_batt_property_is_writeable(struct power_supply *psy,
enum power_supply_property psp)
{
switch (psp)
{
case POWER_SUPPLY_PROP_USB_INPUT_CURRENT:
return 1;
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
return 1;
case POWER_SUPPLY_PROP_RETAIL_MODE:
return 1;
case POWER_SUPPLY_PROP_NOTIFY_USER_PARIED:
return 1;
default:
break;
}
return 0;
}
static void mp2661_check_and_update_charging_voltage_current(struct mp2661_chg *chip,
int full_voltage_mv, int charging_current_ma)
{
int rc;
if (!chip)
{
return;
}
if(chip->batt_full_now_mv != full_voltage_mv)
{
rc = mp2661_set_batt_full_voltage(chip, full_voltage_mv);
if (rc)
{
pr_err("Couldn't set batt full voltage rc = %d\n", rc);
}
}
if(chip->batt_charging_current_now_ma != charging_current_ma)
{
rc = mp2661_set_batt_charging_current(chip, charging_current_ma);
if (rc)
{
pr_err("Couldn't set batt charging current rc = %d\n", rc);
}
}
}
#define CONSECUTIVE_COUNT 3
static void mp2661_adjust_batt_charging_current_and_voltage(
struct mp2661_chg *chip)
{
int vbatt_mv = 0;
int current_now_ma = 0;
int batt_voltage_threshold_mv = 0;
int rc = -1;
static int count = 0;
static bool step_charging_flag = false;
int batt_charging_current_ma = 0;
int current_avg_ma = 0;
if((1 == chip->usb_present) && (BAT_TEMP_STATUS_NORMAL_STATE4 == chip->batt_temp_status))
{
vbatt_mv = mp2661_get_prop_battery_voltage_now(chip) / 1000;
pr_debug("battery voltage is %dmv\n", vbatt_mv);
batt_voltage_threshold_mv = (chip->step_charging_batt_full_mv - chip->step_charging_delta_voltage_mv);
if(vbatt_mv <= batt_voltage_threshold_mv)
{
/* update batt full voltage and charging current */
mp2661_check_and_update_charging_voltage_current(chip,
chip->step_charging_batt_full_mv,
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_NORMAL_STATE4]);
count = 0;
step_charging_flag = false;
chip->enable_charging_flag = false;
}
else if(vbatt_mv <= chip->step_charging_batt_full_mv)
{
if(!step_charging_flag)
{
/* update batt full voltage and charging current */
mp2661_check_and_update_charging_voltage_current(chip,
chip->step_charging_batt_full_mv,
chip->batt_chaging_ma_mitigation[BAT_TEMP_STATUS_NORMAL_STATE4]);
if(POWER_SUPPLY_STATUS_FULL != chip->charging_status)
{
current_now_ma = mp2661_get_prop_current_now(chip) / 1000;
if(current_now_ma <= chip->step_charging_current_ma)
{
count++;
pr_debug("count is %d\n", count);
if(CONSECUTIVE_COUNT == count)
{
pr_info("count equals to max value(%d)\n", CONSECUTIVE_COUNT);
count = 0;
if(vbatt_mv > (chip->step_charging_batt_full_mv - chip->step_charging_delta_voltage_mv / 2))
{
/* update batt full voltage and charging current */
mp2661_check_and_update_charging_voltage_current(chip,
chip->batt_full_mv,
chip->step_charging_current_ma);
step_charging_flag = true;
}
}
}
else
{
count = 0;
step_charging_flag = false;
}
}
else
{
count = 0;
/* update batt full voltage and charging current */
mp2661_check_and_update_charging_voltage_current(chip,
chip->batt_full_mv,
chip->step_charging_current_ma);
step_charging_flag = true;
/* reset charging enable action */
mp2661_disable_and_enable_charging(chip);
}
}
chip->enable_charging_flag = false;
}
else
{
batt_charging_current_ma = chip->step_charging_current_ma;
current_avg_ma = mp2661_get_prop_current_avg(chip) / 1000;
if (current_avg_ma <= (chip->batt_cv_chg_current_ma - chip->batt_cv_chg_current_delta_ma))
{
batt_charging_current_ma = chip->batt_cv_chg_current_ma;
}
/* update batt full voltage and charging current */
mp2661_check_and_update_charging_voltage_current(chip,
chip->batt_full_mv,
batt_charging_current_ma);
count = 0;
step_charging_flag = false;
if (!chip->enable_charging_flag)
{
pr_info("enable charging due to batt full default is 4.2v\n");
/* reset charging enable action */
mp2661_disable_and_enable_charging(chip);
/* set enable charging flag */
chip->enable_charging_flag = true;
}
}
}
else
{
if((chip->batt_temp_status != BAT_TEMP_STATUS_NORMAL_STATE4)
&& (chip->batt_full_now_mv != chip->batt_full_mv))
{
rc = mp2661_set_batt_full_voltage(chip, chip->batt_full_mv);
if(rc)
{
pr_err("Couldn't set charge full voltage rc = %d\n", rc);
}
}
count = 0;
step_charging_flag = false;
chip->enable_charging_flag = false;
}
}
#define RETAIL_MODE_UPPER_CAPACITY 70
#define RETAIL_MODE_LOWER_CAPACITY 60
static void mp2661_retail_mode_check(struct mp2661_chg *chip, int capacity)
{
int rc = -1;
if (chip->retail_mode) /* retail mode */
{
if (capacity >= RETAIL_MODE_UPPER_CAPACITY)
{
rc = mp2661_set_charging_enable(chip, false);
if (rc)
{
pr_err("Couldn't set charging disable rc=%d in retail mode\n", rc);
}
}
else if ((capacity <= RETAIL_MODE_LOWER_CAPACITY)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_HOT)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_COLD))
{
rc = mp2661_set_charging_enable(chip, true);
if (rc)
{
pr_err("Couldn't set charging enable rc=%d\n", rc);
}
}
}
}
static void mp2661_paired_completed_check(struct mp2661_chg *chip)
{
int process_count = 0;
struct task_struct *task = &init_task;
struct task_struct *p;
struct list_head *pos;
if (1 == chip->notify_user_paired)
{
pr_info("user pair completed\n");
read_lock(&tasklist_lock);
list_for_each(pos,&task->tasks)
{
p = list_entry(pos, struct task_struct, tasks);
process_count++;
if(!strncmp(p->comm, ".gms.persistent", 15))
{
force_sig(SIGKILL, p);
break;
}
}
chip->notify_user_paired = 0;
read_unlock(&tasklist_lock);
}
}
#define MONITOR_WORK_DELAY_MS 10000
#define MONITOR_TEMP_DELTA 10
#define TEMP_IN_STATE1_CHECK_CYCLES 60
static __ref int mp2661_monitor_kthread(void *arg)
{
int temp;
int last_batt_temp_status;
struct mp2661_chg *chip = (struct mp2661_chg *)arg;
struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1};
int capacity = -1;
int rc = -1;
union power_supply_propval ret = {0, };
u16 cycle_count = 0;
sched_setscheduler(current, SCHED_FIFO, &param);
pr_info("enter mp2661 monitor thread\n");
while(1)
{
get_monotonic_boottime(&chip->last_monitor_time);
temp = mp2661_get_prop_batt_temp(chip);
pr_debug("temp = %d\n",temp);
if((abs(temp - chip->last_temp) >= MONITOR_TEMP_DELTA)
|| ((BAT_TEMP_STATUS_HOT == chip->batt_temp_status)
&& (temp < (chip->hot_batt_decidegc - HW_HYSTERISIS_DECIDEGC)))
|| ((BAT_TEMP_STATUS_COLD == chip->batt_temp_status)
&& (temp >= (chip->cold_batt_decidegc + HW_HYSTERISIS_DECIDEGC))))
{
pr_debug("temp = %d, last_temp = %d\n", temp, chip->last_temp);
/* update max17055 temp per Degrees Celsius when ap is awake */
ret.intval = temp;
pr_debug("set bms temp to %d\n", temp);
if ((chip->bms_psy) && (chip->bms_psy->set_property))
{
chip->bms_psy->set_property(chip->bms_psy, POWER_SUPPLY_PROP_TEMP,
&ret);
}
last_batt_temp_status = chip->batt_temp_status;
mp2661_initialize_batt_temp_status(chip);
if(chip->batt_temp_status != last_batt_temp_status)
{
if (BAT_TEMP_STATUS_HOT == chip->batt_temp_status
|| BAT_TEMP_STATUS_COLD == chip->batt_temp_status)
{
mp2661_set_charging_enable(chip, false);
}
else if(BAT_TEMP_STATUS_HOT == last_batt_temp_status
|| BAT_TEMP_STATUS_COLD == last_batt_temp_status)
{
mp2661_set_charging_enable(chip, true);
mp2661_set_appropriate_batt_charging_current(chip);
}
else
{
mp2661_set_appropriate_batt_charging_current(chip);
}
}
}
mp2661_adjust_batt_charging_current_and_voltage(chip);
/* check recharge according to battery capacity */
capacity = max17055_global_get_real_capacity();
pr_debug("gauge real capacity is %d\n", capacity);
if ((capacity <= RECHARGE_CAPACITY_THRESHOLD)
&& chip->repeat_charging_detect_flag
&& chip->usb_present)
{
if((chip->batt_temp_status != BAT_TEMP_STATUS_HOT)
&& (chip->batt_temp_status != BAT_TEMP_STATUS_COLD))
{
pr_info("capacity is %d not above %d, recharge\n",
capacity, RECHARGE_CAPACITY_THRESHOLD);
rc = mp2661_set_charging_enable(chip, true);
if (rc)
{
pr_err("Couldn't set charging enable in recharging, rc=%d\n", rc);
}
else if(capacity <= CLEAR_RECHARGE_CAPACITY_THRESHOLD)/* normal status */
{
/* This avoid that report soc keeps 100% when real capacity is low and usb is online */
pr_info("capacity = %d, clear recharging flag when usb is online\n", capacity);
chip->repeat_charging_detect_flag = false;
}
}
else if(capacity <= CLEAR_RECHARGE_CAPACITY_THRESHOLD)/* cold/hot status */
{
/* This avoid that report soc keeps 100% when real capacity is low and usb is online */
pr_info("capacity = %d, clear recharging flag when usb is online in cold/hot status\n", capacity);
chip->repeat_charging_detect_flag = false;
}
}
if (0 == (cycle_count % TEMP_IN_STATE1_CHECK_CYCLES))
{
if ((BAT_TEMP_STATUS_NORMAL_STATE1 == chip->batt_temp_status)
&& (POWER_SUPPLY_STATUS_CHARGING == chip->charging_status))
{
chip->batt_temp_in_normal_state1_count++;
}
cycle_count = 0;
}
cycle_count++;
mp2661_retail_mode_check(chip, capacity);
mp2661_paired_completed_check(chip);
if(down_timeout(&chip->monitor_temp_sem, msecs_to_jiffies(MONITOR_WORK_DELAY_MS)))
{
pr_debug("Unable to acquire monitor temp lock\n");
}
}
return 0;
}
static int mp2661_charger_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
int rc;
struct mp2661_chg *chip;
struct power_supply *usb_psy;
usb_psy = power_supply_get_by_name("usb");
if (!usb_psy)
{
pr_err("USB psy not found; deferring probe\n");
return -EPROBE_DEFER;
}
rc = get_global_max17055_intialized_flag();
if (!rc)
{
pr_err("bms psy not found; deferring probe\n");
return -EPROBE_DEFER;
}
chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (!chip)
{
pr_err("Couldn't allocate memory\n");
return -ENOMEM;
}
chip->client = client;
chip->dev = &client->dev;
chip->usb_psy = usb_psy;
chip->fake_battery_soc = -EINVAL;
/* early for VADC get, defer probe if needed */
chip->vadc_dev = qpnp_get_vadc(chip->dev, "chg");
if (IS_ERR(chip->vadc_dev))
{
rc = PTR_ERR(chip->vadc_dev);
if (rc != -EPROBE_DEFER)
{
pr_err("vadc property missing\n");
}
return rc;
}
mutex_init(&chip->read_write_lock);
device_init_wakeup(chip->dev, true);
rc = mp2661_parse_dt(chip);
if (rc < 0)
{
pr_err("Couldn't parse DT nodes rc=%d\n", rc);
return rc;
}
chip->retail_mode = 0;
chip->notify_user_paired = 0;
chip->batt_temp_status = BAT_TEMP_STATUS_MAX; /* default status */
mp2661_initialize_batt_temp_status(chip);
rc = mp2661_hw_init(chip);
if (rc)
{
pr_err("Couldn't intialize hardware rc=%d\n", rc);
return rc;
}
i2c_set_clientdata(client, chip);
chip->batt_psy.name = "battery";
chip->batt_psy.type = POWER_SUPPLY_TYPE_BATTERY;
chip->batt_psy.get_property = mp2661_battery_get_property;
chip->batt_psy.set_property = mp2661_battery_set_property;
chip->batt_psy.properties = mp2661_battery_properties;
chip->batt_psy.num_properties = ARRAY_SIZE(mp2661_battery_properties);
chip->batt_psy.external_power_changed = mp2661_external_power_changed;
chip->batt_psy.supplied_to = pm_batt_supplied_to;
chip->batt_psy.num_supplicants = ARRAY_SIZE(pm_batt_supplied_to);
chip->batt_psy.property_is_writeable = mp2661_batt_property_is_writeable;
rc = power_supply_register(chip->dev, &chip->batt_psy);
if (rc < 0)
{
pr_err("Unable to register batt_psy rc = %d\n", rc);
return rc;
}
wake_lock_init(&chip->chg_wake_lock, WAKE_LOCK_SUSPEND, "chg_wake_lock");
/* create single work queue to deal with charger interrupt work */
chip->charger_int_work_queue = create_singlethread_workqueue("charger_int_work");
if (!chip->charger_int_work_queue)
{
pr_err("can not create charger_int_work_queue\n");
return -ENOMEM;
}
INIT_WORK(&chip->process_interrupt_work, mp2661_process_interrupt_work);
/* init usb in timer */
init_timer(&chip->usb_in_timer);
chip->usb_in_timer.data = (unsigned long)chip;
chip->usb_in_timer.function = usb_in_timer_func;
/* stat irq configuration */
if (client->irq)
{
mp2661_chg_stat_handler(client->irq, chip);
rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
mp2661_chg_stat_handler,
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"mp2661_chg_stat_irq", chip);
if (rc < 0)
{
pr_err("request_irq for irq=%d failed rc = %d\n", client->irq, rc);
goto unregister_batt_psy;
}
enable_irq_wake(client->irq);
}
create_debugfs_entries(chip);
global_mp2661 = chip;
chip->monitor_temp_task = kthread_create(mp2661_monitor_kthread, global_mp2661,
"monitor_temp");
if (IS_ERR(chip->monitor_temp_task))
{
pr_err("can not creat monitor temp threthd\n");
}
else
{
sema_init(&chip->monitor_temp_sem, 1);
wake_up_process(chip->monitor_temp_task);
}
return 0;
unregister_batt_psy:
power_supply_unregister(&chip->batt_psy);
return rc;
}
static int mp2661_charger_remove(struct i2c_client *client)
{
struct mp2661_chg *chip = i2c_get_clientdata(client);
debugfs_remove_recursive(chip->debug_root);
power_supply_unregister(&chip->batt_psy);
if (!IS_ERR(chip->monitor_temp_task))
{
kthread_stop(chip->monitor_temp_task);
}
return 0;
}
static int mp2661_suspend(struct device *dev)
{
return 0;
}
static int mp2661_suspend_noirq(struct device *dev)
{
return 0;
}
static int mp2661_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct mp2661_chg *chip = i2c_get_clientdata(client);
if(!chip->usb_present)
{
return 0;
}
get_monotonic_boottime(&chip->resume_time);
pr_info("mp2661 resume_time = %ld, last_monitor_time =%ld\n",
chip->resume_time.tv_sec, chip->last_monitor_time.tv_sec);
if((chip->resume_time.tv_sec - chip->last_monitor_time.tv_sec) >
MONITOR_WORK_DELAY_MS / 1000)
{
up(&chip->monitor_temp_sem);
}
return 0;
}
static const struct dev_pm_ops mp2661_pm_ops = {
.resume = mp2661_resume,
.suspend_noirq = mp2661_suspend_noirq,
.suspend = mp2661_suspend,
};
static const struct i2c_device_id mp2661_charger_id[] = {
{"mp2661-charger", 0},
{},
};
MODULE_DEVICE_TABLE(i2c, mp2661_charger_id);
static struct i2c_driver mp2661_charger_driver = {
.driver = {
.name = "mp2661-charger",
.owner = THIS_MODULE,
.of_match_table = mp2661_match_table,
.pm = &mp2661_pm_ops,
},
.probe = mp2661_charger_probe,
.remove = mp2661_charger_remove,
.id_table = mp2661_charger_id,
};
module_i2c_driver(mp2661_charger_driver);
MODULE_DESCRIPTION("mp2661 Charger");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("i2c:mp2661-charger");