#include <linux/mfd/abx500.h>
#include <linux/mfd/abx500/ux500_chargalg.h>
#include <linux/mfd/abx500/ab8500-bm.h>
+#include <linux/notifier.h>
/* Watchdog kick interval */
#define CHG_WD_INTERVAL (6 * HZ)
/* End-of-charge criteria counter */
#define EOC_COND_CNT 10
+/* Plus margin for the low battery threshold */
+#define BAT_PLUS_MARGIN (100)
+
#define to_abx500_chargalg_device_info(x) container_of((x), \
struct abx500_chargalg, chargalg_psy);
STATE_HW_TEMP_PROTECT_INIT,
STATE_HW_TEMP_PROTECT,
STATE_NORMAL_INIT,
+ STATE_USB_PP_PRE_CHARGE,
STATE_NORMAL,
STATE_WAIT_FOR_RECHARGE_INIT,
STATE_WAIT_FOR_RECHARGE,
"HW_TEMP_PROTECT_INIT",
"HW_TEMP_PROTECT",
"NORMAL_INIT",
+ "USB_PP_PRE_CHARGE",
"NORMAL",
"WAIT_FOR_RECHARGE_INIT",
"WAIT_FOR_RECHARGE",
* @batt_data: data of the battery
* @susp_status: current charger suspension status
* @bm: Platform specific battery management information
+ * @parent: pointer to the struct abx500
* @chargalg_psy: structure that holds the battery properties exposed by
* the charging algorithm
* @events: structure for information about events triggered
struct abx500_chargalg_charger_info chg_info;
struct abx500_chargalg_battery_data batt_data;
struct abx500_chargalg_suspension_status susp_status;
+ struct ab8500 *parent;
struct abx500_bm_data *bm;
struct power_supply chargalg_psy;
struct ux500_charger *ac_chg;
struct kobject chargalg_kobject;
};
+/*External charger prepare notifier*/
+BLOCKING_NOTIFIER_HEAD(charger_notifier_list);
+
/* Main battery properties */
static enum power_supply_property abx500_chargalg_props[] = {
POWER_SUPPLY_PROP_STATUS,
di->charge_state = state;
}
+static int abx500_chargalg_check_charger_enable(struct abx500_chargalg *di)
+{
+ switch (di->charge_state) {
+ case STATE_NORMAL:
+ case STATE_MAINTENANCE_A:
+ case STATE_MAINTENANCE_B:
+ break;
+ default:
+ return 0;
+ }
+
+ if (di->chg_info.charger_type & USB_CHG) {
+ return di->usb_chg->ops.check_enable(di->usb_chg,
+ di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+ di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+ } else if ((di->chg_info.charger_type & AC_CHG) &&
+ !(di->ac_chg->external)) {
+ return di->ac_chg->ops.check_enable(di->ac_chg,
+ di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
+ di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
+ }
+ return 0;
+}
+
/**
* abx500_chargalg_check_charger_connection() - Check charger connection change
* @di: pointer to the abx500_chargalg structure
static int abx500_chargalg_ac_en(struct abx500_chargalg *di, int enable,
int vset, int iset)
{
+ static int abx500_chargalg_ex_ac_enable_toggle;
+
if (!di->ac_chg || !di->ac_chg->ops.enable)
return -ENXIO;
di->chg_info.ac_iset = iset;
di->chg_info.ac_vset = vset;
+ /* Enable external charger */
+ if (enable && di->ac_chg->external &&
+ !abx500_chargalg_ex_ac_enable_toggle) {
+ blocking_notifier_call_chain(&charger_notifier_list,
+ 0, di->dev);
+ abx500_chargalg_ex_ac_enable_toggle++;
+ }
+
return di->ac_chg->ops.enable(di->ac_chg, enable, vset, iset);
}
return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset);
}
+ /**
+ * ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path
+ * @di: pointer to the abx500_chargalg structure
+ * @enable: power path enable/disable
+ *
+ * The USB power path will be enable/ disable
+ */
+static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable)
+{
+ if (!di->usb_chg || !di->usb_chg->ops.pp_enable)
+ return -ENXIO;
+
+ return di->usb_chg->ops.pp_enable(di->usb_chg, enable);
+}
+
+/**
+ * ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge
+ * @di: pointer to the abx500_chargalg structure
+ * @enable: USB pre-charge enable/disable
+ *
+ * The USB USB pre-charge will be enable/ disable
+ */
+static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di,
+ bool enable)
+{
+ if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable)
+ return -ENXIO;
+
+ return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable);
+}
+
/**
* abx500_chargalg_update_chg_curr() - Update charger current
* @di: pointer to the abx500_chargalg structure
di->batt_data.avg_curr > 0) {
if (++di->eoc_cnt >= EOC_COND_CNT) {
di->eoc_cnt = 0;
+ if ((di->chg_info.charger_type & USB_CHG) &&
+ (di->usb_chg->power_path))
+ ab8540_chargalg_usb_pp_en(di, true);
di->charge_status = POWER_SUPPLY_STATUS_FULL;
di->maintenance_chg = true;
dev_dbg(di->dev, "EOC reached!\n");
static void abx500_chargalg_algorithm(struct abx500_chargalg *di)
{
int charger_status;
+ int ret;
/* Collect data from all power_supply class devices */
class_for_each_device(power_supply_class, NULL,
abx500_chargalg_check_charger_voltage(di);
charger_status = abx500_chargalg_check_charger_connection(di);
+
+ if (is_ab8500(di->parent)) {
+ ret = abx500_chargalg_check_charger_enable(di);
+ if (ret < 0)
+ dev_err(di->dev, "Checking charger is enabled error"
+ ": Returned Value %d\n", ret);
+ }
+
/*
* First check if we have a charger connected.
* Also we don't allow charging of unknown batteries if configured
break;
case STATE_NORMAL_INIT:
+ if ((di->chg_info.charger_type & USB_CHG) &&
+ di->usb_chg->power_path) {
+ if (di->batt_data.volt >
+ (di->bm->fg_params->lowbat_threshold +
+ BAT_PLUS_MARGIN)) {
+ ab8540_chargalg_usb_pre_chg_en(di, false);
+ ab8540_chargalg_usb_pp_en(di, false);
+ } else {
+ ab8540_chargalg_usb_pp_en(di, true);
+ ab8540_chargalg_usb_pre_chg_en(di, true);
+ abx500_chargalg_state_to(di,
+ STATE_USB_PP_PRE_CHARGE);
+ break;
+ }
+ }
+
abx500_chargalg_start_charging(di,
di->bm->bat_type[di->bm->batt_id].normal_vol_lvl,
di->bm->bat_type[di->bm->batt_id].normal_cur_lvl);
break;
+ case STATE_USB_PP_PRE_CHARGE:
+ if (di->batt_data.volt >
+ (di->bm->fg_params->lowbat_threshold +
+ BAT_PLUS_MARGIN))
+ abx500_chargalg_state_to(di, STATE_NORMAL_INIT);
+ break;
+
case STATE_NORMAL:
handle_maxim_chg_curr(di);
if (di->charge_status == POWER_SUPPLY_STATUS_FULL &&
}
}
- /* get device struct */
+ /* get device struct and parent */
di->dev = &pdev->dev;
+ di->parent = dev_get_drvdata(pdev->dev.parent);
/* chargalg supply */
di->chargalg_psy.name = "abx500_chargalg";