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

Commit 36134348 authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "power: smb1390-psy: Add support for dual charge Pumps"

parents 5604b1fa a2bba45d
Loading
Loading
Loading
Loading
+402 −57
Original line number Diff line number Diff line
@@ -94,6 +94,8 @@
#define SWITCHER_TOGGLE_VOTER	"SWITCHER_TOGGLE_VOTER"
#define SOC_LEVEL_VOTER		"SOC_LEVEL_VOTER"

#define CP_MASTER		0
#define CP_SLAVE		1
#define THERMAL_SUSPEND_DECIDEGC	1400
#define MAX_ILIM_UA			3200000

@@ -119,6 +121,12 @@ enum {
	NUM_IRQS,
};

enum isns_mode {
	ISNS_MODE_OFF = 0,
	ISNS_MODE_ACTIVE,
	ISNS_MODE_STANDBY,
};

enum {
	SWITCHER_EN = 0,
	SMB_PIN_EN,
@@ -157,8 +165,10 @@ struct smb1390 {
	struct votable		*ilim_votable;
	struct votable		*fcc_votable;
	struct votable		*fv_votable;
	struct votable		*cp_awake_votable;

	/* power supplies */
	struct power_supply	*cps_psy;
	struct power_supply	*usb_psy;
	struct power_supply	*batt_psy;
	struct power_supply	*dc_psy;
@@ -179,6 +189,8 @@ struct smb1390 {
	u32			max_temp_alarm_degc;
	u32			max_cutoff_soc;
	u32			pl_output_mode;
	u32			cp_role;
	enum isns_mode		current_capability;
};

struct smb_irq {
@@ -265,6 +277,46 @@ static bool is_psy_voter_available(struct smb1390 *chip)
	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 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;
@@ -320,6 +372,18 @@ static int smb1390_get_cp_en_status(struct smb1390 *chip, int id, bool *enable)
	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;
@@ -425,7 +489,13 @@ static int smb1390_get_die_temp(struct smb1390 *chip,
	return rc;
}

static int smb1390_get_isns(struct smb1390 *chip,
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;
@@ -445,12 +515,105 @@ static int smb1390_get_isns(struct smb1390 *chip,
	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);
	rc = smb1390_masked_write(chip, CORE_ATEST1_SEL_REG,
				ATEST1_OUTPUT_ENABLE_BIT | ATEST1_SEL_MASK,
				ATEST1_OUTPUT_ENABLE_BIT | ISNS_INT_VAL);
	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 set CORE_ATEST1_SEL_REG, rc = %d\n", rc);
			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;
	}

@@ -461,18 +624,23 @@ static int smb1390_get_isns(struct smb1390 *chip,
		goto unlock;
	}

	rc = smb1390_masked_write(chip, CORE_ATEST1_SEL_REG,
				ATEST1_OUTPUT_ENABLE_BIT | ATEST1_SEL_MASK, 0);
	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);

	/* ISNS = 2 * (1496 - 1390_therm_input * 0.00356) * 1000 uA */
	if (rc >= 0)
		val->intval = (1496 * 1000 - div_s64((s64)temp * 3560,
							1000)) * 2;
		val->intval = smb1390_get_isns(temp);

	return rc;
}
@@ -505,19 +673,17 @@ static int smb1390_disable_vote_cb(struct votable *votable, void *data,
{
	struct smb1390 *chip = data;
	int rc = 0;
	u8 mask, val;

	if (!is_psy_voter_available(chip) || chip->suspended)
		return -EAGAIN;

	if (disable) {
		rc = smb1390_masked_write(chip, CORE_CONTROL1_REG,
				   CMD_EN_SWITCHER_BIT, 0);
		if (rc < 0)
			return rc;
	} else {
		rc = smb1390_masked_write(chip, CORE_CONTROL1_REG,
				   CMD_EN_SWITCHER_BIT, CMD_EN_SWITCHER_BIT);
		if (rc < 0)
	mask = CMD_EN_SWITCHER_BIT | CMD_EN_SL_BIT;
	val = is_cps_available(chip) ? mask : CMD_EN_SWITCHER_BIT;
	rc = smb1390_masked_write(chip, CORE_CONTROL1_REG, mask,
				  disable ? 0 : val);
	if (rc < 0) {
		pr_err("Couldn't write CORE_CONTROL1_REG, rc=%d\n", rc);
		return rc;
	}

@@ -533,6 +699,7 @@ 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;

	if (!is_psy_voter_available(chip) || chip->suspended)
@@ -545,20 +712,31 @@ static int smb1390_ilim_vote_cb(struct votable *votable, void *data,
	}

	ilim_uA = min(ilim_uA, MAX_ILIM_UA);
	rc = smb1390_masked_write(chip, CORE_FTRIM_ILIM_REG,
		CFG_ILIM_MASK,
		DIV_ROUND_CLOSEST(max(ilim_uA, 500000) - 500000, 100000));
	if (rc < 0) {
		pr_err("Failed to write ILIM Register, rc=%d\n", rc);
		return rc;
	}

	/* 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 {
		if (is_cps_available(chip)) {
			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\n", ilim_uA);
		vote(chip->disable_votable, ILIM_VOTER, false, 0);
	}
@@ -566,6 +744,21 @@ static int smb1390_ilim_vote_cb(struct votable *votable, void *data,
	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)
{
@@ -761,6 +954,7 @@ static enum power_supply_property smb1390_charge_pump_props[] = {
	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,
@@ -832,7 +1026,10 @@ static int smb1390_get_prop(struct power_supply *psy,
		}
		break;
	case POWER_SUPPLY_PROP_CP_ISNS:
		rc = smb1390_get_isns(chip, val);
		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;
@@ -905,6 +1102,8 @@ static int smb1390_prop_is_writeable(struct power_supply *psy,
	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:
		return 1;
	default:
		break;
@@ -990,6 +1189,8 @@ static void smb1390_release_channels(struct smb1390 *chip)

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))
@@ -1164,14 +1365,23 @@ static void smb1390_create_debugfs(struct smb1390 *chip)
}
#endif

static int smb1390_probe(struct platform_device *pdev)
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)
{
	struct smb1390 *chip;
	int rc;
	struct device_node *revid_dev_node;
	struct pmic_revid_data *pmic_rev_id;
	int rc;

	revid_dev_node = of_parse_phandle(pdev->dev.of_node,
	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");
@@ -1189,23 +1399,9 @@ static int smb1390_probe(struct platform_device *pdev)
		return -EPROBE_DEFER;
	}

	chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL);
	if (!chip)
		return -ENOMEM;

	chip->dev = &pdev->dev;
	chip->pmic_rev_id = pmic_rev_id;
	spin_lock_init(&chip->status_change_lock);
	mutex_init(&chip->die_chan_lock);
	chip->die_temp = -ENODATA;
	chip->pmic_rev_id = pmic_rev_id;
	chip->disabled = true;
	platform_set_drvdata(pdev, chip);

	chip->regmap = dev_get_regmap(chip->dev->parent, NULL);
	if (!chip->regmap) {
		pr_err("Couldn't get regmap\n");
		return -EINVAL;
	}

	rc = smb1390_parse_dt(chip);
	if (rc < 0) {
@@ -1252,9 +1448,6 @@ static int smb1390_probe(struct platform_device *pdev)
	}

	smb1390_create_debugfs(chip);

	pr_debug("smb1390 probed successfully chip_version=%d\n",
			chip->pmic_rev_id->rev4);
	return 0;

out_notifier:
@@ -1268,10 +1461,166 @@ static int smb1390_probe(struct platform_device *pdev)
	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_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_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_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 */
@@ -1282,6 +1631,7 @@ static int smb1390_remove(struct platform_device *pdev)
	wakeup_source_unregister(chip->cp_ws);
	smb1390_destroy_votables(chip);
	smb1390_release_channels(chip);
	platform_set_drvdata(pdev, NULL);
	return 0;
}

@@ -1323,11 +1673,6 @@ static const struct dev_pm_ops smb1390_pm_ops = {
	.resume		= smb1390_resume,
};

static const struct of_device_id match_table[] = {
	{ .compatible = "qcom,smb1390-charger-psy", },
	{ },
};

static struct platform_driver smb1390_driver = {
	.driver	= {
		.name		= "qcom,smb1390-charger-psy",