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

Commit cba3ed80 authored by David Keitel's avatar David Keitel
Browse files

power: qpnp-charger: add support for external current sensing OVP



On some MSM8x26 platforms, board configurations exist which utilize
a current sensing OVP to allow adaptive input current limiting
to accurately measure the current flow through the external parallel
USB path.

Add support for this external OVP part by allowing an MPP to be
configured as a sense line and exposing the converted current
reading via a module parameter which is accessible to userspace.

CRs-Fixed: 651261
Change-Id: Idb47430476c1c19f58a4836a15841b95187baf77
Signed-off-by: default avatarDavid Keitel <dkeitel@codeaurora.org>
parent f351234d
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -110,6 +110,8 @@ Parent node optional properties:
					DC and USB OVP FETs. Please note that this should only
					be enabled in board designs with PM8941 which have DC_IN
					and USB_IN connected via a short.
 - qcom,ext-ovp-isns-enable-gpio	External OVP enable GPIO.
 - qcom,ext-ovp-isns-r-ohm			External ISNS OVP resistance in ohm.

Sub node required structure:
- A qcom,chg node must be a child of an SPMI node that has specified
+133 −5
Original line number Diff line number Diff line
@@ -34,6 +34,9 @@
#include <linux/alarmtimer.h>
#include <linux/time.h>
#include <linux/spinlock.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/qpnp/pin.h>

/* Interrupt offsets */
#define INT_RT_STS(base)			(base + 0x10)
@@ -392,6 +395,9 @@ struct qpnp_chg_chip {
	struct work_struct		reduce_power_stage_work;
	bool				power_stage_workaround_running;
	bool				power_stage_workaround_enable;
	bool				ext_ovp_ic_gpio_enabled;
	unsigned int			ext_ovp_isns_gpio;
	unsigned int			usb_trim_default;
};

static void
@@ -434,6 +440,81 @@ enum usbin_health {
	USBIN_OVP,
};

static int ext_ovp_isns_present;
module_param(ext_ovp_isns_present, int, 0444);
static int ext_ovp_isns_r;
module_param(ext_ovp_isns_r, int, 0444);

static bool ext_ovp_isns_online;
static long ext_ovp_isns_ua;
#define MAX_CURRENT_LENGTH_9A	10
#define ISNS_CURRENT_RATIO	2500
static int ext_ovp_isns_read(char *buffer, const struct kernel_param *kp)
{
	int rc;
	struct qpnp_vadc_result results;
	struct power_supply *batt_psy = power_supply_get_by_name("battery");
	struct qpnp_chg_chip *chip = container_of(batt_psy,
				struct qpnp_chg_chip, batt_psy);

	if (!ext_ovp_isns_present)
		return 0;

	rc = qpnp_vadc_read(chip->vadc_dev, P_MUX7_1_1, &results);
	if (rc) {
		pr_err("Unable to read vbat rc=%d\n", rc);
		return 0;
	}

	pr_debug("voltage %lld uV, current: %d\n mA", results.physical,
			((int) results.physical /
			 (ext_ovp_isns_r / ISNS_CURRENT_RATIO)));

	return snprintf(buffer, MAX_CURRENT_LENGTH_9A, "%d\n",
			((int)results.physical /
			 (ext_ovp_isns_r / ISNS_CURRENT_RATIO)));
}

static int ext_ovp_isns_enable(const char *val, const struct kernel_param *kp)
{
	int rc;
	struct power_supply *batt_psy = power_supply_get_by_name("battery");
	struct qpnp_chg_chip *chip = container_of(batt_psy,
				struct qpnp_chg_chip, batt_psy);

	rc = param_set_bool(val, kp);
	if (rc) {
		pr_err("Unable to set gpio en: %d\n", rc);
		return rc;
	}

	if (*(bool *)kp->arg) {
		gpio_direction_output(
						chip->ext_ovp_isns_gpio, 1);
		chip->ext_ovp_ic_gpio_enabled = 1;
		pr_debug("enabled GPIO\n");
	} else {
		gpio_direction_output(
						chip->ext_ovp_isns_gpio, 0);
		chip->ext_ovp_ic_gpio_enabled = 0;
		pr_debug("disabled GPIO\n");
	}

	return rc;
}

static struct kernel_param_ops ext_ovp_isns_ops = {
	.get = ext_ovp_isns_read,
};
module_param_cb(ext_ovp_isns_ua, &ext_ovp_isns_ops, &ext_ovp_isns_ua, 0644);

static struct kernel_param_ops ext_ovp_en_ops = {
	.set = ext_ovp_isns_enable,
	.get = param_get_bool,
};
module_param_cb(ext_ovp_isns_online, &ext_ovp_en_ops,
		&ext_ovp_isns_online, 0664);

static inline int
get_bpd(const char *name)
{
@@ -890,6 +971,7 @@ qpnp_chg_iusb_trim_set(struct qpnp_chg_chip *chip, int trim)
	return rc;
}

#define IOVP_USB_WALL_TRSH_MA   150
static int
qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA)
{
@@ -1323,7 +1405,13 @@ qpnp_chg_usb_chg_gone_irq_handler(int irq, void *_chip)
	if ((qpnp_chg_is_usb_chg_plugged_in(chip)
			|| qpnp_chg_is_dc_chg_plugged_in(chip))
			&& (usb_sts & CHG_GONE_IRQ)) {
		if (ext_ovp_isns_present) {
			pr_debug("EXT OVP IC ISNS disabled due to ARB WA\n");
			gpio_direction_output(chip->ext_ovp_isns_gpio, 0);
		}

		qpnp_chg_charge_en(chip, 0);

		qpnp_chg_force_run_on_batt(chip, 1);
		schedule_delayed_work(&chip->arb_stop_work,
			msecs_to_jiffies(ARB_STOP_WORK_MS));
@@ -1656,6 +1744,7 @@ qpnp_chg_usb_usbin_valid_irq_handler(int irq, void *_chip)

			qpnp_chg_usb_suspend_enable(chip, 0);
			qpnp_chg_iusbmax_set(chip, QPNP_CHG_I_MAX_MIN_100);
			qpnp_chg_iusb_trim_set(chip, chip->usb_trim_default);
			chip->prev_usb_max_ma = -EINVAL;
			chip->aicl_settled = false;
		} else {
@@ -1986,6 +2075,13 @@ qpnp_chg_chgr_chg_fastchg_irq_handler(int irq, void *_chip)
			}
			if (chip->parallel_ovp_mode)
				switch_parallel_ovp_mode(chip, 1);

			if (ext_ovp_isns_present &&
					chip->ext_ovp_ic_gpio_enabled) {
				pr_debug("EXT OVP IC ISNS enabled\n");
				gpio_direction_output(
						chip->ext_ovp_isns_gpio, 1);
			}
		} else {
			if (chip->parallel_ovp_mode)
				switch_parallel_ovp_mode(chip, 0);
@@ -2599,12 +2695,18 @@ qpnp_batt_external_power_changed(struct power_supply *psy)
					if (!qpnp_is_dc_higher_prio(chip))
						qpnp_chg_idcmax_set(chip,
							QPNP_CHG_I_MAX_MIN_100);
					if (!ext_ovp_present)
						qpnp_chg_iusbmax_set(chip,
							USB_WALL_THRESHOLD_MA);
					else
					if (unlikely(ext_ovp_present)) {
						qpnp_chg_iusbmax_set(chip,
							OVP_USB_WALL_TRSH_MA);
					} else if (unlikely(
							ext_ovp_isns_present)) {
						qpnp_chg_iusb_trim_set(chip, 0);
						qpnp_chg_iusbmax_set(chip,
							IOVP_USB_WALL_TRSH_MA);
					} else {
						qpnp_chg_iusbmax_set(chip,
							USB_WALL_THRESHOLD_MA);
					}
			} else {
				qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
			}
@@ -3146,6 +3248,12 @@ qpnp_chg_regulator_boost_enable(struct regulator_dev *rdev)

	if (qpnp_chg_is_usb_chg_plugged_in(chip) &&
			(chip->flags & BOOST_FLASH_WA)) {

		if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) {
			pr_debug("EXT OVP IC ISNS disabled\n");
			gpio_direction_output(chip->ext_ovp_isns_gpio, 0);
		}

		qpnp_chg_usb_suspend_enable(chip, 1);

		rc = qpnp_chg_masked_write(chip,
@@ -3263,6 +3371,11 @@ qpnp_chg_regulator_boost_disable(struct regulator_dev *rdev)
		qpnp_chg_usb_suspend_enable(chip, 0);
	}

	if (ext_ovp_isns_present && chip->ext_ovp_ic_gpio_enabled) {
		pr_debug("EXT OVP IC ISNS enable\n");
		gpio_direction_output(chip->ext_ovp_isns_gpio, 1);
	}

	return rc;
}

@@ -4832,6 +4945,17 @@ qpnp_charger_read_dt_props(struct qpnp_chg_chip *chip)
	ext_ovp_present = of_property_read_bool(chip->spmi->dev.of_node,
					"qcom,ext-ovp-present");

	/* Check if external IOVP part is configured */
	chip->ext_ovp_isns_gpio = of_get_named_gpio(chip->spmi->dev.of_node,
					"qcom,ext-ovp-isns-enable-gpio", 0);
	if (gpio_is_valid(chip->ext_ovp_isns_gpio)) {
		ext_ovp_isns_present = true;
		rc = of_property_read_u32(chip->spmi->dev.of_node,
				"qcom,ext-ovp-isns-r-ohm", &ext_ovp_isns_r);
		if (rc)
			return rc;
	}

	/* Get the charging-disabled property */
	chip->charging_disabled = of_property_read_bool(chip->spmi->dev.of_node,
					"qcom,charging-disabled");
@@ -4939,6 +5063,9 @@ qpnp_charger_probe(struct spmi_device *spmi)
	if (rc)
		return rc;

	if (ext_ovp_isns_present)
		chip->ext_ovp_ic_gpio_enabled = 0;

	/*
	 * Check if bat_if is set in DT and make sure VADC is present
	 * Also try loading the battery data profile if bat_if exists
@@ -5220,6 +5347,7 @@ qpnp_charger_probe(struct spmi_device *spmi)
		goto unregister_dc_psy;
	}

	chip->usb_trim_default = qpnp_chg_iusb_trim_get(chip);
	qpnp_chg_charge_en(chip, !chip->charging_disabled);
	qpnp_chg_force_run_on_batt(chip, chip->charging_disabled);
	qpnp_chg_set_appropriate_vddmax(chip);