++ struct n810bm *bm = container_of(work, struct n810bm, currmeas_irq_work);
++ int res, ma, mv, temp;
++
++ mutex_lock(&bm->mutex);
++ if (!lipocharge_is_charging(&bm->charger))
++ goto out_unlock;
++
++ tahvo_maskset(bm, TAHVO_REG_CHGCTL,
++ TAHVO_REG_CHGCTL_PWMOVR |
++ TAHVO_REG_CHGCTL_PWMOVRZERO,
++ TAHVO_REG_CHGCTL_PWMOVR);
++ ma = n810bm_measure_batt_current(bm);
++ tahvo_maskset(bm, TAHVO_REG_CHGCTL,
++ TAHVO_REG_CHGCTL_PWMOVR |
++ TAHVO_REG_CHGCTL_PWMOVRZERO,
++ TAHVO_REG_CHGCTL_PWMOVR |
++ TAHVO_REG_CHGCTL_PWMOVRZERO);
++ msleep(10);
++ mv = n810bm_measure_batt_voltage(bm);
++ tahvo_maskset(bm, TAHVO_REG_CHGCTL,
++ TAHVO_REG_CHGCTL_PWMOVR |
++ TAHVO_REG_CHGCTL_PWMOVRZERO,
++ 0);
++ temp = n810bm_measure_batt_temp(bm);
++ if (WARN_ON(mv < 0))
++ goto out_unlock;
++ if (WARN_ON(temp < 0))
++ goto out_unlock;
++
++ if (bm->verbose_charge_log) {
++ dev_info(&bm->pdev->dev,
++ "Battery charge state: %d mV, %d mA (%s)",
++ mv, ma,
++ (ma <= 0) ? "discharging" : "charging");
++ }
++ res = lipocharge_update_state(&bm->charger, mv, ma, temp);
++ if (res) {
++ if (res > 0)
++ dev_info(&bm->pdev->dev, "Battery fully charged");
++ n810bm_stop_charge(bm);
++ }
++out_unlock:
++ mutex_unlock(&bm->mutex);
++}
++
++static void n810bm_tahvo_current_measure_irq_handler(unsigned long data)
++{
++ struct n810bm *bm = (struct n810bm *)data;
++
++ tahvo_ack_irq(TAHVO_INT_BATCURR);
++ schedule_work(&bm->currmeas_irq_work);
++}
++
++#define DEFINE_ATTR_NOTIFY(attr_name) \
++ void n810bm_notify_##attr_name(struct n810bm *bm) \
++ { \
++ set_bit(N810BM_NOTIFY_##attr_name, &bm->notify_flags); \
++ wmb(); \
++ schedule_work(&bm->notify_work); \
++ }
++
++#define DEFINE_SHOW_INT_FUNC(name, member) \
++ static ssize_t n810bm_attr_##name##_show(struct device *dev, \
++ struct device_attribute *attr, \
++ char *buf) \
++ { \
++ struct n810bm *bm = device_to_n810bm(dev); \
++ ssize_t count; \
++ \
++ mutex_lock(&bm->mutex); \
++ count = snprintf(buf, PAGE_SIZE, "%d\n", (int)(bm->member)); \
++ mutex_unlock(&bm->mutex); \
++ \
++ return count; \
++ }
++
++#define DEFINE_STORE_INT_FUNC(name, member) \
++ static ssize_t n810bm_attr_##name##_store(struct device *dev, \
++ struct device_attribute *attr,\
++ const char *buf, size_t count)\
++ { \
++ struct n810bm *bm = device_to_n810bm(dev); \
++ long val; \
++ int err; \
++ \
++ mutex_lock(&bm->mutex); \
++ err = strict_strtol(buf, 0, &val); \
++ if (!err) \
++ bm->member = (typeof(bm->member))val; \
++ mutex_unlock(&bm->mutex); \
++ \
++ return err ? err : count; \
++ }
++
++#define DEFINE_ATTR_SHOW_INT(name, member) \
++ DEFINE_SHOW_INT_FUNC(name, member) \
++ static DEVICE_ATTR(name, S_IRUGO, \
++ n810bm_attr_##name##_show, NULL);
++
++#define DEFINE_ATTR_SHOW_STORE_INT(name, member) \
++ DEFINE_SHOW_INT_FUNC(name, member) \
++ DEFINE_STORE_INT_FUNC(name, member) \
++ static DEVICE_ATTR(name, S_IRUGO | S_IWUSR, \
++ n810bm_attr_##name##_show, \
++ n810bm_attr_##name##_store);
++
++DEFINE_ATTR_SHOW_INT(battery_present, battery_present);
++DEFINE_ATTR_SHOW_INT(charger_present, charger_present);
++DEFINE_ATTR_SHOW_INT(charger_pwm, active_current_pwm);
++static DEFINE_ATTR_NOTIFY(charger_pwm);
++DEFINE_ATTR_SHOW_STORE_INT(charger_enable, charger_enabled);
++DEFINE_ATTR_SHOW_STORE_INT(charger_verbose, verbose_charge_log);
++
++static ssize_t n810bm_attr_battery_charging(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct n810bm *bm = device_to_n810bm(dev);
++ ssize_t count;
++
++ mutex_lock(&bm->mutex);
++ count = snprintf(buf, PAGE_SIZE, "%d\n",
++ (int)lipocharge_is_charging(&bm->charger));
++ mutex_unlock(&bm->mutex);
++
++ return count;
++}
++static DEVICE_ATTR(battery_charging, S_IRUGO,
++ n810bm_attr_battery_charging, NULL);
++static DEFINE_ATTR_NOTIFY(battery_charging);
++
++static ssize_t n810bm_attr_battery_level_show(struct device *dev,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct n810bm *bm = device_to_n810bm(dev);
++ ssize_t count = -ENODEV;