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

Commit c0a18e29 authored by Abhijeet Dharmapurikar's avatar Abhijeet Dharmapurikar Committed by Subbaraman Narayanamurthy
Browse files

qpnp-qnovo: add debounce for charger



The request is to delay ok_to_qnovo for 15 seconds after a charger is
inserted.

To achive that:
Make ok_to_qnovo variable as a votable that can be voted on by
A. HW error conditions
B. Charger is ready  after debounce

Moreover since there could be two chargers -usb and dc, create another
votable to track their individual debounce states. The result of this
votable will feed in to ok_to_qnovo votable as branch B above.

Also since we need to stay awake for the debounce time, create an
awake_votable and vote on it while debounce on either usb or dc is in
progress.

Change-Id: I32dc07ba16ef0515a9683f2702d317c39baa2eba
Signed-off-by: default avatarAbhijeet Dharmapurikar <adharmap@codeaurora.org>
parent 12cb0ec9
Loading
Loading
Loading
Loading
+184 −18
Original line number Diff line number Diff line
@@ -119,6 +119,11 @@
#define QNI_PT_VOTER		"QNI_PT_VOTER"
#define ESR_VOTER		"ESR_VOTER"

#define HW_OK_TO_QNOVO_VOTER	"HW_OK_TO_QNOVO_VOTER"
#define CHG_READY_VOTER		"CHG_READY_VOTER"
#define USB_READY_VOTER		"USB_READY_VOTER"
#define DC_READY_VOTER		"DC_READY_VOTER"

struct qnovo_dt_props {
	bool			external_rsense;
	struct device_node	*revid_dev_node;
@@ -132,6 +137,9 @@ struct qnovo {
	struct device		*dev;
	struct votable		*disable_votable;
	struct votable		*pt_dis_votable;
	struct votable		*not_ok_to_qnovo_votable;
	struct votable		*chg_ready_votable;
	struct votable		*awake_votable;
	struct class		qnovo_class;
	struct pmic_revid_data	*pmic_rev_id;
	u32			wa_flags;
@@ -144,10 +152,15 @@ struct qnovo {
	struct notifier_block	nb;
	struct power_supply	*batt_psy;
	struct power_supply	*bms_psy;
	struct power_supply	*usb_psy;
	struct power_supply	*dc_psy;
	struct work_struct	status_change_work;
	int			fv_uV_request;
	int			fcc_uA_request;
	bool			ok_to_qnovo;
	int			usb_present;
	int			dc_present;
	struct delayed_work	usb_debounce_work;
	struct delayed_work	dc_debounce_work;
};

static int debug_mask;
@@ -246,6 +259,28 @@ static bool is_fg_available(struct qnovo *chip)
	return true;
}

static bool is_usb_available(struct qnovo *chip)
{
	if (!chip->usb_psy)
		chip->usb_psy = power_supply_get_by_name("usb");

	if (!chip->usb_psy)
		return false;

	return true;
}

static bool is_dc_available(struct qnovo *chip)
{
	if (!chip->dc_psy)
		chip->dc_psy = power_supply_get_by_name("dc");

	if (!chip->dc_psy)
		return false;

	return true;
}

static int qnovo_batt_psy_update(struct qnovo *chip, bool disable)
{
	union power_supply_propval pval = {0};
@@ -330,6 +365,43 @@ static int pt_dis_votable_cb(struct votable *votable, void *data, int disable,
	return 0;
}

static int not_ok_to_qnovo_cb(struct votable *votable, void *data,
					int not_ok_to_qnovo,
					const char *client)
{
	struct qnovo *chip = data;

	vote(chip->disable_votable, OK_TO_QNOVO_VOTER, not_ok_to_qnovo, 0);
	if (not_ok_to_qnovo)
		vote(chip->disable_votable, USER_VOTER, true, 0);

	kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
	return 0;
}

static int chg_ready_cb(struct votable *votable, void *data, int ready,
					const char *client)
{
	struct qnovo *chip = data;

	vote(chip->not_ok_to_qnovo_votable, CHG_READY_VOTER, !ready, 0);

	return 0;
}

static int awake_cb(struct votable *votable, void *data, int awake,
					const char *client)
{
	struct qnovo *chip = data;

	if (awake)
		pm_stay_awake(chip->dev);
	else
		pm_relax(chip->dev);

	return 0;
}

static int qnovo_parse_dt(struct qnovo *chip)
{
	struct device_node *node = chip->dev->of_node;
@@ -671,8 +743,9 @@ static ssize_t ok_to_qnovo_show(struct class *c, struct class_attribute *attr,
			char *buf)
{
	struct qnovo *chip = container_of(c, struct qnovo, qnovo_class);
	int val = get_effective_result(chip->not_ok_to_qnovo_votable);

	return snprintf(buf, PAGE_SIZE, "%d\n", chip->ok_to_qnovo);
	return snprintf(buf, PAGE_SIZE, "%d\n", !val);
}

static ssize_t qnovo_enable_show(struct class *c, struct class_attribute *attr,
@@ -1141,43 +1214,101 @@ static int qnovo_update_status(struct qnovo *chip)
{
	u8 val = 0;
	int rc;
	bool ok_to_qnovo;
	bool changed = false;
	bool hw_ok_to_qnovo;

	rc = qnovo_read(chip, QNOVO_ERROR_STS2, &val, 1);
	if (rc < 0) {
		pr_err("Couldn't read error sts rc = %d\n", rc);
		ok_to_qnovo = false;
		hw_ok_to_qnovo = false;
	} else {
		/*
		 * For CV mode keep qnovo enabled, userspace is expected to
		 * disable it after few runs
		 */
		ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ? true : false;
		hw_ok_to_qnovo = (val == ERR_CV_MODE || val == 0) ?
			true : false;
	}

	if (chip->ok_to_qnovo ^ ok_to_qnovo) {
		vote(chip->disable_votable, OK_TO_QNOVO_VOTER, !ok_to_qnovo, 0);
		if (!ok_to_qnovo)
			vote(chip->disable_votable, USER_VOTER, true, 0);
	vote(chip->not_ok_to_qnovo_votable, HW_OK_TO_QNOVO_VOTER,
					!hw_ok_to_qnovo, 0);
	return 0;
}

static void usb_debounce_work(struct work_struct *work)
{
	struct qnovo *chip = container_of(work,
				struct qnovo, usb_debounce_work.work);

		chip->ok_to_qnovo = ok_to_qnovo;
		changed = true;
	vote(chip->chg_ready_votable, USB_READY_VOTER, true, 0);
	vote(chip->awake_votable, USB_READY_VOTER, false, 0);
}

	return changed;
static void dc_debounce_work(struct work_struct *work)
{
	struct qnovo *chip = container_of(work,
				struct qnovo, dc_debounce_work.work);

	vote(chip->chg_ready_votable, DC_READY_VOTER, true, 0);
	vote(chip->awake_votable, DC_READY_VOTER, false, 0);
}

#define DEBOUNCE_MS 15000  /* 15 seconds */
static void status_change_work(struct work_struct *work)
{
	struct qnovo *chip = container_of(work,
			struct qnovo, status_change_work);
	union power_supply_propval pval;
	bool usb_present = false, dc_present = false;
	int rc;

	if (is_fg_available(chip))
		vote(chip->disable_votable, FG_AVAILABLE_VOTER, false, 0);

	if (qnovo_update_status(chip))
		kobject_uevent(&chip->dev->kobj, KOBJ_CHANGE);
	if (is_usb_available(chip)) {
		rc = power_supply_get_property(chip->usb_psy,
				POWER_SUPPLY_PROP_PRESENT, &pval);
		usb_present = (rc < 0) ? 0 : pval.intval;
	}

	if (chip->usb_present && !usb_present) {
		/* removal */
		chip->usb_present = 0;
		cancel_delayed_work_sync(&chip->usb_debounce_work);
		vote(chip->awake_votable, USB_READY_VOTER, false, 0);
		vote(chip->chg_ready_votable, USB_READY_VOTER, false, 0);
	} else if (!chip->usb_present && usb_present) {
		/* insertion */
		chip->usb_present = 1;
		vote(chip->awake_votable, USB_READY_VOTER, true, 0);
		schedule_delayed_work(&chip->usb_debounce_work,
				msecs_to_jiffies(DEBOUNCE_MS));
	}

	if (is_dc_available(chip)) {
		rc = power_supply_get_property(chip->dc_psy,
			POWER_SUPPLY_PROP_PRESENT,
			&pval);
		dc_present = (rc < 0) ? 0 : pval.intval;
	}

	if (usb_present)
		dc_present = 0;

	if (chip->dc_present && !dc_present) {
		/* removal */
		chip->dc_present = 0;
		cancel_delayed_work_sync(&chip->dc_debounce_work);
		vote(chip->awake_votable, DC_READY_VOTER, false, 0);
		vote(chip->chg_ready_votable, DC_READY_VOTER, false, 0);
	} else if (!chip->dc_present && dc_present) {
		/* insertion */
		chip->dc_present = 1;
		vote(chip->awake_votable, DC_READY_VOTER, true, 0);
		schedule_delayed_work(&chip->dc_debounce_work,
				msecs_to_jiffies(DEBOUNCE_MS));
	}

	qnovo_update_status(chip);
}

static int qnovo_notifier_call(struct notifier_block *nb,
@@ -1190,7 +1321,9 @@ static int qnovo_notifier_call(struct notifier_block *nb,
		return NOTIFY_OK;

	if (strcmp(psy->desc->name, "battery") == 0
		|| strcmp(psy->desc->name, "bms") == 0)
		|| strcmp(psy->desc->name, "bms") == 0
		|| strcmp(psy->desc->name, "usb") == 0
		|| strcmp(psy->desc->name, "dc") == 0)
		schedule_work(&chip->status_change_work);

	return NOTIFY_OK;
@@ -1405,12 +1538,37 @@ static int qnovo_probe(struct platform_device *pdev)
		goto destroy_disable_votable;
	}

	chip->not_ok_to_qnovo_votable = create_votable("QNOVO_NOT_OK",
					VOTE_SET_ANY,
					not_ok_to_qnovo_cb, chip);
	if (IS_ERR(chip->not_ok_to_qnovo_votable)) {
		rc = PTR_ERR(chip->not_ok_to_qnovo_votable);
		goto destroy_pt_dis_votable;
	}

	chip->chg_ready_votable = create_votable("QNOVO_CHG_READY",
					VOTE_SET_ANY,
					chg_ready_cb, chip);
	if (IS_ERR(chip->chg_ready_votable)) {
		rc = PTR_ERR(chip->chg_ready_votable);
		goto destroy_not_ok_to_qnovo_votable;
	}

	chip->awake_votable = create_votable("QNOVO_AWAKE", VOTE_SET_ANY,
					awake_cb, chip);
	if (IS_ERR(chip->awake_votable)) {
		rc = PTR_ERR(chip->awake_votable);
		goto destroy_chg_ready_votable;
	}

	INIT_WORK(&chip->status_change_work, status_change_work);
	INIT_DELAYED_WORK(&chip->dc_debounce_work, dc_debounce_work);
	INIT_DELAYED_WORK(&chip->usb_debounce_work, usb_debounce_work);

	rc = qnovo_hw_init(chip);
	if (rc < 0) {
		pr_err("Couldn't initialize hardware rc=%d\n", rc);
		goto destroy_pt_dis_votable;
		goto destroy_awake_votable;
	}

	rc = qnovo_register_notifier(chip);
@@ -1446,6 +1604,12 @@ static int qnovo_probe(struct platform_device *pdev)

unreg_notifier:
	power_supply_unreg_notifier(&chip->nb);
destroy_awake_votable:
	destroy_votable(chip->awake_votable);
destroy_chg_ready_votable:
	destroy_votable(chip->chg_ready_votable);
destroy_not_ok_to_qnovo_votable:
	destroy_votable(chip->not_ok_to_qnovo_votable);
destroy_pt_dis_votable:
	destroy_votable(chip->pt_dis_votable);
destroy_disable_votable:
@@ -1461,6 +1625,8 @@ static int qnovo_remove(struct platform_device *pdev)

	class_unregister(&chip->qnovo_class);
	power_supply_unreg_notifier(&chip->nb);
	destroy_votable(chip->chg_ready_votable);
	destroy_votable(chip->not_ok_to_qnovo_votable);
	destroy_votable(chip->pt_dis_votable);
	destroy_votable(chip->disable_votable);
	platform_set_drvdata(pdev, NULL);