/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
+ * Copyright (C) 2019 XiaoMi, Inc.
*
* 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
#include <linux/qpnp/qpnp-revid.h>
#include "fg-core.h"
#include "fg-reg.h"
+#include <linux/ktime.h>
#define FG_GEN3_DEV_NAME "qcom,fg-gen3"
},
};
-static int fg_gen3_debug_mask;
+static int fg_gen3_debug_mask = FG_IRQ | FG_STATUS;
module_param_named(
debug_mask, fg_gen3_debug_mask, int, S_IRUSR | S_IWUSR
);
#define FULL_CAPACITY 100
#define FULL_SOC_RAW 255
+#define FULL_SOC_REPORT_THR 250
static int fg_get_msoc(struct fg_chip *chip, int *msoc)
{
int rc;
if (rc < 0)
return rc;
- /*
- * To have better endpoints for 0 and 100, it is good to tune the
- * calculation discarding values 0 and 255 while rounding off. Rest
- * of the values 1-254 will be scaled to 1-99. DIV_ROUND_UP will not
- * be suitable here as it rounds up any value higher than 252 to 100.
- */
- if (*msoc == FULL_SOC_RAW)
- *msoc = 100;
- else if (*msoc == 0)
- *msoc = 0;
- else
- *msoc = DIV_ROUND_CLOSEST((*msoc - 1) * (FULL_CAPACITY - 2),
- FULL_SOC_RAW - 2) + 1;
+ if ((*msoc >= FULL_SOC_REPORT_THR)
+ && (*msoc < FULL_SOC_RAW) && chip->report_full) {
+ *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW) + 1;
+ if (*msoc >= FULL_CAPACITY)
+ *msoc = FULL_CAPACITY;
+ } else
+ *msoc = DIV_ROUND_CLOSEST(*msoc * FULL_CAPACITY, FULL_SOC_RAW);
+
return 0;
}
chip->bp.vbatt_full_mv = -EINVAL;
}
+ rc = of_property_read_u32(profile_node, "qcom,nom-batt-capacity-mah",
+ &chip->bp.nom_cap_uah);
+ if (rc < 0) {
+ pr_err("battery nominal capacity unavailable, rc:%d\n", rc);
+ chip->bp.nom_cap_uah = -EINVAL;
+ }
+
data = of_get_property(profile_node, "qcom,fg-profile-data", &len);
if (!data) {
pr_err("No profile data available\n");
rc);
return;
}
-
+/*
prop.intval = chip->bp.fastchg_curr_ma * 1000;
rc = power_supply_set_property(chip->batt_psy,
POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &prop);
rc);
return;
}
-
+*/
fg_dbg(chip, FG_STATUS, "Notified charger on float voltage and FCC\n");
}
return 0;
}
+#define DEFAULT_RECHARGE_SOC_RAW 0xfd
static int fg_set_recharge_soc(struct fg_chip *chip, int recharge_soc)
{
u8 buf;
return 0;
fg_encode(chip->sp, FG_SRAM_RECHARGE_SOC_THR, recharge_soc, &buf);
+
+ if ((buf <= DEFAULT_RECHARGE_SOC_RAW)
+ && (chip->health != POWER_SUPPLY_HEALTH_WARM))
+ buf += 1;
+
rc = fg_sram_write(chip,
chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_word,
chip->sp[FG_SRAM_RECHARGE_SOC_THR].addr_byte, &buf,
* the recharge SOC threshold based on the monotonic SOC at which
* the charge termination had happened.
*/
- if (is_input_present(chip)) {
- if (chip->charge_done) {
- if (!chip->recharge_soc_adjusted) {
- /* Get raw monotonic SOC for calculation */
- rc = fg_get_msoc(chip, &msoc);
- if (rc < 0) {
- pr_err("Error in getting msoc, rc=%d\n",
- rc);
- return rc;
- }
-
- /* Adjust the recharge_soc threshold */
- new_recharge_soc = msoc - (FULL_CAPACITY -
- recharge_soc);
- chip->recharge_soc_adjusted = true;
- } else {
- /* adjusted already, do nothing */
- return 0;
- }
- } else {
- if (!chip->recharge_soc_adjusted)
- return 0;
-
- if (chip->health != POWER_SUPPLY_HEALTH_GOOD)
- return 0;
-
- /* Restore the default value */
- new_recharge_soc = recharge_soc;
- chip->recharge_soc_adjusted = false;
+ if (is_input_present(chip) && !chip->recharge_soc_adjusted
+ && chip->charge_done) {
+ if (chip->health == POWER_SUPPLY_HEALTH_GOOD)
+ return 0;
+ /* Get raw monotonic SOC for calculation */
+ rc = fg_get_msoc(chip, &msoc);
+ if (rc < 0) {
+ pr_err("Error in getting msoc, rc=%d\n", rc);
+ return rc;
}
- } else {
+
+ /* Adjust the recharge_soc threshold */
+ new_recharge_soc = msoc - (FULL_CAPACITY - recharge_soc);
+ chip->recharge_soc_adjusted = true;
+ } else if ((!is_input_present(chip) || chip->health == POWER_SUPPLY_HEALTH_GOOD)
+ && chip->recharge_soc_adjusted) {
/* Restore the default value */
new_recharge_soc = recharge_soc;
chip->recharge_soc_adjusted = false;
+ } else {
+ return 0;
}
rc = fg_set_recharge_soc(chip, new_recharge_soc);
int delay_ms;
union power_supply_propval prop = {0, };
int online = 0;
+ int msoc = 0;
if (usb_psy_initialized(chip)) {
rc = power_supply_get_property(chip->usb_psy,
online = online || prop.intval;
}
+ chip->charge_done = prop.intval;
+ if (chip->charge_done && !chip->report_full) {
+ chip->report_full = true;
+ } else if (!chip->charge_done && chip->report_full) {
+ rc = fg_get_msoc_raw(chip, &msoc);
+ if (rc < 0)
+ pr_err("Error in getting msoc, rc=%d\n", rc);
+ if (msoc < FULL_SOC_REPORT_THR)
+ chip->report_full = false;
+ }
+
+ rc = power_supply_get_property(chip->batt_psy,
+ POWER_SUPPLY_PROP_HEALTH, &prop);
+ if (rc < 0) {
+ pr_err("Error in getting battery health, rc=%d\n", rc);
+ return;
+ }
+ chip->health = prop.intval;
if (chip->online_status == online)
return;
static int fg_get_cycle_count(struct fg_chip *chip)
{
- int count;
+ int count = 0;
+ int i = 0;
if (!chip->cyc_ctr.en)
return 0;
return -EINVAL;
mutex_lock(&chip->cyc_ctr.lock);
- count = chip->cyc_ctr.count[chip->cyc_ctr.id - 1];
+ for (i = 0; i < BUCKET_COUNT; i++)
+ count += chip->cyc_ctr.count[i];
+ count /= BUCKET_COUNT;
mutex_unlock(&chip->cyc_ctr.lock);
return count;
}
+static int fg_set_cycle_count(struct fg_chip *chip, int value)
+{
+ int rc = 0;
+ int i = 0;
+ u8 data[2];
+
+ for (i = 0; i < BUCKET_COUNT; i++) {
+ data[0] = value & 0xFF;
+ data[1] = value >> 8;
+
+ rc = fg_sram_write(chip, CYCLE_COUNT_WORD + (i / 2),
+ CYCLE_COUNT_OFFSET + (i % 2) * 2, data, 2,
+ FG_IMA_DEFAULT);
+ if (rc < 0)
+ pr_err("failed to write BATT_CYCLE[%d] rc=%d\n",
+ i, rc);
+ else
+ chip->cyc_ctr.count[i] = value;
+ }
+ return rc;
+}
+
static void status_change_work(struct work_struct *work)
{
struct fg_chip *chip = container_of(work,
struct fg_chip, status_change_work);
union power_supply_propval prop = {0, };
int rc, batt_temp;
+ int msoc = 0;
if (!batt_psy_initialized(chip)) {
fg_dbg(chip, FG_STATUS, "Charger not available?!\n");
goto out;
}
+ chip->prev_charge_status = chip->charge_status;
chip->charge_status = prop.intval;
rc = power_supply_get_property(chip->batt_psy,
POWER_SUPPLY_PROP_CHARGE_TYPE, &prop);
}
chip->charge_done = prop.intval;
+ if (chip->charge_done && !chip->report_full) {
+ chip->report_full = true;
+ } else if (!chip->charge_done && chip->report_full) {
+ rc = fg_get_msoc_raw(chip, &msoc);
+ if (rc < 0)
+ pr_err("Error in getting msoc, rc=%d\n", rc);
+ if (msoc < FULL_SOC_REPORT_THR)
+ chip->report_full = false;
+ }
+
fg_cycle_counter_update(chip);
fg_cap_learning_update(chip);
}
fg_ttf_update(chip);
- chip->prev_charge_status = chip->charge_status;
+
out:
fg_dbg(chip, FG_POWER_SUPPLY, "charge_status:%d charge_type:%d charge_done:%d\n",
chip->charge_status, chip->charge_type, chip->charge_done);
}
}
- fg_dbg(chip, FG_IRQ, "msoc: %d last_msoc: %d maint_soc: %d delta_soc: %d\n",
- msoc, chip->last_msoc, chip->maint_soc, chip->delta_soc);
+ if (msoc != chip->last_msoc)
+ power_supply_changed(chip->batt_psy);
+
+ pr_err("msoc: %d last_msoc: %d maint_soc: %d delta_soc: %d\n",
+ msoc, chip->last_msoc, chip->maint_soc, chip->delta_soc);
+
chip->last_msoc = msoc;
out:
mutex_unlock(&chip->charge_full_lock);
switch (psp) {
case POWER_SUPPLY_PROP_CAPACITY:
rc = fg_get_prop_capacity(chip, &pval->intval);
+
+ if (chip->param.batt_soc >= 0)
+ pval->intval = chip->param.batt_soc;
break;
case POWER_SUPPLY_PROP_CAPACITY_RAW:
rc = fg_get_msoc_raw(chip, &pval->intval);
rc = fg_get_sram_prop(chip, FG_SRAM_OCV, &pval->intval);
break;
case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
- pval->intval = chip->cl.nom_cap_uah;
+ if (-EINVAL != chip->bp.nom_cap_uah)
+ pval->intval = chip->bp.nom_cap_uah * 1000;
+ else
+ pval->intval = chip->cl.nom_cap_uah;
break;
case POWER_SUPPLY_PROP_RESISTANCE_ID:
pval->intval = chip->batt_id_ohms;
return -EINVAL;
}
break;
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
+ rc = fg_set_cycle_count(chip, pval->intval);
+ pr_info("Cycle count is modified to %d by userspace\n", pval->intval);
+ break;
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
rc = fg_set_constant_chg_voltage(chip, pval->intval);
break;
{
switch (psp) {
case POWER_SUPPLY_PROP_CYCLE_COUNT_ID:
+ case POWER_SUPPLY_PROP_CYCLE_COUNT:
case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
case POWER_SUPPLY_PROP_CC_STEP:
case POWER_SUPPLY_PROP_CC_STEP_SEL:
return rc;
}
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
rc = fg_sram_masked_write(chip, 19, 0, 0x08, 0x08, FG_IMA_DEFAULT);
if (rc < 0) {
pr_err("Error in writing cc soc auto clear rc=%d\n", rc);
}
-#endif
fg_encode(chip->sp, FG_SRAM_ESR_PULSE_THRESH,
chip->dt.esr_pulse_thresh_ma, buf);
if (rc < 0)
pr_err("Error in adjusting timebase, rc=%d\n", rc);
- if (batt_psy_initialized(chip))
- power_supply_changed(chip->batt_psy);
-
return IRQ_HANDLED;
}
rc);
}
+ pr_info("%s: jeita threshold %d, %d, %d, %d\n", __func__,
+ chip->dt.jeita_thresholds[JEITA_COLD],
+ chip->dt.jeita_thresholds[JEITA_COOL],
+ chip->dt.jeita_thresholds[JEITA_WARM],
+ chip->dt.jeita_thresholds[JEITA_HOT]);
+
if (of_property_count_elems_of_size(node,
"qcom,battery-thermal-coefficients",
sizeof(u8)) == BATT_THERM_NUM_COEFFS) {
return 0;
}
+#define SOC_WORK_MS 20000
+
+static void soc_work_fn(struct work_struct *work)
+{
+ struct fg_chip *chip = container_of(work,
+ struct fg_chip,
+ soc_work.work);
+ int msoc = 0, esr_uohms = 0, curr_ua = 0, volt_uv = 0, temp = 0;
+ int soc = 0;
+ int cycle_count = 0;
+ int rc;
+ static int prev_soc = -EINVAL;
+
+ rc = fg_get_prop_capacity(chip, &soc);
+ if (rc < 0)
+ pr_err("Error in getting capacity, rc=%d\n", rc);
+
+ rc = fg_get_msoc_raw(chip, &msoc);
+ if (rc < 0)
+ pr_err("Error in getting msoc, rc=%d\n", rc);
+
+ rc = fg_get_sram_prop(chip, FG_SRAM_ESR, &esr_uohms);
+ if (rc < 0)
+ pr_err("failed to get ESR, rc=%d\n", rc);
+
+ fg_get_battery_current(chip, &curr_ua);
+ if (rc < 0)
+ pr_err("failed to get current, rc=%d\n", rc);
+
+ rc = fg_get_battery_voltage(chip, &volt_uv);
+ if (rc < 0)
+ pr_err("failed to get voltage, rc=%d\n", rc);
+
+ rc = fg_get_battery_temp(chip, &temp);
+ if (rc < 0)
+ pr_err("failed to get temp, rc=%d\n", rc);
+
+ cycle_count = fg_get_cycle_count(chip);
+
+ pr_info("adjust_soc: s %d r %d i %d v %d t %d cc %d m 0x%02x\n",
+ soc, esr_uohms, curr_ua, volt_uv, temp, cycle_count, msoc);
+
+ if (temp < 450 && chip->last_batt_temp >= 450) {
+ /* follow the way that fg_notifier_cb use wake lock */
+ pm_stay_awake(chip->dev);
+ schedule_work(&chip->status_change_work);
+ }
+
+ chip->last_batt_temp = temp;
+
+ /* if soc changes, report power supply change uevent */
+ if (soc != prev_soc) {
+ if (chip->fg_psy)
+ power_supply_changed(chip->fg_psy);
+ prev_soc = soc;
+ }
+
+ schedule_delayed_work(
+ &chip->soc_work, msecs_to_jiffies(SOC_WORK_MS));
+}
+
+static int calculate_delta_time(struct timespec *time_stamp, int *delta_time_s)
+{
+ struct timespec now_time;
+
+ /* default to delta time = 0 if anything fails */
+ *delta_time_s = 0;
+
+ get_monotonic_boottime(&now_time);
+
+ *delta_time_s = (now_time.tv_sec - time_stamp->tv_sec);
+
+ /* remember this time */
+ *time_stamp = now_time;
+ return 0;
+}
+
+static int calculate_average_current(struct fg_chip *chip)
+{
+ int i;
+ int iavg_ma = chip->param.batt_ma;
+
+ /* only continue if ibat has changed */
+ if (chip->param.batt_ma == chip->param.batt_ma_prev)
+ goto unchanged;
+ else
+ chip->param.batt_ma_prev = chip->param.batt_ma;
+
+ chip->param.batt_ma_avg_samples[chip->param.samples_index] = iavg_ma;
+ chip->param.samples_index = (chip->param.samples_index + 1) % BATT_MA_AVG_SAMPLES;
+ chip->param.samples_num++;
+
+ if (chip->param.samples_num >= BATT_MA_AVG_SAMPLES)
+ chip->param.samples_num = BATT_MA_AVG_SAMPLES;
+
+ if (chip->param.samples_num) {
+ iavg_ma = 0;
+ /* maintain a AVG_SAMPLES sample average of ibat */
+ for (i = 0; i < chip->param.samples_num; i++) {
+ pr_debug("iavg_samples_ma[%d] = %d\n", i, chip->param.batt_ma_avg_samples[i]);
+ iavg_ma += chip->param.batt_ma_avg_samples[i];
+ }
+ chip->param.batt_ma_avg = DIV_ROUND_CLOSEST(iavg_ma, chip->param.samples_num);
+ }
+
+unchanged:
+ pr_info("current_now_ma=%d averaged_iavg_ma=%d\n",
+ chip->param.batt_ma, chip->param.batt_ma_avg);
+ return chip->param.batt_ma_avg;
+}
+
+static void fg_battery_soc_smooth_tracking(struct fg_chip *chip)
+{
+ int delta_time = 0;
+ int soc_changed;
+ int last_batt_soc = chip->param.batt_soc;
+ int time_since_last_change_sec;
+
+ struct timespec last_change_time = chip->param.last_soc_change_time;
+
+ calculate_delta_time(&last_change_time, &time_since_last_change_sec);
+
+ if (chip->param.batt_temp > 150) {
+ /* Battery in normal temperture */
+ if (chip->param.batt_ma < 0 ||
+ (abs(chip->param.batt_raw_soc - chip->param.batt_soc) > 2))
+ delta_time = time_since_last_change_sec / 30;
+ else
+ delta_time = time_since_last_change_sec / 60;
+ } else {
+ /* Battery in low temperture */
+ calculate_average_current(chip);
+ /* Calculated average current > 1000mA */
+ if (chip->param.batt_ma_avg > 1000000 ||
+ (abs(chip->param.batt_raw_soc - chip->param.batt_soc) > 2))
+ /* Heavy loading current, ignore battery soc limit*/
+ delta_time = time_since_last_change_sec / 10;
+ else
+ delta_time = time_since_last_change_sec / 20;
+ }
+
+ if (delta_time < 0)
+ delta_time = 0;
+
+ soc_changed = min(1, delta_time);
+
+ if (last_batt_soc >= 0) {
+ if (last_batt_soc != 100
+ && chip->param.batt_raw_soc >= 95
+ && chip->charge_status == POWER_SUPPLY_STATUS_FULL)
+
+ last_batt_soc = chip->param.update_now ?
+ 100 : last_batt_soc + soc_changed;
+ else if (last_batt_soc < chip->param.batt_raw_soc &&
+ chip->param.batt_ma < 0)
+ /* Battery in charging status
+ * update the soc when resuming device
+ */
+ last_batt_soc = chip->param.update_now ?
+ chip->param.batt_raw_soc : last_batt_soc + soc_changed;
+ else if (last_batt_soc > chip->param.batt_raw_soc
+ && chip->param.batt_ma > 0)
+ /* Battery in discharging status
+ * update the soc when resuming device
+ */
+ last_batt_soc = chip->param.update_now ?
+ chip->param.batt_raw_soc : last_batt_soc - soc_changed;
+
+ chip->param.update_now = false;
+ } else {
+ last_batt_soc = chip->param.batt_raw_soc;
+ }
+
+ if (chip->param.batt_soc != last_batt_soc) {
+ chip->param.batt_soc = last_batt_soc;
+ chip->param.last_soc_change_time = last_change_time;
+ if (chip->batt_psy)
+ power_supply_changed(chip->batt_psy);
+ }
+
+ pr_info("soc:%d, last_soc:%d, raw_soc:%d, soc_changed:%d.\n",
+ chip->param.batt_soc, last_batt_soc,
+ chip->param.batt_raw_soc, soc_changed);
+}
+
+#define MONITOR_SOC_WAIT_MS 1000
+#define MONITOR_SOC_WAIT_PER_MS 10000
+static void soc_monitor_work(struct work_struct *work)
+{
+ int rc;
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct fg_chip *chip = container_of(dwork,
+ struct fg_chip, soc_monitor_work);
+
+ rc = fg_get_battery_current(chip, &chip->param.batt_ma);
+ if (rc < 0)
+ pr_err("failded to get battery current, rc=%d\n", rc);
+
+ rc = fg_get_prop_capacity(chip, &chip->param.batt_raw_soc);
+ if (rc < 0)
+ pr_err("failed to get battery capacity, rc=%d\n", rc);
+
+ rc = fg_get_battery_temp(chip, &chip->param.batt_temp);
+ if (rc < 0)
+ pr_err("failed to get battery temperature, rc=%d\n", rc);
+
+ if (chip->soc_reporting_ready)
+ fg_battery_soc_smooth_tracking(chip);
+
+ pr_info("soc:%d, raw_soc:%d, c:%d, s:%d\n",
+ chip->param.batt_soc, chip->param.batt_raw_soc,
+ chip->param.batt_ma, chip->charge_status);
+
+ schedule_delayed_work(&chip->soc_monitor_work,
+ msecs_to_jiffies(MONITOR_SOC_WAIT_PER_MS));
+}
+
static void fg_cleanup(struct fg_chip *chip)
{
alarm_try_to_cancel(&chip->esr_filter_alarm);
spin_lock_init(&chip->suspend_lock);
init_completion(&chip->soc_update);
init_completion(&chip->soc_ready);
+ INIT_DELAYED_WORK(&chip->soc_monitor_work, soc_monitor_work);
INIT_DELAYED_WORK(&chip->profile_load_work, profile_load_work);
INIT_WORK(&chip->status_change_work, status_change_work);
INIT_DELAYED_WORK(&chip->ttf_work, ttf_work);
INIT_DELAYED_WORK(&chip->sram_dump_work, sram_dump_work);
+ INIT_DELAYED_WORK(&chip->soc_work, soc_work_fn);
INIT_WORK(&chip->esr_filter_work, esr_filter_work);
alarm_init(&chip->esr_filter_alarm, ALARM_BOOTTIME,
fg_esr_filter_alarm_cb);
device_init_wakeup(chip->dev, true);
schedule_delayed_work(&chip->profile_load_work, 0);
+ schedule_delayed_work(&chip->soc_work, 0);
+
+ chip->param.batt_soc = -EINVAL;
+ schedule_delayed_work(&chip->soc_monitor_work,
+ msecs_to_jiffies(MONITOR_SOC_WAIT_MS));
pr_debug("FG GEN3 driver probed successfully\n");
return 0;
spin_lock(&chip->suspend_lock);
chip->suspended = false;
+ chip->param.update_now = true;
spin_unlock(&chip->suspend_lock);
+ schedule_delayed_work(&chip->soc_monitor_work,
+ msecs_to_jiffies(MONITOR_SOC_WAIT_MS));
+
return 0;
}
/* Copyright (c) 2016-2019 The Linux Foundation. All rights reserved.
- *
+ * Copyright (C) 2019 XiaoMi, Inc.
* 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.
.name = "fast charge current",
.reg = FAST_CHARGE_CURRENT_CFG_REG,
.min_u = 0,
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
.max_u = 3300000,
-#else
- .max_u = 4500000,
-#endif
.step_u = 25000,
},
.fv = {
.name = "float voltage",
.reg = FLOAT_VOLTAGE_CFG_REG,
.min_u = 3487500,
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
.max_u = 4400000,
-#else
- .max_u = 4920000,
-#endif
.step_u = 7500,
},
.usb_icl = {
.name = "usb input current limit",
.reg = USBIN_CURRENT_LIMIT_CFG_REG,
.min_u = 0,
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
.max_u = 3000000,
-#else
- .max_u = 4800000,
-#endif
.step_u = 25000,
},
.icl_stat = {
.name = "usb otg current limit",
.reg = OTG_CURRENT_LIMIT_CFG_REG,
.min_u = 250000,
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
.max_u = 1500000,
-#else
- .max_u = 2000000,
-#endif
.step_u = 250000,
},
.dc_icl = {
.name = "jeita fcc reduction",
.reg = JEITA_CCCOMP_CFG_REG,
.min_u = 0,
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
.max_u = 3000000,
-#else
- .max_u = 1575000,
-#endif
.step_u = 25000,
},
+ .jeita_fv_comp = {
+ .name = "jeita fv reduction",
+ .reg = JEITA_FVCOMP_CFG_REG,
+ .min_u = 0,
+ .max_u = 472500,
+ .step_u = 7500,
+ },
.freq_buck = {
.name = "buck switching frequency",
.reg = CFG_BUCKBOOST_FREQ_SELECT_BUCK_REG,
struct device_node *revid_dev_node;
int float_option;
int chg_inhibit_thr_mv;
+ int jeita_cool_cc_delta;
+ int jeita_hot_cc_delta;
+ int jeita_low_cc_delta;
bool no_battery;
bool hvdcp_disable;
bool auto_recharge_soc;
bool bad_part;
};
-static int __debug_mask;
+static int __debug_mask = PR_OEM | PR_OTG;
module_param_named(
debug_mask, __debug_mask, int, S_IRUSR | S_IWUSR
);
#define MICRO_1P5A 1500000
#define MICRO_P1A 100000
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
-#define MAX_DCP_ICL_UA 1800000
-#endif
+#define MAX_DCP_ICL_UA 1800000
#define OTG_DEFAULT_DEGLITCH_TIME_MS 50
#define MIN_WD_BARK_TIME 16
#define DEFAULT_WD_BARK_TIME 64
#define BITE_WDOG_TIMEOUT_8S 0x3
#define BARK_WDOG_TIMEOUT_MASK GENMASK(3, 2)
#define BARK_WDOG_TIMEOUT_SHIFT 2
+#define DEFAULT_CRITICAL_JEITA_CCOMP 2975000
+#define JEITA_SOFT_HOT_CC_COMP 1600000
+#define JEITA_SOFT_COOL_CC_COMP 2225000
+
static int smb2_parse_dt(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
if (rc < 0)
chip->dt.usb_icl_ua = -EINVAL;
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
chg->dcp_icl_ua = MAX_DCP_ICL_UA;
-#endif
rc = of_property_read_u32(node,
"qcom,otg-cl-ua", &chg->otg_cl_ua);
if (rc < 0)
chip->dt.wipower_max_uw = -EINVAL;
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
+#ifdef CONFIG_FB
if (of_find_property(node, "qcom,thermal-mitigation-dcp", &byte_len)) {
- chg->thermal_mitigation_dcp = devm_kzalloc(chg->dev, byte_len,
- GFP_KERNEL);
+ chg->thermal_mitigation_dcp = devm_kzalloc(chg->dev, byte_len, GFP_KERNEL);
- if (chg->thermal_mitigation_dcp == NULL)
- return -ENOMEM;
+ if (chg->thermal_mitigation_dcp == NULL)
+ return -ENOMEM;
chg->thermal_levels = byte_len / sizeof(u32);
rc = of_property_read_u32_array(node,
if (rc < 0) {
dev_err(chg->dev,
"Couldn't read threm limits rc = %d\n", rc);
- return rc;
+ return rc;
}
}
-
if (of_find_property(node, "qcom,thermal-mitigation-qc3", &byte_len)) {
- chg->thermal_mitigation_qc3 = devm_kzalloc(chg->dev, byte_len,
- GFP_KERNEL);
+ chg->thermal_mitigation_qc3 = devm_kzalloc(chg->dev, byte_len, GFP_KERNEL);
- if (chg->thermal_mitigation_qc3 == NULL)
- return -ENOMEM;
+ if (chg->thermal_mitigation_qc3 == NULL)
+ return -ENOMEM;
chg->thermal_levels = byte_len / sizeof(u32);
rc = of_property_read_u32_array(node,
chg->thermal_levels);
if (rc < 0) {
dev_err(chg->dev,
- "Couldn't read threm limits rc = %d\n", rc);
- return rc;
+ "Couldn't read threm limits rc = %d\n", rc);
+ return rc;
}
}
-
if (of_find_property(node, "qcom,thermal-mitigation-qc2", &byte_len)) {
- chg->thermal_mitigation_qc2 = devm_kzalloc(chg->dev, byte_len,
- GFP_KERNEL);
+ chg->thermal_mitigation_qc2 = devm_kzalloc(chg->dev, byte_len, GFP_KERNEL);
if (chg->thermal_mitigation_qc2 == NULL)
return -ENOMEM;
chg->micro_usb_mode = of_property_read_bool(node, "qcom,micro-usb");
-#ifndef CONFIG_MACH_XIAOMI_MSM8998
- chg->dcp_icl_ua = chip->dt.usb_icl_ua;
-#endif
-
chg->suspend_input_on_debug_batt = of_property_read_bool(node,
"qcom,suspend-input-on-debug-batt");
if (rc < 0)
chg->otg_delay_ms = OTG_DEFAULT_DEGLITCH_TIME_MS;
+ rc = of_property_read_u32(node, "qcom,fcc-low-temp-delta",
+ &chip->dt.jeita_low_cc_delta);
+ if (rc < 0)
+ chip->dt.jeita_low_cc_delta = DEFAULT_CRITICAL_JEITA_CCOMP;
+ chg->jeita_ccomp_low_delta = chip->dt.jeita_low_cc_delta;
+
+ rc = of_property_read_u32(node, "qcom,fcc-hot-temp-delta",
+ &chip->dt.jeita_hot_cc_delta);
+ if (rc < 0)
+ chip->dt.jeita_hot_cc_delta = JEITA_SOFT_HOT_CC_COMP;
+ chg->jeita_ccomp_hot_delta = chip->dt.jeita_hot_cc_delta;
+
+ rc = of_property_read_u32(node, "qcom,fcc-cool-temp-delta",
+ &chip->dt.jeita_cool_cc_delta);
+ if (rc < 0)
+ chip->dt.jeita_cool_cc_delta = JEITA_SOFT_COOL_CC_COMP;
+ chg->jeita_ccomp_cool_delta = chip->dt.jeita_cool_cc_delta;
+
chg->fcc_stepper_mode = of_property_read_bool(node,
"qcom,fcc-stepping-enable");
-
return 0;
}
POWER_SUPPLY_PROP_CTM_CURRENT_MAX,
POWER_SUPPLY_PROP_HW_CURRENT_MAX,
POWER_SUPPLY_PROP_REAL_TYPE,
+ POWER_SUPPLY_PROP_HVDCP3_TYPE,
POWER_SUPPLY_PROP_PR_SWAP,
POWER_SUPPLY_PROP_PD_VOLTAGE_MAX,
POWER_SUPPLY_PROP_PD_VOLTAGE_MIN,
POWER_SUPPLY_PROP_SDP_CURRENT_MAX,
+ POWER_SUPPLY_PROP_RERUN_APSD,
+ POWER_SUPPLY_PROP_TYPE_RECHECK,
};
static int smb2_usb_get_prop(struct power_supply *psy,
else
val->intval = chg->real_charger_type;
break;
+ case POWER_SUPPLY_PROP_HVDCP3_TYPE:
+ if (chg->pd_active)
+ val->intval = USB_PD;
+ else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP)
+ val->intval = HVDCP2_TYPE;
+ else if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3)
+ val->intval = HVDCP3_CLASSA_18W;
+ else
+ val->intval = HVDCP3_NONE;
+ break;
case POWER_SUPPLY_PROP_TYPEC_MODE:
if (chg->micro_usb_mode)
val->intval = POWER_SUPPLY_TYPEC_NONE;
val->intval = get_client_vote(chg->usb_icl_votable,
USB_PSY_VOTER);
break;
+ case POWER_SUPPLY_PROP_TYPE_RECHECK:
+ rc = smblib_get_prop_type_recheck(chg, val);
+ break;
default:
pr_err("get prop %d is not supported in usb\n", psp);
rc = -EINVAL;
case POWER_SUPPLY_PROP_SDP_CURRENT_MAX:
rc = smblib_set_prop_sdp_current_max(chg, val);
break;
+ case POWER_SUPPLY_PROP_RERUN_APSD:
+ rc = smblib_set_prop_rerun_apsd(chg, val);
+ break;
+ case POWER_SUPPLY_PROP_TYPE_RECHECK:
+ rc = smblib_set_prop_type_recheck(chg, val);
+ break;
default:
pr_err("set prop %d is not supported\n", psp);
rc = -EINVAL;
POWER_SUPPLY_PROP_SW_JEITA_ENABLED,
POWER_SUPPLY_PROP_CHARGE_DONE,
POWER_SUPPLY_PROP_PARALLEL_DISABLE,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
POWER_SUPPLY_PROP_SET_SHIP_MODE,
POWER_SUPPLY_PROP_DIE_HEALTH,
POWER_SUPPLY_PROP_RERUN_AICL,
+ POWER_SUPPLY_PROP_CHARGER_TYPE,
POWER_SUPPLY_PROP_DP_DM,
POWER_SUPPLY_PROP_CHARGE_COUNTER,
POWER_SUPPLY_PROP_FCC_STEPPER_ENABLE,
val->intval = get_client_vote(chg->pl_disable_votable,
USER_VOTER);
break;
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ rc = smblib_get_prop_batt_charge_full(chg, val);
+ break;
case POWER_SUPPLY_PROP_SET_SHIP_MODE:
/* Not in ship mode as long as device is active */
val->intval = 0;
case POWER_SUPPLY_PROP_RERUN_AICL:
val->intval = 0;
break;
+ case POWER_SUPPLY_PROP_CHARGER_TYPE:
+ val->intval = chg->real_charger_type;
+ break;
case POWER_SUPPLY_PROP_CHARGE_COUNTER:
case POWER_SUPPLY_PROP_CHARGE_FULL:
case POWER_SUPPLY_PROP_CYCLE_COUNT:
/*
* disable Type-C factory mode and stay in Attached.SRC state when VCONN
* over-current happens
- */
+ */
rc = smblib_masked_write(chg, TYPE_C_CFG_REG,
- FACTORY_MODE_DETECTION_EN_BIT | VCONN_OC_CFG_BIT, 0);
+ FACTORY_MODE_DETECTION_EN_BIT
+ | VCONN_OC_CFG_BIT, 0);
if (rc < 0) {
dev_err(chg->dev, "Couldn't configure Type-C rc=%d\n", rc);
return rc;
return rc;
}
- /* disable try.SINK mode and legacy cable IRQs */
+ /* disable try.SINK mode by xiaomi and legacy cable IRQs */
rc = smblib_masked_write(chg, TYPE_C_CFG_3_REG, EN_TRYSINK_MODE_BIT |
TYPEC_NONCOMPLIANT_LEGACY_CABLE_INT_EN_BIT |
TYPEC_LEGACY_CABLE_INT_EN_BIT, 0);
dev_err(chg->dev, "Couldn't set Type-C config rc=%d\n", rc);
return rc;
}
+
return rc;
}
return rc;
}
+static int smb2_init_jeita(struct smb2 *chip)
+{
+ struct smb_charger *chg = &chip->chg;
+ int rc;
+
+ /*set cc compensation to 0.3C*/
+ rc = smblib_set_charge_param(chg, &chg->param.jeita_cc_comp, 2225000);
+ if (rc < 0) {
+ pr_err("Couldn't configure jeita_cc rc = %d\n", rc);
+ return rc;
+ }
+ rc = smblib_set_charge_param(chg, &chg->param.jeita_fv_comp, 300000);
+ if (rc < 0) {
+ pr_err("Couldn't configure jeita_cv rc = %d\n", rc);
+ return rc;
+ }
+ rc = smblib_masked_write(chg, JEITA_EN_CFG_REG,
+ JEITA_EN_COLD_SL_FCV_BIT, 0);
+ if (rc < 0)
+ pr_err("Couldn't disable cold_sl_fcv rc=%d\n", rc);
+
+ return 0;
+}
+
static int smb2_init_hw(struct smb2 *chip)
{
struct smb_charger *chg = &chip->chg;
chg->param.freq_boost.max_u = chip->dt.max_freq_khz;
}
+ smb2_init_jeita(chip);
+
/* set a slower soft start setting for OTG */
rc = smblib_masked_write(chg, DC_ENG_SSUPPLY_CFG2_REG,
ENG_SSUPPLY_IVREF_OTG_SS_MASK, OTG_SS_SLOW);
vote(chg->hvdcp_enable_votable, MICRO_USB_VOTER,
chg->micro_usb_mode, 0);
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
/* Operate the QC2.0 in 5V/9V mode i.e. Disable 12V */
rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
- PULSE_COUNT_QC2P0_12V | PULSE_COUNT_QC2P0_9V,
- PULSE_COUNT_QC2P0_9V);
+ PULSE_COUNT_QC2P0_12V | PULSE_COUNT_QC2P0_9V,
+ PULSE_COUNT_QC2P0_9V);
if (rc < 0) {
dev_err(chg->dev,
"Couldn't configure QC2.0 to 9V rc=%d\n", rc);
}
/* Operate the QC3.0 to limit vbus to 6.6v*/
rc = smblib_masked_write(chg, HVDCP_PULSE_COUNT_MAX_REG,
- PULSE_COUNT_QC3P0_mask,
- 0x8);
+ PULSE_COUNT_QC3P0_mask, 0x8);
if (rc < 0) {
dev_err(chg->dev,
"Couldn't configure QC3.0 to 6.6V rc=%d\n", rc);
return rc;
}
-#endif
/*
* AICL configuration:
*/
rc = smblib_masked_write(chg, USBIN_AICL_OPTIONS_CFG_REG,
USBIN_AICL_START_AT_MAX_BIT
- | USBIN_AICL_ADC_EN_BIT, 0);
+ | USBIN_AICL_ADC_EN_BIT
+ | USBIN_AICL_RERUN_EN_BIT, USBIN_AICL_RERUN_EN_BIT);
if (rc < 0) {
dev_err(chg->dev, "Couldn't configure AICL rc=%d\n", rc);
return rc;
return rc;
}
+ /* set usbin collapse timer*/
+ rc = smblib_masked_write(chg, USBIN_LOAD_CFG_REG,
+ USBIN_COLLAPSE_SEL_MASK, 0x1);
+ if (rc < 0) {
+ dev_err(chg->dev, "set usbin collapse timer fault rc=%d\n",
+ rc);
+ }
+
/* disable h/w autonomous parallel charging control */
rc = smblib_masked_write(chg, MISC_CFG_REG,
STAT_PARALLEL_1400MA_EN_CFG_BIT, 0);
/* USB INPUT IRQs */
[USBIN_COLLAPSE_IRQ] = {
.name = "usbin-collapse",
- .handler = smblib_handle_debug,
+ .handler = smblib_handle_usbin_collapse,
},
[USBIN_LT_3P6V_IRQ] = {
.name = "usbin-lt-3p6v",
[SWITCH_POWER_OK_IRQ] = {
.name = "switcher-power-ok",
.handler = smblib_handle_switcher_power_ok,
+ .wake = true,
.storm_data = {true, 1000, 8},
},
};
/* set driver data before resources request it */
platform_set_drvdata(pdev, chip);
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
/* wakeup init should be done at the beginning of smb2_probe */
device_init_wakeup(chg->dev, true);
-#endif
rc = smb2_init_vbus_regulator(chip);
if (rc < 0) {
pr_err("Couldn't get batt charge type rc=%d\n", rc);
goto cleanup;
}
- batt_charge_type = val.intval;
-
-#ifndef CONFIG_MACH_XIAOMI_MSM8998
- device_init_wakeup(chg->dev, true);
-#endif
+ batt_charge_type = val.intval;
chg->last_soc = -EINVAL;
pr_info("QPNP SMB2 probed successfully usb:present=%d type=%d batt:present = %d health = %d charge = %d\n",
usb_present, chg->real_charger_type,
batt_present, batt_health, batt_charge_type);
+
+ schedule_delayed_work(&chg->reg_work, 60 * HZ);
return rc;
cleanup:
AUTO_SRC_DETECT_BIT, AUTO_SRC_DETECT_BIT);
}
-static int smb2_resume(struct device *dev)
+#ifdef CONFIG_FB
+static int smblib_suspend(struct device *dev)
+{
+ struct smb2 *chip = dev_get_drvdata(dev);
+ struct smb_charger *chg = &chip->chg;
+
+ cancel_delayed_work(&chg->screen_on_work);
+ chg->checking_in_progress = false;
+
+ return 0;
+}
+
+static int smblib_resume(struct device *dev)
{
struct smb2 *chip = dev_get_drvdata(dev);
struct smb_charger *chg = &chip->chg;
union power_supply_propval pval = {0, };
int rc;
- if (!chg->batt_psy)
- return 0;
-
rc = smblib_get_prop_batt_capacity(chg, &pval);
- if (rc < 0) {
+ if (rc < 0)
pr_err("Couldn't get batt capacity rc=%d\n", rc);
- return 0;
- }
if (pval.intval != chg->last_soc) {
- power_supply_changed(chg->batt_psy);
+ if (chg->batt_psy)
+ power_supply_changed(chg->batt_psy);
chg->last_soc = pval.intval;
}
}
static const struct dev_pm_ops smb2_pm_ops = {
- .resume = smb2_resume,
+ .suspend = smblib_suspend,
+ .resume = smblib_resume,
};
+#endif
static const struct of_device_id match_table[] = {
{ .compatible = "qcom,qpnp-smb2", },
.name = "qcom,qpnp-smb2",
.owner = THIS_MODULE,
.of_match_table = match_table,
- .pm = &smb2_pm_ops,
+#ifdef CONFIG_FB
+ .pm = &smb2_pm_ops,
+#endif
},
.probe = smb2_probe,
.remove = smb2_remove,
/* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
+ * Copyright (C) 2019 XiaoMi, Inc.
*
* 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
#include "step-chg-jeita.h"
#include "storm-watch.h"
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
-#include <linux/fb.h>
-#endif
-
#ifdef CONFIG_FORCE_FAST_CHARGE
#include <linux/fastchg.h>
#endif
__func__, ##__VA_ARGS__); \
} while (0)
+static bool off_charge_flag;
+
+#ifdef CONFIG_FB
+static int smblib_fb_notifier_cb(struct notifier_block *self,
+ unsigned long event, void *data);
+#endif
+
static bool is_secure(struct smb_charger *chg, int addr)
{
if (addr == SHIP_MODE_REG || addr == FREQ_CLK_DIV_REG)
{
int rc, cc_minus_ua;
u8 stat;
+ union power_supply_propval batt_temp;
rc = smblib_read(chg, BATTERY_CHARGER_STATUS_2_REG, &stat);
if (rc < 0) {
return rc;
}
+ smblib_dbg(chg, PR_REGISTER, "BATTERY_CHARGER_STATUS_2 = 0x%02x\n", stat);
+
if (!(stat & BAT_TEMP_STATUS_SOFT_LIMIT_MASK)) {
*cc_delta_ua = 0;
return 0;
}
+ rc = smblib_get_prop_from_bms(chg,
+ POWER_SUPPLY_PROP_TEMP, &batt_temp);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get batt temp rc=%d\n", rc);
+ return rc;
+ }
+
rc = smblib_get_charge_param(chg, &chg->param.jeita_cc_comp,
&cc_minus_ua);
if (rc < 0) {
return rc;
}
+ if ((stat & BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT) == BAT_TEMP_STATUS_HOT_SOFT_LIMIT_BIT) {
+ if (cc_minus_ua != chg->jeita_ccomp_hot_delta) {
+ rc = smblib_set_charge_param(chg, &chg->param.jeita_cc_comp,
+ chg->jeita_ccomp_hot_delta);
+ if (rc < 0)
+ pr_err("%s: Couldn't configure jeita_cc rc=%d\n", __func__, rc);
+ }
+ } else if ((stat & BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT) == BAT_TEMP_STATUS_COLD_SOFT_LIMIT_BIT) {
+ if (batt_temp.intval >= 0) {
+ if (batt_temp.intval <= BATT_TEMP_CRITICAL_LOW) { /*set 0.1C on 0~5 Degree */
+ rc = smblib_set_charge_param(chg, &chg->param.jeita_cc_comp,
+ chg->jeita_ccomp_low_delta);
+ if (rc < 0)
+ pr_err("%s: Couldn't configure jeita_cc rc=%d\n", __func__, rc);
+ } else { /*set 0.3C on 5~15 Degree */
+ rc = smblib_set_charge_param(chg, &chg->param.jeita_cc_comp,
+ chg->jeita_ccomp_cool_delta);
+ if (rc < 0)
+ pr_err("%s: Couldn't configure jeita_cc rc=%d\n", __func__, rc);
+ }
+ }
+ }
+
+ rc = smblib_get_charge_param(chg, &chg->param.jeita_cc_comp, &cc_minus_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get jeita cc minus rc=%d\n", rc);
+ return rc;
+ }
+
*cc_delta_ua = -cc_minus_ua;
return 0;
}
+static void smblib_monitor_low_temp_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ monitor_low_temp_work.work);
+ int rc, jeita_cc_delta_ua;
+ union power_supply_propval batt_temp;
+
+ rc = smblib_get_prop_from_bms(chg,
+ POWER_SUPPLY_PROP_TEMP, &batt_temp);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get batt temp rc=%d\n", rc);
+ return;
+ }
+ if (batt_temp.intval >= 0 && batt_temp.intval<= BATT_TEMP_COOL_THR) { /*set 0.1C on 0~5 Degree */
+ rc = smblib_get_jeita_cc_delta(chg, &jeita_cc_delta_ua);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get jeita cc delta rc=%d\n", rc);
+ jeita_cc_delta_ua = 0;
+ }
+ }
+ schedule_delayed_work(&chg->monitor_low_temp_work,
+ msecs_to_jiffies(CHG_MONITOR_WORK_DELAY_MS));
+
+}
+
int smblib_icl_override(struct smb_charger *chg, bool override)
{
int rc;
rc);
return result;
}
+ smblib_dbg(chg, PR_REGISTER, "APSD_RESULT = 0x%02x\n", stat);
+ pr_info("%s: APSD_RESULT = 0x%02x\n", __func__, stat);
+
stat &= APSD_RESULT_STATUS_MASK;
for (i = 0; i < ARRAY_SIZE(smblib_apsd_results); i++) {
}
}
+ smblib_err(chg, "%s: write %s to USBIN_SUSPEND_BIT\n", __func__,
+ suspend ? "suspend" : "resume");
+
return rc;
}
}
#define MICRO_5V 5000000
+#define MICRO_6V 6000000
#define MICRO_9V 9000000
#define MICRO_12V 12000000
static int smblib_set_usb_pd_allowed_voltage(struct smb_charger *chg,
smblib_err(chg, "Couldn't re-run APSD rc=%d\n", rc);
}
+#define PERIPHERAL_MASK 0xFF
+static u16 peripheral_base;
+static char log[256] = "";
+static char version[8] = "smb:01:";
+static inline void dump_reg(struct smb_charger *chg, u16 addr,
+ const char *name)
+{
+ u8 reg;
+ int rc;
+ char reg_data[50] = "";
+
+ if (NULL == name) {
+ strlcat(log, "\n", sizeof(log));
+ printk(log);
+ return;
+ }
+
+ rc = smblib_read(chg, addr, ®);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't read OTG status rc=%d\n", rc);
+ /* print one peripheral base registers in one line */
+ if (peripheral_base != (addr & ~PERIPHERAL_MASK)) {
+ peripheral_base = addr & ~PERIPHERAL_MASK;
+ memset(log, 0, sizeof(log));
+ snprintf(reg_data, sizeof(reg_data), "%s%04x ", version, peripheral_base);
+ strlcat(log, reg_data, sizeof(log));
+ }
+ memset(reg_data, 0, sizeof(reg_data));
+ snprintf(reg_data, sizeof(reg_data), "%02x ", reg);
+ strlcat(log, reg_data, sizeof(log));
+
+ smblib_dbg(chg, PR_REGISTER, "%s - %04X = %02X\n",
+ name, addr, reg);
+}
+
+static void dump_regs(struct smb_charger *chg)
+{
+ u16 addr;
+
+ /* charger peripheral */
+ for (addr = 0x6; addr <= 0xE; addr++)
+ dump_reg(chg, CHGR_BASE + addr, "CHGR Status");
+
+ for (addr = 0x10; addr <= 0x1B; addr++)
+ dump_reg(chg, CHGR_BASE + addr, "CHGR INT");
+
+ for (addr = 0x50; addr <= 0x70; addr++)
+ dump_reg(chg, CHGR_BASE + addr, "CHGR Config");
+
+ dump_reg(chg, CHGR_BASE + addr, NULL);
+
+ for (addr = 0x6; addr <= 0x8; addr++)
+ dump_reg(chg, OTG_BASE + addr, "OTG Status");
+
+ for (addr = 0x9; addr <= 0x1B; addr++)
+ dump_reg(chg, OTG_BASE + addr, "OTG INT");
+
+ for (addr = 0x40; addr <= 0x53; addr++)
+ dump_reg(chg, OTG_BASE + addr, "OTG Config");
+
+ dump_reg(chg, OTG_BASE + addr, NULL);
+
+ for (addr = 0x10; addr <= 0x1B; addr++)
+ dump_reg(chg, BATIF_BASE + addr, "BATIF INT");
+
+ for (addr = 0x50; addr <= 0x52; addr++)
+ dump_reg(chg, BATIF_BASE + addr, "BATIF Config");
+
+ for (addr = 0x60; addr <= 0x62; addr++)
+ dump_reg(chg, BATIF_BASE + addr, "BATIF Config");
+
+ for (addr = 0x70; addr <= 0x71; addr++)
+ dump_reg(chg, BATIF_BASE + addr, "BATIF Config");
+
+ dump_reg(chg, BATIF_BASE + addr, NULL);
+
+ for (addr = 0x6; addr <= 0x10; addr++)
+ dump_reg(chg, USBIN_BASE + addr, "USBIN Status");
+
+ for (addr = 0x12; addr <= 0x19; addr++)
+ dump_reg(chg, USBIN_BASE + addr, "USBIN INT ");
+
+ for (addr = 0x40; addr <= 0x43; addr++)
+ dump_reg(chg, USBIN_BASE + addr, "USBIN Cmd ");
+
+ for (addr = 0x58; addr <= 0x70; addr++)
+ dump_reg(chg, USBIN_BASE + addr, "USBIN Config ");
+
+ for (addr = 0x80; addr <= 0x84; addr++)
+ dump_reg(chg, USBIN_BASE + addr, "USBIN Config ");
+
+ dump_reg(chg, USBIN_BASE + addr, NULL);
+
+ for (addr = 0x6; addr <= 0x10; addr++)
+ dump_reg(chg, MISC_BASE + addr, "MISC Status");
+
+ for (addr = 0x15; addr <= 0x1B; addr++)
+ dump_reg(chg, MISC_BASE + addr, "MISC INT");
+
+ for (addr = 0x51; addr <= 0x62; addr++)
+ dump_reg(chg, MISC_BASE + addr, "MISC Config");
+
+ for (addr = 0x70; addr <= 0x76; addr++)
+ dump_reg(chg, MISC_BASE + addr, "MISC Config");
+
+ for (addr = 0x80; addr <= 0x84; addr++)
+ dump_reg(chg, MISC_BASE + addr, "MISC Config");
+
+ for (addr = 0x90; addr <= 0x94; addr++)
+ dump_reg(chg, MISC_BASE + addr, "MISC Config");
+
+ dump_reg(chg, MISC_BASE + addr, NULL);
+
+ for (addr = 0x10; addr <= 0x1A; addr++)
+ dump_reg(chg, 0x1700 + addr, "PDPHY INT");
+
+ for (addr = 0x40; addr <= 0x48; addr++)
+ dump_reg(chg, 0x1700 + addr, "PDPHY INT");
+
+ for (addr = 0x4A; addr <= 0x4C; addr++)
+ dump_reg(chg, 0x1700 + addr, "PDPHY Config");
+
+ dump_reg(chg, 0x1700 + 0x58, "PDPHY Ctrl");
+
+ dump_reg(chg, 0x1700 + addr, NULL);
+}
+
static const struct apsd_result *smblib_update_usb_type(struct smb_charger *chg)
{
const struct apsd_result *apsd_result = smblib_get_apsd_result(chg);
/* if PD is active, APSD is disabled so won't have a valid result */
if (chg->pd_active) {
chg->real_charger_type = POWER_SUPPLY_TYPE_USB_PD;
+ chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_PD;
} else {
/*
* Update real charger type only if its not FLOAT
*/
if (!(apsd_result->pst == POWER_SUPPLY_TYPE_USB_FLOAT &&
chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
- chg->real_charger_type = apsd_result->pst;
+ {
+ chg->real_charger_type = apsd_result->pst;
+ chg->usb_psy_desc.type = apsd_result->pst;
+ }
}
- smblib_dbg(chg, PR_MISC, "APSD=%s PD=%d\n",
+ smblib_dbg(chg, PR_OEM, "APSD=%s PD=%d\n",
apsd_result->name, chg->pd_active);
return apsd_result;
}
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0);
vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0);
+ vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
int smblib_rerun_apsd_if_required(struct smb_charger *chg)
{
union power_supply_propval val;
+ const struct apsd_result *apsd_result;
int rc;
rc = smblib_get_prop_usb_present(chg, &val);
if (!val.intval)
return 0;
+ /*
rc = smblib_request_dpdm(chg, true);
if (rc < 0)
smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
+ */
chg->uusb_apsd_rerun_done = true;
- smblib_rerun_apsd(chg);
+ if (!off_charge_flag) {
+ rc = smblib_request_dpdm(chg, true);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
+ smblib_rerun_apsd(chg);
+ } else {
+ apsd_result = smblib_update_usb_type(chg);
+ /* if apsd result is SDP and off-charge mode, no need rerun apsd */
+ if (!(apsd_result->bit & SDP_CHARGER_BIT)) {
+ rc = smblib_request_dpdm(chg, true);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
+ smblib_rerun_apsd(chg);
+ }
+ }
return 0;
}
{
int rc = 0;
bool override;
-
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
union power_supply_propval val = {0, };
int usb_present = 0;
-#endif
/* suspend and return if 25mA or less is requested */
if (icl_ua < USBIN_25MA)
return smblib_set_usb_suspend(chg, true);
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- disable_irq_nosync(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);
-#endif
-
if (icl_ua == INT_MAX)
goto override_suspend_config;
goto enable_icl_changed_interrupt;
}
} else {
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
rc = smblib_get_prop_usb_present(chg, &val);
if (rc < 0)
smblib_err(chg, "Couldn't get usb present rc = %d\n", rc);
else
usb_present = val.intval;
-
if (usb_present
&& chg->typec_mode == POWER_SUPPLY_TYPEC_NONE)
set_sdp_current(chg, 500000);
else
set_sdp_current(chg, 100000);
-#else
- set_sdp_current(chg, 100000);
-#endif
rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua);
if (rc < 0) {
smblib_err(chg, "Couldn't set HC ICL rc=%d\n", rc);
}
enable_icl_changed_interrupt:
-
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- enable_irq(chg->irq_info[USBIN_ICL_CHANGE_IRQ].irq);
-#endif
-
return rc;
}
stat = stat & BATTERY_CHARGER_STATUS_MASK;
if (!usb_online && !dc_online) {
- switch (stat) {
- case TERMINATE_CHARGE:
- case INHIBIT_CHARGE:
- val->intval = POWER_SUPPLY_STATUS_FULL;
- break;
- default:
- val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
- break;
- }
+ val->intval = POWER_SUPPLY_STATUS_DISCHARGING;
return rc;
}
+ rc = smblib_get_prop_batt_health(chg, &pval);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't read batt health rc=%d\n", rc);
+
+ /*this is a workaround to support type-c apapter without PD*/
+ smblib_dbg(chg, PR_MISC, "typec_en_dis_active = %d\n", chg->typec_en_dis_active);
+
+ if (chg->typec_en_dis_active && pval.intval != POWER_SUPPLY_HEALTH_OVERHEAT
+ && pval.intval != POWER_SUPPLY_HEALTH_COLD)
+ {
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ return 0;
+ }
+
switch (stat) {
case TRICKLE_CHARGE:
case PRE_CHARGE:
break;
case TERMINATE_CHARGE:
case INHIBIT_CHARGE:
- val->intval = POWER_SUPPLY_STATUS_FULL;
+ if (POWER_SUPPLY_HEALTH_WARM == pval.intval)
+ val->intval = POWER_SUPPLY_STATUS_CHARGING;
+ else if (POWER_SUPPLY_HEALTH_OVERHEAT == pval.intval
+ || pval.intval == POWER_SUPPLY_HEALTH_COLD)
+ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ else
+ val->intval = POWER_SUPPLY_STATUS_FULL;
break;
case DISABLE_CHARGE:
val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING;
break;
}
- if (val->intval != POWER_SUPPLY_STATUS_CHARGING)
+ if (val->intval != POWER_SUPPLY_STATUS_CHARGING
+ || pval.intval == POWER_SUPPLY_HEALTH_WARM)
return 0;
rc = smblib_read(chg, BATTERY_CHARGER_STATUS_7_REG, &stat);
if (rc < 0) {
- smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_2 rc=%d\n",
+ smblib_err(chg, "Couldn't read BATTERY_CHARGER_STATUS_7 rc=%d\n",
rc);
return rc;
}
return 0;
}
+int smblib_get_prop_batt_charge_full(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int rc;
+
+ if (!chg->bms_psy)
+ return -EINVAL;
+
+ rc = power_supply_get_property(chg->bms_psy,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN, val);
+ return rc;
+}
+
int smblib_get_prop_batt_charge_done(struct smb_charger *chg,
union power_supply_propval *val)
{
return rc;
}
+ pr_info("BATTERY_CHARGER_STATUS_1 = 0x%02x\n", stat);
stat = stat & BATTERY_CHARGER_STATUS_MASK;
val->intval = (stat == TERMINATE_CHARGE);
+
+ /* if charge is done, clear the chg_awake_voter */
+ if (val->intval == 1)
+ vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0);
return 0;
}
return 0;
}
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
-#define SCREEN_ON_ICL 3200000
-#define SCREEN_ON_CHECK_MS 90000
-#define SCREEN_OFF_CHECK_MS 5000
-static void smblib_fb_state_work(struct work_struct *work)
+#ifdef CONFIG_FB
+#define ICL_MINUS 1600000
+static void smblib_screen_on_work(struct work_struct *work)
{
- struct smb_charger *chg = container_of(work, struct smb_charger,
- fb_state_work.work);
- union power_supply_propval usb_present;
- int rc;
+ struct smb_charger *chg = container_of(work,
+ struct smb_charger, screen_on_work.work);
- rc = smblib_get_prop_usb_present(chg, &usb_present);
+ int rc, usb_present;
+ union power_supply_propval val;
+
+ mutex_lock(&chg->screen_lock);
+ chg->checking_in_progress = false;
+ mutex_unlock(&chg->screen_lock);
+
+ rc = smblib_get_prop_usb_present(chg, &val);
if (rc < 0) {
- smblib_err(chg, "Couldn't check usb present, rc=%d\n", rc);
+ pr_err("Couldn't get usb present rc=%d\n", rc);
return;
}
+ usb_present = val.intval;
- if (!usb_present.intval)
+ /* if usb is not present , no actions */
+ if (!usb_present)
return;
if (!chg->usb_icl_votable) {
- smblib_err(chg, "Couldn't find USB ICL votable\n");
+ pr_err("%s: icl_votable couldn't ready\n", __func__);
return;
}
+ mutex_lock(&chg->screen_lock);
if (chg->screen_on) {
- smblib_dbg(chg, PR_MISC, "Screen is on, lower USB ICL\n");
- vote(chg->usb_icl_votable, FB_SCREEN_VOTER, true, SCREEN_ON_ICL);
+ pr_info("%s: screen_on set ICL %d mA\n", __func__, ICL_MINUS);
+ vote(chg->usb_icl_votable, FB_SCREEN_VOTER, true, ICL_MINUS);
} else {
- smblib_dbg(chg, PR_MISC, "Screen is off, reset USB ICL\n");
+ pr_info("%s: screen_off resume ICL \n", __func__);
vote(chg->usb_icl_votable, FB_SCREEN_VOTER, false, 0);
}
+ mutex_unlock(&chg->screen_lock);
}
-static int smblib_fb_state_cb(struct notifier_block *self,
- unsigned long type, void *data)
+#define SCREEN_ON_CHECK_MS 90000
+#define SCREEN_OFF_CHECK_MS 5000
+static int smblib_fb_notifier_cb(struct notifier_block *self,
+ unsigned long event, void *data)
{
- struct smb_charger *chg = container_of(self,
- struct smb_charger, fb_state_notifier);
struct fb_event *evdata = data;
- unsigned int check_ms;
- unsigned int blank;
-
- if (!evdata || !evdata->data)
- goto end;
-
- if (type != FB_EARLY_EVENT_BLANK)
- goto end;
-
- cancel_delayed_work(&chg->fb_state_work);
-
- blank = *(int *)(evdata->data);
- switch (blank) {
- case FB_BLANK_UNBLANK:
- chg->screen_on = true;
- check_ms = SCREEN_ON_CHECK_MS;
- break;
- case FB_BLANK_POWERDOWN:
- chg->screen_on = false;
- check_ms = SCREEN_OFF_CHECK_MS;
- break;
- default:
- goto end;
+ int screen_check_ms;
+ int *blank;
+
+ struct smb_charger *chg =
+ container_of(self, struct smb_charger, smb_fb_notif);
+
+ mutex_lock(&chg->screen_lock);
+ if (evdata && evdata->data && chg) {
+ if (event == FB_EARLY_EVENT_BLANK) {
+ blank = evdata->data;
+ if (*blank == FB_BLANK_UNBLANK) {
+ chg->screen_on = true;
+ pr_info("%s: screen_on\n", __func__);
+ screen_check_ms = SCREEN_ON_CHECK_MS;
+ } else if (*blank == FB_BLANK_POWERDOWN) {
+ chg->screen_on = false;
+ pr_info("%s: screen_off\n", __func__);
+ screen_check_ms = SCREEN_OFF_CHECK_MS;
+ }
+ if (!chg->checking_in_progress &&
+ ((*blank == FB_BLANK_UNBLANK) ||
+ (*blank == FB_BLANK_POWERDOWN))) {
+ chg->checking_in_progress = true;
+ schedule_delayed_work(&chg->screen_on_work,
+ msecs_to_jiffies(screen_check_ms));
+ }
+ }
+ } else {
+ pr_err("%s: Couldn't get fb event\n", __func__);
+ mutex_unlock(&chg->screen_lock);
+ return 0;
}
-
- queue_delayed_work(system_power_efficient_wq,
- &chg->fb_state_work,
- msecs_to_jiffies(check_ms));
-
-end:
- return NOTIFY_OK;
+ mutex_unlock(&chg->screen_lock);
+ return 0;
}
#endif
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
-#define MAX_CURRENT_PERCENT 100
-#define HIGH_CURRENT_PERCENT 70
-#define MEDIUM_CURRENT_PERCENT 50
-#endif
int smblib_set_prop_system_temp_level(struct smb_charger *chg,
const union power_supply_propval *val)
{
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- int *thermal_mitigation;
- int current_percent;
- bool throttle_current;
-#endif
+ int rc;
+ union power_supply_propval batt_temp;
+
+ rc = smblib_get_prop_from_bms(chg,
+ POWER_SUPPLY_PROP_TEMP, &batt_temp);
+ if (rc < 0) {
+ pr_err("Couldn't get batt temp rc=%d\n", rc);
+ return -EINVAL;
+ }
+
+ pr_info("thermal level:%d,batt temp:%d,thermal_levels:%d "
+ "screen state:%d and chg type:%d\n",
+ val->intval, batt_temp.intval, chg->thermal_levels,
+ chg->screen_on, chg->usb_psy_desc.type);
if (val->intval < 0)
return -EINVAL;
chg->system_temp_level = val->intval;
/* disable parallel charge in case of system temp level */
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
vote(chg->pl_disable_votable, THERMAL_DAEMON_VOTER,
(chg->system_temp_level > 2) ? true : false, 0);
-#else
- vote(chg->pl_disable_votable, THERMAL_DAEMON_VOTER,
- chg->system_temp_level ? true : false, 0);
-#endif
if (chg->system_temp_level == chg->thermal_levels)
return vote(chg->chg_disable_votable,
THERMAL_DAEMON_VOTER, true, 0);
vote(chg->chg_disable_votable, THERMAL_DAEMON_VOTER, false, 0);
-
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- throttle_current = chg->screen_on;
-
if (chg->system_temp_level == 0)
return vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, false, 0);
- switch (chg->real_charger_type) {
+#ifdef CONFIG_FB
+ switch (chg->usb_psy_desc.type) {
+ case POWER_SUPPLY_TYPE_USB_DCP:
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ chg->thermal_mitigation_dcp[chg->system_temp_level]);
+ break;
case POWER_SUPPLY_TYPE_USB_HVDCP:
- thermal_mitigation = chg->thermal_mitigation_qc2;
+ if (chg->screen_on) {
+ if (chg->system_temp_level == 6)
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ chg->thermal_mitigation_qc2[chg->system_temp_level]);
+ else if (chg->system_temp_level < 3) {
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ (chg->thermal_mitigation_qc2[chg->system_temp_level] * 70 / 100));
+ } else {
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ (chg->thermal_mitigation_qc2[chg->system_temp_level] * 50 / 100)); }
+ } else
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ chg->thermal_mitigation_qc2[chg->system_temp_level]);
break;
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
- thermal_mitigation = chg->thermal_mitigation_qc3;
+ if (chg->screen_on) {
+ if (chg->system_temp_level == 6)
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ chg->thermal_mitigation_qc3[chg->system_temp_level]);
+ else if (chg->system_temp_level < 3) {
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ (chg->thermal_mitigation_qc3[chg->system_temp_level] * 70 / 100));
+ } else {
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ (chg->thermal_mitigation_qc3[chg->system_temp_level] * 50 / 100)); }
+ } else
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ chg->thermal_mitigation_qc3[chg->system_temp_level]);
break;
- case POWER_SUPPLY_TYPE_USB_DCP:
default:
- thermal_mitigation = chg->thermal_mitigation_dcp;
- throttle_current = false;
+ vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
+ chg->thermal_mitigation_dcp[chg->system_temp_level]);
break;
}
-
- if (!throttle_current || chg->system_temp_level == 6)
- current_percent = MAX_CURRENT_PERCENT;
- else if (chg->system_temp_level < 3)
- current_percent = HIGH_CURRENT_PERCENT;
- else
- current_percent = MEDIUM_CURRENT_PERCENT;
-
- vote(chg->usb_icl_votable, THERMAL_DAEMON_VOTER, true,
- thermal_mitigation[chg->system_temp_level] * current_percent / 100);
#else
- if (chg->system_temp_level == 0)
- return vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, false, 0);
-
vote(chg->fcc_votable, THERMAL_DAEMON_VOTER, true,
chg->thermal_mitigation[chg->system_temp_level]);
#endif
-
return 0;
}
pr_err("Failed to force 5V\n");
break;
case POWER_SUPPLY_DP_DM_FORCE_9V:
+ /* donot use qcom hvdcp2.0 mode */
+ return 0;
+ /* Force 1A ICL before requesting higher voltage */
+ vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, true, 1000000);
rc = smblib_force_vbus_voltage(chg, FORCE_9V_BIT);
if (rc < 0)
pr_err("Failed to force 9V\n");
break;
case POWER_SUPPLY_DP_DM_FORCE_12V:
+ /* Force 1A ICL before requesting higher voltage */
+ vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, true, 1000000);
rc = smblib_force_vbus_voltage(chg, FORCE_12V_BIT);
if (rc < 0)
pr_err("Failed to force 12V\n");
return rc;
}
+ /*smblib_err(chg, "typec_en_dis_active = %d\n", chg->typec_en_dis_active);*/
+
+ if (chg->typec_en_dis_active) {
+ val->intval = 1;
+ return 0;
+ }
+
rc = smblib_read(chg, POWER_PATH_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg, "Couldn't read POWER_PATH_STATUS rc=%d\n",
#define SDP_CURRENT_UA 500000
#define CDP_CURRENT_UA 1500000
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
#define DCP_CURRENT_UA 1800000
#define HVDCP_CURRENT_UA 1500000
-#define HVDCP3_CURRENT_UA 3000000
-#else
-#define DCP_CURRENT_UA 1500000
-#define HVDCP_CURRENT_UA 3000000
-#endif
+#define HVDCP3_CURRENT_UA 2700000
#define TYPEC_DEFAULT_CURRENT_UA 900000
#define TYPEC_MEDIUM_CURRENT_UA 1500000
#define TYPEC_HIGH_CURRENT_UA 3000000
return rc;
}
+#define FLOAT_CHARGER_UA 1000000
static int smblib_handle_usb_current(struct smb_charger *chg,
int usb_current)
{
*/
typec_mode = smblib_get_prop_typec_mode(chg);
rp_ua = get_rp_based_dcp_current(chg, typec_mode);
+ rp_ua = FLOAT_CHARGER_UA;
rc = vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER,
true, rp_ua);
if (rc < 0)
* real_charger_type
*/
chg->real_charger_type = POWER_SUPPLY_TYPE_USB;
+ chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB;
rc = vote(chg->usb_icl_votable, USB_PSY_VOTER,
true, usb_current);
if (rc < 0)
return rc;
}
+int smblib_set_prop_rerun_apsd(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ if (val->intval == 1) {
+ chg->float_rerun_apsd = true;
+ smblib_rerun_apsd(chg);
+ }
+ return 0;
+}
+
+int smblib_get_prop_type_recheck(struct smb_charger *chg,
+ union power_supply_propval *val)
+{
+ int status = 0;
+
+ if (chg->recheck_charger)
+ status |= BIT(0) << 8;
+
+ status |= chg->precheck_charger_type << 4;
+ status |= chg->real_charger_type;
+
+ val->intval = status;
+
+ return 0;
+}
+
+int smblib_set_prop_type_recheck(struct smb_charger *chg,
+ const union power_supply_propval *val)
+{
+ if (val->intval == 0) {
+ cancel_delayed_work_sync(&chg->charger_type_recheck);
+ chg->recheck_charger = false;
+ }
+ return 0;
+}
+
int smblib_set_prop_boost_current(struct smb_charger *chg,
const union power_supply_propval *val)
{
typec_source_rd = smblib_get_prop_ufp_mode(chg);
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- /* QC 2.0 adapter*/
+ /* QC 2.0 adapter */
if (apsd_result->bit & QC_2P0_BIT) {
*total_current_ua = HVDCP_CURRENT_UA;
return 0;
*total_current_ua = HVDCP3_CURRENT_UA;
return 0;
}
-#else
- /* QC 2.0/3.0 adapter */
- if (apsd_result->bit & (QC_3P0_BIT | QC_2P0_BIT)) {
- *total_current_ua = HVDCP_CURRENT_UA;
- return 0;
- }
-#endif
if (non_compliant) {
switch (apsd_result->bit) {
u8 stat;
int rc;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+ smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name);
rc = smblib_read(chg, BATTERY_CHARGER_STATUS_1_REG, &stat);
if (rc < 0) {
return IRQ_HANDLED;
}
+ smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
rerun_election(chg->fcc_votable);
power_supply_changed(chg->batt_psy);
return IRQ_HANDLED;
return IRQ_HANDLED;
}
+irqreturn_t smblib_handle_usbin_collapse(int irq, void *data)
+{
+ struct smb_irq_data *irq_data = data;
+ struct smb_charger *chg = irq_data->parent_data;
+
+ smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name);
+ return IRQ_HANDLED;
+}
+
irqreturn_t smblib_handle_usbin_uv(int irq, void *data)
{
struct smb_irq_data *irq_data = data;
struct smb_charger *chg = irq_data->parent_data;
struct storm_watch *wdata;
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: %s\n", irq_data->name);
+ smblib_dbg(chg, PR_OEM, "IRQ: %s\n", irq_data->name);
if (!chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data)
return IRQ_HANDLED;
return IRQ_HANDLED;
}
+static void smblib_cc_float_charge_work(struct work_struct *work)
+{
+ union power_supply_propval val = {0, };
+ int rc, usb_present = 0;
+
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ cc_float_charge_work.work);
+
+ rc = smblib_get_prop_usb_present(chg, &val);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get usb present rc = %d\n", rc);
+ return;
+ }
+
+ usb_present = val.intval;
+ /*
+ * if CC pin of C to A cable is not connected to the receptacle
+ * or CC pin is bad or C to C cable CC line is float, vote 500mA and
+ * report charger type as DCP to improve user experience
+ */
+ if (usb_present
+ && (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE)
+ && (chg->cc_float_detected == false)) {
+ chg->cc_float_detected = true;
+ chg->real_charger_type = POWER_SUPPLY_TYPE_USB_DCP;
+ chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_USB_DCP;
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ vote(chg->usb_icl_votable, CC_FLOAT_VOTER, true, 500000);
+ power_supply_changed(chg->batt_psy);
+ }
+}
+
+static void smblib_check_vbus_work(struct work_struct *work)
+{
+ union power_supply_propval val = {0, };
+ int rc, usb_present = 0;
+
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ check_vbus_work.work);
+
+ rc = smblib_get_prop_usb_present(chg, &val);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't get usb present rc = %d\n", rc);
+ return;
+ }
+
+ usb_present = val.intval;
+ if (usb_present) {
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ rc = smblib_get_prop_usb_voltage_now(chg, &val);
+ if (rc < 0)
+ pr_err("Couldn't get usb voltage rc=%d\n", rc);
+ pr_info("VBUS now is %d\n", val.intval);
+ if (val.intval >= QC2_HVDCP_VOL_UV_THR) {
+ pr_info("this is a normal QC2.0 charger\n");
+ /* if use usbmid charging, enable hardware INOV again */
+ vote(chg->hvdcp_hw_inov_dis_votable,
+ UNSTANDARD_QC2_VOTER, false, 0);
+ } else {
+ pr_info("set adapter allowance to 5V and force 5V charge\n");
+ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG,
+ FORCE_5V_BIT, FORCE_5V_BIT);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't force 5V HVDCP rc=%d\n", rc);
+ smblib_set_usb_pd_allowed_voltage(chg, MICRO_5V, MICRO_5V);
+ chg->unstandard_hvdcp = true;
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
+ vote(chg->usb_icl_votable, UNSTANDARD_QC2_VOTER,
+ true, UNSTANDARD_HVDCP2_UA);
+ }
+ }
+ }
+}
+
static void smblib_micro_usb_plugin(struct smb_charger *chg, bool vbus_rising)
{
if (vbus_rising) {
false, 0);
}
}
+ if (chg->cc_float_detected) {
+ chg->cc_float_detected = false;
+ chg->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+ vote(chg->usb_icl_votable, CC_FLOAT_VOTER, false, 0);
+ vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0);
+ cancel_delayed_work_sync(&chg->charger_type_recheck);
}
power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
+ smblib_dbg(chg, PR_OEM, "IRQ: usbin-plugin %s\n",
vbus_rising ? "attached" : "detached");
}
rc = smblib_request_dpdm(chg, true);
if (rc < 0)
smblib_err(chg, "Couldn't to enable DPDM rc=%d\n", rc);
+ vote(chg->awake_votable, CHG_AWAKE_VOTER, true, 0);
+
if (chg->fcc_stepper_mode)
vote(chg->fcc_votable, FCC_STEPPER_VOTER, false, 0);
+ if (get_client_vote_locked(chg->usb_icl_votable, USER_VOTER) != 0) {
+ rc = smblib_set_usb_suspend(chg, false);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't resume input rc=%d\n", rc);
+ }
+ if (is_client_vote_enabled(chg->usb_icl_votable, USB_PSY_VOTER)) {
+ rc = vote(chg->usb_icl_votable, USB_PSY_VOTER, false, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't unvote USB_PSY rc=%d\n", rc);
+ }
+
/* Schedule work to enable parallel charger */
vote(chg->awake_votable, PL_DELAY_VOTER, true, 0);
schedule_delayed_work(&chg->pl_enable_work,
msecs_to_jiffies(PL_DELAY_MS));
+ schedule_delayed_work(&chg->monitor_low_temp_work,
+ msecs_to_jiffies(CHG_MONITOR_WORK_DELAY_MS));
+/*
+ schedule_delayed_work(&chg->cc_float_charge_work,
+ msecs_to_jiffies(CC_FLOAT_WORK_START_DELAY_MS));
+*/
+ schedule_delayed_work(&chg->typec_reenable_work,
+ msecs_to_jiffies(300));
} else {
+ cancel_delayed_work_sync(&chg->charger_type_recheck);
+ cancel_delayed_work_sync(&chg->typec_reenable_work);
+
if (chg->wa_flags & BOOST_BACK_WA) {
data = chg->irq_info[SWITCH_POWER_OK_IRQ].irq_data;
if (data) {
rc = smblib_request_dpdm(chg, false);
if (rc < 0)
smblib_err(chg, "Couldn't disable DPDM rc=%d\n", rc);
+ vote(chg->usb_icl_votable, CC_FLOAT_VOTER, false, 0);
+ if (chg->cc_float_detected) {
+ chg->cc_float_detected = false;
+ chg->real_charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ chg->usb_psy_desc.type = POWER_SUPPLY_TYPE_UNKNOWN;
+ }
+ chg->recheck_charger = false;
+ chg->legacy = false;
+ chg->precheck_charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+ vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0);
}
if (chg->micro_usb_mode)
smblib_micro_usb_plugin(chg, vbus_rising);
power_supply_changed(chg->usb_psy);
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: usbin-plugin %s\n",
+ smblib_dbg(chg, PR_OEM, "IRQ: usbin-plugin %s\n",
vbus_rising ? "attached" : "detached");
}
smblib_check_ov_condition(chg);
power_supply_changed(chg->usb_main_psy);
if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP) {
+ /* if unstandard hvdcp is detected, set opt freq to 5V freq */
+ if (chg->unstandard_hvdcp) {
+ smblib_set_opt_freq_buck(chg,
+ chg->chg_freq.freq_5V);
+ return;
+ }
rc = smblib_read(chg, QC_CHANGE_STATUS_REG, &stat);
if (rc < 0) {
smblib_err(chg,
case QC_9V_BIT:
smblib_set_opt_freq_buck(chg,
chg->chg_freq.freq_9V);
+ vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
break;
case QC_12V_BIT:
smblib_set_opt_freq_buck(chg,
chg->chg_freq.freq_12V);
+ vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
break;
default:
smblib_set_opt_freq_buck(chg,
bool rising)
{
const struct apsd_result *apsd_result;
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- int current_ua;
-#endif
int rc;
+ int current_ua;
if (!rising)
return;
/* the APSD done handler will set the USB supply type */
apsd_result = smblib_get_apsd_result(chg);
- if (get_effective_result(chg->hvdcp_hw_inov_dis_votable)) {
+
if (apsd_result->pst == POWER_SUPPLY_TYPE_USB_HVDCP) {
- /* force HVDCP2 to 9V if INOV is disabled */
- rc = smblib_masked_write(chg, CMD_HVDCP_2_REG,
- FORCE_9V_BIT, FORCE_9V_BIT);
- if (rc < 0)
- smblib_err(chg,
- "Couldn't force 9V HVDCP rc=%d\n", rc);
+ /* disable hardware INOV and force HVDCP2 to 9V */
+ if (!chg->check_vbus_once) {
+ vote(chg->hvdcp_hw_inov_dis_votable,
+ UNSTANDARD_QC2_VOTER, true, 0);
+ /* force HVDCP2 to 9V if INOV is disabled */
+ rc = smblib_masked_write(chg, CMD_HVDCP_2_REG,
+ FORCE_9V_BIT, FORCE_9V_BIT);
+ if (rc < 0)
+ smblib_err(chg,
+ "Couldn't force 9V HVDCP rc=%d\n", rc);
+ }
}
- }
- smblib_dbg(chg, PR_INTERRUPT, "IRQ: hvdcp-3p0-auth-done rising; %s detected\n",
- apsd_result->name);
-
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
if (apsd_result->bit & QC_2P0_BIT) {
- current_ua = HVDCP_CURRENT_UA;
+ if (!chg->unstandard_hvdcp)
+ current_ua = HVDCP_CURRENT_UA;
+ else
+ current_ua = DCP_CURRENT_UA;
+
+ if (!chg->check_vbus_once) {
+ schedule_delayed_work(&chg->check_vbus_work,
+ msecs_to_jiffies(CHECK_VBUS_WORK_DELAY_MS));
+ chg->check_vbus_once = true;
+ }
} else if (apsd_result->bit & QC_3P0_BIT) {
current_ua = HVDCP3_CURRENT_UA;
}
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true,
current_ua);
-#endif
+
+ smblib_dbg(chg, PR_OEM, "rising; %s detected and current was %d\n",
+ apsd_result->name, current_ua);
}
static void smblib_handle_hvdcp_check_timeout(struct smb_charger *chg,
static void smblib_force_legacy_icl(struct smb_charger *chg, int pst)
{
-#ifndef CONFIG_MACH_XIAOMI_MSM8998
int typec_mode;
int rp_ua;
-#endif
/* while PD is active it should have complete ICL control */
if (chg->pd_active)
*/
if (!is_client_vote_enabled(chg->usb_icl_votable,
USB_PSY_VOTER))
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 500000);
-#else
-
- vote(chg->usb_icl_votable, USB_PSY_VOTER, true, 100000);
-#endif
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
break;
case POWER_SUPPLY_TYPE_USB_CDP:
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
break;
case POWER_SUPPLY_TYPE_USB_DCP:
-#ifndef CONFIG_MACH_XIAOMI_MSM8998
typec_mode = smblib_get_prop_typec_mode(chg);
rp_ua = get_rp_based_dcp_current(chg, typec_mode);
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, rp_ua);
break;
-#endif
case POWER_SUPPLY_TYPE_USB_FLOAT:
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1800000);
-#else
/*
* limit ICL to 100mA, the USB driver will enumerate to check
* if this is a SDP and appropriately set the current
*/
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
-#endif
+ if (chg->recheck_charger)
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1000000);
+ else
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
break;
case POWER_SUPPLY_TYPE_USB_HVDCP:
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1500000);
break;
-#endif
case POWER_SUPPLY_TYPE_USB_HVDCP_3:
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 3000000);
break;
static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
{
const struct apsd_result *apsd_result;
+ union power_supply_propval pval = {0, };
+ int usb_present = 0, ret = 0;
+
+#ifdef CONFIG_FB
+ union power_supply_propval val = {0, };
+ int rc = 0;
+#endif
if (!rising)
return;
apsd_result = smblib_update_usb_type(chg);
+ if (is_client_vote_enabled(chg->usb_icl_votable,
+ CC_FLOAT_VOTER)) {
+ if (chg->typec_mode != POWER_SUPPLY_TYPEC_NONE)
+ vote(chg->usb_icl_votable, CC_FLOAT_VOTER, false, 0);
+ }
if (!chg->typec_legacy_valid)
smblib_force_legacy_icl(chg, apsd_result->pst);
/* if not DCP then no hvdcp timeout happens, Enable pd here. */
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER,
false, 0);
+ /* if floated charger is detected, and audio accessory set icl to 500 */
+ if (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER)
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 500000);
break;
case DCP_CHARGER_BIT:
if (chg->wa_flags & QC_CHARGER_DETECTION_WA_BIT)
break;
}
+ if (chg->float_rerun_apsd) {
+ ret = smblib_get_prop_usb_present(chg, &pval);
+ if (ret < 0) {
+ smblib_err(chg, "Couldn't get usb present ret = %d\n", ret);
+ return;
+ }
+ usb_present = pval.intval;
+ if (!usb_present)
+ return;
+ if (apsd_result->bit & QC_2P0_BIT) {
+ pval.intval = 0;
+ smblib_set_prop_pd_active(chg, &pval);
+ chg->float_rerun_apsd = false;
+ } else if (apsd_result->bit & FLOAT_CHARGER_BIT) {
+ vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 1000000);
+ chg->float_rerun_apsd = false;
+ }
+ }
+
+#ifdef CONFIG_FB
+ val.intval = chg->system_temp_level;
+ rc = power_supply_set_property(chg->batt_psy, POWER_SUPPLY_PROP_SYSTEM_TEMP_LEVEL, &val);
+ if (rc < 0) {
+ smblib_err(chg, "Could not set charger control limit =%d\n", rc);
+ return;
+ }
+#endif
+
smblib_dbg(chg, PR_INTERRUPT, "IRQ: apsd-done rising; %s detected\n",
apsd_result->name);
}
cancel_delayed_work_sync(&chg->pl_enable_work);
cancel_delayed_work_sync(&chg->hvdcp_detect_work);
+ cancel_delayed_work_sync(&chg->monitor_low_temp_work);
+ cancel_delayed_work_sync(&chg->check_vbus_work);
/* reset input current limit voters */
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, true, 100000);
vote(chg->usb_icl_votable, DCP_VOTER, false, 0);
vote(chg->usb_icl_votable, PL_USBIN_USBIN_VOTER, false, 0);
vote(chg->usb_icl_votable, SW_QC3_VOTER, false, 0);
+ vote(chg->usb_icl_votable, CC_FLOAT_VOTER, false, 0);
+ vote(chg->usb_icl_votable, HVDCP2_ICL_VOTER, false, 0);
+ vote(chg->usb_icl_votable, UNSTANDARD_QC2_VOTER, false, 0);
/* reset hvdcp voters */
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER, true, 0);
vote(chg->hvdcp_disable_votable_indirect, PD_INACTIVE_VOTER, true, 0);
vote(chg->hvdcp_hw_inov_dis_votable, OV_VOTER, false, 0);
+ vote(chg->hvdcp_hw_inov_dis_votable, UNSTANDARD_QC2_VOTER, false, 0);
/* reset power delivery voters */
vote(chg->pd_allowed_votable, PD_VOTER, false, 0);
vote(chg->pd_disallowed_votable_indirect, CC_DETACHED_VOTER, true, 0);
vote(chg->pd_disallowed_votable_indirect, HVDCP_TIMEOUT_VOTER, true, 0);
+ vote(chg->hvdcp_hw_inov_dis_votable, UNSTANDARD_QC2_VOTER, false, 0);
/* reset usb irq voters */
vote(chg->usb_irq_enable_votable, PD_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_I_VOTER, false, 0);
vote(chg->pl_enable_votable_indirect, USBIN_V_VOTER, false, 0);
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
+ vote(chg->awake_votable, CHG_AWAKE_VOTER, false, 0);
vote(chg->usb_icl_votable, USBIN_USBIN_BOOST_VOTER, false, 0);
chg->vconn_attempts = 0;
chg->pd_active = 0;
chg->pd_hard_reset = 0;
chg->typec_legacy_valid = false;
+ chg->cc_float_detected = false;
+ chg->float_rerun_apsd = false;
+ chg->check_vbus_once = false;
+ chg->unstandard_hvdcp = false;
/* write back the default FLOAT charger configuration */
rc = smblib_masked_write(chg, USBIN_OPTIONS_2_CFG_REG,
typec_sink_removal(chg);
smblib_update_usb_type(chg);
+ smblib_dbg(chg, PR_OEM, "done\n");
if (chg->use_extcon) {
if (chg->otg_present)
smblib_notify_usb_host(chg, false);
chg->typec_mode = typec_mode;
+ cancel_delayed_work_sync(&chg->typec_reenable_work);
+ cancel_delayed_work_sync(&chg->cc_float_charge_work);
+
if (!chg->typec_present && chg->typec_mode != POWER_SUPPLY_TYPEC_NONE) {
chg->typec_present = true;
- smblib_dbg(chg, PR_MISC, "TypeC %s insertion\n",
+ smblib_dbg(chg, PR_OEM, "TypeC %s insertion\n",
smblib_typec_mode_name[chg->typec_mode]);
smblib_handle_typec_insertion(chg);
+ schedule_delayed_work(&chg->charger_type_recheck, msecs_to_jiffies(20000));
} else if (chg->typec_present &&
chg->typec_mode == POWER_SUPPLY_TYPEC_NONE) {
+ cancel_delayed_work_sync(&chg->charger_type_recheck);
chg->typec_present = false;
- smblib_dbg(chg, PR_MISC, "TypeC removal\n");
+ smblib_dbg(chg, PR_OEM, "TypeC removal\n");
smblib_handle_typec_removal(chg);
}
return IRQ_HANDLED;
}
+ smblib_dbg(chg, PR_OEM, "enter\n");
+
if (chg->cc2_detach_wa_active || chg->typec_en_dis_active ||
chg->try_sink_active) {
- smblib_dbg(chg, PR_MISC | PR_INTERRUPT, "Ignoring since %s active\n",
+ smblib_dbg(chg, PR_OEM, "Ignoring since %s active\n",
chg->cc2_detach_wa_active ?
"cc2_detach_wa" : "typec_en_dis");
return IRQ_HANDLED;
vote(chg->awake_votable, PL_DELAY_VOTER, false, 0);
}
+#define CHARGING_PERIOD_S 500
+#define NOT_CHARGING_PERIOD_S 1800
+static void smblib_reg_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ reg_work.work);
+ int rc, usb_present;
+ union power_supply_propval val;
+ int icl_settle, usb_cur_in, usb_vol_in, charger_temp;
+ int main_fcc, parallel_fcc, parallel_charging_enable, connector_health;
+ int charge_type, typec_mode, typec_orientation;
+
+ dump_regs(chg);
+ rc = smblib_get_prop_usb_present(chg, &val);
+ if (rc < 0) {
+ pr_err("Couldn't get usb present rc=%d\n", rc);
+ schedule_delayed_work(&chg->reg_work,
+ NOT_CHARGING_PERIOD_S * HZ);
+ return;
+ }
+ usb_present = val.intval;
+
+ if (usb_present) {
+ smblib_dbg(chg, PR_OEM, "ICL vote value is %d voted by %s\n",
+ get_effective_result(chg->usb_icl_votable),
+ get_effective_client(chg->usb_icl_votable));
+ smblib_dbg(chg, PR_OEM, "FCC vote value is %d voted by %s\n",
+ get_effective_result(chg->fcc_votable),
+ get_effective_client(chg->fcc_votable));
+ smblib_dbg(chg, PR_OEM, "FV vote value is %d voted by %s\n",
+ get_effective_result(chg->fv_votable),
+ get_effective_client(chg->fv_votable));
+
+ power_supply_get_property(chg->usb_psy,
+ POWER_SUPPLY_PROP_INPUT_CURRENT_NOW, &val);
+ usb_cur_in = val.intval;
+
+ power_supply_get_property(chg->usb_psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val);
+ usb_vol_in = val.intval;
+
+ power_supply_get_property(chg->usb_psy,
+ POWER_SUPPLY_PROP_CURRENT_MAX, &val);
+ icl_settle = val.intval;
+
+ power_supply_get_property(chg->usb_psy,
+ POWER_SUPPLY_PROP_REAL_TYPE, &val);
+ charge_type = val.intval;
+
+ power_supply_get_property(chg->batt_psy,
+ POWER_SUPPLY_PROP_CHARGER_TEMP, &val);
+ charger_temp = val.intval;
+
+ power_supply_get_property(chg->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_MODE, &val);
+ typec_mode = val.intval;
+
+ power_supply_get_property(chg->usb_psy,
+ POWER_SUPPLY_PROP_TYPEC_CC_ORIENTATION, &val);
+ typec_orientation = val.intval;
+
+ smblib_dbg(chg, PR_OEM, "ICL settle value[%d], Charger_temp[%d], usbin adc current[%d], vbusin adc vol[%d]\n",
+ icl_settle, charger_temp, usb_cur_in, usb_vol_in);
+
+ if (!chg->usb_main_psy) {
+ chg->usb_main_psy = power_supply_get_by_name("main");
+ } else {
+ power_supply_get_property(chg->usb_main_psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &val);
+ main_fcc = val.intval;
+ smblib_dbg(chg, PR_OEM, "Main FCC[%d] ", main_fcc);
+ }
+
+ if (!chg->pl.psy) {
+ chg->pl.psy = power_supply_get_by_name("parallel");
+ } else {
+ power_supply_get_property(chg->pl.psy,
+ POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, &val);
+ parallel_fcc = val.intval;
+ power_supply_get_property(chg->pl.psy,
+ POWER_SUPPLY_PROP_INPUT_SUSPEND, &val);
+ parallel_charging_enable = !val.intval;
+ power_supply_get_property(chg->pl.psy,
+ POWER_SUPPLY_PROP_CONNECTOR_HEALTH, &val);
+ connector_health = val.intval;
+ smblib_dbg(chg, PR_OEM, "parallel ENABLE[%d] FCC[%d] CONNECTOR_HEALTH[%d]\n",
+ parallel_charging_enable, parallel_fcc, connector_health);
+ }
+
+ smblib_dbg(chg, PR_OEM, "Type-C orientation[%d], Type-C mode[%d], Real Charge Type[%d]\n",
+ typec_orientation, typec_mode, charge_type);
+
+ schedule_delayed_work(&chg->reg_work, CHARGING_PERIOD_S * HZ);
+ }
+ else
+ schedule_delayed_work(&chg->reg_work, NOT_CHARGING_PERIOD_S * HZ);
+}
+
static void smblib_legacy_detection_work(struct work_struct *work)
{
struct smb_charger *chg = container_of(work, struct smb_charger,
mutex_lock(&chg->lock);
chg->typec_en_dis_active = 1;
- smblib_dbg(chg, PR_MISC, "running legacy unknown workaround\n");
+ smblib_dbg(chg, PR_OEM, "running legacy unknown workaround\n");
rc = smblib_masked_write(chg,
TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
TYPEC_DISABLE_CMD_BIT,
smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
/* wait for type-c detection to complete */
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- msleep(1000);
-#else
msleep(400);
-#endif
rc = smblib_read(chg, TYPE_C_STATUS_5_REG, &stat);
if (rc < 0) {
vote(chg->usb_icl_votable, LEGACY_UNKNOWN_VOTER, false, 0);
legacy = stat & TYPEC_LEGACY_CABLE_STATUS_BIT;
rp_high = chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_HIGH;
- smblib_dbg(chg, PR_MISC, "legacy workaround done legacy = %d rp_high = %d\n",
+ chg->legacy = legacy;
+ smblib_dbg(chg, PR_OEM, "legacy workaround done legacy = %d rp_high = %d\n",
legacy, rp_high);
if (!legacy || !rp_high)
vote(chg->hvdcp_disable_votable_indirect, VBUS_CC_SHORT_VOTER,
mutex_unlock(&chg->lock);
}
+static void smblib_typec_reenable_work(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ typec_reenable_work.work);
+ int rc;
+ u8 stat;
+
+ if (chg->pr_swap_in_progress || chg->pd_hard_reset)
+ return;
+
+ mutex_lock(&chg->lock);
+
+ rc = smblib_read(chg, TYPE_C_STATUS_4_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read typec stat5 rc = %d\n", rc);
+ goto unlock;
+ }
+
+
+ if (stat == TYPEC_VBUS_STATUS_BIT) {
+ smblib_dbg(chg, PR_OEM, "running typec reenable workaround\n");
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT,
+ TYPEC_DISABLE_CMD_BIT);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable type-c rc=%d\n", rc);
+
+ msleep(200);
+
+ rc = smblib_masked_write(chg,
+ TYPE_C_INTRPT_ENB_SOFTWARE_CTRL_REG,
+ TYPEC_DISABLE_CMD_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't enable type-c rc=%d\n", rc);
+
+ /* wait for type-c detection to complete */
+
+ }
+
+unlock:
+
+ mutex_unlock(&chg->lock);
+
+ schedule_delayed_work(&chg->cc_float_charge_work,
+ msecs_to_jiffies(200));
+
+}
+
+#define TYPE_RECHECK_TIME_20S 20000
+#define TYPE_RECHECK_TIME_5S 5000
+#define TYPE_RECHECK_COUNT 3
+
+static void smblib_charger_type_recheck(struct work_struct *work)
+{
+ struct smb_charger *chg = container_of(work, struct smb_charger,
+ charger_type_recheck.work);
+ int rc, hvdcp;
+ u8 stat;
+ int recheck_time = TYPE_RECHECK_TIME_5S;
+ static int last_charger_type, check_count;
+
+ smblib_dbg(chg, PR_OEM, "typec_mode:%d,last:%d:real:%d\n",
+ chg->typec_mode, last_charger_type, chg->real_charger_type);
+
+ if (last_charger_type != chg->real_charger_type)
+ check_count--;
+ last_charger_type = chg->real_charger_type;
+
+ if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_HVDCP_3 ||
+ chg->pd_active || (check_count >= TYPE_RECHECK_COUNT) ||
+ chg->check_vbus_once ||
+ ((chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) &&
+ (chg->typec_mode == POWER_SUPPLY_TYPEC_SINK_AUDIO_ADAPTER))) {
+ smblib_dbg(chg, PR_MISC, "hvdcp detect or check_count = %d break\n",
+ check_count);
+ check_count = 0;
+ return;
+ }
+
+ if (chg->typec_mode == POWER_SUPPLY_TYPEC_NONE)
+ goto check_next;
+
+ if (!chg->recheck_charger)
+ chg->precheck_charger_type = chg->real_charger_type;
+ chg->recheck_charger = true;
+
+ /* disable APSD CC trigger since CC is attached */
+ rc = smblib_masked_write(chg, TYPE_C_CFG_REG, APSD_START_ON_CC_BIT, 0);
+ if (rc < 0)
+ smblib_err(chg, "Couldn't disable APSD_START_ON_CC rc=%d\n", rc);
+
+ rc = smblib_read(chg, APSD_STATUS_REG, &stat);
+ if (rc < 0) {
+ smblib_err(chg, "Couldn't read APSD status rc=%d\n", rc);
+ return;
+ }
+ hvdcp = stat & QC_CHARGER_BIT;
+ smblib_dbg(chg, PR_OEM, "hvdcp:%d chg->legacy:%d check_count:%d\n",
+ hvdcp, chg->legacy, check_count);
+
+ if (hvdcp && !chg->legacy) {
+ recheck_time = TYPE_RECHECK_TIME_20S;
+ __smblib_set_prop_pd_active(chg, 0);
+ } else
+ smblib_rerun_apsd_if_required(chg);
+
+check_next:
+ check_count++;
+ schedule_delayed_work(&chg->charger_type_recheck, msecs_to_jiffies(recheck_time));
+}
+
static int smblib_create_votables(struct smb_charger *chg)
{
int rc = 0;
mutex_init(&chg->lock);
mutex_init(&chg->write_lock);
mutex_init(&chg->otg_oc_lock);
+#ifdef CONFIG_FB
+ mutex_init(&chg->screen_lock);
+#endif
mutex_init(&chg->vconn_oc_lock);
INIT_WORK(&chg->bms_update_work, bms_update_work);
INIT_WORK(&chg->rdstd_cc2_detach_work, rdstd_cc2_detach_work);
INIT_WORK(&chg->otg_oc_work, smblib_otg_oc_work);
INIT_WORK(&chg->vconn_oc_work, smblib_vconn_oc_work);
INIT_DELAYED_WORK(&chg->otg_ss_done_work, smblib_otg_ss_done_work);
+ INIT_DELAYED_WORK(&chg->reg_work, smblib_reg_work);
INIT_DELAYED_WORK(&chg->icl_change_work, smblib_icl_change_work);
INIT_DELAYED_WORK(&chg->pl_enable_work, smblib_pl_enable_work);
INIT_WORK(&chg->legacy_detection_work, smblib_legacy_detection_work);
INIT_DELAYED_WORK(&chg->uusb_otg_work, smblib_uusb_otg_work);
INIT_DELAYED_WORK(&chg->bb_removal_work, smblib_bb_removal_work);
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- INIT_DELAYED_WORK(&chg->fb_state_work, smblib_fb_state_work);
-#endif
-
+ INIT_DELAYED_WORK(&chg->monitor_low_temp_work, smblib_monitor_low_temp_work);
+ INIT_DELAYED_WORK(&chg->cc_float_charge_work, smblib_cc_float_charge_work);
+ INIT_DELAYED_WORK(&chg->typec_reenable_work, smblib_typec_reenable_work);
+ INIT_DELAYED_WORK(&chg->charger_type_recheck, smblib_charger_type_recheck);
+ INIT_DELAYED_WORK(&chg->check_vbus_work, smblib_check_vbus_work);
chg->fake_capacity = -EINVAL;
chg->fake_input_current_limited = -EINVAL;
return rc;
}
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- chg->fb_state_notifier.notifier_call = smblib_fb_state_cb;
- rc = fb_register_client(&chg->fb_state_notifier);
- if (rc < 0) {
- smblib_err(chg,
- "Couldn't register notifier rc=%d\n", rc);
- return rc;
- }
-#endif
-
chg->bms_psy = power_supply_get_by_name("bms");
chg->pl.psy = power_supply_get_by_name("parallel");
+#ifdef CONFIG_FB
+ if (&chg->smb_fb_notif) {
+ chg->checking_in_progress = false;
+ chg->smb_fb_notif.notifier_call = smblib_fb_notifier_cb;
+ rc = fb_register_client(&chg->smb_fb_notif);
+ if (rc < 0) {
+ smblib_err(chg,
+ "Couldn't register notifier rc=%d\n", rc);
+ return rc;
+ }
+ INIT_DELAYED_WORK(&chg->screen_on_work, smblib_screen_on_work);
+ } else
+ smblib_err(chg, "Unsupported fb notifier \n");
+#endif
break;
case PARALLEL_SLAVE:
break;
cancel_work_sync(&chg->legacy_detection_work);
cancel_delayed_work_sync(&chg->uusb_otg_work);
cancel_delayed_work_sync(&chg->bb_removal_work);
-#ifdef CONFIG_MACH_XIAOMI_MSM8998
- cancel_delayed_work_sync(&chg->fb_state_work);
- fb_unregister_client(&chg->fb_state_notifier);
-#endif
+ cancel_delayed_work_sync(&chg->typec_reenable_work);
+ cancel_delayed_work_sync(&chg->charger_type_recheck);
+ cancel_delayed_work_sync(&chg->check_vbus_work);
power_supply_unreg_notifier(&chg->nb);
smblib_destroy_votables(chg);
qcom_step_chg_deinit();
smblib_err(chg, "Unsupported mode %d\n", chg->mode);
return -EINVAL;
}
-
+#ifdef CONFIG_FB
+ fb_unregister_client(&chg->smb_fb_notif);
+#endif
smblib_iio_deinit(chg);
return 0;
}
+
+static int __init early_parse_off_charge_flag(char *p)
+{
+ if (p) {
+ if (!strcmp(p, "charger"))
+ off_charge_flag = true;
+ }
+
+ return 0;
+}
+early_param("androidboot.mode", early_parse_off_charge_flag);