diff options
| author | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2017-08-30 08:25:59 +0000 |
| commit | 8e4bff4cab0ddac6060645b0715210484d02ff40 (patch) | |
| tree | 3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/battery/sprd27x3_fuelgauge4samsung.c | |
Diffstat (limited to 'drivers/battery/sprd27x3_fuelgauge4samsung.c')
| -rwxr-xr-x | drivers/battery/sprd27x3_fuelgauge4samsung.c | 357 |
1 files changed, 357 insertions, 0 deletions
diff --git a/drivers/battery/sprd27x3_fuelgauge4samsung.c b/drivers/battery/sprd27x3_fuelgauge4samsung.c new file mode 100755 index 00000000..ced830ae --- /dev/null +++ b/drivers/battery/sprd27x3_fuelgauge4samsung.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2013 Spreadtrum Communications Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <soc/sprd/hardware.h> +#include <soc/sprd/sci.h> +#include <soc/sprd/sci_glb_regs.h> +#include <linux/io.h> +#include <soc/sprd/adi.h> +#include <linux/wakelock.h> +#include <linux/hrtimer.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/battery/sec_fuelgauge.h> +#include <linux/sprd_battery_common.h> + +int sprdbat_interpolate(int x, int n, struct sprdbat_table_data *tab) +{ + int index; + int y; + + if (x >= tab[0].x) + y = tab[0].y; + else if (x <= tab[n - 1].x) + y = tab[n - 1].y; + else { + /* find interval */ + for (index = 1; index < n; index++) + if (x > tab[index].x) + break; + /* interpolate */ + y = (tab[index - 1].y - tab[index].y) * (x - tab[index].x) + * 2 / (tab[index - 1].x - tab[index].x); + y = (y + 1) / 2; + y += tab[index].y; + } + return y; +} +static void print_pdata(struct sprd_battery_platform_data *pdata) +{ +#define PDATA_LOG(format, arg...) printk("sprdbat pdata: " format, ## arg) + int i; + + + PDATA_LOG("chg_bat_safety_vol:%d\n", pdata->chg_bat_safety_vol); + + PDATA_LOG("irq_fgu:%d\n", pdata->irq_fgu); + PDATA_LOG("fgu_mode:%d\n", pdata->fgu_mode); + PDATA_LOG("alm_soc:%d\n", pdata->alm_soc); + PDATA_LOG("alm_vol:%d\n", pdata->alm_vol); + PDATA_LOG("soft_vbat_uvlo:%d\n", pdata->soft_vbat_uvlo); + PDATA_LOG("soft_vbat_ovp:%d\n", pdata->soft_vbat_ovp); + PDATA_LOG("rint:%d\n", pdata->rint); + PDATA_LOG("cnom:%d\n", pdata->cnom); + PDATA_LOG("rsense_real:%d\n", pdata->rsense_real); + PDATA_LOG("rsense_spec:%d\n", pdata->rsense_spec); + PDATA_LOG("relax_current:%d\n", pdata->relax_current); + PDATA_LOG("fgu_cal_ajust:%d\n", pdata->fgu_cal_ajust); + PDATA_LOG("qmax_update_period:%d\n", pdata->qmax_update_period); + PDATA_LOG("ocv_tab_size:%d\n", pdata->ocv_tab_size); + for (i = 0; i < pdata->ocv_tab_size; i++) { + PDATA_LOG("ocv_tab i=%d x:%d,y:%d\n", i, pdata->ocv_tab[i].x, + pdata->ocv_tab[i].y); + } + for (i = 0; i < pdata->temp_tab_size; i++) { + PDATA_LOG("temp_tab_size i=%d x:%d,y:%d\n", i, + pdata->temp_tab[i].x, pdata->temp_tab[i].y); + } + +} + +static uint32_t init_flag = 0; +#define SPRDBAT_ONE_PERCENT_TIME (40) +#define SPRDBAT_AVOID_JUMPING_TEMI (SPRDBAT_ONE_PERCENT_TIME) + +static unsigned long sprdbat_update_capacity_time; +static uint32_t update_capacity; + +static unsigned long sprdbat_last_query_time; +struct sprd_battery_platform_data *bat_pdata; + +bool sec_hal_fg_init(fuelgauge_variable_t * fg_var) +{ + struct sprd_battery_platform_data *pdata = (&get_battery_data(fg_var))->pdata ; + bat_pdata = pdata; + pr_info("register sec_hal_fg_init\n"); + print_pdata(pdata); //debug + sprdfgu_init(pdata); + { + struct timespec cur_time; + get_monotonic_boottime(&cur_time); + sprdbat_update_capacity_time = cur_time.tv_sec; + update_capacity = sprdfgu_poweron_capacity(); + } + init_flag = 1; + return true; +} + +static uint32_t sprdbat_update_capacty(void) +{ + uint32_t fgu_capacity = sprdfgu_read_capacity(); + uint32_t flush_time = 0; + uint32_t period_time = 0; + struct timespec cur_time; + union power_supply_propval value; + + psy_do_property("battery", get, POWER_SUPPLY_PROP_STATUS, value); + + get_monotonic_boottime(&cur_time); + flush_time = cur_time.tv_sec - sprdbat_update_capacity_time; + period_time = cur_time.tv_sec - sprdbat_last_query_time; + sprdbat_last_query_time = cur_time.tv_sec; + + pr_info("fgu_capacity: = %d,flush_time: = %d,period_time:=%d\n", + fgu_capacity, flush_time, period_time); + + switch (value.intval) { + case POWER_SUPPLY_STATUS_CHARGING: + if (fgu_capacity < update_capacity) { + if (sprdfgu_read_batcurrent() > 0) { + pr_info("avoid vol jumping\n"); + fgu_capacity = update_capacity; + } else { + if (period_time <= SPRDBAT_AVOID_JUMPING_TEMI) { + fgu_capacity =update_capacity - 1; + pr_info("avoid jumping! fgu_capacity: = %d\n", fgu_capacity); + } + if ((update_capacity - fgu_capacity) >= + (flush_time / SPRDBAT_ONE_PERCENT_TIME)) { + fgu_capacity = + update_capacity - + flush_time / SPRDBAT_ONE_PERCENT_TIME; + } + } + } else if (fgu_capacity > update_capacity) { + if (period_time <= SPRDBAT_AVOID_JUMPING_TEMI) { + fgu_capacity = + update_capacity + 1; + pr_info("avoid jumping! fgu_capacity: = %d\n",fgu_capacity); + } + if ((fgu_capacity - update_capacity) >= + (flush_time / SPRDBAT_ONE_PERCENT_TIME)) { + fgu_capacity = + update_capacity + + flush_time / SPRDBAT_ONE_PERCENT_TIME; + } + } + /*when soc=100 and adp plugin occur, keep on 100, */ + if (100 == update_capacity) { + sprdbat_update_capacity_time = cur_time.tv_sec; + fgu_capacity = 100; + } else { + if (fgu_capacity >= 100) { + fgu_capacity = 99; + } + } + + break; + case POWER_SUPPLY_STATUS_NOT_CHARGING: + case POWER_SUPPLY_STATUS_DISCHARGING: + if (fgu_capacity >= update_capacity) { + fgu_capacity = update_capacity; + } else { + if (period_time <= SPRDBAT_AVOID_JUMPING_TEMI) { + fgu_capacity = + update_capacity - 1; + pr_info + ("avoid jumping! fgu_capacity: = %d\n", + fgu_capacity); + } + if ((update_capacity - fgu_capacity) >= + (flush_time / SPRDBAT_ONE_PERCENT_TIME)) { + fgu_capacity = + update_capacity - + flush_time / SPRDBAT_ONE_PERCENT_TIME; + } + } + + if (sprdfgu_read_vbat_vol() < bat_pdata->soft_vbat_uvlo) { + pr_info("soft uvlo vol 0...\n"); + fgu_capacity = 0; + } + + break; + case POWER_SUPPLY_STATUS_FULL: + sprdbat_update_capacity_time = cur_time.tv_sec; + if (fgu_capacity != 100) { + fgu_capacity = 100; + } + break; + default: + break; + } + + if (fgu_capacity != update_capacity) { + update_capacity = fgu_capacity; + sprdbat_update_capacity_time = cur_time.tv_sec; + } + sprdfgu_record_cap(update_capacity); + return update_capacity; + +} + +bool sec_hal_fg_reset(fuelgauge_variable_t * fg_var) +{ + msleep(1000); //wait a moment,insure voltage REG of FG is updated after power supply voltage be changed + sprdfgu_reset(); + { + struct timespec cur_time; + get_monotonic_boottime(&cur_time); + sprdbat_update_capacity_time = cur_time.tv_sec; //time need to be updated,too. + update_capacity = sprdfgu_poweron_capacity(); //same as your change + } + return true; +} +bool sec_hal_fg_suspend(fuelgauge_variable_t * fg_var) +{ + sprdfgu_pm_op(1); + return true; +} + +bool sec_hal_fg_resume(fuelgauge_variable_t * fg_var) +{ + sprdfgu_pm_op(0); + return true; +} +bool sec_hal_fg_fuelalert_init(fuelgauge_variable_t * fg_var , int val) +{ + return true; +} +bool sec_hal_fg_is_fuelalerted(fuelgauge_variable_t * fg_var) +{ + return false; +} +bool sec_hal_fg_full_charged(fuelgauge_variable_t * fg_var) +{ + return true; +} + +bool sec_hal_fg_fuelalert_process(void *irq_data, bool is_fuel_alerted) +{ + return true; +} + +bool sec_hal_fg_get_property(fuelgauge_variable_t * fg_var, + enum power_supply_property psp, + union power_supply_propval *val) +{ + + switch (psp) { + + /* Cell voltage (VCELL, mV) */ + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = sprdfgu_read_vbat_vol(); + break; + + /* Additional Voltage Information (mV) */ + case POWER_SUPPLY_PROP_VOLTAGE_AVG: + switch (val->intval) { + case SEC_BATTEY_VOLTAGE_AVERAGE: + val->intval = sprdfgu_read_vbat_vol(); + break; + case SEC_BATTEY_VOLTAGE_OCV: + val->intval = sprdfgu_read_vbat_ocv(); + break; + } + break; + + /* Current (mA) */ + case POWER_SUPPLY_PROP_CURRENT_NOW: + val->intval = sprdfgu_read_batcurrent(); + break; + + /* Average Current (mA) */ + case POWER_SUPPLY_PROP_CURRENT_AVG: + val->intval = sprdfgu_read_batcurrent(); + break; + + /* SOC (%) */ + case POWER_SUPPLY_PROP_CAPACITY: + if (init_flag) { +#if 0 + int cap = sprdfgu_read_capacity(); + if (100 == cap) { + union power_supply_propval value; + psy_do_property("battery", get, + POWER_SUPPLY_PROP_STATUS, + value); + if (POWER_SUPPLY_STATUS_CHARGING == + value.intval) { + pr_info + ("sec_hal_fg_get_property soc == 100, but is charging\n", + psp); + cap = 99; + } + } +#else + int cap = sprdbat_update_capacty(); +#endif + val->intval = cap * 10; + } else { + val->intval = 500; + } + break; + + /* Battery Temperature */ + case POWER_SUPPLY_PROP_TEMP: + + /* Target Temperature */ + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + default: + return false; + } + pr_info("sec_hal_fg_get_property-%d,val->intval = %d\n", psp, + val->intval); + return true; +} + +bool sec_hal_fg_set_property(fuelgauge_variable_t * fg_var, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + switch (psp) { + + case POWER_SUPPLY_PROP_ONLINE: + if (val->intval == POWER_SUPPLY_TYPE_BATTERY) { + sprdfgu_adp_status_set(0); + } else { + sprdfgu_adp_status_set(1); + } + break; + /* Battery Temperature */ + case POWER_SUPPLY_PROP_TEMP: + + /* Target Temperature */ + case POWER_SUPPLY_PROP_TEMP_AMBIENT: + break; + default: + return false; + } + return true; +} + |
