Merge "power: qpnp-charger: add VCP workaround"
diff --git a/drivers/power/qpnp-charger.c b/drivers/power/qpnp-charger.c
index dda8976..0185a3e 100644
--- a/drivers/power/qpnp-charger.c
+++ b/drivers/power/qpnp-charger.c
@@ -20,6 +20,7 @@
 #include <linux/of_device.h>
 #include <linux/radix-tree.h>
 #include <linux/interrupt.h>
+#include <linux/delay.h>
 #include <linux/qpnp/qpnp-adc.h>
 #include <linux/power_supply.h>
 #include <linux/bitops.h>
@@ -69,14 +70,17 @@
 #define CHGR_CHG_WDOG_PET			0x64
 #define CHGR_CHG_WDOG_EN			0x65
 #define CHGR_USB_IUSB_MAX			0x44
+#define CHGR_USB_USB_SUSP			0x47
 #define CHGR_USB_ENUM_T_STOP			0x4E
 #define CHGR_CHG_TEMP_THRESH			0x66
 #define CHGR_BAT_IF_PRES_STATUS			0x08
-#define CHGR_BAT_TEMP_STATUS			0x09
+#define CHGR_STATUS				0x09
 #define CHGR_BAT_IF_VCP				0x42
 #define CHGR_BAT_IF_BATFET_CTRL1		0x90
 #define CHGR_MISC_BOOT_DONE			0x42
+#define CHGR_BUCK_COMPARATOR_OVRIDE_3		0xED
 #define MISC_REVISION2				0x01
+#define SEC_ACCESS				0xD0
 
 /* SMBB peripheral subtype values */
 #define REG_OFFSET_PERP_SUBTYPE			0x05
@@ -133,6 +137,9 @@
 /* smbb_misc_interrupts */
 #define TFTWDOG_IRQ			BIT(0)
 
+/* Workaround flags */
+#define CHG_FLAGS_VCP_WA		BIT(0)
+
 /**
  * struct qpnp_chg_chip - device information
  * @dev:			device pointer to access the parent
@@ -156,6 +163,8 @@
  * @usb_psy			power supply to export information to userspace
  * @bms_psy			power supply to export information to userspace
  * @batt_psy:			power supply to export information to userspace
+ * @flags:			flags to activate specific workarounds
+ *				throughout the driver
  *
  */
 struct qpnp_chg_chip {
@@ -185,6 +194,7 @@
 	struct power_supply		*usb_psy;
 	struct power_supply		*bms_psy;
 	struct power_supply		batt_psy;
+	uint32_t			flags;
 };
 
 static struct qpnp_chg_chip *the_chip;
@@ -258,6 +268,7 @@
 	return 0;
 }
 
+#define USB_VALID_BIT	BIT(7)
 static int
 qpnp_chg_is_usb_chg_plugged_in(struct qpnp_chg_chip *chip)
 {
@@ -265,16 +276,16 @@
 	int rc;
 
 	rc = qpnp_chg_read(chip, &usbin_valid_rt_sts,
-				 INT_RT_STS(chip->usb_chgpth_base), 1);
+				 chip->usb_chgpth_base + CHGR_STATUS , 1);
 
 	if (rc) {
 		pr_err("spmi read failed: addr=%03X, rc=%d\n",
-				INT_RT_STS(chip->usb_chgpth_base), rc);
+				chip->usb_chgpth_base + CHGR_STATUS, rc);
 		return rc;
 	}
 	pr_debug("chgr usb sts 0x%x\n", usbin_valid_rt_sts);
 
-	return (usbin_valid_rt_sts & USBIN_VALID_IRQ) ? 1 : 0;
+	return (usbin_valid_rt_sts & USB_VALID_BIT) ? 1 : 0;
 }
 
 static int
@@ -294,7 +305,6 @@
 	return (dcin_valid_rt_sts & DCIN_VALID_IRQ) ? 1 : 0;
 }
 
-#define VCP_IUSBMAX_SETTING_MA			2000
 #define QPNP_CHG_IUSB_MAX_MIN_100		100
 #define QPNP_CHG_IUSB_MAX_MIN_150		150
 #define QPNP_CHG_IUSB_MAX_MIN_MA		200
@@ -303,7 +313,8 @@
 static int
 qpnp_chg_iusbmax_set(struct qpnp_chg_chip *chip, int mA)
 {
-	u8 usb_reg = 0;
+	int rc = 0;
+	u8 usb_reg = 0, temp = 8;
 
 	if (mA == QPNP_CHG_IUSB_MAX_MIN_100) {
 		usb_reg = 0x00;
@@ -323,17 +334,42 @@
 		return -EINVAL;
 	}
 
-	/* Hack for VCP issue make sure IUSBMAX setting
-	 * is at least 2 A to not brown out device */
-	mA = VCP_IUSBMAX_SETTING_MA;
-
 	usb_reg = mA / QPNP_CHG_IUSB_MAX_STEP_MA;
 
+	if (chip->flags & CHG_FLAGS_VCP_WA) {
+		temp = 0xA5;
+		rc =  qpnp_chg_write(chip, &temp,
+			chip->buck_base + SEC_ACCESS, 1);
+		rc =  qpnp_chg_masked_write(chip,
+			chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+			0x0C, 0x0C, 1);
+	}
+
 	pr_debug("current=%d setting 0x%x\n", mA, usb_reg);
-	return qpnp_chg_write(chip, &usb_reg,
+	rc = qpnp_chg_write(chip, &usb_reg,
 		chip->usb_chgpth_base + CHGR_USB_IUSB_MAX, 1);
-	pr_debug("done\n");
-	return 0;
+
+	if (chip->flags & CHG_FLAGS_VCP_WA) {
+		temp = 0xA5;
+		udelay(200);
+		rc =  qpnp_chg_write(chip, &temp,
+			chip->buck_base + SEC_ACCESS, 1);
+		rc =  qpnp_chg_masked_write(chip,
+			chip->buck_base + CHGR_BUCK_COMPARATOR_OVRIDE_3,
+			0x0C, 0x00, 1);
+	}
+
+	return rc;
+}
+
+#define USB_SUSPEND_BIT	BIT(0)
+static int
+qpnp_chg_usb_suspend_enable(struct qpnp_chg_chip *chip, int enable)
+{
+	return qpnp_chg_masked_write(chip,
+			chip->usb_chgpth_base + CHGR_USB_USB_SUSP,
+			USB_SUSPEND_BIT,
+			enable ? USB_SUSPEND_BIT : 0, 1);
 }
 
 #define ENUM_T_STOP_BIT		BIT(0)
@@ -350,11 +386,6 @@
 		chip->usb_present = usb_present;
 		power_supply_set_present(chip->usb_psy,
 			chip->usb_present);
-	} else if (!(chip->usb_present && usb_present)) {
-			qpnp_chg_masked_write(chip,
-				chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
-				ENUM_T_STOP_BIT,
-				ENUM_T_STOP_BIT, 1);
 	}
 
 	return IRQ_HANDLED;
@@ -368,7 +399,7 @@
 	int rc, usb_present;
 
 	rc = qpnp_chg_masked_write(chip,
-		chip->usb_chgpth_base + CHGR_CHG_FAILED,
+		chip->chgr_base + CHGR_CHG_FAILED,
 		CHGR_CHG_FAILED_BIT,
 		CHGR_CHG_FAILED_BIT, 1);
 	if (rc)
@@ -488,7 +519,7 @@
 	int rc;
 
 	rc = qpnp_chg_read(chip, &batt_health,
-				chip->bat_if_base + CHGR_BAT_TEMP_STATUS, 1);
+				chip->bat_if_base + CHGR_STATUS, 1);
 	if (rc) {
 		pr_err("Couldn't read battery health read failed rc=%d\n", rc);
 		return POWER_SUPPLY_HEALTH_UNKNOWN;
@@ -647,8 +678,13 @@
 		chip->usb_psy->get_property(chip->usb_psy,
 			  POWER_SUPPLY_PROP_CURRENT_MAX, &ret);
 		qpnp_chg_iusbmax_set(chip, ret.intval / 1000);
+		if ((ret.intval / 1000) <= QPNP_CHG_IUSB_MAX_MIN_MA)
+			qpnp_chg_usb_suspend_enable(chip, 1);
+		else
+			qpnp_chg_usb_suspend_enable(chip, 0);
 	} else {
-		qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_MA);
+		qpnp_chg_iusbmax_set(chip, QPNP_CHG_IUSB_MAX_MIN_100);
+		qpnp_chg_usb_suspend_enable(chip, 0);
 	}
 
 	pr_debug("end of power supply changed\n");
@@ -863,6 +899,14 @@
 		chip->chgr_base + CHGR_VDD_MAX, 1);
 }
 
+
+static void
+qpnp_chg_setup_flags(struct qpnp_chg_chip *chip)
+{
+	if (chip->revision > 0)
+		chip->flags |= CHG_FLAGS_VCP_WA;
+}
+
 #define WDOG_EN_BIT	BIT(7)
 static int
 qpnp_chg_hwinit(struct qpnp_chg_chip *chip, u8 subtype,
@@ -944,7 +988,7 @@
 	case SMBB_BAT_IF_SUBTYPE:
 		/* HACK: Unlock secure access to override temp comparator */
 		rc = qpnp_chg_masked_write(chip,
-				chip->bat_if_base + 0xD0,
+				chip->bat_if_base + SEC_ACCESS,
 				0xA5, 0xA5, 1);
 		pr_debug("override hot cold\n");
 		rc = qpnp_chg_masked_write(chip,
@@ -980,6 +1024,12 @@
 				return -ENXIO;
 			}
 		}
+
+		rc = qpnp_chg_masked_write(chip,
+			chip->usb_chgpth_base + CHGR_USB_ENUM_T_STOP,
+			ENUM_T_STOP_BIT,
+			ENUM_T_STOP_BIT, 1);
+
 		break;
 	case SMBB_DC_CHGPTH_SUBTYPE:
 		break;
@@ -1199,6 +1249,9 @@
 		goto unregister_dc;
 	}
 
+	/* Turn on appropriate workaround flags */
+	qpnp_chg_setup_flags(chip);
+
 	power_supply_set_present(chip->usb_psy,
 			qpnp_chg_is_usb_chg_plugged_in(chip));