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

Commit e14a4374 authored by Umang Chheda's avatar Umang Chheda Committed by Gerrit - the friendly Code Review server
Browse files

power: supply: smb5: PMI632: Add change to support uusb and DCIN



PMI632 does not have DCIN input. Add changes to
detect DCIN or uUSB insertion via GPIO and enable
charging for the same.

Change-Id: I4f5600041048d2fe1bfd8ded030fb718e55beaca
Signed-off-by: default avatarUmang Chheda <quic_uchheda@quicinc.com>
parent 4729283a
Loading
Loading
Loading
Loading
+137 −5
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#include <linux/debugfs.h>
@@ -19,6 +21,7 @@
#include <linux/power_supply.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/log2.h>
#include <linux/qpnp/qpnp-revid.h>
#include <linux/regulator/driver.h>
@@ -671,6 +674,110 @@ static int smb5_parse_dt(struct smb5 *chip)
	if (!rc && tmp < DCIN_ICL_MAX_UA)
		chg->wls_icl_ua = tmp;

	chg->aicl_disable = of_property_read_bool(node, "qcom,aicl-disable");

	chg->dcin_uusb_over_gpio_en = of_property_read_bool(node,
					"qcom,dcin-uusb-over-gpio-en");

	if (chg->dcin_uusb_over_gpio_en) {
		chg->micro_usb_gpio = of_get_named_gpio(node,
					"qcom,micro-usb-gpio", 0);
		if (!gpio_is_valid(chg->micro_usb_gpio)) {
			pr_err(" micro_usb_gpio not specified\n");
		} else {
			rc = devm_gpio_request(chg->dev, chg->micro_usb_gpio,
						"micro_usb");
			if (rc)
				pr_err("request micro_usb_gpio failed, rc=%d\n",
						rc);

			rc = gpio_direction_input(chg->micro_usb_gpio);
			if (rc)
				pr_err("Unable to set dir for micro_usb_gpio\n");

			chg->micro_usb_irq = gpio_to_irq(chg->micro_usb_gpio);

			rc = devm_request_threaded_irq(chg->dev,
						chg->micro_usb_irq,
						NULL,
						smb_micro_usb_irq_handler,
						IRQF_TRIGGER_RISING |
						IRQF_TRIGGER_FALLING |
						IRQF_ONESHOT,
						"micro_usb_irq", chg);
			if (rc < 0)
				dev_err(chg->dev, "Unable to request micro_usb_irq: %dn",
						rc);

			enable_irq_wake(chg->micro_usb_irq);
		}

		chg->dc_9v_gpio = of_get_named_gpio(node, "qcom,dc-9v-gpio", 0);

		if (!gpio_is_valid(chg->dc_9v_gpio)) {
			pr_err("dc_9v_gpio not specified\n");
		} else {
			rc = devm_gpio_request(chg->dev, chg->dc_9v_gpio,
						"dc_9v");
			if (rc)
				pr_err("Request dc_9v gpio failed, rc=%d\n",
					rc);

			rc = gpio_direction_input(chg->dc_9v_gpio);
			if (rc)
				pr_err("unable to set dir for dc_9v gpio\n");

			chg->dc_9v_irq = gpio_to_irq(chg->dc_9v_gpio);

			rc = devm_request_threaded_irq(chg->dev, chg->dc_9v_irq,
						NULL,
						smb_micro_usb_irq_handler,
						IRQF_TRIGGER_RISING |
						IRQF_TRIGGER_FALLING |
						IRQF_ONESHOT,
						"dc_9v_irq", chg);
			if (rc < 0)
				dev_err(chg->dev, "Unable to request dc_9v_irq: %dn",
					rc);
			enable_irq_wake(chg->dc_9v_irq);
		}

		chg->usb_switch_gpio = of_get_named_gpio(node,
					"qcom,usb-switch-gpio", 0);

		if (!gpio_is_valid(chg->usb_switch_gpio)) {
			pr_err("usb_switch_gpio not specified\n");
		} else {
			rc = devm_gpio_request(chg->dev, chg->usb_switch_gpio,
						"usb_switch");
			if (rc)
				pr_err("Request usb_switch gpio failed, rc=%d\n",
					rc);

			rc = gpio_direction_output(chg->usb_switch_gpio, 1);
			if (rc)
				pr_err("Unable to set dir for usb_switch gpio\n");
		}

		chg->usb_hub_33v_en_gpio = of_get_named_gpio(node,
						"qcom,usb-hub-33v-en-gpio", 0);

		if (!gpio_is_valid(chg->usb_hub_33v_en_gpio)) {
			pr_err("usb_hub_33v_en_gpio not specified\n");
		} else {
			rc = devm_gpio_request(chg->dev,
						chg->usb_hub_33v_en_gpio,
						"usb_hub_33v_en");
			if (rc)
				pr_err("Request usb_hub_33v_en gpio failed, rc=%d\n",
					 rc);

			rc = gpio_direction_output(chg->usb_hub_33v_en_gpio, 1);
			if (rc)
				pr_err("Unable to set dir for usb_hub_33v_en gpio\n");
		}
	}

	return 0;
}

@@ -770,8 +877,14 @@ static int smb5_usb_get_prop(struct power_supply *psy,
		else
			val->intval = 1;

		if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN)
		if (chg->real_charger_type == POWER_SUPPLY_TYPE_UNKNOWN) {
			if (chg->dcin_uusb_over_gpio_en &&
				gpio_is_valid(chg->dc_9v_gpio) &&
				gpio_get_value(chg->dc_9v_gpio))
				val->intval = 1;
			else
				val->intval = 0;
		}
		break;
	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
		rc = smblib_get_prop_usb_voltage_max_design(chg, val);
@@ -2717,8 +2830,24 @@ static int smb5_init_hw(struct smb5 *chip)
	 * configuration enable/disable ADB based AICL and Suspend on collapse.
	 */
	mask = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_ADC_EN_BIT
			| USBIN_AICL_EN_BIT | SUSPEND_ON_COLLAPSE_USBIN_BIT;
			| USBIN_AICL_EN_BIT |
			SUSPEND_ON_COLLAPSE_USBIN_BIT;

	/* Disable AICL if battery is not present. */
	rc = smblib_get_prop_batt_present(chg, &pval);
	if (rc < 0) {
		pr_err("Couldn't get battery status rc=%d\n", rc);
		return rc;
	}

	if (pval.intval && !chg->aicl_disable) {
		val = USBIN_AICL_PERIODIC_RERUN_EN_BIT | USBIN_AICL_EN_BIT;
		pr_info("battery present = %d AICL on\n", pval.intval);
	} else {
		val = 0;
		pr_err("battery present = %d AICL off\n", pval.intval);
	}

	if (!chip->dt.disable_suspend_on_collapse)
		val |= SUSPEND_ON_COLLAPSE_USBIN_BIT;
	if (chip->dt.adc_based_aicl)
@@ -3695,6 +3824,9 @@ static int smb5_probe(struct platform_device *pdev)

	device_init_wakeup(chg->dev, true);

	if (chg->dcin_uusb_over_gpio_en && gpio_is_valid(chg->micro_usb_gpio))
		smb_micro_usb_irq_handler(chg->micro_usb_irq, chg);

	pr_info("QPNP SMB5 probed successfully\n");

	return rc;
+199 −36
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#include <linux/device.h>
@@ -21,6 +23,7 @@
#include <linux/pmic-voter.h>
#include <linux/of_batterydata.h>
#include <linux/ktime.h>
#include <linux/gpio.h>
#include "smb5-lib.h"
#include "smb5-reg.h"
#include "schgm-flash.h"
@@ -1415,20 +1418,60 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua)
	if (icl_ua == INT_MAX)
		goto set_mode;

	if (!chg->dcin_uusb_over_gpio_en) {
		/* configure current */
		if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB
			&& (chg->typec_legacy
			|| chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
			|| chg->connector_type ==
				POWER_SUPPLY_CONNECTOR_MICRO_USB)) {
			rc = set_sdp_current(chg, icl_ua);
			if (rc < 0) {
				smblib_err(chg,
					"Couldn't set SDP ICL rc=%d\n", rc);
				goto out;
			}
		} else {
			/*
			 * Try USB 2.0/3,0 option first on USB path
			 * when maximum input current limit is 500mA
			 * or below for better accuracy; in case of error,
			 * proceed to use USB high-current mode.
			 */
			if (icl_ua <= USBIN_500MA) {
				rc = set_sdp_current(chg, icl_ua);
				if (rc >= 0)
					goto unsuspend;
			}
			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);
				goto out;
			}
			icl_override = SW_OVERRIDE_HC_MODE;
		}
	} else {
		/* configure current */
		if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB
			&& (chg->typec_legacy
			|| chg->typec_mode == POWER_SUPPLY_TYPEC_SOURCE_DEFAULT
		|| chg->connector_type == POWER_SUPPLY_CONNECTOR_MICRO_USB)) {
			|| chg->connector_type ==
				POWER_SUPPLY_CONNECTOR_MICRO_USB)
			&& (gpio_is_valid(chg->dc_9v_gpio) &&
			gpio_get_value(chg->dc_9v_gpio) == 0)) {
			rc = set_sdp_current(chg, icl_ua);
			if (rc < 0) {
			smblib_err(chg, "Couldn't set SDP ICL rc=%d\n", rc);
				smblib_err(chg, "Couldn't set SDP ICL rc=%d\n",
						rc);
				goto out;
			}
		} else {
			/*
		 * Try USB 2.0/3,0 option first on USB path when maximum input
		 * current limit is 500mA or below for better accuracy; in case
			 * Try USB 2.0/3,0 option first on USB path
			 * when maximum input current limit is 500mA or
			 * below for better accuracy; in case
			 * of error, proceed to use USB high-current mode.
			 */
			if (icl_ua <= USBIN_500MA) {
@@ -1436,14 +1479,16 @@ int smblib_set_icl_current(struct smb_charger *chg, int icl_ua)
				if (rc >= 0)
					goto unsuspend;
			}

		rc = smblib_set_charge_param(chg, &chg->param.usb_icl, icl_ua);
			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);
				smblib_err(chg,
					"Couldn't set HC ICL rc=%d\n", rc);
				goto out;
			}
			icl_override = SW_OVERRIDE_HC_MODE;
		}
	}

set_mode:
	rc = smblib_icl_override(chg, icl_override);
@@ -4133,6 +4178,11 @@ static int smblib_handle_usb_current(struct smb_charger *chg,
		if (!rc && !val.intval)
			return 0;

		if (chg->dcin_uusb_over_gpio_en &&
			gpio_is_valid(chg->dc_9v_gpio) &&
			gpio_get_value(chg->dc_9v_gpio))
			usb_current = DCP_CURRENT_UA;

		/* if flash is active force 500mA */
		if ((usb_current < SDP_CURRENT_UA) && is_flash_active(chg))
			usb_current = SDP_CURRENT_UA;
@@ -4149,7 +4199,6 @@ static int smblib_handle_usb_current(struct smb_charger *chg,
			pr_err("Couldn't remove SW_ICL_MAX vote rc=%d\n", rc);
			return rc;
		}

	}

	return 0;
@@ -5455,6 +5504,17 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst)
		 */
		if (!is_client_vote_enabled(chg->usb_icl_votable,
						USB_PSY_VOTER)) {
			/* Set current for DC_IN or uUSB based on flash
			 * active.
			 */
			if (chg->dcin_uusb_over_gpio_en &&
				gpio_is_valid(chg->dc_9v_gpio) &&
				gpio_get_value(chg->dc_9v_gpio))
				vote(chg->usb_icl_votable, GPIO_DCIN_VOTER,
					true, is_flash_active(chg) ?
						SDP_CURRENT_UA :
						DCP_CURRENT_UA);
			else
				/* if flash is active force 500mA */
				vote(chg->usb_icl_votable, USB_PSY_VOTER, true,
						is_flash_active(chg) ?
@@ -5472,11 +5532,22 @@ static void update_sw_icl_max(struct smb_charger *chg, int pst)
		break;
	case POWER_SUPPLY_TYPE_USB_FLOAT:
		/*
		 * limit ICL to 100mA, the USB driver will enumerate to check
		 * if this is a SDP and appropriately set the current
		 * If DC_IN or uUSB is detected then vote
		 * for DCP current.
		 */
		vote(chg->usb_icl_votable, SW_ICL_MAX_VOTER, true,
					SDP_100_MA);
		if (chg->dcin_uusb_over_gpio_en &&
			gpio_is_valid(chg->dc_9v_gpio) &&
			gpio_get_value(chg->dc_9v_gpio))
			vote(chg->usb_icl_votable, GPIO_DCIN_VOTER,
				true, DCP_CURRENT_UA);
		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, SW_ICL_MAX_VOTER,
					true, SDP_100_MA);
		break;
	case POWER_SUPPLY_TYPE_UNKNOWN:
	default:
@@ -5501,8 +5572,14 @@ static void smblib_handle_apsd_done(struct smb_charger *chg, bool rising)
	case SDP_CHARGER_BIT:
	case CDP_CHARGER_BIT:
	case FLOAT_CHARGER_BIT:
		if (chg->use_extcon)
		if (chg->use_extcon) {
			if (!chg->dcin_uusb_over_gpio_en) {
				smblib_notify_device_mode(chg, true);
			} else if (gpio_is_valid(chg->dc_9v_gpio) &&
					!gpio_get_value(chg->dc_9v_gpio)) {
				smblib_notify_device_mode(chg, true);
			}
		}
		break;
	case OCP_CHARGER_BIT:
	case DCP_CHARGER_BIT:
@@ -6581,6 +6658,47 @@ irqreturn_t usbin_ov_irq_handler(int irq, void *data)
	return IRQ_HANDLED;
}

irqreturn_t smb_micro_usb_irq_handler(int irq, void *data)
{
	struct smb_charger *chg = data;
	int is_usb_present = (gpio_is_valid(chg->micro_usb_gpio) &&
				gpio_get_value(chg->micro_usb_gpio));
	int is_dc_9v_present = (gpio_is_valid(chg->dc_9v_gpio) &&
					gpio_get_value(chg->dc_9v_gpio));

	smblib_dbg(chg, PR_MISC, "is_usb_present = %d is_dc_9v_present = %d\n",
				is_usb_present, is_dc_9v_present);

	if (!gpio_is_valid(chg->usb_switch_gpio)) {
		smblib_err(chg, "usb_switch_gpio valid :%d\n",
			gpio_is_valid(chg->usb_switch_gpio));
		return IRQ_HANDLED;
	}

	if (is_usb_present) {
		gpio_set_value(chg->usb_switch_gpio, 0);
		smblib_notify_usb_host(chg, false);
	} else {
		gpio_set_value(chg->usb_switch_gpio, 1);
		smblib_notify_device_mode(chg, false);

		if (is_dc_9v_present &&
				chg->micro_usb_pre_state) {
			smblib_request_dpdm(chg, false);
			smblib_rerun_apsd_if_required(chg);
			smblib_update_usb_type(chg);
			schedule_delayed_work(&chg->micro_usb_switch_work,
						msecs_to_jiffies(1500));
			return IRQ_HANDLED;
		}
	}

	schedule_delayed_work(&chg->micro_usb_switch_work,
					msecs_to_jiffies(1000));

	return IRQ_HANDLED;
}

/**************
 * Additional USB PSY getters/setters
 * that call interrupt functions
@@ -6668,6 +6786,47 @@ int smblib_set_prop_pr_swap_in_progress(struct smb_charger *chg,
/***************
 * Work Queues *
 ***************/
static void smblib_micro_usb_switch_work(struct work_struct *work)
{
	struct smb_charger *chg = container_of(work, struct smb_charger,
						micro_usb_switch_work.work);
	int is_usb_present = (gpio_is_valid(chg->micro_usb_gpio) &&
					gpio_get_value(chg->micro_usb_gpio));
	int is_dc_9v_present = (gpio_is_valid(chg->dc_9v_gpio) &&
					gpio_get_value(chg->dc_9v_gpio));

	if (!gpio_is_valid(chg->micro_usb_gpio) &&
		!gpio_is_valid(chg->dc_9v_gpio)) {
		smblib_err(chg, "Both micro_usb gpio and dc_9v_gpio are invalid\n");
		return;
	}

	smblib_dbg(chg, PR_MISC, "is_usb_present=%d is_dc_9v_present=%d\n",
				is_usb_present, is_dc_9v_present);

	if (is_usb_present) {
		smblib_notify_device_mode(chg, true);
		if (chg->real_charger_type == POWER_SUPPLY_TYPE_USB_FLOAT) {
			smblib_request_dpdm(chg, false);
			smblib_rerun_apsd_if_required(chg);
			smblib_update_usb_type(chg);
		}

		if (is_dc_9v_present &&
			(chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
			smblib_handle_usb_current(chg, DCP_CURRENT_UA);
		else if ((is_dc_9v_present == 0)
			&& (chg->real_charger_type == POWER_SUPPLY_TYPE_USB))
			smblib_handle_usb_current(chg, SDP_CURRENT_UA);
	} else {
		smblib_notify_usb_host(chg, false);
		msleep(100);
		smblib_notify_usb_host(chg, true);
	}

	chg->micro_usb_pre_state = is_usb_present;
}

static void smblib_pr_lock_clear_work(struct work_struct *work)
{
	struct smb_charger *chg = container_of(work, struct smb_charger,
@@ -7633,6 +7792,10 @@ int smblib_init(struct smb_charger *chg)
					smblib_pr_swap_detach_work);
	INIT_DELAYED_WORK(&chg->pr_lock_clear_work,
					smblib_pr_lock_clear_work);
	if (chg->dcin_uusb_over_gpio_en)
		INIT_DELAYED_WORK(&chg->micro_usb_switch_work,
					smblib_micro_usb_switch_work);

	setup_timer(&chg->apsd_timer, apsd_timer_cb, (unsigned long)chg);

	if (chg->wa_flags & CHG_TERMINATION_WA) {
+15 −0
Original line number Diff line number Diff line
@@ -8,6 +8,8 @@
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
 */

#ifndef __SMB5_CHARGER_H
@@ -86,6 +88,7 @@ enum print_reason {
#define MAIN_FCC_VOTER			"MAIN_FCC_VOTER"
#define DCIN_AICL_VOTER			"DCIN_AICL_VOTER"
#define OVERHEAT_LIMIT_VOTER		"OVERHEAT_LIMIT_VOTER"
#define GPIO_DCIN_VOTER			"GPIO_DCIN_VOTER"

#define BOOST_BACK_STORM_COUNT	3
#define WEAK_CHG_STORM_COUNT	8
@@ -469,6 +472,7 @@ struct smb_charger {
	struct delayed_work	role_reversal_check;
	struct delayed_work	pr_swap_detach_work;
	struct delayed_work	pr_lock_clear_work;
	struct delayed_work	micro_usb_switch_work;

	struct alarm		lpd_recheck_timer;
	struct alarm		moisture_protection_alarm;
@@ -609,6 +613,16 @@ struct smb_charger {
	int			dcin_uv_count;
	ktime_t			dcin_uv_last_time;
	int			last_wls_vout;
	/* GPIO DCIN Supply */
	int			micro_usb_gpio;
	int			micro_usb_irq;
	int			dc_9v_gpio;
	int			dc_9v_irq;
	int			usb_switch_gpio;
	int			usb_hub_33v_en_gpio;
	int			micro_usb_pre_state;
	bool			dcin_uusb_over_gpio_en;
	bool			aicl_disable;
};

int smblib_read(struct smb_charger *chg, u16 addr, u8 *val);
@@ -668,6 +682,7 @@ irqreturn_t typec_or_rid_detection_change_irq_handler(int irq, void *data);
irqreturn_t temp_change_irq_handler(int irq, void *data);
irqreturn_t usbin_ov_irq_handler(int irq, void *data);
irqreturn_t sdam_sts_change_irq_handler(int irq, void *data);
irqreturn_t smb_micro_usb_irq_handler(int irq, void *data);
int smblib_get_prop_input_suspend(struct smb_charger *chg,
				union power_supply_propval *val);
int smblib_get_prop_batt_present(struct smb_charger *chg,