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

Commit 7146f53c authored by Xiaozhe Shi's avatar Xiaozhe Shi
Browse files

power: qpnp-smbcharger: support external boost for OTG



During DC charging, some devices may want to support USB OTG with an
external boost. The PMI8994 has an 5V audio boost that can be utilized
to provide the 5V that OTG requires.

Add the usb_otg_switch fixed regulator to enable the switch that
connects the boost with USBIN via PMI8994 GPIO 5.

Add the smbcharger_external_otg regulator to disable the usb charge path
when the 5V is provided.

The layout of the regulator supply chain will be the following:
	- usb_otg_switch
		- fixed regulator that controlls gpio to enable/disable
		  OVP FET
	- supplied by: smbcharger_external_otg
		- smbcharger regulator that disables usb charging and USB
		  source detect
	- supplied by: pmi8994_boost_5v
		- rpm regulator that enables the 5V smart boost in the
		  PMI8994.

The usb_otg_switch can be used in place of smbcharger_charger_otg to
provide the 5V in place of the charger operating in reverse if specified
in the devicetree.

CRs-Fixed: 659471
Change-Id: Iad7929e2dc6eac4e85351074267dd8f5ac4292e9
Signed-off-by: default avatarXiaozhe Shi <xiaozhes@codeaurora.org>
parent aab25926
Loading
Loading
Loading
Loading
+15 −2
Original line number Diff line number Diff line
@@ -88,6 +88,19 @@ Sub node required properties:
			 - power-ok:		Triggers when the charger
						switcher turns on or off.

Regulator Subnodes:
- qcom,smbcharger-boost-otg	A subnode for a regulator device that turns on
				the charger boost for OTG operation.
- qcom,smbcharger-external-otg	A subnode for a regulator device that switches
				off charging and the USB input charge path
				in order to allow an external regulator to
				operate. This can be used in place of the
				qcom,smbcharger-boost-otg if an external boost
				is available.

Regulator Sub node required properties:
- regulator-name		A name string for the regulator in question

Optional Properties:
- qcom,battery-psy-name		The name of the main battery power supply that
				the charger will register. Failing to define
@@ -154,8 +167,8 @@ Optional Properties:
				50, 100, 150, 200, 250, 300, 500, 600.
- qcom,iterm-disabled		Disables the termination current feature. This
				is a boolean property.
- regulator-name		A string used as a descriptive name for OTG
				regulator.
- otg-parent-supply		A phandle to an external boost regulator for
				OTG if it exists.
- qcom,thermal-mitigation:	Array of input current limit values for
				different system thermal mitigation levels.
				This should be a flat array that denotates the
+1 −2
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@
			qcom,vadc-meas-int-mode;
		};

		pmi8994_charger: pmi8994_otg_supply: qcom,qpnp-smbcharger {
		pmi8994_charger: qcom,qpnp-smbcharger {
			spmi-dev-container;
			compatible = "qcom,qpnp-smbcharger";
			#address-cells = <1>;
@@ -186,7 +186,6 @@
			qcom,parallel-usb-min-current-ma = <1400>;
			qcom,parallel-9v-usb-min-current-ma = <900>;
			qcom,parallel-allowed-lowering-ma = <500>;
			regulator-name = "pmi8994_otg_vreg";

			qcom,chgr@1000 {
				reg = <0x1000 0x100>;
+19 −0
Original line number Diff line number Diff line
@@ -767,4 +767,23 @@
		enable-active-high;
		gpio = <&pm8994_gpios 9 0>;
	};

	usb_otg_switch: usb-otg-switch {
		compatible = "regulator-fixed";
		regulator-name = "usb_otg_vreg";
		vin-supply = <&smbcharger_external_otg>;
		enable-active-high;
		gpio = <&pmi8994_gpios 5 0>;
	};
};

&pmi8994_charger {
	otg-parent-supply = <&pmi8994_boost_5v>;
	smbcharger_charger_otg: qcom,smbcharger-boost-otg {
		regulator-name = "smbcharger_charger_otg";
	};

	smbcharger_external_otg: qcom,smbcharger-external-otg {
		regulator-name = "smbcharger_external_otg";
	};
};
+1 −1
Original line number Diff line number Diff line
@@ -1329,7 +1329,7 @@
		interrupt-names = "hs_phy_irq", "pwr_event_irq", "pmic_id_irq";

		USB3_GDSC-supply = <&gdsc_usb30>;
		vbus_dwc3-supply = <&pmi8994_otg_supply>;
		vbus_dwc3-supply = <&smbcharger_charger_otg>;
		qcom,dwc-usb3-msm-tx-fifo-size = <29696>;
		qcom,dwc-usb3-msm-qdss-tx-fifo-size = <8192>;
		qcom,otg-capability;
+169 −2
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ struct smbchg_chip {
	int				prechg_safety_time;
	int				bmd_pin_src;
	int				resume_soc_threshold;
	bool				use_vfloat_adjustments;
	bool				iterm_disabled;
	bool				bmd_algo_disabled;
	bool				soft_vfloat_comp_disabled;
@@ -91,6 +92,7 @@ struct smbchg_chip {
	bool				low_icl_wa_on;
	bool				battery_unknown;
	bool				charge_unknown_battery;
	u8				original_usbin_allowance;
	struct parallel_usb_cfg		parallel;

	/* flash current prediction */
@@ -153,6 +155,7 @@ struct smbchg_chip {
	bool				psy_registered;

	struct smbchg_regulator		otg_vreg;
	struct smbchg_regulator		ext_otg_vreg;
	struct work_struct		usb_set_online_work;
	spinlock_t			sec_access_lock;
	struct mutex			current_change_lock;
@@ -827,6 +830,11 @@ enum enable_reason {
	 * temperature levels rise
	 */
	REASON_THERMAL = BIT(4),
	/*
	 * an external OTG supply is being used, suspend charge path so the
	 * charger does not accidentally try to charge from the external supply.
	 */
	REASON_OTG = BIT(5),
};

static struct power_supply *get_parallel_psy(struct smbchg_chip *chip)
@@ -857,6 +865,18 @@ static void smbchg_usb_update_online_work(struct work_struct *work)
	mutex_unlock(&chip->usb_set_online_lock);
}

static bool smbchg_primary_usb_is_en(struct smbchg_chip *chip,
		enum enable_reason reason)
{
	bool enabled;

	mutex_lock(&chip->usb_en_lock);
	enabled = (chip->usb_suspended & reason) == 0;
	mutex_unlock(&chip->usb_en_lock);

	return enabled;
}

static int smbchg_primary_usb_en(struct smbchg_chip *chip, bool enable,
		enum enable_reason reason, bool *changed)
{
@@ -1996,13 +2016,114 @@ struct regulator_ops smbchg_otg_reg_ops = {
	.is_enabled	= smbchg_otg_regulator_is_enable,
};

#define USBIN_CHGR_CFG			0xF1
#define USBIN_ADAPTER_9V		0x3
#define HVDCP_EN_BIT			BIT(3)
static int smbchg_external_otg_regulator_enable(struct regulator_dev *rdev)
{
	bool changed;
	int rc = 0;
	struct smbchg_chip *chip = rdev_get_drvdata(rdev);

	rc = smbchg_primary_usb_en(chip, false, REASON_OTG, &changed);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't suspend charger rc=%d\n", rc);
		return rc;
	}

	rc = smbchg_read(chip, &chip->original_usbin_allowance,
			chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);
		return rc;
	}

	/*
	 * To disallow source detect and usbin_uv interrupts, set the adapter
	 * allowance to 9V, so that the audio boost operating in reverse never
	 * gets detected as a valid input
	 */
	rc = smbchg_sec_masked_write(chip,
				chip->usb_chgpth_base + CHGPTH_CFG,
				HVDCP_EN_BIT, 0);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't disable HVDCP rc=%d\n", rc);
		return rc;
	}

	rc = smbchg_sec_masked_write(chip,
				chip->usb_chgpth_base + USBIN_CHGR_CFG,
				0xFF, USBIN_ADAPTER_9V);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
		return rc;
	}

	pr_smb(PR_STATUS, "Enabling OTG Boost\n");
	return rc;
}

static int smbchg_external_otg_regulator_disable(struct regulator_dev *rdev)
{
	bool changed;
	int rc = 0;
	struct smbchg_chip *chip = rdev_get_drvdata(rdev);

	rc = smbchg_primary_usb_en(chip, true, REASON_OTG, &changed);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't unsuspend charger rc=%d\n", rc);
		return rc;
	}

	/*
	 * Reenable HVDCP and set the adapter allowance back to the original
	 * value in order to allow normal USBs to be recognized as a valid
	 * input.
	 */
	rc = smbchg_sec_masked_write(chip,
				chip->usb_chgpth_base + CHGPTH_CFG,
				HVDCP_EN_BIT, HVDCP_EN_BIT);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't enable HVDCP rc=%d\n", rc);
		return rc;
	}

	rc = smbchg_sec_masked_write(chip,
				chip->usb_chgpth_base + USBIN_CHGR_CFG,
				0xFF, chip->original_usbin_allowance);
	if (rc < 0) {
		dev_err(chip->dev, "Couldn't write usb allowance rc=%d\n", rc);
		return rc;
	}

	pr_smb(PR_STATUS, "Disabling OTG Boost\n");
	return rc;
}

static int smbchg_external_otg_regulator_is_enable(struct regulator_dev *rdev)
{
	struct smbchg_chip *chip = rdev_get_drvdata(rdev);

	return !smbchg_primary_usb_is_en(chip, REASON_OTG);
}

struct regulator_ops smbchg_external_otg_reg_ops = {
	.enable		= smbchg_external_otg_regulator_enable,
	.disable	= smbchg_external_otg_regulator_disable,
	.is_enabled	= smbchg_external_otg_regulator_is_enable,
};

static int smbchg_regulator_init(struct smbchg_chip *chip)
{
	int rc = 0;
	struct regulator_init_data *init_data;
	struct regulator_config cfg = {};
	struct device_node *regulator_node;

	regulator_node = of_get_child_by_name(chip->dev->of_node,
			"qcom,smbcharger-boost-otg");

	init_data = of_get_regulator_init_data(chip->dev, chip->dev->of_node);
	init_data = of_get_regulator_init_data(chip->dev, regulator_node);
	if (!init_data) {
		dev_err(chip->dev, "Unable to allocate memory\n");
		return -ENOMEM;
@@ -2017,7 +2138,7 @@ static int smbchg_regulator_init(struct smbchg_chip *chip)
		cfg.dev = chip->dev;
		cfg.init_data = init_data;
		cfg.driver_data = chip;
		cfg.of_node = chip->dev->of_node;
		cfg.of_node = regulator_node;

		init_data->constraints.valid_ops_mask
			|= REGULATOR_CHANGE_STATUS;
@@ -2033,6 +2154,45 @@ static int smbchg_regulator_init(struct smbchg_chip *chip)
		}
	}

	if (rc)
		return rc;

	regulator_node = of_get_child_by_name(chip->dev->of_node,
			"qcom,smbcharger-external-otg");
	init_data = of_get_regulator_init_data(chip->dev, regulator_node);
	if (!init_data) {
		dev_err(chip->dev, "Unable to allocate memory\n");
		return -ENOMEM;
	}

	if (init_data->constraints.name) {
		if (of_get_property(chip->dev->of_node,
					"otg-parent-supply", NULL))
			init_data->supply_regulator = "otg-parent";
		chip->ext_otg_vreg.rdesc.owner = THIS_MODULE;
		chip->ext_otg_vreg.rdesc.type = REGULATOR_VOLTAGE;
		chip->ext_otg_vreg.rdesc.ops = &smbchg_external_otg_reg_ops;
		chip->ext_otg_vreg.rdesc.name = init_data->constraints.name;

		cfg.dev = chip->dev;
		cfg.init_data = init_data;
		cfg.driver_data = chip;
		cfg.of_node = regulator_node;

		init_data->constraints.valid_ops_mask
			|= REGULATOR_CHANGE_STATUS;

		chip->ext_otg_vreg.rdev = regulator_register(
					&chip->ext_otg_vreg.rdesc, &cfg);
		if (IS_ERR(chip->ext_otg_vreg.rdev)) {
			rc = PTR_ERR(chip->ext_otg_vreg.rdev);
			chip->ext_otg_vreg.rdev = NULL;
			if (rc != -EPROBE_DEFER)
				dev_err(chip->dev,
					"external OTG reg failed, rc=%d\n", rc);
		}
	}

	return rc;
}

@@ -2040,6 +2200,8 @@ static void smbchg_regulator_deinit(struct smbchg_chip *chip)
{
	if (chip->otg_vreg.rdev)
		regulator_unregister(chip->otg_vreg.rdev);
	if (chip->ext_otg_vreg.rdev)
		regulator_unregister(chip->ext_otg_vreg.rdev);
}

#define REVISION1_REG			0x0
@@ -2879,6 +3041,11 @@ static int smbchg_hw_init(struct smbchg_chip *chip)
		return rc;
	}

	rc = smbchg_read(chip, &chip->original_usbin_allowance,
			chip->usb_chgpth_base + USBIN_CHGR_CFG, 1);
	if (rc < 0)
		dev_err(chip->dev, "Couldn't read usb allowance rc=%d\n", rc);

	return rc;
}