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/power/sprd_ext_ic_power.c | |
Diffstat (limited to 'drivers/power/sprd_ext_ic_power.c')
| -rw-r--r-- | drivers/power/sprd_ext_ic_power.c | 1409 |
1 files changed, 1409 insertions, 0 deletions
diff --git a/drivers/power/sprd_ext_ic_power.c b/drivers/power/sprd_ext_ic_power.c new file mode 100644 index 00000000..d2204e32 --- /dev/null +++ b/drivers/power/sprd_ext_ic_power.c @@ -0,0 +1,1409 @@ + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/power_supply.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/interrupt.h> +#include <asm/io.h> +#include <linux/hrtimer.h> +#include <linux/spinlock.h> +#include <linux/gpio.h> +#include <linux/irq.h> +#include <linux/wakelock.h> +#include <linux/delay.h> +#include <soc/sprd/hardware.h> +#include <soc/sprd/gpio.h> +#include <linux/device.h> + +#include <linux/slab.h> +#include <linux/jiffies.h> +#include "sprd_battery.h" + +#include <linux/leds.h> + +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> + +#include <soc/sprd/sci.h> +#include <soc/sprd/sci_glb_regs.h> +#include <soc/sprd/adi.h> +#include <soc/sprd/adc.h> +#include <soc/sprd/usb.h> + +#ifdef CONFIG_SPRD_CHARGER_SM5701 +#include <linux/power/sm5701.h> +#endif + +#ifdef CONFIG_SPRD_CHARGER_FAN5405 +#include "fan5405.h" +#endif + +#define SPRDBAT_ONE_PERCENT_TIME (30) +#define SPRDBAT_VALID_CAP 40 + +static struct sprdbat_drivier_data *sprdbat_data; +struct delayed_work sprdbat_charge_work; +static unsigned long sprdbat_update_capacity_time; +static uint32_t poweron_capacity; +static uint32_t sprdbat_start_chg; + +struct sprd_ext_ic_operations *sprd_ext_ic_op; + +/************************************************************************* + + Use to recourd state of fan54015 report + + status_0: charge idle + when charge stop will enter this state and start charge depend on stop condition + + status_1: charging process + in this status will clear charge_done_flag charge_err_flag. + + status_2: charge done + in this status charge_done_flag will set true + + status_3: someting err happened when charging + in this stats charge_err_flag will set true + +***************************************************************************/ + +static int status_0, status_1, status_2, status_3 = 0; +static int charge_done_flag = 0; +static int charge_err_flag = 0; +static int charge_timeout_flag = 0; + +extern struct sprdbat_auxadc_cal adc_cal; + +#ifdef CONFIG_SPRD_EXT_IC_POWER + +extern uint32_t sprdchg_read_vbat_vol(void); +extern int sprdfgu_read_batcurrent(void); + +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; +} + +uint16_t capacity_table_adc[][2] = { + {4180, 100} + , + {4100, 95} + , + {3980, 80} + , + {3900, 70} + , + {3840, 60} + , + {3800, 50} + , + {3760, 40} + , + {3730, 30} + , + {3700, 20} + , + {3650, 15} + , + {3600, 5} + , + {3400, 0} + , +}; + +static int battery_internal_impedance_fan = 270; + +uint32_t sprdadc_read_vbat_vol(void) +{ + int vchg_value; + vchg_value = sprdchg_read_vbat_vol(); //chanel ???? + return vchg_value; +} + +uint32_t sprdadc_read_vbat_ocv(void) +{ + return sprdadc_read_vbat_vol() - + (sprdfgu_read_batcurrent() * battery_internal_impedance_fan) / 1000; +} + +static int otg_enable_power_on = 0; + +int sprd_otg_enable_power_on(void) +{ + return otg_enable_power_on; +} + +static uint32_t sprdadc_vol2capacity(uint32_t voltage) +{ + uint16_t percentum; + int32_t temp; + uint16_t table_size; + int pos = 0; + + printk("sprdadc_vol2capacity voltage: %d\n", voltage); + + table_size = ARRAY_SIZE(capacity_table_adc); + for (pos = 0; pos < table_size - 1; pos++) { + if (voltage > capacity_table_adc[pos][0]) + break; + } + if (pos == 0) { + percentum = 100; + } else { + temp = + capacity_table_adc[pos][1] - capacity_table_adc[pos - 1][1]; + temp = temp * (voltage - capacity_table_adc[pos][0]); + temp = + temp / (capacity_table_adc[pos][0] - + capacity_table_adc[pos - 1][0]); + temp = temp + capacity_table_adc[pos][1]; + if (temp < 0) + temp = 0; + percentum = temp; + } + + return percentum; +} + +uint32_t sprdadc_read_capacity(void) +{ + int32_t voltage; + int cap = 0; +#if 0 + voltage = sprdadc_read_vbat_ocv(); +#else + voltage = sprdadc_read_vbat_vol(); +#endif + return sprdadc_vol2capacity(voltage); +} + +#endif + +static enum power_supply_property sprdbat_battery_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_CAPACITY, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_TEMP, + +}; + +static enum power_supply_property sprdbat_ac_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static enum power_supply_property sprdbat_usb_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static ssize_t sprdbat_show_caliberate(struct device *dev, + struct device_attribute *attr, + char *buf); + +static ssize_t sprdbat_store_caliberate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static int sprdbat_stop_charge(void); +static int sprdbat_start_charge(void); + +#define SPRDBAT_CALIBERATE_ATTR(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IRUGO | S_IWUSR | S_IWGRP, }, \ + .show = sprdbat_show_caliberate, \ + .store = sprdbat_store_caliberate, \ +} + +#define SPRDBAT_CALIBERATE_ATTR_RO(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IRUGO, }, \ + .show = sprdbat_show_caliberate, \ +} + +#define SPRDBAT_CALIBERATE_ATTR_WO(_name) \ +{ \ + .attr = { .name = #_name, .mode = S_IWUSR | S_IWGRP, }, \ + .store = sprdbat_store_caliberate, \ +} + +static struct device_attribute sprd_caliberate[] = { + SPRDBAT_CALIBERATE_ATTR_RO(real_time_voltage), + SPRDBAT_CALIBERATE_ATTR_WO(stop_charge), + SPRDBAT_CALIBERATE_ATTR_RO(real_time_current), + SPRDBAT_CALIBERATE_ATTR_WO(battery_0), + SPRDBAT_CALIBERATE_ATTR_WO(battery_1), + SPRDBAT_CALIBERATE_ATTR(hw_switch_point), + SPRDBAT_CALIBERATE_ATTR_RO(charger_voltage), + SPRDBAT_CALIBERATE_ATTR_RO(real_time_vbat_adc), + SPRDBAT_CALIBERATE_ATTR_WO(save_capacity), + SPRDBAT_CALIBERATE_ATTR_RO(temp_adc), +}; + +enum sprdbat_attribute { + BATTERY_VOLTAGE = 0, + STOP_CHARGE, + BATTERY_NOW_CURRENT, + BATTERY_0, + BATTERY_1, + HW_SWITCH_POINT, + CHARGER_VOLTAGE, + BATTERY_ADC, + SAVE_CAPACITY, + TEMP_ADC +}; +static ssize_t sprdbat_show_caliberate(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int i = 0; + const ptrdiff_t off = attr - sprd_caliberate; + int adc_value; + int voltage; + uint32_t now_current; + + switch (off) { + case BATTERY_VOLTAGE: + voltage = sprdchg_read_vbat_vol(); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", voltage); + break; + case BATTERY_NOW_CURRENT: + if (sprdbat_data->bat_info.module_state == + POWER_SUPPLY_STATUS_CHARGING) { + now_current = sprdchg_read_chg_current(); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + now_current); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", + "discharging"); + } + break; + case HW_SWITCH_POINT: + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", + sprdbat_data->bat_info.cccv_point); + break; + case CHARGER_VOLTAGE: + if (sprdbat_data->bat_info.module_state == + POWER_SUPPLY_STATUS_CHARGING) { + voltage = sprdchg_read_vchg_vol(); + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", voltage); + } else { + i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n", + "discharging"); + } + + break; + case BATTERY_ADC: + adc_value = sci_adc_get_value(ADC_CHANNEL_VBAT, false); + if (adc_value < 0) + adc_value = 0; + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", adc_value); + break; + case TEMP_ADC: + adc_value = sprdbat_read_temp_adc(); + if (adc_value < 0) + adc_value = 0; + i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", adc_value); + break; + default: + i = -EINVAL; + break; + } + + return i; +} + +static ssize_t sprdbat_store_caliberate(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned long set_value; + const ptrdiff_t off = attr - sprd_caliberate; + + set_value = simple_strtoul(buf, NULL, 10); + printk("battery calibrate value %d %lu\n", off, set_value); + + mutex_lock(&sprdbat_data->lock); + switch (off) { + case STOP_CHARGE: + sprdbat_stop_charge(); + break; + case BATTERY_0: + adc_cal.p0_vol = set_value & 0xffff; //only for debug + adc_cal.p0_adc = (set_value >> 16) & 0xffff; + break; + case BATTERY_1: + adc_cal.p1_vol = set_value & 0xffff; + adc_cal.p1_adc = (set_value >> 16) & 0xffff; + adc_cal.cal_type = SPRDBAT_AUXADC_CAL_NV; + break; + case HW_SWITCH_POINT: + + break; + case SAVE_CAPACITY: + { + int temp = set_value - poweron_capacity; + + printk("battery temp:%d\n", temp); + if (abs(temp) > SPRDBAT_VALID_CAP || 0 == set_value) { + printk("battery poweron capacity:%lu,%d\n", + set_value, poweron_capacity); + sprdbat_data->bat_info.capacity = + poweron_capacity; + } else { + printk("battery old capacity:%lu,%d\n", + set_value, poweron_capacity); + sprdbat_data->bat_info.capacity = set_value; + } + power_supply_changed(&sprdbat_data->battery); + } + break; + default: + count = -EINVAL; + break; + } + mutex_unlock(&sprdbat_data->lock); + return count; +} + +static int sprdbat_remove_caliberate_attr(struct device *dev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sprd_caliberate); i++) { + device_remove_file(dev, &sprd_caliberate[i]); + } + return 0; +} + +static char *supply_list[] = { + "battery", +}; + +static char *battery_supply_list[] = { + "audio-ldo", + "sprdfgu", +}; + +static void sprdbat_chg_print_log(void) +{ + struct timespec cur_time; + + printk("sprd_ext_ic-----sprdbat_chg_print_log\n"); + + get_monotonic_boottime(&cur_time); + + printk("sprd_ext_ic-----cur_time:%ld\n", cur_time.tv_sec); + printk("sprd_ext_ic-----adp_type:%d\n", + sprdbat_data->bat_info.adp_type); + printk("sprd_ext_ic-----bat_health:%d\n", + sprdbat_data->bat_info.bat_health); + printk("sprd_ext_ic-----module_state:%d\n", + sprdbat_data->bat_info.module_state); + + printk("sprd_ext_ic-----chg_start_time:%ld\n", + sprdbat_data->bat_info.chg_start_time); + printk("sprd_ext_ic-----capacity:%d\n", + sprdbat_data->bat_info.capacity); + printk("sprd_ext_ic-----vbat_vol:%d\n", + sprdbat_data->bat_info.vbat_vol); + printk("sprd_ext_ic-----vbat_ocv:%d\n", + sprdbat_data->bat_info.vbat_ocv); + printk("sprd_ext_ic-----cur_temp:%d\n", + sprdbat_data->bat_info.cur_temp); + printk("sprd_ext_ic-----bat_current:%d\n", + sprdbat_data->bat_info.bat_current); + printk("sprd_ext_ic-----chging_current:%d\n", + sprdbat_data->bat_info.chging_current); + printk("sprd_ext_ic-----chg_current_type:%d\n", + sprdbat_data->bat_info.chg_current_type); + printk("sprd_ext_ic-----aux vbat vol:%d\n", sprdchg_read_vbat_vol()); + printk("sprd_ext_ic-----vchg_vol:%d\n", sprdchg_read_vchg_vol()); + printk("sprd_ext_ic-----sprdbat_chg_print_log end\n"); + +} + +void sprd_extic_otg_power(int enable) +{ + if (sprd_ext_ic_op == NULL) { + printk("power on with OTG plug in\n"); + otg_enable_power_on = 1; + } else { + otg_enable_power_on = 0; + sprd_ext_ic_op->otg_charge_ext(enable); + } +} + +static int plugin_callback(int usb_cable, void *data) +{ + printk("charger plug in interrupt happen\n"); + + mutex_lock(&sprdbat_data->lock); + wake_lock_timeout(&(sprdbat_data->charger_plug_out_lock), + SPRDBAT_PLUG_WAKELOCK_TIME_SEC * HZ); + + sprdbat_data->bat_info.module_state = POWER_SUPPLY_STATUS_CHARGING; + + sprdbat_data->bat_info.adp_type = sprdchg_charger_is_adapter(); + if (sprdbat_data->bat_info.adp_type == ADP_TYPE_DCP) + sprdbat_data->bat_info.ac_online = 1; + else + sprdbat_data->bat_info.usb_online = 1; + + sprdbat_adp_plug_nodify(1); + + sprdbat_start_charge(); +// sprdchg_timer_enable(sprdbat_data->pdata->chg_polling_time); + sprdchg_timer_enable(10); //sprdbat_data->pdata->chg_polling_time + mutex_unlock(&sprdbat_data->lock); + + printk("plugin_callback:sprdbat_data->bat_info.adp_type:%d\n", + sprdbat_data->bat_info.adp_type); + + if (sprdbat_data->bat_info.adp_type == ADP_TYPE_DCP) + power_supply_changed(&sprdbat_data->ac); + else + power_supply_changed(&sprdbat_data->usb); + + printk("plugin_callback: end...\n"); + return 0; +} + +static int plugout_callback(int usb_cable, void *data) +{ + uint32_t adp_type = sprdbat_data->bat_info.adp_type; + + printk("charger plug out interrupt happen\n"); + + mutex_lock(&sprdbat_data->lock); + + sprdchg_timer_disable(); + + wake_lock_timeout(&(sprdbat_data->charger_plug_out_lock), + SPRDBAT_PLUG_WAKELOCK_TIME_SEC * HZ); + sprdbat_adp_plug_nodify(0); + sprdbat_data->bat_info.adp_type = ADP_TYPE_UNKNOW; + sprdbat_data->bat_info.ac_online = 0; + sprdbat_data->bat_info.usb_online = 0; + + sprdbat_data->bat_info.module_state = POWER_SUPPLY_STATUS_DISCHARGING; + + sprdbat_stop_charge(); + mutex_unlock(&sprdbat_data->lock); + + if (adp_type == ADP_TYPE_DCP) + power_supply_changed(&sprdbat_data->ac); + else + power_supply_changed(&sprdbat_data->usb); + + printk("charger plug out END....\n"); + return 0; +} + +static struct usb_hotplug_callback power_cb = { + .plugin = plugin_callback, + .plugout = plugout_callback, + .data = NULL, +}; + +// +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_end_vol_h:%d\n", pdata->chg_end_vol_h); + PDATA_LOG("chg_end_vol_l:%d\n", pdata->chg_end_vol_l); + PDATA_LOG("chg_end_vol_pure:%d\n", pdata->chg_end_vol_pure); + PDATA_LOG("chg_bat_safety_vol:%d\n", pdata->chg_bat_safety_vol); + PDATA_LOG("rechg_vol:%d\n", pdata->rechg_vol); + PDATA_LOG("adp_cdp_cur:%d\n", pdata->adp_cdp_cur); + PDATA_LOG("adp_dcp_cur:%d\n", pdata->adp_dcp_cur); + PDATA_LOG("adp_sdp_cur:%d\n", pdata->adp_sdp_cur); + PDATA_LOG("ovp_stop:%d\n", pdata->ovp_stop); + PDATA_LOG("ovp_restart:%d\n", pdata->ovp_restart); + PDATA_LOG("chg_timeout:%d\n", pdata->chg_timeout); + PDATA_LOG("chgtimeout_show_full:%d\n", pdata->chgtimeout_show_full); + PDATA_LOG("chg_rechg_timeout:%d\n", pdata->chg_rechg_timeout); + PDATA_LOG("chg_cv_timeout:%d\n", pdata->chg_cv_timeout); + PDATA_LOG("chg_eoc_level:%d\n", pdata->chg_eoc_level); + PDATA_LOG("cccv_default:%d\n", pdata->cccv_default); + PDATA_LOG("chg_end_cur:%d\n", pdata->chg_end_cur); + PDATA_LOG("otp_high_stop:%d\n", pdata->otp_high_stop); + PDATA_LOG("otp_high_restart:%d\n", pdata->otp_high_restart); + PDATA_LOG("otp_low_stop:%d\n", pdata->otp_low_stop); + PDATA_LOG("otp_low_restart:%d\n", pdata->otp_low_restart); + PDATA_LOG("chg_polling_time:%d\n", pdata->chg_polling_time); + PDATA_LOG("chg_polling_time_fast:%d\n", pdata->chg_polling_time_fast); + PDATA_LOG("bat_polling_time:%d\n", pdata->bat_polling_time); + PDATA_LOG("bat_polling_time_fast:%d\n", pdata->bat_polling_time_fast); + PDATA_LOG("bat_polling_time_sleep:%d\n", pdata->bat_polling_time_sleep); + PDATA_LOG("cap_one_per_time:%d\n", pdata->cap_one_per_time); + PDATA_LOG("cap_one_per_time_fast:%d\n", pdata->cap_one_per_time_fast); + PDATA_LOG("cap_valid_range_poweron:%d\n", + pdata->cap_valid_range_poweron); + PDATA_LOG("temp_support:%d\n", pdata->temp_support); + PDATA_LOG("temp_adc_ch:%d\n", pdata->temp_adc_ch); + PDATA_LOG("temp_adc_scale:%d\n", pdata->temp_adc_scale); + PDATA_LOG("temp_adc_sample_cnt:%d\n", pdata->temp_adc_sample_cnt); + PDATA_LOG("temp_table_mode:%d\n", pdata->temp_table_mode); + PDATA_LOG("temp_tab_size:%d\n", pdata->temp_tab_size); + PDATA_LOG("gpio_vchg_detect:%d\n", pdata->gpio_vchg_detect); +// PDATA_LOG("gpio_cv_state:%d\n", pdata->gpio_cv_state); + PDATA_LOG("gpio_vchg_ovi:%d\n", pdata->gpio_vchg_ovi); + PDATA_LOG("irq_chg_timer:%d\n", pdata->irq_chg_timer); + PDATA_LOG("irq_fgu:%d\n", pdata->irq_fgu); + PDATA_LOG("chg_reg_base:%d\n", pdata->chg_reg_base); + PDATA_LOG("fgu_reg_base:%d\n", pdata->fgu_reg_base); + 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); + } + +} + +#ifdef CONFIG_OF +static struct sprd_battery_platform_data *sprdbat_parse_dt(struct + platform_device + *pdev) +{ + struct sprd_battery_platform_data *pdata = NULL; + struct device_node *np = pdev->dev.of_node; + struct device_node *temp_np = NULL; + unsigned int irq_num; + int ret, i, temp; + + pdata = devm_kzalloc(&pdev->dev, + sizeof(struct sprd_battery_platform_data), + GFP_KERNEL); + if (!pdata) { + return NULL; + } +// pdata->gpio_cv_state = (uint32_t) of_get_named_gpio(np, "gpios", 1); + pdata->gpio_vchg_ovi = (uint32_t) of_get_named_gpio(np, "gpios", 2); + temp_np = of_get_child_by_name(np, "sprd_chg"); + if (!temp_np) { + goto err_parse_dt; + } + irq_num = irq_of_parse_and_map(temp_np, 0); + pdata->irq_chg_timer = irq_num; + printk("sprd_chg dts irq_num =%s, %d\n", temp_np->name, irq_num); + + temp_np = of_get_child_by_name(np, "sprd_fgu"); + if (!temp_np) { + goto err_parse_dt; + } + irq_num = irq_of_parse_and_map(temp_np, 0); + pdata->irq_fgu = irq_num; + printk("sprd_fgu dts irq_num = %d\n", irq_num); + + ret = of_property_read_u32(np, "chg-end-vol-h", &pdata->chg_end_vol_h); + ret = of_property_read_u32(np, "chg-end-vol-l", &pdata->chg_end_vol_l); + ret = + of_property_read_u32(np, "chg-end-vol-pure", + &pdata->chg_end_vol_pure); + ret = + of_property_read_u32(np, "chg-bat-safety-vol", + &pdata->chg_bat_safety_vol); + ret = of_property_read_u32(np, "rechg-vol", &pdata->rechg_vol); + ret = of_property_read_u32(np, "adp-cdp-cur", &pdata->adp_cdp_cur); + ret = of_property_read_u32(np, "adp-dcp-cur", &pdata->adp_dcp_cur); + ret = of_property_read_u32(np, "adp-sdp-cur", &pdata->adp_sdp_cur); + ret = of_property_read_u32(np, "ovp-stop", &pdata->ovp_stop); + ret = of_property_read_u32(np, "ovp-restart", &pdata->ovp_restart); + ret = of_property_read_u32(np, "chg-timeout", &pdata->chg_timeout); + ret = + of_property_read_u32(np, "chgtimeout-show-full", + &pdata->chgtimeout_show_full); + ret = + of_property_read_u32(np, "chg-rechg-timeout", + &pdata->chg_rechg_timeout); + ret = + of_property_read_u32(np, "chg-cv-timeout", &pdata->chg_cv_timeout); + ret = of_property_read_u32(np, "chg-eoc-level", &pdata->chg_eoc_level); + ret = of_property_read_u32(np, "cccv-default", &pdata->cccv_default); + ret = + of_property_read_u32(np, "chg-end-cur", + (u32 *) (&pdata->chg_end_cur)); + ret = of_property_read_u32(np, "otp-high-stop", (u32 *) (&temp)); + pdata->otp_high_stop = temp - 1000; + ret = of_property_read_u32(np, "otp-high-restart", (u32 *) (&temp)); + pdata->otp_high_restart = temp - 1000; + ret = of_property_read_u32(np, "otp-low-stop", (u32 *) (&temp)); + pdata->otp_low_stop = temp - 1000; + ret = of_property_read_u32(np, "otp-low-restart", (u32 *) (&temp)); + pdata->otp_low_restart = temp - 1000; + ret = + of_property_read_u32(np, "chg-polling-time", + &pdata->chg_polling_time); + ret = + of_property_read_u32(np, "chg-polling-time-fast", + &pdata->chg_polling_time_fast); + ret = + of_property_read_u32(np, "bat-polling-time", + &pdata->bat_polling_time); + ret = + of_property_read_u32(np, "bat-polling-time-fast", + &pdata->bat_polling_time_fast); + ret = + of_property_read_u32(np, "cap-one-per-time", + &pdata->cap_one_per_time); + ret = + of_property_read_u32(np, "cap-valid-range-poweron", + (u32 *) (&pdata->cap_valid_range_poweron)); + ret = + of_property_read_u32(np, "temp-support", + (u32 *) (&pdata->temp_support)); + ret = + of_property_read_u32(np, "temp-adc-ch", + (u32 *) (&pdata->temp_adc_ch)); + ret = + of_property_read_u32(np, "temp-adc-scale", + (u32 *) (&pdata->temp_adc_scale)); + ret = + of_property_read_u32(np, "temp-adc-sample-cnt", + (u32 *) (&pdata->temp_adc_sample_cnt)); + ret = + of_property_read_u32(np, "temp-table-mode", + (u32 *) (&pdata->temp_table_mode)); + ret = of_property_read_u32(np, "fgu-mode", &pdata->fgu_mode); + ret = of_property_read_u32(np, "alm-soc", &pdata->alm_soc); + ret = of_property_read_u32(np, "alm-vol", &pdata->alm_vol); + ret = + of_property_read_u32(np, "soft-vbat-uvlo", &pdata->soft_vbat_uvlo); + ret = of_property_read_u32(np, "rint", (u32 *) (&pdata->rint)); + ret = of_property_read_u32(np, "cnom", (u32 *) (&pdata->cnom)); + ret = + of_property_read_u32(np, "rsense-real", + (u32 *) (&pdata->rsense_real)); + ret = + of_property_read_u32(np, "rsense-spec", + (u32 *) (&pdata->rsense_spec)); + ret = of_property_read_u32(np, "relax-current", &pdata->relax_current); + ret = + of_property_read_u32(np, "fgu-cal-ajust", + (u32 *) (&pdata->fgu_cal_ajust)); + ret = + of_property_read_u32(np, "ocv-tab-size", + (u32 *) (&pdata->ocv_tab_size)); + ret = + of_property_read_u32(np, "temp-tab-size", + (u32 *) (&pdata->temp_tab_size)); + + pdata->temp_tab = kzalloc(sizeof(struct sprdbat_table_data) * + pdata->temp_tab_size, GFP_KERNEL); + + for (i = 0; i < pdata->temp_tab_size; i++) { + ret = of_property_read_u32_index(np, "temp-tab-val", i, + &pdata->temp_tab[i].x); + ret = of_property_read_u32_index(np, "temp-tab-temp", i, &temp); + pdata->temp_tab[i].y = temp - 1000; + } + + pdata->ocv_tab = kzalloc(sizeof(struct sprdbat_table_data) * + pdata->ocv_tab_size, GFP_KERNEL); + for (i = 0; i < pdata->ocv_tab_size; i++) { + ret = of_property_read_u32_index(np, "ocv-tab-vol", i, + (u32 *) (&pdata-> + ocv_tab[i].x)); + ret = + of_property_read_u32_index(np, "ocv-tab-cap", i, + (u32 *) (&pdata->ocv_tab[i].y)); + } + + return pdata; + +err_parse_dt: + dev_err(&pdev->dev, "Parsing device tree data error.\n"); + return NULL; +} +#else +static struct sprd_battery_platform_data *sprdbat_parse_dt(struct + platform_device + *pdev) +{ + return NULL; +} +#endif + +/* +static void sprdbat_param_init(struct sprdbat_drivier_data *data) +{ + data->bat_param.chg_end_vol_pure = SPRDBAT_CHG_END_VOL_PURE; + data->bat_param.chg_end_vol_h = SPRDBAT_CHG_END_H; + data->bat_param.chg_end_vol_l = SPRDBAT_CHG_END_L; + data->bat_param.rechg_vol = SPRDBAT_RECHG_VOL; + data->bat_param.chg_end_cur = SPRDBAT_CHG_END_CUR; + data->bat_param.adp_cdp_cur = SPRDBAT_CDP_CUR_LEVEL; + data->bat_param.adp_dcp_cur = SPRDBAT_DCP_CUR_LEVEL; + data->bat_param.adp_sdp_cur = SPRDBAT_SDP_CUR_LEVEL; + data->bat_param.ovp_stop = SPRDBAT_OVP_STOP_VOL; + data->bat_param.ovp_restart = SPRDBAT_OVP_RESTERT_VOL; + data->bat_param.otp_high_stop = SPRDBAT_OTP_HIGH_STOP; + data->bat_param.otp_high_restart = SPRDBAT_OTP_HIGH_RESTART; + data->bat_param.otp_low_stop = SPRDBAT_OTP_LOW_STOP; + data->bat_param.otp_low_restart = SPRDBAT_OTP_LOW_RESTART; + data->bat_param.chg_timeout = SPRDBAT_CHG_NORMAL_TIMEOUT; + data->bat_param.vbat_uvlo = SPRDBAT_BAT_SOFT_UVLO; + return; +} +*/ + +static void sprdbat_info_init(struct sprdbat_drivier_data *data) +{ + struct timespec cur_time; + data->bat_info.adp_type = ADP_TYPE_UNKNOW; + data->bat_info.bat_health = POWER_SUPPLY_HEALTH_GOOD; + data->bat_info.module_state = POWER_SUPPLY_STATUS_DISCHARGING; + data->bat_info.chg_stop_flags = SPRDBAT_CHG_END_NONE_BIT; + data->bat_info.chg_start_time = 0; + get_monotonic_boottime(&cur_time); + sprdbat_update_capacity_time = cur_time.tv_sec; + data->bat_info.capacity = sprdfgu_poweron_capacity(); //~0; + poweron_capacity = sprdfgu_poweron_capacity(); + data->bat_info.soc = sprdfgu_read_soc(); + data->bat_info.vbat_vol = sprdbat_read_vbat_vol(); + data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv(); + data->bat_info.cur_temp = sprdbat_read_temp(); + data->bat_info.bat_current = sprdfgu_read_batcurrent(); + data->bat_info.chging_current = 0; + data->bat_info.chg_current_type = sprdbat_data->pdata->adp_sdp_cur; + return; +} + +static int sprdbat_battery_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct sprdbat_drivier_data *data = container_of(psy, + struct + sprdbat_drivier_data, + battery); + + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = data->bat_info.module_state; + break; + + case POWER_SUPPLY_PROP_HEALTH: + val->intval = data->bat_info.bat_health; + break; + + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = POWER_SUPPLY_TECHNOLOGY_LION; + break; + + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = data->bat_info.capacity; + break; + + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = data->bat_info.vbat_vol * 1000; + break; + + case POWER_SUPPLY_PROP_TEMP: + val->intval = data->bat_info.cur_temp; + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sprdbat_ac_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct sprdbat_drivier_data *data = container_of(psy, + struct + sprdbat_drivier_data, + ac); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = data->bat_info.ac_online ? 1 : 0; + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sprdbat_usb_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int ret = 0; + struct sprdbat_drivier_data *data = container_of(psy, + struct + sprdbat_drivier_data, + usb); + + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = data->bat_info.usb_online ? 1 : 0; + break; + + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int sprdbat_start_charge(void) +{ + struct timespec cur_time; + printk("fan54015 ext ic start charge\n"); + sprd_ext_ic_op->ic_init(); + if (sprdbat_data->bat_info.adp_type == ADP_TYPE_DCP) { + sprd_ext_ic_op->charge_start_ext(1); //AC charger + } else { + sprd_ext_ic_op->charge_start_ext(0); //usb charger + } + + get_monotonic_boottime(&cur_time); + sprdbat_data->bat_info.chg_start_time = cur_time.tv_sec; + + printk + ("sprdbat_start_charge bat_health:%d,chg_start_time:%ld,chg_current_type:%d\n", + sprdbat_data->bat_info.bat_health, + sprdbat_data->bat_info.chg_start_time, + sprdbat_data->bat_info.chg_current_type); + + sprdbat_start_chg = 1; + + power_supply_changed(&sprdbat_data->battery); + + return 0; +} + +static int sprdbat_stop_charge(void) +{ + sprd_ext_ic_op->charge_stop_ext(); + sprdbat_data->bat_info.chg_start_time = 0; + printk("sprdbat_stop_charge\n"); + + power_supply_changed(&sprdbat_data->battery); + + return 0; +} + +static int sprdbat_creat_caliberate_attr(struct device *dev) +{ + int i, rc; + + for (i = 0; i < ARRAY_SIZE(sprd_caliberate); i++) { + rc = device_create_file(dev, &sprd_caliberate[i]); + if (rc) + goto sprd_attrs_failed; + } + goto sprd_attrs_succeed; + +sprd_attrs_failed: + while (i--) + device_remove_file(dev, &sprd_caliberate[i]); + +sprd_attrs_succeed: + return rc; +} + +static void sprdbat_update_capacty(void) +{ +#if 0 + uint32_t fgu_capacity = sprdadc_read_capacity(); +#else + uint32_t fgu_capacity = sprdfgu_read_capacity(); +#endif + uint32_t flush_time = 0; + struct timespec cur_time; + if (sprdbat_data->bat_info.capacity == ~0) { + return; + } + get_monotonic_boottime(&cur_time); + + flush_time = cur_time.tv_sec - sprdbat_update_capacity_time; + printk("fgu_capacity = %d,flush_time = %d\n", fgu_capacity, flush_time); + switch (sprdbat_data->bat_info.module_state) { + case POWER_SUPPLY_STATUS_CHARGING: + case POWER_SUPPLY_STATUS_NOT_CHARGING: + if (fgu_capacity < sprdbat_data->bat_info.capacity) { + fgu_capacity = sprdbat_data->bat_info.capacity; + } else { + if ((fgu_capacity - sprdbat_data->bat_info.capacity) >= + flush_time / SPRDBAT_ONE_PERCENT_TIME) { + fgu_capacity = + sprdbat_data->bat_info.capacity + + flush_time / SPRDBAT_ONE_PERCENT_TIME; + } + } + if (100 == sprdbat_data->bat_info.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_DISCHARGING: + if (fgu_capacity > sprdbat_data->bat_info.capacity) { + fgu_capacity = sprdbat_data->bat_info.capacity; + } else { + if ((sprdbat_data->bat_info.capacity - fgu_capacity) >= + flush_time / SPRDBAT_ONE_PERCENT_TIME) { + fgu_capacity = + sprdbat_data->bat_info.capacity - + flush_time / SPRDBAT_ONE_PERCENT_TIME; + } + } + break; + + case POWER_SUPPLY_STATUS_FULL: + if (fgu_capacity != 100) { + fgu_capacity = 100; + } + break; + + default: + BUG_ON(1); + break; + } + if (sprdbat_data->bat_info.vbat_vol < + sprdbat_data->pdata->soft_vbat_uvlo) { + fgu_capacity = 0; + printk("Power down by soft uvlow vbat_vol = %d\n", + sprdbat_data->bat_info.vbat_vol); + } + + if (fgu_capacity != sprdbat_data->bat_info.capacity) { + sprdbat_data->bat_info.capacity = fgu_capacity; + sprdbat_update_capacity_time = cur_time.tv_sec; + power_supply_changed(&sprdbat_data->battery); + } +} + +static int sprdbat_is_chg_timeout(void) +{ + struct timespec cur_time; + + get_monotonic_boottime(&cur_time); + if (cur_time.tv_sec - sprdbat_data->bat_info.chg_start_time > + sprdbat_data->pdata->chg_timeout) + return 1; + else + return 0; +} + +static void sprdbat_battery_works(struct work_struct *work) +{ + printk("sprdbat_battery_works\n"); + mutex_lock(&sprdbat_data->lock); + + //#ifdef SPRD_CHAEGE_EXT_FAN54015 +#if 0 + sprdbat_data->bat_info.vbat_vol = sprdadc_read_vbat_vol(); + sprdbat_data->bat_info.cur_temp = sprdbat_read_temp(); + sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent(); + sprdbat_data->bat_info.vbat_ocv = sprdadc_read_vbat_ocv(); + /*battery has no pin connect so use board ground insteat battery - so that VOL = OCV */ +#if 1 + sprdbat_data->bat_info.vbat_ocv = sprdbat_data->bat_info.vbat_vol; + printk("pengwei test sprdbat_data->bat_info.vbat_vol is %d\n", + sprdbat_data->bat_info.vbat_ocv); +#endif +#else + sprdbat_data->bat_info.vbat_vol = sprdbat_read_vbat_vol(); + sprdbat_data->bat_info.cur_temp = sprdbat_read_temp(); + sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent(); + sprdbat_data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv(); +#endif + + sprdbat_update_capacty(); + sprdbat_chg_print_log(); + + if (sprdbat_data->bat_info.module_state == POWER_SUPPLY_STATUS_CHARGING) { + schedule_delayed_work(&sprdbat_data->battery_work, + sprdbat_data->pdata-> + bat_polling_time_fast * HZ); + } else { + schedule_delayed_work(&sprdbat_data->battery_work, + sprdbat_data->pdata->bat_polling_time * + HZ); + } + + mutex_unlock(&sprdbat_data->lock); + +} + +static void sprdbat_battery_sleep_works(struct work_struct *work) +{ + printk("sprdbat_battery_sleep_works\n"); + if (schedule_delayed_work(&sprdbat_data->battery_work, 0) == 0) { + cancel_delayed_work_sync(&sprdbat_data->battery_work); + schedule_delayed_work(&sprdbat_data->battery_work, 0); + } +} + +static void sprdbat_charge_works(struct work_struct *work) +{ + + int battery_state; + + printk("sprdbat_charge_works----------start\n"); + + mutex_lock(&sprdbat_data->lock); + + battery_state = sprd_ext_ic_op->get_charging_status(); + printk("######## battery_state = %d\n", battery_state); + if (battery_state == 0x00) { + status_0++; + printk("###### now state is ready\n"); + if (charge_err_flag) { + printk("SPRDBAT_OVI_RESTART_E\n"); + sprdbat_start_charge(); + } + if ((charge_done_flag != 0) || (charge_timeout_flag == 1)) { + if (sprdbat_data->bat_info.vbat_ocv <= + sprdbat_data->pdata->rechg_vol) { + printk + ("recharge!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + sprdbat_start_charge(); + } + } + if (sprdbat_data->bat_info.module_state != + POWER_SUPPLY_STATUS_FULL) + sprdbat_data->bat_info.module_state = + POWER_SUPPLY_STATUS_NOT_CHARGING; + } else if (battery_state == 0x01) { + status_1++; + printk("###### now state is charging\n"); + charge_done_flag = 0; + charge_err_flag = 0; + + if (sprdbat_data->bat_info.module_state != + POWER_SUPPLY_STATUS_FULL) + sprdbat_data->bat_info.module_state = + POWER_SUPPLY_STATUS_CHARGING; + } else if (battery_state == 0x02) { + status_2++; + printk("###### now state is charge done\n"); + sprdbat_stop_charge(); + charge_done_flag = 1; + sprdbat_data->bat_info.module_state = POWER_SUPPLY_STATUS_FULL; + } else if (battery_state == 0x03) { + status_3++; + printk("###### now state is porper charge\n"); + sprdbat_stop_charge(); + charge_err_flag = 1; + +// sprdbat_data->bat_info.module_state = +// POWER_SUPPLY_STATUS_NOT_CHARGING; + } + + if (sprdbat_is_chg_timeout()) { + printk("chg timeout!!!!!!!!!!!!!\n"); + + if (sprdbat_data->bat_info.vbat_ocv > + sprdbat_data->pdata->rechg_vol) { + charge_timeout_flag = 1; + sprdbat_stop_charge(); + } else { + charge_timeout_flag = 0; +// sprdbat_stop_charge(); + } + printk("chg timeout!!!!!!!!!!!!! flag = %d\n", + charge_timeout_flag); + } + + printk + ("######## status number!!!!\n status_0 = %d\n status_1 = %d\n status_2 = %d\n status_3 = %d\n charge_done_flag = %d\n charge_timeout_flag = %d\n", + status_0, status_1, status_2, status_3, charge_done_flag, + charge_timeout_flag); + + mutex_unlock(&sprdbat_data->lock); + + sprdbat_chg_print_log(); + printk("sprdbat_charge_works----------end\n"); + +} + +static int sprdbat_timer_handler(void *data) +{ + sprdbat_data->bat_info.vbat_vol = sprdfgu_read_vbat_vol(); + sprdbat_data->bat_info.vbat_ocv = sprdfgu_read_vbat_ocv(); + sprdbat_data->bat_info.bat_current = sprdfgu_read_batcurrent(); + + printk("sprdbat_timer_handler----------vbat_vol %d,ocv:%d\n", + sprdbat_data->bat_info.vbat_vol, + sprdbat_data->bat_info.vbat_ocv); + printk("sprdbat_timer_handler----------bat_current %d\n", + sprdbat_data->bat_info.bat_current); + + queue_delayed_work(sprdbat_data->monitor_wqueue, + sprdbat_data->charge_work, 0); + + return 0; + +} + +static int sprdbat_probe(struct platform_device *pdev) +{ + int ret = -ENODEV; + struct sprdbat_drivier_data *data; +// struct resource *res = NULL; + + struct device_node *np = pdev->dev.of_node; + + printk("sprdbat_fan54015_probe start\n"); + +#ifdef CONFIG_OF + if (!np) { + dev_err(&pdev->dev, "device node not found\n"); + return -EINVAL; + } +#endif + + data = kzalloc(sizeof(*data), GFP_KERNEL); + if (data == NULL) { + ret = -ENOMEM; + goto err_data_alloc_failed; + } + + if (np) { + data->pdata = sprdbat_parse_dt(pdev); + } else { + data->pdata = dev_get_platdata(&pdev->dev); + } + + data->dev = &pdev->dev; + platform_set_drvdata(pdev, data); + sprdbat_data = data; +// sprdbat_param_init(data); + + sprd_ext_ic_op = sprd_get_ext_ic_ops(); + + data->battery.properties = sprdbat_battery_props; + data->battery.num_properties = ARRAY_SIZE(sprdbat_battery_props); + data->battery.get_property = sprdbat_battery_get_property; + data->battery.name = "battery"; + data->battery.type = POWER_SUPPLY_TYPE_BATTERY; + data->battery.supplied_to = battery_supply_list; + data->battery.num_supplicants = ARRAY_SIZE(battery_supply_list); + + data->ac.properties = sprdbat_ac_props; + data->ac.num_properties = ARRAY_SIZE(sprdbat_ac_props); + data->ac.get_property = sprdbat_ac_get_property; + data->ac.name = "ac"; + data->ac.type = POWER_SUPPLY_TYPE_MAINS; + data->ac.supplied_to = supply_list; + data->ac.num_supplicants = ARRAY_SIZE(supply_list); + + data->usb.properties = sprdbat_usb_props; + data->usb.num_properties = ARRAY_SIZE(sprdbat_usb_props); + data->usb.get_property = sprdbat_usb_get_property; + data->usb.name = "usb"; + data->usb.type = POWER_SUPPLY_TYPE_USB; + data->usb.supplied_to = supply_list; + data->usb.num_supplicants = ARRAY_SIZE(supply_list); + + data->start_charge = sprdbat_start_charge; + data->stop_charge = sprdbat_stop_charge; + + ret = power_supply_register(&pdev->dev, &data->usb); + if (ret) + goto err_usb_failed; + + ret = power_supply_register(&pdev->dev, &data->ac); + if (ret) + goto err_ac_failed; + + ret = power_supply_register(&pdev->dev, &data->battery); + if (ret) + goto err_battery_failed; + + sprdbat_creat_caliberate_attr(data->battery.dev); + +/*use gpio irq*/ +#if 0 + ret = gpio_request(); + if (ret) { + } + gpio_direction_input() + set_irq_flags(irq, flags); + ret = + request_irq(unsigned int irq, irq_handler_t handler, + unsigned long irqflags, const char *devname, + void *dev_id) +#endif + mutex_init(&data->lock); + wake_lock_init(&(data->charger_plug_out_lock), WAKE_LOCK_SUSPEND, + "charger_plug_out_lock"); + + INIT_DELAYED_WORK(&data->battery_work, sprdbat_battery_works); + INIT_DELAYED_WORK(&data->battery_sleep_work, + sprdbat_battery_sleep_works); + INIT_DELAYED_WORK(&sprdbat_charge_work, sprdbat_charge_works); + data->charge_work = &sprdbat_charge_work; +#if 0 + INIT_DELAYED_WORK(); //for irq +#endif + + data->monitor_wqueue = create_singlethread_workqueue("sprdbat_monitor"); + + sprdchg_timer_init(sprdbat_timer_handler, data); +// sprdchg_set_chg_ovp(data->bat_param.ovp_stop); + sprdchg_init(data->pdata); + sprdfgu_init(data->pdata); + +#ifdef CONFIG_LEDS_TRIGGERS + data->charging_led.name = "sprdbat_charging_led"; + data->charging_led.default_trigger = "battery-charging"; + data->charging_led.brightness_set = sprdchg_led_brightness_set; + led_classdev_register(&pdev->dev, &data->charging_led); +#endif + + sprdbat_info_init(data); + +// sprdbat_notifier.notifier_call = sprdbat_fgu_event; +// sprdfgu_register_notifier(&sprdbat_notifier); + usb_register_hotplug_callback(&power_cb); + + schedule_delayed_work(&data->battery_work, + sprdbat_data->pdata->bat_polling_time * HZ); + printk("sprdbat_probe----------end\n"); + return 0; + +err_data_alloc_failed: + sprdbat_data = NULL; + +err_battery_failed: + power_supply_unregister(&data->battery); + +err_ac_failed: + power_supply_unregister(&data->ac); + +err_usb_failed: + power_supply_unregister(&data->usb); + return ret; +} + +static int sprdbat_remove(struct platform_device *pdev) +{ + struct sprdbat_drivier_data *data = platform_get_drvdata(pdev); + + sprdbat_remove_caliberate_attr(data->battery.dev); + + power_supply_unregister(&data->battery); + power_supply_unregister(&data->ac); + power_supply_unregister(&data->usb); + + kfree(data); + sprdbat_data = NULL; + return 0; +} + +static int sprdbat_resume(struct platform_device *pdev) +{ + schedule_delayed_work(&sprdbat_data->battery_sleep_work, 0); + sprdfgu_pm_op(0); + sprd_ext_ic_op->timer_callback_ext(); //use to wake up 54015 timer 10S + return 0; +} + +static int sprdbat_suspend(struct platform_device *pdev, pm_message_t state) +{ + sprdfgu_pm_op(1); //enable low power wake up + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id battery_of_match[] = { + {.compatible = "sprd,sprd-battery",}, + {} +}; +#endif + +static struct platform_driver sprdbat_driver = { + .probe = sprdbat_probe, + .remove = sprdbat_remove, + .suspend = sprdbat_suspend, + .resume = sprdbat_resume, + .driver = { + .name = "sprd-battery", +#ifdef CONFIG_OF + .of_match_table = of_match_ptr(battery_of_match), +#endif + } +}; + +static int __init sprd_fan54015_init(void) +{ + return platform_driver_register(&sprdbat_driver); +} + +static void __exit sprd_fan54015_exit(void) +{ + return platform_driver_unregister(&sprdbat_driver); +} + +module_init(sprd_fan54015_init); +module_exit(sprd_fan54015_exit); + +MODULE_AUTHOR("wei.peng@spreadtrum.com"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Battery and charger driver for FAN54015"); |
