diff options
author | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 09:48:20 +0200 |
---|---|---|
committer | codeworkx <codeworkx@cyanogenmod.com> | 2012-09-22 14:02:16 +0200 |
commit | 2489007e7d740ccbc3e0a202914e243ad5178787 (patch) | |
tree | b8e6380ea7b1da63474ad68a5dba997e01146043 /drivers/battery | |
parent | 5f67568eb31e3a813c7c52461dcf66ade15fc2e7 (diff) | |
download | kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.zip kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.gz kernel_samsung_smdk4412-2489007e7d740ccbc3e0a202914e243ad5178787.tar.bz2 |
merge opensource jb u5
Change-Id: I1aaec157aa196f3448eff8636134fce89a814cf2
Diffstat (limited to 'drivers/battery')
-rw-r--r-- | drivers/battery/Makefile | 3 | ||||
-rw-r--r-- | drivers/battery/battery-factory.c | 253 | ||||
-rw-r--r-- | drivers/battery/battery-factory.h | 1 | ||||
-rw-r--r-- | drivers/battery/max17047_fuelgauge.c | 102 | ||||
-rw-r--r-- | drivers/battery/max77693_charger.c | 619 | ||||
-rw-r--r-- | drivers/battery/samsung_battery.c | 868 | ||||
-rw-r--r-- | drivers/battery/samsung_battery_s2plus.c | 1273 |
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, ®_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, ®_data); - pr_debug("%s: chgprot = %d\n", __func__, reg_data); + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_01, ®_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, ®_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, ®_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, ®_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, + ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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, ®_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"); |