#include #include #include #include #include #include #include #include #include #include #include #include #include "axp-core.h" #include "axp-charger.h" static int axp_power_key; static enum AW_CHARGE_TYPE axp_usbcurflag = CHARGE_AC; static enum AW_CHARGE_TYPE axp_usbvolflag = CHARGE_AC; static bool battery_initialized; static DEFINE_SPINLOCK(axp_powerkey_lock); void axp_powerkey_set(int value) { spin_lock(&axp_powerkey_lock); axp_power_key = value; spin_unlock(&axp_powerkey_lock); } EXPORT_SYMBOL_GPL(axp_powerkey_set); int axp_powerkey_get(void) { int value; spin_lock(&axp_powerkey_lock); value = axp_power_key; spin_unlock(&axp_powerkey_lock); return value; } EXPORT_SYMBOL_GPL(axp_powerkey_get); int axp_usbvol(enum AW_CHARGE_TYPE type) { axp_usbvolflag = type; return 0; } EXPORT_SYMBOL_GPL(axp_usbvol); int axp_usbcur(enum AW_CHARGE_TYPE type) { axp_usbcurflag = type; return 0; } EXPORT_SYMBOL_GPL(axp_usbcur); void axp_charger_update_state(struct axp_charger_dev *chg_dev) { u8 val; struct axp_ac_info *ac = chg_dev->spy_info->ac; struct axp_usb_info *usb = chg_dev->spy_info->usb; struct axp_battery_info *batt = chg_dev->spy_info->batt; struct axp_regmap *map = chg_dev->chip->regmap; axp_regmap_read(map, ac->det_offset, &val); spin_lock(&chg_dev->charger_lock); chg_dev->ac_det = (val & 1 << ac->det_bit) ? 1 : 0; spin_unlock(&chg_dev->charger_lock); if (usb->det_unused == 0) { axp_regmap_read(map, usb->det_offset, &val); spin_lock(&chg_dev->charger_lock); chg_dev->usb_det = (val & 1 << usb->det_bit) ? 1 : 0; spin_unlock(&chg_dev->charger_lock); } else if (usb->det_unused == 1) { chg_dev->usb_det = 0; } axp_regmap_read(map, ac->valid_offset, &val); spin_lock(&chg_dev->charger_lock); chg_dev->ac_valid = (val & 1 << ac->valid_bit) ? 1 : 0; spin_unlock(&chg_dev->charger_lock); if (usb->det_unused == 0) { axp_regmap_read(map, usb->valid_offset, &val); spin_lock(&chg_dev->charger_lock); chg_dev->usb_valid = (val & 1 << usb->valid_bit) ? 1 : 0; spin_unlock(&chg_dev->charger_lock); } else if (usb->det_unused == 1) { chg_dev->usb_valid = 0; } chg_dev->ext_valid = (chg_dev->ac_det || chg_dev->usb_det); axp_regmap_read(map, batt->det_offset, &val); spin_lock(&chg_dev->charger_lock); if (batt->det_unused == 0) { if (batt->det_valid == 1) { if ((chg_dev->ac_det && chg_dev->ac_valid) || (chg_dev->usb_det && chg_dev->usb_valid)) { if ((val & (1 << batt->det_bit)) && (val & (1 << batt->det_valid_bit))) chg_dev->bat_det = 1; else chg_dev->bat_det = 0; } else { if (val & (1 << batt->det_bit)) chg_dev->bat_det = 1; else chg_dev->bat_det = 0; } } else if (batt->det_valid == 0) { chg_dev->bat_det = (val & 1 << batt->det_bit) ? 1 : 0; } } else if (batt->det_unused == 1) { chg_dev->bat_det = 0; } spin_unlock(&chg_dev->charger_lock); axp_regmap_read(map, ac->in_short_offset, &val); spin_lock(&chg_dev->charger_lock); chg_dev->in_short = (val & 1 << ac->in_short_bit) ? 1 : 0; if (!chg_dev->in_short) chg_dev->ac_charging = chg_dev->ac_valid; spin_unlock(&chg_dev->charger_lock); axp_regmap_read(map, batt->cur_direction_offset, &val); spin_lock(&chg_dev->charger_lock); if (val & 1 << batt->cur_direction_bit) chg_dev->bat_current_direction = 1; else chg_dev->bat_current_direction = 0; spin_unlock(&chg_dev->charger_lock); axp_regmap_read(map, batt->chgstat_offset, &val); spin_lock(&chg_dev->charger_lock); chg_dev->charging = (val & 1 << batt->chgstat_bit) ? 1 : 0; spin_unlock(&chg_dev->charger_lock); } static void axp_charger_get_bat_temp(struct axp_charger_dev *chg_dev) { struct axp_battery_info *batt = chg_dev->spy_info->batt; int bat_temp = 30; if (batt->get_bat_temp) bat_temp = batt->get_bat_temp(chg_dev); chg_dev->bat_temp = bat_temp; } void axp_charger_update_value(struct axp_charger_dev *chg_dev) { struct axp_ac_info *ac = chg_dev->spy_info->ac; struct axp_usb_info *usb = chg_dev->spy_info->usb; struct axp_battery_info *batt = chg_dev->spy_info->batt; int bat_vol, bat_cur, bat_discur, ac_vol, ac_cur, usb_vol, usb_cur; bat_vol = batt->get_vbat(chg_dev); bat_cur = batt->get_ibat(chg_dev); bat_discur = batt->get_disibat(chg_dev); ac_vol = ac->get_ac_voltage(chg_dev); ac_cur = ac->get_ac_current(chg_dev); usb_vol = usb->get_usb_voltage(chg_dev); usb_cur = usb->get_usb_current(chg_dev); axp_charger_get_bat_temp(chg_dev); spin_lock(&chg_dev->charger_lock); chg_dev->bat_vol = bat_vol; chg_dev->bat_cur = bat_cur; chg_dev->bat_discur = bat_discur; chg_dev->ac_vol = ac_vol; chg_dev->ac_cur = ac_cur; chg_dev->usb_vol = usb_vol; chg_dev->usb_cur = usb_cur; spin_unlock(&chg_dev->charger_lock); } int axp_charger_vts_to_temp(int data, struct axp_config_info *axp_config) { int temp; if (data < 80) temp = 30; else if (data < axp_config->pmu_bat_temp_para16) temp = 80; else if (data <= axp_config->pmu_bat_temp_para15) temp = 70 + (axp_config->pmu_bat_temp_para15 - data) * 10 / (axp_config->pmu_bat_temp_para15 - axp_config->pmu_bat_temp_para16); else if (data <= axp_config->pmu_bat_temp_para14) temp = 60 + (axp_config->pmu_bat_temp_para14 - data) * 10 / (axp_config->pmu_bat_temp_para14 - axp_config->pmu_bat_temp_para15); else if (data <= axp_config->pmu_bat_temp_para13) temp = 55 + (axp_config->pmu_bat_temp_para13 - data) * 5 / (axp_config->pmu_bat_temp_para13 - axp_config->pmu_bat_temp_para14); else if (data <= axp_config->pmu_bat_temp_para12) temp = 50 + (axp_config->pmu_bat_temp_para12 - data) * 5 / (axp_config->pmu_bat_temp_para12 - axp_config->pmu_bat_temp_para13); else if (data <= axp_config->pmu_bat_temp_para11) temp = 45 + (axp_config->pmu_bat_temp_para11 - data) * 5 / (axp_config->pmu_bat_temp_para11 - axp_config->pmu_bat_temp_para12); else if (data <= axp_config->pmu_bat_temp_para10) temp = 40 + (axp_config->pmu_bat_temp_para10 - data) * 5 / (axp_config->pmu_bat_temp_para10 - axp_config->pmu_bat_temp_para11); else if (data <= axp_config->pmu_bat_temp_para9) temp = 30 + (axp_config->pmu_bat_temp_para9 - data) * 10 / (axp_config->pmu_bat_temp_para9 - axp_config->pmu_bat_temp_para10); else if (data <= axp_config->pmu_bat_temp_para8) temp = 20 + (axp_config->pmu_bat_temp_para8 - data) * 10 / (axp_config->pmu_bat_temp_para8 - axp_config->pmu_bat_temp_para9); else if (data <= axp_config->pmu_bat_temp_para7) temp = 10 + (axp_config->pmu_bat_temp_para7 - data) * 10 / (axp_config->pmu_bat_temp_para7 - axp_config->pmu_bat_temp_para8); else if (data <= axp_config->pmu_bat_temp_para6) temp = 5 + (axp_config->pmu_bat_temp_para6 - data) * 5 / (axp_config->pmu_bat_temp_para6 - axp_config->pmu_bat_temp_para7); else if (data <= axp_config->pmu_bat_temp_para5) temp = 0 + (axp_config->pmu_bat_temp_para5 - data) * 5 / (axp_config->pmu_bat_temp_para5 - axp_config->pmu_bat_temp_para6); else if (data <= axp_config->pmu_bat_temp_para4) temp = -5 + (axp_config->pmu_bat_temp_para4 - data) * 5 / (axp_config->pmu_bat_temp_para4 - axp_config->pmu_bat_temp_para5); else if (data <= axp_config->pmu_bat_temp_para3) temp = -10 + (axp_config->pmu_bat_temp_para3 - data) * 5 / (axp_config->pmu_bat_temp_para3 - axp_config->pmu_bat_temp_para4); else if (data <= axp_config->pmu_bat_temp_para2) temp = -15 + (axp_config->pmu_bat_temp_para2 - data) * 5 / (axp_config->pmu_bat_temp_para2 - axp_config->pmu_bat_temp_para3); else if (data <= axp_config->pmu_bat_temp_para1) temp = -25 + (axp_config->pmu_bat_temp_para1 - data) * 10 / (axp_config->pmu_bat_temp_para1 - axp_config->pmu_bat_temp_para2); else temp = -25; return temp; } static void axp_usb_ac_check_status(struct axp_charger_dev *chg_dev) { chg_dev->usb_pc_charging = (((CHARGE_USB_20 == axp_usbcurflag) || (CHARGE_USB_30 == axp_usbcurflag)) && (chg_dev->ext_valid)); chg_dev->usb_adapter_charging = ((0 == chg_dev->ac_valid) && (CHARGE_USB_20 != axp_usbcurflag) && (CHARGE_USB_30 != axp_usbcurflag) && (chg_dev->ext_valid)); if (chg_dev->in_short) chg_dev->ac_charging = ((chg_dev->usb_adapter_charging == 0) && (chg_dev->usb_pc_charging == 0) && (chg_dev->ext_valid)); else chg_dev->ac_charging = chg_dev->ac_valid; power_supply_changed(&chg_dev->ac); power_supply_changed(&chg_dev->usb); AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "ac_charging=%d\n", chg_dev->ac_charging); AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "usb_pc_charging=%d\n", chg_dev->usb_pc_charging); AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "usb_adapter_charging=%d\n", chg_dev->usb_adapter_charging); AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "usb_det=%d ac_det=%d\n", chg_dev->usb_det, chg_dev->ac_det); } static void axp_charger_update_usb_state(unsigned long data) { struct axp_charger_dev *chg_dev = (struct axp_charger_dev *)data; axp_usb_ac_check_status(chg_dev); if (chg_dev->bat_det) schedule_delayed_work(&(chg_dev->usbwork), 0); } static void axp_usb(struct work_struct *work) { struct axp_charger_dev *chg_dev = container_of(work, struct axp_charger_dev, usbwork.work); struct axp_usb_info *usb = chg_dev->spy_info->usb; struct axp_ac_info *ac = chg_dev->spy_info->ac; AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "[axp_usb] axp_usbcurflag = %d\n", axp_usbcurflag); axp_charger_update_state(chg_dev); if (chg_dev->in_short) { /* usb and ac in short*/ if (!chg_dev->usb_valid) { /*usb or usb adapter can not be used*/ AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "USB not insert!\n"); usb->set_usb_ihold(chg_dev, 500); } else if (CHARGE_USB_20 == axp_usbcurflag) { if (usb->usb_pc_cur) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_cur %d mA\n", usb->usb_pc_cur); usb->set_usb_ihold(chg_dev, usb->usb_pc_cur); } else { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_cur 500 mA\n"); usb->set_usb_ihold(chg_dev, 500); } } else if (CHARGE_USB_30 == axp_usbcurflag) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_cur 900 mA\n"); usb->set_usb_ihold(chg_dev, 900); } else { /* usb adapter */ if (usb->usb_ad_cur) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_ad_cur %d mA\n", usb->usb_ad_cur); } else { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_ad_cur no limit\n"); } usb->set_usb_ihold(chg_dev, usb->usb_ad_cur); } if (CHARGE_USB_20 == axp_usbvolflag) { if (usb->usb_pc_vol) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_vol %d mV\n", usb->usb_pc_vol); usb->set_usb_vhold(chg_dev, usb->usb_pc_vol); } } else if (CHARGE_USB_30 == axp_usbvolflag) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_vol 4700 mV\n"); usb->set_usb_vhold(chg_dev, 4700); } else { if (usb->usb_ad_vol) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_ad_vol %d mV\n", usb->usb_ad_vol); usb->set_usb_vhold(chg_dev, usb->usb_ad_vol); } } } else { if (!chg_dev->ac_valid && !chg_dev->usb_valid) { /*usb and ac can not be used*/ AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "AC and USB not insert!\n"); usb->set_usb_ihold(chg_dev, 500); } else if (CHARGE_USB_20 == axp_usbcurflag) { if (usb->usb_pc_cur) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_cur %d mA\n", usb->usb_pc_cur); usb->set_usb_ihold(chg_dev, usb->usb_pc_cur); } else { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_cur 500 mA\n"); usb->set_usb_ihold(chg_dev, 500); } } else if (CHARGE_USB_30 == axp_usbcurflag) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_cur 900 mA\n"); usb->set_usb_ihold(chg_dev, 900); } else { if (chg_dev->usb_adapter_charging) { if ((usb->usb_ad_cur)) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set adapter cur %d mA\n", usb->usb_ad_cur); } else { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set adapter cur no limit\n"); } usb->set_usb_ihold(chg_dev, usb->usb_ad_cur); } } if (CHARGE_USB_20 == axp_usbvolflag) { if (usb->usb_pc_vol) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_vol %d mV\n", usb->usb_pc_vol); usb->set_usb_vhold(chg_dev, usb->usb_pc_vol); } } else if (CHARGE_USB_30 == axp_usbvolflag) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set usb_pc_vol 4700 mV\n"); usb->set_usb_vhold(chg_dev, 4700); } else { if (ac->ac_vol) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "set ac_vol %d mV\n", ac->ac_vol); ac->set_ac_vhold(chg_dev, ac->ac_vol); } } } } void axp_battery_update_vol(struct axp_charger_dev *chg_dev) { s32 rest_vol = 0; struct axp_battery_info *batt = chg_dev->spy_info->batt; rest_vol = batt->get_rest_cap(chg_dev); spin_lock(&chg_dev->charger_lock); if (rest_vol > 100) { AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "AXP rest_vol = %d\n", rest_vol); chg_dev->rest_vol = 100; } else { chg_dev->rest_vol = rest_vol; } spin_unlock(&chg_dev->charger_lock); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->rest_vol = %d\n", chg_dev->rest_vol); } static enum power_supply_property axp_battery_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN, POWER_SUPPLY_PROP_CAPACITY, POWER_SUPPLY_PROP_TEMP, }; static enum power_supply_property axp_ac_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, }; static enum power_supply_property axp_usb_props[] = { POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, }; static void axp_battery_check_status(struct axp_charger_dev *chg_dev, union power_supply_propval *val) { if (chg_dev->bat_det) { if (chg_dev->ext_valid) { if (chg_dev->rest_vol == 100) val->intval = POWER_SUPPLY_STATUS_FULL; else if (chg_dev->charging) val->intval = POWER_SUPPLY_STATUS_CHARGING; else val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; } else { val->intval = POWER_SUPPLY_STATUS_DISCHARGING; } } else { val->intval = POWER_SUPPLY_STATUS_FULL; } } static void axp_battery_check_health(struct axp_charger_dev *chg_dev, union power_supply_propval *val) { struct axp_battery_info *batt = chg_dev->spy_info->batt; val->intval = batt->get_bat_health(chg_dev); } static s32 axp_battery_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct axp_charger_dev *chg_dev = container_of(psy, struct axp_charger_dev, batt); s32 ret = 0; switch (psp) { case POWER_SUPPLY_PROP_STATUS: axp_battery_check_status(chg_dev, val); break; case POWER_SUPPLY_PROP_HEALTH: axp_battery_check_health(chg_dev, val); break; case POWER_SUPPLY_PROP_TECHNOLOGY: val->intval = chg_dev->battery_info->technology; break; case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: val->intval = chg_dev->battery_info->voltage_max_design; break; case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: val->intval = chg_dev->battery_info->voltage_min_design; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = chg_dev->bat_vol * 1000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = (chg_dev->bat_cur - chg_dev->bat_discur) * 1000; break; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = psy->name; break; case POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: val->intval = chg_dev->battery_info->energy_full_design; break; case POWER_SUPPLY_PROP_CAPACITY: val->intval = chg_dev->rest_vol; break; case POWER_SUPPLY_PROP_ONLINE: { /* in order to get hardware state, * we must update charger state now. * by sunny at 2012-12-23 11:06:15. */ axp_charger_update_state(chg_dev); val->intval = !chg_dev->bat_current_direction; break; } case POWER_SUPPLY_PROP_PRESENT: val->intval = chg_dev->bat_det; break; case POWER_SUPPLY_PROP_TEMP: axp_charger_get_bat_temp(chg_dev); val->intval = chg_dev->bat_temp; break; default: ret = -EINVAL; break; } return ret; } static s32 axp_ac_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct axp_charger_dev *chg_dev = container_of(psy, struct axp_charger_dev, ac); s32 ret = 0; switch (psp) { case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = psy->name; break; case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_ONLINE: val->intval = (chg_dev->ac_charging || chg_dev->usb_adapter_charging); break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = chg_dev->ac_vol * 1000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = chg_dev->ac_cur * 1000; break; default: ret = -EINVAL; break; } return ret; } static s32 axp_usb_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct axp_charger_dev *chg_dev = container_of(psy, struct axp_charger_dev, usb); s32 ret = 0; switch (psp) { case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = psy->name; break; case POWER_SUPPLY_PROP_PRESENT: case POWER_SUPPLY_PROP_ONLINE: val->intval = chg_dev->usb_pc_charging; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: val->intval = chg_dev->usb_vol * 1000; break; case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = chg_dev->usb_cur * 1000; break; default: ret = -EINVAL; break; } return ret; } static char *supply_list[] = { "battery", }; static void axp_battery_setup_psy(struct axp_charger_dev *chg_dev) { struct power_supply *batt = &chg_dev->batt; struct power_supply *ac = &chg_dev->ac; struct power_supply *usb = &chg_dev->usb; struct power_supply_info *info = chg_dev->battery_info; batt->name = "battery"; batt->use_for_apm = info->use_for_apm; batt->type = POWER_SUPPLY_TYPE_BATTERY; batt->get_property = axp_battery_get_property; batt->properties = axp_battery_props; batt->num_properties = ARRAY_SIZE(axp_battery_props); ac->name = "ac"; ac->type = POWER_SUPPLY_TYPE_MAINS; ac->get_property = axp_ac_get_property; ac->supplied_to = supply_list, ac->num_supplicants = ARRAY_SIZE(supply_list), ac->properties = axp_ac_props; ac->num_properties = ARRAY_SIZE(axp_ac_props); usb->name = "usb"; usb->type = POWER_SUPPLY_TYPE_USB; usb->get_property = axp_usb_get_property; usb->supplied_to = supply_list, usb->num_supplicants = ARRAY_SIZE(supply_list), usb->properties = axp_usb_props; usb->num_properties = ARRAY_SIZE(axp_usb_props); } static void axp_charging_monitor(struct work_struct *work) { struct axp_charger_dev *chg_dev = container_of(work, struct axp_charger_dev, work.work); static s32 pre_rest_vol; static bool pre_bat_curr_dir; axp_charger_update_state(chg_dev); axp_charger_update_value(chg_dev); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->bat_vol = %d\n", chg_dev->bat_vol); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->bat_cur = %d\n", chg_dev->bat_cur); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->bat_discur = %d\n", chg_dev->bat_discur); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->is_charging = %d\n", chg_dev->charging); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->bat_current_direction = %d\n", chg_dev->bat_current_direction); AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "charger->ext_valid = %d\n", chg_dev->ext_valid); if (chg_dev->private_debug) chg_dev->private_debug(chg_dev); axp_battery_update_vol(chg_dev); /* if battery volume changed, inform uevent */ if ((chg_dev->rest_vol - pre_rest_vol) || (chg_dev->bat_current_direction != pre_bat_curr_dir) ) { AXP_DEBUG(AXP_SPLY, chg_dev->chip->pmu_num, "battery vol change: %d->%d\n", pre_rest_vol, chg_dev->rest_vol); pre_rest_vol = chg_dev->rest_vol; pre_bat_curr_dir = chg_dev->bat_current_direction; power_supply_changed(&chg_dev->batt); } /* reschedule for the next time */ schedule_delayed_work(&chg_dev->work, chg_dev->interval); } void axp_change(struct axp_charger_dev *chg_dev) { AXP_DEBUG(AXP_INT, chg_dev->chip->pmu_num, "battery state change\n"); axp_charger_update_state(chg_dev); axp_charger_update_value(chg_dev); if (chg_dev->bat_det && battery_initialized) power_supply_changed(&chg_dev->batt); } EXPORT_SYMBOL_GPL(axp_change); void axp_usbac_in(struct axp_charger_dev *chg_dev) { struct axp_usb_info *usb = chg_dev->spy_info->usb; axp_usbcur(CHARGE_AC); axp_usbvol(CHARGE_AC); AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "axp ac/usb in!\n"); if (timer_pending(&chg_dev->usb_status_timer)) del_timer_sync(&chg_dev->usb_status_timer); /* must limit the current now, * and will again fix it while usb/ac detect finished! */ if (usb->usb_pc_cur) usb->set_usb_ihold(chg_dev, usb->usb_pc_cur); else usb->set_usb_ihold(chg_dev, 500); /* this is about 3.5s, * while the flag set in usb drivers after usb plugged */ mod_timer(&chg_dev->usb_status_timer, jiffies + msecs_to_jiffies(5000)); axp_usb_ac_check_status(chg_dev); } EXPORT_SYMBOL_GPL(axp_usbac_in); void axp_usbac_out(struct axp_charger_dev *chg_dev) { AXP_DEBUG(AXP_CHG, chg_dev->chip->pmu_num, "axp ac/usb out!\n"); if (timer_pending(&chg_dev->usb_status_timer)) del_timer_sync(&chg_dev->usb_status_timer); /* if we plugged usb & ac at the same time, * then unpluged ac quickly while the usb driver * do not finished detecting, * the charger type is error!So delay the charger type report 2s */ mod_timer(&chg_dev->usb_status_timer, jiffies + msecs_to_jiffies(2000)); axp_usb_ac_check_status(chg_dev); } EXPORT_SYMBOL_GPL(axp_usbac_out); void axp_capchange(struct axp_charger_dev *chg_dev) { AXP_DEBUG(AXP_INT, chg_dev->chip->pmu_num, "battery change\n"); axp_charger_update_state(chg_dev); axp_charger_update_value(chg_dev); axp_battery_update_vol(chg_dev); if (chg_dev->bat_det) { AXP_DEBUG(AXP_INT, chg_dev->chip->pmu_num, "rest_vol = %d\n", chg_dev->rest_vol); if (!battery_initialized) { power_supply_register(chg_dev->dev, &chg_dev->batt); schedule_delayed_work(&chg_dev->usbwork, 0); schedule_delayed_work(&chg_dev->work, 0); power_supply_changed(&chg_dev->batt); battery_initialized = true; } } else { if (battery_initialized) { cancel_delayed_work_sync(&chg_dev->work); cancel_delayed_work_sync(&chg_dev->usbwork); power_supply_unregister(&chg_dev->batt); battery_initialized = false; } } } EXPORT_SYMBOL_GPL(axp_capchange); irqreturn_t axp_usb_in_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_usb_connect = 1; axp_change(chg_dev); axp_usbac_in(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_usb_out_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_usb_connect = 0; axp_change(chg_dev); axp_usbac_out(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_ac_in_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_change(chg_dev); axp_usbac_in(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_ac_out_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_change(chg_dev); axp_usbac_out(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_capchange_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_capchange(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_change_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_change(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_low_warning1_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_change(chg_dev); return IRQ_HANDLED; } irqreturn_t axp_low_warning2_isr(int irq, void *data) { struct axp_charger_dev *chg_dev = data; axp_change(chg_dev); return IRQ_HANDLED; } void axp_charger_suspend(struct axp_charger_dev *chg_dev) { struct axp_battery_info *batt = chg_dev->spy_info->batt; axp_charger_update_state(chg_dev); if (chg_dev->bat_det) { schedule_delayed_work(&chg_dev->usbwork, 0); flush_delayed_work(&chg_dev->usbwork); cancel_delayed_work_sync(&chg_dev->work); cancel_delayed_work_sync(&chg_dev->usbwork); batt->set_chg_cur(chg_dev, batt->suspend_chgcur); } } EXPORT_SYMBOL_GPL(axp_charger_suspend); void axp_charger_resume(struct axp_charger_dev *chg_dev) { struct axp_battery_info *batt = chg_dev->spy_info->batt; axp_charger_update_state(chg_dev); axp_charger_update_value(chg_dev); axp_battery_update_vol(chg_dev); batt->set_chg_cur(chg_dev, batt->runtime_chgcur); power_supply_changed(&chg_dev->ac); power_supply_changed(&chg_dev->usb); if (chg_dev->bat_det) { power_supply_changed(&chg_dev->batt); schedule_delayed_work(&chg_dev->work, chg_dev->interval); schedule_delayed_work(&chg_dev->usbwork, msecs_to_jiffies(7 * 1000)); } } EXPORT_SYMBOL_GPL(axp_charger_resume); void axp_charger_shutdown(struct axp_charger_dev *chg_dev) { struct axp_battery_info *batt = chg_dev->spy_info->batt; axp_charger_update_state(chg_dev); if (chg_dev->bat_det) { schedule_delayed_work(&chg_dev->usbwork, 0); flush_delayed_work(&chg_dev->usbwork); cancel_delayed_work_sync(&chg_dev->work); cancel_delayed_work_sync(&chg_dev->usbwork); batt->set_chg_cur(chg_dev, batt->shutdown_chgcur); } } EXPORT_SYMBOL_GPL(axp_charger_shutdown); struct axp_charger_dev *axp_power_supply_register(struct device *dev, struct axp_dev *axp_dev, struct power_supply_info *battery_info, struct axp_supply_info *info) { int ret; struct axp_charger_dev *chg_dev; chg_dev = devm_kzalloc(dev, sizeof(*chg_dev), GFP_KERNEL); if (chg_dev == NULL) return NULL; chg_dev->dev = dev; chg_dev->spy_info = info; chg_dev->chip = axp_dev; chg_dev->battery_info = battery_info; axp_battery_setup_psy(chg_dev); axp_charger_update_state(chg_dev); if (chg_dev->bat_det) { ret = power_supply_register(dev, &chg_dev->batt); if (ret) goto err_ps_register; battery_initialized = true; } ret = power_supply_register(dev, &chg_dev->ac); if (ret) { if (chg_dev->bat_det) { power_supply_unregister(&chg_dev->batt); goto err_ps_register; } } ret = power_supply_register(dev, &chg_dev->usb); if (ret) { power_supply_unregister(&chg_dev->ac); if (chg_dev->bat_det) { power_supply_unregister(&chg_dev->batt); goto err_ps_register; } } spin_lock_init(&chg_dev->charger_lock); if (info->ac->ac_vol && info->ac->set_ac_vhold) info->ac->set_ac_vhold(chg_dev, info->ac->ac_vol); if (info->usb->usb_pc_vol && info->usb->set_usb_vhold) info->usb->set_usb_vhold(chg_dev, info->usb->usb_pc_vol); if (info->batt->runtime_chgcur && info->batt->set_chg_cur) info->batt->set_chg_cur(chg_dev, info->batt->runtime_chgcur); setup_timer(&chg_dev->usb_status_timer, axp_charger_update_usb_state, (unsigned long)chg_dev); INIT_DELAYED_WORK(&(chg_dev->usbwork), axp_usb); axp_usb_ac_check_status(chg_dev); axp_battery_update_vol(chg_dev); spin_lock(&chg_dev->charger_lock); chg_dev->interval = msecs_to_jiffies(10 * 1000); spin_unlock(&chg_dev->charger_lock); INIT_DELAYED_WORK(&chg_dev->work, axp_charging_monitor); if (chg_dev->bat_det) { schedule_delayed_work(&chg_dev->work, chg_dev->interval); schedule_delayed_work(&(chg_dev->usbwork), msecs_to_jiffies(30 * 1000)); } return chg_dev; err_ps_register: return NULL; } EXPORT_SYMBOL_GPL(axp_power_supply_register); void axp_power_supply_unregister(struct axp_charger_dev *chg_dev) { del_timer_sync(&chg_dev->usb_status_timer); power_supply_unregister(&chg_dev->usb); power_supply_unregister(&chg_dev->ac); if (chg_dev->bat_det && battery_initialized) { cancel_delayed_work_sync(&chg_dev->work); cancel_delayed_work_sync(&chg_dev->usbwork); power_supply_unregister(&chg_dev->batt); battery_initialized = false; } } EXPORT_SYMBOL_GPL(axp_power_supply_unregister); int axp_charger_dt_parse(struct device_node *node, struct axp_config_info *axp_config) { if (!of_device_is_available(node)) { pr_err("%s: failed\n", __func__); return -1; } AXP_OF_PROP_READ(pmu_battery_rdc, BATRDC); AXP_OF_PROP_READ(pmu_battery_cap, 4000); AXP_OF_PROP_READ(pmu_batdeten, 1); AXP_OF_PROP_READ(pmu_chg_ic_temp, 0); AXP_OF_PROP_READ(pmu_runtime_chgcur, INTCHGCUR / 1000); AXP_OF_PROP_READ(pmu_suspend_chgcur, 1200); AXP_OF_PROP_READ(pmu_shutdown_chgcur, 1200); AXP_OF_PROP_READ(pmu_init_chgvol, INTCHGVOL / 1000); AXP_OF_PROP_READ(pmu_init_chgend_rate, INTCHGENDRATE); AXP_OF_PROP_READ(pmu_init_chg_enabled, 1); AXP_OF_PROP_READ(pmu_init_bc_en, 0); AXP_OF_PROP_READ(pmu_init_adc_freq, INTADCFREQ); AXP_OF_PROP_READ(pmu_init_adcts_freq, INTADCFREQC); AXP_OF_PROP_READ(pmu_init_chg_pretime, INTCHGPRETIME); AXP_OF_PROP_READ(pmu_init_chg_csttime, INTCHGCSTTIME); AXP_OF_PROP_READ(pmu_batt_cap_correct, 1); AXP_OF_PROP_READ(pmu_chg_end_on_en, 0); AXP_OF_PROP_READ(ocv_coulumb_100, 0); AXP_OF_PROP_READ(pmu_discharge_ltf, 3200); AXP_OF_PROP_READ(pmu_discharge_htf, 237); AXP_OF_PROP_READ(pmu_bat_para1, OCVREG0); AXP_OF_PROP_READ(pmu_bat_para2, OCVREG1); AXP_OF_PROP_READ(pmu_bat_para3, OCVREG2); AXP_OF_PROP_READ(pmu_bat_para4, OCVREG3); AXP_OF_PROP_READ(pmu_bat_para5, OCVREG4); AXP_OF_PROP_READ(pmu_bat_para6, OCVREG5); AXP_OF_PROP_READ(pmu_bat_para7, OCVREG6); AXP_OF_PROP_READ(pmu_bat_para8, OCVREG7); AXP_OF_PROP_READ(pmu_bat_para9, OCVREG8); AXP_OF_PROP_READ(pmu_bat_para10, OCVREG9); AXP_OF_PROP_READ(pmu_bat_para11, OCVREGA); AXP_OF_PROP_READ(pmu_bat_para12, OCVREGB); AXP_OF_PROP_READ(pmu_bat_para13, OCVREGC); AXP_OF_PROP_READ(pmu_bat_para14, OCVREGD); AXP_OF_PROP_READ(pmu_bat_para15, OCVREGE); AXP_OF_PROP_READ(pmu_bat_para16, OCVREGF); AXP_OF_PROP_READ(pmu_bat_para17, OCVREG10); AXP_OF_PROP_READ(pmu_bat_para18, OCVREG11); AXP_OF_PROP_READ(pmu_bat_para19, OCVREG12); AXP_OF_PROP_READ(pmu_bat_para20, OCVREG13); AXP_OF_PROP_READ(pmu_bat_para21, OCVREG14); AXP_OF_PROP_READ(pmu_bat_para22, OCVREG15); AXP_OF_PROP_READ(pmu_bat_para23, OCVREG16); AXP_OF_PROP_READ(pmu_bat_para24, OCVREG17); AXP_OF_PROP_READ(pmu_bat_para25, OCVREG18); AXP_OF_PROP_READ(pmu_bat_para26, OCVREG19); AXP_OF_PROP_READ(pmu_bat_para27, OCVREG1A); AXP_OF_PROP_READ(pmu_bat_para28, OCVREG1B); AXP_OF_PROP_READ(pmu_bat_para29, OCVREG1C); AXP_OF_PROP_READ(pmu_bat_para30, OCVREG1D); AXP_OF_PROP_READ(pmu_bat_para31, OCVREG1E); AXP_OF_PROP_READ(pmu_bat_para32, OCVREG1F); AXP_OF_PROP_READ(pmu_ac_vol, 4400); AXP_OF_PROP_READ(pmu_usbpc_vol, 4400); AXP_OF_PROP_READ(pmu_ac_cur, 0); AXP_OF_PROP_READ(pmu_usbpc_cur, 0); AXP_OF_PROP_READ(pmu_pwroff_vol, 3300); AXP_OF_PROP_READ(pmu_pwron_vol, 2900); AXP_OF_PROP_READ(pmu_battery_warning_level1, 15); AXP_OF_PROP_READ(pmu_battery_warning_level2, 0); AXP_OF_PROP_READ(pmu_restvol_adjust_time, 30); AXP_OF_PROP_READ(pmu_ocv_cou_adjust_time, 60); AXP_OF_PROP_READ(pmu_chgled_func, 0); AXP_OF_PROP_READ(pmu_chgled_type, 0); AXP_OF_PROP_READ(pmu_bat_temp_enable, 0); AXP_OF_PROP_READ(pmu_bat_charge_ltf, 0xA5); AXP_OF_PROP_READ(pmu_bat_charge_htf, 0x1F); AXP_OF_PROP_READ(pmu_bat_shutdown_ltf, 0xFC); AXP_OF_PROP_READ(pmu_bat_shutdown_htf, 0x16); AXP_OF_PROP_READ(pmu_bat_temp_para1, 0); AXP_OF_PROP_READ(pmu_bat_temp_para2, 0); AXP_OF_PROP_READ(pmu_bat_temp_para3, 0); AXP_OF_PROP_READ(pmu_bat_temp_para4, 0); AXP_OF_PROP_READ(pmu_bat_temp_para5, 0); AXP_OF_PROP_READ(pmu_bat_temp_para6, 0); AXP_OF_PROP_READ(pmu_bat_temp_para7, 0); AXP_OF_PROP_READ(pmu_bat_temp_para8, 0); AXP_OF_PROP_READ(pmu_bat_temp_para9, 0); AXP_OF_PROP_READ(pmu_bat_temp_para10, 0); AXP_OF_PROP_READ(pmu_bat_temp_para11, 0); AXP_OF_PROP_READ(pmu_bat_temp_para12, 0); AXP_OF_PROP_READ(pmu_bat_temp_para13, 0); AXP_OF_PROP_READ(pmu_bat_temp_para14, 0); AXP_OF_PROP_READ(pmu_bat_temp_para15, 0); AXP_OF_PROP_READ(pmu_bat_temp_para16, 0); AXP_OF_PROP_READ(pmu_bat_unused, 0); AXP_OF_PROP_READ(power_start, 0); AXP_OF_PROP_READ(pmu_ocv_en, 1); AXP_OF_PROP_READ(pmu_cou_en, 1); AXP_OF_PROP_READ(pmu_update_min_time, UPDATEMINTIME); return 0; } EXPORT_SYMBOL_GPL(axp_charger_dt_parse);