summaryrefslogtreecommitdiff
path: root/drivers/battery/sec_battery.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/battery/sec_battery.c')
-rwxr-xr-xdrivers/battery/sec_battery.c3955
1 files changed, 3955 insertions, 0 deletions
diff --git a/drivers/battery/sec_battery.c b/drivers/battery/sec_battery.c
new file mode 100755
index 00000000..83b21e6d
--- /dev/null
+++ b/drivers/battery/sec_battery.c
@@ -0,0 +1,3955 @@
+/*
+ * sec_battery.c
+ * Samsung Mobile Battery Driver
+ *
+ * Copyright (C) 2012 Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/battery/sec_battery.h>
+#if defined(CONFIG_MACH_CORSICA_VE) || defined(CONFIG_MACH_VIVALTO)
+extern int get_hw_rev();
+#endif
+//extern int sprdbat_creat_caliberate_attr(struct device *dev);
+
+extern void sec_bat_initial_check(void);
+
+static struct device_attribute sec_battery_attrs[] = {
+ SEC_BATTERY_ATTR(batt_reset_soc),
+ SEC_BATTERY_ATTR(batt_read_raw_soc),
+ SEC_BATTERY_ATTR(batt_read_adj_soc),
+ SEC_BATTERY_ATTR(batt_type),
+ SEC_BATTERY_ATTR(batt_vfocv),
+ SEC_BATTERY_ATTR(batt_vol_adc),
+ SEC_BATTERY_ATTR(batt_vol_adc_cal),
+ SEC_BATTERY_ATTR(batt_vol_aver),
+ SEC_BATTERY_ATTR(batt_vol_adc_aver),
+ SEC_BATTERY_ATTR(batt_current_ua_now),
+ SEC_BATTERY_ATTR(batt_current_ua_avg),
+ SEC_BATTERY_ATTR(batt_temp),
+ SEC_BATTERY_ATTR(batt_temp_adc),
+ SEC_BATTERY_ATTR(batt_temp_aver),
+ SEC_BATTERY_ATTR(batt_temp_adc_aver),
+ SEC_BATTERY_ATTR(batt_vf_adc),
+ SEC_BATTERY_ATTR(batt_slate_mode),
+
+ SEC_BATTERY_ATTR(batt_lp_charging),
+ SEC_BATTERY_ATTR(siop_activated),
+ SEC_BATTERY_ATTR(siop_level),
+ SEC_BATTERY_ATTR(batt_charging_source),
+ SEC_BATTERY_ATTR(fg_reg_dump),
+ SEC_BATTERY_ATTR(fg_reset_cap),
+ SEC_BATTERY_ATTR(fg_capacity),
+ SEC_BATTERY_ATTR(auth),
+ SEC_BATTERY_ATTR(chg_current_adc),
+ SEC_BATTERY_ATTR(wc_adc),
+ SEC_BATTERY_ATTR(wc_status),
+ SEC_BATTERY_ATTR(wc_enable),
+ SEC_BATTERY_ATTR(factory_mode),
+ SEC_BATTERY_ATTR(update),
+ SEC_BATTERY_ATTR(test_mode),
+
+ SEC_BATTERY_ATTR(call),
+ SEC_BATTERY_ATTR(2g_call),
+ SEC_BATTERY_ATTR(talk_gsm),
+ SEC_BATTERY_ATTR(3g_call),
+ SEC_BATTERY_ATTR(talk_wcdma),
+ SEC_BATTERY_ATTR(music),
+ SEC_BATTERY_ATTR(video),
+ SEC_BATTERY_ATTR(browser),
+ SEC_BATTERY_ATTR(hotspot),
+ SEC_BATTERY_ATTR(camera),
+ SEC_BATTERY_ATTR(camcorger),
+ SEC_BATTERY_ATTR(data_call),
+ SEC_BATTERY_ATTR(wifi),
+ SEC_BATTERY_ATTR(wibro),
+ SEC_BATTERY_ATTR(lte),
+ SEC_BATTERY_ATTR(lcd),
+ SEC_BATTERY_ATTR(gps),
+ SEC_BATTERY_ATTR(event),
+#if defined(CONFIG_SAMSUNG_BATTERY_ENG_TEST)
+ SEC_BATTERY_ATTR(test_charge_current),
+ SEC_BATTERY_ATTR(set_stability_test),
+#endif
+};
+
+static enum power_supply_property sec_battery_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_CHARGE_TYPE,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_TEMP_AMBIENT,
+};
+
+static enum power_supply_property sec_power_props[] = {
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static enum power_supply_property sec_ps_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_ONLINE,
+};
+
+static char *supply_list[] = {
+ "battery",
+};
+
+char *sec_bat_charging_mode_str[] = {
+ "None",
+ "Normal",
+ "Additional",
+ "Re-Charging",
+ "ABS"
+};
+
+char *sec_bat_status_str[] = {
+ "Unknown",
+ "Charging",
+ "Discharging",
+ "Not-charging",
+ "Full"
+};
+
+char *sec_bat_health_str[] = {
+ "Unknown",
+ "Good",
+ "Overheat",
+ "Warm",
+ "Dead",
+ "OverVoltage",
+ "UnspecFailure",
+ "Cold",
+ "Cool",
+ "Watchdog timer expier",
+ "Safety timer expier",
+ "UnderVoltage",
+ "Overheatlimit"
+};
+
+static int sec_bat_set_charge(
+ struct sec_battery_info *battery,
+ bool enable)
+{
+ union power_supply_propval val;
+
+ struct timespec ts;
+ ktime_t current_time;
+
+ val.intval = battery->status;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_STATUS, val);
+#if defined(ANDROID_ALARM_ACTIVATED)
+ current_time = alarm_get_elapsed_realtime();
+ ts = ktime_to_timespec(current_time);
+#else
+ current_time = ktime_get_boottime();
+ ts = ktime_to_timespec(current_time);
+#endif
+
+ if (enable) {
+ val.intval = battery->cable_type;
+ /*Reset charging start time only in initial charging start */
+ if (battery->charging_start_time == 0) {
+ battery->charging_start_time =
+ ts.tv_sec ? ts.tv_sec : 1;
+ battery->charging_next_time =
+ battery->pdata->charging_reset_time;
+ }
+ } else {
+ val.intval = POWER_SUPPLY_TYPE_BATTERY;
+ battery->charging_start_time = 0;
+ battery->charging_passed_time = 0;
+ battery->charging_next_time = 0;
+ battery->charging_fullcharged_time = 0;
+ battery->full_check_cnt = 0;
+ }
+
+ battery->temp_high_cnt = 0;
+ battery->temp_low_cnt = 0;
+ battery->temp_recover_cnt = 0;
+
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_ONLINE, val);
+
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_ONLINE, val);
+
+ return 0;
+}
+
+static int sec_bat_get_adc_data(struct sec_battery_info *battery,
+ int adc_ch, int count)
+{
+ int adc_data;
+ int adc_max;
+ int adc_min;
+ int adc_total;
+ int i;
+
+ adc_data = 0;
+ adc_max = 0;
+ adc_min = 0;
+ adc_total = 0;
+
+ for (i = 0; i < count; i++) {
+ mutex_lock(&battery->adclock);
+#ifdef CONFIG_OF
+ adc_data = adc_read(battery, adc_ch);
+#else
+ adc_data = adc_read(battery->pdata, adc_ch);
+#endif
+ mutex_unlock(&battery->adclock);
+
+ if (adc_data < 0)
+ goto err;
+
+ if (i != 0) {
+ if (adc_data > adc_max)
+ adc_max = adc_data;
+ else if (adc_data < adc_min)
+ adc_min = adc_data;
+ } else {
+ adc_max = adc_data;
+ adc_min = adc_data;
+ }
+ adc_total += adc_data;
+ }
+
+ return (adc_total - adc_max - adc_min) / (count - 2);
+err:
+ return adc_data;
+}
+
+/*
+static unsigned long calculate_average_adc(
+ struct sec_battery_info *battery,
+ int channel, int adc)
+{
+ unsigned int cnt = 0;
+ int total_adc = 0;
+ int average_adc = 0;
+ int index = 0;
+
+ cnt = battery->adc_sample[channel].cnt;
+ total_adc = battery->adc_sample[channel].total_adc;
+
+ if (adc < 0) {
+ dev_err(battery->dev,
+ "%s : Invalid ADC : %d\n", __func__, adc);
+ adc = battery->adc_sample[channel].average_adc;
+ }
+
+ if (cnt < ADC_SAMPLE_COUNT) {
+ battery->adc_sample[channel].adc_arr[cnt] = adc;
+ battery->adc_sample[channel].index = cnt;
+ battery->adc_sample[channel].cnt = ++cnt;
+
+ total_adc += adc;
+ average_adc = total_adc / cnt;
+ } else {
+ index = battery->adc_sample[channel].index;
+ if (++index >= ADC_SAMPLE_COUNT)
+ index = 0;
+
+ total_adc = total_adc -
+ battery->adc_sample[channel].adc_arr[index] + adc;
+ average_adc = total_adc / ADC_SAMPLE_COUNT;
+
+ battery->adc_sample[channel].adc_arr[index] = adc;
+ battery->adc_sample[channel].index = index;
+ }
+
+ battery->adc_sample[channel].total_adc = total_adc;
+ battery->adc_sample[channel].average_adc = average_adc;
+
+ return average_adc;
+}
+*/
+
+static int sec_bat_get_adc_value(
+ struct sec_battery_info *battery, int channel)
+{
+ int adc;
+
+ adc = sec_bat_get_adc_data(battery, channel,
+ battery->pdata->adc_check_count);
+
+ if (adc < 0) {
+ dev_err(battery->dev,
+ "%s: Error in ADC\n", __func__);
+ return adc;
+ }
+
+ return adc;
+}
+
+static int sec_bat_get_charger_type_adc
+ (struct sec_battery_info *battery)
+{
+ /* It is true something valid is
+ connected to the device for charging.
+ By default this something is considered to be USB.*/
+ int result = POWER_SUPPLY_TYPE_USB;
+
+ int adc = 0;
+ int i;
+
+ /* Do NOT check cable type when cable_switch_check() returns false
+ * and keep current cable type
+ */
+ if (battery->pdata->cable_switch_check &&
+ !battery->pdata->cable_switch_check())
+ return battery->cable_type;
+
+ adc = sec_bat_get_adc_value(battery,
+ SEC_BAT_ADC_CHANNEL_CABLE_CHECK);
+
+ /* Do NOT check cable type when cable_switch_normal() returns false
+ * and keep current cable type
+ */
+ if (battery->pdata->cable_switch_normal &&
+ !battery->pdata->cable_switch_normal())
+ return battery->cable_type;
+
+ for (i = 0; i < SEC_SIZEOF_POWER_SUPPLY_TYPE; i++)
+ if ((adc > battery->pdata->cable_adc_value[i].min) &&
+ (adc < battery->pdata->cable_adc_value[i].max))
+ break;
+ if (i >= SEC_SIZEOF_POWER_SUPPLY_TYPE)
+ dev_err(battery->dev,
+ "%s : default USB\n", __func__);
+ else
+ result = i;
+
+ dev_dbg(battery->dev, "%s : result(%d), adc(%d)\n",
+ __func__, result, adc);
+
+ return result;
+}
+
+static bool sec_bat_check_vf_adc(struct sec_battery_info *battery)
+{
+ int adc;
+
+ adc = sec_bat_get_adc_data(battery,
+ SEC_BAT_ADC_CHANNEL_BAT_CHECK,
+ battery->pdata->adc_check_count);
+
+ if (adc < 0) {
+ dev_err(battery->dev, "%s: VF ADC error\n", __func__);
+ adc = battery->check_adc_value;
+ } else
+ battery->check_adc_value = adc;
+
+ if ((battery->check_adc_value < battery->pdata->check_adc_max) &&
+ (battery->check_adc_value >= battery->pdata->check_adc_min))
+ return true;
+ else
+ return false;
+}
+
+static bool sec_bat_check_by_psy(struct sec_battery_info *battery)
+{
+ char *psy_name;
+ union power_supply_propval value;
+ bool ret;
+ ret = true;
+
+ switch (battery->pdata->battery_check_type) {
+ case SEC_BATTERY_CHECK_PMIC:
+ psy_name = battery->pdata->pmic_name;
+ break;
+ case SEC_BATTERY_CHECK_FUELGAUGE:
+ psy_name = battery->pdata->fuelgauge_name;
+ break;
+ case SEC_BATTERY_CHECK_CHARGER:
+ psy_name = battery->pdata->charger_name;
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Invalid Battery Check Type\n", __func__);
+ ret = false;
+ goto battery_check_error;
+ break;
+ }
+
+ psy_do_property(psy_name, get,
+ POWER_SUPPLY_PROP_PRESENT, value);
+ ret = (bool)value.intval;
+
+battery_check_error:
+ return ret;
+}
+
+static bool sec_bat_check(struct sec_battery_info *battery)
+{
+ bool ret;
+ ret = true;
+
+ if (battery->pdata->check_jig_status) {
+ if (battery->factory_mode
+ || battery->pdata->check_jig_status()) {
+ dev_dbg(battery->dev, "%s: No need to check in factory mode\n",
+ __func__);
+ return ret;
+ }
+ } else if (battery->factory_mode) {
+ dev_dbg(battery->dev, "%s: No need to check in factory mode\n",
+ __func__);
+ return ret;
+ }
+
+ if (battery->health != POWER_SUPPLY_HEALTH_GOOD &&
+ battery->health != POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) {
+ dev_dbg(battery->dev, "%s: No need to check\n", __func__);
+ return ret;
+ }
+
+ switch (battery->pdata->battery_check_type) {
+ case SEC_BATTERY_CHECK_ADC:
+ ret = sec_bat_check_vf_adc(battery);
+ break;
+ case SEC_BATTERY_CHECK_INT:
+ case SEC_BATTERY_CHECK_CALLBACK:
+ if(battery->cable_type == POWER_SUPPLY_TYPE_BATTERY) {
+ ret = battery->present;
+ } else {
+ if (battery->pdata->check_battery_callback)
+ ret = battery->pdata->check_battery_callback(battery);
+ }
+ break;
+ case SEC_BATTERY_CHECK_PMIC:
+ case SEC_BATTERY_CHECK_FUELGAUGE:
+ case SEC_BATTERY_CHECK_CHARGER:
+ ret = sec_bat_check_by_psy(battery);
+ break;
+ case SEC_BATTERY_CHECK_NONE:
+ dev_info(battery->dev, "%s: No Check\n", __func__);
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static bool sec_bat_get_cable_type(
+ struct sec_battery_info *battery,
+ int cable_source_type)
+{
+ bool ret;
+ int cable_type;
+
+ ret = false;
+ cable_type = battery->cable_type;
+
+ if (cable_source_type & SEC_BATTERY_CABLE_SOURCE_CALLBACK) {
+ if (battery->pdata->check_cable_callback)
+ cable_type =
+ battery->pdata->check_cable_callback(battery);
+ }
+
+ if (cable_source_type & SEC_BATTERY_CABLE_SOURCE_ADC) {
+ if (gpio_get_value_cansleep(
+ battery->pdata->bat_gpio_ta_nconnected) ^
+ battery->pdata->bat_polarity_ta_nconnected)
+ cable_type = POWER_SUPPLY_TYPE_BATTERY;
+ else
+ cable_type =
+ sec_bat_get_charger_type_adc(battery);
+ }
+
+ if (battery->cable_type == cable_type) {
+ dev_dbg(battery->dev,
+ "%s: No need to change cable status\n", __func__);
+ } else {
+ if (cable_type < POWER_SUPPLY_TYPE_BATTERY ||
+ cable_type >= SEC_SIZEOF_POWER_SUPPLY_TYPE) {
+ dev_err(battery->dev,
+ "%s: Invalid cable type\n", __func__);
+ } else {
+ battery->cable_type = cable_type;
+ if (battery->pdata->check_cable_result_callback)
+ battery->pdata->check_cable_result_callback(
+ battery->cable_type);
+
+ ret = true;
+
+ dev_dbg(battery->dev, "%s: Cable Changed (%d)\n",
+ __func__, battery->cable_type);
+ }
+ }
+
+ return ret;
+}
+
+static bool sec_bat_battery_cable_check(struct sec_battery_info *battery)
+{
+ if (!sec_bat_check(battery)) {
+ if (battery->check_count < battery->pdata->check_count)
+ battery->check_count++;
+ else {
+ dev_err(battery->dev,
+ "%s: Battery Disconnected\n", __func__);
+ battery->present = false;
+ battery->health = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
+
+ if (battery->status !=
+ POWER_SUPPLY_STATUS_DISCHARGING) {
+ battery->status =
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ sec_bat_set_charge(battery, false);
+ }
+
+ if (battery->pdata->check_battery_result_callback)
+ battery->pdata->
+ check_battery_result_callback();
+ return false;
+ }
+ } else
+ battery->check_count = 0;
+
+ battery->present = true;
+
+ if (battery->health == POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) {
+ battery->health = POWER_SUPPLY_HEALTH_GOOD;
+
+ if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING) {
+ battery->status = POWER_SUPPLY_STATUS_CHARGING;
+
+ sec_bat_set_charge(battery, true);
+ }
+ }
+
+ dev_info(battery->dev, "%s: Battery Connected\n", __func__);
+
+ if (battery->pdata->cable_check_type &
+ SEC_BATTERY_CABLE_CHECK_POLLING) {
+ if (sec_bat_get_cable_type(battery,
+ battery->pdata->cable_source_type)) {
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue,
+ &battery->cable_work);
+ }
+ }
+ return true;
+};
+
+static int sec_bat_ovp_uvlo_by_psy(struct sec_battery_info *battery)
+{
+ char *psy_name;
+ union power_supply_propval value;
+
+ value.intval = POWER_SUPPLY_HEALTH_GOOD;
+
+ switch (battery->pdata->ovp_uvlo_check_type) {
+ case SEC_BATTERY_OVP_UVLO_PMICPOLLING:
+ psy_name = battery->pdata->pmic_name;
+ break;
+ case SEC_BATTERY_OVP_UVLO_CHGPOLLING:
+ psy_name = battery->pdata->charger_name;
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Invalid OVP/UVLO Check Type\n", __func__);
+ goto ovp_uvlo_check_error;
+ break;
+ }
+
+ psy_do_property(psy_name, get,
+ POWER_SUPPLY_PROP_HEALTH, value);
+
+ovp_uvlo_check_error:
+ return value.intval;
+}
+
+static bool sec_bat_ovp_uvlo_result(
+ struct sec_battery_info *battery, int health)
+{
+ if (battery->health != health) {
+ battery->health = health;
+ switch (health) {
+ case POWER_SUPPLY_HEALTH_GOOD:
+ dev_info(battery->dev, "%s: Safe voltage\n", __func__);
+ dev_info(battery->dev, "%s: is_recharging : %d\n", __func__, battery->is_recharging);
+ if (battery->is_recharging)
+ battery->status =
+ POWER_SUPPLY_STATUS_FULL;
+ else /* Normal Charging */
+ battery->status =
+ POWER_SUPPLY_STATUS_CHARGING;
+ break;
+ case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+ case POWER_SUPPLY_HEALTH_UNDERVOLTAGE:
+ dev_info(battery->dev,
+ "%s: Unsafe voltage (%d)\n",
+ __func__, health);
+ battery->status =
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ /* Take the wakelock during 10 seconds
+ when over-voltage status is detected */
+ wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
+ break;
+ }
+ power_supply_changed(&battery->psy_bat);
+ return true;
+ }
+
+ return false;
+}
+
+static bool sec_bat_ovp_uvlo(struct sec_battery_info *battery)
+{
+ int health;
+
+ if (battery->pdata->check_jig_status) {
+ if (battery->factory_mode
+ || battery->pdata->check_jig_status()) {
+ dev_dbg(battery->dev,
+ "%s: No need to check in factory mode\n",
+ __func__);
+ return false;
+ }
+ } else if (battery->factory_mode) {
+ dev_dbg(battery->dev, "%s: No need to check in factory mode\n",
+ __func__);
+ return false;
+ }
+
+ if (battery->health != POWER_SUPPLY_HEALTH_GOOD &&
+ battery->health != POWER_SUPPLY_HEALTH_OVERVOLTAGE &&
+ battery->health != POWER_SUPPLY_HEALTH_UNDERVOLTAGE) {
+ dev_dbg(battery->dev, "%s: No need to check\n", __func__);
+ return false;
+ }
+
+ health = battery->health;
+
+ switch (battery->pdata->ovp_uvlo_check_type) {
+ case SEC_BATTERY_OVP_UVLO_CALLBACK:
+ if (battery->pdata->ovp_uvlo_callback)
+ health = battery->pdata->ovp_uvlo_callback();
+ break;
+ case SEC_BATTERY_OVP_UVLO_PMICPOLLING:
+ case SEC_BATTERY_OVP_UVLO_CHGPOLLING:
+ health = sec_bat_ovp_uvlo_by_psy(battery);
+ break;
+ case SEC_BATTERY_OVP_UVLO_PMICINT:
+ case SEC_BATTERY_OVP_UVLO_CHGINT:
+ /* nothing for interrupt check */
+ default:
+ break;
+ }
+
+ return sec_bat_ovp_uvlo_result(battery, health);
+}
+
+static bool sec_bat_check_recharge(struct sec_battery_info *battery)
+{
+ if ((battery->status == POWER_SUPPLY_STATUS_CHARGING) &&
+ (battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) &&
+ (battery->charging_mode == SEC_BATTERY_CHARGING_NONE)) {
+ dev_info(battery->dev,
+ "%s: Re-charging by NOTIMEFULL (%d)\n",
+ __func__, battery->capacity);
+ goto check_recharge_check_count;
+ }
+
+ if (battery->status == POWER_SUPPLY_STATUS_FULL &&
+ battery->charging_mode == SEC_BATTERY_CHARGING_NONE) {
+ if ((battery->pdata->recharge_condition_type &
+ SEC_BATTERY_RECHARGE_CONDITION_SOC) &&
+ (battery->capacity <=
+ battery->pdata->recharge_condition_soc)) {
+ dev_info(battery->dev,
+ "%s: Re-charging by SOC (%d)\n",
+ __func__, battery->capacity);
+ goto check_recharge_check_count;
+ }
+
+ if ((battery->pdata->recharge_condition_type &
+ SEC_BATTERY_RECHARGE_CONDITION_AVGVCELL) &&
+ (battery->voltage_avg <=
+ battery->pdata->recharge_condition_avgvcell)) {
+ dev_info(battery->dev,
+ "%s: Re-charging by average VCELL (%d)\n",
+ __func__, battery->voltage_avg);
+ goto check_recharge_check_count;
+ }
+
+ if ((battery->pdata->recharge_condition_type &
+ SEC_BATTERY_RECHARGE_CONDITION_VCELL) &&
+ (battery->voltage_now <=
+ battery->pdata->recharge_condition_vcell)) {
+ dev_info(battery->dev,
+ "%s: Re-charging by VCELL (%d)\n",
+ __func__, battery->voltage_now);
+ goto check_recharge_check_count;
+ }
+ }
+
+ battery->recharge_check_cnt = 0;
+ return false;
+
+check_recharge_check_count:
+ if (battery->recharge_check_cnt <
+ battery->pdata->recharge_check_count)
+ battery->recharge_check_cnt++;
+ dev_dbg(battery->dev,
+ "%s: recharge count = %d\n",
+ __func__, battery->recharge_check_cnt);
+
+ if (battery->recharge_check_cnt >=
+ battery->pdata->recharge_check_count)
+ return true;
+ else
+ return false;
+}
+
+static bool sec_bat_voltage_check(struct sec_battery_info *battery)
+{
+ if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ dev_dbg(battery->dev,
+ "%s: Charging Disabled\n", __func__);
+ return true;
+ }
+
+ /* OVP/UVLO check */
+ if (sec_bat_ovp_uvlo(battery)) {
+ if (battery->pdata->ovp_uvlo_result_callback)
+ battery->pdata->
+ ovp_uvlo_result_callback(battery->health);
+ return false;
+ }
+
+ /* Re-Charging check */
+ if (sec_bat_check_recharge(battery)) {
+ if (battery->pdata->full_check_type !=
+ SEC_BATTERY_FULLCHARGED_NONE)
+ battery->charging_mode = SEC_BATTERY_CHARGING_1ST;
+ else
+ battery->charging_mode = SEC_BATTERY_CHARGING_2ND;
+ battery->is_recharging = true;
+ sec_bat_set_charge(battery, true);
+ return false;
+ }
+
+ return true;
+}
+
+static bool sec_bat_get_temperature_by_adc(
+ struct sec_battery_info *battery,
+ enum power_supply_property psp,
+ union power_supply_propval *value)
+{
+ int temp = 0;
+ int temp_adc;
+ int low = 0;
+ int high = 0;
+ int mid = 0;
+ int channel;
+ const sec_bat_adc_table_data_t *temp_adc_table;
+ unsigned int temp_adc_table_size;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP:
+ channel = SEC_BAT_ADC_CHANNEL_TEMP;
+ temp_adc_table = battery->pdata->temp_adc_table;
+ temp_adc_table_size =
+ battery->pdata->temp_adc_table_size;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_AMBIENT:
+ channel = SEC_BAT_ADC_CHANNEL_TEMP_AMBIENT;
+ temp_adc_table = battery->pdata->temp_amb_adc_table;
+ temp_adc_table_size =
+ battery->pdata->temp_amb_adc_table_size;
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Invalid Property\n", __func__);
+ return false;
+ }
+
+ temp_adc = sec_bat_get_adc_value(battery, channel);
+ if (temp_adc < 0)
+ return true;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_TEMP:
+ battery->temp_adc = temp_adc;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_AMBIENT:
+ battery->temp_ambient_adc = temp_adc;
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Invalid Property\n", __func__);
+ return false;
+ }
+
+ if (temp_adc_table[0].adc >= temp_adc) {
+ temp = temp_adc_table[0].data;
+ goto temp_by_adc_goto;
+ } else if (temp_adc_table[temp_adc_table_size-1].adc <= temp_adc) {
+ temp = temp_adc_table[temp_adc_table_size-1].data;
+ goto temp_by_adc_goto;
+ }
+
+ high = temp_adc_table_size - 1;
+
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (temp_adc_table[mid].adc > temp_adc)
+ high = mid - 1;
+ else if (temp_adc_table[mid].adc < temp_adc)
+ low = mid + 1;
+ else {
+ temp = temp_adc_table[mid].data;
+ goto temp_by_adc_goto;
+ }
+ }
+
+ temp = temp_adc_table[high].data;
+ temp += (((int)temp_adc_table[low].data - (int)temp_adc_table[high].data) *
+ ((int)temp_adc - (int)temp_adc_table[high].adc)) /
+ ((int)temp_adc_table[low].adc - (int)temp_adc_table[high].adc);
+
+temp_by_adc_goto:
+ value->intval = temp;
+
+ dev_info(battery->dev,
+ "%s: Temp(%d), Temp-ADC(%d)\n",
+ __func__, temp, temp_adc);
+
+ return true;
+}
+
+static bool sec_bat_temperature(
+ struct sec_battery_info *battery)
+{
+ bool ret;
+ ret = true;
+
+ if (battery->pdata->event_check && battery->event) {
+ battery->temp_high_threshold =
+ battery->pdata->temp_high_threshold_event;
+ battery->temp_high_recovery =
+ battery->pdata->temp_high_recovery_event;
+ battery->temp_low_recovery =
+ battery->pdata->temp_low_recovery_event;
+ battery->temp_low_threshold =
+ battery->pdata->temp_low_threshold_event;
+ } else if (battery->pdata->is_lpm && battery->pdata->is_lpm()) {
+ battery->temp_high_threshold =
+ battery->pdata->temp_high_threshold_lpm;
+ battery->temp_high_recovery =
+ battery->pdata->temp_high_recovery_lpm;
+ battery->temp_low_recovery =
+ battery->pdata->temp_low_recovery_lpm;
+ battery->temp_low_threshold =
+ battery->pdata->temp_low_threshold_lpm;
+ } else {
+ battery->temp_high_threshold =
+ battery->pdata->temp_high_threshold_normal;
+ battery->temp_high_recovery =
+ battery->pdata->temp_high_recovery_normal;
+ battery->temp_low_recovery =
+ battery->pdata->temp_low_recovery_normal;
+ battery->temp_low_threshold =
+ battery->pdata->temp_low_threshold_normal;
+ }
+
+ dev_info(battery->dev,
+ "%s: HT(%d), HR(%d), LT(%d), LR(%d)\n",
+ __func__, battery->temp_high_threshold,
+ battery->temp_high_recovery,
+ battery->temp_low_threshold,
+ battery->temp_low_recovery);
+ return ret;
+}
+
+static bool sec_bat_temperature_check(
+ struct sec_battery_info *battery)
+{
+ int temp_value;
+ bool health_changed;
+
+ /* set health_changed true as default
+ * this value will be changed after temperature check
+ */
+ health_changed = true;
+
+ if (battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ dev_dbg(battery->dev,
+ "%s: Charging Disabled\n", __func__);
+ return true;
+ }
+
+ if (battery->health != POWER_SUPPLY_HEALTH_GOOD &&
+ battery->health != POWER_SUPPLY_HEALTH_OVERHEAT &&
+ battery->health != POWER_SUPPLY_HEALTH_COLD) {
+ dev_dbg(battery->dev, "%s: No need to check\n", __func__);
+ return false;
+ }
+
+ sec_bat_temperature(battery);
+
+ switch (battery->pdata->temp_check_type) {
+ case SEC_BATTERY_TEMP_CHECK_ADC:
+ temp_value = battery->temp_adc;
+ break;
+ case SEC_BATTERY_TEMP_CHECK_TEMP:
+ temp_value = battery->temperature;
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Invalid Temp Check Type\n", __func__);
+ return true;
+ }
+
+ if (temp_value >= battery->temp_high_threshold) {
+ if (battery->health != POWER_SUPPLY_HEALTH_OVERHEAT) {
+ if (battery->temp_high_cnt <
+ battery->pdata->temp_check_count)
+ battery->temp_high_cnt++;
+ dev_dbg(battery->dev,
+ "%s: high count = %d\n",
+ __func__, battery->temp_high_cnt);
+ }
+ } else if ((temp_value <= battery->temp_high_recovery) &&
+ (temp_value >= battery->temp_low_recovery)) {
+ if (battery->health == POWER_SUPPLY_HEALTH_OVERHEAT ||
+ battery->health == POWER_SUPPLY_HEALTH_COLD) {
+ if (battery->temp_recover_cnt <
+ battery->pdata->temp_check_count)
+ battery->temp_recover_cnt++;
+ dev_dbg(battery->dev,
+ "%s: recovery count = %d\n",
+ __func__, battery->temp_recover_cnt);
+ }
+ } else if (temp_value <= battery->temp_low_threshold) {
+ if (battery->health != POWER_SUPPLY_HEALTH_COLD) {
+ if (battery->temp_low_cnt <
+ battery->pdata->temp_check_count)
+ battery->temp_low_cnt++;
+ dev_dbg(battery->dev,
+ "%s: low count = %d\n",
+ __func__, battery->temp_low_cnt);
+ }
+ } else {
+ battery->temp_high_cnt = 0;
+ battery->temp_low_cnt = 0;
+ battery->temp_recover_cnt = 0;
+ }
+
+ if (battery->temp_high_cnt >=
+ battery->pdata->temp_check_count)
+ battery->health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ else if (battery->temp_low_cnt >=
+ battery->pdata->temp_check_count)
+ battery->health = POWER_SUPPLY_HEALTH_COLD;
+ else if (battery->temp_recover_cnt >=
+ battery->pdata->temp_check_count)
+ battery->health = POWER_SUPPLY_HEALTH_GOOD;
+ else
+ health_changed = false;
+
+ if (health_changed) {
+ if ((battery->health == POWER_SUPPLY_HEALTH_OVERHEAT) ||
+ (battery->health == POWER_SUPPLY_HEALTH_COLD)) {
+ dev_info(battery->dev,
+ "%s: Unsafe Temperature\n", __func__);
+ battery->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ /* change charging current to battery (default 0mA) */
+ sec_bat_set_charge(battery, false);
+ } else {
+ dev_info(battery->dev,
+ "%s: Safe Temperature\n", __func__);
+ /* if recovered from not charging */
+ if ((battery->health == POWER_SUPPLY_HEALTH_GOOD) &&
+ (battery->status ==
+ POWER_SUPPLY_STATUS_NOT_CHARGING)) {
+ if (battery->is_recharging)
+ battery->status =
+ POWER_SUPPLY_STATUS_FULL;
+ else /* Normal Charging */
+ battery->status =
+ POWER_SUPPLY_STATUS_CHARGING;
+ /* turn on charger by cable type */
+ sec_bat_set_charge(battery, true);
+ }
+ }
+ return false;
+ }
+ return true;
+};
+
+static void sec_bat_event_program_alarm(
+ struct sec_battery_info *battery, int seconds)
+{
+#if defined(ANDROID_ALARM_ACTIVATED)
+ ktime_t low_interval = ktime_set(seconds - 10, 0);
+ ktime_t slack = ktime_set(20, 0);
+ ktime_t next;
+
+ next = ktime_add(battery->last_event_time, low_interval);
+
+ alarm_start_range(&battery->event_termination_alarm,
+ next, ktime_add(next, slack));
+#else
+ alarm_start(&battery->event_termination_alarm,
+ ktime_add(battery->last_event_time, ktime_set(seconds - 10, 0)));
+#endif
+}
+
+#if defined(ANDROID_ALARM_ACTIVATED)
+static void sec_bat_event_expired_timer_func(struct alarm *alarm)
+#else
+static enum alarmtimer_restart sec_bat_event_expired_timer_func(
+ struct alarm *alarm, ktime_t now)
+#endif
+{
+ struct sec_battery_info *battery =
+ container_of(alarm, struct sec_battery_info,
+ event_termination_alarm);
+
+ battery->event &= (~battery->event_wait);
+ dev_info(battery->dev,
+ "%s: event expired (0x%x)\n", __func__, battery->event);
+
+#if !defined(ANDROID_ALARM_ACTIVATED)
+ return ALARMTIMER_NORESTART;
+#endif
+}
+
+static void sec_bat_event_set(
+ struct sec_battery_info *battery, int event, int enable)
+{
+ if (!battery->pdata->event_check)
+ return;
+
+ /* ignore duplicated deactivation of same event
+ * only if the event is one last event
+ */
+ if (!enable && (battery->event == battery->event_wait)) {
+ dev_info(battery->dev,
+ "%s: ignore duplicated deactivation of same event\n",
+ __func__);
+ return;
+ }
+
+ alarm_cancel(&battery->event_termination_alarm);
+ battery->event &= (~battery->event_wait);
+
+ if (enable) {
+ battery->event_wait = 0;
+ battery->event |= event;
+
+ dev_info(battery->dev,
+ "%s: event set (0x%x)\n", __func__, battery->event);
+ } else {
+ if (battery->event == 0) {
+ dev_dbg(battery->dev,
+ "%s: nothing to clear\n", __func__);
+ return; /* nothing to clear */
+ }
+ battery->event_wait = event;
+#if defined(ANDROID_ALARM_ACTIVATED)
+ battery->last_event_time = alarm_get_elapsed_realtime();
+#else
+ battery->last_event_time = ktime_get_boottime();
+#endif
+ sec_bat_event_program_alarm(battery,
+ battery->pdata->event_waiting_time);
+ dev_info(battery->dev,
+ "%s: start timer (curr 0x%x, wait 0x%x)\n",
+ __func__, battery->event, battery->event_wait);
+ }
+}
+
+static bool sec_bat_check_fullcharged_condition(
+ struct sec_battery_info *battery)
+{
+ int full_check_type;
+
+ if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST)
+ full_check_type = battery->pdata->full_check_type;
+ else
+ full_check_type = battery->pdata->full_check_type_2nd;
+
+ switch (full_check_type) {
+ case SEC_BATTERY_FULLCHARGED_ADC:
+ case SEC_BATTERY_FULLCHARGED_FG_CURRENT:
+ case SEC_BATTERY_FULLCHARGED_SOC:
+ case SEC_BATTERY_FULLCHARGED_CHGGPIO:
+ case SEC_BATTERY_FULLCHARGED_CHGPSY:
+ break;
+
+ /* If these is NOT full check type or NONE full check type,
+ * it is full-charged
+ */
+ case SEC_BATTERY_FULLCHARGED_CHGINT:
+ case SEC_BATTERY_FULLCHARGED_TIME:
+ case SEC_BATTERY_FULLCHARGED_NONE:
+ default:
+ return true;
+ break;
+ }
+
+ if (battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_SOC) {
+ if (battery->capacity <
+ battery->pdata->full_condition_soc) {
+ dev_dbg(battery->dev,
+ "%s: Not enough SOC (%d%%)\n",
+ __func__, battery->capacity);
+ return false;
+ }
+ }
+
+ if (battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_VCELL) {
+ if (battery->voltage_now <
+ battery->pdata->full_condition_vcell) {
+ dev_dbg(battery->dev,
+ "%s: Not enough VCELL (%dmV)\n",
+ __func__, battery->voltage_now);
+ return false;
+ }
+ }
+
+ if (battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_AVGVCELL) {
+ if (battery->voltage_avg <
+ battery->pdata->full_condition_avgvcell) {
+ dev_dbg(battery->dev,
+ "%s: Not enough AVGVCELL (%dmV)\n",
+ __func__, battery->voltage_avg);
+ return false;
+ }
+ }
+
+ if (battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_OCV) {
+ if (battery->voltage_ocv <
+ battery->pdata->full_condition_ocv) {
+ dev_dbg(battery->dev,
+ "%s: Not enough OCV (%dmV)\n",
+ __func__, battery->voltage_ocv);
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static void sec_bat_do_test_function(
+ struct sec_battery_info *battery)
+{
+ union power_supply_propval value;
+
+ switch (battery->test_mode) {
+ case 1:
+ if (battery->status == POWER_SUPPLY_STATUS_CHARGING) {
+ sec_bat_set_charge(battery, false);
+ battery->status =
+ POWER_SUPPLY_STATUS_DISCHARGING;
+ }
+ break;
+ case 2:
+ if(battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ sec_bat_set_charge(battery, true);
+ psy_do_property(battery->pdata->charger_name, get,
+ POWER_SUPPLY_PROP_STATUS, value);
+ battery->status = value.intval;
+ }
+ battery->test_mode = 0;
+ break;
+ case 3: // clear temp block
+ battery->health = POWER_SUPPLY_HEALTH_GOOD;
+ battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ break;
+ case 4:
+ if(battery->status == POWER_SUPPLY_STATUS_DISCHARGING) {
+ sec_bat_set_charge(battery, true);
+ psy_do_property(battery->pdata->charger_name, get,
+ POWER_SUPPLY_PROP_STATUS, value);
+ battery->status = value.intval;
+ }
+ break;
+ default:
+ pr_info("%s: error test: unknown state\n", __func__);
+ break;
+ }
+}
+
+static bool sec_bat_time_management(
+ struct sec_battery_info *battery)
+{
+ unsigned long charging_time;
+ struct timespec ts;
+#if defined(ANDROID_ALARM_ACTIVATED)
+ ktime_t current_time;
+
+ current_time = alarm_get_elapsed_realtime();
+ ts = ktime_to_timespec(current_time);
+#else
+ get_monotonic_boottime(&ts);
+#endif
+
+ if (battery->charging_start_time == 0) {
+ dev_dbg(battery->dev,
+ "%s: Charging Disabled\n", __func__);
+ return true;
+ }
+
+ if (ts.tv_sec >= battery->charging_start_time)
+ charging_time = ts.tv_sec - battery->charging_start_time;
+ else
+ charging_time = 0xFFFFFFFF - battery->charging_start_time
+ + ts.tv_sec;
+
+ battery->charging_passed_time = charging_time;
+
+ dev_dbg(battery->dev,
+ "%s: Charging Time : %ld secs\n", __func__,
+ battery->charging_passed_time);
+
+ switch (battery->status) {
+ case POWER_SUPPLY_STATUS_FULL:
+ if (battery->is_recharging && (charging_time >
+ battery->pdata->recharging_total_time)) {
+ dev_info(battery->dev,
+ "%s: Recharging Timer Expired\n", __func__);
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ battery->is_recharging = false;
+ if (sec_bat_set_charge(battery, false)) {
+ dev_err(battery->dev,
+ "%s: Fail to Set Charger\n", __func__);
+ return true;
+ }
+
+ return false;
+ }
+ break;
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if ((battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) &&
+ (battery->is_recharging && (charging_time >
+ battery->pdata->recharging_total_time))) {
+ dev_info(battery->dev,
+ "%s: Recharging Timer Expired\n", __func__);
+ if (battery->capacity >= 100)
+ battery->status = POWER_SUPPLY_STATUS_FULL;
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ battery->is_recharging = false;
+ if (sec_bat_set_charge(battery, false)) {
+ dev_err(battery->dev,
+ "%s: Fail to Set Charger\n", __func__);
+ return true;
+ }
+ return false;
+ } else if (!battery->is_recharging &&
+ (charging_time > battery->pdata->charging_total_time)) {
+ dev_info(battery->dev,
+ "%s: Charging Timer Expired\n", __func__);
+ if (battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_NOTIMEFULL) {
+ if (battery->capacity >= 100)
+ battery->status =
+ POWER_SUPPLY_STATUS_FULL;
+ } else
+ battery->status = POWER_SUPPLY_STATUS_FULL;
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ if (sec_bat_set_charge(battery, false)) {
+ dev_err(battery->dev,
+ "%s: Fail to Set Charger\n", __func__);
+ return true;
+ }
+
+ return false;
+ }
+ if (battery->pdata->charging_reset_time) {
+ if (charging_time > battery->charging_next_time) {
+ /*reset current in charging status */
+ battery->charging_next_time =
+ battery->charging_passed_time +
+ (battery->pdata->charging_reset_time);
+
+ dev_dbg(battery->dev,
+ "%s: Reset charging current\n",
+ __func__);
+ if (sec_bat_set_charge(battery, true)) {
+ dev_err(battery->dev,
+ "%s: Fail to Set Charger\n",
+ __func__);
+ return true;
+ }
+ }
+ }
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Undefine Battery Status\n", __func__);
+ return true;
+ }
+
+ return true;
+}
+
+static bool sec_bat_check_fullcharged(
+ struct sec_battery_info *battery)
+{
+ union power_supply_propval value;
+ int current_adc;
+ int full_check_type;
+ bool ret;
+ int err;
+
+ ret = false;
+
+ if (!sec_bat_check_fullcharged_condition(battery))
+ goto not_full_charged;
+
+ if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST)
+ full_check_type = battery->pdata->full_check_type;
+ else
+ full_check_type = battery->pdata->full_check_type_2nd;
+
+ switch (full_check_type) {
+ case SEC_BATTERY_FULLCHARGED_ADC:
+ current_adc =
+ sec_bat_get_adc_value(battery,
+ SEC_BAT_ADC_CHANNEL_FULL_CHECK);
+
+ dev_dbg(battery->dev,
+ "%s: Current ADC (%d)\n",
+ __func__, current_adc);
+
+ if (current_adc < 0)
+ break;
+ battery->current_adc = current_adc;
+
+ if (battery->current_adc <
+ (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_1ST ?
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_1st :
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_2nd)) {
+ battery->full_check_cnt++;
+ dev_dbg(battery->dev,
+ "%s: Full Check ADC (%d)\n",
+ __func__,
+ battery->full_check_cnt);
+ } else
+ battery->full_check_cnt = 0;
+ break;
+
+ case SEC_BATTERY_FULLCHARGED_FG_CURRENT:
+ if ((battery->current_now > 20 && battery->current_now <
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_1st) &&
+ (battery->current_avg > 20 && battery->current_avg <
+ (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_1ST ?
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_1st :
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_2nd))) {
+ battery->full_check_cnt++;
+ dev_dbg(battery->dev,
+ "%s: Full Check Current (%d)\n",
+ __func__,
+ battery->full_check_cnt);
+ } else
+ battery->full_check_cnt = 0;
+ break;
+
+ case SEC_BATTERY_FULLCHARGED_TIME:
+ if ((battery->charging_mode ==
+ SEC_BATTERY_CHARGING_2ND ?
+ (battery->charging_passed_time -
+ battery->charging_fullcharged_time) :
+ battery->charging_passed_time) >
+ (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_1ST ?
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_1st :
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_2nd)) {
+ battery->full_check_cnt++;
+ dev_dbg(battery->dev,
+ "%s: Full Check Time (%d)\n",
+ __func__,
+ battery->full_check_cnt);
+ } else
+ battery->full_check_cnt = 0;
+ break;
+
+ case SEC_BATTERY_FULLCHARGED_SOC:
+ if (battery->capacity <=
+ (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_1ST ?
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_1st :
+ battery->pdata->charging_current[
+ battery->cable_type].full_check_current_2nd)) {
+ battery->full_check_cnt++;
+ dev_dbg(battery->dev,
+ "%s: Full Check SOC (%d)\n",
+ __func__,
+ battery->full_check_cnt);
+ } else
+ battery->full_check_cnt = 0;
+ break;
+
+ case SEC_BATTERY_FULLCHARGED_CHGGPIO:
+ err = gpio_request(
+ battery->pdata->chg_gpio_full_check,
+ "GPIO_CHG_FULL");
+ if (err) {
+ dev_err(battery->dev,
+ "%s: Error in Request of GPIO\n", __func__);
+ break;
+ }
+ if (!(gpio_get_value_cansleep(
+ battery->pdata->chg_gpio_full_check) ^
+ !battery->pdata->chg_polarity_full_check)) {
+ battery->full_check_cnt++;
+ dev_dbg(battery->dev,
+ "%s: Full Check GPIO (%d)\n",
+ __func__, battery->full_check_cnt);
+ } else
+ battery->full_check_cnt = 0;
+ gpio_free(battery->pdata->chg_gpio_full_check);
+ break;
+
+ case SEC_BATTERY_FULLCHARGED_CHGINT:
+ case SEC_BATTERY_FULLCHARGED_CHGPSY:
+ psy_do_property(battery->pdata->charger_name, get,
+ POWER_SUPPLY_PROP_STATUS, value);
+
+ if (value.intval == POWER_SUPPLY_STATUS_FULL) {
+ battery->full_check_cnt++;
+ dev_info(battery->dev,
+ "%s: Full Check Charger (%d)\n",
+ __func__, battery->full_check_cnt);
+ } else {
+ battery->full_check_cnt = 0;
+ }
+ break;
+
+ /* If these is NOT full check type or NONE full check type,
+ * it is full-charged
+ */
+ case SEC_BATTERY_FULLCHARGED_NONE:
+ battery->full_check_cnt = 0;
+ ret = true;
+ break;
+ default:
+ dev_err(battery->dev,
+ "%s: Invalid Full Check\n", __func__);
+ break;
+ }
+
+ if (battery->full_check_cnt >=
+ battery->pdata->full_check_count) {
+ battery->full_check_cnt = 0;
+ ret = true;
+ }
+
+not_full_charged:
+ return ret;
+}
+
+static void sec_bat_do_fullcharged(
+ struct sec_battery_info *battery)
+{
+ union power_supply_propval value;
+
+ /* To let charger/fuel gauge know the full status,
+ * set status before calling sec_bat_set_charge()
+ */
+ battery->status = POWER_SUPPLY_STATUS_FULL;
+
+ if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST) {
+ battery->charging_mode = SEC_BATTERY_CHARGING_2ND;
+ battery->charging_fullcharged_time =
+ battery->charging_passed_time;
+ sec_bat_set_charge(battery, true);
+ } else {
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ battery->is_recharging = false;
+ sec_bat_set_charge(battery, false);
+
+ value.intval = POWER_SUPPLY_STATUS_FULL;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_STATUS, value);
+ }
+
+ /* platform can NOT get information of battery
+ * because wakeup time is too short to check uevent
+ * To make sure that target is wakeup if full-charged,
+ * activated wake lock in a few seconds
+ */
+ if (battery->pdata->polling_type == SEC_BATTERY_MONITOR_ALARM)
+ wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
+}
+
+static bool sec_bat_fullcharged_check(
+ struct sec_battery_info *battery)
+{
+ if ((battery->charging_mode == SEC_BATTERY_CHARGING_NONE) ||
+ (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING)) {
+ dev_dbg(battery->dev,
+ "%s: No Need to Check Full-Charged\n", __func__);
+ return true;
+ }
+
+ if (sec_bat_check_fullcharged(battery))
+ sec_bat_do_fullcharged(battery);
+
+ dev_info(battery->dev,
+ "%s: Charging Mode : %s\n", __func__,
+ battery->is_recharging ?
+ sec_bat_charging_mode_str[SEC_BATTERY_CHARGING_RECHARGING] :
+ sec_bat_charging_mode_str[battery->charging_mode]);
+
+ return true;
+}
+
+#if defined(CONFIG_TOUCHSCREEN_IST30XXA)
+int battery_temp;
+#endif
+
+static void sec_bat_get_battery_info(
+ struct sec_battery_info *battery)
+{
+ union power_supply_propval value;
+
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, value);
+ battery->voltage_now = value.intval;
+
+ value.intval = SEC_BATTEY_VOLTAGE_AVERAGE;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG, value);
+ battery->voltage_avg = value.intval;
+
+ value.intval = SEC_BATTEY_VOLTAGE_OCV;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG, value);
+ battery->voltage_ocv = value.intval;
+
+ value.intval = SEC_BATTEY_CURRENT_MA;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CURRENT_NOW, value);
+ battery->current_now = value.intval;
+
+ value.intval = SEC_BATTEY_CURRENT_MA;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CURRENT_AVG, value);
+ battery->current_avg = value.intval;
+
+ /* input current limit in charger */
+ psy_do_property(battery->pdata->charger_name, get,
+ POWER_SUPPLY_PROP_CURRENT_MAX, value);
+ battery->current_max = value.intval;
+
+ /* To get SOC value (NOT raw SOC), need to reset value */
+ value.intval = 0;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CAPACITY, value);
+ battery->capacity = value.intval;
+#if defined(CONFIG_MACH_CORSICA_VE)
+ if (get_hw_rev() < 0x2) {
+ dev_info(battery->dev,
+ "%s: get_hw_rev (%d)\n",
+ __func__, get_hw_rev());
+ battery->capacity = 50;
+ battery->voltage_ocv = 4000;
+ battery->voltage_avg = 4000;
+ battery->voltage_now = 4000;
+ }
+#endif
+
+ switch (battery->pdata->thermal_source) {
+ case SEC_BATTERY_THERMAL_SOURCE_FG:
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_TEMP, value);
+ battery->temperature = value.intval;
+
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_TEMP_AMBIENT, value);
+ battery->temper_amb = value.intval;
+ break;
+ case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
+ if (battery->pdata->get_temperature_callback) {
+ battery->pdata->get_temperature_callback(
+ POWER_SUPPLY_PROP_TEMP, &value);
+ battery->temperature = value.intval;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_TEMP, value);
+
+ battery->pdata->get_temperature_callback(
+ POWER_SUPPLY_PROP_TEMP_AMBIENT, &value);
+ battery->temper_amb = value.intval;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_TEMP_AMBIENT, value);
+ }
+ break;
+ case SEC_BATTERY_THERMAL_SOURCE_ADC:
+ sec_bat_get_temperature_by_adc(battery,
+ POWER_SUPPLY_PROP_TEMP, &value);
+ battery->temperature = value.intval;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_TEMP, value);
+
+ sec_bat_get_temperature_by_adc(battery,
+ POWER_SUPPLY_PROP_TEMP_AMBIENT, &value);
+ battery->temper_amb = value.intval;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_TEMP_AMBIENT, value);
+ break;
+ default:
+ break;
+ }
+
+#if defined(CONFIG_TOUCHSCREEN_IST30XXA)
+ battery_temp = battery->temperature;
+#endif
+
+ dev_info(battery->dev,
+ "%s:Vnow(%dmV),Inow(%dmA),Imax(%dmA),SOC(%d%%),Tbat(%d)\n",
+ __func__,
+ battery->voltage_now, battery->current_now,
+ battery->current_max, battery->capacity, battery->temperature);
+ dev_dbg(battery->dev,
+ "%s,Vavg(%dmV),Vocv(%dmV),Tamb(%d),"
+ "Iavg(%dmA),Iadc(%d)\n",
+ battery->present ? "Connected" : "Disconnected",
+ battery->voltage_avg, battery->voltage_ocv,
+ battery->temper_amb,
+ battery->current_avg, battery->current_adc);
+}
+
+static void sec_bat_polling_work(struct work_struct *work)
+{
+ struct sec_battery_info *battery = container_of(
+ work, struct sec_battery_info, polling_work.work);
+
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ dev_dbg(battery->dev, "%s: Activated\n", __func__);
+}
+
+static void sec_bat_program_alarm(
+ struct sec_battery_info *battery, int seconds)
+{
+#if defined(ANDROID_ALARM_ACTIVATED)
+ ktime_t low_interval = ktime_set(seconds, 0);
+ ktime_t slack = ktime_set(10, 0);
+ ktime_t next;
+
+ next = ktime_add(battery->last_poll_time, low_interval);
+ alarm_start_range(&battery->polling_alarm,
+ next, ktime_add(next, slack));
+#else
+ alarm_start(&battery->polling_alarm,
+ ktime_add(battery->last_poll_time, ktime_set(seconds, 0)));
+#endif
+}
+
+
+static unsigned int sec_bat_get_polling_time(
+ struct sec_battery_info *battery)
+{
+ if (battery->status ==
+ POWER_SUPPLY_STATUS_FULL)
+ battery->polling_time =
+ battery->pdata->polling_time[
+ POWER_SUPPLY_STATUS_CHARGING];
+ else
+ battery->polling_time =
+ battery->pdata->polling_time[
+ battery->status];
+
+ battery->polling_short = true;
+
+ switch (battery->status) {
+ case POWER_SUPPLY_STATUS_CHARGING:
+ if (battery->polling_in_sleep)
+ battery->polling_short = false;
+ break;
+ case POWER_SUPPLY_STATUS_DISCHARGING:
+ if (battery->polling_in_sleep)
+ battery->polling_time =
+ battery->pdata->polling_time[
+ SEC_BATTERY_POLLING_TIME_SLEEP];
+ else
+ battery->polling_time =
+ battery->pdata->polling_time[
+ battery->status];
+ battery->polling_short = false;
+ break;
+ case POWER_SUPPLY_STATUS_FULL:
+ if (battery->polling_in_sleep) {
+ if (!(battery->pdata->full_condition_type &
+ SEC_BATTERY_FULL_CONDITION_NOSLEEPINFULL) &&
+ battery->charging_mode ==
+ SEC_BATTERY_CHARGING_NONE)
+ battery->polling_time =
+ battery->pdata->polling_time[
+ SEC_BATTERY_POLLING_TIME_SLEEP];
+ battery->polling_short = false;
+ } else {
+ if (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_NONE)
+ battery->polling_short = false;
+ }
+ break;
+ }
+
+ if (battery->polling_short)
+ return battery->pdata->polling_time[
+ SEC_BATTERY_POLLING_TIME_BASIC];
+ else
+ return battery->polling_time;
+}
+
+static bool sec_bat_is_short_polling(
+ struct sec_battery_info *battery)
+{
+ /* Change the full and short monitoring sequence
+ * Originally, full monitoring was the last time of polling_count
+ * But change full monitoring to first time
+ * because temperature check is too late
+ */
+ if (!battery->polling_short || battery->polling_count == 1)
+ return false;
+ else
+ return true;
+}
+
+static void sec_bat_update_polling_count(
+ struct sec_battery_info *battery)
+{
+ /* do NOT change polling count in sleep
+ * even though it is short polling
+ * to keep polling count along sleep/wakeup
+ */
+ if (battery->polling_short && battery->polling_in_sleep)
+ return;
+
+ if (battery->polling_short &&
+ ((battery->polling_time /
+ battery->pdata->polling_time[
+ SEC_BATTERY_POLLING_TIME_BASIC])
+ > battery->polling_count))
+ battery->polling_count++;
+ else
+ battery->polling_count = 1; /* initial value = 1 */
+}
+
+static void sec_bat_set_polling(
+ struct sec_battery_info *battery)
+{
+ unsigned int polling_time_temp;
+
+ dev_dbg(battery->dev, "%s: Start\n", __func__);
+
+ polling_time_temp = sec_bat_get_polling_time(battery);
+
+ dev_dbg(battery->dev,
+ "%s: Status:%s, Sleep:%s, Charging:%s, Short Poll:%s\n",
+ __func__, sec_bat_status_str[battery->status],
+ battery->polling_in_sleep ? "Yes" : "No",
+ (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_NONE) ? "No" : "Yes",
+ battery->polling_short ? "Yes" : "No");
+ dev_dbg(battery->dev,
+ "%s: Polling time %d/%d sec.\n", __func__,
+ battery->polling_short ?
+ (polling_time_temp * battery->polling_count) :
+ polling_time_temp, battery->polling_time);
+
+ /* To sync with log above,
+ * change polling count after log is displayed
+ * Do NOT update polling count in initial monitor
+ */
+ if (!battery->pdata->monitor_initial_count)
+ sec_bat_update_polling_count(battery);
+ else
+ dev_dbg(battery->dev,
+ "%s: Initial monitor %d times left.\n", __func__,
+ battery->pdata->monitor_initial_count);
+
+ switch (battery->pdata->polling_type) {
+ case SEC_BATTERY_MONITOR_WORKQUEUE:
+ if (battery->pdata->monitor_initial_count) {
+ battery->pdata->monitor_initial_count--;
+ schedule_delayed_work(&battery->polling_work, HZ);
+ } else
+ schedule_delayed_work(&battery->polling_work,
+ polling_time_temp * HZ);
+ break;
+ case SEC_BATTERY_MONITOR_ALARM:
+#if defined(ANDROID_ALARM_ACTIVATED)
+ battery->last_poll_time = alarm_get_elapsed_realtime();
+#else
+ battery->last_poll_time = ktime_get_boottime();
+#endif
+
+ if (battery->pdata->monitor_initial_count) {
+ battery->pdata->monitor_initial_count--;
+ sec_bat_program_alarm(battery, 1);
+ } else
+ sec_bat_program_alarm(battery, polling_time_temp);
+ break;
+ case SEC_BATTERY_MONITOR_TIMER:
+ break;
+ default:
+ break;
+ }
+ dev_dbg(battery->dev, "%s: End\n", __func__);
+}
+
+static void sec_bat_monitor_work(
+ struct work_struct *work)
+{
+ struct sec_battery_info *battery =
+ container_of(work, struct sec_battery_info,
+ monitor_work.work);
+
+ dev_dbg(battery->dev, "%s: Start\n", __func__);
+
+/*#if !defined(ANDROID_ALARM_ACTIVATED)
+ alarm_cancel(&battery->polling_alarm);
+#endif *//* ANDROID_ALARM_ACTIVATED */
+
+ /* monitor once after wakeup */
+ if (battery->polling_in_sleep)
+ battery->polling_in_sleep = false;
+
+ sec_bat_get_battery_info(battery);
+
+ /* 0. test mode */
+ if (battery->test_mode) {
+ dev_dbg(battery->dev, "%s: Test Mode\n", __func__);
+ sec_bat_do_test_function(battery);
+ if (battery->test_mode != 0)
+ goto continue_monitor;
+ }
+
+ /* 1. battery check */
+ if (!sec_bat_battery_cable_check(battery))
+ goto continue_monitor;
+
+ /* 2. voltage check */
+ if (!sec_bat_voltage_check(battery))
+ goto continue_monitor;
+
+ /* monitor short routine in initial monitor */
+ if (battery->pdata->monitor_initial_count ||
+ sec_bat_is_short_polling(battery))
+ goto continue_monitor;
+
+ /* 3. time management */
+ if (!sec_bat_time_management(battery))
+ goto continue_monitor;
+
+ /* 4. temperature check */
+ if (!sec_bat_temperature_check(battery))
+ goto continue_monitor;
+
+ /* 5. full charging check */
+ sec_bat_fullcharged_check(battery);
+
+ /* 6. additional check */
+ if (battery->pdata->monitor_additional_check)
+ battery->pdata->monitor_additional_check();
+
+continue_monitor:
+ dev_info(battery->dev,
+ "%s: Status(%s), mode(%s), Health(%s), Cable(%d), siop_level(%d)\n",
+ __func__,
+ sec_bat_status_str[battery->status],
+ sec_bat_charging_mode_str[battery->charging_mode],
+ sec_bat_health_str[battery->health],
+ battery->cable_type, battery->siop_level);
+
+#if defined(CONFIG_SAMSUNG_BATTERY_ENG_TEST)
+ dev_info(battery->dev,
+ "%s: battery->stability_test(%d), battery->eng_not_full_status(%d)\n",
+ __func__, battery->stability_test, battery->eng_not_full_status);
+#endif
+ power_supply_changed(&battery->psy_bat);
+
+ sec_bat_set_polling(battery);
+
+ if (battery->capacity <= 0)
+ wake_lock_timeout(&battery->monitor_wake_lock, HZ * 5);
+ else
+ wake_unlock(&battery->monitor_wake_lock);
+
+ dev_dbg(battery->dev, "%s: End\n", __func__);
+
+ return;
+}
+
+#if defined(ANDROID_ALARM_ACTIVATED)
+static void sec_bat_alarm(struct alarm *alarm)
+#else
+static enum alarmtimer_restart sec_bat_alarm(
+ struct alarm *alarm, ktime_t now)
+
+#endif
+{
+ struct sec_battery_info *battery = container_of(alarm,
+ struct sec_battery_info, polling_alarm);
+
+ dev_dbg(battery->dev,
+ "%s\n", __func__);
+
+ /* In wake up, monitor work will be queued in complete function
+ * To avoid duplicated queuing of monitor work,
+ * do NOT queue monitor work in wake up by polling alarm
+ */
+ if (!battery->polling_in_sleep) {
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ dev_dbg(battery->dev, "%s: Activated\n", __func__);
+ }
+#if !defined(ANDROID_ALARM_ACTIVATED)
+ return ALARMTIMER_NORESTART;
+#endif
+}
+
+
+static void sec_bat_cable_work(struct work_struct *work)
+{
+ struct sec_battery_info *battery = container_of(work,
+ struct sec_battery_info, cable_work);
+ union power_supply_propval val;
+ int wl_cur, wr_cur, current_cable_type;
+
+ dev_info(battery->dev, "%s: Start\n", __func__);
+
+ wl_cur = battery->pdata->charging_current[
+ POWER_SUPPLY_TYPE_WIRELESS].input_current_limit;
+ wr_cur = battery->pdata->charging_current[
+ battery->wire_status].input_current_limit;
+ if (battery->wc_status && battery->wc_enable &&
+ (wl_cur > wr_cur))
+ current_cable_type = POWER_SUPPLY_TYPE_WIRELESS;
+ else
+ current_cable_type = battery->wire_status;
+
+ if (current_cable_type == battery->cable_type) {
+ dev_info(battery->dev,
+ "%s: Cable is NOT Changed(%d)\n",
+ __func__, battery->cable_type);
+ /* Do NOT activate cable work for NOT changed */
+ goto end_of_cable_work;
+ }
+
+ battery->cable_type = current_cable_type;
+ if (battery->pdata->check_cable_result_callback)
+ battery->pdata->check_cable_result_callback(
+ battery->cable_type);
+ /* platform can NOT get information of cable connection
+ * because wakeup time is too short to check uevent
+ * To make sure that target is wakeup
+ * if cable is connected and disconnected,
+ * activated wake lock in a few seconds
+ */
+ wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
+
+ if (battery->cable_type == POWER_SUPPLY_TYPE_BATTERY ||
+ ((battery->pdata->cable_check_type &
+ SEC_BATTERY_CABLE_CHECK_NOINCOMPATIBLECHARGE) &&
+ battery->cable_type == POWER_SUPPLY_TYPE_UNKNOWN)) {
+ if (battery->status == POWER_SUPPLY_STATUS_FULL) {
+ val.intval = POWER_SUPPLY_TYPE_BATTERY;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_CHARGE_FULL, val);
+ /* To get SOC value (NOT raw SOC), need to reset value */
+ val.intval = 0;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CAPACITY, val);
+ battery->capacity = val.intval;
+ }
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ battery->is_recharging = false;
+ battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ battery->health = POWER_SUPPLY_HEALTH_GOOD;
+
+ if (sec_bat_set_charge(battery, false))
+ goto end_of_cable_work;
+ } else {
+ /* Do NOT display the charging icon when OTG is enabled */
+ if (battery->cable_type == POWER_SUPPLY_TYPE_OTG) {
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ if (battery->pdata->full_check_type !=
+ SEC_BATTERY_FULLCHARGED_NONE)
+ battery->charging_mode =
+ SEC_BATTERY_CHARGING_1ST;
+ else
+ battery->charging_mode =
+ SEC_BATTERY_CHARGING_2ND;
+ battery->status = POWER_SUPPLY_STATUS_CHARGING;
+ }
+
+ if (sec_bat_set_charge(battery, true))
+ goto end_of_cable_work;
+
+#if defined(ANDROID_ALARM_ACTIVATED)
+ /* No need for wakelock in Alarm */
+ if (battery->pdata->polling_type != SEC_BATTERY_MONITOR_ALARM)
+ wake_lock(&battery->vbus_wake_lock);
+#endif
+ }
+
+ /* polling time should be reset when cable is changed
+ * polling_in_sleep should be reset also
+ * before polling time is re-calculated
+ * to prevent from counting 1 for events
+ * right after cable is connected
+ */
+ battery->polling_in_sleep = false;
+ sec_bat_get_polling_time(battery);
+
+ dev_info(battery->dev,
+ "%s: Status:%s, Sleep:%s, Charging:%s, Short Poll:%s\n",
+ __func__, sec_bat_status_str[battery->status],
+ battery->polling_in_sleep ? "Yes" : "No",
+ (battery->charging_mode ==
+ SEC_BATTERY_CHARGING_NONE) ? "No" : "Yes",
+ battery->polling_short ? "Yes" : "No");
+ dev_info(battery->dev,
+ "%s: Polling time is reset to %d sec.\n", __func__,
+ battery->polling_time);
+
+ battery->polling_count = 1; /* initial value = 1 */
+
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work,
+ msecs_to_jiffies(500));
+end_of_cable_work:
+ wake_unlock(&battery->cable_wake_lock);
+ dev_dbg(battery->dev, "%s: End\n", __func__);
+}
+
+ssize_t sec_bat_show_attrs(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_bat);
+ const ptrdiff_t offset = attr - sec_battery_attrs;
+ union power_supply_propval value;
+ int i = 0;
+
+ switch (offset) {
+ case BATT_RESET_SOC:
+ break;
+ case BATT_READ_RAW_SOC:
+ {
+ union power_supply_propval value;
+
+ value.intval =
+ SEC_FUELGAUGE_CAPACITY_TYPE_RAW;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CAPACITY, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ value.intval);
+ }
+ break;
+ case BATT_READ_ADJ_SOC:
+ break;
+ case BATT_TYPE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%s\n",
+ battery->pdata->vendor);
+ break;
+ case BATT_VFOCV:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->voltage_ocv);
+ break;
+ case BATT_VOL_ADC:
+ break;
+ case BATT_VOL_ADC_CAL:
+ break;
+ case BATT_VOL_AVER:
+ break;
+ case BATT_VOL_ADC_AVER:
+ break;
+
+ case BATT_CURRENT_UA_NOW:
+ {
+ union power_supply_propval value;
+
+ value.intval = SEC_BATTEY_CURRENT_UA;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CURRENT_NOW, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ value.intval);
+ }
+ break;
+ case BATT_CURRENT_UA_AVG:
+ {
+ union power_supply_propval value;
+
+ value.intval = SEC_BATTEY_CURRENT_UA;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_CURRENT_AVG, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ value.intval);
+ }
+ break;
+
+ case BATT_TEMP:
+ switch (battery->pdata->thermal_source) {
+ case SEC_BATTERY_THERMAL_SOURCE_FG:
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_TEMP, value);
+ break;
+ case SEC_BATTERY_THERMAL_SOURCE_CALLBACK:
+ if (battery->pdata->get_temperature_callback) {
+ battery->pdata->get_temperature_callback(
+ POWER_SUPPLY_PROP_TEMP, &value);
+ }
+ break;
+ case SEC_BATTERY_THERMAL_SOURCE_ADC:
+ sec_bat_get_temperature_by_adc(battery,
+ POWER_SUPPLY_PROP_TEMP, &value);
+ break;
+ default:
+ break;
+ }
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ value.intval);
+ break;
+ case BATT_TEMP_ADC:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->temp_adc);
+ break;
+ case BATT_TEMP_AVER:
+ break;
+ case BATT_TEMP_ADC_AVER:
+ break;
+ case BATT_VF_ADC:
+ break;
+ case BATT_SLATE_MODE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->slate_mode);
+ break;
+
+ case BATT_LP_CHARGING:
+ if (battery->pdata->is_lpm) {
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->pdata->is_lpm() ? 1 : 0);
+ }
+ break;
+ case SIOP_ACTIVATED:
+ break;
+ case SIOP_LEVEL:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->siop_level);
+ break;
+ case BATT_CHARGING_SOURCE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->cable_type);
+ break;
+ case FG_REG_DUMP:
+ break;
+ case FG_RESET_CAP:
+ break;
+ case FG_CAPACITY:
+ {
+ union power_supply_propval value;
+
+ value.intval =
+ SEC_BATTEY_CAPACITY_DESIGNED;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_ENERGY_NOW, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x ",
+ value.intval);
+
+ value.intval =
+ SEC_BATTEY_CAPACITY_ABSOLUTE;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_ENERGY_NOW, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x ",
+ value.intval);
+
+ value.intval =
+ SEC_BATTEY_CAPACITY_TEMPERARY;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_ENERGY_NOW, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x ",
+ value.intval);
+
+ value.intval =
+ SEC_BATTEY_CAPACITY_CURRENT;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_ENERGY_NOW, value);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "0x%04x\n",
+ value.intval);
+ }
+ break;
+ case AUTH:
+ break;
+ case CHG_CURRENT_ADC:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->current_adc);
+ break;
+ case WC_ADC:
+ break;
+ case WC_STATUS:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->cable_type == POWER_SUPPLY_TYPE_WIRELESS));
+ break;
+ case WC_ENABLE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->wc_enable);
+ break;
+ case FACTORY_MODE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->factory_mode);
+ break;
+ case UPDATE:
+ break;
+ case TEST_MODE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->test_mode);
+ break;
+
+ case BATT_EVENT_CALL:
+ case BATT_EVENT_2G_CALL:
+ case BATT_EVENT_TALK_GSM:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_2G_CALL) ? 1 : 0);
+ break;
+ case BATT_EVENT_3G_CALL:
+ case BATT_EVENT_TALK_WCDMA:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_3G_CALL) ? 1 : 0);
+ break;
+ case BATT_EVENT_MUSIC:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_MUSIC) ? 1 : 0);
+ break;
+ case BATT_EVENT_VIDEO:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_VIDEO) ? 1 : 0);
+ break;
+ case BATT_EVENT_BROWSER:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_BROWSER) ? 1 : 0);
+ break;
+ case BATT_EVENT_HOTSPOT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_HOTSPOT) ? 1 : 0);
+ break;
+ case BATT_EVENT_CAMERA:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_CAMERA) ? 1 : 0);
+ break;
+ case BATT_EVENT_CAMCORDER:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_CAMCORDER) ? 1 : 0);
+ break;
+ case BATT_EVENT_DATA_CALL:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_DATA_CALL) ? 1 : 0);
+ break;
+ case BATT_EVENT_WIFI:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_WIFI) ? 1 : 0);
+ break;
+ case BATT_EVENT_WIBRO:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_WIBRO) ? 1 : 0);
+ break;
+ case BATT_EVENT_LTE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_LTE) ? 1 : 0);
+ break;
+ case BATT_EVENT_LCD:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_LCD) ? 1 : 0);
+ break;
+ case BATT_EVENT_GPS:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ (battery->event & EVENT_GPS) ? 1 : 0);
+ break;
+ case BATT_EVENT:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->event);
+ break;
+#if defined(CONFIG_SAMSUNG_BATTERY_ENG_TEST)
+ case BATT_TEST_CHARGE_CURRENT:
+ {
+ union power_supply_propval value;
+
+ psy_do_property(battery->pdata->charger_name, get,
+ POWER_SUPPLY_PROP_CURRENT_NOW, value);
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ value.intval);
+ }
+ break;
+ case BATT_STABILITY_TEST:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ battery->stability_test);
+ break;
+#endif
+ default:
+ i = -EINVAL;
+ }
+
+ return i;
+}
+
+ssize_t sec_bat_store_attrs(
+ struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct power_supply *psy = dev_get_drvdata(dev);
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_bat);
+ const ptrdiff_t offset = attr - sec_battery_attrs;
+ int ret = -EINVAL;
+ int x = 0;
+
+ switch (offset) {
+ case BATT_RESET_SOC:
+ /* Do NOT reset fuel gauge in charging mode */
+ if (battery->pdata->check_cable_callback &&
+ battery->pdata->check_jig_status) {
+ if ((battery->pdata->check_cable_callback(battery) ==
+ POWER_SUPPLY_TYPE_BATTERY) ||
+ battery->pdata->check_jig_status()) {
+ union power_supply_propval value;
+ battery->voltage_now = 1234;
+ battery->voltage_avg = 1234;
+ power_supply_changed(&battery->psy_bat);
+
+ value.intval =
+ SEC_FUELGAUGE_CAPACITY_TYPE_RESET;
+ psy_do_property(battery->pdata->fuelgauge_name, set,
+ POWER_SUPPLY_PROP_CAPACITY, value);
+ dev_info(battery->dev,"do reset SOC\n");
+ /* update battery info */
+ sec_bat_get_battery_info(battery);
+ }
+ }
+ ret = count;
+ break;
+ case BATT_READ_RAW_SOC:
+ break;
+ case BATT_READ_ADJ_SOC:
+ break;
+ case BATT_TYPE:
+ break;
+ case BATT_VFOCV:
+ break;
+ case BATT_VOL_ADC:
+ break;
+ case BATT_VOL_ADC_CAL:
+ break;
+ case BATT_VOL_AVER:
+ break;
+ case BATT_VOL_ADC_AVER:
+ break;
+ case BATT_CURRENT_UA_NOW:
+ break;
+ case BATT_CURRENT_UA_AVG:
+ break;
+ case BATT_TEMP:
+ break;
+ case BATT_TEMP_ADC:
+ break;
+ case BATT_TEMP_AVER:
+ break;
+ case BATT_TEMP_ADC_AVER:
+ break;
+ case BATT_VF_ADC:
+ break;
+ case BATT_SLATE_MODE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ union power_supply_propval value;
+ if (x == 1) {
+ value.intval = POWER_SUPPLY_TYPE_BATTERY;
+ battery->slate_mode = true;
+ } else if (x == 0) {
+ value.intval = POWER_SUPPLY_TYPE_USB;
+ battery->slate_mode = false;
+ } else {
+ dev_info(battery->dev,
+ "%s: SLATE MODE unknown command\n",
+ __func__);
+ return -EINVAL;
+ }
+ psy_do_property("battery", set,
+ POWER_SUPPLY_PROP_ONLINE, value);
+ if (battery->slate_mode) {
+ value.intval = 0;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ value);
+ }
+ ret = count;
+ }
+ break;
+
+ case BATT_LP_CHARGING:
+ break;
+ case SIOP_ACTIVATED:
+ break;
+ case SIOP_LEVEL:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ union power_supply_propval value;
+ dev_info(battery->dev,
+ "%s: siop level: %d\n", __func__, x);
+ if (x >= 0 && x <= 100)
+ battery->siop_level = x;
+ else
+ battery->siop_level = 100;
+ value.intval = battery->siop_level;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, value);
+ ret = count;
+ }
+ break;
+ case BATT_CHARGING_SOURCE:
+ break;
+ case FG_REG_DUMP:
+ break;
+ case FG_RESET_CAP:
+ break;
+ case FG_CAPACITY:
+ break;
+ case AUTH:
+ break;
+ case CHG_CURRENT_ADC:
+ break;
+ case WC_ADC:
+ break;
+ case WC_STATUS:
+ break;
+ case WC_ENABLE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ if (x == 0) {
+ battery->wc_enable = false;
+ } else if (x == 1) {
+ battery->wc_enable = true;
+ } else {
+ dev_info(battery->dev,
+ "%s: WPC ENABLE unknown command\n",
+ __func__);
+ return -EINVAL;
+ }
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue,
+ &battery->cable_work);
+ ret = count;
+ }
+ break;
+ case FACTORY_MODE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ battery->factory_mode = x ? true : false;
+ ret = count;
+ }
+ break;
+ case UPDATE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ /* update battery info */
+ sec_bat_get_battery_info(battery);
+ ret = count;
+ }
+ break;
+ case TEST_MODE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ battery->test_mode = x;
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue,
+ &battery->monitor_work, 0);
+ ret = count;
+ }
+ break;
+
+ case BATT_EVENT_CALL:
+ case BATT_EVENT_2G_CALL:
+ case BATT_EVENT_TALK_GSM:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_2G_CALL, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_3G_CALL:
+ case BATT_EVENT_TALK_WCDMA:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_3G_CALL, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_MUSIC:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_MUSIC, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_VIDEO:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_VIDEO, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_BROWSER:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_BROWSER, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_HOTSPOT:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_HOTSPOT, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_CAMERA:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_CAMERA, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_CAMCORDER:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_CAMCORDER, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_DATA_CALL:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_DATA_CALL, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_WIFI:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_WIFI, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_WIBRO:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_WIBRO, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_LTE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_LTE, x);
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_LCD:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ /* we need to test
+ sec_bat_event_set(battery, EVENT_LCD, x);
+ */
+ ret = count;
+ }
+ break;
+ case BATT_EVENT_GPS:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ sec_bat_event_set(battery, EVENT_GPS, x);
+ ret = count;
+ }
+ break;
+#if defined(CONFIG_SAMSUNG_BATTERY_ENG_TEST)
+ case BATT_TEST_CHARGE_CURRENT:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ if (x >= 0 && x <= 2000) {
+ union power_supply_propval value;
+ dev_err(battery->dev,
+ "%s: BATT_TEST_CHARGE_CURRENT(%d)\n", __func__, x);
+ battery->pdata->charging_current[
+ POWER_SUPPLY_TYPE_USB].input_current_limit = x;
+ battery->pdata->charging_current[
+ POWER_SUPPLY_TYPE_USB].fast_charging_current = x;
+ if (x > 500) {
+ battery->eng_not_full_status = true;
+ battery->pdata->temp_check_type =
+ SEC_BATTERY_TEMP_CHECK_NONE;
+ battery->pdata->charging_total_time =
+ 10000 * 60 * 60;
+ }
+ if (battery->cable_type == POWER_SUPPLY_TYPE_USB) {
+ value.intval = x;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ value);
+ }
+ }
+ ret = count;
+ }
+ break;
+ case BATT_STABILITY_TEST:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ union power_supply_propval value;
+ dev_err(battery->dev,
+ "%s: BATT_STABILITY_TEST(%d)\n", __func__, x);
+ if (x) {
+ battery->stability_test = true;
+ battery->eng_not_full_status = true;
+ value.intval = POWER_SUPPLY_TYPE_WIRELESS;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, value);
+ psy_do_property("battery", set,
+ POWER_SUPPLY_PROP_ONLINE, value);
+ }
+ else {
+ battery->stability_test = false;
+ value.intval = POWER_SUPPLY_TYPE_MAINS;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, value);
+ }
+ }
+ break;
+#endif
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int sec_bat_create_attrs(struct device *dev)
+{
+ int i, rc;
+
+ for (i = 0; i < ARRAY_SIZE(sec_battery_attrs); i++) {
+ rc = device_create_file(dev, &sec_battery_attrs[i]);
+ if (rc)
+ goto create_attrs_failed;
+ }
+ goto create_attrs_succeed;
+
+create_attrs_failed:
+ while (i--)
+ device_remove_file(dev, &sec_battery_attrs[i]);
+create_attrs_succeed:
+ return rc;
+}
+
+static int sec_bat_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_bat);
+ int current_cable_type;
+ int full_check_type;
+ union power_supply_propval value;
+
+ dev_dbg(battery->dev,
+ "%s: (%d,%d)\n", __func__, psp, val->intval);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (battery->charging_mode == SEC_BATTERY_CHARGING_1ST)
+ full_check_type = battery->pdata->full_check_type;
+ else
+ full_check_type = battery->pdata->full_check_type_2nd;
+ if ((full_check_type == SEC_BATTERY_FULLCHARGED_CHGINT) &&
+ (val->intval == POWER_SUPPLY_STATUS_FULL))
+ sec_bat_do_fullcharged(battery);
+ battery->status = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ sec_bat_ovp_uvlo_result(battery, val->intval);
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ current_cable_type = val->intval;
+
+ if (current_cable_type < 0) {
+ dev_info(battery->dev,
+ "%s: ignore event(%d)\n",
+ __func__, current_cable_type);
+ } else if (current_cable_type == POWER_SUPPLY_TYPE_OTG) {
+ value.intval = current_cable_type;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_ONLINE, value);
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue,
+ &battery->monitor_work, 0);
+
+ break;
+#if 0
+ } else if (current_cable_type == POWER_SUPPLY_TYPE_WIRELESS) {
+ battery->wc_status = true;
+ } else if (current_cable_type == POWER_SUPPLY_TYPE_WIRELESS_REMOVE) {
+ battery->wc_status = false;
+ if (battery->wire_status != POWER_SUPPLY_TYPE_BATTERY)
+ current_cable_type = battery->wire_status;
+#endif
+ } else {
+ battery->wire_status = current_cable_type;
+ if ((battery->wire_status == POWER_SUPPLY_TYPE_BATTERY)
+ && battery->wc_status)
+ current_cable_type = POWER_SUPPLY_TYPE_WIRELESS;
+ }
+ dev_info(battery->dev,
+ "%s: current_cable(%d), wc_status(%d), wire_status(%d)\n",
+ __func__, current_cable_type, battery->wc_status,
+ battery->wire_status);
+
+ /* cable is attached or detached
+ * if current_cable_type is minus value,
+ * check cable by sec_bat_get_cable_type()
+ * although SEC_BATTERY_CABLE_SOURCE_EXTERNAL is set
+ * (0 is POWER_SUPPLY_TYPE_UNKNOWN)
+ */
+ if ((current_cable_type >= 0) &&
+ (current_cable_type <= SEC_SIZEOF_POWER_SUPPLY_TYPE) &&
+ (battery->pdata->cable_source_type &
+ SEC_BATTERY_CABLE_SOURCE_EXTERNAL ||
+ battery->pdata->cable_source_type &
+ SEC_BATTERY_CABLE_SOURCE_EXTENDED)) {
+
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue,
+ &battery->cable_work);
+ } else {
+ if (sec_bat_get_cable_type(battery,
+ battery->pdata->cable_source_type)) {
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue,
+ &battery->cable_work);
+ }
+ }
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ battery->capacity = val->intval;
+ power_supply_changed(&battery->psy_bat);
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ cancel_delayed_work(&battery->monitor_work);
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work_on(0, battery->monitor_wqueue,
+ &battery->monitor_work, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sec_bat_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_bat);
+ union power_supply_propval value;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) ||
+ (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) {
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
+ } else {
+ if ((battery->pdata->cable_check_type &
+ SEC_BATTERY_CABLE_CHECK_NOUSBCHARGE) &&
+ (battery->pdata->is_lpm &&
+ !battery->pdata->is_lpm())) {
+ switch (battery->cable_type) {
+ case POWER_SUPPLY_TYPE_USB:
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ case POWER_SUPPLY_TYPE_USB_ACA:
+ val->intval =
+ POWER_SUPPLY_STATUS_DISCHARGING;
+ return 0;
+ }
+ }
+ val->intval = battery->status;
+ }
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ psy_do_property(battery->pdata->charger_name, get,
+ POWER_SUPPLY_PROP_CHARGE_TYPE, value);
+ if (value.intval == POWER_SUPPLY_CHARGE_TYPE_UNKNOWN)
+ /* if error in CHARGE_TYPE of charger
+ * set CHARGE_TYPE as NONE
+ */
+ val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE;
+ else
+ val->intval = value.intval;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = battery->health;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = battery->present;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = battery->cable_type;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = battery->pdata->technology;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ if (battery->pdata->check_jig_status) {
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW, value);
+ battery->voltage_now = value.intval;
+ dev_err(battery->dev,
+ "%s: voltage now(%d)\n", __func__, battery->voltage_now);
+ }
+ /* voltage value should be in uV */
+ val->intval = battery->voltage_now * 1000;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+#ifdef CONFIG_SEC_FACTORY
+ value.intval = SEC_BATTEY_VOLTAGE_AVERAGE;
+ psy_do_property(battery->pdata->fuelgauge_name, get,
+ POWER_SUPPLY_PROP_VOLTAGE_AVG, value);
+ battery->voltage_avg = value.intval;
+ dev_err(battery->dev,
+ "%s: voltage avg(%d)\n", __func__, battery->voltage_avg);
+#endif
+ /* voltage value should be in uV */
+ val->intval = battery->voltage_avg * 1000;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = battery->current_now;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = battery->current_avg;
+ break;
+ /* charging mode (differ from power supply) */
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ val->intval = battery->charging_mode;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+#if defined(CONFIG_SAMSUNG_BATTERY_ENG_TEST)
+ if (battery->status == POWER_SUPPLY_STATUS_FULL) {
+ if(battery->eng_not_full_status == true)
+ val->intval = battery->capacity;
+ else
+ val->intval = 100;
+ } else {
+ val->intval = battery->capacity;
+ }
+#else
+ /* In full-charged status, SOC is always 100% */
+ if (battery->status == POWER_SUPPLY_STATUS_FULL)
+ val->intval = 100;
+ else
+ val->intval = battery->capacity;
+#endif
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = battery->temperature;
+ break;
+ case POWER_SUPPLY_PROP_TEMP_AMBIENT:
+ val->intval = battery->temper_amb;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int sec_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_usb);
+
+ if (psp != POWER_SUPPLY_PROP_ONLINE)
+ return -EINVAL;
+
+ if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) ||
+ (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) {
+ val->intval = 0;
+ return 0;
+ }
+ /* Set enable=1 only if the USB charger is connected */
+ switch (battery->wire_status) {
+ case POWER_SUPPLY_TYPE_USB:
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ case POWER_SUPPLY_TYPE_USB_CDP:
+ case POWER_SUPPLY_TYPE_USB_ACA:
+ case POWER_SUPPLY_TYPE_MHL_USB:
+ val->intval = 1;
+ break;
+ default:
+ val->intval = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int sec_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_ac);
+
+ if (psp != POWER_SUPPLY_PROP_ONLINE)
+ return -EINVAL;
+
+ if ((battery->health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) ||
+ (battery->health == POWER_SUPPLY_HEALTH_UNDERVOLTAGE)) {
+ val->intval = 0;
+ return 0;
+ }
+
+ /* Set enable=1 only if the AC charger is connected */
+ switch (battery->cable_type) {
+ case POWER_SUPPLY_TYPE_MAINS:
+ case POWER_SUPPLY_TYPE_HV_MAINS:
+ case POWER_SUPPLY_TYPE_MISC:
+ case POWER_SUPPLY_TYPE_CARDOCK:
+ case POWER_SUPPLY_TYPE_UARTOFF:
+ case POWER_SUPPLY_TYPE_LAN_HUB:
+ case POWER_SUPPLY_TYPE_UNKNOWN:
+ case POWER_SUPPLY_TYPE_MHL_500:
+ case POWER_SUPPLY_TYPE_MHL_900:
+ case POWER_SUPPLY_TYPE_MHL_1500:
+ case POWER_SUPPLY_TYPE_SMART_OTG:
+ case POWER_SUPPLY_TYPE_SMART_NOTG:
+ val->intval = 1;
+ break;
+ default:
+ val->intval = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int sec_wireless_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_wireless);
+
+ if (psp != POWER_SUPPLY_PROP_ONLINE)
+ return -EINVAL;
+
+ if (battery->wc_status)
+ val->intval = 1;
+ else
+ val->intval = 0;
+
+ return 0;
+}
+
+static int sec_wireless_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_wireless);
+
+ if (psp != POWER_SUPPLY_PROP_ONLINE)
+ return -EINVAL;
+
+ battery->wc_status = val->intval;
+
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue, &battery->cable_work);
+
+ return 0;
+}
+
+static int sec_ps_set_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ const union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_ps);
+ union power_supply_propval value;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if ((val->intval == 0) || (val->intval ==1)) {
+ battery->ps_enable = val->intval;
+ dev_info(battery->dev,
+ "%s: power sharing cable set (%d)\n", __func__, battery->ps_enable);
+ value.intval = POWER_SUPPLY_TYPE_POWER_SHARING;
+ psy_do_property(battery->pdata->charger_name, set,
+ POWER_SUPPLY_PROP_ONLINE, value);
+ } else {
+ dev_err(battery->dev,
+ "%s: invalid setting (%d)\n", __func__, val->intval);
+ }
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (val->intval == POWER_SUPPLY_TYPE_POWER_SHARING) {
+ battery->ps_status = true;
+ dev_info(battery->dev,
+ "%s: power sharing cable plugin (%d)\n", __func__, battery->ps_status);
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ } else {
+ battery->ps_status = false;
+ dev_info(battery->dev,
+ "%s: power sharing cable plugout (%d)\n", __func__, battery->ps_status);
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sec_ps_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ struct sec_battery_info *battery =
+ container_of(psy, struct sec_battery_info, psy_ps);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ if (battery->ps_enable)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ if (battery->ps_status)
+ val->intval = 1;
+ else
+ val->intval = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* TODO unused */
+#if 0
+static irqreturn_t sec_bat_irq_thread(int irq, void *irq_data)
+{
+ struct sec_battery_info *battery = irq_data;
+
+ dev_info(battery->dev, "%s:(bat_irq occured_start\n", __func__);
+
+ if (battery->pdata->cable_check_type &
+ SEC_BATTERY_CABLE_CHECK_INT) {
+ if (battery->pdata->is_interrupt_cable_check_possible &&
+ !battery->pdata->is_interrupt_cable_check_possible(
+ battery->extended_cable_type))
+ goto no_cable_check;
+ else {
+ if (sec_bat_get_cable_type(battery,
+ battery->pdata->cable_source_type)) {
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue,
+ &battery->cable_work);
+ }
+ }
+ }
+
+no_cable_check:
+ if (battery->pdata->battery_check_type ==
+ SEC_BATTERY_CHECK_INT) {
+ if (battery_pdata->check_battery_callback)
+ battery->present = battery->pdata->check_battery_callback();
+
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ }
+
+ return IRQ_HANDLED;
+}
+#endif
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+static int sec_bat_cable_check(struct sec_battery_info *battery,
+ muic_attached_dev_t attached_dev)
+{
+ int current_cable_type = POWER_SUPPLY_TYPE_UNKNOWN;
+
+ pr_info("[%s]ATTACHED(%d)\n", __func__, attached_dev);
+
+ switch (attached_dev)
+ {
+ case ATTACHED_DEV_JIG_UART_OFF_MUIC:
+ case ATTACHED_DEV_SMARTDOCK_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_BATTERY;
+ break;
+ case ATTACHED_DEV_OTG_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_OTG;
+ break;
+ case ATTACHED_DEV_USB_MUIC:
+ case ATTACHED_DEV_JIG_USB_OFF_MUIC:
+ case ATTACHED_DEV_JIG_USB_ON_MUIC:
+ case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_USB;
+ break;
+ case ATTACHED_DEV_JIG_UART_OFF_VB_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_UARTOFF;
+ break;
+ case ATTACHED_DEV_TA_MUIC:
+ case ATTACHED_DEV_CARDOCK_MUIC:
+ case ATTACHED_DEV_DESKDOCK_MUIC:
+ case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_MAINS;
+ break;
+ case ATTACHED_DEV_CDP_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_USB_CDP;
+ break;
+ case ATTACHED_DEV_HV_MUIC:
+ current_cable_type = POWER_SUPPLY_TYPE_HV_MAINS;
+ break;
+ default:
+ pr_err("%s: invalid type for charger:%d\n",
+ __func__, attached_dev);
+ }
+
+ return current_cable_type;
+
+}
+
+static int batt_handle_notification(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
+ const char *cmd;
+ int cable_type;
+ struct sec_battery_info *battery =
+ container_of(nb, struct sec_battery_info,
+ batt_nb);
+
+ switch (action) {
+ case MUIC_NOTIFY_CMD_DETACH:
+ case MUIC_NOTIFY_CMD_LOGICALLY_DETACH:
+ cmd = "DETACH";
+ cable_type = POWER_SUPPLY_TYPE_BATTERY;
+ break;
+ case MUIC_NOTIFY_CMD_ATTACH:
+ case MUIC_NOTIFY_CMD_LOGICALLY_ATTACH:
+ cmd = "ATTACH";
+ cable_type = sec_bat_cable_check(battery, attached_dev);
+ break;
+ default:
+ cmd = "ERROR";
+ cable_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ break;
+ }
+
+ if (cable_type < 0) {
+ dev_info(battery->dev, "%s: ignore event(%d)\n",
+ __func__, cable_type);
+ } else if (cable_type == POWER_SUPPLY_TYPE_POWER_SHARING) {
+ battery->ps_status = true;
+ dev_info(battery->dev,
+ "%s: power sharing cable plugin (%d)\n", __func__, battery->ps_status);
+ } else if (cable_type == POWER_SUPPLY_TYPE_WIRELESS) {
+ battery->wc_status = true;
+#if 0
+ } else if (cable_type == POWER_SUPPLY_TYPE_WIRELESS_REMOVE) {
+ battery->wc_status = false;
+ if (battery->wire_status != POWER_SUPPLY_TYPE_BATTERY)
+ cable_type = battery->wire_status;
+#endif
+ } else {
+ battery->wire_status = cable_type;
+ if ((battery->wire_status == POWER_SUPPLY_TYPE_BATTERY)
+ && battery->wc_status && !battery->ps_status)
+ cable_type = POWER_SUPPLY_TYPE_WIRELESS;
+ }
+ dev_info(battery->dev,
+ "%s: current_cable(%d), wc_status(%d), wire_status(%d)\n",
+ __func__, cable_type, battery->wc_status,
+ battery->wire_status);
+
+ if ((cable_type >= 0) &&
+ cable_type <= SEC_SIZEOF_POWER_SUPPLY_TYPE) {
+ if (cable_type == POWER_SUPPLY_TYPE_POWER_SHARING) {
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ } else if((cable_type == POWER_SUPPLY_TYPE_BATTERY)
+ && battery->ps_status) {
+ battery->ps_status = false;
+ dev_info(battery->dev,
+ "%s: power sharing cable plugout (%d)\n", __func__, battery->ps_status);
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ } else if(cable_type != battery->cable_type) {
+ wake_lock(&battery->cable_wake_lock);
+ queue_work(battery->monitor_wqueue, &battery->cable_work);
+ } else {
+ dev_info(battery->dev,
+ "%s: Cable is Not Changed(%d)\n",
+ __func__, battery->cable_type);
+ }
+ }
+
+ pr_info("%s: CMD=%s, attached_dev=%d\n", __func__, cmd, attached_dev);
+
+ return 0;
+}
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#ifdef CONFIG_OF
+static int sec_bat_parse_dt(struct device *dev,
+ struct sec_battery_info *battery)
+{
+ struct device_node *np = dev->of_node;
+ sec_battery_platform_data_t *pdata = battery->pdata;
+ int ret, len;
+ unsigned int i;
+ const u32 *p;
+
+ if (!np) {
+ pr_info("%s: np NULL\n", __func__);
+ return 1;
+ }
+
+ ret = of_property_read_string(np,
+ "battery,vendor", (char const **)&pdata->vendor);
+ if (ret)
+ pr_info("%s: Vendor is Empty\n", __func__);
+
+ ret = of_property_read_string(np,
+ "battery,charger_name", (char const **)&pdata->charger_name);
+ if (ret)
+ pr_info("%s: Vendor is Empty\n", __func__);
+
+ ret = of_property_read_string(np,
+ "battery,fuelgauge_name", (char const **)&pdata->fuelgauge_name);
+ if (ret)
+ pr_info("%s: Vendor is Empty\n", __func__);
+
+ ret = of_property_read_string(np,
+ "battery,chip_vendor", (char const **)&pdata->chip_vendor);
+ if (ret)
+ pr_info("%s: Vendor is Empty\n", __func__);
+
+ ret = of_property_read_u32(np, "battery,technology",
+ &pdata->technology);
+
+ ret = of_property_read_u32(np, "battery,battery_check_type",
+ &pdata->battery_check_type);
+
+ p = of_get_property(np, "battery,polling_time", &len);
+
+ len = len / sizeof(u32);
+
+ pdata->polling_time = kzalloc(sizeof(*pdata->polling_time) * len, GFP_KERNEL);
+
+ ret = of_property_read_u32_array(np, "battery,polling_time",
+ pdata->polling_time, len);
+
+ p = of_get_property(np, "battery,temp_table_adc", &len);
+
+ len = len / sizeof(u32);
+
+ pdata->temp_adc_table_size = len;
+ pdata->temp_amb_adc_table_size = len;
+
+ pdata->temp_adc_table = kzalloc(sizeof(sec_bat_adc_table_data_t) *
+ pdata->temp_adc_table_size, GFP_KERNEL);
+ pdata->temp_amb_adc_table = kzalloc(sizeof(sec_bat_adc_table_data_t) *
+ pdata->temp_adc_table_size, GFP_KERNEL);
+
+ for(i = 0; i < pdata->temp_adc_table_size; i++) {
+ ret = of_property_read_u32_index(np, "battery,temp_table_adc", i,
+ &pdata->temp_adc_table[i].adc);
+ ret = of_property_read_u32_index(np, "battery,temp_table_data", i,
+ &pdata->temp_adc_table[i].data);
+ ret = of_property_read_u32_index(np, "battery,temp_table_adc", i,
+ &pdata->temp_amb_adc_table[i].adc);
+ ret = of_property_read_u32_index(np, "battery,temp_table_data", i,
+ &pdata->temp_amb_adc_table[i].data);
+ }
+
+ p = of_get_property(np, "battery,input_current_limit", &len);
+
+ len = len / sizeof(u32);
+
+ pdata->charging_current = kzalloc(sizeof(sec_charging_current_t) * len,
+ GFP_KERNEL);
+
+ for(i = 0; i < len; i++) {
+ ret = of_property_read_u32_index(np,
+ "battery,input_current_limit", i,
+ &pdata->charging_current[i].input_current_limit);
+ ret = of_property_read_u32_index(np,
+ "battery,fast_charging_current", i,
+ &pdata->charging_current[i].fast_charging_current);
+ ret = of_property_read_u32_index(np,
+ "battery,full_check_current_1st", i,
+ &pdata->charging_current[i].full_check_current_1st);
+ ret = of_property_read_u32_index(np,
+ "battery,full_check_current_2nd", i,
+ &pdata->charging_current[i].full_check_current_2nd);
+ }
+
+ ret = of_property_read_u32(np, "battery,adc_check_count",
+ &pdata->adc_check_count);
+
+ ret = of_property_read_u32(np, "battery,temp_adc_type",
+ &pdata->temp_adc_type);
+
+ ret = of_property_read_u32(np, "battery,cable_check_type",
+ &pdata->cable_check_type);
+
+ ret = of_property_read_u32(np, "battery,cable_source_type",
+ &pdata->cable_source_type);
+
+ ret = of_property_read_u32(np, "battery,event_waiting_time",
+ &pdata->event_waiting_time);
+
+ ret = of_property_read_u32(np, "battery,polling_type",
+ &pdata->polling_type);
+
+ ret = of_property_read_u32(np, "battery,monitor_initial_count",
+ &pdata->monitor_initial_count);
+
+ ret = of_property_read_u32(np, "battery,check_count",
+ &pdata->check_count);
+
+ ret = of_property_read_u32(np, "battery,check_adc_max",
+ &pdata->check_adc_max);
+
+ ret = of_property_read_u32(np, "battery,check_adc_min",
+ &pdata->check_adc_min);
+
+ ret = of_property_read_u32(np, "battery,ovp_uvlo_check_type",
+ &pdata->ovp_uvlo_check_type);
+
+ ret = of_property_read_u32(np, "battery,thermal_source",
+ &pdata->thermal_source);
+
+ ret = of_property_read_u32(np, "battery,temp_check_type",
+ &pdata->temp_check_type);
+
+ ret = of_property_read_u32(np, "battery,temp_check_count",
+ &pdata->temp_check_count);
+
+ ret = of_property_read_u32(np, "battery,temp_high_threshold_event",
+ &pdata->temp_high_threshold_event);
+
+ ret = of_property_read_u32(np, "battery,temp_high_recovery_event",
+ &pdata->temp_high_recovery_event);
+
+ ret = of_property_read_u32(np, "battery,temp_low_threshold_event",
+ &pdata->temp_low_threshold_event);
+
+ ret = of_property_read_u32(np, "battery,temp_low_recovery_event",
+ &pdata->temp_low_recovery_event);
+
+ ret = of_property_read_u32(np, "battery,temp_high_threshold_event",
+ &pdata->temp_high_threshold_normal);
+
+ ret = of_property_read_u32(np, "battery,temp_high_recovery_event",
+ &pdata->temp_high_recovery_normal);
+
+ ret = of_property_read_u32(np, "battery,temp_low_threshold_event",
+ &pdata->temp_low_threshold_normal);
+
+ ret = of_property_read_u32(np, "battery,temp_low_recovery_event",
+ &pdata->temp_low_recovery_normal);
+
+ ret = of_property_read_u32(np, "battery,temp_high_threshold_event",
+ &pdata->temp_high_threshold_lpm);
+
+ ret = of_property_read_u32(np, "battery,temp_high_recovery_event",
+ &pdata->temp_high_recovery_lpm);
+
+ ret = of_property_read_u32(np, "battery,temp_low_threshold_event",
+ &pdata->temp_low_threshold_lpm);
+
+ ret = of_property_read_u32(np, "battery,temp_low_recovery_event",
+ &pdata->temp_low_recovery_lpm);
+
+ ret = of_property_read_u32(np, "battery,full_check_type",
+ &pdata->full_check_type);
+
+ ret = of_property_read_u32(np, "battery,full_check_type_2nd",
+ &pdata->full_check_type_2nd);
+
+ ret = of_property_read_u32(np, "battery,full_check_count",
+ &pdata->full_check_count);
+
+ ret = of_property_read_u32(np, "battery,chg_gpio_full_check",
+ &pdata->chg_gpio_full_check);
+
+ ret = of_property_read_u32(np, "battery,chg_polarity_full_check",
+ &pdata->chg_polarity_full_check);
+
+ ret = of_property_read_u32(np, "battery,full_condition_type",
+ &pdata->full_condition_type);
+
+ ret = of_property_read_u32(np, "battery,full_condition_soc",
+ &pdata->full_condition_soc);
+
+ ret = of_property_read_u32(np, "battery,full_condition_vcell",
+ &pdata->full_condition_vcell);
+
+ ret = of_property_read_u32(np, "battery,recharge_check_count",
+ &pdata->recharge_check_count);
+
+ ret = of_property_read_u32(np, "battery,recharge_condition_type",
+ &pdata->recharge_condition_type);
+
+ ret = of_property_read_u32(np, "battery,recharge_condition_soc",
+ &pdata->recharge_condition_soc);
+
+ ret = of_property_read_u32(np, "battery,recharge_condition_vcell",
+ &pdata->recharge_condition_vcell);
+
+ ret = of_property_read_u32(np, "battery,charging_total_time",
+ (unsigned int *)&pdata->charging_total_time);
+
+ ret = of_property_read_u32(np, "battery,recharging_total_time",
+ (unsigned int *)&pdata->recharging_total_time);
+
+ ret = of_property_read_u32(np, "battery,charging_reset_time",
+ (unsigned int *)&pdata->charging_reset_time);
+
+ ret = of_property_read_u32(np, "battery,charging_reset_time",
+ (unsigned int *)&pdata->charging_reset_time);
+
+ pr_info("%s: vendor : %s, technology : %d, cable_check_type : %d\n"
+ "cable_source_type : %d, event_waiting_time : %d\n"
+ "polling_type : %d, initial_count : %d, check_count : %d\n"
+ "check_adc_max : %d, check_adc_min : %d\n"
+ "ovp_uvlo_check_type : %d, thermal_source : %d\n"
+ "temp_check_type : %d, temp_check_count : %d\n"
+ "temp_high_threshold_even : %d, temp_high_recovery_event : %d\n",
+ __func__,
+ pdata->vendor, pdata->technology,pdata->cable_check_type,
+ pdata->cable_source_type, pdata->event_waiting_time,
+ pdata->polling_type, pdata->monitor_initial_count,
+ pdata->check_count, pdata->check_adc_max, pdata->check_adc_min,
+ pdata->ovp_uvlo_check_type, pdata->thermal_source,
+ pdata->temp_check_type, pdata->temp_check_count,
+ pdata->temp_high_threshold_event, pdata->temp_high_recovery_event);
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_OF
+extern sec_battery_platform_data_t sec_battery_pdata;
+#endif
+
+static int sec_battery_probe(struct platform_device *pdev)
+{
+ sec_battery_platform_data_t *pdata = NULL;
+ struct sec_battery_info *battery;
+ int ret = 0;
+#ifndef CONFIG_OF
+ int i;
+#endif
+
+ union power_supply_propval value;
+
+ dev_dbg(&pdev->dev,
+ "%s: SEC Battery Driver Loading\n", __func__);
+
+ battery = kzalloc(sizeof(*battery), GFP_KERNEL);
+ if (!battery)
+ return -ENOMEM;
+
+ if (pdev->dev.of_node) {
+ pdata = devm_kzalloc(&pdev->dev,
+ sizeof(sec_battery_platform_data_t),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev, "Failed to allocate memory\n");
+ ret = -ENOMEM;
+ goto err_bat_free;
+ }
+
+ battery->pdata = pdata;
+#ifdef CONFIG_OF
+ if (sec_bat_parse_dt(&pdev->dev, battery))
+ dev_err(&pdev->dev,
+ "%s: Failed to get battery init\n", __func__);
+#endif
+ } else {
+ pdata = dev_get_platdata(&pdev->dev);
+ battery->pdata = pdata;
+ }
+
+ platform_set_drvdata(pdev, battery);
+
+ battery->dev = &pdev->dev;
+
+ mutex_init(&battery->adclock);
+
+ dev_dbg(battery->dev, "%s: ADC init\n", __func__);
+
+#ifdef CONFIG_OF
+ // adc_init(pdev, battery);
+#else
+ for (i = 0; i < SEC_BAT_ADC_CHANNEL_FULL_CHECK; i++)
+ adc_init(pdev, pdata, i);
+#endif
+
+ wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND,
+ "sec-battery-monitor");
+ wake_lock_init(&battery->cable_wake_lock, WAKE_LOCK_SUSPEND,
+ "sec-battery-cable");
+ wake_lock_init(&battery->vbus_wake_lock, WAKE_LOCK_SUSPEND,
+ "sec-battery-vbus");
+
+ /* initialization of battery info */
+ battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ battery->health = POWER_SUPPLY_HEALTH_GOOD;
+ battery->present = true;
+
+ battery->polling_count = 1; /* initial value = 1 */
+ battery->polling_time = pdata->polling_time[
+ SEC_BATTERY_POLLING_TIME_DISCHARGING];
+ battery->polling_in_sleep = false;
+ battery->polling_short = false;
+
+ battery->check_count = 0;
+ battery->check_adc_count = 0;
+ battery->check_adc_value = 0;
+
+ battery->charging_start_time = 0;
+ battery->charging_passed_time = 0;
+ battery->charging_next_time = 0;
+ battery->charging_fullcharged_time = 0;
+ battery->siop_level = 100;
+ battery->wc_enable = 1;
+#if defined(CONFIG_SAMSUNG_BATTERY_ENG_TEST)
+ battery->stability_test = 0;
+ battery->eng_not_full_status = 0;
+#endif
+ battery->wc_status = 0;
+ battery->ps_status= 0;
+ battery->wire_status = POWER_SUPPLY_TYPE_BATTERY;
+
+#if defined(ANDROID_ALARM_ACTIVATED)
+ alarm_init(&battery->event_termination_alarm,
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ sec_bat_event_expired_timer_func);
+#else
+ alarm_init(&battery->event_termination_alarm,
+ ALARM_BOOTTIME,
+ sec_bat_event_expired_timer_func);
+#endif
+
+ battery->temp_high_threshold =
+ pdata->temp_high_threshold_normal;
+ battery->temp_high_recovery =
+ pdata->temp_high_recovery_normal;
+ battery->temp_low_recovery =
+ pdata->temp_low_recovery_normal;
+ battery->temp_low_threshold =
+ pdata->temp_low_threshold_normal;
+
+ battery->charging_mode = SEC_BATTERY_CHARGING_NONE;
+ battery->is_recharging = false;
+ battery->cable_type = POWER_SUPPLY_TYPE_BATTERY;
+ battery->test_mode = 0;
+ battery->factory_mode = false;
+ battery->slate_mode = false;
+
+ battery->psy_bat.name = "battery",
+ battery->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY,
+ battery->psy_bat.properties = sec_battery_props,
+ battery->psy_bat.num_properties = ARRAY_SIZE(sec_battery_props),
+ battery->psy_bat.get_property = sec_bat_get_property,
+ battery->psy_bat.set_property = sec_bat_set_property,
+ battery->psy_usb.name = "usb",
+ battery->psy_usb.type = POWER_SUPPLY_TYPE_USB,
+ battery->psy_usb.supplied_to = supply_list,
+ battery->psy_usb.num_supplicants = ARRAY_SIZE(supply_list),
+ battery->psy_usb.properties = sec_power_props,
+ battery->psy_usb.num_properties = ARRAY_SIZE(sec_power_props),
+ battery->psy_usb.get_property = sec_usb_get_property,
+ battery->psy_ac.name = "ac",
+ battery->psy_ac.type = POWER_SUPPLY_TYPE_MAINS,
+ battery->psy_ac.supplied_to = supply_list,
+ battery->psy_ac.num_supplicants = ARRAY_SIZE(supply_list),
+ battery->psy_ac.properties = sec_power_props,
+ battery->psy_ac.num_properties = ARRAY_SIZE(sec_power_props),
+ battery->psy_ac.get_property = sec_ac_get_property;
+ battery->psy_wireless.name = "wireless",
+ battery->psy_wireless.type = POWER_SUPPLY_TYPE_WIRELESS,
+ battery->psy_wireless.supplied_to = supply_list,
+ battery->psy_wireless.num_supplicants = ARRAY_SIZE(supply_list),
+ battery->psy_wireless.properties = sec_power_props,
+ battery->psy_wireless.num_properties = ARRAY_SIZE(sec_power_props),
+ battery->psy_wireless.get_property = sec_wireless_get_property;
+ battery->psy_wireless.set_property = sec_wireless_set_property;
+ battery->psy_ps.name = "ps",
+ battery->psy_ps.type = POWER_SUPPLY_TYPE_POWER_SHARING,
+ battery->psy_ps.supplied_to = supply_list,
+ battery->psy_ps.num_supplicants = ARRAY_SIZE(supply_list),
+ battery->psy_ps.properties = sec_ps_props,
+ battery->psy_ps.num_properties = ARRAY_SIZE(sec_ps_props),
+ battery->psy_ps.get_property = sec_ps_get_property;
+ battery->psy_ps.set_property = sec_ps_set_property;
+
+ /* create work queue */
+ battery->monitor_wqueue =
+ create_singlethread_workqueue(dev_name(&pdev->dev));
+ if (!battery->monitor_wqueue) {
+ dev_err(battery->dev,
+ "%s: Fail to Create Workqueue\n", __func__);
+ goto err_wake_lock;
+ }
+
+ INIT_DELAYED_WORK(&battery->monitor_work, sec_bat_monitor_work);
+ INIT_WORK(&battery->cable_work, sec_bat_cable_work);
+
+ switch (pdata->polling_type) {
+ case SEC_BATTERY_MONITOR_WORKQUEUE:
+ INIT_DELAYED_WORK(&battery->polling_work,
+ sec_bat_polling_work);
+ break;
+ case SEC_BATTERY_MONITOR_ALARM:
+#if defined(ANDROID_ALARM_ACTIVATED)
+ battery->last_poll_time = alarm_get_elapsed_realtime();
+ alarm_init(&battery->polling_alarm,
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ sec_bat_alarm);
+#else
+ battery->last_poll_time = ktime_get_boottime();
+ alarm_init(&battery->polling_alarm, ALARM_BOOTTIME,
+ sec_bat_alarm);
+#endif
+ break;
+
+ default:
+ break;
+ }
+
+ /* init power supplier framework */
+ ret = power_supply_register(&pdev->dev, &battery->psy_ps);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s: Failed to Register psy_ps\n", __func__);
+ goto err_workqueue;
+ }
+
+ ret = power_supply_register(&pdev->dev, &battery->psy_wireless);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s: Failed to Register psy_wireless\n", __func__);
+ goto err_supply_unreg_ps;
+ }
+
+ ret = power_supply_register(&pdev->dev, &battery->psy_usb);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s: Failed to Register psy_usb\n", __func__);
+ goto err_supply_unreg_wireless;
+ }
+
+ ret = power_supply_register(&pdev->dev, &battery->psy_ac);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s: Failed to Register psy_ac\n", __func__);
+ goto err_supply_unreg_usb;
+ }
+
+ ret = power_supply_register(&pdev->dev, &battery->psy_bat);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s: Failed to Register psy_bat\n", __func__);
+ goto err_supply_unreg_ac;
+ }
+
+ if (battery->pdata->bat_gpio_init && !battery->pdata->bat_gpio_init()) {
+ dev_err(battery->dev,
+ "%s: Failed to Initialize GPIO\n", __func__);
+ goto err_supply_unreg_bat;
+ }
+#if 0
+ if (battery->pdata->bat_irq) {
+ ret = request_threaded_irq(battery->pdata->bat_irq,
+ NULL, sec_bat_irq_thread,
+ battery->pdata->bat_irq_attr,
+ "battery-irq", battery);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s: Failed to Request IRQ, %d, %d\n", __func__, battery->pdata->bat_irq, ret);
+ goto err_supply_unreg_ac;
+ }
+
+ ret = enable_irq_wake(battery->pdata->bat_irq);
+ if (ret < 0)
+ dev_err(battery->dev,
+ "%s: Failed to Enable Wakeup Source(%d)\n",
+ __func__, ret);
+ }
+#endif
+
+ ret = sec_bat_create_attrs(battery->psy_bat.dev);
+ if (ret) {
+ dev_err(battery->dev,
+ "%s : Failed to create_attrs\n", __func__);
+ goto err_req_irq;
+ }
+ //sprdbat_creat_caliberate_attr(battery->psy_bat.dev);
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+ muic_notifier_register(&battery->batt_nb,
+ batt_handle_notification,
+ MUIC_NOTIFY_DEV_CHARGER);
+#endif
+
+ if (pdata->initial_check)
+ pdata->initial_check(battery);
+ else
+ sec_bat_initial_check();
+
+ psy_do_property("battery", get,
+ POWER_SUPPLY_PROP_ONLINE, value);
+
+ if (value.intval == POWER_SUPPLY_TYPE_BATTERY) {
+ dev_info(&pdev->dev,
+ "%s: SEC Battery Driver Monitorwork\n", __func__);
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+ }
+
+ if (battery->pdata->check_battery_callback)
+ battery->present = battery->pdata->check_battery_callback(battery);
+
+ dev_info(battery->dev,
+ "%s: SEC Battery Driver Loaded\n", __func__);
+ //sec_bat_set_charge(battery, true);
+ return 0;
+
+err_req_irq:
+ if (battery->pdata->bat_irq)
+ free_irq(battery->pdata->bat_irq, battery);
+err_supply_unreg_ac:
+ power_supply_unregister(&battery->psy_ac);
+err_supply_unreg_usb:
+ power_supply_unregister(&battery->psy_usb);
+err_supply_unreg_bat:
+ power_supply_unregister(&battery->psy_bat);
+err_supply_unreg_wireless:
+ power_supply_unregister(&battery->psy_wireless);
+err_supply_unreg_ps:
+ power_supply_unregister(&battery->psy_ps);
+err_workqueue:
+ destroy_workqueue(battery->monitor_wqueue);
+err_wake_lock:
+ wake_lock_destroy(&battery->monitor_wake_lock);
+ wake_lock_destroy(&battery->cable_wake_lock);
+ wake_lock_destroy(&battery->vbus_wake_lock);
+ mutex_destroy(&battery->adclock);
+ kfree(pdata);
+err_bat_free:
+ kfree(battery);
+
+ return ret;
+}
+
+static int sec_battery_remove(struct platform_device *pdev)
+{
+ struct sec_battery_info *battery = platform_get_drvdata(pdev);
+#ifndef CONFIG_OF
+ int i;
+#endif
+
+ dev_dbg(battery->dev, "%s: Start\n", __func__);
+
+ switch (battery->pdata->polling_type) {
+ case SEC_BATTERY_MONITOR_WORKQUEUE:
+ cancel_delayed_work(&battery->polling_work);
+ break;
+ case SEC_BATTERY_MONITOR_ALARM:
+ alarm_cancel(&battery->polling_alarm);
+ break;
+ default:
+ break;
+ }
+
+ alarm_cancel(&battery->event_termination_alarm);
+ flush_workqueue(battery->monitor_wqueue);
+ destroy_workqueue(battery->monitor_wqueue);
+ wake_lock_destroy(&battery->monitor_wake_lock);
+ wake_lock_destroy(&battery->cable_wake_lock);
+ wake_lock_destroy(&battery->vbus_wake_lock);
+
+ mutex_destroy(&battery->adclock);
+#ifdef CONFIG_OF
+ // adc_exit(battery);
+#else
+ for (i = 0; i < SEC_BAT_ADC_CHANNEL_FULL_CHECK; i++)
+ adc_exit(battery->pdata, i);
+#endif
+
+ power_supply_unregister(&battery->psy_ps);
+ power_supply_unregister(&battery->psy_wireless);
+ power_supply_unregister(&battery->psy_ac);
+ power_supply_unregister(&battery->psy_usb);
+ power_supply_unregister(&battery->psy_bat);
+
+ dev_dbg(battery->dev, "%s: End\n", __func__);
+ kfree(battery);
+
+ return 0;
+}
+
+static int sec_battery_prepare(struct device *dev)
+{
+ struct sec_battery_info *battery
+ = dev_get_drvdata(dev);
+
+ dev_dbg(battery->dev, "%s: Start\n", __func__);
+
+ switch (battery->pdata->polling_type) {
+ case SEC_BATTERY_MONITOR_WORKQUEUE:
+ cancel_delayed_work(&battery->polling_work);
+ break;
+ case SEC_BATTERY_MONITOR_ALARM:
+ alarm_cancel(&battery->polling_alarm);
+ break;
+ default:
+ break;
+ }
+ cancel_delayed_work_sync(&battery->monitor_work);
+
+ battery->polling_in_sleep = true;
+
+ sec_bat_set_polling(battery);
+
+ /* cancel work for polling
+ * that is set in sec_bat_set_polling()
+ * no need for polling in sleep
+ */
+ if (battery->pdata->polling_type ==
+ SEC_BATTERY_MONITOR_WORKQUEUE)
+ cancel_delayed_work(&battery->polling_work);
+
+ dev_dbg(battery->dev, "%s: End\n", __func__);
+
+ return 0;
+}
+
+static int sec_battery_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int sec_battery_resume(struct device *dev)
+{
+ return 0;
+}
+
+static void sec_battery_complete(struct device *dev)
+{
+ struct sec_battery_info *battery
+ = dev_get_drvdata(dev);
+
+ dev_dbg(battery->dev, "%s: Start\n", __func__);
+
+ /* cancel current alarm and reset after monitor work */
+ if (battery->pdata->polling_type == SEC_BATTERY_MONITOR_ALARM)
+ alarm_cancel(&battery->polling_alarm);
+
+ wake_lock(&battery->monitor_wake_lock);
+ queue_delayed_work(battery->monitor_wqueue,
+ &battery->monitor_work, msecs_to_jiffies(500));
+
+ dev_dbg(battery->dev, "%s: End\n", __func__);
+
+ return;
+}
+
+static void sec_battery_shutdown(struct device *dev)
+{
+}
+
+#ifdef CONFIG_OF
+static struct of_device_id sec_battery_dt_ids[] = {
+ { .compatible = "samsung,sec-battery" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sec_battery_dt_ids);
+#endif /* CONFIG_OF */
+
+static const struct dev_pm_ops sec_battery_pm_ops = {
+ .prepare = sec_battery_prepare,
+ .suspend = sec_battery_suspend,
+ .resume = sec_battery_resume,
+ .complete = sec_battery_complete,
+};
+
+static struct platform_driver sec_battery_driver = {
+ .driver = {
+ .name = "sec-battery",
+ .owner = THIS_MODULE,
+ .pm = &sec_battery_pm_ops,
+ .shutdown = sec_battery_shutdown,
+#ifdef CONFIG_OF
+ .of_match_table = sec_battery_dt_ids,
+#endif
+ },
+ .probe = sec_battery_probe,
+ .remove = sec_battery_remove,
+};
+
+static int __init sec_battery_init(void)
+{
+ return platform_driver_register(&sec_battery_driver);
+}
+
+static void __exit sec_battery_exit(void)
+{
+ platform_driver_unregister(&sec_battery_driver);
+}
+
+late_initcall(sec_battery_init);
+module_exit(sec_battery_exit);
+
+MODULE_DESCRIPTION("Samsung Battery Driver");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_LICENSE("GPL");