Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 38e9705a authored by Fenglin Wu's avatar Fenglin Wu
Browse files

power: qpnp-fg: Add features to support external charger



Add following optional features for qpnp-fg to work with external
charger:
  1) Ignore false negative isense as requested;
  2) Enable soft JEITA irq as requested and use it for hard JEITA
     detection.

CRs-Fixed: 991446
Change-Id: I3bc8f312de404e4bcade6382cab2f0bd340de88f
Signed-off-by: default avatarFenglin Wu <fenglinw@codeaurora.org>
parent e3e3c695
Loading
Loading
Loading
Loading
+307 −1
Original line number Diff line number Diff line
@@ -337,7 +337,8 @@ module_param_named(

struct fg_irq {
	int			irq;
	unsigned long		disabled;
	bool			disabled;
	bool			wakeup;
};

enum fg_soc_irq {
@@ -521,6 +522,8 @@ struct fg_chip {
	bool			charging_disabled;
	bool			use_vbat_low_empty_soc;
	bool			fg_shutdown;
	bool			use_soft_jeita_irq;
	bool			allow_false_negative_isense;
	struct delayed_work	update_jeita_setting;
	struct delayed_work	update_sram_data;
	struct delayed_work	update_temp_work;
@@ -545,6 +548,7 @@ struct fg_chip {
	int			prev_status;
	int			health;
	enum fg_batt_aging_mode	batt_aging_mode;
	struct alarm		hard_jeita_alarm;
	/* capacity learning */
	struct fg_learning_data	learning_data;
	struct alarm		fg_cap_learning_alarm;
@@ -565,6 +569,8 @@ struct fg_chip {
	bool			jeita_hysteresis_support;
	bool			batt_hot;
	bool			batt_cold;
	bool			batt_warm;
	bool			batt_cool;
	int			cold_hysteresis;
	int			hot_hysteresis;
	/* ESR pulse tuning */
@@ -2286,6 +2292,25 @@ static int set_prop_sense_type(struct fg_chip *chip, int ext_sense_type)
	return 0;
}

#define IGNORE_FALSE_NEGATIVE_ISENSE_BIT	BIT(3)
static int set_prop_ignore_false_negative_isense(struct fg_chip *chip,
							bool ignore)
{
	int rc;

	rc = fg_mem_masked_write(chip, EXTERNAL_SENSE_SELECT,
			IGNORE_FALSE_NEGATIVE_ISENSE_BIT,
			ignore ? IGNORE_FALSE_NEGATIVE_ISENSE_BIT : 0,
			EXTERNAL_SENSE_OFFSET);
	if (rc) {
		pr_err("failed to %s isense false negative ignore rc=%d\n",
				ignore ? "enable" : "disable", rc);
		return rc;
	}

	return 0;
}

#define EXPONENT_MASK		0xF800
#define MANTISSA_MASK		0x3FF
#define SIGN			BIT(10)
@@ -3220,6 +3245,8 @@ static enum power_supply_property fg_power_props[] = {
	POWER_SUPPLY_PROP_CYCLE_COUNT_ID,
	POWER_SUPPLY_PROP_HI_POWER,
	POWER_SUPPLY_PROP_SOC_REPORTING_READY,
	POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE,
	POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION,
};

static int fg_power_get_property(struct power_supply *psy,
@@ -3250,6 +3277,12 @@ static int fg_power_get_property(struct power_supply *psy,
	case POWER_SUPPLY_PROP_CURRENT_NOW:
		val->intval = get_sram_prop_now(chip, FG_DATA_CURRENT);
		break;
	case POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE:
		val->intval = !chip->allow_false_negative_isense;
		break;
	case POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION:
		val->intval = chip->use_soft_jeita_irq;
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
		val->intval = get_sram_prop_now(chip, FG_DATA_VOLTAGE);
		break;
@@ -4252,6 +4285,67 @@ static int fg_power_set_property(struct power_supply *psy,
		if (val->intval)
			update_sram_data(chip, &unused);
		break;
	case POWER_SUPPLY_PROP_IGNORE_FALSE_NEGATIVE_ISENSE:
		rc = set_prop_ignore_false_negative_isense(chip, !!val->intval);
		if (rc)
			pr_err("set_prop_ignore_false_negative_isense failed, rc=%d\n",
							rc);
		else
			chip->allow_false_negative_isense = !val->intval;
		break;
	case POWER_SUPPLY_PROP_ENABLE_JEITA_DETECTION:
		if (chip->use_soft_jeita_irq == !!val->intval) {
			pr_debug("JEITA irq %s, ignore!\n",
				chip->use_soft_jeita_irq ?
				"enabled" : "disabled");
			break;
		}
		chip->use_soft_jeita_irq = !!val->intval;
		if (chip->use_soft_jeita_irq) {
			if (chip->batt_irq[JEITA_SOFT_COLD].disabled) {
				enable_irq(
					chip->batt_irq[JEITA_SOFT_COLD].irq);
				chip->batt_irq[JEITA_SOFT_COLD].disabled =
								false;
			}
			if (!chip->batt_irq[JEITA_SOFT_COLD].wakeup) {
				enable_irq_wake(
					chip->batt_irq[JEITA_SOFT_COLD].irq);
				chip->batt_irq[JEITA_SOFT_COLD].wakeup = true;
			}
			if (chip->batt_irq[JEITA_SOFT_HOT].disabled) {
				enable_irq(
					chip->batt_irq[JEITA_SOFT_HOT].irq);
				chip->batt_irq[JEITA_SOFT_HOT].disabled = false;
			}
			if (!chip->batt_irq[JEITA_SOFT_HOT].wakeup) {
				enable_irq_wake(
					chip->batt_irq[JEITA_SOFT_HOT].irq);
				chip->batt_irq[JEITA_SOFT_HOT].wakeup = true;
			}
		} else {
			if (chip->batt_irq[JEITA_SOFT_COLD].wakeup) {
				disable_irq_wake(
					chip->batt_irq[JEITA_SOFT_COLD].irq);
				chip->batt_irq[JEITA_SOFT_COLD].wakeup = false;
			}
			if (!chip->batt_irq[JEITA_SOFT_COLD].disabled) {
				disable_irq_nosync(
					chip->batt_irq[JEITA_SOFT_COLD].irq);
				chip->batt_irq[JEITA_SOFT_COLD].disabled = true;
			}
			if (chip->batt_irq[JEITA_SOFT_HOT].wakeup) {
				disable_irq_wake(
					chip->batt_irq[JEITA_SOFT_HOT].irq);
				chip->batt_irq[JEITA_SOFT_HOT].wakeup = false;
			}
			if (!chip->batt_irq[JEITA_SOFT_HOT].disabled) {
				disable_irq_nosync(
					chip->batt_irq[JEITA_SOFT_HOT].irq);
				chip->batt_irq[JEITA_SOFT_HOT].disabled = true;
			}
		}
		break;
	case POWER_SUPPLY_PROP_STATUS:
		chip->prev_status = chip->status;
		chip->status = val->intval;
@@ -4535,6 +4629,168 @@ static void cc_soc_store_work(struct work_struct *work)
	fg_relax(&chip->cc_soc_wakeup_source);
}

#define HARD_JEITA_ALARM_CHECK_NS	10000000000
static enum alarmtimer_restart fg_hard_jeita_alarm_cb(struct alarm *alarm,
						ktime_t now)
{
	struct fg_chip *chip = container_of(alarm,
			struct fg_chip, hard_jeita_alarm);
	int rc, health = POWER_SUPPLY_HEALTH_UNKNOWN;
	u8 regval;
	bool batt_hot, batt_cold;
	union power_supply_propval val = {0, };

	if (!is_usb_present(chip)) {
		pr_debug("USB plugged out, stop the timer!\n");
		return ALARMTIMER_NORESTART;
	}

	rc = fg_read(chip, &regval, BATT_INFO_STS(chip->batt_base), 1);
	if (rc) {
		pr_err("read batt_sts failed, rc=%d\n", rc);
		goto recheck;
	}

	batt_hot = !!(regval & JEITA_HARD_HOT_RT_STS);
	batt_cold = !!(regval & JEITA_HARD_COLD_RT_STS);
	if (batt_hot && batt_cold) {
		pr_debug("Hot && cold can't co-exist\n");
		goto recheck;
	}

	if ((batt_hot == chip->batt_hot) && (batt_cold == chip->batt_cold)) {
		pr_debug("battery JEITA state not changed, ignore\n");
		goto recheck;
	}

	if (batt_cold != chip->batt_cold) {
		/* cool --> cold */
		if (chip->batt_cool) {
			chip->batt_cool = false;
			chip->batt_cold = true;
			health = POWER_SUPPLY_HEALTH_COLD;
		} else if (chip->batt_cold) { /* cold --> cool */
			chip->batt_cool = true;
			chip->batt_cold = false;
			health = POWER_SUPPLY_HEALTH_COOL;
		}
	}

	if (batt_hot != chip->batt_hot) {
		/* warm --> hot */
		if (chip->batt_warm) {
			chip->batt_warm = false;
			chip->batt_hot = true;
			health = POWER_SUPPLY_HEALTH_OVERHEAT;
		} else if (chip->batt_hot) { /* hot --> warm */
			chip->batt_hot = false;
			chip->batt_warm = true;
			health = POWER_SUPPLY_HEALTH_WARM;
		}
	}

	if (health != POWER_SUPPLY_HEALTH_UNKNOWN) {
		pr_debug("FG report battery health: %d\n", health);
		val.intval = health;
		rc = chip->batt_psy->set_property(chip->batt_psy,
				POWER_SUPPLY_PROP_HEALTH, &val);
		if (rc)
			pr_err("Set batt_psy health: %d failed\n", health);
	}

recheck:
	alarm_forward_now(alarm, ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
	return ALARMTIMER_RESTART;
}

#define BATT_SOFT_COLD_STS	BIT(0)
#define BATT_SOFT_HOT_STS	BIT(1)
static irqreturn_t fg_jeita_soft_hot_irq_handler(int irq, void *_chip)
{
	int rc;
	struct fg_chip *chip = _chip;
	u8 regval;
	bool batt_warm;
	union power_supply_propval val = {0, };

	rc = fg_read(chip, &regval, INT_RT_STS(chip->batt_base), 1);
	if (rc) {
		pr_err("spmi read failed: addr=%03X, rc=%d\n",
				INT_RT_STS(chip->batt_base), rc);
		return IRQ_HANDLED;
	}

	batt_warm = !!(regval & BATT_SOFT_HOT_STS);
	if (chip->batt_warm == batt_warm) {
		pr_debug("warm state not change, ignore!\n");
		return IRQ_HANDLED;
	}

	chip->batt_warm = batt_warm;
	if (batt_warm) {
		val.intval = POWER_SUPPLY_HEALTH_WARM;
		chip->batt_psy->set_property(chip->batt_psy,
			POWER_SUPPLY_PROP_HEALTH, &val);
		/* kick the alarm timer for hard hot polling */
		rc = alarm_start_relative(&chip->hard_jeita_alarm,
				ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
		if (rc)
			pr_err("start alarm for hard HOT detection failed, rc=%d\n",
									rc);
	} else {
		val.intval = POWER_SUPPLY_HEALTH_GOOD;
		chip->batt_psy->set_property(chip->batt_psy,
			POWER_SUPPLY_PROP_HEALTH, &val);
		/* cancel the alarm timer */
		alarm_try_to_cancel(&chip->hard_jeita_alarm);
	}

	return IRQ_HANDLED;
}

static irqreturn_t fg_jeita_soft_cold_irq_handler(int irq, void *_chip)
{
	int rc;
	struct fg_chip *chip = _chip;
	u8 regval;
	bool batt_cool;
	union power_supply_propval val = {0, };

	rc = fg_read(chip, &regval, INT_RT_STS(chip->batt_base), 1);
	if (rc) {
		pr_err("spmi read failed: addr=%03X, rc=%d\n",
				INT_RT_STS(chip->batt_base), rc);
		return IRQ_HANDLED;
	}

	batt_cool = !!(regval & BATT_SOFT_COLD_STS);
	if (chip->batt_cool == batt_cool) {
		pr_debug("cool state not change, ignore\n");
		return IRQ_HANDLED;
	}

	chip->batt_cool = batt_cool;
	if (batt_cool) {
		val.intval = POWER_SUPPLY_HEALTH_COOL;
		chip->batt_psy->set_property(chip->batt_psy,
			POWER_SUPPLY_PROP_HEALTH, &val);
		/* kick the alarm timer for hard cold polling */
		rc = alarm_start_relative(&chip->hard_jeita_alarm,
				ns_to_ktime(HARD_JEITA_ALARM_CHECK_NS));
		if (rc)
			pr_err("start alarm for hard COLD detection failed, rc=%d\n",
									rc);
	} else {
		val.intval = POWER_SUPPLY_HEALTH_GOOD;
		chip->batt_psy->set_property(chip->batt_psy,
			POWER_SUPPLY_PROP_HEALTH, &val);
		/* cancel the alarm timer */
		alarm_try_to_cancel(&chip->hard_jeita_alarm);
	}

	return IRQ_HANDLED;
}

#define SOC_FIRST_EST_DONE	BIT(5)
static bool is_first_est_done(struct fg_chip *chip)
{
@@ -6690,6 +6946,53 @@ static int fg_init_irqs(struct fg_chip *chip)
			}
			break;
		case FG_BATT:
			chip->batt_irq[JEITA_SOFT_COLD].irq =
				spmi_get_irq_byname(chip->spmi, spmi_resource,
						"soft-cold");
			if (chip->batt_irq[JEITA_SOFT_COLD].irq < 0) {
				pr_err("Unable to get soft-cold irq\n");
				rc = -EINVAL;
				return rc;
			}
			rc = devm_request_threaded_irq(chip->dev,
					chip->batt_irq[JEITA_SOFT_COLD].irq,
					NULL,
					fg_jeita_soft_cold_irq_handler,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING |
					IRQF_ONESHOT,
					"soft-cold", chip);
			if (rc < 0) {
				pr_err("Can't request %d soft-cold: %d\n",
					chip->batt_irq[JEITA_SOFT_COLD].irq,
								rc);
				return rc;
			}
			disable_irq(chip->batt_irq[JEITA_SOFT_COLD].irq);
			chip->batt_irq[JEITA_SOFT_COLD].disabled = true;
			chip->batt_irq[JEITA_SOFT_HOT].irq =
				spmi_get_irq_byname(chip->spmi, spmi_resource,
					"soft-hot");
			if (chip->batt_irq[JEITA_SOFT_HOT].irq < 0) {
				pr_err("Unable to get soft-hot irq\n");
				rc = -EINVAL;
				return rc;
			}
			rc = devm_request_threaded_irq(chip->dev,
					chip->batt_irq[JEITA_SOFT_HOT].irq,
					NULL,
					fg_jeita_soft_hot_irq_handler,
					IRQF_TRIGGER_RISING |
					IRQF_TRIGGER_FALLING |
					IRQF_ONESHOT,
					"soft-hot", chip);
			if (rc < 0) {
				pr_err("Can't request %d soft-hot: %d\n",
					chip->batt_irq[JEITA_SOFT_HOT].irq, rc);
				return rc;
			}
			disable_irq(chip->batt_irq[JEITA_SOFT_HOT].irq);
			chip->batt_irq[JEITA_SOFT_HOT].disabled = true;
			chip->batt_irq[BATT_MISSING].irq = spmi_get_irq_byname(
					chip->spmi, spmi_resource,
					"batt-missing");
@@ -6760,6 +7063,7 @@ static void fg_cancel_all_works(struct fg_chip *chip)
	cancel_delayed_work_sync(&chip->check_empty_work);
	cancel_delayed_work_sync(&chip->batt_profile_init);
	alarm_try_to_cancel(&chip->fg_cap_learning_alarm);
	alarm_try_to_cancel(&chip->hard_jeita_alarm);
	if (!chip->ima_error_handling)
		cancel_work_sync(&chip->ima_error_recovery_work);
	cancel_work_sync(&chip->rslow_comp_work);
@@ -8065,6 +8369,8 @@ static int fg_probe(struct spmi_device *spmi)
	INIT_WORK(&chip->cc_soc_store_work, cc_soc_store_work);
	alarm_init(&chip->fg_cap_learning_alarm, ALARM_BOOTTIME,
			fg_cap_learning_alarm_cb);
	alarm_init(&chip->hard_jeita_alarm, ALARM_BOOTTIME,
			fg_hard_jeita_alarm_cb);
	init_completion(&chip->sram_access_granted);
	init_completion(&chip->sram_access_revoked);
	complete_all(&chip->sram_access_revoked);