unplugged-kernel/drivers/power/supply/mediatek/charger/mm8013.c

535 lines
12 KiB
C

/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/module.h>
#include <linux/param.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/idr.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
#include <mt-plat/v1/mtk_battery.h>
#include "mtk_battery_internal.h"
#include <linux/time.h>
#define DRIVER_VERSION "1.0.0"
#define REG_CONTROL 0x00
#define REG_TEMPERATURE 0x06
#define REG_VOLTAGE 0x08
#define REG_FLAGS 0x0a
#define REG_CURRENT_AVG 0x14
#define REG_CYCLECOUNT 0x2a
#define REG_SOC 0x2c
#define REG_CHARGEVOLT 0x30
#define REG_CURRENT 0x72
#define REG_BLOCKDATAOFFSET 0x3e
#define REG_BLOCKDATA 0x40
struct mm8013_chip {
struct i2c_client *client;
struct delayed_work battery_update_work;
struct mutex i2c_rw_lock;
};
struct mm8013_chip *_chip;
static int mm8013_write_reg(struct i2c_client *client, u8 reg, u16 value)
{
int ret;
struct mm8013_chip *chip = i2c_get_clientdata(client);
mutex_lock(&chip->i2c_rw_lock);
mdelay(4);
ret = i2c_smbus_write_word_data(client, reg, value);
if (ret < 0)
dev_info(&client->dev, "%s: err %d\n", __func__, ret);
mutex_unlock(&chip->i2c_rw_lock);
return ret;
}
static int mm8013_read_reg(struct i2c_client *client, u8 reg)
{
int ret;
struct mm8013_chip *chip = i2c_get_clientdata(client);
mutex_lock(&chip->i2c_rw_lock);
mdelay(4);
ret = i2c_smbus_read_word_data(client, reg);
if (ret < 0)
dev_info(&client->dev, "%s: err %d\n", __func__, ret);
mutex_unlock(&chip->i2c_rw_lock);
return ret;
}
static int mm8013_soc(struct mm8013_chip *chip, int *val)
{
int soc = mm8013_read_reg(chip->client, REG_SOC);
if (soc < 0)
return soc;
if (soc > 100)
soc = 100;
*val = soc;
return 0;
}
static int mm8013_voltage(struct mm8013_chip *chip, int *val)
{
int volt = mm8013_read_reg(chip->client, REG_VOLTAGE);
if (volt < 0)
return volt;
*val = volt;
return 0;
}
static int mm8013_current_avg(struct mm8013_chip *chip, int *val)
{
int curr = mm8013_read_reg(chip->client, REG_CURRENT_AVG);
if (curr < 0)
return curr;
if (curr > 32767)
curr -= 65536;
*val = curr*10;
return 0;
}
static int mm8013_current(struct mm8013_chip *chip, int *val)
{
int curr = mm8013_read_reg(chip->client, REG_CURRENT);
if (curr < 0)
return curr;
if (curr > 32767)
curr -= 65536;
*val = curr*10;
return 0;
}
static int mm8013_temperature(struct mm8013_chip *chip, int *val)
{
int temp = mm8013_read_reg(chip->client, REG_TEMPERATURE);
if (temp < 0)
return temp;
*val = temp - 2731;
return 0;
}
static int mm8013_cyclecount(struct mm8013_chip *chip, int *val)
{
int count = mm8013_read_reg(chip->client, REG_CYCLECOUNT);
if (count < 0)
return count;
*val = count;
return 0;
}
static int mm8013_fgstatus(struct mm8013_chip *chip)
{
int ret;
int st;
int cycle;
int volt;
//bool bat_maintain;
int req = 0;
//Full Charge bit check
ret = mm8013_read_reg(chip->client, REG_FLAGS);
if (ret < 0) {
dev_info(&chip->client->dev, "[%s] get REG_FLAGS failed!\n", __func__);
return ret;
}
st = ret & 0x0200;
dev_info(&chip->client->dev, "[%s] get REG_FLAGS<%.4x>!\n", __func__, st);
if (st != 0x0200)
return ret;
//Rtc battery maintain flag check
//ret = rtc_get_bat_maintain(&bat_maintain);
//if (ret) {
// dev_info(&chip->client->dev, "[%s] get RTC bat maintain flag failed!\n", __func__);
// return ret;
//}
//dev_info(&chip->client->dev, "[%s] bat maintain flag<%d/%s>!\n", __func__,
// bat_maintain, bat_maintain?"enable":"disable");
//if (!bat_maintain) {
// return 0;
//}
//CycleCount check
ret = mm8013_read_reg(chip->client, REG_CYCLECOUNT);
if (ret < 0) {
dev_info(&chip->client->dev, "[%s] get REG_CYCLECOUNT failed!\n", __func__);
return ret;
}
cycle = ret;
dev_info(&chip->client->dev, "[%s] get REG_CYCLECOUNT<%d>!\n", __func__, cycle);
//ChargeVoltage check
ret = mm8013_read_reg(chip->client, REG_CHARGEVOLT);
if (ret < 0) {
dev_info(&chip->client->dev, "[%s] get REG_CHARGEVOLT failed!\n", __func__);
return ret;
}
volt = ret;
dev_info(&chip->client->dev, "[%s] get REG_CHARGEVOLT<%d>!\n", __func__, ret);
if (cycle < 150) {
if (volt != 4430)
req = 4430;
} else if (cycle < 400) {
if (volt != 4380)
req = 4380;
} else {
if (volt != 4230)
req = 4230;
}
if (req != 0) {
ret = mm8013_write_reg(chip->client, REG_CHARGEVOLT, req);
if (ret < 0) {
dev_info(&chip->client->dev, "[%s] set REG_CHARGEVOLT<%d> failed!\n",
__func__, ret);
return ret;
}
ret = mm8013_read_reg(chip->client, REG_CHARGEVOLT);
if (ret < 0) {
dev_info(&chip->client->dev, "[%s] get REG_CHARGEVOLT<%d> failed!\n",
__func__, ret);
return ret;
}
dev_info(&chip->client->dev, "[%s] update REG_CHARGEVOLT<req:%d ret:%d>!\n",
__func__, req, ret);
if (ret != req)
return ret;
}
return 0;
}
static int mm8013_checkdevice(struct mm8013_chip *chip)
{
int ret;
ret = mm8013_write_reg(chip->client, 0x0, 0x0008);
if (ret < 0)
return ret;
ret = mm8013_read_reg(chip->client, 0x0);
if (ret < 0)
return ret;
if (ret == 0x0102) {
ret = 2;
/* hardwareinfo_set_prop(HARDWARE_BATTERY_ID, "P98980AA1_SCUD_4V35_7500MAH"); */
} else if (ret == 0x0101) {
ret = 1;
/* hardwareinfo_set_prop(HARDWARE_BATTERY_ID, "P98980AA1_SUNWODA_4V35_7500MAH"); */
} else {
ret = 0;
}
dev_info(&chip->client->dev, "%s battery id:%d\n", __func__, ret);
return ret;
}
int mm8013_get_info(enum power_supply_property info_type, int *val)
{
int ret;
if (!_chip)
return -1;
switch (info_type) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
ret = mm8013_voltage(_chip, val);
break;
case POWER_SUPPLY_PROP_CAPACITY:
ret = mm8013_soc(_chip, val);
break;
case POWER_SUPPLY_PROP_CYCLE_COUNT:
ret = mm8013_cyclecount(_chip, val);
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
ret = mm8013_current(_chip, val);
break;
case POWER_SUPPLY_PROP_CURRENT_AVG:
ret = mm8013_current_avg(_chip, val);
break;
case POWER_SUPPLY_PROP_TEMP:
ret = mm8013_temperature(_chip, val);
break;
default:
ret = -1;
break;
}
return ret;
}
static int force_get_tbat_mm8013(bool update, int *temp)
{
static int pre_bat_temp = -50;
static struct timespec pre_time;
struct timespec ctime, dtime;
int bat_temp;
int ret;
if (update || pre_bat_temp == -50) {
ret = mm8013_get_info(POWER_SUPPLY_PROP_TEMP, &bat_temp);
if (ret)
return ret;
if (pre_bat_temp == -50) {
get_monotonic_boottime(&pre_time);
} else {
get_monotonic_boottime(&ctime);
dtime = timespec_sub(ctime, pre_time);
if (((dtime.tv_sec <= 20) &&
(abs(pre_bat_temp-bat_temp) >= 50)) || bat_temp >= 580) {
bm_err("[%s] battery temperature %d, raise from %d in %d sec!\n",
__func__, bat_temp, pre_bat_temp, dtime.tv_sec);
WARN_ON_ONCE(1);
}
}
pre_bat_temp = bat_temp;
*temp = bat_temp;
pre_time = ctime;
} else {
*temp = pre_bat_temp;
}
return 0;
}
/* keep 100% after battery full, and hold until battery voltage
* below 4250mV. To avoid capacity jump, only 1% change is
* allowed in 1 minute.
*/
#if defined(BAT_CAPACITY_ADJUST)
bool battery_adjust_capacity(int *capacity)
{
static bool keep_full;
static int last_capacity = -1;
static struct timespec pre_time;
struct timespec ctime, dtime;
int avg_value;
if (battery_main.BAT_STATUS == POWER_SUPPLY_STATUS_FULL
&& *capacity == 100) {
keep_full = 1;
} else if (battery_main.BAT_STATUS == POWER_SUPPLY_STATUS_CHARGING) {
if (keep_full && battery_main.BAT_batt_vol > 4250)
*capacity = 100;
else if (keep_full && battery_main.BAT_batt_vol <= 4250)
keep_full = 1;
} else {
keep_full = 1;
}
if (last_capacity == -1) {
get_monotonic_boottime(&pre_time);
last_capacity = *capacity;
} else if (keep_full) {
get_monotonic_boottime(&pre_time);
last_capacity = 100;
} else if (last_capacity != *capacity) {
get_monotonic_boottime(&ctime);
dtime = timespec_sub(ctime, pre_time);
if (battery_main.BAT_STATUS == POWER_SUPPLY_STATUS_FULL && *capacity < 100) {
if (dtime.tv_sec >= 10) {
*capacity = last_capacity+(last_capacity < 100?1:0);
pre_time = ctime;
last_capacity = *capacity;
} else {
*capacity = last_capacity;
}
} else {
if (dtime.tv_sec >= 60) {
mm8013_current_avg(_chip, &avg_value);
if (avg_value < 0 && last_capacity > *capacity) {
*capacity = last_capacity - 1;
} else if (battery_main.BAT_STATUS == POWER_SUPPLY_STATUS_CHARGING
&& *capacity > last_capacity) {
*capacity = last_capacity + 1;
} else {
*capacity = last_capacity;
}
pre_time = ctime;
last_capacity = *capacity;
} else {
*capacity = last_capacity;
}
}
}
return false;
}
#endif
static void mm8013_battery_update_work(struct work_struct *work)
{
struct mm8013_chip *chip = container_of(to_delayed_work(work),
struct mm8013_chip, battery_update_work);
int ret;
int battery_temp = 0, cycle_count = 0, capacity = 0;
bm_notice("[%s] enter\n", __func__);
ret = force_get_tbat_mm8013(1, &battery_temp);
if (ret) {
bm_err("[%s] force_get_tbat_mm8013 fail, ret(%d)\n", __func__, ret);
battery_temp = 500;
}
gm.fixed_bat_tmp = (battery_temp / 10);
battery_main.BAT_batt_temp = gm.fixed_bat_tmp;
if (!mm8013_get_info(POWER_SUPPLY_PROP_CYCLE_COUNT, &cycle_count))
gm.bat_cycle = cycle_count;
if (!mm8013_get_info(POWER_SUPPLY_PROP_CAPACITY, &capacity)) {
#if defined(BAT_CAPACITY_ADJUST)
battery_adjust_capacity(&capacity);
#endif
battery_main.BAT_CAPACITY = capacity;
gm.ui_soc = capacity;
gm.fixed_uisoc = capacity;
gm.soc = capacity;
if (capacity >= 100)
gm.is_force_full = 1;
}
bm_notice("ftmp(%d),btmp(%d),cycle(%d),ui_soc(%d),fixed_uisoc(%d),soc(%d)\n",
gm.fixed_bat_tmp, battery_main.BAT_batt_temp, gm.bat_cycle,
gm.ui_soc, gm.fixed_uisoc, gm.soc);
battery_update(&battery_main);
mm8013_fgstatus(chip);
schedule_delayed_work(&chip->battery_update_work, msecs_to_jiffies(5*1000));
}
static int mm8013_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct mm8013_chip *chip;
int ret;
dev_info(&client->dev, "[%s] enter, start\n", __func__);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) {
dev_info(&client->dev, "[%s] not support i2c word ops!\n", __func__);
return -EIO;
}
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
if (!chip)
return -ENOMEM;
mutex_init(&chip->i2c_rw_lock);
chip->client = client;
i2c_set_clientdata(client, chip);
ret = mm8013_checkdevice(chip);
if (ret < 0) {
dev_info(&client->dev, "[%s] failed to access\n", __func__);
if (ret == -EREMOTEIO)
ret = -EPROBE_DEFER;
return ret;
}
switch (ret) {
case 1:
dev_info(&client->dev, "%s battery id:1\n", __func__);
break;
case 2:
dev_info(&client->dev, "%s battery id:2\n", __func__);
break;
default:
dev_info(&client->dev, "%s battery id:0\n", __func__);
break;
}
_chip = chip;
INIT_DELAYED_WORK(&chip->battery_update_work, mm8013_battery_update_work);
schedule_delayed_work(&chip->battery_update_work, msecs_to_jiffies(5*1000));
dev_info(&client->dev, "%s ok\n", __func__);
return 0;
}
static int mm8013_remove(struct i2c_client *client)
{
struct mm8013_chip *chip = i2c_get_clientdata(client);
i2c_set_clientdata(client, NULL);
kfree(chip);
return 0;
}
static void mm8013_shutdown(struct i2c_client *client)
{
int ret;
ret = mm8013_write_reg(client, REG_CONTROL, 0x0013);
if (ret < 0)
return;
dev_info(&client->dev, "%s shutdown\n", __func__, ret);
}
static const struct i2c_device_id mm8013_id[] = {
{ "mm8013", 0 },
{},
};
MODULE_DEVICE_TABLE(i2c, mm8013_id);
static const struct of_device_id mm8013_match_table[] = {
{ .compatible = "mediatek,mm8013",},
{ },
};
static struct i2c_driver mm8013_i2c_driver = {
.driver = {
.name = "mm8013",
.of_match_table = mm8013_match_table,
},
.probe = mm8013_probe,
.remove = mm8013_remove,
.id_table = mm8013_id,
.shutdown = mm8013_shutdown,
};
static inline int mm8013_i2c_init(void)
{
return i2c_add_driver(&mm8013_i2c_driver);
}
static int __init mm8013_init(void)
{
return mm8013_i2c_init();
}
module_init(mm8013_init);
static void __exit mm8013_exit(void)
{
i2c_del_driver(&mm8013_i2c_driver);
}
module_exit(mm8013_exit);
MODULE_AUTHOR("qilong.zhang@archmind.com");
MODULE_DESCRIPTION("I2C bus driver for mm8013X gauge");
MODULE_LICENSE("GPL v2");