Merge "power: smb1351: Update the driver to use extcon"
diff --git a/drivers/power/supply/qcom/smb1351-charger.c b/drivers/power/supply/qcom/smb1351-charger.c
index dfedece..63d57fe 100644
--- a/drivers/power/supply/qcom/smb1351-charger.c
+++ b/drivers/power/supply/qcom/smb1351-charger.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2016-2017 The Linux Foundation. All rights reserved.
+/* Copyright (c) 2016-2018 The Linux Foundation. All rights reserved.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 and
@@ -15,6 +15,7 @@
 #include <linux/i2c.h>
 #include <linux/debugfs.h>
 #include <linux/errno.h>
+#include <linux/extcon.h>
 #include <linux/module.h>
 #include <linux/interrupt.h>
 #include <linux/slab.h>
@@ -376,6 +377,7 @@
 #define USB2_MAX_CURRENT_MA			500
 #define USB3_MIN_CURRENT_MA			150
 #define USB3_MAX_CURRENT_MA			900
+#define DCP_MAX_CURRENT_MA			1500
 #define SMB1351_IRQ_REG_COUNT			8
 #define SMB1351_CHG_PRE_MIN_MA			100
 #define SMB1351_CHG_FAST_MIN_MA			1000
@@ -424,6 +426,12 @@
 	[SMB1351] = "SMB1351",
 };
 
+static const unsigned int smb1351_extcon_cable[] = {
+	EXTCON_USB,
+	EXTCON_USB_HOST,
+	EXTCON_NONE,
+};
+
 struct smb1351_charger {
 	struct i2c_client	*client;
 	struct device		*dev;
@@ -472,6 +480,7 @@
 	enum chip_version	version;
 
 	/* psy */
+	struct power_supply_desc	usb_psy_d;
 	struct power_supply	*usb_psy;
 	int			usb_psy_ma;
 	struct power_supply	*bms_psy;
@@ -505,6 +514,12 @@
 	/* pinctrl parameters */
 	const char		*pinctrl_state_name;
 	struct pinctrl		*smb_pinctrl;
+
+	/* standalone */
+	bool			charger_present;
+	struct extcon_dev       *extcon;
+	struct regulator	*dpdm_reg;
+	enum power_supply_type	charger_type;
 };
 
 struct smb_irq_info {
@@ -633,6 +648,65 @@
 	return rc;
 }
 
+static int smb1351_get_closest_usb_setpoint(int val)
+{
+	int i;
+
+	for (i = ARRAY_SIZE(usb_chg_current) - 1; i >= 0; i--) {
+		if (usb_chg_current[i] <= val)
+			break;
+	}
+	if (i < 0)
+		i = 0;
+
+	if (i >= ARRAY_SIZE(usb_chg_current) - 1)
+		return ARRAY_SIZE(usb_chg_current) - 1;
+
+	/* check what is closer, i or i + 1 */
+	if (abs(usb_chg_current[i] - val) < abs(usb_chg_current[i + 1] - val))
+		return i;
+	else
+		return i + 1;
+}
+
+static int smb1351_request_dpdm(struct smb1351_charger *chip, bool enable)
+{
+	int rc = 0;
+
+	/* fetch the DPDM regulator */
+	if (!chip->dpdm_reg && of_get_property(chip->dev->of_node,
+				"dpdm-supply", NULL)) {
+		chip->dpdm_reg = devm_regulator_get(chip->dev, "dpdm");
+		if (IS_ERR(chip->dpdm_reg)) {
+			rc = PTR_ERR(chip->dpdm_reg);
+			pr_err("Couldn't get dpdm regulator rc=%d\n",
+					rc);
+			chip->dpdm_reg = NULL;
+			return rc;
+		}
+	}
+
+	if (enable) {
+		if (chip->dpdm_reg && !regulator_is_enabled(chip->dpdm_reg)) {
+			pr_err("enabling DPDM regulator\n");
+			rc = regulator_enable(chip->dpdm_reg);
+			if (rc < 0)
+				pr_err("Couldn't enable dpdm regulator rc=%d\n",
+					rc);
+		}
+	} else {
+		if (chip->dpdm_reg && regulator_is_enabled(chip->dpdm_reg)) {
+			pr_err("disabling DPDM regulator\n");
+			rc = regulator_disable(chip->dpdm_reg);
+			if (rc < 0)
+				pr_err("Couldn't disable dpdm regulator rc=%d\n",
+					rc);
+		}
+	}
+
+	return rc;
+}
+
 static int smb1351_usb_suspend(struct smb1351_charger *chip, int reason,
 					bool suspend)
 {
@@ -1290,6 +1364,75 @@
 	return rc;
 }
 
+static char *smb1351_usb_supplicants[] = {
+	"bms",
+};
+
+static enum power_supply_property smb1351_usb_properties[] = {
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_CURRENT_MAX,
+	POWER_SUPPLY_PROP_TYPE,
+};
+
+static int smb1351_usb_get_property(struct power_supply *psy,
+		enum power_supply_property psp,
+				  union power_supply_propval *val)
+{
+	struct smb1351_charger *chip = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		val->intval = chip->usb_psy_ma * 1000;
+		break;
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = chip->chg_present;
+		break;
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = chip->chg_present && !chip->usb_suspended_status;
+		break;
+	case POWER_SUPPLY_PROP_TYPE:
+		val->intval = chip->charger_type;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int smb1351_usb_set_property(struct power_supply *psy,
+				  enum power_supply_property psp,
+				  const union power_supply_propval *val)
+{
+	struct smb1351_charger *chip = power_supply_get_drvdata(psy);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		chip->usb_psy_ma = val->intval / 1000;
+		smb1351_enable_volatile_writes(chip);
+		smb1351_set_usb_chg_current(chip, chip->usb_psy_ma);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	power_supply_changed(psy);
+	return 0;
+}
+
+static int smb1351_usb_is_writeable(struct power_supply *psy,
+					enum power_supply_property psp)
+{
+	switch (psp) {
+	case POWER_SUPPLY_PROP_CURRENT_MAX:
+		return 1;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
 static int smb1351_batt_property_is_writeable(struct power_supply *psy,
 					enum power_supply_property psp)
 {
@@ -1535,27 +1678,6 @@
 	return 0;
 }
 
-static int smb1351_get_closest_usb_setpoint(int val)
-{
-	int i;
-
-	for (i = ARRAY_SIZE(usb_chg_current) - 1; i >= 0; i--) {
-		if (usb_chg_current[i] <= val)
-			break;
-	}
-	if (i < 0)
-		i = 0;
-
-	if (i >= ARRAY_SIZE(usb_chg_current) - 1)
-		return ARRAY_SIZE(usb_chg_current) - 1;
-
-	/* check what is closer, i or i + 1 */
-	if (abs(usb_chg_current[i] - val) < abs(usb_chg_current[i + 1] - val))
-		return i;
-	else
-		return i + 1;
-}
-
 static bool smb1351_is_input_current_limited(struct smb1351_charger *chip)
 {
 	int rc;
@@ -2001,10 +2123,10 @@
 static int smb1351_apsd_complete_handler(struct smb1351_charger *chip,
 						u8 status)
 {
-	int rc;
+	int rc, usb_psy_ma = 0;
 	u8 reg = 0;
-	union power_supply_propval prop = {0, };
 	enum power_supply_type type = POWER_SUPPLY_TYPE_UNKNOWN;
+	union extcon_property_value val;
 
 	/*
 	 * If apsd is disabled, charger detection is done by
@@ -2055,18 +2177,12 @@
 						type, chip->chg_present);
 		if (!chip->battery_missing && !chip->apsd_rerun) {
 			if (type == POWER_SUPPLY_TYPE_USB) {
-				pr_debug("Setting usb psy dp=f dm=f SDP and rerun\n");
-				prop.intval = POWER_SUPPLY_DP_DM_DPF_DMF;
-				power_supply_set_property(chip->usb_psy,
-						POWER_SUPPLY_PROP_DP_DM, &prop);
+				smb1351_request_dpdm(chip, false);
+				smb1351_request_dpdm(chip, true);
 				chip->apsd_rerun = true;
 				rerun_apsd(chip);
 				return 0;
 			}
-			pr_debug("Set usb psy dp=f dm=f DCP and no rerun\n");
-			prop.intval = POWER_SUPPLY_DP_DM_DPF_DMF;
-			power_supply_set_property(chip->usb_psy,
-					POWER_SUPPLY_PROP_DP_DM, &prop);
 		}
 		/*
 		 * If defined force hvdcp 2p0 property,
@@ -2087,35 +2203,33 @@
 					msecs_to_jiffies(HVDCP_NOTIFY_MS));
 		}
 
-		prop.intval = type;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_TYPE, &prop);
-		/*
-		 * SMB is now done sampling the D+/D- lines,
-		 * indicate USB driver
-		 */
-		pr_debug("updating usb_psy present=%d\n", chip->chg_present);
-		prop.intval = chip->chg_present;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_PRESENT,
-				&prop);
+		chip->charger_type = type;
+		if (type == POWER_SUPPLY_TYPE_USB
+				|| type == POWER_SUPPLY_TYPE_USB_CDP) {
+			val.intval = true;
+			extcon_set_property(chip->extcon, EXTCON_USB,
+						EXTCON_PROP_USB_SS, val);
+			extcon_set_cable_state_(chip->extcon, EXTCON_USB, true);
+		}
 		chip->apsd_rerun = false;
+
+		/* set the charge current as required */
+		if (type == POWER_SUPPLY_TYPE_USB)
+			usb_psy_ma = USB2_MIN_CURRENT_MA;
+		else /* DCP */
+			usb_psy_ma = DCP_MAX_CURRENT_MA;
+
+		chip->usb_psy_ma = usb_psy_ma;
+		smb1351_enable_volatile_writes(chip);
+		rc = smb1351_set_usb_chg_current(chip, chip->usb_psy_ma);
+		if (rc < 0)
+			pr_err("Failed to set USB current rc=%d\n", rc);
+
 	} else if (!chip->apsd_rerun) {
 		/* Handle Charger removal */
-		prop.intval = POWER_SUPPLY_TYPE_UNKNOWN;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_TYPE, &prop);
-
-		chip->chg_present = false;
-		prop.intval = chip->chg_present;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_PRESENT,
-				&prop);
-
-		pr_debug("Set usb psy dm=r df=r\n");
-		prop.intval = POWER_SUPPLY_DP_DM_DPR_DMR;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_DP_DM, &prop);
+		chip->charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+		extcon_set_cable_state_(chip->extcon, EXTCON_USB, false);
+		smb1351_request_dpdm(chip, false);
 	}
 
 	return 0;
@@ -2163,42 +2277,7 @@
 
 static int smb1351_usbin_uv_handler(struct smb1351_charger *chip, u8 status)
 {
-	union power_supply_propval pval = {0, };
-
-	/* use this to detect USB insertion only if !apsd */
-	if (chip->disable_apsd) {
-		/*
-		 * If APSD is disabled, src det interrupt won't trigger.
-		 * Hence use usbin_uv for removal and insertion notification
-		 */
-		if (status == 0) {
-			chip->chg_present = true;
-			pr_debug("updating usb_psy present=%d\n",
-						chip->chg_present);
-			pval.intval = POWER_SUPPLY_TYPE_USB;
-			power_supply_set_property(chip->usb_psy,
-					POWER_SUPPLY_PROP_TYPE, &pval);
-
-			pval.intval = chip->chg_present;
-			power_supply_set_property(chip->usb_psy,
-					POWER_SUPPLY_PROP_PRESENT,
-					&pval);
-		} else {
-			chip->chg_present = false;
-
-			pval.intval = POWER_SUPPLY_TYPE_UNKNOWN;
-			power_supply_set_property(chip->usb_psy,
-					POWER_SUPPLY_PROP_TYPE, &pval);
-
-			pr_debug("updating usb_psy present=%d\n",
-							chip->chg_present);
-			pval.intval = chip->chg_present;
-			power_supply_set_property(chip->usb_psy,
-					POWER_SUPPLY_PROP_PRESENT,
-					&pval);
-		}
-		return 0;
-	}
+	smb1351_request_dpdm(chip, !!status);
 
 	if (status) {
 		cancel_delayed_work_sync(&chip->hvdcp_det_work);
@@ -2218,24 +2297,19 @@
 {
 	int rc;
 	u8 reg = 0;
-	union power_supply_propval pval = {0, };
 
 	rc = smb1351_read_reg(chip, IRQ_E_REG, &reg);
 	if (rc)
 		pr_err("Couldn't read IRQ_E rc = %d\n", rc);
 
 	if (status != 0) {
-		chip->chg_present = false;
 		chip->usbin_ov = true;
+		chip->charger_type = POWER_SUPPLY_TYPE_UNKNOWN;
+		if (chip->chg_present)
+			extcon_set_cable_state_(chip->extcon, EXTCON_USB,
+						false);
+		chip->chg_present = false;
 
-		pval.intval = POWER_SUPPLY_TYPE_UNKNOWN;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_TYPE, &pval);
-
-		pval.intval = chip->chg_present;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_PRESENT,
-				&pval);
 	} else {
 		chip->usbin_ov = false;
 		if (reg & IRQ_USBIN_UV_BIT)
@@ -2244,13 +2318,6 @@
 			smb1351_apsd_complete_handler(chip, 1);
 	}
 
-	if (chip->usb_psy) {
-		pval.intval = status ? POWER_SUPPLY_HEALTH_OVERVOLTAGE
-					: POWER_SUPPLY_HEALTH_GOOD;
-		power_supply_set_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_HEALTH, &pval);
-		pr_debug("chip ov status is %d\n", pval.intval);
-	}
 	pr_debug("chip->chg_present = %d\n", chip->chg_present);
 
 	return 0;
@@ -2319,6 +2386,21 @@
 	return 0;
 }
 
+static int smb1351_rid_handler(struct smb1351_charger *chip,
+						u8 status)
+{
+	union extcon_property_value val;
+
+	if (!!status) {
+		val.intval = true;
+		extcon_set_property(chip->extcon, EXTCON_USB_HOST,
+					EXTCON_PROP_USB_SS, val);
+	}
+	extcon_set_cable_state_(chip->extcon, EXTCON_USB_HOST, !!status);
+
+	return 0;
+}
+
 static struct irq_handler_info handlers[] = {
 	[0] = {
 		.stat_reg	= IRQ_A_REG,
@@ -2413,6 +2495,7 @@
 			{	.name	 = "otg_oc_retry",
 			},
 			{	.name	 = "rid",
+				.smb_irq = smb1351_rid_handler,
 			},
 			{	.name	 = "otg_fail",
 			},
@@ -2524,30 +2607,21 @@
 {
 	struct smb1351_charger *chip = power_supply_get_drvdata(psy);
 	union power_supply_propval prop = {0,};
-	int rc, current_limit = 0, online = 0;
+	int rc, current_limit = 0;
 
 	if (chip->bms_psy_name)
 		chip->bms_psy =
 			power_supply_get_by_name((char *)chip->bms_psy_name);
 
 	rc = power_supply_get_property(chip->usb_psy,
-				POWER_SUPPLY_PROP_ONLINE, &prop);
-	if (rc)
-		pr_err("Couldn't read USB online property, rc=%d\n", rc);
-	else
-		online = prop.intval;
-
-	rc = power_supply_get_property(chip->usb_psy,
 				POWER_SUPPLY_PROP_CURRENT_MAX, &prop);
 	if (rc)
 		pr_err("Couldn't read USB current_max property, rc=%d\n", rc);
 	else
 		current_limit = prop.intval / 1000;
 
-	pr_debug("online = %d, current_limit = %d\n", online, current_limit);
+	pr_debug("current_limit = %d\n", current_limit);
 
-	smb1351_enable_volatile_writes(chip);
-	smb1351_set_usb_chg_current(chip, current_limit);
 
 	pr_debug("updating batt psy\n");
 }
@@ -3002,28 +3076,65 @@
 {
 	int rc;
 	struct smb1351_charger *chip;
-	struct power_supply *usb_psy;
 	struct power_supply_config batt_psy_cfg = {};
+	struct power_supply_config usb_psy_cfg = {};
 	u8 reg = 0;
 
-	usb_psy = power_supply_get_by_name("usb");
-	if (!usb_psy) {
-		pr_debug("USB psy not found; deferring probe\n");
-		return -EPROBE_DEFER;
-	}
-
 	chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
 	if (!chip)
 		return -ENOMEM;
 
 	chip->client = client;
 	chip->dev = &client->dev;
-	chip->usb_psy = usb_psy;
 	chip->fake_battery_soc = -EINVAL;
+
+	chip->extcon = devm_extcon_dev_allocate(chip->dev,
+					smb1351_extcon_cable);
+	if (IS_ERR(chip->extcon)) {
+		pr_err("failed to allocate extcon device\n");
+		rc = PTR_ERR(chip->extcon);
+		return rc;
+	}
+
+	rc = devm_extcon_dev_register(chip->dev, chip->extcon);
+	if (rc) {
+		pr_err("failed to register extcon device\n");
+		return rc;
+	}
+
+	rc = extcon_set_property_capability(chip->extcon,
+			EXTCON_USB, EXTCON_PROP_USB_SS);
+	rc |= extcon_set_property_capability(chip->extcon,
+			EXTCON_USB_HOST, EXTCON_PROP_USB_SS);
+	if (rc < 0) {
+		pr_err("Failed to register extcon capability rc=%d\n", rc);
+		return rc;
+	}
+
+	chip->usb_psy_d.name = "usb";
+	chip->usb_psy_d.type = POWER_SUPPLY_TYPE_USB;
+	chip->usb_psy_d.get_property = smb1351_usb_get_property;
+	chip->usb_psy_d.set_property = smb1351_usb_set_property;
+	chip->usb_psy_d.properties = smb1351_usb_properties;
+	chip->usb_psy_d.num_properties = ARRAY_SIZE(smb1351_usb_properties);
+	chip->usb_psy_d.property_is_writeable = smb1351_usb_is_writeable;
+
+	usb_psy_cfg.drv_data = chip;
+	usb_psy_cfg.supplied_to = smb1351_usb_supplicants;
+	usb_psy_cfg.num_supplicants = ARRAY_SIZE(smb1351_usb_supplicants);
+
+	chip->usb_psy = devm_power_supply_register(chip->dev,
+				&chip->usb_psy_d, &usb_psy_cfg);
+	if (IS_ERR(chip->usb_psy)) {
+		pr_err("Unable to register usb_psy rc = %ld\n",
+			PTR_ERR(chip->usb_psy));
+		rc = PTR_ERR(chip->usb_psy);
+		return rc;
+	}
+
 	INIT_DELAYED_WORK(&chip->chg_remove_work, smb1351_chg_remove_work);
 	INIT_DELAYED_WORK(&chip->hvdcp_det_work, smb1351_hvdcp_det_work);
 	device_init_wakeup(chip->dev, true);
-
 	/* probe the device to check if its actually connected */
 	rc = smb1351_read_reg(chip, CHG_REVISION_REG, &reg);
 	if (rc) {