aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/battery
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/battery')
-rw-r--r--drivers/battery/Makefile3
-rw-r--r--drivers/battery/battery-factory.c253
-rw-r--r--drivers/battery/battery-factory.h1
-rw-r--r--drivers/battery/max17047_fuelgauge.c102
-rw-r--r--drivers/battery/max77693_charger.c619
-rw-r--r--drivers/battery/samsung_battery.c868
-rw-r--r--drivers/battery/samsung_battery_s2plus.c1273
7 files changed, 1422 insertions, 1697 deletions
diff --git a/drivers/battery/Makefile b/drivers/battery/Makefile
index e70ab69..bb1af5e 100644
--- a/drivers/battery/Makefile
+++ b/drivers/battery/Makefile
@@ -2,9 +2,6 @@
obj-$(CONFIG_BATTERY_SAMSUNG) += samsung_battery.o \
battery-factory.o
-obj-$(CONFIG_BATTERY_SAMSUNG_S2PLUS) += samsung_battery_s2plus.o \
- battery-factory.o
-
obj-$(CONFIG_MAX8997_CHARGER) += max8997-charger.o
obj-$(CONFIG_BATTERY_MAX17043_FUELGAUGE) += max17043_fuelgauge.o
diff --git a/drivers/battery/battery-factory.c b/drivers/battery/battery-factory.c
index e244c8e..c1cd9b1 100644
--- a/drivers/battery/battery-factory.c
+++ b/drivers/battery/battery-factory.c
@@ -17,53 +17,64 @@
#include "battery-factory.h"
-static ssize_t battery_show_property(struct device *dev,
+/* prototype */
+static ssize_t factory_show_property(struct device *dev,
struct device_attribute *attr, char *buf);
-static ssize_t battery_store_property(struct device *dev,
+static ssize_t factory_store_property(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count);
-#define BATTERY_ATTR(_name) \
+static ssize_t ctia_show_property(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t ctia_store_property(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+#define FACTORY_ATTR(_name) \
{ \
.attr = { .name = #_name, \
.mode = S_IRUGO | S_IWUSR | S_IWGRP, \
}, \
- .show = battery_show_property, \
- .store = battery_store_property, \
+ .show = factory_show_property, \
+ .store = factory_store_property, \
}
-static struct device_attribute battery_attrs[] = {
- BATTERY_ATTR(batt_reset_soc),
- BATTERY_ATTR(batt_read_raw_soc),
- BATTERY_ATTR(batt_read_adj_soc),
- BATTERY_ATTR(batt_type),
- BATTERY_ATTR(batt_temp_adc),
- BATTERY_ATTR(batt_temp_aver),
- BATTERY_ATTR(batt_temp_adc_aver),
- BATTERY_ATTR(batt_vol_aver),
- BATTERY_ATTR(batt_vfocv),
- BATTERY_ATTR(batt_lp_charging),
- BATTERY_ATTR(batt_charging_source),
- BATTERY_ATTR(test_mode),
- BATTERY_ATTR(batt_error_test),
- BATTERY_ATTR(siop_activated),
- BATTERY_ATTR(wc_status),
- BATTERY_ATTR(wpc_pin_state),
- BATTERY_ATTR(factory_mode),
- BATTERY_ATTR(update),
+static struct device_attribute factory_attrs[] = {
+ FACTORY_ATTR(batt_reset_soc),
+ FACTORY_ATTR(batt_read_raw_soc),
+ FACTORY_ATTR(batt_read_adj_soc),
+ FACTORY_ATTR(batt_type),
+ FACTORY_ATTR(batt_temp_adc),
+ FACTORY_ATTR(batt_temp_aver),
+ FACTORY_ATTR(batt_temp_adc_aver),
+ FACTORY_ATTR(batt_vol_aver),
+ FACTORY_ATTR(batt_vfocv),
+ FACTORY_ATTR(batt_lp_charging),
+ FACTORY_ATTR(batt_charging_source),
+ FACTORY_ATTR(test_mode),
+ FACTORY_ATTR(batt_error_test),
+ FACTORY_ATTR(siop_activated),
+ FACTORY_ATTR(siop_level),
+ FACTORY_ATTR(wc_status),
+ FACTORY_ATTR(wpc_pin_state),
+ FACTORY_ATTR(factory_mode),
+ FACTORY_ATTR(update),
+ FACTORY_ATTR(batt_slate_mode),
+ FACTORY_ATTR(batt_vf_adc),
/* not use */
- BATTERY_ATTR(batt_vol_adc),
- BATTERY_ATTR(batt_vol_adc_cal),
- BATTERY_ATTR(batt_vol_adc_aver),
- BATTERY_ATTR(batt_temp_adc_cal),
- BATTERY_ATTR(batt_vf_adc),
- BATTERY_ATTR(auth_battery),
+ FACTORY_ATTR(batt_vol_adc),
+ FACTORY_ATTR(batt_vol_adc_cal),
+ FACTORY_ATTR(batt_vol_adc_aver),
+ FACTORY_ATTR(batt_temp_adc_cal),
+ FACTORY_ATTR(auth_battery),
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
- BATTERY_ATTR(batt_temp_adc_spec),
- BATTERY_ATTR(batt_sysrev),
+ FACTORY_ATTR(batt_temp_adc_spec),
+ FACTORY_ATTR(batt_sysrev),
#endif
};
@@ -82,17 +93,19 @@ enum {
TEST_MODE,
BATT_ERROR_TEST,
SIOP_ACTIVATED,
+ SIOP_LEVEL,
WC_STATUS,
WPC_PIN_STATE,
FACTORY_MODE,
UPDATE,
+ BATT_SLATE_MODE,
+ BATT_VF_ADC,
/* not use */
BATT_VOL_ADC,
BATT_VOL_ADC_CAL,
BATT_VOL_ADC_AVER,
BATT_TEMP_ADC_CAL,
- BATT_VF_ADC,
AUTH_BATTERY,
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
@@ -101,15 +114,15 @@ enum {
#endif
};
-static ssize_t battery_show_property(struct device *dev,
+static ssize_t factory_show_property(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct battery_info *info = dev_get_drvdata(dev->parent);
int i;
int cnt, dat, d_max, d_min, d_total;
int val;
- const ptrdiff_t off = attr - battery_attrs;
- pr_debug("%s: %s\n", __func__, battery_attrs[off].attr.name);
+ const ptrdiff_t off = attr - factory_attrs;
+ pr_debug("%s: %s\n", __func__, factory_attrs[off].attr.name);
i = 0;
val = 0;
@@ -120,8 +133,8 @@ static ssize_t battery_show_property(struct device *dev,
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
break;
case BATT_READ_ADJ_SOC:
- battery_get_info(info, POWER_SUPPLY_PROP_CAPACITY);
- val = info->battery_soc;
+ val = info->battery_soc =
+ battery_get_info(info, POWER_SUPPLY_PROP_CAPACITY);
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
break;
case BATT_TYPE:
@@ -185,8 +198,8 @@ static ssize_t battery_show_property(struct device *dev,
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
break;
case BATT_CHARGING_SOURCE:
- battery_get_info(info, POWER_SUPPLY_PROP_ONLINE);
- val = info->cable_type;
+ val = info->cable_type =
+ battery_get_info(info, POWER_SUPPLY_PROP_ONLINE);
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
break;
case TEST_MODE:
@@ -202,6 +215,10 @@ static ssize_t battery_show_property(struct device *dev,
val = info->siop_state;
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
break;
+ case SIOP_LEVEL:
+ val = info->siop_lv;
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
+ break;
case WC_STATUS:
case WPC_PIN_STATE:
#ifdef CONFIG_BATTERY_WPC_CHARGER
@@ -215,11 +232,19 @@ static ssize_t battery_show_property(struct device *dev,
i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
info->factory_mode);
break;
+ case BATT_SLATE_MODE:
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n",
+ info->slate_mode);
+ break;
+ case BATT_VF_ADC:
+ battery_get_info(info, POWER_SUPPLY_PROP_PRESENT);
+ val = info->battery_vf_adc;
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d\n", val);
+ break;
case BATT_VOL_ADC:
case BATT_VOL_ADC_CAL:
case BATT_VOL_ADC_AVER:
case BATT_TEMP_ADC_CAL:
- case BATT_VF_ADC:
case AUTH_BATTERY:
i += scnprintf(buf + i, PAGE_SIZE - i, "N/A\n");
break;
@@ -244,15 +269,15 @@ static ssize_t battery_show_property(struct device *dev,
return i;
}
-static ssize_t battery_store_property(struct device *dev,
+static ssize_t factory_store_property(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
struct battery_info *info = dev_get_drvdata(dev->parent);
int x;
int ret;
- const ptrdiff_t off = attr - battery_attrs;
- pr_info("%s: %s\n", __func__, battery_attrs[off].attr.name);
+ const ptrdiff_t off = attr - factory_attrs;
+ pr_info("%s: %s\n", __func__, factory_attrs[off].attr.name);
x = 0;
ret = 0;
@@ -264,6 +289,9 @@ static ssize_t battery_store_property(struct device *dev,
battery_control_info(info,
POWER_SUPPLY_PROP_CAPACITY,
1);
+ info->battery_soc =
+ battery_get_info(info,
+ POWER_SUPPLY_PROP_CAPACITY);
} else
pr_info("%s: Not supported param.\n", __func__);
ret = count;
@@ -288,17 +316,19 @@ static ssize_t battery_store_property(struct device *dev,
case SIOP_ACTIVATED:
if (sscanf(buf, "%d\n", &x) == 1) {
info->siop_state = x;
-
- if (info->siop_state == SIOP_ACTIVE)
- info->siop_charge_current =
- info->pdata->chg_curr_usb;
-
pr_info("%s: SIOP %s\n", __func__,
(info->siop_state ?
"activated" : "deactivated"));
ret = count;
}
break;
+ case SIOP_LEVEL:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ info->siop_lv = x;
+ pr_info("%s: SIOP level %d\n", __func__, info->siop_lv);
+ ret = count;
+ }
+ break;
case FACTORY_MODE:
if (sscanf(buf, "%d\n", &x) == 1) {
if (x)
@@ -315,6 +345,18 @@ static ssize_t battery_store_property(struct device *dev,
pr_info("%s: battery update\n", __func__);
ret = count;
break;
+ case BATT_SLATE_MODE:
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ if (x)
+ info->slate_mode = 1;
+ else
+ info->slate_mode = 0;
+
+ pr_info("%s: slate_mode %s\n", __func__,
+ (info->slate_mode ? "set" : "clear"));
+ ret = count;
+ }
+ break;
default:
ret = -EINVAL;
}
@@ -324,23 +366,118 @@ static ssize_t battery_store_property(struct device *dev,
return ret;
}
+#define CTIA_ATTR(_name) \
+{ \
+ .attr = { .name = #_name, \
+ .mode = S_IRUGO | S_IWUSR | S_IWGRP, \
+ }, \
+ .show = ctia_show_property, \
+ .store = ctia_store_property, \
+}
+
+/* CTIA */
+static struct device_attribute ctia_attrs[] = {
+ CTIA_ATTR(talk_wcdma),
+ CTIA_ATTR(talk_gsm),
+ CTIA_ATTR(call),
+ CTIA_ATTR(video),
+ CTIA_ATTR(music),
+ CTIA_ATTR(browser),
+ CTIA_ATTR(hotspot),
+ CTIA_ATTR(camera),
+ CTIA_ATTR(data_call),
+ CTIA_ATTR(gps),
+ CTIA_ATTR(lte),
+ CTIA_ATTR(wifi),
+ CTIA_ATTR(use),
+};
+
+static ssize_t ctia_show_property(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct battery_info *info = dev_get_drvdata(dev->parent);
+ int i = 0;
+ const ptrdiff_t off = attr - ctia_attrs;
+ pr_info("%s: %s\n", __func__, ctia_attrs[off].attr.name);
+
+ i += scnprintf(buf + i, PAGE_SIZE - i, "%d 0x%04x\n",
+ info->event_state, info->event_type);
+
+ return i;
+}
+
+static ssize_t ctia_store_property(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct battery_info *info = dev_get_drvdata(dev->parent);
+ int x = 0;
+ int ret = -EINVAL;
+ const ptrdiff_t off = attr - ctia_attrs;
+ pr_info("%s: %s\n", __func__, ctia_attrs[off].attr.name);
+
+ if (sscanf(buf, "%d\n", &x) == 1) {
+ if (x == 1) {
+ info->event_type |= (1 << off);
+ pr_info("%s: set case #%d, event(0x%04x)\n",
+ __func__, off, info->event_type);
+ } else if (x == 0) {
+ info->event_type &= ~(1 << off);
+ pr_info("%s: clear case #%d, event(0x%04x)\n",
+ __func__, off, info->event_type);
+ } else {
+ pr_info("%s: invalid case #%d, event(0x%04x)\n",
+ __func__, off, info->event_type);
+ }
+ ret = count;
+ }
+
+ battery_event_control(info);
+
+ return ret;
+}
+
void battery_create_attrs(struct device *dev)
{
+ struct battery_info *info = dev_get_drvdata(dev->parent);
int i, rc;
+ pr_info("%s\n", __func__);
- for (i = 0; i < ARRAY_SIZE(battery_attrs); i++) {
- rc = device_create_file(dev, &battery_attrs[i]);
- pr_debug("%s: battery attr.: %s\n", __func__,
- battery_attrs[i].attr.name);
+ for (i = 0; i < ARRAY_SIZE(factory_attrs); i++) {
+ rc = device_create_file(dev, &factory_attrs[i]);
+ pr_debug("%s: factory attr: %s\n", __func__,
+ factory_attrs[i].attr.name);
if (rc)
- goto create_attrs_failed;
+ goto create_factory_attrs_failed;
+ }
+ pr_info("%s: factory attrs created\n", __func__);
+
+ if (!info->pdata->ctia_spec) {
+ pr_info("%s: not support CTIA spec\n", __func__);
+ return;
}
- goto succeed;
-create_attrs_failed:
+ for (i = 0; i < ARRAY_SIZE(ctia_attrs); i++) {
+ rc = device_create_file(dev, &ctia_attrs[i]);
+ pr_debug("%s: CTIA attr: %s\n", __func__,
+ ctia_attrs[i].attr.name);
+ if (rc)
+ goto create_ctia_attrs_failed;
+ }
+ pr_info("%s: CTIA attrs created\n", __func__);
+
+ return;
+
+create_factory_attrs_failed:
+ pr_info("%s: factory attrs created failed\n", __func__);
+ while (i--)
+ device_remove_file(dev, &factory_attrs[i]);
+ return;
+
+create_ctia_attrs_failed:
+ pr_info("%s: CTIA attrs created failed\n", __func__);
while (i--)
- device_remove_file(dev, &battery_attrs[i]);
-succeed:
+ device_remove_file(dev, &ctia_attrs[i]);
return;
}
diff --git a/drivers/battery/battery-factory.h b/drivers/battery/battery-factory.h
index c2f9b93..459249d 100644
--- a/drivers/battery/battery-factory.h
+++ b/drivers/battery/battery-factory.h
@@ -31,6 +31,7 @@ extern void battery_update_info(struct battery_info *info);
extern void battery_control_info(struct battery_info *info,
enum power_supply_property property,
int intval);
+extern void battery_event_control(struct battery_info *info);
#endif /* CONFIG_SYSFS */
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
diff --git a/drivers/battery/max17047_fuelgauge.c b/drivers/battery/max17047_fuelgauge.c
index fc01547..ca87c15 100644
--- a/drivers/battery/max17047_fuelgauge.c
+++ b/drivers/battery/max17047_fuelgauge.c
@@ -100,6 +100,11 @@ struct max17047_fuelgauge_data {
/* adjust full soc */
int full_soc;
+#ifdef USE_TRIM_ERROR_DETECTION
+ /* trim error state */
+ bool trim_err;
+#endif
+
#ifdef CONFIG_DEBUG_FS
struct dentry *fg_debugfs_dir;
#endif
@@ -260,55 +265,32 @@ static int max17047_get_rawsoc(struct i2c_client *client)
static int max17047_get_soc(struct i2c_client *client)
{
struct max17047_fuelgauge_data *fg_data = i2c_get_clientdata(client);
- int rawsoc, soc;
+ int rawsoc, soc, fullsoc, empty;
pr_debug("%s\n", __func__);
rawsoc = max17047_get_rawsoc(fg_data->client);
-#if defined(CONFIG_MACH_C1_KOR_SKT) || \
- defined(CONFIG_MACH_C1_KOR_KT) || \
- defined(CONFIG_MACH_C1_KOR_LGT)
- if (fg_data->full_soc <= 0)
- fg_data->full_soc = FULL_SOC_DEFAULT;
-
- soc = fg_data->soc =
- ((rawsoc < 0) ? 0 : (min((rawsoc * 100 /
- fg_data->full_soc), 100)));
-#elif defined(CONFIG_MACH_M0_KOR_SKT) || \
- defined(CONFIG_MACH_M0_KOR_KT)
- if (fg_data->full_soc <= 0)
- fg_data->full_soc = FULL_SOC_DEFAULT;
-
- soc = fg_data->soc =
- ((rawsoc < 29) ? 0 : (min(((rawsoc - 29) * 100 /
- (fg_data->full_soc - 29)), 100)));
-#elif defined(CONFIG_MACH_T0_KOR_SKT) || \
- defined(CONFIG_MACH_T0_KOR_KT) || \
- defined(CONFIG_MACH_T0_KOR_LGT)
- if (fg_data->full_soc <= 0)
- fg_data->full_soc = FULL_SOC_DEFAULT;
+#if defined(CONFIG_MACH_C1)
+ empty = 0;
+#else /* M0, T0,,, */
+ empty = 29;
+#endif
- soc = fg_data->soc =
- ((rawsoc < 29) ? 0 : (min(((rawsoc - 29) * 100 /
- (fg_data->full_soc - 29)), 100)));
-#elif defined(CONFIG_MACH_M0_CTC)
if (fg_data->full_soc <= 0)
fg_data->full_soc = FULL_SOC_DEFAULT;
+ fullsoc = fg_data->full_soc - empty;
+ rawsoc -= empty;
- soc = fg_data->soc =
- ((rawsoc < 29) ? 0 : (min(((rawsoc - 29) * 100 /
- (fg_data->full_soc - 29)), 100)));
-#else
- /* M0 */
- if (fg_data->full_soc <= 0)
- fg_data->full_soc = FULL_SOC_DEFAULT;
+/* adjust fullsoc value for fast termination */
+#if defined(USE_2STEP_TERM) && !defined(CONFIG_TARGET_LOCALE_KOR)
+ fullsoc *= 99;
+ fullsoc /= 100;
+#endif
soc = fg_data->soc =
- ((rawsoc < 29) ? 0 : (min(((rawsoc - 29) * 100 /
- (fg_data->full_soc - 29)), 100)));
-#endif
+ ((rawsoc < empty) ? 0 : (min((rawsoc * 100 / fullsoc), 100)));
- pr_debug("%s: SOC(%d, %d)\n", __func__, soc, rawsoc);
+ pr_info("%s: SOC(%d, %d / %d)\n", __func__, soc, rawsoc, fullsoc);
return soc;
}
@@ -371,8 +353,8 @@ static void max17047_adjust_fullsoc(struct i2c_client *client)
}
if (prev_full_soc != fg_data->full_soc)
- pr_info("%s : full_soc = %d, keep_soc = %d\n", __func__,
- fg_data->full_soc, keep_soc);
+ pr_info("%s : p_full_soc(%d), full_soc(%d), keep_soc(%d)\n",
+ __func__, prev_full_soc, fg_data->full_soc, keep_soc);
}
/* SOC% alert, disabled(0xFF00) */
@@ -503,7 +485,6 @@ static void max17047_update_work(struct work_struct *work)
if (!battery_psy || !battery_psy->set_property) {
pr_err("%s: fail to get battery power supply\n", __func__);
- mutex_unlock(&fg_data->irq_lock);
return;
}
@@ -554,8 +535,9 @@ static enum power_supply_property max17047_fuelgauge_props[] = {
/* Temp: Init max17047 sample has trim value error. For detecting that. */
#define TRIM_ERROR_DETECT_VOLTAGE1 2500000
#define TRIM_ERROR_DETECT_VOLTAGE2 3600000
-static int max17047_detect_trim_error(struct max17047_fuelgauge_data *fg_data)
+static bool max17047_detect_trim_error(struct max17047_fuelgauge_data *fg_data)
{
+ bool ret = false;
int vcell, soc;
vcell = max17047_get_vcell(fg_data->client);
@@ -563,12 +545,12 @@ static int max17047_detect_trim_error(struct max17047_fuelgauge_data *fg_data)
if (((vcell < TRIM_ERROR_DETECT_VOLTAGE1) ||
(vcell == TRIM_ERROR_DETECT_VOLTAGE2)) && (soc == 0)) {
- pr_debug("%s: (maybe)It's a trim error version. "
+ pr_err("%s: (maybe)It's a trim error version. "
"VCELL(%d), SOC(%d)\n", __func__, vcell, soc);
- return 1;
+ ret = true;
}
- return 0;
+ return ret;
}
#endif
@@ -581,7 +563,7 @@ static int max17047_get_property(struct power_supply *psy,
fuelgauge);
#ifdef USE_TRIM_ERROR_DETECTION
- if (max17047_detect_trim_error(fg_data)) {
+ if (fg_data->trim_err == true) {
switch (psp) {
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
@@ -674,7 +656,6 @@ static irqreturn_t max17047_fuelgauge_isr(int irq, void *data)
{
struct max17047_fuelgauge_data *fg_data = data;
struct i2c_client *client = fg_data->client;
- union power_supply_propval value;
u8 i2c_data[2];
pr_info("%s: irq(%d)\n", __func__, irq);
mutex_lock(&fg_data->irq_lock);
@@ -683,6 +664,7 @@ static irqreturn_t max17047_fuelgauge_isr(int irq, void *data)
pr_info("%s: MAX17047_REG_STATUS(0x%02x%02x)\n", __func__,
i2c_data[1], i2c_data[0]);
+ cancel_delayed_work(&fg_data->update_work);
wake_lock(&fg_data->update_wake_lock);
schedule_delayed_work(&fg_data->update_work, msecs_to_jiffies(1000));
@@ -842,10 +824,15 @@ static int __devinit max17047_fuelgauge_i2c_probe(struct i2c_client *client,
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct max17047_fuelgauge_data *fg_data;
- int ret;
- u8 i2c_data[2];
+ struct max17047_platform_data *pdata = client->dev.platform_data;
+ int ret = -ENODEV;
int rawsoc, firstsoc;
- pr_info("%s: max17047 Fuel gauge Driver Loading\n", __func__);
+ pr_info("%s: fuelgauge init\n", __func__);
+
+ if (!pdata) {
+ pr_err("%s: no platform data\n", __func__);
+ return -ENODEV;
+ }
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
@@ -855,7 +842,7 @@ static int __devinit max17047_fuelgauge_i2c_probe(struct i2c_client *client,
return -ENOMEM;
fg_data->client = client;
- fg_data->pdata = client->dev.platform_data;
+ fg_data->pdata = pdata;
i2c_set_clientdata(client, fg_data);
@@ -864,6 +851,11 @@ static int __devinit max17047_fuelgauge_i2c_probe(struct i2c_client *client,
wake_lock_init(&fg_data->update_wake_lock, WAKE_LOCK_SUSPEND,
"fuel-update");
+#ifdef USE_TRIM_ERROR_DETECTION
+ /* trim error detect */
+ fg_data->trim_err = max17047_detect_trim_error(fg_data);
+#endif
+
/* Initialize full_soc, set this before fisrt SOC reading */
fg_data->full_soc = FULL_SOC_DEFAULT;
/* first full_soc update */
@@ -899,6 +891,9 @@ static int __devinit max17047_fuelgauge_i2c_probe(struct i2c_client *client,
/* Initialize fuelgauge alert */
max17047_alert_init(fg_data);
+ INIT_DELAYED_WORK_DEFERRABLE(&fg_data->update_work,
+ max17047_update_work);
+
/* Request IRQ */
fg_data->irq = gpio_to_irq(fg_data->pdata->irq_gpio);
ret = gpio_request(fg_data->pdata->irq_gpio, "fuelgauge-irq");
@@ -926,8 +921,6 @@ static int __devinit max17047_fuelgauge_i2c_probe(struct i2c_client *client,
goto err_enable_irq;
}
- INIT_DELAYED_WORK_DEFERRABLE(&fg_data->update_work,
- max17047_update_work);
#ifdef DEBUG_FUELGAUGE_POLLING
INIT_DELAYED_WORK_DEFERRABLE(&fg_data->polling_work,
max17047_polling_work);
@@ -936,8 +929,7 @@ static int __devinit max17047_fuelgauge_i2c_probe(struct i2c_client *client,
max17047_test_read(fg_data);
#endif
- max17047_i2c_read(client, MAX17047_REG_VERSION, i2c_data);
- pr_info("max17047 fuelgauge(rev.%d%d) initialized.\n", i2c_data[0], i2c_data[1]);
+ pr_info("%s: probe complete\n", __func__);
#if defined(CONFIG_TARGET_LOCALE_KOR)
#ifdef CONFIG_DEBUG_FS
diff --git a/drivers/battery/max77693_charger.c b/drivers/battery/max77693_charger.c
index c17bbac..8609e6e 100644
--- a/drivers/battery/max77693_charger.c
+++ b/drivers/battery/max77693_charger.c
@@ -100,11 +100,26 @@
/* MAX77693_CHG_REG_CHG_CNFG_02 */
#define MAX77693_CHG_CC 0x3F
+/* MAX77693_CHG_REG_CHG_CNFG_03 */
+#define MAX77693_TO_ITH_MASK 0x06
+#define MAX77693_TO_ITH_SHIFT 0
+#define MAX77693_TO_TIME_MASK 0x38
+#define MAX77693_TO_TIME_SHIFT 3
+
/* MAX77693_CHG_REG_CHG_CNFG_04 */
#define MAX77693_CHG_MINVSYS_MASK 0xE0
#define MAX77693_CHG_MINVSYS_SHIFT 5
-#define MAX77693_CHG_PRM_MASK 0x1F
-#define MAX77693_CHG_PRM_SHIFT 0
+#define MAX77693_CHG_MINVSYS_3_6V 0x06
+#define MAX77693_CHG_CV_PRM_MASK 0x1F
+#define MAX77693_CHG_CV_PRM_SHIFT 0
+#define MAX77693_CHG_CV_PRM_4_20V 0x16
+#define MAX77693_CHG_CV_PRM_4_35V 0x1D
+#define MAX77693_CHG_CV_PRM_4_40V 0x1F
+
+/* MAX77693_CHG_REG_CHG_CNFG_06 */
+#define MAX77693_CHG_CHGPROT 0x0C
+#define MAX77693_CHG_CHGPROT_SHIFT 2
+#define MAX77693_CHG_CHGPROT_UNLOCK 0x03
/* MAX77693_CHG_REG_CHG_CNFG_09 */
#define MAX77693_CHG_CHGIN_LIM 0x7F
@@ -126,6 +141,13 @@
/* irq */
#define IRQ_DEBOUNCE_TIME 20 /* msec */
+/* charger unlock */
+#define CHG_UNLOCK_RETRY 10
+#define CHG_UNLOCK_DELAY 100
+
+/* power stabe guarantee */
+#define STABLE_POWER_DELAY 500
+
/* charger type detection */
#define DET_ERR_RETRY 5
#define DET_ERR_DELAY 200
@@ -138,8 +160,9 @@
/* soft regulation */
#define SW_REG_CURR_STEP_MA 100
-#define SW_REG_START_DELAY 1000
-#define SW_REG_STEP_DELAY 500
+#define SW_REG_CURR_MIN_MA 100
+#define SW_REG_START_DELAY 500
+#define SW_REG_STEP_DELAY 50
struct max77693_charger_data {
struct max77693_dev *max77693;
@@ -162,6 +185,9 @@ struct max77693_charger_data {
unsigned int battery_state;
unsigned int battery_present;
unsigned int cable_type;
+ unsigned int cable_sub_type;
+ unsigned int cable_pwr_type;
+ unsigned int dock_type;
unsigned int charging_current;
unsigned int vbus_state;
@@ -174,6 +200,7 @@ struct max77693_charger_data {
/* software regulation */
bool soft_reg_state;
int soft_reg_current;
+ bool soft_reg_ing;
/* unsufficient power */
bool reg_loop_deted;
@@ -216,39 +243,35 @@ static void max77693_dump_reg(struct max77693_charger_data *chg_data)
}
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
-static int max77693_is_topoff_state(struct max77693_charger_data *chg_data)
+static bool max77693_charger_unlock(struct max77693_charger_data *chg_data);
+static void max77693_charger_reg_init(struct max77693_charger_data *chg_data);
+
+static void check_charger_unlock_state(struct max77693_charger_data *chg_data)
{
struct i2c_client *i2c = chg_data->max77693->i2c;
- int state;
- u8 reg_data;
+ bool need_reg_init = false;
pr_debug("%s\n", __func__);
- max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_01, &reg_data);
- reg_data = ((reg_data & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT);
- pr_debug("%s: CHG_DTLS(0x%02x)\n", __func__, reg_data);
-
- if (reg_data == 0x3 || reg_data == 0x4)
- return 1;
- else
- return 0;
+ need_reg_init = max77693_charger_unlock(chg_data);
+ if (need_reg_init) {
+ pr_err("%s: charger locked state, reg init\n", __func__);
+ max77693_charger_reg_init(chg_data);
+ }
}
+#endif
-static bool check_charger_unlock_state(struct max77693_charger_data *chg_data)
+static int max77693_get_topoff_state(struct max77693_charger_data *chg_data)
{
struct i2c_client *i2c = chg_data->max77693->i2c;
u8 reg_data;
pr_debug("%s\n", __func__);
- max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, &reg_data);
- pr_debug("%s: chgprot = %d\n", __func__, reg_data);
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_01, &reg_data);
+ reg_data = ((reg_data & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT);
+ pr_debug("%s: CHG_DTLS(0x%02x)\n", __func__, reg_data);
- if ((reg_data&0x0C) != 0x0C) {
- pr_info("%s: NOT unlock!(%d)\n", __func__, reg_data);
- return false;
- } else
- return true;
+ return (reg_data == 0x4);
}
-#endif
static int max77693_get_battery_present(struct max77693_charger_data *chg_data)
{
@@ -348,9 +371,9 @@ static int max77693_get_charger_state(struct max77693_charger_data *chg_data)
case 0x0:
case 0x1:
case 0x2:
- case 0x3:
state = POWER_SUPPLY_STATUS_CHARGING;
break;
+ case 0x3:
case 0x4:
state = POWER_SUPPLY_STATUS_FULL;
break;
@@ -378,7 +401,7 @@ static void max77693_set_charger_state(struct max77693_charger_data *chg_data,
{
struct i2c_client *i2c = chg_data->max77693->i2c;
u8 reg_data;
- pr_debug("%s: enable=%d\n", __func__, enable);
+ pr_debug("%s: enable(%d)\n", __func__, enable);
max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, &reg_data);
@@ -396,7 +419,7 @@ static void max77693_set_buck(struct max77693_charger_data *chg_data,
{
struct i2c_client *i2c = chg_data->max77693->i2c;
u8 reg_data;
- pr_debug("%s: enable=%d\n", __func__, enable);
+ pr_debug("%s: enable(%d)\n", __func__, enable);
max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, &reg_data);
@@ -434,25 +457,22 @@ void max77693_set_input_current(struct max77693_charger_data *chg_data,
int step;
pr_debug("%s: set input current as %dmA\n", __func__, set_current);
+ mutex_lock(&chg_data->ops_lock);
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
- if (!check_charger_unlock_state(chg_data))
- pr_err("%s: charger NOT unlock state!!!\n", __func__);
+ check_charger_unlock_state(chg_data);
#endif
if (set_current == OFF_CURR) {
- pr_debug("%s: buck off current(%d)\n", __func__, set_current);
- max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, 0);
-
- max77693_set_buck(chg_data, DISABLE);
+ max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, set_current);
if (chg_data->soft_reg_state == true) {
pr_info("%s: exit soft regulation loop\n", __func__);
chg_data->soft_reg_state = false;
}
+ mutex_unlock(&chg_data->ops_lock);
return;
- } else
- max77693_set_buck(chg_data, ENABLE);
+ }
/* Set input current limit */
if (chg_data->soft_reg_state) {
@@ -460,8 +480,9 @@ void max77693_set_input_current(struct max77693_charger_data *chg_data,
chg_data->soft_reg_current);
in_curr = max77693_get_input_current(chg_data);
if (in_curr == chg_data->soft_reg_current) {
- pr_info("%s: same input current: %dmA\n",
+ pr_debug("%s: same input current: %dmA\n",
__func__, in_curr);
+ mutex_unlock(&chg_data->ops_lock);
return;
}
set_curr_reg = (chg_data->soft_reg_current / 20);
@@ -489,6 +510,8 @@ void max77693_set_input_current(struct max77693_charger_data *chg_data,
} while (now_curr_reg < set_curr_reg);
max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, set_curr_reg);
+
+ mutex_unlock(&chg_data->ops_lock);
}
int max77693_get_charge_current(struct max77693_charger_data *chg_data)
@@ -516,8 +539,7 @@ void max77693_set_charge_current(struct max77693_charger_data *chg_data,
pr_debug("%s: set charge current as %dmA\n", __func__, set_current);
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
- if (!check_charger_unlock_state(chg_data))
- pr_err("%s: charger NOT unlock state!!!\n", __func__);
+ check_charger_unlock_state(chg_data);
#endif
max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, &reg_data);
@@ -593,6 +615,7 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data)
int state = POWER_SUPPLY_TYPE_BATTERY;
u8 reg_data;
int muic_cb_typ;
+ u8 dtls_00, chgin_dtls = 0;
u8 mu_st2, vbvolt = 0;
pr_debug("%s\n", __func__);
@@ -601,7 +624,19 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data)
/* dock detect from muic */
if ((muic_cb_typ == CABLE_TYPE_CARDOCK_MUIC) ||
- (muic_cb_typ == CABLE_TYPE_DESKDOCK_MUIC)) {
+ (muic_cb_typ == CABLE_TYPE_DESKDOCK_MUIC) ||
+ (muic_cb_typ == CABLE_TYPE_SMARTDOCK_MUIC) ||
+ (muic_cb_typ == CABLE_TYPE_SMARTDOCK_TA_MUIC) ||
+ (muic_cb_typ == CABLE_TYPE_SMARTDOCK_USB_MUIC) ||
+ (muic_cb_typ == CABLE_TYPE_AUDIODOCK_MUIC)) {
+
+ chg_data->dock_type = muic_cb_typ;
+
+ /* read chgin, but not use */
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_DTLS_00, &dtls_00);
+ chgin_dtls = ((dtls_00 & MAX77693_CHGIN_DTLS) >>
+ MAX77693_CHGIN_DTLS_SHIFT);
/* check vbvolt */
max77693_read_reg(chg_data->max77693->muic,
@@ -609,8 +644,8 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data)
vbvolt = ((mu_st2 & MAX77693_VBVOLT) >>
MAX77693_VBVOLT_SHIFT);
- pr_info("%s: dock detected(%d), vbvolt(%d)\n", __func__,
- muic_cb_typ, vbvolt);
+ pr_info("%s: dock detected(%d), vbvolt(%d), chgin(0x%02x)\n",
+ __func__, muic_cb_typ, vbvolt, chgin_dtls);
if (vbvolt == ENABLE) {
max77693_read_reg(chg_data->max77693->i2c,
@@ -627,9 +662,11 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data)
MAX77693_CHG_REG_CHG_CNFG_00, reg_data);
state = POWER_SUPPLY_TYPE_BATTERY;
}
- } else
+ } else {
pr_debug("%s: dock not detected(%d), vbvolt(%d)\n", __func__,
muic_cb_typ, vbvolt);
+ chg_data->dock_type = 0;
+ }
return state;
}
@@ -640,16 +677,17 @@ static int max77693_get_cable_type(struct max77693_charger_data *chg_data)
u8 reg_data, mu_adc, mu_adc1k, otg;
u8 dtls_00, chgin_dtls;
u8 mu_st2, chgdetrun, vbvolt, chgtyp, dxovp;
+ int muic_cb_typ;
bool wc_state;
bool retry_det, chg_det_erred;
+ bool otg_detected = false;
int retry_cnt = 0;
pr_debug("%s\n", __func__);
mutex_lock(&chg_data->ops_lock);
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
- if (!check_charger_unlock_state(chg_data))
- pr_err("%s: charger NOT unlock state!!!\n", __func__);
+ check_charger_unlock_state(chg_data);
#endif
/* If OTG enabled, skip detecting charger cable */
@@ -666,10 +704,28 @@ static int max77693_get_cable_type(struct max77693_charger_data *chg_data)
pr_debug("%s: CHG_REG_CHG_CNFG_00(0x%02x)\n", __func__, reg_data);
otg = reg_data & MAX77693_MODE_OTG;
- if (otg || (mu_adc == 0x00 && !mu_adc1k)) {
+#ifdef CONFIG_MACH_GC1
+ /* In Factory mode using anyway Jig to switch between USB <--> UART
+ * sees a momentary 301K resistance as that of an OTG. Disabling
+ * charging INTRS now can lead to USB and MTP drivers not getting
+ * recognized in in subsequent switches.
+ * Factory Mode BOOT(on) USB
+ */
+ if (mu_adc == 0x19) {
+ pr_info("%s: jig usb cable(adc(0x%x))\n", __func__, mu_adc);
+ state = POWER_SUPPLY_TYPE_USB;
+ goto chg_det_finish;
+ }
+#endif
+
+ muic_cb_typ = max77693_muic_get_charging_type();
+ /* if type detection by otg, do not otg check */
+ if ((muic_cb_typ != CABLE_TYPE_AUDIODOCK_MUIC) &&
+ (((otg || (mu_adc == 0x00 && !mu_adc1k))))) {
pr_info("%s: otg enabled(otg(0x%x), adc(0x%x))\n",
__func__, otg, mu_adc);
state = POWER_SUPPLY_TYPE_BATTERY;
+ otg_detected = true;
goto chg_det_finish;
}
@@ -740,13 +796,23 @@ static int max77693_get_cable_type(struct max77693_charger_data *chg_data)
/* check D+/D- ovp */
dxovp = ((mu_st2 & MAX77693_DXOVP) >>
MAX77693_DXOVP_SHIFT);
- if (dxovp) {
+ if ((vbvolt == 0x1) && (dxovp)) {
pr_err("%s: D+/D- ovp state\n", __func__);
+
+ /* disable CHGIN protection FETs */
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_CNFG_00,
+ &reg_data);
+ reg_data |= CHG_CNFG_00_DIS_MUIC_CTRL_MASK;
+ max77693_write_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_CNFG_00,
+ reg_data);
+
chg_det_erred = true;
- state = POWER_SUPPLY_TYPE_USB;
+ state = POWER_SUPPLY_TYPE_MAINS;
goto chg_det_finish;
} else {
- pr_err("%s: async power and chgtyp\n", __func__);
+ pr_err("%s: async power & chgtyp\n", __func__);
goto chg_det_err;
}
}
@@ -787,7 +853,6 @@ chg_det_err:
state = POWER_SUPPLY_TYPE_BATTERY;
break;
case 0x1: /* USB cabled */
- case 0x4: /* Apple 500mA charger */
state = POWER_SUPPLY_TYPE_USB;
#ifdef CONFIG_BATTERY_WPC_CHARGER
wc_state = max77693_get_wc_state(chg_data);
@@ -799,6 +864,7 @@ chg_det_err:
state = POWER_SUPPLY_TYPE_USB_CDP;
break;
case 0x3: /* Dedicated charger(up to 1.5A) */
+ case 0x4: /* Apple 500mA charger */
case 0x5: /* Apple 1A or 2A charger */
case 0x6: /* Special charger */
state = POWER_SUPPLY_TYPE_MAINS;
@@ -812,9 +878,20 @@ chg_det_finish:
if (chg_det_erred)
pr_err("%s: cable type(%d)\n", __func__, state);
- /* if cable is nothing, clear soft reg state flag */
- if (state == POWER_SUPPLY_TYPE_BATTERY)
+ /* if cable is nothing,,, */
+ if (state == POWER_SUPPLY_TYPE_BATTERY) {
+ if (!otg_detected) {
+ /* enable CHGIN protection FETs */
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_CNFG_00, &reg_data);
+ reg_data &= ~CHG_CNFG_00_DIS_MUIC_CTRL_MASK;
+ max77693_write_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_CNFG_00, reg_data);
+ }
+
+ /* clear soft reg state flag */
chg_data->soft_reg_state = false;
+ }
chg_data->cable_type = state;
@@ -888,31 +965,122 @@ static int max77693_get_battery_state(struct max77693_charger_data *chg_data)
return state;
}
+/* extended online type */
+static int max77693_get_online_type(struct max77693_charger_data *chg_data)
+{
+ int m_typ;
+ int state = 0;
+ pr_info("%s\n", __func__);
+
+ m_typ = max77693_get_cable_type(chg_data);
+
+ pr_info("%s: main(%d), sub(%d), pwr(%d)\n", __func__, m_typ,
+ chg_data->cable_sub_type,
+ chg_data->cable_pwr_type);
+
+ state = ((m_typ << ONLINE_TYPE_MAIN_SHIFT) |
+ (chg_data->cable_sub_type << ONLINE_TYPE_SUB_SHIFT) |
+ (chg_data->cable_pwr_type << ONLINE_TYPE_PWR_SHIFT));
+
+ pr_info("%s: online(0x%08x)\n", __func__, state);
+
+ return state;
+}
+
+void max77693_set_online_type(struct max77693_charger_data *chg_data, int data)
+{
+ int m_typ, s_typ, p_typ;
+ pr_info("%s: type(0x%08x)\n", __func__, data);
+
+ /* | 31-24: RSVD | 23-16: MAIN TYPE |
+ 15-8: SUB TYPE | 7-0: POWER TYPE | */
+ data &= ~(ONLINE_TYPE_RSVD_MASK);
+ m_typ = ((data & ONLINE_TYPE_MAIN_MASK) >> ONLINE_TYPE_MAIN_SHIFT);
+ chg_data->cable_sub_type = s_typ =
+ ((data & ONLINE_TYPE_SUB_MASK) >> ONLINE_TYPE_SUB_SHIFT);
+ chg_data->cable_pwr_type = p_typ =
+ ((data & ONLINE_TYPE_PWR_MASK) >> ONLINE_TYPE_PWR_SHIFT);
+ pr_info("%s: main(%d), sub(%d), pwr(%d)\n", __func__,
+ m_typ, s_typ, p_typ);
+
+ cancel_delayed_work(&chg_data->update_work);
+ wake_lock(&chg_data->update_wake_lock);
+ schedule_delayed_work(&chg_data->update_work,
+ msecs_to_jiffies(STABLE_POWER_DELAY));
+}
+
/* get cable type from muic */
void max77693_set_muic_cb_type(struct max77693_charger_data *chg_data, int data)
{
pr_info("%s: muic cable type(%d)\n", __func__, data);
+#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
+ check_charger_unlock_state(chg_data);
+#endif
+
cancel_delayed_work(&chg_data->update_work);
wake_lock(&chg_data->update_wake_lock);
- schedule_delayed_work(&chg_data->update_work, msecs_to_jiffies(500));
+ schedule_delayed_work(&chg_data->update_work,
+ msecs_to_jiffies(STABLE_POWER_DELAY));
}
-static void max77693_charger_reg_init(struct max77693_charger_data *chg_data)
+static bool max77693_charger_unlock(struct max77693_charger_data *chg_data)
{
struct i2c_client *i2c = chg_data->max77693->i2c;
u8 reg_data;
+ u8 chgprot;
+ int retry_cnt = 0;
+ bool need_init = false;
pr_debug("%s\n", __func__);
- /* unlock charger setting protect */
- reg_data = (0x03 << 2);
- max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, reg_data);
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, &reg_data);
+ chgprot = ((reg_data & MAX77693_CHG_CHGPROT) >>
+ MAX77693_CHG_CHGPROT_SHIFT);
+
+ if (chgprot == MAX77693_CHG_CHGPROT_UNLOCK) {
+ pr_debug("%s: unlocked state, return\n", __func__);
+ need_init = false;
+ goto unlock_finish;
+ }
+
+ do {
+ max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06,
+ (MAX77693_CHG_CHGPROT_UNLOCK <<
+ MAX77693_CHG_CHGPROT_SHIFT));
+
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, &reg_data);
+ chgprot = ((reg_data & MAX77693_CHG_CHGPROT) >>
+ MAX77693_CHG_CHGPROT_SHIFT);
+
+ if (chgprot != MAX77693_CHG_CHGPROT_UNLOCK) {
+ pr_err("%s: unlock err, chgprot(0x%x), retry(%d)\n",
+ __func__, chgprot, retry_cnt);
+ msleep(CHG_UNLOCK_DELAY);
+ } else {
+ pr_info("%s: unlock success, chgprot(0x%x)\n",
+ __func__, chgprot);
+ need_init = true;
+ break;
+ }
+ } while ((chgprot != MAX77693_CHG_CHGPROT_UNLOCK) &&
+ (++retry_cnt < CHG_UNLOCK_RETRY));
+
+unlock_finish:
+ return need_init;
+}
+
+static void max77693_charger_reg_init(struct max77693_charger_data *chg_data)
+{
+ struct i2c_client *i2c = chg_data->max77693->i2c;
+ u8 reg_data;
+ pr_debug("%s\n", __func__);
/*
* fast charge timer 10hrs
* restart threshold disable
* pre-qual charge enable(default)
*/
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_01, &reg_data);
reg_data = (0x04 << 0) | (0x03 << 4);
max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_01, reg_data);
@@ -920,6 +1088,7 @@ static void max77693_charger_reg_init(struct max77693_charger_data *chg_data)
* charge current 466mA(default)
* otg current limit 900mA
*/
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, &reg_data);
reg_data = (1 << 7);
max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, reg_data);
@@ -927,51 +1096,93 @@ static void max77693_charger_reg_init(struct max77693_charger_data *chg_data)
* top off current 100mA
* top off timer 0min
*/
- if (chg_data->max77693->pmic_rev == MAX77693_REV_PASS1)
- reg_data = (0x03 << 0); /* 125mA */
- else
- reg_data = (0x00 << 0); /* 100mA */
-
- reg_data |= (0x00 << 3);
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_03, &reg_data);
+ if (chg_data->max77693->pmic_rev == MAX77693_REV_PASS1) {
+ reg_data = (0x03 << 0); /* 125mA */
+ reg_data |= (0x00 << 3); /* 0min */
+ } else {
+#if defined(USE_2STEP_TERM) /* now only T0 */
+ reg_data = (0x04 << 0); /* 200mA */
+ reg_data |= (0x04 << 3); /* 40min */
+#else
+#if defined(CONFIG_MACH_GC1)
+ reg_data = (0x02 << 0); /* 150mA */
+ reg_data |= (0x00 << 3); /* 0min */
+#else /* M0, C1,,, */
+ reg_data = (0x00 << 0); /* 100mA */
+ reg_data |= (0x00 << 3); /* 0min */
+#endif
+#endif
+ }
max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_03, reg_data);
/*
* cv voltage 4.2V or 4.35V
* MINVSYS 3.6V(default)
*/
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_04, &reg_data);
+ reg_data &= (~MAX77693_CHG_MINVSYS_MASK);
+ reg_data |= (MAX77693_CHG_MINVSYS_3_6V << MAX77693_CHG_MINVSYS_SHIFT);
+ reg_data &= (~MAX77693_CHG_CV_PRM_MASK);
+#if defined(CONFIG_MACH_M0)
if ((system_rev != 3) && (system_rev >= 1))
- reg_data = (0xDD << 0);
+ reg_data |= (MAX77693_CHG_CV_PRM_4_35V << 0);
else
- reg_data = (0xD6 << 0);
+ reg_data |= (MAX77693_CHG_CV_PRM_4_20V << 0);
+#else /* C1, C2, M3, T0, ... */
+ reg_data |= (MAX77693_CHG_CV_PRM_4_35V << 0);
+#endif
/*
* For GC1 Model, MINVSYS is 3.4V.
- * For GC1 Model PRMV( Primary Charge Regn. Voltage) = 4.25V.
+ * For GC1 Model PRMV( Primary Charge Regn. Voltage) = 4.2V.
* Actual expected regulated voltage needs to be 4.2V but due to
- * internal resistance and circuit deviation we need to set the
- * benchmark a bit higher(4.25V).
+ * internal resistance and circuit deviation we might have to set the
+ * benchmark a bit higher sometimes. (4.225V now)
*/
#if defined(CONFIG_MACH_GC1)
- reg_data &= (~MAX77693_CHG_PRM_MASK);
- reg_data |= (0x18 << MAX77693_CHG_PRM_SHIFT);
+ reg_data &= (~MAX77693_CHG_CV_PRM_MASK);
+ reg_data |= (0x16 << MAX77693_CHG_CV_PRM_SHIFT);
reg_data &= (~MAX77693_CHG_MINVSYS_MASK);
reg_data |= (0x4 << MAX77693_CHG_MINVSYS_SHIFT);
#endif
-
pr_info("%s: battery cv voltage %s, (sysrev %d)\n", __func__,
- (((reg_data & MAX77693_CHG_PRM_MASK) == \
- (0x1D << MAX77693_CHG_PRM_SHIFT)) ? "4.35V" : "4.2V"),
- system_rev);
-
+ (((reg_data & MAX77693_CHG_CV_PRM_MASK) == \
+ (MAX77693_CHG_CV_PRM_4_35V << MAX77693_CHG_CV_PRM_SHIFT)) ?
+ "4.35V" : "4.2V"), system_rev);
max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_04, reg_data);
- /* VBYPSET 5V */
- reg_data = 0x50;
- max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_11, reg_data);
-
max77693_dump_reg(chg_data);
}
+static void max77693_reduce_input(struct max77693_charger_data *chg_data,
+ unsigned int curr)
+{
+ struct i2c_client *i2c = chg_data->max77693->i2c;
+ u8 reg_data;
+ pr_debug("%s: reduce %dmA\n", __func__, curr);
+
+ max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, &reg_data);
+ reg_data &= MAX77693_CHG_CHGIN_LIM;
+ chg_data->soft_reg_current = reg_data * 20;
+
+ if (chg_data->soft_reg_current < curr) {
+ pr_err("%s: recude curr(%d) is under now curr(%d)\n", __func__,
+ curr, chg_data->soft_reg_current);
+ return;
+ }
+
+ chg_data->soft_reg_current -= curr;
+ chg_data->soft_reg_current = max(chg_data->soft_reg_current,
+ SW_REG_CURR_MIN_MA);
+ pr_info("%s: %dmA to %dmA\n", __func__,
+ reg_data * 20, chg_data->soft_reg_current);
+
+ reg_data = (chg_data->soft_reg_current / 20);
+ pr_debug("%s: reg_data(0x%02x)\n", __func__, reg_data);
+ max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, reg_data);
+}
+
static void max77693_update_work(struct work_struct *work)
{
struct max77693_charger_data *chg_data = container_of(work,
@@ -1010,8 +1221,6 @@ static void max77693_update_work(struct work_struct *work)
switch (chg_data->irq - chg_data->max77693->irq_base) {
case MAX77693_CHG_IRQ_CHGIN_I:
- /* guarantee detection time */
- mdelay(100);
vbus_state = max77693_get_vbus_state(chg_data);
if (vbus_state == POWER_SUPPLY_VBUS_WEAK) {
pr_info("%s: vbus weak\n", __func__);
@@ -1039,49 +1248,91 @@ static void max77693_softreg_work(struct work_struct *work)
struct max77693_charger_data *chg_data = container_of(work,
struct max77693_charger_data,
softreg_work.work);
- struct i2c_client *i2c = chg_data->max77693->i2c;
- u8 reg_data;
- u8 int_ok, dtls_02, byp_dtls;
- pr_info("%s\n", __func__);
+ u8 int_ok;
+ u8 dtls_00, chgin_dtls;
+ u8 dtls_01, chg_dtls;
+ u8 dtls_02, byp_dtls;
+ u8 mu_st2, vbvolt;
+ u8 cnfg_09;
+ int in_curr = 0;
+ pr_debug("%s\n", __func__);
mutex_lock(&chg_data->ops_lock);
+ /* charger */
max77693_read_reg(chg_data->max77693->i2c,
- MAX77693_CHG_REG_CHG_INT_OK,
- &int_ok);
-
+ MAX77693_CHG_REG_CHG_INT_OK, &int_ok);
max77693_read_reg(chg_data->max77693->i2c,
- MAX77693_CHG_REG_CHG_DTLS_02,
- &dtls_02);
+ MAX77693_CHG_REG_CHG_DTLS_00, &dtls_00);
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_DTLS_01, &dtls_01);
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_DTLS_02, &dtls_02);
+ chgin_dtls = ((dtls_00 & MAX77693_CHGIN_DTLS) >>
+ MAX77693_CHGIN_DTLS_SHIFT);
+ chg_dtls = ((dtls_01 & MAX77693_CHG_DTLS) >>
+ MAX77693_CHG_DTLS_SHIFT);
byp_dtls = ((dtls_02 & MAX77693_BYP_DTLS) >>
MAX77693_BYP_DTLS_SHIFT);
- pr_info("%s: INT_OK(0x%02x), BYP_DTLS(0x%02x)\n",
- __func__, int_ok, byp_dtls);
- if (byp_dtls & MAX77693_BYP_DTLS3) {
- pr_info("%s: unstable power state for %dms\n", __func__,
- SW_REG_START_DELAY);
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_CNFG_09, &cnfg_09);
+ cnfg_09 &= MAX77693_CHG_CHGIN_LIM;
+ in_curr = cnfg_09 * 20;
+
+ /* muic */
+ max77693_read_reg(chg_data->max77693->muic,
+ MAX77693_MUIC_REG_STATUS2, &mu_st2);
+ vbvolt = ((mu_st2 & MAX77693_VBVOLT) >>
+ MAX77693_VBVOLT_SHIFT);
+
+ pr_info("%s: INT_OK(0x%x), CHGIN(0x%x), CHG(0x%x), "
+ "BYP(0x%x), ST2(0x%x), IN_CURR(%d)\n", __func__,
+ int_ok, chgin_dtls, chg_dtls,
+ byp_dtls, mu_st2, in_curr);
+
+ if ((in_curr > SW_REG_CURR_STEP_MA) && (chg_dtls != 0x8) &&
+ ((byp_dtls & MAX77693_BYP_DTLS3) ||
+ ((chgin_dtls != 0x3) && (vbvolt == 0x1)))) {
+ pr_info("%s: unstable power\n", __func__);
+
+ /* set soft regulation progress */
+ chg_data->soft_reg_ing = true;
+
/* enable soft regulation loop */
chg_data->soft_reg_state = true;
- max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, &reg_data);
- reg_data &= MAX77693_CHG_CHGIN_LIM;
- chg_data->soft_reg_current = reg_data * 20;
- chg_data->soft_reg_current -= SW_REG_CURR_STEP_MA;
- chg_data->soft_reg_current = max(chg_data->soft_reg_current, 0);
- pr_info("%s: %dmA to %dmA\n", __func__,
- reg_data * 20, chg_data->soft_reg_current);
+ max77693_reduce_input(chg_data, SW_REG_CURR_STEP_MA);
- reg_data = (chg_data->soft_reg_current / 20);
- pr_debug("%s: reg_data(0x%02x)\n", __func__, reg_data);
- max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, reg_data);
+ /* cancel update wq */
+ cancel_delayed_work(&chg_data->update_work);
+ /* schedule softreg wq */
wake_lock(&chg_data->softreg_wake_lock);
schedule_delayed_work(&chg_data->softreg_work,
msecs_to_jiffies(SW_REG_STEP_DELAY));
} else {
- pr_info("%s: (recover) stable power\n", __func__);
+ /* check cable detached */
+ if ((!in_curr) ||
+ ((chgin_dtls == 0x0) && (vbvolt == 0x0)) ||
+ ((byp_dtls == 0x0) && (chg_dtls == 0x8))) {
+ pr_info("%s: maybe cable is detached\n", __func__);
+
+ cancel_delayed_work(&chg_data->update_work);
+ wake_lock(&chg_data->update_wake_lock);
+ schedule_delayed_work(&chg_data->update_work,
+ msecs_to_jiffies(STABLE_POWER_DELAY));
+ }
+
+ /* for margin */
+ if (chg_data->soft_reg_ing == true) {
+ pr_info("%s: stable power, reduce 1 more step "
+ "for margin\n", __func__);
+ max77693_reduce_input(chg_data, SW_REG_CURR_STEP_MA);
+ chg_data->soft_reg_ing = false;
+ }
+
wake_unlock(&chg_data->softreg_wake_lock);
}
@@ -1096,7 +1347,8 @@ static enum power_supply_property max77693_charger_props[] = {
POWER_SUPPLY_PROP_PRESENT,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_MAX,
- POWER_SUPPLY_PROP_CURRENT_NOW
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CHARGE_FULL
};
static int max77693_charger_get_property(struct power_supply *psy,
@@ -1121,7 +1373,11 @@ static int max77693_charger_get_property(struct power_supply *psy,
val->intval = max77693_get_battery_present(chg_data);
break;
case POWER_SUPPLY_PROP_ONLINE:
+#if defined(EXTENDED_ONLINE_TYPE)
+ val->intval = max77693_get_online_type(chg_data);
+#else
val->intval = max77693_get_cable_type(chg_data);
+#endif
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
val->intval = max77693_get_input_current(chg_data);
@@ -1129,11 +1385,9 @@ static int max77693_charger_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = max77693_get_charge_current(chg_data);
break;
-#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
case POWER_SUPPLY_PROP_CHARGE_FULL:
- val->intval = max77693_is_topoff_state(chg_data);
+ val->intval = max77693_get_topoff_state(chg_data);
break;
-#endif
default:
return -EINVAL;
}
@@ -1154,7 +1408,15 @@ static int max77693_charger_set_property(struct power_supply *psy,
max77693_set_charger_state(chg_data, val->intval);
break;
case POWER_SUPPLY_PROP_ONLINE:
+#if !defined(USE_CHGIN_INTR)
max77693_set_muic_cb_type(chg_data, val->intval);
+#else
+#if defined(EXTENDED_ONLINE_TYPE)
+ max77693_set_online_type(chg_data, val->intval);
+#else
+ return -EINVAL;
+#endif
+#endif
break;
case POWER_SUPPLY_PROP_CURRENT_MAX:
max77693_set_input_current(chg_data, val->intval);
@@ -1172,7 +1434,8 @@ static int max77693_charger_set_property(struct power_supply *psy,
static irqreturn_t max77693_bypass_irq(int irq, void *data)
{
struct max77693_charger_data *chg_data = data;
- u8 int_ok, dtls_02;
+ bool need_reg_init = false;
+ u8 int_ok, dtls_02, cnfg_00;
u8 byp_dtls;
#ifdef CONFIG_USB_HOST_NOTIFY
struct host_notifier_platform_data *host_noti_pdata =
@@ -1182,6 +1445,13 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data)
mutex_lock(&chg_data->irq_lock);
+ /* check and unlock */
+ need_reg_init = max77693_charger_unlock(chg_data);
+ if (need_reg_init) {
+ pr_err("%s: charger locked state, reg init\n", __func__);
+ max77693_charger_reg_init(chg_data);
+ }
+
max77693_read_reg(chg_data->max77693->i2c,
MAX77693_CHG_REG_CHG_INT_OK,
&int_ok);
@@ -1205,9 +1475,16 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data)
host_state_notify(&host_noti_pdata->ndev,
NOTIFY_HOST_OVERCURRENT);
#endif
+ max77693_read_reg(chg_data->max77693->i2c,
+ MAX77693_CHG_REG_CHG_CNFG_00,
+ &cnfg_00);
+ cnfg_00 &= ~(CHG_CNFG_00_OTG_MASK
+ | CHG_CNFG_00_BOOST_MASK
+ | CHG_CNFG_00_DIS_MUIC_CTRL_MASK);
+ cnfg_00 |= CHG_CNFG_00_BUCK_MASK;
max77693_write_reg(chg_data->max77693->i2c,
MAX77693_CHG_REG_CHG_CNFG_00,
- MAX77693_MODE_DEFAULT);
+ cnfg_00);
break;
case 0x8:
pr_err("%s: chgin regulation loop is active\n", __func__);
@@ -1228,7 +1505,8 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data)
cancel_delayed_work(&chg_data->update_work);
wake_lock(&chg_data->update_wake_lock);
- schedule_delayed_work(&chg_data->update_work, msecs_to_jiffies(100));
+ schedule_delayed_work(&chg_data->update_work,
+ msecs_to_jiffies(STABLE_POWER_DELAY));
mutex_unlock(&chg_data->irq_lock);
@@ -1239,13 +1517,22 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data)
static irqreturn_t max77693_charger_irq(int irq, void *data)
{
struct max77693_charger_data *chg_data = data;
- u8 prev_int_ok, int_ok, dtls_00, dtls_01;
- u8 thm_dtls, chgin_dtls, chg_dtls, bat_dtls;
- u8 mu_st2;
+ bool need_reg_init = false;
+ u8 prev_int_ok, int_ok;
+ u8 dtls_00, thm_dtls, chgin_dtls;
+ u8 dtls_01, chg_dtls, bat_dtls;
+ u8 mu_st2, vbvolt;
pr_info("%s: irq(%d)\n", __func__, irq);
mutex_lock(&chg_data->irq_lock);
+ /* check and unlock */
+ need_reg_init = max77693_charger_unlock(chg_data);
+ if (need_reg_init) {
+ pr_err("%s: charger locked state, reg init\n", __func__);
+ max77693_charger_reg_init(chg_data);
+ }
+
max77693_read_reg(chg_data->max77693->i2c,
MAX77693_CHG_REG_CHG_INT_OK,
&prev_int_ok);
@@ -1283,14 +1570,36 @@ static irqreturn_t max77693_charger_irq(int irq, void *data)
/* muic */
max77693_read_reg(chg_data->max77693->muic,
MAX77693_MUIC_REG_STATUS2, &mu_st2);
+ vbvolt = ((mu_st2 & MAX77693_VBVOLT) >>
+ MAX77693_VBVOLT_SHIFT);
pr_info("%s: INT_OK(0x%x), THM(0x%x), CHGIN(0x%x), CHG(0x%x), BAT(0x%x), "
"ST2(0x%x)\n", __func__,
int_ok, thm_dtls, chgin_dtls,
chg_dtls, bat_dtls, mu_st2);
+#if defined(USE_CHGIN_INTR)
+ if (((chgin_dtls == 0x0) || (chgin_dtls == 0x1)) &&
+ (vbvolt == 0x1) && (chg_dtls != 0x8)) {
+ pr_info("%s: abnormal power state: chgin(%d), vb(%d), chg(%d)\n",
+ __func__, chgin_dtls, vbvolt, chg_dtls);
+
+ /* enable soft regulation loop */
+ chg_data->soft_reg_state = true;
+
+ /* first, reduce */
+ max77693_reduce_input(chg_data, SW_REG_CURR_STEP_MA);
+
+ /* software regulation */
+ wake_lock(&chg_data->softreg_wake_lock);
+ schedule_delayed_work(&chg_data->softreg_work,
+ msecs_to_jiffies(SW_REG_STEP_DELAY));
+ }
+#endif
+
cancel_delayed_work(&chg_data->update_work);
wake_lock(&chg_data->update_wake_lock);
- schedule_delayed_work(&chg_data->update_work, msecs_to_jiffies(500));
+ schedule_delayed_work(&chg_data->update_work,
+ msecs_to_jiffies(STABLE_POWER_DELAY));
mutex_unlock(&chg_data->irq_lock);
@@ -1301,11 +1610,19 @@ static irqreturn_t max77693_charger_irq(int irq, void *data)
static irqreturn_t wpc_charger_irq(int irq, void *data)
{
struct max77693_charger_data *chg_data = data;
+ bool need_reg_init = false;
int wc_w_state, wc_v_state, wc_v_pud_state;
pr_info("%s: irq(%d)\n", __func__, irq);
mutex_lock(&chg_data->irq_lock);
+ /* check and unlock */
+ need_reg_init = max77693_charger_unlock(chg_data);
+ if (need_reg_init) {
+ pr_err("%s: charger locked state, reg init\n", __func__);
+ max77693_charger_reg_init(chg_data);
+ }
+
wc_w_state = wc_v_state = 0;
wc_w_state = !gpio_get_value(chg_data->wc_w_gpio);
@@ -1349,7 +1666,7 @@ static irqreturn_t wpc_charger_irq(int irq, void *data)
cancel_delayed_work(&chg_data->update_work);
wake_lock(&chg_data->update_wake_lock);
schedule_delayed_work(&chg_data->update_work,
- msecs_to_jiffies(500));
+ msecs_to_jiffies(STABLE_POWER_DELAY));
}
chg_data->wc_w_state = wc_w_state;
@@ -1412,7 +1729,7 @@ static ssize_t max77693_debugfs_read_registers(struct file *filp,
if (!buf)
return -ENOMEM;
- for (i = 0xB2; i <= 0xC6; i++) {
+ for (i = 0xB0; i <= 0xC6; i++) {
max77693_read_reg(chg_data->max77693->i2c, i, &val);
len += snprintf(buf + len, PAGE_SIZE - len,
"%x=%02x", i, val);
@@ -1442,8 +1759,12 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev)
struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent);
struct max77693_platform_data *pdata = dev_get_platdata(max77693->dev);
int ret;
+ pr_info("%s: charger init\n", __func__);
- pr_info("%s: charger init start\n", __func__);
+ if (!pdata) {
+ pr_err("%s: no platform data\n", __func__);
+ return -ENODEV;
+ }
chg_data = kzalloc(sizeof(struct max77693_charger_data), GFP_KERNEL);
if (!chg_data)
@@ -1460,6 +1781,9 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev)
wake_lock_init(&chg_data->softreg_wake_lock, WAKE_LOCK_SUSPEND,
"charger-softreg");
+ /* unlock charger setting protect */
+ max77693_charger_unlock(chg_data);
+
chg_data->charger_pdata = pdata->charger_data;
if (!pdata->charger_data->init_data)
max77693_charger_reg_init(chg_data);
@@ -1472,6 +1796,9 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev)
chg_data->irq_charge = max77693->irq_base + MAX77693_CHG_IRQ_CHG_I;
chg_data->irq_chargin = max77693->irq_base + MAX77693_CHG_IRQ_CHGIN_I;
+ INIT_DELAYED_WORK(&chg_data->update_work, max77693_update_work);
+ INIT_DELAYED_WORK(&chg_data->softreg_work, max77693_softreg_work);
+
chg_data->charger.name = "max77693-charger",
chg_data->charger.type = POWER_SUPPLY_TYPE_BATTERY,
chg_data->charger.properties = max77693_charger_props,
@@ -1485,12 +1812,6 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev)
goto err_kfree;
}
- INIT_DELAYED_WORK_DEFERRABLE(&chg_data->update_work,
- max77693_update_work);
-
- INIT_DELAYED_WORK_DEFERRABLE(&chg_data->softreg_work,
- max77693_softreg_work);
-
ret = request_threaded_irq(chg_data->irq_bypass, NULL,
max77693_bypass_irq, 0, "bypass-irq", chg_data);
if (ret < 0)
@@ -1509,9 +1830,17 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev)
pr_err("%s: fail to request charge IRQ: %d: %d\n",
__func__, chg_data->irq_charge, ret);
+#if defined(USE_CHGIN_INTR)
+ ret = request_threaded_irq(chg_data->irq_chargin, NULL,
+ max77693_charger_irq, 0, "chargin-irq", chg_data);
+ if (ret < 0)
+ pr_err("%s: fail to request charge IRQ: %d: %d\n",
+ __func__, chg_data->irq_chargin, ret);
+#endif
+
#ifdef CONFIG_BATTERY_WPC_CHARGER
chg_data->wc_pwr_det = chg_data->charger_pdata->wc_pwr_det;
-#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_C1VZW) || \
+#if defined(CONFIG_MACH_M0) || \
defined(CONFIG_MACH_GRANDE) || defined(CONFIG_MACH_IRON)
if (system_rev >= 0xA)
chg_data->wc_pwr_det = true;
@@ -1520,7 +1849,7 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev)
if (system_rev >= 0x6)
chg_data->wc_pwr_det = true;
#endif
-#if defined(CONFIG_MACH_C2)
+#if defined(CONFIG_MACH_M3)
chg_data->wc_pwr_det = true;
#endif
@@ -1619,7 +1948,7 @@ wpc_init_finish:
#endif
#endif
- pr_info("%s: charger init complete\n", __func__);
+ pr_info("%s: probe complete\n", __func__);
return 0;
@@ -1657,11 +1986,14 @@ static int __devexit max77693_charger_remove(struct platform_device *pdev)
*/
#ifdef CONFIG_SLP
static u8 saved_int_mask;
+#endif
static int max77693_charger_suspend(struct device *dev)
{
struct max77693_dev *max77693 = dev_get_drvdata(dev->parent);
u8 int_mask;
+ pr_info("%s\n", __func__);
+#ifdef CONFIG_SLP
/* Save the masking value */
max77693_read_reg(max77693->i2c,
MAX77693_CHG_REG_CHG_INT_MASK,
@@ -1672,31 +2004,56 @@ static int max77693_charger_suspend(struct device *dev)
max77693_write_reg(max77693->i2c,
MAX77693_CHG_REG_CHG_INT_MASK,
int_mask);
+#else
+#if defined(USE_CHGIN_INTR)
+ /* disable chgin irq */
+ max77693_read_reg(max77693->i2c,
+ MAX77693_CHG_REG_CHG_INT_MASK,
+ &int_mask);
+ int_mask |= MAX77693_CHGIN_IM;
+ max77693_write_reg(max77693->i2c,
+ MAX77693_CHG_REG_CHG_INT_MASK,
+ int_mask);
+#endif
+#endif
return 0;
}
static int max77693_charger_resume(struct device *dev)
{
struct max77693_dev *max77693 = dev_get_drvdata(dev->parent);
+ u8 int_mask;
+ pr_info("%s\n", __func__);
+#ifdef CONFIG_SLP
/* Restore the saved masking value */
max77693_write_reg(max77693->i2c,
MAX77693_CHG_REG_CHG_INT_MASK,
saved_int_mask);
+#else
+#if defined(USE_CHGIN_INTR)
+ /* enable chgin irq */
+ max77693_read_reg(max77693->i2c,
+ MAX77693_CHG_REG_CHG_INT_MASK,
+ &int_mask);
+ int_mask &= (~MAX77693_CHGIN_IM);
+ max77693_write_reg(max77693->i2c,
+ MAX77693_CHG_REG_CHG_INT_MASK,
+ int_mask);
+#endif
+#endif
+
return 0;
}
static SIMPLE_DEV_PM_OPS(max77693_charger_pm_ops, max77693_charger_suspend,
max77693_charger_resume);
-#endif
static struct platform_driver max77693_charger_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "max77693-charger",
-#ifdef CONFIG_SLP
.pm = &max77693_charger_pm_ops,
-#endif
},
.probe = max77693_charger_probe,
.remove = __devexit_p(max77693_charger_remove),
diff --git a/drivers/battery/samsung_battery.c b/drivers/battery/samsung_battery.c
index d1cee8f..c63430b 100644
--- a/drivers/battery/samsung_battery.c
+++ b/drivers/battery/samsung_battery.c
@@ -31,9 +31,13 @@
#include <linux/workqueue.h>
#include <linux/proc_fs.h>
#include <linux/android_alarm.h>
+#include <linux/regulator/machine.h>
#include <linux/battery/samsung_battery.h>
#include <mach/regs-pmu.h>
#include "battery-factory.h"
+#ifdef CONFIG_BATTERY_MAX77693_CHARGER
+#include <linux/mfd/max77693-private.h>
+#endif
#if defined(CONFIG_S3C_ADC)
#include <plat/adc.h>
#endif
@@ -45,8 +49,8 @@ static char *supply_list[] = {
"battery",
};
+
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
-static bool battery_terminal_check_support(struct battery_info *info);
static void battery_error_control(struct battery_info *info);
#endif
@@ -69,7 +73,9 @@ static int battery_get_cable(struct battery_info *info)
{
union power_supply_propval value;
int cable_type = 0;
-
+#if defined(EXTENDED_ONLINE_TYPE)
+ int online_val;
+#endif
pr_debug("%s\n", __func__);
mutex_lock(&info->ops_lock);
@@ -78,7 +84,25 @@ static int battery_get_cable(struct battery_info *info)
case CABLE_DET_CHARGER:
info->psy_charger->get_property(info->psy_charger,
POWER_SUPPLY_PROP_ONLINE, &value);
+
+#if defined(EXTENDED_ONLINE_TYPE)
+ /* | 31-24: RSVD | 23-16: MAIN TYPE |
+ 15-8: SUB TYPE | 7-0: POWER TYPE | */
+ online_val = value.intval;
+ online_val &= ~(ONLINE_TYPE_RSVD_MASK);
+ cable_type = ((online_val & ONLINE_TYPE_MAIN_MASK) >>
+ ONLINE_TYPE_MAIN_SHIFT);
+ info->cable_sub_type = ((online_val & ONLINE_TYPE_SUB_MASK) >>
+ ONLINE_TYPE_SUB_SHIFT);
+ info->cable_pwr_type = ((online_val & ONLINE_TYPE_PWR_MASK) >>
+ ONLINE_TYPE_PWR_SHIFT);
+ pr_info("%s: main(%d), sub(%d), pwr(%d)\n", __func__,
+ cable_type,
+ info->cable_sub_type,
+ info->cable_pwr_type);
+#else
cable_type = value.intval;
+#endif
break;
default:
pr_err("%s: not support src(%d)\n", __func__,
@@ -172,6 +196,117 @@ static int battery_get_temper(struct battery_info *info)
return temper;
}
+#define ADC_REG_NAME "vcc_adc_1.8v"
+static int battery_set_adc_power(struct battery_info *info, bool en)
+{
+ struct regulator *regulator;
+ int is_en;
+ int ret = 0;
+ pr_debug("%s\n", __func__);
+
+ regulator = regulator_get(NULL, ADC_REG_NAME);
+ if (IS_ERR(regulator))
+ return -ENODEV;
+
+ is_en = regulator_is_enabled(regulator);
+
+ if (is_en != en)
+ pr_info("%s: %s: is_en(%d), en(%d)\n", __func__,
+ ADC_REG_NAME, is_en, en);
+
+ if (!is_en && en)
+ ret = regulator_enable(regulator);
+ else if (is_en && !en)
+ ret = regulator_force_disable(regulator);
+
+ info->adc_pwr_st = en;
+
+ regulator_put(regulator);
+
+ return ret;
+}
+
+static int battery_get_vf(struct battery_info *info)
+{
+ union power_supply_propval value;
+ int present = 0;
+ int adc;
+ pr_debug("%s\n", __func__);
+
+ if (info->factory_mode) {
+ pr_debug("%s: No need to check battery in factory mode\n",
+ __func__);
+ return 1;
+ }
+
+ mutex_lock(&info->ops_lock);
+
+ switch (info->pdata->vf_det_src) {
+ case VF_DET_ADC:
+#if defined(CONFIG_S3C_ADC)
+ if (info->pdata->vf_det_src == VF_DET_ADC)
+ battery_set_adc_power(info, 1);
+ adc = s3c_adc_read(info->adc_client, info->pdata->vf_det_ch);
+ if (info->pdata->vf_det_src == VF_DET_ADC)
+ battery_set_adc_power(info, 0);
+#else
+ adc = 350; /* temporary value */
+#endif
+ info->battery_vf_adc = adc;
+ present = INRANGE(adc, info->pdata->vf_det_th_l,
+ info->pdata->vf_det_th_h);
+ if (!present)
+ pr_info("%s: adc(%d), out of range(%d ~ %d)\n",
+ __func__, adc,
+ info->pdata->vf_det_th_l,
+ info->pdata->vf_det_th_h);
+ break;
+ case VF_DET_CHARGER:
+ info->psy_charger->get_property(info->psy_charger,
+ POWER_SUPPLY_PROP_PRESENT, &value);
+ present = value.intval;
+ break;
+ case VF_DET_GPIO:
+ present = !gpio_get_value(info->batdet_gpio);
+ break;
+ default:
+ pr_err("%s: not support src(%d)\n", __func__,
+ info->pdata->vf_det_src);
+ present = 1; /* always detected */
+ break;
+ }
+
+ pr_debug("%s: present(%d)\n", __func__, present);
+
+ mutex_unlock(&info->ops_lock);
+ return present;
+}
+
+/* judge power off or not by current_avg */
+static int battery_get_curr_avg(struct battery_info *info)
+{
+ int curr_avg;
+ pr_debug("%s\n", __func__);
+
+ /* if 0% && under min voltage && low power charging, power off */
+ if ((info->battery_soc <= PWROFF_SOC) &&
+ (info->battery_vcell < info->pdata->voltage_min) &&
+ (info->battery_v_diff < 0) &&
+ (info->input_current < info->pdata->chg_curr_ta)) {
+ pr_info("%s: soc(%d), vol(%d < %d), diff(%d), in_curr(%d)\n",
+ __func__, info->battery_soc,
+ (info->battery_vcell / 1000),
+ (info->pdata->voltage_min / 1000),
+ info->battery_v_diff,
+ info->input_current);
+ curr_avg = -1;
+ } else {
+ curr_avg = info->input_current;
+ }
+
+ return curr_avg;
+}
+
/* Get info from power supply at realtime */
int battery_get_info(struct battery_info *info,
enum power_supply_property property)
@@ -194,15 +329,15 @@ int battery_get_info(struct battery_info *info,
case POWER_SUPPLY_PROP_STATUS:
case POWER_SUPPLY_PROP_CHARGE_TYPE:
case POWER_SUPPLY_PROP_HEALTH:
- case POWER_SUPPLY_PROP_PRESENT:
case POWER_SUPPLY_PROP_CURRENT_MAX:
case POWER_SUPPLY_PROP_CURRENT_NOW:
-#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
case POWER_SUPPLY_PROP_CHARGE_FULL:
-#endif
info->psy_charger->get_property(info->psy_charger,
property, &value);
break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ value.intval = battery_get_vf(info);
+ break;
case POWER_SUPPLY_PROP_ONLINE:
value.intval = battery_get_cable(info);
break;
@@ -212,6 +347,10 @@ int battery_get_info(struct battery_info *info,
info->psy_fuelgauge->get_property(info->psy_fuelgauge,
property, &value);
break;
+ /* Update current_avg */
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ value.intval = battery_get_curr_avg(info);
+ break;
/* Update from fuelgauge or adc */
case POWER_SUPPLY_PROP_TEMP:
value.intval = battery_get_temper(info);
@@ -230,7 +369,10 @@ void battery_update_info(struct battery_info *info)
int temper;
/* Update from Charger */
- info->cable_type = battery_get_cable(info);
+ if (info->slate_mode)
+ info->cable_type = POWER_SUPPLY_TYPE_BATTERY;
+ else
+ info->cable_type = battery_get_cable(info);
info->psy_charger->get_property(info->psy_charger,
POWER_SUPPLY_PROP_STATUS, &value);
@@ -254,9 +396,7 @@ void battery_update_info(struct battery_info *info)
info->battery_health = value.intval;
#endif
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_PRESENT, &value);
- info->battery_present = value.intval;
+ info->battery_present = battery_get_vf(info);
info->psy_charger->get_property(info->psy_charger,
POWER_SUPPLY_PROP_CURRENT_NOW, &value);
@@ -275,7 +415,7 @@ void battery_update_info(struct battery_info *info)
/* Fuelgauge power off state */
if ((info->cable_type != POWER_SUPPLY_TYPE_BATTERY) &&
- (info->battery_present == 0)) {
+ (info->battery_present == 0) && (info->monitor_count)) {
pr_info("%s: abnormal fuelgauge power state\n", __func__);
goto update_finish;
}
@@ -321,10 +461,16 @@ void battery_update_info(struct battery_info *info)
info->battery_t_delta = temper - info->battery_temper;
info->battery_temper = temper;
+ /* update current_avg later */
+ info->charge_current_avg = battery_get_curr_avg(info);
+
update_finish:
switch (info->battery_error_test) {
case 0:
pr_debug("%s: error test: not test modde\n", __func__);
+#if defined(CONFIG_TARGET_LOCALE_KOR)
+ info->errortest_stopcharging = false;
+#endif
break;
case 1:
pr_info("%s: error test: full charged\n", __func__);
@@ -346,12 +492,17 @@ update_finish:
pr_info("%s: error test: vf error\n", __func__);
info->battery_present = 0;
break;
+#if defined(CONFIG_TARGET_LOCALE_KOR)
+ case 6:
+ info->errortest_stopcharging = true;
+ break;
+#endif
default:
pr_info("%s: error test: unknown state\n", __func__);
break;
}
- pr_info("%s: state(%d), type(%d), "
+ pr_debug("%s: state(%d), type(%d), "
"health(%d), present(%d), "
"cable(%d), curr(%d), "
"soc(%d), raw(%d), "
@@ -396,22 +547,99 @@ void battery_control_info(struct battery_info *info,
}
}
-static void samsung_battery_alarm_start(struct alarm *alarm)
+static void battery_event_alarm(struct alarm *alarm)
{
struct battery_info *info = container_of(alarm, struct battery_info,
- alarm);
- pr_debug("%s\n", __func__);
+ event_alarm);
+ pr_info("%s: exit event state, %ds gone\n", __func__,
+ info->pdata->event_time);
+
+ /* clear event state */
+ info->event_state = EVENT_STATE_CLEAR;
wake_lock(&info->monitor_wake_lock);
schedule_work(&info->monitor_work);
}
+void battery_event_control(struct battery_info *info)
+{
+ int event_num;
+ ktime_t interval, next, slack;
+ /* sync with event_type in samsung_battery.h */
+ char *event_type_name[] = { "WCDMA CALL", "GSM CALL", "CALL",
+ "VIDEO", "MUSIC", "BROWSER",
+ "HOTSPOT", "CAMERA", "DATA CALL",
+ "GPS", "LTE", "WIFI",
+ "USE", "UNKNOWN"
+ };
+
+ pr_debug("%s\n", __func__);
+
+ if (info->event_type) {
+ pr_info("%s: in event state(%d), type(0x%04x)\n", __func__,
+ info->event_state, info->event_type);
+
+ for (event_num = 0; event_num < EVENT_TYPE_MAX; event_num++) {
+ if (info->event_type & (1 << event_num))
+ pr_info("%s: %d: %s\n", __func__, event_num,
+ event_type_name[event_num]);
+ }
+
+ if (info->event_state == EVENT_STATE_SET) {
+ pr_info("%s: event already set, event(%d, 0x%04x)\n",
+ __func__, info->event_state, info->event_type);
+ } else if (info->event_state == EVENT_STATE_IN_TIMER) {
+ pr_info("%s: cancel event timer\n", __func__);
+
+ alarm_cancel(&info->event_alarm);
+
+ info->event_state = EVENT_STATE_SET;
+
+ wake_lock(&info->monitor_wake_lock);
+ schedule_work(&info->monitor_work);
+ } else {
+ pr_info("%s: enter event state(%d, 0x%04x)\n",
+ __func__, info->event_state, info->event_type);
+
+ info->event_state = EVENT_STATE_SET;
+
+ wake_lock(&info->monitor_wake_lock);
+ schedule_work(&info->monitor_work);
+ }
+ } else {
+ pr_info("%s: clear event type(0x%04x), wait %ds\n", __func__,
+ info->event_type, info->pdata->event_time);
+
+ if (info->event_state == EVENT_STATE_SET) {
+ pr_info("%s: start event timer\n", __func__);
+ info->last_poll = alarm_get_elapsed_realtime();
+
+ interval = ktime_set(info->pdata->event_time, 0);
+ next = ktime_add(info->last_poll, interval);
+ slack = ktime_set(20, 0);
+
+ alarm_start_range(&info->event_alarm, next,
+ ktime_add(next, slack));
+
+ info->event_state = EVENT_STATE_IN_TIMER;
+ } else {
+ pr_info("%s: event already clear, event(%d, 0x%04x)\n",
+ __func__, info->event_state, info->event_type);
+ }
+ }
+}
+
static void battery_notify_full_state(struct battery_info *info)
{
union power_supply_propval value;
+ pr_debug("%s: r(%d), f(%d), rs(%d), fs(%d), s(%d)\n", __func__,
+ info->recharge_phase, info->full_charged_state,
+ info->battery_raw_soc, info->battery_full_soc,
+ info->battery_soc);
if ((info->recharge_phase && info->full_charged_state) ||
- ((info->battery_raw_soc > info->battery_full_soc) &&
+ ((info->charge_real_state != POWER_SUPPLY_STATUS_DISCHARGING) &&
+ (info->battery_raw_soc > info->battery_full_soc) &&
(info->battery_soc == 100))) {
/* notify full state to fuel guage */
value.intval = POWER_SUPPLY_STATUS_FULL;
@@ -420,6 +648,16 @@ static void battery_notify_full_state(struct battery_info *info)
}
}
+static void battery_monitor_alarm(struct alarm *alarm)
+{
+ struct battery_info *info = container_of(alarm, struct battery_info,
+ monitor_alarm);
+ pr_debug("%s\n", __func__);
+
+ wake_lock(&info->monitor_wake_lock);
+ schedule_work(&info->monitor_work);
+}
+
static void battery_monitor_interval(struct battery_info *info)
{
ktime_t interval, next, slack;
@@ -454,6 +692,10 @@ static void battery_monitor_interval(struct battery_info *info)
break;
}
+ /* 5 times after boot, apply no interval (1 sec) */
+ if (info->monitor_count < 5)
+ info->monitor_interval = 1;
+
/* apply monitor interval weight */
if (info->monitor_weight != 100) {
pr_info("%s: apply weight(%d), %d -> %d\n", __func__,
@@ -470,7 +712,7 @@ static void battery_monitor_interval(struct battery_info *info)
next = ktime_add(info->last_poll, interval);
slack = ktime_set(20, 0);
- alarm_start_range(&info->alarm, next, ktime_add(next, slack));
+ alarm_start_range(&info->monitor_alarm, next, ktime_add(next, slack));
local_irq_restore(flags);
}
@@ -503,8 +745,12 @@ static bool battery_abstimer_cond(struct battery_info *info)
struct timespec current_time;
pr_debug("%s\n", __func__);
+ /* always update time for info data */
+ ktime = alarm_get_elapsed_realtime();
+ info->current_time = current_time = ktime_to_timespec(ktime);
+
if ((info->cable_type == POWER_SUPPLY_TYPE_USB) ||
- (info->full_charged_state == true) ||
+ (info->full_charged_state != STATUS_NOT_FULL) ||
(info->charge_start_time == 0)) {
pr_debug("%s: not abstimer state, cb(%d), f(%d), t(%d)\n",
__func__, info->cable_type,
@@ -514,9 +760,6 @@ static bool battery_abstimer_cond(struct battery_info *info)
return false;
}
- ktime = alarm_get_elapsed_realtime();
- current_time = ktime_to_timespec(ktime);
-
if (info->recharge_phase) {
abstimer_duration = info->pdata->abstimer_recharge_duration;
} else {
@@ -534,6 +777,7 @@ static bool battery_abstimer_cond(struct battery_info *info)
(int)current_time.tv_sec, info->charge_start_time,
abstimer_duration);
info->abstimer_state = true;
+ info->abstimer_active = (int)current_time.tv_sec;
} else {
pr_debug("%s: not abstimer state, t(%d - %d ?? %d)\n", __func__,
(int)current_time.tv_sec, info->charge_start_time,
@@ -548,10 +792,11 @@ static bool battery_fullcharged_cond(struct battery_info *info)
{
int f_cond_soc;
int f_cond_vcell;
+ int full_state;
pr_debug("%s\n", __func__);
- /* max voltage - 50mV */
- f_cond_vcell = info->pdata->voltage_max - 50000;
+ /* max voltage - RECHG_DROP_VALUE: recharge voltage */
+ f_cond_vcell = info->pdata->voltage_max - RECHG_DROP_VALUE;
/* max soc - 5% */
f_cond_soc = 95;
@@ -566,10 +811,32 @@ static bool battery_fullcharged_cond(struct battery_info *info)
pr_info("%s: real full charged, v(%d), s(%d)\n",
__func__, info->battery_vcell,
info->battery_soc);
- info->full_charged_state = true;
+#if defined(USE_2STEP_TERM)
+ full_state = battery_get_info(info,
+ POWER_SUPPLY_PROP_CHARGE_FULL);
+ if (!full_state) {
+ if (info->full_charged_state != STATUS_1ST_FULL)
+ pr_info("%s: 1st full by current\n",
+ __func__);
+
+ info->full_charged_state = STATUS_1ST_FULL;
+
+ return false;
+ } else {
+ if (info->full_charged_state != STATUS_2ND_FULL)
+ pr_info("%s: 2nd full by timer\n",
+ __func__);
+
+ info->full_charged_state = STATUS_2ND_FULL;
+
+ return true;
+ }
+#else
+ info->full_charged_state = STATUS_1ST_FULL;
return true;
+#endif
} else {
- pr_info("%s: charger full charged, v(%d), s(%d)\n",
+ pr_info("%s: not real full charged, v(%d), s(%d)\n",
__func__, info->battery_vcell,
info->battery_soc);
@@ -587,13 +854,13 @@ static bool battery_fullcharged_cond(struct battery_info *info)
POWER_SUPPLY_PROP_STATUS);
return false;
}
- } else if (info->full_charged_state == true) {
+ } else if (info->full_charged_state != STATUS_NOT_FULL) {
pr_debug("%s: already full charged, v(%d), s(%d)\n", __func__,
info->battery_vcell, info->battery_soc);
} else {
pr_debug("%s: not full charged, v(%d), s(%d)\n", __func__,
info->battery_vcell, info->battery_soc);
- info->full_charged_state = false;
+ info->full_charged_state = STATUS_NOT_FULL;
}
return false;
@@ -645,7 +912,6 @@ static bool battery_health_cond(struct battery_info *info)
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
if (info->cable_type == POWER_SUPPLY_TYPE_BATTERY) {
info->health_state = false;
- info->is_unspec_phase = false;
return false;
}
#endif
@@ -681,10 +947,6 @@ static bool battery_health_cond(struct battery_info *info)
info->battery_health);
info->health_state = false;
-#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
- if (battery_terminal_check_support(info))
- pr_err("%s: health support, error state!\n", __func__);
-#endif
}
return info->health_state;
@@ -692,8 +954,41 @@ static bool battery_health_cond(struct battery_info *info)
static bool battery_temper_cond(struct battery_info *info)
{
+ int ovh_stop, ovh_recover;
+ int frz_stop, frz_recover;
pr_debug("%s\n", __func__);
+ /* update overheat temperature threshold */
+ if ((info->pdata->ctia_spec == true) && (info->lpm_state)) {
+ ovh_stop = info->pdata->lpm_overheat_stop_temp;
+ ovh_recover = info->pdata->lpm_overheat_recovery_temp;
+ frz_stop = info->pdata->lpm_freeze_stop_temp;
+ frz_recover = info->pdata->lpm_freeze_recovery_temp;
+ } else if ((info->pdata->ctia_spec == true) &&
+ (info->event_state != EVENT_STATE_CLEAR)) {
+ ovh_stop = info->pdata->event_overheat_stop_temp;
+ ovh_recover = info->pdata->event_overheat_recovery_temp;
+ frz_stop = info->pdata->event_freeze_stop_temp;
+ frz_recover = info->pdata->event_freeze_recovery_temp;
+ pr_info("%s: ovh(%d/%d), frz(%d/%d), lpm(%d), "
+ "event(%d, 0x%04x)\n", __func__,
+ ovh_stop, ovh_recover, frz_stop, frz_recover,
+ info->lpm_state, info->event_state, info->event_type);
+ } else {
+ ovh_stop = info->pdata->overheat_stop_temp;
+ ovh_recover = info->pdata->overheat_recovery_temp;
+ frz_stop = info->pdata->freeze_stop_temp;
+ frz_recover = info->pdata->freeze_recovery_temp;
+ }
+
+#if defined(CONFIG_MACH_T0_USA_SPR)
+ /* unver rev0.7, do not stop charging by tempereture */
+ if (system_rev < 7) {
+ ovh_stop = info->battery_temper + 1;
+ frz_stop = info->battery_temper - 1;
+ }
+#endif
+
if (info->temper_state == false) {
if (info->charge_real_state != POWER_SUPPLY_STATUS_CHARGING) {
pr_debug("%s: r_state !charging, cs(%d)\n",
@@ -703,21 +998,15 @@ static bool battery_temper_cond(struct battery_info *info)
pr_debug("%s: check charging stop temper "
"cond: %d ?? %d ~ %d\n", __func__,
- info->battery_temper,
- info->pdata->freeze_stop_temp,
- info->pdata->overheat_stop_temp);
+ info->battery_temper, frz_stop, ovh_stop);
- if (info->battery_temper >=
- info->pdata->overheat_stop_temp) {
+ if (info->battery_temper >= ovh_stop) {
pr_info("%s: stop by overheated, t(%d ? %d)\n",
- __func__, info->battery_temper,
- info->pdata->overheat_stop_temp);
+ __func__, info->battery_temper, ovh_stop);
info->overheated_state = true;
- } else if (info->battery_temper <=
- info->pdata->freeze_stop_temp) {
- pr_info("%s: stop by overheated, t(%d ? %d)\n",
- __func__, info->battery_temper,
- info->pdata->freeze_stop_temp);
+ } else if (info->battery_temper <= frz_stop) {
+ pr_info("%s: stop by freezed, t(%d ? %d)\n",
+ __func__, info->battery_temper, frz_stop);
info->freezed_state = true;
} else
pr_debug("%s: normal charging, t(%d)\n", __func__,
@@ -725,23 +1014,17 @@ static bool battery_temper_cond(struct battery_info *info)
} else {
pr_debug("%s: check charging recovery temper "
"cond: %d ?? %d ~ %d\n", __func__,
- info->battery_temper,
- info->pdata->freeze_recovery_temp,
- info->pdata->overheat_recovery_temp);
+ info->battery_temper, frz_recover, ovh_recover);
if ((info->overheated_state == true) &&
- (info->battery_temper <=
- info->pdata->overheat_recovery_temp)) {
+ (info->battery_temper <= ovh_recover)) {
pr_info("%s: recovery from overheated, t(%d ? %d)\n",
- __func__, info->battery_temper,
- info->pdata->overheat_recovery_temp);
+ __func__, info->battery_temper, ovh_recover);
info->overheated_state = false;
} else if ((info->freezed_state == true) &&
- (info->battery_temper >=
- info->pdata->freeze_recovery_temp)) {
+ (info->battery_temper >= frz_recover)) {
pr_info("%s: recovery from freezed, t(%d ? %d)\n",
- __func__, info->battery_temper,
- info->pdata->freeze_recovery_temp);
+ __func__, info->battery_temper, frz_recover);
info->freezed_state = false;
} else
pr_info("%s: charge stopped, t(%d)\n", __func__,
@@ -781,17 +1064,38 @@ static void battery_charge_control(struct battery_info *info,
ktime = alarm_get_elapsed_realtime();
current_time = ktime_to_timespec(ktime);
- if ((chg_curr != 0) && (info->siop_state == true)) {
- pr_info("%s: siop state, charge current is %dmA\n", __func__,
- info->siop_charge_current);
- chg_curr = info->siop_charge_current;
+ if ((info->cable_type != POWER_SUPPLY_TYPE_BATTERY) &&
+ (chg_curr > 0) && (info->siop_state == true)) {
+
+ switch (info->siop_lv) {
+ case SIOP_LV1:
+ info->siop_charge_current =
+ info->pdata->chg_curr_siop_lv1;
+ break;
+ case SIOP_LV2:
+ info->siop_charge_current =
+ info->pdata->chg_curr_siop_lv2;
+ break;
+ case SIOP_LV3:
+ info->siop_charge_current =
+ info->pdata->chg_curr_siop_lv3;
+ break;
+ default:
+ info->siop_charge_current =
+ info->pdata->chg_curr_usb;
+ break;
+ }
+
+ chg_curr = MIN(chg_curr, info->siop_charge_current);
+ pr_info("%s: siop state, level(%d), cc(%d)\n",
+ __func__, info->siop_lv, chg_curr);
}
if (in_curr == KEEP_CURR)
goto charge_current_con;
/* input current limit */
- in_curr = min(in_curr, info->pdata->in_curr_limit);
+ in_curr = MIN(in_curr, info->pdata->in_curr_limit);
/* check charge input before and after */
if (info->input_current == ((in_curr / 20) * 20)) {
@@ -843,7 +1147,7 @@ charge_state_con:
battery_control_info(info, POWER_SUPPLY_PROP_STATUS, ENABLE);
info->charge_start_time = current_time.tv_sec;
- pr_err("%s: charge enabled, current as %d/%dmA @%d\n",
+ pr_info("%s: charge enabled, current as %d/%dmA @%d\n",
__func__, info->charge_current, info->input_current,
info->charge_start_time);
@@ -863,7 +1167,7 @@ charge_state_con:
} else if ((chg_curr == 0) && (info->charge_start_time != 0)) {
battery_control_info(info, POWER_SUPPLY_PROP_STATUS, DISABLE);
- pr_err("%s: charge disabled, current as %d/%dmA @%d\n",
+ pr_info("%s: charge disabled, current as %d/%dmA @%d\n",
__func__, info->charge_current, info->input_current,
(int)current_time.tv_sec);
@@ -905,13 +1209,17 @@ charge_state_con:
static void battery_indicator_icon(struct battery_info *info)
{
if (info->cable_type != POWER_SUPPLY_TYPE_BATTERY) {
- if (info->full_charged_state == true) {
+ if (info->full_charged_state != STATUS_NOT_FULL) {
info->charge_virt_state =
POWER_SUPPLY_STATUS_FULL;
info->battery_soc = 100;
- } else if (info->abstimer_state == true) {
- info->charge_virt_state =
- POWER_SUPPLY_STATUS_CHARGING;
+ } else if (info->abstimer_active) {
+ if (info->battery_soc == 100)
+ info->charge_virt_state =
+ POWER_SUPPLY_STATUS_FULL;
+ else
+ info->charge_virt_state =
+ POWER_SUPPLY_STATUS_CHARGING;
} else if (info->recharge_phase == true) {
info->charge_virt_state =
POWER_SUPPLY_STATUS_CHARGING;
@@ -1005,7 +1313,8 @@ static void battery_interval_calulation(struct battery_info *info)
}
/* prevent critical low voltage factor */
- if ((info->battery_vcell < (info->pdata->voltage_min - 100000)) ||
+ if ((info->battery_vcell <
+ (info->pdata->voltage_min - PWROFF_MARGIN)) ||
(info->battery_vfocv < info->pdata->voltage_min)) {
pr_info("%s: voltage(%d) too low state\n", __func__,
info->battery_vcell);
@@ -1053,16 +1362,16 @@ static void battery_interval_calulation(struct battery_info *info)
if (info->lpm_state == true)
info->monitor_weight *= 2;
- /* 2 times after boot(about 1min), apply charging interval(30sec) */
- if (info->monitor_count < 2) {
- pr_info("%s: now in booting, set 30s\n", __func__);
- info->monitor_mode = MONITOR_EMER_LV1;
+ /* 5 times after boot, apply no interval (1 sec) */
+ if (info->monitor_count < 5) {
+ pr_info("%s: now in booting, set 1s\n", __func__);
+ info->monitor_mode = MONITOR_EMER_LV1; /* dummy value */
return;
}
/*
* prevent low voltage phase
- * default, vcell is lower than min_voltage + 50mV, -20%
+ * default, vcell is lower than min_voltage + 50mV, -30%
*/
if (info->battery_vcell < (info->pdata->voltage_min + 50000)) {
info->monitor_mode = MONITOR_EMER_LV1;
@@ -1147,10 +1456,16 @@ static void battery_monitor_work(struct work_struct *work)
{
struct battery_info *info = container_of(work, struct battery_info,
monitor_work);
+ int muic_cb_typ;
pr_debug("%s\n", __func__);
mutex_lock(&info->mon_lock);
+ if (info->battery_test_mode) {
+ pr_info("%s: now in test mode, not updated\n", __func__);
+ goto monitor_finish;
+ }
+
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
/* first, check cable-type */
info->cable_type = battery_get_cable(info);
@@ -1163,8 +1478,9 @@ static void battery_monitor_work(struct work_struct *work)
info->overheated_state = false;
info->freezed_state = false;
info->temper_state = false;
- info->full_charged_state = false;
+ info->full_charged_state = STATUS_NOT_FULL;
info->abstimer_state = false;
+ info->abstimer_active = false;
info->recharge_phase = false;
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
@@ -1184,28 +1500,25 @@ static void battery_monitor_work(struct work_struct *work)
/* Check battery state from charger and fuelgauge */
battery_update_info(info);
-#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
- /* check unspec recovery */
- if (info->is_unspec_phase) {
- if ((info->battery_health !=
- POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) &&
- (battery_terminal_check_support(info) == false)) {
- pr_info("%s: recover from unspec phase!\n", __func__);
- info->is_unspec_recovery = true;
+ /* adc ldo , vf irq control */
+ if (info->pdata->vf_det_src == VF_DET_GPIO) {
+ if (info->cable_type == POWER_SUPPLY_TYPE_BATTERY) {
+ if (info->batdet_irq_st) {
+ disable_irq(info->batdet_irq);
+ info->batdet_irq_st = false;
+ }
+ if (info->adc_pwr_st)
+ battery_set_adc_power(info, 0);
+ } else {
+ if (!info->adc_pwr_st)
+ battery_set_adc_power(info, 1);
+ if (!info->batdet_irq_st) {
+ enable_irq(info->batdet_irq);
+ info->batdet_irq_st = true;
+ }
}
}
- /* If it's recovery phase from unspec state, go to charge_ok */
- if (info->is_unspec_recovery) {
- pr_info("%s: recovered from unspec phase"
- ": re-setting charge current!\n", __func__);
- battery_charge_control(info, OFF_CURR, OFF_CURR);
- info->is_unspec_recovery = false;
- info->is_unspec_phase = false;
- goto charge_ok;
- }
-#endif
-
/* if battery is missed state, do not check charge scenario */
if (info->battery_present == 0)
goto monitor_finish;
@@ -1215,6 +1528,14 @@ static void battery_monitor_work(struct work_struct *work)
goto charge_ok;
/* Below is charger is connected state */
+#if defined(CONFIG_TARGET_LOCALE_KOR)
+ if (info->errortest_stopcharging) {
+ pr_info("%s: charge stopped by error_test mode\n", __func__);
+ battery_charge_control(info, OFF_CURR, OFF_CURR);
+ goto monitor_finish;
+ }
+#endif
+
if (battery_temper_cond(info) == true) {
pr_info("%s: charge stopped by temperature\n", __func__);
battery_charge_control(info, OFF_CURR, OFF_CURR);
@@ -1246,7 +1567,9 @@ static void battery_monitor_work(struct work_struct *work)
}
charge_ok:
+#if defined(CONFIG_MACH_GC1)
pr_err("%s: Updated Cable State(%d)\n", __func__, info->cable_type);
+#endif
switch (info->cable_type) {
case POWER_SUPPLY_TYPE_BATTERY:
if (!info->pdata->suspend_chging)
@@ -1257,15 +1580,16 @@ charge_ok:
info->overheated_state = false;
info->freezed_state = false;
info->temper_state = false;
- info->full_charged_state = false;
+ info->full_charged_state = STATUS_NOT_FULL;
info->abstimer_state = false;
+ info->abstimer_active = false;
info->recharge_phase = false;
break;
case POWER_SUPPLY_TYPE_MAINS:
if (!info->pdata->suspend_chging)
wake_lock(&info->charge_wake_lock);
battery_charge_control(info, info->pdata->chg_curr_ta,
- info->pdata->chg_curr_ta);
+ info->pdata->in_curr_limit);
break;
case POWER_SUPPLY_TYPE_USB:
if (!info->pdata->suspend_chging)
@@ -1282,8 +1606,45 @@ charge_ok:
case POWER_SUPPLY_TYPE_DOCK:
if (!info->pdata->suspend_chging)
wake_lock(&info->charge_wake_lock);
- battery_charge_control(info, info->pdata->chg_curr_dock,
- info->pdata->chg_curr_dock);
+ muic_cb_typ = max77693_muic_get_charging_type();
+ switch (muic_cb_typ) {
+ case CABLE_TYPE_AUDIODOCK_MUIC:
+ pr_info("%s: audio dock, %d\n",
+ __func__, DOCK_TYPE_AUDIO_CURR);
+ battery_charge_control(info,
+ DOCK_TYPE_AUDIO_CURR,
+ DOCK_TYPE_AUDIO_CURR);
+ break;
+ case CABLE_TYPE_SMARTDOCK_TA_MUIC:
+ if (info->cable_sub_type == ONLINE_SUB_TYPE_SMART_OTG) {
+ pr_info("%s: smart dock ta & host, %d\n",
+ __func__, DOCK_TYPE_SMART_OTG_CURR);
+ battery_charge_control(info,
+ DOCK_TYPE_SMART_OTG_CURR,
+ DOCK_TYPE_SMART_OTG_CURR);
+ } else {
+ pr_info("%s: smart dock ta & no host, %d\n",
+ __func__, DOCK_TYPE_SMART_NOTG_CURR);
+ battery_charge_control(info,
+ DOCK_TYPE_SMART_NOTG_CURR,
+ DOCK_TYPE_SMART_NOTG_CURR);
+ }
+ break;
+ case CABLE_TYPE_SMARTDOCK_USB_MUIC:
+ pr_info("%s: smart dock usb(low), %d\n",
+ __func__, DOCK_TYPE_LOW_CURR);
+ battery_charge_control(info,
+ DOCK_TYPE_LOW_CURR,
+ DOCK_TYPE_LOW_CURR);
+ break;
+ default:
+ pr_info("%s: general dock, %d\n",
+ __func__, info->pdata->chg_curr_dock);
+ battery_charge_control(info,
+ info->pdata->chg_curr_dock,
+ info->pdata->chg_curr_dock);
+ break;
+ }
break;
case POWER_SUPPLY_TYPE_WIRELESS:
if (!info->pdata->suspend_chging)
@@ -1299,6 +1660,7 @@ monitor_finish:
/* icon indicator */
battery_indicator_icon(info);
+ /* nofify full state to fuelgauge */
battery_notify_full_state(info);
/* dynamic battery polling interval */
@@ -1311,29 +1673,48 @@ monitor_finish:
if (info->pdata->led_indicator == true)
battery_indicator_led(info);
- pr_info("[%d] bat: s(%d, %d), v(%d, %d), b(%d), "
- "t(%d.%d), h(%d), "
- "cs(%d, %d), cb(%d), cr(%d, %d), "
- "a(%d), f(%d), r(%d), t(%d)\n",
+ power_supply_changed(&info->psy_bat);
+
+ pr_info("[%d] bat: s(%d, %d), v(%d, %d), "
+ "t(%d.%d), "
+ "cs(%d, %d), cb(%d), cr(%d, %d)",
++info->monitor_count,
info->battery_soc,
info->battery_r_s_delta,
info->battery_vcell / 1000,
info->battery_v_diff / 1000,
- info->battery_present,
info->battery_temper / 10, info->battery_temper % 10,
- info->battery_health,
info->charge_real_state,
info->charge_virt_state,
info->cable_type,
info->charge_current,
- info->input_current,
- info->abstimer_state,
- info->full_charged_state,
- info->recharge_phase,
- info->charge_start_time);
+ info->input_current);
- power_supply_changed(&info->psy_bat);
+ if (info->battery_present == 0)
+ pr_cont(", b(%d)", info->battery_present);
+ if (info->battery_health != POWER_SUPPLY_HEALTH_GOOD)
+ pr_cont(", h(%d)", info->battery_health);
+ if (info->abstimer_state == 1)
+ pr_cont(", a(%d)", info->abstimer_state);
+ if (info->abstimer_active)
+ pr_cont(", aa(%d)", info->abstimer_active);
+ if (info->full_charged_state != STATUS_NOT_FULL)
+ pr_cont(", f(%d)", info->full_charged_state);
+ if (info->recharge_phase == 1)
+ pr_cont(", r(%d)", info->recharge_phase);
+ if (info->charge_start_time != 0)
+ pr_cont(", t(%d)", ((int)info->current_time.tv_sec -
+ info->charge_start_time));
+ if (info->event_state != EVENT_STATE_CLEAR)
+ pr_cont(", e(%d, 0x%04x)", info->event_state, info->event_type);
+ if (info->siop_state)
+ pr_cont(", op(%d, %d)", info->siop_state, info->siop_lv);
+
+ pr_cont("\n");
+
+ /* check current_avg */
+ if (info->charge_current_avg < 0)
+ pr_info("%s: charging but discharging, power off\n", __func__);
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
/* prevent suspend for ui-update */
@@ -1429,35 +1810,6 @@ static void battery_error_work(struct work_struct *work)
}
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
-static bool battery_terminal_check_support(struct battery_info *info)
-{
- int full_mode;
- int vcell;
- bool ret = false;
- pr_debug("%s\n", __func__);
-
- full_mode = battery_get_info(info, POWER_SUPPLY_PROP_CHARGE_FULL);
- vcell = battery_get_info(info, POWER_SUPPLY_PROP_VOLTAGE_NOW);
- pr_debug("%s: chg_status = %d, vcell = %d\n",
- __func__, full_mode, vcell);
-
- if (full_mode && (vcell <= 3800000)) {
- pr_info("%s: top-off or done mode, but low voltage(%d)\n",
- __func__, vcell / 1000);
- /* check again */
- vcell = battery_get_info(info, POWER_SUPPLY_PROP_VOLTAGE_NOW);
- if (vcell <= 3800000) {
- pr_info("%s: top-off or done mode, but low voltage(%d), "
- "set health error!\n",
- __func__, vcell / 1000);
- info->health_state = true;
- ret = true;
- }
- }
-
- return ret;
-}
-
static void battery_error_control(struct battery_info *info)
{
pr_info("%s\n", __func__);
@@ -1478,21 +1830,12 @@ static void battery_error_control(struct battery_info *info)
POWER_SUPPLY_STATUS_NOT_CHARGING;
}
} else if (info->health_state == true) {
- if ((info->battery_health ==
- POWER_SUPPLY_HEALTH_UNSPEC_FAILURE) ||
- battery_terminal_check_support(info)) {
- pr_info("%s: battery unspec, "
- "disable charging and off the "
- "system path!\n", __func__);
-
- /* invalid top-off state,
- assume terminals(+/-) open */
- battery_charge_control(info, OFF_CURR, OFF_CURR);
- pr_info("%s: set unspec phase!\n", __func__);
- info->is_unspec_phase = true;
- } else if (info->battery_health ==
+ if (info->battery_health ==
POWER_SUPPLY_HEALTH_OVERVOLTAGE)
pr_info("%s: vbus ovp state!", __func__);
+ else if (info->battery_health ==
+ POWER_SUPPLY_HEALTH_UNSPEC_FAILURE)
+ pr_info("%s: uspec state from charger", __func__);
}
return;
@@ -1512,7 +1855,15 @@ static enum power_supply_property samsung_battery_props[] = {
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CURRENT_MAX,
POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_CURRENT_AVG,
+#ifdef CONFIG_SLP
+ POWER_SUPPLY_PROP_CHARGE_FULL,
+ POWER_SUPPLY_PROP_CHARGE_NOW,
+#endif
POWER_SUPPLY_PROP_CAPACITY,
+#ifdef CONFIG_SLP
+ POWER_SUPPLY_PROP_CAPACITY_RAW,
+#endif
POWER_SUPPLY_PROP_TEMP,
};
@@ -1556,9 +1907,29 @@ static int samsung_battery_get_property(struct power_supply *ps,
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = info->charge_current;
break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ val->intval = info->charge_current_avg;
+ break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = info->battery_soc;
break;
+#ifdef CONFIG_SLP
+ case POWER_SUPPLY_PROP_CAPACITY_RAW:
+ val->intval = info->battery_raw_soc;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL:
+ if (info->full_charged_state)
+ val->intval = true;
+ else
+ val->intval = false;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_NOW:
+ if (info->charge_real_state == POWER_SUPPLY_STATUS_CHARGING)
+ val->intval = true;
+ else
+ val->intval = false;
+ break;
+#endif
case POWER_SUPPLY_PROP_TEMP:
val->intval = info->battery_temper;
break;
@@ -1590,21 +1961,59 @@ static int samsung_battery_set_property(struct power_supply *ps,
return 0;
}
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- case POWER_SUPPLY_PROP_HEALTH:
- case POWER_SUPPLY_PROP_PRESENT:
- case POWER_SUPPLY_PROP_ONLINE:
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- case POWER_SUPPLY_PROP_CURRENT_MAX:
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- case POWER_SUPPLY_PROP_CAPACITY:
- case POWER_SUPPLY_PROP_TEMP:
- break;
- default:
- return -EINVAL;
+ if (info->battery_test_mode) {
+ pr_info("%s: set test value: psp(%d), val(%d)\n",
+ __func__, psp, val->intval);
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ info->charge_virt_state = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CHARGE_TYPE:
+ info->charge_type = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ info->battery_health = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_PRESENT:
+ info->battery_present = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_ONLINE:
+ info->cable_type = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ info->battery_vcell = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_MAX:
+ info->input_current = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ info->charge_current = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_AVG:
+ info->charge_current_avg = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_CAPACITY:
+ info->battery_soc = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_TEMP:
+ info->battery_temper = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+ info->pdata->voltage_max = val->intval;
+ break;
+ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
+ info->pdata->voltage_min = val->intval;
+ break;
+ default:
+ return -EINVAL;
+ }
+ } else {
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ break;
+ default:
+ return -EINVAL;
+ }
}
cancel_work_sync(&info->monitor_work);
@@ -1625,8 +2034,10 @@ static int samsung_usb_get_property(struct power_supply *ps,
return -EINVAL;
/* Set enable=1 only if the USB charger is connected */
- val->intval = (info->cable_type == POWER_SUPPLY_TYPE_USB) ||
- (info->cable_type == POWER_SUPPLY_TYPE_USB_CDP);
+ val->intval = ((info->charge_virt_state !=
+ POWER_SUPPLY_STATUS_DISCHARGING) &&
+ ((info->cable_type == POWER_SUPPLY_TYPE_USB) ||
+ (info->cable_type == POWER_SUPPLY_TYPE_USB_CDP)));
return 0;
}
@@ -1642,22 +2053,48 @@ static int samsung_ac_get_property(struct power_supply *ps,
return -EINVAL;
/* Set enable=1 only if the AC charger is connected */
- val->intval = (info->cable_type == POWER_SUPPLY_TYPE_MAINS) ||
- (info->cable_type == POWER_SUPPLY_TYPE_MISC) ||
- (info->cable_type == POWER_SUPPLY_TYPE_DOCK) ||
- (info->cable_type == POWER_SUPPLY_TYPE_WIRELESS);
+ val->intval = ((info->charge_virt_state !=
+ POWER_SUPPLY_STATUS_DISCHARGING) &&
+ ((info->cable_type == POWER_SUPPLY_TYPE_MAINS) ||
+ (info->cable_type == POWER_SUPPLY_TYPE_MISC) ||
+ (info->cable_type == POWER_SUPPLY_TYPE_DOCK) ||
+ (info->cable_type == POWER_SUPPLY_TYPE_WIRELESS)));
return 0;
}
+static irqreturn_t battery_isr(int irq, void *data)
+{
+ struct battery_info *info = data;
+ int bat_gpio;
+ pr_info("%s: irq(%d)\n", __func__, irq);
+
+ bat_gpio = gpio_get_value(info->batdet_gpio);
+ pr_info("%s: battery present gpio(%d)\n", __func__, bat_gpio);
+
+ cancel_work_sync(&info->monitor_work);
+ wake_lock(&info->monitor_wake_lock);
+ schedule_work(&info->monitor_work);
+
+ return IRQ_HANDLED;
+}
+
static __devinit int samsung_battery_probe(struct platform_device *pdev)
{
struct battery_info *info;
- int ret = 0;
+ struct samsung_battery_platform_data *pdata = pdev->dev.platform_data;
+ int ret = -ENODEV;
char *temper_src_name[] = { "fuelgauge", "ap adc",
"ext adc", "unknown"
};
- pr_info("%s: SAMSUNG Battery Driver Loading\n", __func__);
+ char *vf_src_name[] = { "adc", "charger irq", "gpio", "unknown"
+ };
+ pr_info("%s: battery init\n", __func__);
+
+ if (!pdata) {
+ pr_err("%s: no platform data\n", __func__);
+ return -ENODEV;
+ }
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
@@ -1666,7 +2103,7 @@ static __devinit int samsung_battery_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, info);
info->dev = &pdev->dev;
- info->pdata = pdev->dev.platform_data;
+ info->pdata = pdata;
/* Check charger name and fuelgauge name. */
if (!info->pdata->charger_name || !info->pdata->fuelgauge_name) {
@@ -1699,19 +2136,37 @@ static __devinit int samsung_battery_probe(struct platform_device *pdev)
goto err_psy_get;
}
+#if defined(CONFIG_MACH_M0)
/* WORKAROUND: set battery pdata in driver */
if (system_rev == 3) {
info->pdata->temper_src = TEMPER_EXT_ADC;
info->pdata->temper_ch = 7;
}
+#endif
pr_info("%s: Temperature source: %s\n", __func__,
temper_src_name[info->pdata->temper_src]);
+
+ /* not supported H/W rev for VF ADC */
+#if defined(CONFIG_MACH_T0) && defined(CONFIG_TARGET_LOCALE_USA)
+ if (system_rev < 7)
+ info->pdata->vf_det_src = VF_DET_CHARGER;
+#endif
+ pr_info("%s: VF detect source: %s\n", __func__,
+ vf_src_name[info->pdata->vf_det_src]);
+
/* recalculate recharge voltage, it depends on max voltage value */
info->pdata->recharge_voltage = info->pdata->voltage_max -
RECHG_DROP_VALUE;
pr_info("%s: Recharge voltage: %d\n", __func__,
info->pdata->recharge_voltage);
+
+ if (info->pdata->ctia_spec == true) {
+ pr_info("%s: applied CTIA spec, event time(%ds)\n",
+ __func__, info->pdata->event_time);
+ } else
+ pr_info("%s: not applied CTIA spec\n", __func__);
+
#if defined(CONFIG_S3C_ADC)
/* adc register */
info->adc_client = s3c_adc_register(pdev, NULL, NULL, 0);
@@ -1730,18 +2185,21 @@ static __devinit int samsung_battery_probe(struct platform_device *pdev)
pr_info("%s: boot with charging, s(%d)\n", __func__,
info->charge_real_state);
info->charge_start_time = 1;
+ battery_set_adc_power(info, 1);
} else {
pr_info("%s: boot without charging, s(%d)\n", __func__,
info->charge_real_state);
info->charge_start_time = 0;
}
- info->full_charged_state = false;
+ info->full_charged_state = STATUS_NOT_FULL;
info->abstimer_state = false;
+ info->abstimer_active = false;
info->recharge_phase = false;
info->siop_charge_current = info->pdata->chg_curr_usb;
info->monitor_mode = MONITOR_NORM;
info->led_state = BATT_LED_DISCHARGING;
info->monitor_count = 0;
+ info->slate_mode = 0;
/* LPM charging state */
info->lpm_state = lpcharge;
@@ -1808,10 +2266,54 @@ static __devinit int samsung_battery_probe(struct platform_device *pdev)
goto err_psy_reg_ac;
}
+ /* battery present irq */
+ if (!info->pdata->batt_present_gpio) {
+ pr_info("%s: do not support battery gpio detect\n", __func__);
+ goto gpio_bat_det_finish;
+ } else
+ pr_info("%s: support battery gpio detection\n", __func__);
+
+ info->batdet_gpio = info->pdata->batt_present_gpio;
+ info->batdet_irq = gpio_to_irq(info->batdet_gpio);
+ ret = gpio_request(info->batdet_gpio, "battery_present_n");
+ if (ret) {
+ pr_err("%s: failed requesting gpio %d\n", __func__,
+ info->batdet_gpio);
+ goto err_irq;
+ }
+ gpio_direction_input(info->batdet_gpio);
+ gpio_free(info->batdet_gpio);
+
+ ret = request_threaded_irq(info->batdet_irq, NULL,
+ battery_isr,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "batdet-irq", info);
+ if (ret) {
+ pr_err("%s: fail to request batdet irq: %d: %d\n",
+ __func__, info->batdet_irq, ret);
+ goto err_irq;
+ }
+
+ ret = enable_irq_wake(info->batdet_irq);
+ if (ret) {
+ pr_err("%s: failed enable irq wake %d\n", __func__,
+ info->batdet_irq);
+ goto err_enable_irq;
+ }
+
+ info->batdet_irq_st = true;
+gpio_bat_det_finish:
+
/* Using android alarm for gauging instead of workqueue */
info->last_poll = alarm_get_elapsed_realtime();
- alarm_init(&info->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
- samsung_battery_alarm_start);
+ alarm_init(&info->monitor_alarm,
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ battery_monitor_alarm);
+
+ if (info->pdata->ctia_spec == true)
+ alarm_init(&info->event_alarm,
+ ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ battery_event_alarm);
/* update battery init status */
schedule_work(&info->monitor_work);
@@ -1829,14 +2331,20 @@ static __devinit int samsung_battery_probe(struct platform_device *pdev)
}
#endif
- pr_info("%s: SAMSUNG Battery Driver Loaded\n", __func__);
+ pr_info("%s: probe complete\n", __func__);
+
return 0;
+err_enable_irq:
+ free_irq(info->batdet_irq, info);
+err_irq:
+ power_supply_unregister(&info->psy_ac);
err_psy_reg_ac:
power_supply_unregister(&info->psy_usb);
err_psy_reg_usb:
power_supply_unregister(&info->psy_bat);
err_psy_reg_bat:
+ s3c_adc_release(info->adc_client);
wake_lock_destroy(&info->monitor_wake_lock);
wake_lock_destroy(&info->emer_wake_lock);
#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC)
@@ -1861,6 +2369,10 @@ static int __devexit samsung_battery_remove(struct platform_device *pdev)
remove_proc_entry("battery_info_proc", NULL);
+ alarm_cancel(&info->monitor_alarm);
+ if (info->pdata->ctia_spec == true)
+ alarm_cancel(&info->event_alarm);
+
cancel_work_sync(&info->error_work);
cancel_work_sync(&info->monitor_work);
@@ -1913,6 +2425,8 @@ static void samsung_battery_complete(struct device *dev)
pr_info("%s\n", __func__);
info->monitor_mode = MONITOR_NORM;
+
+ battery_monitor_interval(info);
}
static int samsung_battery_suspend(struct device *dev)
diff --git a/drivers/battery/samsung_battery_s2plus.c b/drivers/battery/samsung_battery_s2plus.c
deleted file mode 100644
index 8f26e4f..0000000
--- a/drivers/battery/samsung_battery_s2plus.c
+++ /dev/null
@@ -1,1273 +0,0 @@
-/*
- * samsung_battery.c
- *
- * Copyright (C) 2011 Samsung Electronics
- * SangYoung Son <hello.son@samsung.com>
- *
- * based on sec_battery.c
- *
- * Copyright (C) 2012 Samsung Electronics
- * Jaecheol Kim <jc22.kim@samsung.com>
- *
- * add sub-charger based on samsung_battery.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/err.h>
-#include <linux/init.h>
-#include <linux/interrupt.h>
-#include <linux/io.h>
-#include <linux/reboot.h>
-#include <linux/jiffies.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/wakelock.h>
-#include <linux/workqueue.h>
-#include <linux/proc_fs.h>
-#include <linux/android_alarm.h>
-#include <linux/battery/samsung_battery.h>
-#include <mach/regs-pmu.h>
-#include "battery-factory.h"
-#if defined(CONFIG_S3C_ADC)
-#include <plat/adc.h>
-#endif
-
-static char *supply_list[] = {
- "battery",
-};
-
-/* Get LP charging mode state */
-unsigned int lpcharge;
-static int battery_get_lpm_state(char *str)
-{
- get_option(&str, &lpcharge);
- pr_info("%s: Low power charging mode: %d\n", __func__, lpcharge);
-
- return lpcharge;
-}
-__setup("lpcharge=", battery_get_lpm_state);
-
-/* Temperature from fuelgauge or adc */
-static int battery_get_temper(struct battery_info *info)
-{
- union power_supply_propval value;
- int temper = 0;
- int retry_cnt = 0;
- pr_debug("%s\n", __func__);
-
- switch (info->pdata->temper_src) {
- case TEMPER_FUELGAUGE:
- info->psy_fuelgauge->get_property(info->psy_fuelgauge,
- POWER_SUPPLY_PROP_TEMP, &value);
- temper = value.intval;
- break;
- case TEMPER_AP_ADC:
-#if defined(CONFIG_S3C_ADC)
- do {
- info->battery_temper_adc =
- s3c_adc_read(info->adc_client,
- info->pdata->temper_ch);
-
- if (info->battery_temper_adc < 0) {
- pr_info("%s: adc read(%d), retry(%d)", __func__,
- info->battery_temper_adc, retry_cnt);
- retry_cnt++;
- msleep(100);
- }
- } while ((info->battery_temper_adc < 0) && (retry_cnt <= 5));
-
- if (info->battery_temper_adc < 0) {
- pr_info("%s: adc read error(%d), temper set as 30.0",
- __func__, info->battery_temper_adc);
- temper = 300;
- } else {
- temper = info->pdata->covert_adc(
- info->battery_temper_adc,
- info->pdata->temper_ch);
- }
-#endif
- break;
- case TEMPER_EXT_ADC:
-#if defined(CONFIG_STMPE811_ADC)
- temper = stmpe811_get_adc_value(info->pdata->temper_ch);
-#endif
- break;
- case TEMPER_UNKNOWN:
- default:
- pr_info("%s: invalid temper src(%d)\n", __func__,
- info->pdata->temper_src);
- temper = 300;
- break;
- }
-
- pr_debug("%s: temper(%d), source(%d)\n", __func__,
- temper, info->pdata->temper_src);
- return temper;
-}
-
-/* Get info from power supply at realtime */
-int battery_get_info(struct battery_info *info,
- enum power_supply_property property)
-{
- union power_supply_propval value;
- value.intval = 0;
-
- if (info->battery_error_test) {
- pr_info("%s: in test mode(%d), do not update\n", __func__,
- info->battery_error_test);
- return -EPERM;
- }
-
- switch (property) {
- /* Update from charger */
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- case POWER_SUPPLY_PROP_HEALTH:
- case POWER_SUPPLY_PROP_PRESENT:
- case POWER_SUPPLY_PROP_ONLINE:
- info->psy_charger->get_property(info->psy_charger,
- property, &value);
- break;
- case POWER_SUPPLY_PROP_STATUS:
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- if (info->use_sub_charger) {
- info->psy_sub_charger->get_property(info->psy_sub_charger,
- property, &value);
- } else {
- info->psy_charger->get_property(info->psy_charger,
- property, &value);
- }
- break;
- /* Update from fuelgauge */
- case POWER_SUPPLY_PROP_CAPACITY: /* Only Adjusted SOC */
- case POWER_SUPPLY_PROP_VOLTAGE_NOW: /* Only VCELL */
- info->psy_fuelgauge->get_property(info->psy_fuelgauge,
- property, &value);
- break;
- /* Update from fuelgauge or adc */
- case POWER_SUPPLY_PROP_TEMP:
- value.intval = battery_get_temper(info);
- break;
- default:
- break;
- }
-
- return value.intval;
-}
-
-/* Update all values for battery */
-void battery_update_info(struct battery_info *info)
-{
- union power_supply_propval value;
-
- if (info->use_sub_charger) {
- /* Update from Charger */
- info->psy_sub_charger->get_property(info->psy_sub_charger,
- POWER_SUPPLY_PROP_STATUS, &value);
- info->charge_real_state = info->charge_virt_state = value.intval;
-
- info->psy_sub_charger->get_property(info->psy_sub_charger,
- POWER_SUPPLY_PROP_CURRENT_NOW, &value);
- info->charge_current = value.intval;
-
- } else {
- /* Update from Charger */
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_STATUS, &value);
- info->charge_real_state = info->charge_virt_state = value.intval;
-
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_CURRENT_NOW, &value);
- info->charge_current = value.intval;
- }
-
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_HEALTH, &value);
- info->battery_health = value.intval;
-
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_PRESENT, &value);
- info->battery_present = value.intval;
-
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_ONLINE, &value);
- info->cable_type = value.intval;
-
- info->psy_charger->get_property(info->psy_charger,
- POWER_SUPPLY_PROP_CHARGE_TYPE, &value);
- info->charge_type = value.intval;
-
- /* Fuelgauge power off state */
- if ((info->cable_type != POWER_SUPPLY_TYPE_BATTERY) &&
- (info->battery_present == 0)) {
- pr_info("%s: Abnormal fuelgauge power state\n", __func__);
- goto update_finish;
- }
-
- /* Update from Fuelgauge */
- value.intval = SOC_TYPE_ADJUSTED;
- info->psy_fuelgauge->get_property(info->psy_fuelgauge,
- POWER_SUPPLY_PROP_CAPACITY, &value);
- info->battery_soc = value.intval;
-
- value.intval = SOC_TYPE_RAW;
- info->psy_fuelgauge->get_property(info->psy_fuelgauge,
- POWER_SUPPLY_PROP_CAPACITY, &value);
- info->battery_raw_soc = value.intval;
-
- value.intval = VOLTAGE_TYPE_VCELL;
- info->psy_fuelgauge->get_property(info->psy_fuelgauge,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- &value);
- info->battery_vcell = value.intval;
-
- value.intval = VOLTAGE_TYPE_VFOCV;
- info->psy_fuelgauge->get_property(info->psy_fuelgauge,
- POWER_SUPPLY_PROP_VOLTAGE_NOW,
- &value);
- info->battery_vfocv = value.intval;
-
- info->battery_temper = battery_get_temper(info);
-
-update_finish:
- switch (info->battery_error_test) {
- case 0:
- pr_debug("%s: error test: normal state\n", __func__);
- break;
- case 1:
- pr_info("%s: error test: full charged\n", __func__);
- info->charge_real_state = POWER_SUPPLY_STATUS_FULL;
- info->battery_vcell = 4200000;
- info->battery_soc = 100;
- break;
- case 2:
- pr_info("%s: error test: freezed\n", __func__);
- info->battery_temper = info->pdata->freeze_stop_temp - 10;
- break;
- case 3:
- pr_info("%s: error test: overheated\n", __func__);
- info->battery_temper = info->pdata->overheat_stop_temp + 10;
- break;
- case 4:
- pr_info("%s: error test: ovp\n", __func__);
- break;
- case 5:
- pr_info("%s: error test: vf error\n", __func__);
- info->battery_present = 0;
- break;
- default:
- pr_info("%s: error test: unknown state\n", __func__);
- break;
- }
-
- pr_debug("%s: state(%d), type(%d), "
- "health(%d), present(%d), "
- "cable(%d), curr(%d), "
- "soc(%d), raw(%d), "
- "vol(%d), ocv(%d), tmp(%d)\n", __func__,
- info->charge_real_state, info->charge_type,
- info->battery_health, info->battery_present,
- info->cable_type, info->charge_current,
- info->battery_soc, info->battery_raw_soc,
- info->battery_vcell, info->battery_vfocv,
- info->battery_temper);
-}
-
-/* Control charger and fuelgauge */
-void battery_control_info(struct battery_info *info,
- enum power_supply_property property, int intval)
-{
- union power_supply_propval value;
-
- value.intval = intval;
-
- switch (property) {
- /* Control to charger */
- case POWER_SUPPLY_PROP_STATUS:
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- if (info->use_sub_charger) {
- info->psy_sub_charger->set_property(info->psy_sub_charger,
- property, &value);
- } else {
- info->psy_charger->set_property(info->psy_charger,
- property, &value);
- }
- break;
-
- /* Control to fuelgauge */
- case POWER_SUPPLY_PROP_CAPACITY:
- info->psy_fuelgauge->set_property(info->psy_fuelgauge,
- property, &value);
- break;
- default:
- break;
- }
-}
-
-/* Support property from battery */
-static enum power_supply_property samsung_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_CURRENT_NOW,
- POWER_SUPPLY_PROP_CAPACITY,
- POWER_SUPPLY_PROP_TEMP,
-};
-
-/* Support property from usb, ac */
-static enum power_supply_property samsung_power_props[] = {
- POWER_SUPPLY_PROP_ONLINE,
-};
-
-static int samsung_battery_get_property(struct power_supply *ps,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct battery_info *info = container_of(ps, struct battery_info,
- psy_bat);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- val->intval = info->charge_virt_state;
- break;
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- val->intval = info->charge_type;
- break;
- case POWER_SUPPLY_PROP_HEALTH:
- val->intval = info->battery_health;
- break;
- case POWER_SUPPLY_PROP_PRESENT:
- val->intval = info->battery_present;
- break;
- case POWER_SUPPLY_PROP_ONLINE:
- val->intval = info->cable_type;
- break;
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- val->intval = info->battery_vcell;
- break;
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- val->intval = info->charge_current;
- break;
- case POWER_SUPPLY_PROP_CAPACITY:
- val->intval = info->battery_soc;
- break;
- case POWER_SUPPLY_PROP_TEMP:
- val->intval = info->battery_temper;
- break;
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int samsung_battery_set_property(struct power_supply *ps,
- enum power_supply_property psp,
- const union power_supply_propval *val)
-{
- struct battery_info *info = container_of(ps, struct battery_info,
- psy_bat);
-
- switch (psp) {
- case POWER_SUPPLY_PROP_STATUS:
- case POWER_SUPPLY_PROP_CHARGE_TYPE:
- case POWER_SUPPLY_PROP_HEALTH:
- case POWER_SUPPLY_PROP_PRESENT:
- case POWER_SUPPLY_PROP_ONLINE:
- case POWER_SUPPLY_PROP_TECHNOLOGY:
- case POWER_SUPPLY_PROP_VOLTAGE_NOW:
- case POWER_SUPPLY_PROP_CURRENT_NOW:
- case POWER_SUPPLY_PROP_CAPACITY:
- case POWER_SUPPLY_PROP_TEMP:
- break;
- default:
- return -EINVAL;
- }
-
- wake_lock(&info->monitor_wake_lock);
- schedule_work(&info->monitor_work);
-
- return 0;
-}
-
-static int samsung_usb_get_property(struct power_supply *ps,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct battery_info *info = container_of(ps, struct battery_info,
- psy_usb);
-
- if (psp != POWER_SUPPLY_PROP_ONLINE)
- return -EINVAL;
-
- /* Set enable=1 only if the USB charger is connected */
- val->intval = (info->cable_type == POWER_SUPPLY_TYPE_USB) ||
- (info->cable_type == POWER_SUPPLY_TYPE_USB_CDP);
-
- return 0;
-}
-
-static int samsung_ac_get_property(struct power_supply *ps,
- enum power_supply_property psp,
- union power_supply_propval *val)
-{
- struct battery_info *info = container_of(ps, struct battery_info,
- psy_ac);
-
- if (psp != POWER_SUPPLY_PROP_ONLINE)
- return -EINVAL;
-
- /* Set enable=1 only if the AC charger is connected */
- val->intval = (info->cable_type == POWER_SUPPLY_TYPE_MAINS) ||
- (info->cable_type == POWER_SUPPLY_TYPE_MISC) ||
- (info->cable_type == POWER_SUPPLY_TYPE_WIRELESS);
-
- return 0;
-}
-
-static void samsung_battery_alarm_start(struct alarm *alarm)
-{
- struct battery_info *info = container_of(alarm, struct battery_info,
- alarm);
- pr_debug("%s\n", __func__);
-
- wake_lock(&info->monitor_wake_lock);
- schedule_work(&info->monitor_work);
-}
-
-static void samsung_battery_next_monitor(struct battery_info *info)
-{
- ktime_t interval, next;
- unsigned long flags;
- pr_debug("%s\n", __func__);
-
- local_irq_save(flags);
-
- info->last_poll = alarm_get_elapsed_realtime();
-
- switch (info->monitor_mode) {
- case MONITOR_CHNG:
- info->monitor_interval = info->pdata->chng_interval;
- break;
- case MONITOR_CHNG_SUSP:
- info->monitor_interval = info->pdata->chng_susp_interval;
- break;
- case MONITOR_NORM:
- info->monitor_interval = info->pdata->norm_interval;
- break;
- case MONITOR_NORM_SUSP:
- info->monitor_interval = info->pdata->norm_susp_interval;
- break;
- case MONITOR_EMER:
- info->monitor_interval = info->pdata->emer_interval;
- break;
- default:
- info->monitor_interval = info->pdata->norm_interval;
- break;
- }
-
- pr_debug("%s: monitor mode(%d), interval(%d)\n", __func__,
- info->monitor_mode, info->monitor_interval);
-
- interval = ktime_set(info->monitor_interval, 0);
- next = ktime_add(info->last_poll, interval);
- alarm_start_range(&info->alarm, next, next);
-
- local_irq_restore(flags);
-}
-
-static bool battery_recharge_cond(struct battery_info *info)
-{
- pr_debug("%s\n", __func__);
-
- if (info->charge_real_state == POWER_SUPPLY_STATUS_CHARGING) {
- pr_debug("%s: not recharge cond., now charging\n", __func__);
- return false;
- }
-
- if (info->battery_vcell < info->pdata->recharge_voltage) {
- pr_info("%s: recharge start(%d ?? %d)\n", __func__,
- info->battery_vcell, info->pdata->recharge_voltage);
- return true;
- } else
- pr_debug("%s: not recharge cond., vcell is enough\n", __func__);
-
- return false;
-}
-
-static bool battery_abstimer_cond(struct battery_info *info)
-{
- unsigned int abstimer_duration;
- ktime_t ktime;
- struct timespec current_time;
- pr_debug("%s\n", __func__);
-
- if ((info->cable_type != POWER_SUPPLY_TYPE_MAINS) ||
- (info->full_charged_state == true) ||
- (info->charge_start_time == 0)) {
- info->abstimer_state = false;
- return false;
- }
-
- ktime = alarm_get_elapsed_realtime();
- current_time = ktime_to_timespec(ktime);
-
- if (info->recharge_phase)
- abstimer_duration = info->pdata->abstimer_recharge_duration;
- else
- abstimer_duration = info->pdata->abstimer_charge_duration;
-
- if ((current_time.tv_sec - info->charge_start_time) >
- abstimer_duration) {
- pr_info("%s: charge time out(%d - %d ?? %d)\n", __func__,
- (int)current_time.tv_sec,
- info->charge_start_time,
- abstimer_duration);
- info->abstimer_state = true;
- } else {
- pr_debug("%s: not abstimer condition\n", __func__);
- info->abstimer_state = false;
- }
-
- return info->abstimer_state;
-}
-
-static bool battery_fullcharged_cond(struct battery_info *info)
-{
- pr_debug("%s\n", __func__);
-
- if ((info->charge_real_state == POWER_SUPPLY_STATUS_FULL) &&
- (info->battery_vcell > 4150000) &&
- (info->battery_soc > 95)) {
- pr_info("%s: real full charged(%d, %d)\n", __func__,
- info->battery_vcell, info->battery_soc);
- info->full_charged_state = true;
- return true;
- } else if (info->full_charged_state == true) {
- pr_debug("%s: already full charged\n", __func__);
- } else {
- pr_debug("%s: not full charged\n", __func__);
- info->full_charged_state = false;
- }
-
- /* Add some more full charged case(current sensing, etc...) */
-
- return false;
-}
-
-static bool battery_vf_cond(struct battery_info *info)
-{
- pr_debug("%s\n", __func__);
-
-#if defined(CONFIG_MACH_P11)
- /* FIXME: fix P11 build error temporarily */
-#else
- /* jig detect by MUIC */
- if (is_jig_attached == JIG_ON) {
- pr_info("%s: JIG ON, do not check\n", __func__);
- info->vf_state = false;
- return false;
- }
-#endif
-
- /* TODO: Check VF from ADC */
-
- /* Now, battery present from charger */
- info->battery_present =
- battery_get_info(info, POWER_SUPPLY_PROP_PRESENT);
- if (info->battery_present == 0) {
- pr_info("%s: battery is not detected.\n", __func__);
- info->vf_state = true;
- } else {
- pr_debug("%s: battery is detected.\n", __func__);
- info->vf_state = false;
- }
-
- return info->vf_state;
-}
-
-static bool battery_health_cond(struct battery_info *info)
-{
- pr_debug("%s\n", __func__);
-
- if (info->battery_health == POWER_SUPPLY_HEALTH_DEAD) {
- pr_info("%s: battery dead(%d)\n", __func__,
- info->battery_health);
- info->health_state = true;
- } else if (info->battery_health == POWER_SUPPLY_HEALTH_OVERVOLTAGE) {
- pr_info("%s: battery overvoltage(%d)\n", __func__,
- info->battery_health);
- info->health_state = true;
- } else {
- pr_debug("%s: battery good(%d)\n", __func__,
- info->battery_health);
- info->health_state = false;
- }
-
- return info->health_state;
-}
-
-static bool battery_temp_cond(struct battery_info *info)
-{
- pr_debug("%s\n", __func__);
-
- if (info->temperature_state == false) {
- if (info->charge_real_state != POWER_SUPPLY_STATUS_CHARGING) {
- pr_debug("%s: not charging state\n", __func__);
- return false;
- }
-
- pr_debug("%s: check charging stop temp."
- "cond: %d ?? %d ~ %d\n", __func__,
- info->battery_temper,
- info->pdata->freeze_stop_temp,
- info->pdata->overheat_stop_temp);
-
- if (info->battery_temper >=
- info->pdata->overheat_stop_temp) {
- pr_info("%s: stop by overheated temp\n", __func__);
- info->overheated_state = true;
- } else if (info->battery_temper <=
- info->pdata->freeze_stop_temp) {
- pr_info("%s: stop by freezed temp\n", __func__);
- info->freezed_state = true;
- } else
- pr_debug("%s: normal charging temp\n", __func__);
- } else {
- pr_debug("%s: check charging recovery temp."
- "cond: %d ?? %d ~ %d\n", __func__,
- info->battery_temper,
- info->pdata->freeze_recovery_temp,
- info->pdata->overheat_recovery_temp);
-
- if ((info->overheated_state == true) &&
- (info->battery_temper <=
- info->pdata->overheat_recovery_temp)) {
- pr_info("%s: recovery from overheated\n",
- __func__);
- info->overheated_state = false;
- } else if ((info->freezed_state == true) &&
- (info->battery_temper >=
- info->pdata->freeze_recovery_temp)) {
- pr_info("%s: recovery from freezed\n",
- __func__);
- info->freezed_state = false;
- } else
- pr_info("%s: charge stopped temp\n", __func__);
- }
-
- if (info->overheated_state == true) {
- info->battery_health = POWER_SUPPLY_HEALTH_OVERHEAT;
- info->freezed_state = false;
- info->temperature_state = true;
- } else if (info->freezed_state == true) {
- info->battery_health = POWER_SUPPLY_HEALTH_COLD;
- info->overheated_state = false;
- info->temperature_state = true;
- } else {
- info->overheated_state = false;
- info->freezed_state = false;
- info->temperature_state = false;
- }
-
- return info->temperature_state;
-}
-
-static void battery_charge_control(struct battery_info *info,
- int enable,
- int set_current)
-{
- ktime_t ktime;
- struct timespec current_time;
- pr_debug("%s\n", __func__);
-
- ktime = alarm_get_elapsed_realtime();
- current_time = ktime_to_timespec(ktime);
-
- if (set_current == CHARGER_KEEP_CURRENT)
- goto charge_state_control;
-
- if (!info->use_sub_charger) {
- if (info->siop_state == true) {
- pr_debug("%s: siop state, charge current is %dmA\n", __func__,
- info->siop_charge_current);
- set_current = info->siop_charge_current;
- }
-
- /* check charge current before and after */
- if (info->charge_current == ((set_current * 3 / 100) * 333 / 10)) {
- /*
- * (current * 3 / 100) is converted value
- * for register setting.
- * (register current * 333 / 10) is actual value
- * for charging
- */
- pr_debug("%s: same charge current: %dmA\n", __func__,
- set_current);
- } else {
- battery_control_info(info,
- POWER_SUPPLY_PROP_CURRENT_NOW,
- set_current);
- pr_info("%s: update charge current: %dmA\n", __func__,
- set_current);
- }
- }
-
- info->charge_current =
- battery_get_info(info, POWER_SUPPLY_PROP_CURRENT_NOW);
-
-charge_state_control:
- /* check charge state before and after */
- if ((enable == CHARGE_ENABLE) &&
- (info->charge_start_time == 0)) {
- battery_control_info(info,
- POWER_SUPPLY_PROP_STATUS,
- CHARGE_ENABLE);
-
- info->charge_start_time = current_time.tv_sec;
- pr_info("%s: charge enabled, current as %dmA @%d\n", __func__,
- info->charge_current, info->charge_start_time);
- } else if ((enable == CHARGE_DISABLE) &&
- (info->charge_start_time != 0)) {
- battery_control_info(info,
- POWER_SUPPLY_PROP_STATUS,
- CHARGE_DISABLE);
-
- pr_info("%s: charge disabled, current as %dmA @%d\n", __func__,
- info->charge_current, (int)current_time.tv_sec);
-
- info->charge_start_time = 0;
- } else {
- pr_debug("%s: same charge state(%s), current as %dmA @%d\n",
- __func__, (enable ? "enabled" : "disabled"),
- info->charge_current, info->charge_start_time);
- }
-
- info->charge_real_state =
- battery_get_info(info, POWER_SUPPLY_PROP_STATUS);
-}
-
-static void battery_monitor_work(struct work_struct *work)
-{
- struct battery_info *info = container_of(work, struct battery_info,
- monitor_work);
- pr_debug("%s\n", __func__);
-
- /* If battery is not connected, clear flag for charge scenario */
- if (battery_vf_cond(info) == true) {
- pr_info("%s: battery error\n", __func__);
- info->overheated_state = false;
- info->freezed_state = false;
- info->temperature_state = false;
- info->full_charged_state = false;
- info->abstimer_state = false;
- info->recharge_phase = false;
-
- schedule_work(&info->error_work);
- }
-
- /* Check battery state from charger and fuelgauge */
- battery_update_info(info);
-
- /* If charger is not connected, do not check charge scenario */
- if (info->cable_type == POWER_SUPPLY_TYPE_BATTERY)
- goto charge_ok;
-
- /* Below is charger is connected state */
- if (battery_temp_cond(info) == true) {
- pr_info("%s: charge stopped by temperature\n", __func__);
- battery_charge_control(info,
- CHARGE_DISABLE, CHARGER_OFF_CURRENT);
- goto monitor_finish;
- }
-
- if (battery_health_cond(info) == true) {
- pr_info("%s: bad health state\n", __func__);
- goto monitor_finish;
- }
-
- if (battery_fullcharged_cond(info) == true) {
- pr_info("%s: full charged state\n", __func__);
- battery_charge_control(info,
- CHARGE_DISABLE, CHARGER_KEEP_CURRENT);
- info->recharge_phase = true;
- goto monitor_finish;
- }
-
- if (battery_abstimer_cond(info) == true) {
- pr_info("%s: abstimer state\n", __func__);
- battery_charge_control(info,
- CHARGE_DISABLE, CHARGER_OFF_CURRENT);
- info->recharge_phase = true;
- goto monitor_finish;
- }
-
- if (info->recharge_phase == true) {
- if (battery_recharge_cond(info) == true) {
- pr_info("%s: recharge condition\n", __func__);
- goto charge_ok;
- } else {
- pr_debug("%s: not recharge\n", __func__);
- goto monitor_finish;
- }
- }
-
-charge_ok:
- switch (info->cable_type) {
- case POWER_SUPPLY_TYPE_BATTERY:
- if (!info->pdata->suspend_chging)
- wake_unlock(&info->charge_wake_lock);
- battery_charge_control(info,
- CHARGE_DISABLE, CHARGER_OFF_CURRENT);
- info->charge_virt_state = POWER_SUPPLY_STATUS_DISCHARGING;
-
- /* clear charge scenario state */
- info->overheated_state = false;
- info->freezed_state = false;
- info->temperature_state = false;
- info->full_charged_state = false;
- info->abstimer_state = false;
- info->recharge_phase = false;
- break;
- case POWER_SUPPLY_TYPE_MAINS:
- if (!info->pdata->suspend_chging)
- wake_lock(&info->charge_wake_lock);
- battery_charge_control(info, CHARGE_ENABLE, CHARGER_AC_CURRENT_S2PLUS);
- info->charge_virt_state = POWER_SUPPLY_STATUS_CHARGING;
- break;
- case POWER_SUPPLY_TYPE_USB:
- if (!info->pdata->suspend_chging)
- wake_lock(&info->charge_wake_lock);
- battery_charge_control(info,
- CHARGE_ENABLE, CHARGER_USB_CURRENT);
- info->charge_virt_state = POWER_SUPPLY_STATUS_CHARGING;
- break;
- case POWER_SUPPLY_TYPE_USB_CDP:
- if (!info->pdata->suspend_chging)
- wake_lock(&info->charge_wake_lock);
- battery_charge_control(info,
- CHARGE_ENABLE, CHARGER_CDP_CURRENT);
- info->charge_virt_state = POWER_SUPPLY_STATUS_CHARGING;
- break;
- case POWER_SUPPLY_TYPE_WIRELESS:
- if (!info->pdata->suspend_chging)
- wake_lock(&info->charge_wake_lock);
- battery_charge_control(info,
- CHARGE_ENABLE, CHARGER_WPC_CURRENT);
- info->charge_virt_state = POWER_SUPPLY_STATUS_CHARGING;
- break;
- default:
- break;
- }
-
-monitor_finish:
- /* Overwrite charge state for UI(icon) */
- if (info->full_charged_state == true) {
- info->charge_virt_state = POWER_SUPPLY_STATUS_FULL;
- info->battery_soc = 100;
- } else if (info->abstimer_state == true) {
- info->charge_virt_state = POWER_SUPPLY_STATUS_CHARGING;
- } else if (info->recharge_phase == true) {
- info->charge_virt_state = POWER_SUPPLY_STATUS_CHARGING;
- }
-
- if (info->cable_type != POWER_SUPPLY_TYPE_BATTERY) {
- if (info->temperature_state == true)
- info->charge_virt_state =
- POWER_SUPPLY_STATUS_NOT_CHARGING;
-
- if (info->vf_state == true) {
- info->charge_virt_state =
- POWER_SUPPLY_STATUS_NOT_CHARGING;
- /* to be considered */
- info->battery_health =
- POWER_SUPPLY_HEALTH_UNSPEC_FAILURE;
- }
-
- if (info->health_state == true)
- info->charge_virt_state =
- POWER_SUPPLY_STATUS_NOT_CHARGING;
- }
-
- /* monitoring interval */
- if (info->charge_virt_state == POWER_SUPPLY_STATUS_NOT_CHARGING) {
- pr_debug("%s: emergency(not charging) state\n", __func__);
- info->monitor_mode = MONITOR_EMER;
- wake_lock(&info->emer_wake_lock);
- } else {
- pr_debug("%s: normal state\n", __func__);
- info->monitor_mode = MONITOR_NORM;
- wake_unlock(&info->emer_wake_lock);
- }
-
- pr_info("bat: s(%d), v(%d, %d), b(%d), "
- "t(%d.%d), h(%d), "
- "ch(%d, %d), cb(%d), cr(%d), "
- "abs(%d), f(%d), rch(%d), t(%d)\n",
- info->battery_soc,
- info->battery_vcell / 1000,
- info->battery_vfocv / 1000,
- info->battery_present,
- info->battery_temper / 10, info->battery_temper % 10,
- info->battery_health,
- info->charge_real_state,
- info->charge_virt_state,
- info->cable_type,
- info->charge_current,
- info->abstimer_state,
- info->full_charged_state,
- info->recharge_phase, info->charge_start_time);
-
- /*
- * WORKAROUND: Do not power off, if vell is over 3400mV
- */
- if (info->battery_soc == 0) {
- if (info->battery_vcell > 3400) {
- pr_info("%s: soc 0%%, but vcell(%d) is over 3400mV, "
- "do not power off\n",
- __func__, info->battery_vcell);
- info->battery_soc = 1;
- }
- }
-
- power_supply_changed(&info->psy_bat);
-
- /* prevent suspend before starting the alarm */
- samsung_battery_next_monitor(info);
-
- wake_unlock(&info->monitor_wake_lock);
-
- return;
-}
-
-static void battery_error_work(struct work_struct *work)
-{
- struct battery_info *info = container_of(work, struct battery_info,
- error_work);
- int err_cnt;
- int old_vcell, new_vcell, vcell_diff;
- pr_info("%s\n", __func__);
-
- if (info->vf_state == true) {
- pr_info("%s: battery error state\n", __func__);
- old_vcell = info->battery_vcell;
- new_vcell = 0;
- for (err_cnt = 1; err_cnt <= VF_CHECK_COUNT; err_cnt++) {
- /* check jig first */
- if (is_jig_attached == JIG_ON) {
- pr_info("%s: JIG detected, return\n", __func__);
- return;
- }
- info->battery_present =
- battery_get_info(info,
- POWER_SUPPLY_PROP_PRESENT);
- if (info->battery_present == 0) {
- pr_info("%s: battery still error(%d)\n",
- __func__, err_cnt);
- msleep(VF_CHECK_DELAY);
- } else {
- pr_info("%s: battery detect ok, "
- "check soc\n", __func__);
- new_vcell = battery_get_info(info,
- POWER_SUPPLY_PROP_VOLTAGE_NOW);
- vcell_diff = abs(old_vcell - new_vcell);
- pr_info("%s: check vcell: %d -> %d, diff: %d\n",
- __func__, info->battery_vcell,
- new_vcell, vcell_diff);
- if (vcell_diff > RESET_SOC_DIFF_TH) {
- pr_info("%s: reset soc\n", __func__);
- battery_control_info(info,
- POWER_SUPPLY_PROP_CAPACITY, 1);
- } else
- pr_info("%s: keep soc\n", __func__);
- break;
- }
-
- if (err_cnt == VF_CHECK_COUNT) {
- pr_info("%s: battery error, power off\n",
- __func__);
- battery_charge_control(info,
- CHARGE_DISABLE, CHARGER_OFF_CURRENT);
- }
- }
- }
-
- return;
-}
-
-static __devinit int samsung_battery_probe(struct platform_device *pdev)
-{
- struct battery_info *info;
- int ret = 0;
- char *temper_src_name[] = { "fuelgauge", "ap adc",
- "ext adc", "unknown"
- };
- pr_info("%s: SAMSUNG Battery Driver Loading\n", __func__);
-
- info = kzalloc(sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- platform_set_drvdata(pdev, info);
-
- info->dev = &pdev->dev;
- info->pdata = pdev->dev.platform_data;
-
- /* Check charger name and fuelgauge name. */
- if (!info->pdata->charger_name || !info->pdata->fuelgauge_name) {
- pr_err("%s: no charger or fuel gauge name\n", __func__);
- goto err_kfree;
- }
- info->charger_name = info->pdata->charger_name;
- info->fuelgauge_name = info->pdata->fuelgauge_name;
-
- pr_info("%s: Charger name: %s\n", __func__, info->charger_name);
- pr_info("%s: Fuelgauge name: %s\n", __func__, info->fuelgauge_name);
-
- info->psy_charger = power_supply_get_by_name(info->charger_name);
- info->psy_fuelgauge = power_supply_get_by_name(info->fuelgauge_name);
-
- if (!info->psy_charger || !info->psy_fuelgauge) {
- pr_err("%s: fail to get power supply\n", __func__);
- goto err_kfree;
- }
-
- info->use_sub_charger = info->pdata->use_sub_charger;
- if (info->use_sub_charger) {
- info->sub_charger_name = info->pdata->sub_charger_name;
- pr_info("%s: subcharger name: %s\n", __func__,
- info->sub_charger_name);
- info->psy_sub_charger = power_supply_get_by_name(info->sub_charger_name);
-
- if (!info->psy_sub_charger) {
- pr_err("%s fail to get sub charger\n", __func__);
- goto err_kfree;
- }
- }
- /* force set S2PLUS recharge voltage */
- info->pdata->recharge_voltage = 4150000;
-
- pr_info("%s: Temperature source: %s\n", __func__,
- temper_src_name[info->pdata->temper_src]);
- pr_info("%s: Recharge voltage: %d\n", __func__,
- info->pdata->recharge_voltage);
-
-#if defined(CONFIG_S3C_ADC)
- info->adc_client = s3c_adc_register(pdev, NULL, NULL, 0);
-#endif
-
- /* init battery info */
- info->full_charged_state = false;
- info->abstimer_state = false;
- info->recharge_phase = false;
- info->siop_charge_current = CHARGER_USB_CURRENT;
- info->monitor_mode = MONITOR_NORM;
-
- /* LPM charging state */
- info->lpm_state = lpcharge;
-
- wake_lock_init(&info->monitor_wake_lock, WAKE_LOCK_SUSPEND,
- "battery-monitor");
- wake_lock_init(&info->emer_wake_lock, WAKE_LOCK_SUSPEND,
- "battery-emergency");
- if (!info->pdata->suspend_chging)
- wake_lock_init(&info->charge_wake_lock,
- WAKE_LOCK_SUSPEND, "battery-charging");
-
- /* Init wq for battery */
- INIT_WORK(&info->error_work, battery_error_work);
- INIT_WORK(&info->monitor_work, battery_monitor_work);
-
- /* Init Power supply class */
- info->psy_bat.name = "battery";
- info->psy_bat.type = POWER_SUPPLY_TYPE_BATTERY;
- info->psy_bat.properties = samsung_battery_props;
- info->psy_bat.num_properties = ARRAY_SIZE(samsung_battery_props);
- info->psy_bat.get_property = samsung_battery_get_property;
- info->psy_bat.set_property = samsung_battery_set_property;
-
- info->psy_usb.name = "usb";
- info->psy_usb.type = POWER_SUPPLY_TYPE_USB;
- info->psy_usb.supplied_to = supply_list;
- info->psy_usb.num_supplicants = ARRAY_SIZE(supply_list);
- info->psy_usb.properties = samsung_power_props;
- info->psy_usb.num_properties = ARRAY_SIZE(samsung_power_props);
- info->psy_usb.get_property = samsung_usb_get_property;
-
- info->psy_ac.name = "ac";
- info->psy_ac.type = POWER_SUPPLY_TYPE_MAINS;
- info->psy_ac.supplied_to = supply_list;
- info->psy_ac.num_supplicants = ARRAY_SIZE(supply_list);
- info->psy_ac.properties = samsung_power_props;
- info->psy_ac.num_properties = ARRAY_SIZE(samsung_power_props);
- info->psy_ac.get_property = samsung_ac_get_property;
-
- ret = power_supply_register(&pdev->dev, &info->psy_bat);
- if (ret) {
- pr_err("%s: failed to register psy_bat\n", __func__);
- goto err_psy_reg_bat;
- }
-
- ret = power_supply_register(&pdev->dev, &info->psy_usb);
- if (ret) {
- pr_err("%s: failed to register psy_usb\n", __func__);
- goto err_psy_reg_usb;
- }
-
- ret = power_supply_register(&pdev->dev, &info->psy_ac);
- if (ret) {
- pr_err("%s: failed to register psy_ac\n", __func__);
- goto err_psy_reg_ac;
- }
-
- /* Using android alarm for gauging instead of workqueue */
- info->last_poll = alarm_get_elapsed_realtime();
- alarm_init(&info->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
- samsung_battery_alarm_start);
-
- /* update battery init status */
- schedule_work(&info->monitor_work);
-
- /* Create samsung detail attributes */
- battery_create_attrs(info->psy_bat.dev);
-
- pr_info("%s: SAMSUNG Battery Driver Loaded\n", __func__);
- return 0;
-
- err_psy_reg_ac:
- power_supply_unregister(&info->psy_usb);
- err_psy_reg_usb:
- power_supply_unregister(&info->psy_bat);
- err_psy_reg_bat:
- wake_lock_destroy(&info->monitor_wake_lock);
- wake_lock_destroy(&info->emer_wake_lock);
- if (!info->pdata->suspend_chging)
- wake_lock_destroy(&info->charge_wake_lock);
- err_kfree:
- kfree(info);
-
- return ret;
-}
-
-static int __devexit samsung_battery_remove(struct platform_device *pdev)
-{
- struct battery_info *info = platform_get_drvdata(pdev);
-
- remove_proc_entry("battery_info_proc", NULL);
-
- cancel_work_sync(&info->error_work);
- cancel_work_sync(&info->monitor_work);
-
- power_supply_unregister(&info->psy_bat);
- power_supply_unregister(&info->psy_usb);
- power_supply_unregister(&info->psy_ac);
-
- wake_lock_destroy(&info->monitor_wake_lock);
- wake_lock_destroy(&info->emer_wake_lock);
- if (!info->pdata->suspend_chging)
- wake_lock_destroy(&info->charge_wake_lock);
-
- kfree(info);
-
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int samsung_battery_prepare(struct device *dev)
-{
- struct battery_info *info = dev_get_drvdata(dev);
- pr_info("%s\n", __func__);
-
- if (info->monitor_mode != MONITOR_EMER) {
- if (info->charge_real_state == POWER_SUPPLY_STATUS_CHARGING)
- info->monitor_mode = MONITOR_CHNG_SUSP;
- else
- info->monitor_mode = MONITOR_NORM_SUSP;
- }
-
- samsung_battery_next_monitor(info);
-
- return 0;
-}
-
-static void samsung_battery_complete(struct device *dev)
-{
- struct battery_info *info = dev_get_drvdata(dev);
- pr_info("%s\n", __func__);
-
- info->monitor_mode = MONITOR_NORM;
-}
-
-static int samsung_battery_suspend(struct device *dev)
-{
- struct battery_info *info = dev_get_drvdata(dev);
- pr_info("%s\n", __func__);
-
- flush_work_sync(&info->monitor_work);
-
- return 0;
-}
-
-static int samsung_battery_resume(struct device *dev)
-{
- struct battery_info *info = dev_get_drvdata(dev);
- pr_info("%s\n", __func__);
-
- schedule_work(&info->monitor_work);
-
- return 0;
-}
-
-static const struct dev_pm_ops samsung_battery_pm_ops = {
- .prepare = samsung_battery_prepare,
- .complete = samsung_battery_complete,
- .suspend = samsung_battery_suspend,
- .resume = samsung_battery_resume,
-};
-#endif
-
-static struct platform_driver samsung_battery_driver = {
- .driver = {
- .name = "samsung-battery",
- .owner = THIS_MODULE,
-#ifdef CONFIG_PM
- .pm = &samsung_battery_pm_ops,
-#endif
- },
- .probe = samsung_battery_probe,
- .remove = __devexit_p(samsung_battery_remove),
-};
-
-static int __init samsung_battery_init(void)
-{
- return platform_driver_register(&samsung_battery_driver);
-}
-
-static void __exit samsung_battery_exit(void)
-{
- platform_driver_unregister(&samsung_battery_driver);
-}
-
-late_initcall(samsung_battery_init);
-module_exit(samsung_battery_exit);
-
-MODULE_AUTHOR("Jaecheol Kim <jc22.kim@samsung.com>");
-MODULE_DESCRIPTION("SAMSUNG battery driver");
-MODULE_LICENSE("GPL");