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++) {
#if defined(CONFIG_MACH_P11) || defined(CONFIG_MACH_P10)
			/* FIXME: fix P11 build error temporarily */
#else
			if (is_jig_attached == JIG_ON) {
				pr_info("%s: JIG detected, return\n", __func__);
				return;
			}
#endif
			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 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 - factory_attrs;
	pr_info("%s: %s\n", __func__, factory_attrs[off].attr.name);

	x = 0;
	ret = 0;
	switch (off) {
	case BATT_RESET_SOC:
		if (sscanf(buf, "%d\n", &x) == 1) {
			if (x == 1) {
				pr_info("%s: Reset SOC.\n", __func__);
				battery_control_info(info,
						POWER_SUPPLY_PROP_CAPACITY,
						1);
			} else
				pr_info("%s: Not supported param.\n", __func__);
			ret = count;
		}
		break;
	case TEST_MODE:
		if (sscanf(buf, "%d\n", &x) == 1) {
			info->battery_test_mode = x;
			pr_info("%s: battery test mode: %d\n", __func__,
						info->battery_test_mode);
			ret = count;
		}
		break;
	case BATT_ERROR_TEST:
		if (sscanf(buf, "%d\n", &x) == 1) {
			info->battery_error_test = x;
			pr_info("%s: battery error test: %d\n", __func__,
						info->battery_error_test);
			ret = count;
		}
		break;
	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 FACTORY_MODE:
		if (sscanf(buf, "%d\n", &x) == 1) {
			if (x)
				info->factory_mode = true;
			else
				info->factory_mode = false;

			pr_info("%s: factory mode %s\n", __func__,
				(info->factory_mode ? "set" : "clear"));
			ret = count;
		}
		break;
	case UPDATE:
		pr_info("%s: battery update\n", __func__);
		ret = count;
		break;
	default:
		ret = -EINVAL;
	}

	schedule_work(&info->monitor_work);

	return ret;
}
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->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 = info->charge_virt_state =
		battery_get_info(info, POWER_SUPPLY_PROP_STATUS);
}