text: /* Copyright (c) 2017-2019 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
   * only version 2 as published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it will be useful,
   * 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.
   */
  
  #define pr_fmt(fmt) "SMB1390: %s: " fmt, __func__
  
  #include <linux/debugfs.h>
  #include <linux/device.h>
  #include <linux/interrupt.h>
  #include <linux/module.h>
  #include <linux/qpnp/qpnp-revid.h>
  #include <linux/of.h>
  #include <linux/of_device.h>
  #include <linux/of_irq.h>
  #include <linux/platform_device.h>
  #include <linux/pmic-voter.h>
  #include <linux/power_supply.h>
  #include <linux/regmap.h>
  #include <linux/iio/consumer.h>
  
  #define MISC_CSIR_LSB_REG		0x9F1
  #define MISC_CSIR_MSB_REG		0x9F2
  #define CORE_STATUS1_REG		0x1006
  #define WIN_OV_BIT			BIT(0)
  #define WIN_UV_BIT			BIT(1)
  #define EN_PIN_OUT_BIT			BIT(2)
  #define LCM_AUTO_BIT			BIT(3)
  #define LCM_PIN_BIT			BIT(4)
  #define ILIM_BIT			BIT(5)
  #define TEMP_ALARM_BIT			BIT(6)
  #define VPH_OV_SOFT_BIT			BIT(7)
  
  #define CORE_STATUS2_REG		0x1007
  #define SWITCHER_HOLD_OFF_BIT		BIT(0)
  #define VPH_OV_HARD_BIT			BIT(1)
  #define TSD_BIT				BIT(2)
  #define IREV_BIT			BIT(3)
  #define IOC_BIT				BIT(4)
  #define VIN_UV_BIT			BIT(5)
  #define VIN_OV_BIT			BIT(6)
  #define EN_PIN_OUT2_BIT			BIT(7)
  
  #define CORE_STATUS3_REG		0x1008
  #define EN_SL_BIT			BIT(0)
  #define IIN_REF_SS_DONE_BIT		BIT(1)
  #define FLYCAP_SS_DONE_BIT		BIT(2)
  #define SL_DETECTED_BIT			BIT(3)
  
  #define CORE_INT_RT_STS_REG		0x1010
  #define SWITCHER_OFF_WINDOW_STS_BIT	BIT(0)
  #define SWITCHER_OFF_FAULT_STS_BIT	BIT(1)
  #define TSD_STS_BIT			BIT(2)
  #define IREV_STS_BIT			BIT(3)
  #define VPH_OV_HARD_STS_BIT		BIT(4)
  #define VPH_OV_SOFT_STS_BIT		BIT(5)
  #define ILIM_STS_BIT			BIT(6)
  #define TEMP_ALARM_STS_BIT		BIT(7)
  
  #define CORE_CONTROL1_REG		0x1020
  #define CMD_EN_SWITCHER_BIT		BIT(0)
  #define CMD_EN_SL_BIT			BIT(1)
  
  #define CORE_FTRIM_ILIM_REG		0x1030
  #define CFG_ILIM_MASK			GENMASK(4, 0)
  
  #define CORE_FTRIM_CTRL_REG		0x1031
  #define TEMP_ALERT_LVL_MASK		GENMASK(6, 5)
  #define TEMP_ALERT_LVL_SHIFT		5
  #define TEMP_BUFFER_OUTPUT_BIT		BIT(7)
  
  #define CORE_FTRIM_LVL_REG		0x1033
  #define CFG_WIN_HI_MASK			GENMASK(3, 2)
  #define WIN_OV_LVL_1000MV		0x08
  
  #define CORE_FTRIM_MISC_REG		0x1034
  #define TR_WIN_1P5X_BIT			BIT(0)
  #define TR_IREV_BIT			BIT(1)
  #define WINDOW_DETECTION_DELTA_X1P0	0
  #define WINDOW_DETECTION_DELTA_X1P5	1
  
  #define CORE_FTRIM_DIS_REG		0x1035
  #define TR_DIS_ILIM_DET_BIT		BIT(4)
  
  #define CORE_ATEST1_SEL_REG		0x10E2
  #define ATEST1_OUTPUT_ENABLE_BIT	BIT(7)
  #define ATEST1_SEL_MASK			GENMASK(6, 0)
  #define ISNS_INT_VAL			0x09
  
  #define CP_VOTER		"CP_VOTER"
  #define USER_VOTER		"USER_VOTER"
  #define ILIM_VOTER		"ILIM_VOTER"
  #define TAPER_END_VOTER		"TAPER_END_VOTER"
  #define FCC_VOTER		"FCC_VOTER"
  #define ICL_VOTER		"ICL_VOTER"
  #define WIRELESS_VOTER		"WIRELESS_VOTER"
  #define SRC_VOTER		"SRC_VOTER"
  #define SWITCHER_TOGGLE_VOTER	"SWITCHER_TOGGLE_VOTER"
  #define SOC_LEVEL_VOTER		"SOC_LEVEL_VOTER"
  #define HW_DISABLE_VOTER	"HW_DISABLE_VOTER"
  #define CC_MODE_VOTER		"CC_MODE_VOTER"
  #define MAIN_DISABLE_VOTER	"MAIN_DISABLE_VOTER"
  #define TAPER_MAIN_ICL_LIMIT_VOTER	"TAPER_MAIN_ICL_LIMIT_VOTER"
  
  #define CP_MASTER		0
  #define CP_SLAVE		1
  #define THERMAL_SUSPEND_DECIDEGC	1400
  #define MAX_ILIM_UA			3200000
  #define MAX_ILIM_DUAL_CP_UA		6400000
  #define CC_MODE_TAPER_DELTA_UA		200000
  #define DEFAULT_TAPER_DELTA_UA		100000
  #define CC_MODE_TAPER_MAIN_ICL_UA	500000
  
  #define smb1390_dbg(chip, reason, fmt, ...)				\
  	do {								\
  		if (chip->debug_mask & (reason))			\
  			pr_info("SMB1390: %s: " fmt, __func__,		\
  				##__VA_ARGS__);				\
  		else							\
  			pr_debug("SMB1390: %s: " fmt, __func__,		\
  				##__VA_ARGS__);				\
  	} while (0)
  
  enum {
  	SWITCHER_OFF_WINDOW_IRQ = 0,
  	SWITCHER_OFF_FAULT_IRQ,
  	TSD_IRQ,
  	IREV_IRQ,
  	VPH_OV_HARD_IRQ,
  	VPH_OV_SOFT_IRQ,
  	ILIM_IRQ,
  	TEMP_ALARM_IRQ,
  	NUM_IRQS,
  };
  
  enum isns_mode {
  	ISNS_MODE_OFF = 0,
  	ISNS_MODE_ACTIVE,
  	ISNS_MODE_STANDBY,
  };
  
  enum {
  	SWITCHER_EN = 0,
  	SMB_PIN_EN,
  };
  
  enum print_reason {
  	PR_INTERRUPT		= BIT(0),
  	PR_REGISTER		= BIT(1),
  	PR_INFO			= BIT(2),
  	PR_EXT_DEPENDENCY	= BIT(3),
  	PR_MISC			= BIT(4),
  };
  
  struct smb1390_iio {
  	struct iio_channel	*die_temp_chan;
  };
  
  struct smb1390 {
  	struct device		*dev;
  	struct regmap		*regmap;
  	struct notifier_block	nb;
  	struct wakeup_source	*cp_ws;
  	struct dentry		*dfs_root;
  	struct pmic_revid_data	*pmic_rev_id;
  
  	/* work structs */
  	struct work_struct	status_change_work;
  	struct work_struct	taper_work;
  
  	/* mutexes */
  	spinlock_t		status_change_lock;
  	struct mutex		die_chan_lock;
  
  	/* votables */
  	struct votable		*disable_votable;
  	struct votable		*ilim_votable;
  	struct votable		*fcc_votable;
  	struct votable		*fv_votable;
  	struct votable		*cp_awake_votable;
  	struct votable		*slave_disable_votable;
  	struct votable		*usb_icl_votable;
  	struct votable		*fcc_main_votable;
  
  	/* power supplies */
  	struct power_supply	*cps_psy;
  	struct power_supply	*usb_psy;
  	struct power_supply	*batt_psy;
  	struct power_supply	*dc_psy;
  	struct power_supply	*cp_master_psy;
  
  	int			irqs[NUM_IRQS];
  	bool			status_change_running;
  	bool			taper_work_running;
  	bool			smb_init_done;
  	struct smb1390_iio	iio;
  	int			irq_status;
  	int			taper_entry_fv;
  	bool			switcher_enabled;
  	int			die_temp;
  	bool			suspended;
  	bool			disabled;
  	u32			debug_mask;
  	u32			min_ilim_ua;
  	u32			max_temp_alarm_degc;
  	u32			max_cutoff_soc;
  	u32			pl_output_mode;
  	u32			pl_input_mode;
  	u32			cp_role;
  	enum isns_mode		current_capability;
  	bool			batt_soc_validated;
  	int			cp_slave_thr_taper_ua;
  	int			cc_mode_taper_main_icl_ua;
  };
  
  struct smb_cfg {
  	u16	address;
  	u8	mask;
  	u8	val;
  };
  
  /* SMB1390 rev2/3 for dual charge */
  static const struct smb_cfg smb1390_dual[] = {
  	{0x1031, 0xff, 0x7A},
  	{0x1032, 0xff, 0x07},
  	{0x1035, 0xff, 0x63},
  	{0x1036, 0xff, 0x80},
  	{0x103A, 0xff, 0x44},
  };
  
  /* SMB1390 rev3, CSIR2500, for triple charge */
  static const struct smb_cfg smb1390_csir2500_triple[] = {
  	{0x1030, 0x80, 0x80},
  	{0x1031, 0xff, 0x72},
  	{0x1032, 0xff, 0x03},
  	{0x1033, 0x04, 0x04},
  	{0x1034, 0x80, 0x00},
  	{0x1035, 0xff, 0xE3},
  	{0x1036, 0xff, 0xA0},
  	{0x1037, 0xff, 0x80},
  	{0x1039, 0xff, 0x30},
  	{0x103A, 0xff, 0x40},
  	{0x103B, 0xff, 0x20},
  	{0x103E, 0xff, 0x00},
  };
  
  /* SMB1390 rev3, CSIR 2515 or 2519 for triple charge */
  static const struct smb_cfg smb1390_triple[] = {
  	{0x1031, 0xff, 0x72},
  	{0x1032, 0xff, 0x03},
  	{0x1035, 0xff, 0xE3},
  	{0x1036, 0xff, 0xA0},
  	{0x103A, 0xff, 0x40},
  	{0x1037, 0x04, 0x00},
  };
  
  struct smb_irq {
  	const char		*name;
  	const irq_handler_t	handler;
  	const bool		wake;
  };
  
  static const struct smb_irq smb_irqs[];
  
  static int smb1390_read(struct smb1390 *chip, int reg, int *val)
  {
  	int rc;
  
  	rc = regmap_read(chip->regmap, reg, val);
  	if (rc < 0)
  		pr_err("Couldn't read 0x%04x\n", reg);
  
  	return rc;
  }
  
  static int smb1390_masked_write(struct smb1390 *chip, int reg, int mask,
  				int val)
  {
  	int rc;
  
  	smb1390_dbg(chip, PR_REGISTER, "Writing 0x%02x to 0x%04x with mask 0x%02x\n",
  			val, reg, mask);
  	rc = regmap_update_bits(chip->regmap, reg, mask, val);
  	if (rc < 0)
  		pr_err("Couldn't write 0x%02x to 0x%04x with mask 0x%02x\n",
  		       val, reg, mask);
  
  	return rc;
  }
  
  static bool is_psy_voter_available(struct smb1390 *chip)
  {
  	if (!chip->batt_psy) {
  		chip->batt_psy = power_supply_get_by_name("battery");
  		if (!chip->batt_psy) {
  			smb1390_dbg(chip, PR_EXT_DEPENDENCY, "Couldn't find battery psy\n");
  			return false;
  		}
  	}
  
  	if (!chip->usb_psy) {
  		chip->usb_psy = power_supply_get_by_name("usb");
  		if (!chip->usb_psy) {
  			smb1390_dbg(chip, PR_EXT_DEPENDENCY, "Couldn't find usb psy\n");
  			return false;
  		}
  	}
  
  	if (!chip->dc_psy) {
  		chip->dc_psy = power_supply_get_by_name("dc");
  		if (!chip->dc_psy) {
  			smb1390_dbg(chip, PR_EXT_DEPENDENCY, "Couldn't find dc psy\n");
  			return false;
  		}
  	}
  
  	if (!chip->fcc_votable) {
  		chip->fcc_votable = find_votable("FCC");
  		if (!chip->fcc_votable) {
  			smb1390_dbg(chip, PR_EXT_DEPENDENCY, "Couldn't find FCC votable\n");
  			return false;
  		}
  	}
  
  	if (!chip->fv_votable) {
  		chip->fv_votable = find_votable("FV");
  		if (!chip->fv_votable) {
  			smb1390_dbg(chip, PR_EXT_DEPENDENCY, "Couldn't find FV votable\n");
  			return false;
  		}
  	}
  
  	if (!chip->usb_icl_votable) {
  		chip->usb_icl_votable = find_votable("USB_ICL");
  		if (!chip->usb_icl_votable) {
  			smb1390_dbg(chip, PR_EXT_DEPENDENCY, "Couldn't find ICL votable\n");
  			return false;
  		}
  	}
  
  	if (!chip->disable_votable) {
  		smb1390_dbg(chip, PR_MISC, "Couldn't find CP DISABLE votable\n");
  		return false;
  	}
  
  	return true;
  }
  
  static int smb1390_isns_mode_control(struct smb1390 *chip, enum isns_mode mode)
  {
  	int rc;
  	u8 val;
  
  	switch  (mode) {
  	case ISNS_MODE_ACTIVE:
  		val = ATEST1_OUTPUT_ENABLE_BIT | ISNS_INT_VAL;
  		break;
  	case ISNS_MODE_STANDBY:
  		val = ATEST1_OUTPUT_ENABLE_BIT;
  		break;
  	case ISNS_MODE_OFF:
  	default:
  		val = 0;
  		break;
  	}
  
  	rc = smb1390_masked_write(chip, CORE_ATEST1_SEL_REG,
  				ATEST1_OUTPUT_ENABLE_BIT | ATEST1_SEL_MASK,
  				val);
  	if (rc < 0)
  		pr_err("Couldn't set CORE_ATEST1_SEL_REG, rc = %d\n", rc);
  
  	return rc;
  }
  
  static bool smb1390_is_adapter_cc_mode(struct smb1390 *chip)
  {
  	int rc;
  	union power_supply_propval pval = {0, };
  
  	if (!chip->usb_psy) {
  		chip->usb_psy = power_supply_get_by_name("usb");
  		if (!chip->usb_psy)
  			return false;
  	}
  
  	rc = power_supply_get_property(chip->usb_psy,
  				POWER_SUPPLY_PROP_ADAPTER_CC_MODE,
  				&pval);
  	if (rc < 0) {
  		pr_err("Couldn't get PPS CC mode status rc=%d\n", rc);
  		return false;
  	}
  
  	return pval.intval;
  }
  
  static bool is_cps_available(struct smb1390 *chip)
  {
  	if (!chip->cps_psy)
  		chip->cps_psy = power_supply_get_by_name("cp_slave");
  	else
  		return true;
  
  	if (!chip->cps_psy)
  		return false;
  
  	return true;
  }
  
  static void cp_toggle_switcher(struct smb1390 *chip)
  {
  	int rc;
  
  	/*
  	 * Disable ILIM detection before toggling the switcher
  	 * to prevent any ILIM interrupt storm while toggling
  	 * the switcher.
  	 */
  	rc = regmap_update_bits(chip->regmap, CORE_FTRIM_DIS_REG,
  			TR_DIS_ILIM_DET_BIT, TR_DIS_ILIM_DET_BIT);
  	if (rc < 0)
  		pr_err("Couldn't disable ILIM rc=%d\n", rc);
  
  	vote(chip->disable_votable, SWITCHER_TOGGLE_VOTER, true, 0);
  
  	/* Delay for toggling switcher */
  	usleep_range(20, 30);
  
  	vote(chip->disable_votable, SWITCHER_TOGGLE_VOTER, false, 0);
  
  	rc = regmap_update_bits(chip->regmap, CORE_FTRIM_DIS_REG,
  			TR_DIS_ILIM_DET_BIT, 0);
  	if (rc < 0)
  		pr_err("Couldn't enable ILIM rc=%d\n", rc);
  }
  
  static int smb1390_get_cp_en_status(struct smb1390 *chip, int id, bool *enable)
  {
  	int rc = 0, status;
  
  	rc = smb1390_read(chip, CORE_STATUS2_REG, &status);
  	if (rc < 0) {
  		pr_err("Couldn't read CP_STATUS_2 register, rc=%d\n", rc);
  		return rc;
  	}
  
  	switch (id) {
  	case SWITCHER_EN:
  		*enable = !!(status & EN_PIN_OUT2_BIT) &&
  				!(status & SWITCHER_HOLD_OFF_BIT);
  		break;
  	case SMB_PIN_EN:
  		*enable = !!(status & EN_PIN_OUT2_BIT);
  		break;
  	default:
  		smb1390_dbg(chip, PR_MISC, "cp_en status %d is not supported",
  				id);
  		rc = -EINVAL;
  		break;
  	}
  
  	return rc;
  }
  
  static int smb1390_set_ilim(struct smb1390 *chip, int ilim_ua)
  {
  	int rc;
  
  	rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG,
  			CFG_ILIM_MASK, ilim_ua);
  	if (rc < 0)
  		pr_err("Failed to write ILIM Register, rc=%d\n", rc);
  
  	return rc;
  }
  
  static irqreturn_t default_irq_handler(int irq, void *data)
  {
  	struct smb1390 *chip = data;
  	int i, rc;
  	bool enable;
  
  	for (i = 0; i < NUM_IRQS; ++i) {
  		if (irq == chip->irqs[i]) {
  			smb1390_dbg(chip, PR_INTERRUPT, "%s IRQ triggered\n",
  				smb_irqs[i].name);
  			chip->irq_status |= 1 << i;
  		}
  	}
  
  	rc = smb1390_get_cp_en_status(chip, SWITCHER_EN, &enable);
  	if (!rc) {
  		if (chip->switcher_enabled != enable) {
  			chip->switcher_enabled = enable;
  			if (chip->fcc_votable)
  				rerun_election(chip->fcc_votable);
  		}
  	}
  
  	if (chip->cp_master_psy)
  		power_supply_changed(chip->cp_master_psy);
  
  	return IRQ_HANDLED;
  }
  
  static const struct smb_irq smb_irqs[] = {
  	[SWITCHER_OFF_WINDOW_IRQ] = {
  		.name		= "switcher-off-window",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[SWITCHER_OFF_FAULT_IRQ] = {
  		.name		= "switcher-off-fault",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[TSD_IRQ] = {
  		.name		= "tsd-fault",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[IREV_IRQ] = {
  		.name		= "irev-fault",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[VPH_OV_HARD_IRQ] = {
  		.name		= "vph-ov-hard",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[VPH_OV_SOFT_IRQ] = {
  		.name		= "vph-ov-soft",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[ILIM_IRQ] = {
  		.name		= "ilim",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  	[TEMP_ALARM_IRQ] = {
  		.name		= "temp-alarm",
  		.handler	= default_irq_handler,
  		.wake		= true,
  	},
  };
  
  static int smb1390_get_die_temp(struct smb1390 *chip,
  			union power_supply_propval *val)
  {
  	int die_temp_deciC = 0;
  	int rc = 0;
  	bool enable;
  
  	/*
  	 * If SMB1390 chip is not enabled, adc channel read may render
  	 * erroneous value. Return error to signify, adc read is not admissible
  	 */
  	rc = smb1390_get_cp_en_status(chip, SMB_PIN_EN, &enable);
  	if (rc < 0) {
  		pr_err("Couldn't get SMB_PIN enable status, rc=%d\n", rc);
  		return rc;
  	}
  
  	if (!enable)
  		return -ENODATA;
  
  	mutex_lock(&chip->die_chan_lock);
  	rc = iio_read_channel_processed(chip->iio.die_temp_chan,
  			&die_temp_deciC);
  	mutex_unlock(&chip->die_chan_lock);
  
  	if (rc < 0)
  		pr_err("Couldn't read die chan, rc = %d\n", rc);
  	else
  		val->intval = die_temp_deciC / 100;
  
  	return rc;
  }
  
  static int smb1390_get_isns(int temp)
  {
  	/* ISNS = 2 * (1496 - 1390_therm_input * 0.00356) * 1000 uA */
  	return ((1496 * 1000 - div_s64((s64)temp * 3560, 1000)) * 2);
  }
  
  static int smb1390_get_isns_master(struct smb1390 *chip,
  			union power_supply_propval *val)
  {
  	int temp = 0;
  	int rc;
  	bool enable;
  
  	/*
  	 * If SMB1390 chip is not enabled, adc channel read may render
  	 * erroneous value. Return error to signify, adc read is not admissible
  	 */
  	rc = smb1390_get_cp_en_status(chip, SMB_PIN_EN, &enable);
  	if (rc < 0) {
  		pr_err("Couldn't get SMB_PIN enable status, rc=%d\n", rc);
  		return rc;
  	}
  
  	if (!enable)
  		return -ENODATA;
  
  	/*
  	 * Since master and slave share temp_pin line
  	 * which is re-used to measure isns, configure the
  	 * master as follows:
  	 * 1. Put slave in standby mode
  	 * 2. Configure master to provide current reading
  	 * 3. Read current value
  	 * 4. Configure master back to report temperature
  	 */
  	mutex_lock(&chip->die_chan_lock);
  	if (is_cps_available(chip)) {
  		val->intval = ISNS_MODE_STANDBY;
  		rc = power_supply_set_property(chip->cps_psy,
  				POWER_SUPPLY_PROP_CURRENT_CAPABILITY, val);
  		if (rc < 0) {
  			pr_err("Couldn't change slave charging state rc=%d\n",
  				rc);
  			goto unlock;
  		}
  	}
  
  	rc = smb1390_isns_mode_control(chip, ISNS_MODE_ACTIVE);
  	if (rc < 0) {
  		pr_err("Failed to set master in Active mode, rc=%d\n", rc);
  		goto unlock;
  	}
  
  	rc = iio_read_channel_processed(chip->iio.die_temp_chan,
  			&temp);
  	if (rc < 0) {
  		pr_err("Couldn't read die_temp chan for isns, rc = %d\n", rc);
  		goto unlock;
  	}
  
  	rc = smb1390_isns_mode_control(chip, ISNS_MODE_OFF);
  	if (rc < 0)
  		pr_err("Couldn't set master to off mode, rc = %d\n", rc);
  
  	if (is_cps_available(chip)) {
  		val->intval = ISNS_MODE_OFF;
  		rc = power_supply_set_property(chip->cps_psy,
  				POWER_SUPPLY_PROP_CURRENT_CAPABILITY, val);
  		if (rc < 0)
  			pr_err("Couldn't change slave charging state rc=%d\n",
  				rc);
  	}
  
  unlock:
  	mutex_unlock(&chip->die_chan_lock);
  
  	if (rc >= 0)
  		val->intval = smb1390_get_isns(temp);
  
  	return rc;
  }
  
  static int smb1390_get_isns_slave(struct smb1390 *chip,
  			union power_supply_propval *val)
  {
  	int temp = 0;
  	int rc;
  	bool enable;
  
  	if (!is_cps_available(chip)) {
  		val->intval = 0;
  		return 0;
  	}
  	/*
  	 * If SMB1390 chip is not enabled, adc channel read may render
  	 * erroneous value. Return error to signify, adc read is not admissible
  	 */
  	rc = smb1390_get_cp_en_status(chip, SMB_PIN_EN, &enable);
  	if (rc < 0) {
  		pr_err("Couldn't get SMB_PIN enable status, rc=%d\n", rc);
  		return rc;
  	}
  
  	if (!enable)
  		return -ENODATA;
  
  	/*
  	 * Since master and slave share temp_pin line
  	 * which is re-used to measure isns, configure the
  	 * slave as follows:
  	 * 1. Put slave in standby mode
  	 * 2. Configure slave to in Active mode to provide current reading
  	 * 3. Read current value
  	 */
  	mutex_lock(&chip->die_chan_lock);
  	rc = smb1390_isns_mode_control(chip, ISNS_MODE_STANDBY);
  	if (rc < 0)
  		goto unlock;
  
  	val->intval = ISNS_MODE_ACTIVE;
  	rc = power_supply_set_property(chip->cps_psy,
  			POWER_SUPPLY_PROP_CURRENT_CAPABILITY, val);
  	if (rc < 0) {
  		pr_err("Couldn't change slave charging state rc=%d\n",
  			rc);
  		goto unlock;
  	}
  
  	rc = iio_read_channel_processed(chip->iio.die_temp_chan,
  			&temp);
  	if (rc < 0) {
  		pr_err("Couldn't read die chan for isns, rc = %d\n", rc);
  		goto unlock;
  	}
  
  	val->intval = ISNS_MODE_OFF;
  	rc = power_supply_set_property(chip->cps_psy,
  			POWER_SUPPLY_PROP_CURRENT_CAPABILITY, val);
  	if (rc < 0)
  		pr_err("Couldn't change slave charging state rc=%d\n",
  			rc);
  
  	rc = smb1390_isns_mode_control(chip, ISNS_MODE_OFF);
  	if (rc < 0)
  		pr_err("Couldn't set CORE_ATEST1_SEL_REG, rc = %d\n", rc);
  
  
  unlock:
  	mutex_unlock(&chip->die_chan_lock);
  
  	if (rc >= 0)
  		val->intval = smb1390_get_isns(temp);
  
  	return rc;
  }
  
  static int smb1390_get_cp_ilim(struct smb1390 *chip,
  			       union power_supply_propval *val)
  {
  	int rc = 0, status;
  
  	if (is_cps_available(chip)) {
  		if (!chip->ilim_votable) {
  			chip->ilim_votable = find_votable("CP_ILIM");
  			if (!chip->ilim_votable)
  				return -EINVAL;
  		}
  
  		val->intval = get_effective_result(chip->ilim_votable);
  	} else {
  		rc = smb1390_read(chip, CORE_FTRIM_ILIM_REG, &status);
  		if (!rc)
  			val->intval =
  				((status & CFG_ILIM_MASK) * 100000)
  					+ 500000;
  	}
  
  	return rc;
  }
  
  static int smb1390_is_batt_soc_valid(struct smb1390 *chip)
  {
  	int rc;
  	union power_supply_propval pval = {0, };
  
  	if (!chip->batt_psy)
  		goto out;
  
  	rc = power_supply_get_property(chip->batt_psy,
  			POWER_SUPPLY_PROP_CAPACITY, &pval);
  	if (rc < 0) {
  		pr_err("Couldn't get CAPACITY rc=%d\n", rc);
  		goto out;
  	}
  
  	if (pval.intval >= chip->max_cutoff_soc)
  		return false;
  
  out:
  	return true;
  }
  
  static int smb1390_triple_init_hw(struct smb1390 *chip)
  {
  	int i, rc = 0;
  	int csir_lsb = 0, csir_msb = 0;
  	u16 csir = 0;
  
  	smb1390_read(chip, MISC_CSIR_LSB_REG, &csir_lsb);
  	smb1390_read(chip, MISC_CSIR_MSB_REG, &csir_msb);
  	csir = ((csir_msb << 8) | csir_lsb);
  	smb1390_dbg(chip, PR_INFO, "CSIR register = 0x%04x\n", csir);
  
  	if (csir == 0x2500) {
  		for (i = 0; i < ARRAY_SIZE(smb1390_csir2500_triple); i++) {
  			rc = smb1390_masked_write(chip,
  				smb1390_csir2500_triple[i].address,
  				smb1390_csir2500_triple[i].mask,
  				smb1390_csir2500_triple[i].val);
  			if (rc < 0) {
  				pr_err("Failed to configure SMB1390 for triple chg config for address 0x%04x rc=%d\n",
  				       smb1390_csir2500_triple[i].address, rc);
  				return rc;
  			}
  		}
  	} else {
  		for (i = 0; i < ARRAY_SIZE(smb1390_triple); i++) {
  			rc = smb1390_masked_write(chip,
  				smb1390_triple[i].address,
  				smb1390_triple[i].mask,
  				smb1390_triple[i].val);
  			if (rc < 0) {
  				pr_err("Failed to configure SMB1390 for triple chg config for address 0x%04x rc=%d\n",
  				       smb1390_triple[i].address, rc);
  				return rc;
  			}
  		}
  	}
  
  	smb1390_dbg(chip, PR_INFO, "Configured SMB1390 charge pump for triple chg config\n");
  	chip->smb_init_done = true;
  	return rc;
  }
  
  static int smb1390_dual_init_hw(struct smb1390 *chip)
  {
  	int rc = 0, i;
  
  	for (i = 0; i < ARRAY_SIZE(smb1390_dual); i++) {
  		rc = smb1390_masked_write(chip,
  			smb1390_dual[i].address,
  			smb1390_dual[i].mask,
  			smb1390_dual[i].val);
  		if (rc < 0) {
  			pr_err("Failed to configure SMB1390 for dual chg config for address 0x%04x rc=%d\n",
  			       smb1390_dual[i].address, rc);
  			return rc;
  		}
  	}
  
  	smb1390_dbg(chip, PR_INFO, "Configured SMB1390 charge pump for Dual chg config\n");
  	return rc;
  }
  
  /* voter callbacks */
  static int smb1390_disable_vote_cb(struct votable *votable, void *data,
  				  int disable, const char *client)
  {
  	struct smb1390 *chip = data;
  	int rc = 0;
  
  	if (!is_psy_voter_available(chip) || chip->suspended)
  		return -EAGAIN;
  
  	if (is_cps_available(chip))
  		vote(chip->slave_disable_votable, MAIN_DISABLE_VOTER,
  					disable ? true : false, 0);
  
  	rc = smb1390_masked_write(chip, CORE_CONTROL1_REG, CMD_EN_SWITCHER_BIT,
  				  disable ? 0 : CMD_EN_SWITCHER_BIT);
  	if (rc < 0) {
  		pr_err("Couldn't write CORE_CONTROL1_REG, rc=%d\n", rc);
  		return rc;
  	}
  
  	/* charging may have been disabled by ILIM; send uevent */
  	if (chip->cp_master_psy && (disable != chip->disabled))
  		power_supply_changed(chip->cp_master_psy);
  
  	chip->disabled = disable;
  	return rc;
  }
  
  static int smb1390_slave_disable_vote_cb(struct votable *votable, void *data,
  			      int disable, const char *client)
  {
  	struct smb1390 *chip = data;
  	int rc = 0, ilim_ua = 0;
  
  	rc = smb1390_masked_write(chip, CORE_CONTROL1_REG, CMD_EN_SL_BIT,
  					disable ? 0 : CMD_EN_SL_BIT);
  	if (rc < 0) {
  		pr_err("Couldn't %s slave rc=%d\n",
  				disable ? "disable" : "enable", rc);
  		return rc;
  	}
  
  	/* Re-distribute ILIM to Master CP when Slave is disabled */
  	if (disable && (chip->ilim_votable)) {
  		ilim_ua = get_effective_result_locked(chip->ilim_votable);
  		if (ilim_ua > MAX_ILIM_UA)
  			ilim_ua = MAX_ILIM_UA;
  
  		if (ilim_ua < 500000) {
  			smb1390_dbg(chip, PR_INFO, "ILIM too low, not re-distributing, ilim=%duA\n",
  								ilim_ua);
  			return 0;
  		}
  
  		rc = smb1390_set_ilim(chip,
  		      DIV_ROUND_CLOSEST(ilim_ua - 500000, 100000));
  		if (rc < 0) {
  			pr_err("Failed to set ILIM, rc=%d\n", rc);
  			return rc;
  		}
  
  		smb1390_dbg(chip, PR_INFO, "Master ILIM set to %duA\n",
  								ilim_ua);
  	}
  
  	return rc;
  }
  
  static int smb1390_ilim_vote_cb(struct votable *votable, void *data,
  			      int ilim_uA, const char *client)
  {
  	struct smb1390 *chip = data;
  	union power_supply_propval pval = {0, };
  	int rc = 0;
  	bool slave_enabled = false;
  
  	if (!is_psy_voter_available(chip) || chip->suspended)
  		return -EAGAIN;
  
  	/* ILIM should always have at least one active vote */
  	if (!client) {
  		pr_err("Client missing\n");
  		return -EINVAL;
  	}
  
  	ilim_uA = min(ilim_uA, (is_cps_available(chip) ?
  				MAX_ILIM_DUAL_CP_UA : MAX_ILIM_UA));
  	/* ILIM less than min_ilim_ua, disable charging */
  	if (ilim_uA < chip->min_ilim_ua) {
  		smb1390_dbg(chip, PR_INFO, "ILIM %duA is too low to allow charging\n",
  			ilim_uA);
  		vote(chip->disable_votable, ILIM_VOTER, true, 0);
  	} else {
  		/* Disable Slave CP if ILIM is < 2 * min ILIM */
  		if (is_cps_available(chip)) {
  			vote(chip->slave_disable_votable, ILIM_VOTER,
  				(ilim_uA < (2 * chip->min_ilim_ua)), 0);
  
  			if (get_effective_result(chip->slave_disable_votable)
  									== 0)
  				slave_enabled = true;
  		}
  
  		if (slave_enabled) {
  			ilim_uA /= 2;
  			pval.intval = DIV_ROUND_CLOSEST(ilim_uA - 500000,
  					100000);
  			rc = power_supply_set_property(chip->cps_psy,
  					POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
  					&pval);
  			if (rc < 0)
  				pr_err("Couldn't change slave ilim  rc=%d\n",
  					rc);
  		}
  
  		rc = smb1390_set_ilim(chip,
  		      DIV_ROUND_CLOSEST(ilim_uA - 500000, 100000));
  		if (rc < 0) {
  			pr_err("Failed to set ILIM, rc=%d\n", rc);
  			return rc;
  		}
  
  		smb1390_dbg(chip, PR_INFO, "ILIM set to %duA slave_enabled%d\n",
  						ilim_uA, slave_enabled);
  		vote(chip->disable_votable, ILIM_VOTER, false, 0);
  	}
  
  	return rc;
  }
  
  static int smb1390_awake_vote_cb(struct votable *votable, void *data,
  				 int awake, const char *client)
  {
  	struct smb1390 *chip = data;
  
  	if (awake)
  		__pm_stay_awake(chip->cp_ws);
  	else
  		__pm_relax(chip->cp_ws);
  
  	smb1390_dbg(chip, PR_INFO, "client: %s awake: %d\n", client, awake);
  
  	return 0;
  }
  
  static int smb1390_notifier_cb(struct notifier_block *nb,
  			       unsigned long event, void *data)
  {
  	struct smb1390 *chip = container_of(nb, struct smb1390, nb);
  	struct power_supply *psy = data;
  	int rc;
  	unsigned long flags;
  
  	if (event != PSY_EVENT_PROP_CHANGED)
  		return NOTIFY_OK;
  
  	if (strcmp(psy->desc->name, "battery") == 0
  				|| strcmp(psy->desc->name, "usb") == 0
  				|| strcmp(psy->desc->name, "main") == 0
  				|| strcmp(psy->desc->name, "cp_slave") == 0) {
  		spin_lock_irqsave(&chip->status_change_lock, flags);
  
  		if (!chip->status_change_running) {
  			chip->status_change_running = true;
  			pm_stay_awake(chip->dev);
  			schedule_work(&chip->status_change_work);
  		}
  		spin_unlock_irqrestore(&chip->status_change_lock, flags);
  
  		/*
  		 * If not already configured for triple chg, configure master
  		 * SMB1390 here for triple chg, if slave is detected.
  		 */
  		if (is_cps_available(chip) && !chip->smb_init_done) {
  			smb1390_dbg(chip, PR_INFO, "SMB1390 slave has registered, configure for triple charging\n");
  			rc = smb1390_triple_init_hw(chip);
  			if (rc < 0)
  				pr_err("Couldn't configure SMB1390 for triple-chg config rc=%d\n",
  					rc);
  		}
  	}
  
  	return NOTIFY_OK;
  }
  
  #define ILIM_NR			10
  #define ILIM_DR			8
  #define ILIM_FACTOR(ilim)	((ilim * ILIM_NR) / ILIM_DR)
  
  static void smb1390_configure_ilim(struct smb1390 *chip, int mode)
  {
  	int rc;
  	union power_supply_propval pval = {0, };
  
  	/* PPS adapter reply on the current advertised by the adapter */
  	if ((chip->pl_output_mode == POWER_SUPPLY_PL_OUTPUT_VPH)
  			&& (mode == POWER_SUPPLY_CP_PPS)) {
  		rc = power_supply_get_property(chip->usb_psy,
  				POWER_SUPPLY_PROP_PD_CURRENT_MAX, &pval);
  		if (rc < 0)
  			pr_err("Couldn't get PD CURRENT MAX rc=%d\n", rc);
  		else
  			vote(chip->ilim_votable, ICL_VOTER,
  					true, ILIM_FACTOR(pval.intval));
  	}
  
  	/* QC3.0/Wireless adapter rely on the settled AICL for USBMID_USBMID */
  	if ((chip->pl_input_mode == POWER_SUPPLY_PL_USBMID_USBMID)
  			&& (mode == POWER_SUPPLY_CP_HVDCP3)) {
  		rc = power_supply_get_property(chip->usb_psy,
  				POWER_SUPPLY_PROP_INPUT_CURRENT_SETTLED, &pval);
  		if (rc < 0)
  			pr_err("Couldn't get usb aicl rc=%d\n", rc);
  		else
  			vote(chip->ilim_votable, ICL_VOTER, true, pval.intval);
  	}
  }
  
  static void smb1390_status_change_work(struct work_struct *work)
  {
  	struct smb1390 *chip = container_of(work, struct smb1390,
  					    status_change_work);
  	union power_supply_propval pval = {0, };
  	int rc;
  
  	if (!is_psy_voter_available(chip))
  		goto out;
  
  	/*
  	 * If batt soc is not valid upon bootup, but becomes
  	 * valid due to the battery discharging later, remove
  	 * vote from SOC_LEVEL_VOTER.
  	 */
  	if (smb1390_is_batt_soc_valid(chip))
  		vote(chip->disable_votable, SOC_LEVEL_VOTER, false, 0);
  
  	rc = power_supply_get_property(chip->usb_psy,
  			POWER_SUPPLY_PROP_SMB_EN_MODE, &pval);
  	if (rc < 0) {
  		pr_err("Couldn't get usb present rc=%d\n", rc);
  		goto out;
  	}
  
  	if (pval.intval == POWER_SUPPLY_CHARGER_SEC_CP) {
  		rc = power_supply_get_property(chip->usb_psy,
  				POWER_SUPPLY_PROP_SMB_EN_REASON, &pval);
  		if (rc < 0) {
  			pr_err("Couldn't get cp reason rc=%d\n", rc);
  			goto out;
  		}
  
  		/*
  		 * Slave SMB1390 is not required for the power-rating of QC3
  		 */
  		if (pval.intval != POWER_SUPPLY_CP_HVDCP3)
  			vote(chip->slave_disable_votable, SRC_VOTER, false, 0);
  
  		/* Check for SOC threshold only once before enabling CP */
  		vote(chip->disable_votable, SRC_VOTER, false, 0);
  		if (!chip->batt_soc_validated) {
  			vote(chip->disable_votable, SOC_LEVEL_VOTER,
  				smb1390_is_batt_soc_valid(chip) ?
  				false : true, 0);
  			chip->batt_soc_validated = true;
  		}
  
  		if (pval.intval == POWER_SUPPLY_CP_WIRELESS) {
  			vote(chip->ilim_votable, ICL_VOTER, false, 0);
  			rc = power_supply_get_property(chip->dc_psy,
  					POWER_SUPPLY_PROP_CURRENT_MAX, &pval);
  			if (rc < 0)
  				pr_err("Couldn't get dc icl rc=%d\n", rc);
  			else
  				vote(chip->ilim_votable, WIRELESS_VOTER, true,
  						pval.intval);
  		} else {
  			vote(chip->ilim_votable, WIRELESS_VOTER, false, 0);
  			smb1390_configure_ilim(chip, pval.intval);
  		}
  
  		/*
  		 * Remove SMB1390 Taper condition disable vote if float voltage
  		 * increased in comparison to voltage at which it entered taper.
  		 */
  		if (chip->taper_entry_fv <
  				get_effective_result(chip->fv_votable))
  			vote(chip->disable_votable, TAPER_END_VOTER, false, 0);
  
  		/*
  		 * all votes that would result in disabling the charge pump have
  		 * been cast; ensure the charhe pump is still enabled before
  		 * continuing.
  		 */
  		if (get_effective_result(chip->disable_votable))
  			goto out;
  
  		rc = power_supply_get_property(chip->batt_psy,
  				POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
  		if (rc < 0) {
  			pr_err("Couldn't get charge type rc=%d\n", rc);
  		} else if (pval.intval ==
  				POWER_SUPPLY_CHARGE_TYPE_TAPER) {
  			/*
  			 * mutual exclusion is already guaranteed by
  			 * chip->status_change_running
  			 */
  			if (!chip->taper_work_running) {
  				chip->taper_work_running = true;
  				queue_work(system_long_wq,
  					   &chip->taper_work);
  			}
  		}
  	} else {
  		chip->batt_soc_validated = false;
  		vote(chip->slave_disable_votable, SRC_VOTER, true, 0);
  		vote(chip->disable_votable, SRC_VOTER, true, 0);
  		vote(chip->disable_votable, TAPER_END_VOTER, false, 0);
  		vote(chip->fcc_votable, CP_VOTER, false, 0);
  		vote(chip->disable_votable, SOC_LEVEL_VOTER, true, 0);
  		vote_override(chip->ilim_votable, CC_MODE_VOTER, false, 0);
  		vote(chip->slave_disable_votable, TAPER_END_VOTER, false, 0);
  		vote(chip->slave_disable_votable, MAIN_DISABLE_VOTER, true, 0);
  		vote_override(chip->usb_icl_votable, TAPER_MAIN_ICL_LIMIT_VOTER,
  								false, 0);
  	}
  
  out:
  	pm_relax(chip->dev);
  	chip->status_change_running = false;
  }
  
  static int smb1390_validate_slave_chg_taper(struct smb1390 *chip, int fcc_uA)
  {
  	int rc = 0;
  
  	/*
  	 * In Collapse mode, while in Taper, Disable the slave SMB1390
  	 * when FCC drops below a specified threshold.
  	 */
  	if (fcc_uA < (chip->cp_slave_thr_taper_ua) && is_cps_available(chip)) {
  		vote(chip->slave_disable_votable, TAPER_END_VOTER,
  					true, 0);
  		/*
  		 * Set ILIM of master SMB1390 to Max value = 3.2A once slave is
  		 * disabled to prevent ILIM irq storm.
  		 */
  		smb1390_dbg(chip, PR_INFO, "Set Master ILIM to MAX, post Slave disable in taper, fcc=%d\n",
  									fcc_uA);
  		vote_override(chip->ilim_votable, CC_MODE_VOTER,
  				smb1390_is_adapter_cc_mode(chip),
  				MAX_ILIM_DUAL_CP_UA);
  
  		if (chip->usb_icl_votable)
  			vote_override(chip->usb_icl_votable,
  				      TAPER_MAIN_ICL_LIMIT_VOTER,
  				      smb1390_is_adapter_cc_mode(chip),
  				      chip->cc_mode_taper_main_icl_ua);
  	}
  
  	return rc;
  }
  
  static void smb1390_taper_work(struct work_struct *work)
  {
  	struct smb1390 *chip = container_of(work, struct smb1390, taper_work);
  	union power_supply_propval pval = {0, };
  	int rc, fcc_uA, delta_fcc_uA, main_fcc_ua = 0;
  
  	if (!is_psy_voter_available(chip))
  		goto out;
  
  	if (!chip->fcc_main_votable)
  		chip->fcc_main_votable = find_votable("FCC_MAIN");
  
  	if (chip->fcc_main_votable)
  		main_fcc_ua = get_effective_result(chip->fcc_main_votable);
  
  	if (main_fcc_ua < 0)
  		main_fcc_ua = 0;
  
  	chip->taper_entry_fv = get_effective_result(chip->fv_votable);
  	while (true) {
  		rc = power_supply_get_property(chip->batt_psy,
  					POWER_SUPPLY_PROP_CHARGE_TYPE, &pval);
  		if (rc < 0) {
  			pr_err("Couldn't get charge type rc=%d\n", rc);
  			goto out;
  		}
  
  		if (get_effective_result(chip->fv_votable) >
  						chip->taper_entry_fv) {
  			smb1390_dbg(chip, PR_INFO, "Float voltage increased. Exiting taper\n");
  			goto out;
  		} else {
  			chip->taper_entry_fv =
  					get_effective_result(chip->fv_votable);
  		}
  
  		if (pval.intval == POWER_SUPPLY_CHARGE_TYPE_TAPER) {
  			delta_fcc_uA =
  				(smb1390_is_adapter_cc_mode(chip) ?
  							CC_MODE_TAPER_DELTA_UA :
  							DEFAULT_TAPER_DELTA_UA);
  			fcc_uA = get_effective_result(chip->fcc_votable)
  								- delta_fcc_uA;
  			smb1390_dbg(chip, PR_INFO, "taper work reducing FCC to %duA\n",
  				fcc_uA);
  			vote(chip->fcc_votable, CP_VOTER, true, fcc_uA);
  			rc = smb1390_validate_slave_chg_taper(chip, (fcc_uA -
  							      main_fcc_ua));
  			if (rc < 0) {
  				pr_err("Couldn't Disable slave in Taper, rc=%d\n",
  				       rc);
  				goto out;
  			}
  
  			if ((fcc_uA - main_fcc_ua) < (chip->min_ilim_ua * 2)) {
  				vote(chip->disable_votable, TAPER_END_VOTER,
  								true, 0);
  				/*
  				 * When master CP is disabled, reset all votes
  				 * on ICL to enable Main charger to pump
  				 * charging current.
  				 */
  				if (chip->usb_icl_votable)
  					vote_override(chip->usb_icl_votable,
  						TAPER_MAIN_ICL_LIMIT_VOTER,
  						false, 0);
  				goto out;
  			}
  		} else {
  			smb1390_dbg(chip, PR_INFO, "In fast charging. Wait for next taper\n");
  		}
  
  		msleep(500);
  	}
  out:
  	smb1390_dbg(chip, PR_INFO, "taper work exit\n");
  	vote(chip->fcc_votable, CP_VOTER, false, 0);
  	chip->taper_work_running = false;
  }
  
  static enum power_supply_property smb1390_charge_pump_props[] = {
  	POWER_SUPPLY_PROP_CP_STATUS1,
  	POWER_SUPPLY_PROP_CP_STATUS2,
  	POWER_SUPPLY_PROP_CP_ENABLE,
  	POWER_SUPPLY_PROP_CP_SWITCHER_EN,
  	POWER_SUPPLY_PROP_CP_DIE_TEMP,
  	POWER_SUPPLY_PROP_CP_ISNS,
  	POWER_SUPPLY_PROP_CP_ISNS_SLAVE,
  	POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER,
  	POWER_SUPPLY_PROP_CP_IRQ_STATUS,
  	POWER_SUPPLY_PROP_CP_ILIM,
  	POWER_SUPPLY_PROP_CHIP_VERSION,
  	POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE,
  	POWER_SUPPLY_PROP_MIN_ICL,
  	POWER_SUPPLY_PROP_MODEL_NAME,
  	POWER_SUPPLY_PROP_PARALLEL_MODE,
  };
  
  static int smb1390_get_prop(struct power_supply *psy,
  			enum power_supply_property prop,
  			union power_supply_propval *val)
  {
  	struct smb1390 *chip = power_supply_get_drvdata(psy);
  	int rc = 0, status;
  	bool enable;
  
  	switch (prop) {
  	case POWER_SUPPLY_PROP_CP_STATUS1:
  		rc = smb1390_read(chip, CORE_STATUS1_REG, &status);
  		if (!rc)
  			val->intval = status;
  		break;
  	case POWER_SUPPLY_PROP_CP_STATUS2:
  		rc = smb1390_read(chip, CORE_STATUS2_REG, &status);
  		if (!rc)
  			val->intval = status;
  		break;
  	case POWER_SUPPLY_PROP_CP_ENABLE:
  		rc = smb1390_get_cp_en_status(chip, SMB_PIN_EN, &enable);
  		if (!rc)
  			val->intval = enable &&
  				!get_effective_result(chip->disable_votable);
  		break;
  	case POWER_SUPPLY_PROP_CP_SWITCHER_EN:
  		if (chip->suspended) {
  			val->intval = chip->switcher_enabled;
  		} else {
  			rc = smb1390_get_cp_en_status(chip, SWITCHER_EN,
  					&enable);
  			if (!rc)
  				val->intval = enable;
  		}
  		break;
  	case POWER_SUPPLY_PROP_CP_DIE_TEMP:
  		if (chip->suspended) {
  			if (chip->die_temp != -ENODATA)
  				val->intval = chip->die_temp;
  			else
  				rc = -ENODATA;
  		} else {
  			/*
  			 * Add a filter to the die temp value read:
  			 * If temp > THERMAL_SUSPEND_DECIDEGC then
  			 *	- treat it as an error and report last valid
  			 *	  cached temperature.
  			 *	- return -ENODATA if the cached value is
  			 *	  invalid.
  			 */
  
  			rc = smb1390_get_die_temp(chip, val);
  			if (rc >= 0) {
  				if (val->intval <= THERMAL_SUSPEND_DECIDEGC)
  					chip->die_temp = val->intval;
  				else if (chip->die_temp == -ENODATA)
  					rc = -ENODATA;
  				else
  					val->intval = chip->die_temp;
  			}
  		}
  		break;
  	case POWER_SUPPLY_PROP_CP_ISNS:
  		rc = smb1390_get_isns_master(chip, val);
  		break;
  	case POWER_SUPPLY_PROP_CP_ISNS_SLAVE:
  		rc = smb1390_get_isns_slave(chip, val);
  		break;
  	case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
  		val->intval = 0;
  		break;
  	case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
  		/*
  		 * irq_status variable stores provious IRQs that have been
  		 * handled by kernel, but not addressed by user space daemon.
  		 */
  		val->intval = chip->irq_status;
  		rc = smb1390_read(chip, CORE_INT_RT_STS_REG, &status);
  		if (!rc)
  			val->intval |= status;
  		break;
  	case POWER_SUPPLY_PROP_CP_ILIM:
  		rc = smb1390_get_cp_ilim(chip, val);
  		break;
  	case POWER_SUPPLY_PROP_CHIP_VERSION:
  		val->intval = chip->pmic_rev_id->rev4;
  		break;
  	case POWER_SUPPLY_PROP_PARALLEL_OUTPUT_MODE:
  		val->intval = chip->pl_output_mode;
  		break;
  	case POWER_SUPPLY_PROP_MIN_ICL:
  		val->intval = chip->min_ilim_ua;
  		break;
  	case POWER_SUPPLY_PROP_MODEL_NAME:
  		val->strval = (chip->pmic_rev_id->rev4 > 2) ? "SMB1390_V3" :
  								"SMB1390_V2";
  		break;
  	case POWER_SUPPLY_PROP_PARALLEL_MODE:
  		val->intval = chip->pl_input_mode;
  		break;
  	default:
  		smb1390_dbg(chip, PR_MISC, "charge pump power supply get prop %d not supported\n",
  			prop);
  		return -EINVAL;
  	}
  
  	return rc;
  }
  
  static int smb1390_set_prop(struct power_supply *psy,
  			enum power_supply_property prop,
  			const union power_supply_propval *val)
  {
  	struct smb1390 *chip = power_supply_get_drvdata(psy);
  	int rc = 0;
  
  	switch (prop) {
  	case POWER_SUPPLY_PROP_CP_ENABLE:
  		vote(chip->disable_votable, USER_VOTER, !val->intval, 0);
  		break;
  	case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
  		if (val->intval)
  			cp_toggle_switcher(chip);
  		break;
  	case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
  		chip->irq_status = val->intval;
  		break;
  	case POWER_SUPPLY_PROP_CP_ILIM:
  		if (chip->ilim_votable)
  			vote_override(chip->ilim_votable, CC_MODE_VOTER,
  					(val->intval > 0), val->intval);
  		break;
  	default:
  		smb1390_dbg(chip, PR_MISC, "charge pump power supply set prop %d not supported\n",
  			prop);
  		return -EINVAL;
  	}
  
  	return rc;
  }
  
  static int smb1390_prop_is_writeable(struct power_supply *psy,
  				enum power_supply_property prop)
  {
  	switch (prop) {
  	case POWER_SUPPLY_PROP_CP_ENABLE:
  	case POWER_SUPPLY_PROP_CP_TOGGLE_SWITCHER:
  	case POWER_SUPPLY_PROP_CP_IRQ_STATUS:
  	case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
  	case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
  	case POWER_SUPPLY_PROP_CP_ILIM:
  		return 1;
  	default:
  		break;
  	}
  
  	return 0;
  }
  
  static struct power_supply_desc charge_pump_psy_desc = {
  	.name			= "charge_pump_master",
  	.type			= POWER_SUPPLY_TYPE_CHARGE_PUMP,
  	.properties		= smb1390_charge_pump_props,
  	.num_properties		= ARRAY_SIZE(smb1390_charge_pump_props),
  	.get_property		= smb1390_get_prop,
  	.set_property		= smb1390_set_prop,
  	.property_is_writeable	= smb1390_prop_is_writeable,
  };
  
  static int smb1390_init_charge_pump_psy(struct smb1390 *chip)
  {
  	struct power_supply_config charge_pump_cfg = {};
  
  	charge_pump_cfg.drv_data = chip;
  	charge_pump_cfg.of_node = chip->dev->of_node;
  
  	chip->cp_master_psy = devm_power_supply_register(chip->dev,
  							&charge_pump_psy_desc,
  							&charge_pump_cfg);
  	if (IS_ERR(chip->cp_master_psy)) {
  		pr_err("Couldn't register charge pump power supply\n");
  		return PTR_ERR(chip->cp_master_psy);
  	}
  
  	return 0;
  }
  
  static int smb1390_parse_dt(struct smb1390 *chip)
  {
  	int rc;
  
  	rc = of_property_match_string(chip->dev->of_node, "io-channel-names",
  			"cp_die_temp");
  	if (rc >= 0) {
  		chip->iio.die_temp_chan =
  			iio_channel_get(chip->dev, "cp_die_temp");
  		if (IS_ERR(chip->iio.die_temp_chan)) {
  			rc = PTR_ERR(chip->iio.die_temp_chan);
  			if (rc != -EPROBE_DEFER)
  				dev_err(chip->dev,
  					"cp_die_temp channel unavailable %d\n",
  					rc);
  			chip->iio.die_temp_chan = NULL;
  			return rc;
  		}
  	} else {
  		return rc;
  	}
  
  	chip->min_ilim_ua = 1000000; /* 1A */
  	of_property_read_u32(chip->dev->of_node, "qcom,min-ilim-ua",
  			&chip->min_ilim_ua);
  
  	chip->max_temp_alarm_degc = 110;
  	of_property_read_u32(chip->dev->of_node, "qcom,max-temp-alarm-degc",
  			&chip->max_temp_alarm_degc);
  
  	chip->max_cutoff_soc = 85; /* 85% */
  	of_property_read_u32(chip->dev->of_node, "qcom,max-cutoff-soc",
  			&chip->max_cutoff_soc);
  
  	/* Default parallel output configuration is VPH connection */
  	chip->pl_output_mode = POWER_SUPPLY_PL_OUTPUT_VPH;
  	of_property_read_u32(chip->dev->of_node, "qcom,parallel-output-mode",
  			&chip->pl_output_mode);
  
  	/* Default parallel input configuration is USBMID connection */
  	chip->pl_input_mode = POWER_SUPPLY_PL_USBMID_USBMID;
  	of_property_read_u32(chip->dev->of_node, "qcom,parallel-input-mode",
  			&chip->pl_input_mode);
  
  	chip->cp_slave_thr_taper_ua = smb1390_is_adapter_cc_mode(chip) ?
  			(3 * chip->min_ilim_ua) : (4 * chip->min_ilim_ua);
  	of_property_read_u32(chip->dev->of_node, "qcom,cp-slave-thr-taper-ua",
  			      &chip->cp_slave_thr_taper_ua);
  
  	chip->cc_mode_taper_main_icl_ua = CC_MODE_TAPER_MAIN_ICL_UA;
  	of_property_read_u32(chip->dev->of_node,
  			     "qcom,cc-mode-taper-main-icl-ua",
  			     &chip->cc_mode_taper_main_icl_ua);
  
  	return 0;
  }
  
  static void smb1390_release_channels(struct smb1390 *chip)
  {
  	if (!IS_ERR_OR_NULL(chip->iio.die_temp_chan))
  		iio_channel_release(chip->iio.die_temp_chan);
  }
  
  static int smb1390_create_votables(struct smb1390 *chip)
  {
  	chip->cp_awake_votable = create_votable("CP_AWAKE",
  			VOTE_SET_ANY, smb1390_awake_vote_cb, chip);
  	chip->disable_votable = create_votable("CP_DISABLE",
  			VOTE_SET_ANY, smb1390_disable_vote_cb, chip);
  	if (IS_ERR(chip->disable_votable))
  		return PTR_ERR(chip->disable_votable);
  
  	chip->ilim_votable = create_votable("CP_ILIM",
  			VOTE_MIN, smb1390_ilim_vote_cb, chip);
  	if (IS_ERR(chip->ilim_votable))
  		return PTR_ERR(chip->ilim_votable);
  
  	chip->slave_disable_votable = create_votable("CP_SLAVE_DISABLE",
  			VOTE_SET_ANY, smb1390_slave_disable_vote_cb, chip);
  	if (IS_ERR(chip->slave_disable_votable))
  		return PTR_ERR(chip->slave_disable_votable);
  
  	/* Keep slave SMB disabled */
  	vote(chip->slave_disable_votable, SRC_VOTER, true, 0);
  	/*
  	 * charge pump is initially disabled; this indirectly votes to allow
  	 * traditional parallel charging if present
  	 */
  	vote(chip->disable_votable, USER_VOTER, true, 0);
  	/* keep charge pump disabled if SOC is above threshold */
  	vote(chip->disable_votable, SOC_LEVEL_VOTER,
  			smb1390_is_batt_soc_valid(chip) ? false : true, 0);
  
  	/*
  	 * In case SMB1390 probe happens after FCC value has been configured,
  	 * update ilim vote to reflect FCC / 2 value, this is only applicable
  	 * when SMB1390 is directly connected to VBAT.
  	 */
  	if ((chip->pl_output_mode != POWER_SUPPLY_PL_OUTPUT_VPH)
  			&& chip->fcc_votable)
  		vote(chip->ilim_votable, FCC_VOTER, true,
  			get_effective_result(chip->fcc_votable) / 2);
  
  	return 0;
  }
  
  static void smb1390_destroy_votables(struct smb1390 *chip)
  {
  	destroy_votable(chip->disable_votable);
  	destroy_votable(chip->ilim_votable);
  }
  
  static int smb1390_init_hw(struct smb1390 *chip)
  {
  	int rc = 0, val;
  
  	/*
  	 * Improve ILIM accuracy:
  	 *  - Configure window (Vin - 2Vout) OV level to 1000mV
  	 *  - Configure VOUT tracking value to 1.0
  	 */
  	rc = smb1390_masked_write(chip, CORE_FTRIM_LVL_REG,
  			CFG_WIN_HI_MASK, WIN_OV_LVL_1000MV);
  	if (rc < 0)
  		return rc;
  
  	rc = smb1390_masked_write(chip, CORE_FTRIM_MISC_REG,
  			TR_WIN_1P5X_BIT, WINDOW_DETECTION_DELTA_X1P0);
  	if (rc < 0)
  		return rc;
  
  	switch (chip->max_temp_alarm_degc) {
  	case 125:
  		val = 0x00;
  		break;
  	case 95:
  		val = 0x02;
  		break;
  	case 85:
  		val = 0x03;
  		break;
  	case 110:
  	default:
  		val = 0x01;
  		break;
  	}
  	rc = smb1390_masked_write(chip, CORE_FTRIM_CTRL_REG,
  			TEMP_ALERT_LVL_MASK, val << TEMP_ALERT_LVL_SHIFT);
  	if (rc < 0) {
  		pr_err("Failed to write CORE_FTRIM_CTRL_REG rc=%d\n", rc);
  		return rc;
  	}
  
  	/* Configure IREV threshold to 200mA */
  	rc = smb1390_masked_write(chip, CORE_FTRIM_MISC_REG, TR_IREV_BIT, 0);
  	if (rc < 0) {
  		pr_err("Couldn't configure IREV threshold rc=%d\n", rc);
  		return rc;
  	}
  	/*
  	 * If the slave charger has registered, configure Master SMB1390 for
  	 * triple-chg config, else configure for dual. Later, if the slave
  	 * charger registers, re-configure for triple chg config from the
  	 * power-supply notifier.
  	 */
  	if (!chip->smb_init_done) {
  		if (is_cps_available(chip))
  			rc = smb1390_triple_init_hw(chip);
  		else
  			rc = smb1390_dual_init_hw(chip);
  	}
  
  	return rc;
  }
  
  static int smb1390_get_irq_index_byname(const char *irq_name)
  {
  	int i;
  
  	for (i = 0; i < ARRAY_SIZE(smb_irqs); i++) {
  		if (strcmp(smb_irqs[i].name, irq_name) == 0)
  			return i;
  	}
  
  	return -ENOENT;
  }
  
  static int smb1390_request_interrupt(struct smb1390 *chip,
  				struct device_node *node,
  				const char *irq_name)
  {
  	int rc = 0, irq, irq_index;
  
  	irq = of_irq_get_byname(node, irq_name);
  	if (irq < 0) {
  		pr_err("Couldn't get irq %s byname\n", irq_name);
  		return irq;
  	}
  
  	irq_index = smb1390_get_irq_index_byname(irq_name);
  	if (irq_index < 0) {
  		pr_err("%s is not a defined irq\n", irq_name);
  		return irq_index;
  	}
  
  	if (!smb_irqs[irq_index].handler)
  		return 0;
  
  	rc = devm_request_threaded_irq(chip->dev, irq, NULL,
  				smb_irqs[irq_index].handler,
  				IRQF_ONESHOT, irq_name, chip);
  	if (rc < 0) {
  		pr_err("Couldn't request irq %d rc=%d\n", irq, rc);
  		return rc;
  	}
  
  	chip->irqs[irq_index] = irq;
  	if (smb_irqs[irq_index].wake)
  		enable_irq_wake(irq);
  
  	return rc;
  }
  
  static int smb1390_request_interrupts(struct smb1390 *chip)
  {
  	struct device_node *node = chip->dev->of_node;
  	struct device_node *child;
  	int rc = 0;
  	const char *name;
  	struct property *prop;
  
  	for_each_available_child_of_node(node, child) {
  		of_property_for_each_string(child, "interrupt-names",
  					    prop, name) {
  			rc = smb1390_request_interrupt(chip, child, name);
  			if (rc < 0) {
  				pr_err("Couldn't request interrupt %s rc=%d\n",
  					name, rc);
  				return rc;
  			}
  		}
  	}
  
  	return rc;
  }
  
  #ifdef CONFIG_DEBUG_FS
  static void smb1390_create_debugfs(struct smb1390 *chip)
  {
  	struct dentry *entry;
  
  	chip->dfs_root = debugfs_create_dir("smb1390_charger_psy", NULL);
  	if (IS_ERR_OR_NULL(chip->dfs_root)) {
  		pr_err("Failed to create debugfs directory, rc=%ld\n",
  					(long)chip->dfs_root);
  		return;
  	}
  
  	entry = debugfs_create_u32("debug_mask", 0600, chip->dfs_root,
  			&chip->debug_mask);
  	if (IS_ERR_OR_NULL(entry)) {
  		pr_err("Failed to create debug_mask, rc=%ld\n", (long)entry);
  		debugfs_remove_recursive(chip->dfs_root);
  	}
  }
  #else
  static void smb1390_create_debugfs(struct smb1390 *chip)
  {
  }
  #endif
  
  static const struct of_device_id match_table[] = {
  	{ .compatible = "qcom,smb1390-charger-psy",
  	  .data = (void *)CP_MASTER
  	},
  	{ .compatible = "qcom,smb1390-slave",
  	  .data = (void *)CP_SLAVE
  	},
  	{ },
  };
  
  static int smb1390_master_probe(struct smb1390 *chip)
  {
  	int rc;
  	struct device_node *revid_dev_node;
  	struct pmic_revid_data *pmic_rev_id;
  
  	revid_dev_node = of_parse_phandle(chip->dev->of_node,
  					  "qcom,pmic-revid", 0);
  	if (!revid_dev_node) {
  		pr_err("Missing qcom,pmic-revid property\n");
  		return -EINVAL;
  	}
  
  	pmic_rev_id = get_revid_data(revid_dev_node);
  	of_node_put(revid_dev_node);
  	if (IS_ERR_OR_NULL(pmic_rev_id)) {
  		/*
  		 * the revid peripheral must be registered, any failure
  		 * here only indicates that the rev-id module has not
  		 * probed yet.
  		 */
  		return -EPROBE_DEFER;
  	}
  
  	chip->pmic_rev_id = pmic_rev_id;
  	spin_lock_init(&chip->status_change_lock);
  	mutex_init(&chip->die_chan_lock);
  
  	rc = smb1390_parse_dt(chip);
  	if (rc < 0) {
  		pr_err("Couldn't parse device tree rc=%d\n", rc);
  		return rc;
  	}
  
  	chip->cp_ws = wakeup_source_register("qcom-chargepump");
  	if (!chip->cp_ws)
  		return -ENOMEM;
  
  	INIT_WORK(&chip->status_change_work, smb1390_status_change_work);
  	INIT_WORK(&chip->taper_work, smb1390_taper_work);
  
  	rc = smb1390_init_hw(chip);
  	if (rc < 0) {
  		pr_err("Couldn't init hardware rc=%d\n", rc);
  		goto out_work;
  	}
  
  	rc = smb1390_create_votables(chip);
  	if (rc < 0) {
  		pr_err("Couldn't create votables rc=%d\n", rc);
  		goto out_work;
  	}
  
  	smb1390_dbg(chip, PR_INFO, "Detected revid=0x%02x\n",
  			 chip->pmic_rev_id->rev4);
  	if (chip->pmic_rev_id->rev4 <= 0x02 && chip->pl_output_mode !=
  			POWER_SUPPLY_PL_OUTPUT_VPH) {
  		pr_err("Incompatible SMB1390 HW detected, Disabling the charge pump\n");
  		if (chip->disable_votable)
  			vote(chip->disable_votable, HW_DISABLE_VOTER,
  			     true, 0);
  	}
  
  	rc = smb1390_init_charge_pump_psy(chip);
  	if (rc < 0) {
  		pr_err("Couldn't initialize charge pump psy rc=%d\n", rc);
  		goto out_votables;
  	}
  
  	chip->nb.notifier_call = smb1390_notifier_cb;
  	rc = power_supply_reg_notifier(&chip->nb);
  	if (rc < 0) {
  		pr_err("Couldn't register psy notifier rc=%d\n", rc);
  		goto out_votables;
  	}
  
  	rc = smb1390_request_interrupts(chip);
  	if (rc < 0) {
  		pr_err("Couldn't request interrupts rc=%d\n", rc);
  		goto out_notifier;
  	}
  
  	smb1390_create_debugfs(chip);
  	return 0;
  
  out_notifier:
  	power_supply_unreg_notifier(&chip->nb);
  out_votables:
  	smb1390_destroy_votables(chip);
  out_work:
  	cancel_work(&chip->taper_work);
  	cancel_work(&chip->status_change_work);
  	wakeup_source_unregister(chip->cp_ws);
  	return rc;
  }
  
  static enum power_supply_property smb1390_cp_slave_props[] = {
  	POWER_SUPPLY_PROP_INPUT_CURRENT_MAX,
  	POWER_SUPPLY_PROP_CURRENT_CAPABILITY,
  };
  
  static int smb1390_slave_prop_is_writeable(struct power_supply *psy,
  				enum power_supply_property prop)
  {
  	return 0;
  }
  
  static int smb1390_cp_slave_get_prop(struct power_supply *psy,
  		enum power_supply_property psp,
  		union power_supply_propval *val)
  {
  	struct smb1390 *chip = power_supply_get_drvdata(psy);
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
  		val->intval = 0;
  		if (!chip->ilim_votable)
  			chip->ilim_votable = find_votable("CP_ILIM");
  		if (chip->ilim_votable)
  			val->intval =
  				get_effective_result_locked(chip->ilim_votable);
  		break;
  	case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
  		val->intval = (int)chip->current_capability;
  		break;
  	default:
  		smb1390_dbg(chip, PR_MISC, "SMB 1390 slave power supply get prop %d not supported\n",
  			psp);
  		return -EINVAL;
  	}
  
  	return 0;
  }
  
  static int smb1390_cp_slave_set_prop(struct power_supply *psy,
  		enum power_supply_property psp,
  		const union power_supply_propval *val)
  {
  	struct smb1390 *chip = power_supply_get_drvdata(psy);
  	int rc = 0;
  
  	switch (psp) {
  	case POWER_SUPPLY_PROP_INPUT_CURRENT_MAX:
  		rc = smb1390_set_ilim(chip, val->intval);
  		break;
  	case POWER_SUPPLY_PROP_CURRENT_CAPABILITY:
  		chip->current_capability = (enum isns_mode)val->intval;
  		rc = smb1390_isns_mode_control(chip, val->intval);
  		break;
  	default:
  		smb1390_dbg(chip, PR_MISC, "SMB 1390 slave power supply set prop %d not supported\n",
  			psp);
  		return -EINVAL;
  	}
  
  	return 0;
  };
  
  static const struct power_supply_desc cps_psy_desc = {
  	.name = "cp_slave",
  	.type = POWER_SUPPLY_TYPE_PARALLEL,
  	.properties = smb1390_cp_slave_props,
  	.num_properties = ARRAY_SIZE(smb1390_cp_slave_props),
  	.get_property = smb1390_cp_slave_get_prop,
  	.set_property = smb1390_cp_slave_set_prop,
  	.property_is_writeable = smb1390_slave_prop_is_writeable,
  };
  
  static int smb1390_init_cps_psy(struct smb1390 *chip)
  {
  	struct power_supply_config cps_cfg = {};
  
  	cps_cfg.drv_data = chip;
  	cps_cfg.of_node = chip->dev->of_node;
  	chip->cps_psy = devm_power_supply_register(chip->dev,
  						  &cps_psy_desc,
  						  &cps_cfg);
  	if (IS_ERR(chip->cps_psy)) {
  		pr_err("Couldn't register CP slave power supply\n");
  		return PTR_ERR(chip->cps_psy);
  	}
  
  	return 0;
  }
  
  static int smb1390_slave_probe(struct smb1390 *chip)
  {
  	int stat, rc;
  
  	/* a "hello" read to test the presence of the slave PMIC */
  	rc = smb1390_read(chip, CORE_STATUS1_REG, &stat);
  	if (rc < 0) {
  		pr_err("Couldn't find slave SMB1390\n");
  		return -EINVAL;
  	}
  
  	rc = smb1390_triple_init_hw(chip);
  	if (rc < 0)
  		return rc;
  
  	/* Configure Slave CP Temp buffer O/P to High Impedance */
  	rc = smb1390_masked_write(chip, CORE_FTRIM_CTRL_REG,
  				  TEMP_BUFFER_OUTPUT_BIT,
  				  TEMP_BUFFER_OUTPUT_BIT);
  	if (rc < 0) {
  		pr_err("Couldn't configure Slave temp Buffer rc=%d\n", rc);
  		return rc;
  	}
  
  	rc = smb1390_init_cps_psy(chip);
  	if (rc < 0)
  		pr_err("Couldn't initialize cps psy rc=%d\n", rc);
  
  	return rc;
  }
  
  static int smb1390_probe(struct platform_device *pdev)
  {
  	struct smb1390 *chip;
  	int rc;
  
  	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
  	if (!chip)
  		return -ENOMEM;
  
  	chip->dev = &pdev->dev;
  	chip->die_temp = -ENODATA;
  	chip->disabled = true;
  
  	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
  	if (!chip->regmap) {
  		pr_err("Couldn't get regmap\n");
  		return -EINVAL;
  	}
  
  	platform_set_drvdata(pdev, chip);
  	chip->cp_role = (int)of_device_get_match_data(chip->dev);
  	switch (chip->cp_role) {
  	case CP_MASTER:
  		rc = smb1390_master_probe(chip);
  		break;
  	case CP_SLAVE:
  		rc = smb1390_slave_probe(chip);
  		break;
  	default:
  		pr_err("Couldn't find a matching role %d\n", chip->cp_role);
  		rc = -EINVAL;
  		goto cleanup;
  	}
  
  	if (rc < 0) {
  		if (rc != -EPROBE_DEFER)
  			pr_err("Couldn't probe SMB1390 %s rc=%d\n",
  			       chip->cp_role ? "Slave" : "Master", rc);
  		goto cleanup;
  	}
  
  	pr_info("smb1390 %s probed successfully\n", chip->cp_role ? "Slave" :
  		"Master");
  	return 0;
  
  cleanup:
  	platform_set_drvdata(pdev, NULL);
  	return rc;
  }
  
  static int smb1390_remove(struct platform_device *pdev)
  {
  	struct smb1390 *chip = platform_get_drvdata(pdev);
  
  	if (chip->cp_role !=  CP_MASTER) {
  		platform_set_drvdata(pdev, NULL);
  		return 0;
  	}
  
  	power_supply_unreg_notifier(&chip->nb);
  
  	/* explicitly disable charging */
  	vote(chip->disable_votable, USER_VOTER, true, 0);
  	vote(chip->disable_votable, SOC_LEVEL_VOTER, true, 0);
  	cancel_work(&chip->taper_work);
  	cancel_work(&chip->status_change_work);
  	wakeup_source_unregister(chip->cp_ws);
  	smb1390_destroy_votables(chip);
  	smb1390_release_channels(chip);
  	platform_set_drvdata(pdev, NULL);
  	return 0;
  }
  
  static void smb1390_shutdown(struct platform_device *pdev)
  {
  	struct smb1390 *chip = platform_get_drvdata(pdev);
  	int rc;
  
  	power_supply_unreg_notifier(&chip->nb);
  	/* Disable SMB1390 */
  	smb1390_dbg(chip, PR_MISC, "Disabling SMB1390\n");
  	rc = smb1390_masked_write(chip, CORE_CONTROL1_REG,
  					CMD_EN_SWITCHER_BIT, 0);
  	if (rc < 0)
  		pr_err("Couldn't disable chip rc=%d\n", rc);
  }
  
  static int smb1390_suspend(struct device *dev)
  {
  	struct smb1390 *chip = dev_get_drvdata(dev);
  
  	chip->suspended = true;
  	return 0;
  }
  
  static int smb1390_resume(struct device *dev)
  {
  	struct smb1390 *chip = dev_get_drvdata(dev);
  
  	chip->suspended = false;
  
  	/* ILIM rerun is applicable for both master and slave */
  	if (!chip->ilim_votable)
  		chip->ilim_votable = find_votable("CP_ILIM");
  
  	if (chip->ilim_votable)
  		rerun_election(chip->ilim_votable);
  
  	/* Run disable votable for master only */
  	if (chip->cp_role == CP_MASTER)
  		rerun_election(chip->disable_votable);
  
  	return 0;
  }
  
  static const struct dev_pm_ops smb1390_pm_ops = {
  	.suspend	= smb1390_suspend,
  	.resume		= smb1390_resume,
  };
  
  static struct platform_driver smb1390_driver = {
  	.driver	= {
  		.name		= "qcom,smb1390-charger-psy",
  		.owner		= THIS_MODULE,
  		.pm		= &smb1390_pm_ops,
  		.of_match_table	= match_table,
  	},
  	.probe	= smb1390_probe,
  	.remove	= smb1390_remove,
  	.shutdown	= smb1390_shutdown,
  };
  module_platform_driver(smb1390_driver);
  
  MODULE_DESCRIPTION("SMB1390 Charge Pump Driver");
  MODULE_LICENSE("GPL v2");
  