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

Commit 7e7da019 authored by David Collins's avatar David Collins
Browse files

power: qcom: apm: add support for msm8996pro



Mutual exclusion between APM switching and LMH register accesses
is only required for HMSS version 1.2 and below on MSM8996.  It
is not required for HMSS version 1.2 on MSM8996-Pro.  Add support
for MSM8996-Pro so that the LMH mutex is not locked during APM
switching on MSM8996-Pro parts.

Change-Id: If09454827ed12c4d436c2fc792f5adcd45ef4312
Signed-off-by: default avatarDavid Collins <collinsd@codeaurora.org>
parent 67b8b76a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ SRAM minimum operating voltage, the APM controller can be used to request a
switch to a power supply that will guarantee logic state retention.

Required properties
- compatible:	"qcom,msm-apm", "qcom,msmtitanium-apm"
- compatible:	"qcom,msm-apm", "qcom,msm8996pro-apm", "qcom,msmtitanium-apm"
- reg:		Specifies physical base address and size of memory mapped regions
		containing the APM controller, APCS CSR, APC PLL controller, and
		SPM event registers.
+125 −55
Original line number Diff line number Diff line
@@ -89,11 +89,10 @@ enum {

enum {
	MSM8996_ID,
	MSM8996PRO_ID,
	MSMTITANIUM_ID,
};

static int msm_id[] = {MSM8996_ID, MSMTITANIUM_ID};

struct msm_apm_ctrl_dev {
	struct list_head	list;
	struct device		*dev;
@@ -368,19 +367,43 @@ static int msm_apm_secure_clock_source_override(
	return 0;
}

static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
static int msm8996_apm_wait_for_switch(struct msm_apm_ctrl_dev *ctrl_dev,
					u32 done_val)
{
	int i, timeout = MSM_APM_SWITCH_TIMEOUT_US;
	int timeout = MSM_APM_SWITCH_TIMEOUT_US;
	u32 regval;
	int ret = 0;

	while (timeout > 0) {
		regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS);
		if ((regval & MSM_APM_CTL_STS_MASK) == done_val)
			break;

		udelay(1);
		timeout--;
	}

	if (timeout == 0) {
		dev_err(ctrl_dev->dev, "%s switch timed out. APCC_APM_CTL_STS=0x%x\n",
			done_val == MSM_APM_MX_DONE_VAL
				? "APCC to MX" : "MX to APCC",
			regval);
		return -ETIMEDOUT;
	}

	return 0;
}

static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
	unsigned long flags;
	int i, ret;

	mutex_lock(&scm_lmh_lock);
	spin_lock_irqsave(&ctrl_dev->lock, flags);

	ret = msm_apm_secure_clock_source_override(ctrl_dev, true);
	if (ret)
		return ret;
		goto done;

	/* Perform revision-specific programming steps */
	if (ctrl_dev->version < HMSS_VERSION_1P2) {
@@ -412,21 +435,7 @@ static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
	/* Ensure write above completes before delaying */
	mb();

	while (timeout > 0) {
		regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS);
		if ((regval & MSM_APM_CTL_STS_MASK) ==
		    MSM_APM_MX_DONE_VAL)
			break;

		udelay(1);
		timeout--;
	}

	if (timeout == 0) {
		ret = -ETIMEDOUT;
		dev_err(ctrl_dev->dev, "APCC to MX APM switch timed out. APCC_APM_CTL_STS=0x%x\n",
			regval);
	}
	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_MX_DONE_VAL);

	/* Perform revision-specific programming steps */
	if (ctrl_dev->version < HMSS_VERSION_1P2) {
@@ -449,15 +458,21 @@ static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
				       ctrl_dev->apcs_spm_events_addr[i]);
	}

	/*
	 * Ensure that HMSS v1.0/v1.1 register writes are completed before
	 * bailing out in the case of a switching time out.
	 */
	if (ret)
		goto done;

	ret = msm_apm_secure_clock_source_override(ctrl_dev, false);
	if (ret)
		return ret;
		goto done;

	if (!ret) {
	ctrl_dev->supply = MSM_APM_SUPPLY_MX;
	dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n");
	}

done:
	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
	mutex_unlock(&scm_lmh_lock);

@@ -466,17 +481,15 @@ static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)

static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
{
	int i, timeout = MSM_APM_SWITCH_TIMEOUT_US;
	u32 regval;
	int ret = 0;
	unsigned long flags;
	int i, ret;

	mutex_lock(&scm_lmh_lock);
	spin_lock_irqsave(&ctrl_dev->lock, flags);

	ret = msm_apm_secure_clock_source_override(ctrl_dev, true);
	if (ret)
		return ret;
		goto done;

	/* Perform revision-specific programming steps */
	if (ctrl_dev->version < HMSS_VERSION_1P2) {
@@ -508,21 +521,7 @@ static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
	/* Ensure write above completes before delaying */
	mb();

	while (timeout > 0) {
		regval = readl_relaxed(ctrl_dev->reg_base + APCC_APM_CTL_STS);
		if ((regval & MSM_APM_CTL_STS_MASK) ==
		    MSM_APM_APCC_DONE_VAL)
			break;

		udelay(1);
		timeout--;
	}

	if (timeout == 0) {
		ret = -ETIMEDOUT;
		dev_err(ctrl_dev->dev, "MX to APCC APM switch timed out. APCC_APM_CTL_STS=0x%x\n",
			regval);
	}
	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_APCC_DONE_VAL);

	/* Perform revision-specific programming steps */
	if (ctrl_dev->version < HMSS_VERSION_1P2) {
@@ -545,21 +544,81 @@ static int msm8996_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
			       ctrl_dev->apc1_pll_ctl_addr);
	}

	/*
	 * Ensure that HMSS v1.0/v1.1 register writes are completed before
	 * bailing out in the case of a switching time out.
	 */
	if (ret)
		goto done;

	ret = msm_apm_secure_clock_source_override(ctrl_dev, false);
	if (ret)
		return ret;
		goto done;

	if (!ret) {
	ctrl_dev->supply = MSM_APM_SUPPLY_APCC;
	dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n");
	}

done:
	spin_unlock_irqrestore(&ctrl_dev->lock, flags);
	mutex_unlock(&scm_lmh_lock);

	return ret;
}

static int msm8996pro_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&ctrl_dev->lock, flags);

	/* Switch arrays to MX supply and wait for its completion */
	writel_relaxed(MSM_APM_MX_MODE_VAL, ctrl_dev->reg_base +
		       APCC_APM_MODE);

	/* Ensure write above completes before delaying */
	mb();

	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_MX_DONE_VAL);
	if (ret)
		goto done;

	ctrl_dev->supply = MSM_APM_SUPPLY_MX;
	dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n");

done:
	spin_unlock_irqrestore(&ctrl_dev->lock, flags);

	return ret;
}

static int msm8996pro_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
{
	unsigned long flags;
	int ret;

	spin_lock_irqsave(&ctrl_dev->lock, flags);

	/* Switch arrays to APCC supply and wait for its completion */
	writel_relaxed(MSM_APM_APCC_MODE_VAL, ctrl_dev->reg_base +
		       APCC_APM_MODE);

	/* Ensure write above completes before delaying */
	mb();

	ret = msm8996_apm_wait_for_switch(ctrl_dev, MSM_APM_APCC_DONE_VAL);
	if (ret)
		goto done;

	ctrl_dev->supply = MSM_APM_SUPPLY_APCC;
	dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n");

done:
	spin_unlock_irqrestore(&ctrl_dev->lock, flags);

	return ret;
}

/* Titanium register value definitions */
#define MSMTITANIUM_APM_MX_MODE_VAL            0x00
#define MSMTITANIUM_APM_APCC_MODE_VAL          0x02
@@ -660,6 +719,9 @@ static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
	case MSM8996_ID:
		ret = msm8996_apm_switch_to_mx(ctrl_dev);
		break;
	case MSM8996PRO_ID:
		ret = msm8996pro_apm_switch_to_mx(ctrl_dev);
		break;
	case MSMTITANIUM_ID:
		ret = msmtitanium_apm_switch_to_mx(ctrl_dev);
		break;
@@ -676,6 +738,9 @@ static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
	case MSM8996_ID:
		ret = msm8996_apm_switch_to_apcc(ctrl_dev);
		break;
	case MSM8996PRO_ID:
		ret = msm8996pro_apm_switch_to_apcc(ctrl_dev);
		break;
	case MSMTITANIUM_ID:
		ret = msmtitanium_apm_switch_to_apcc(ctrl_dev);
		break;
@@ -870,11 +935,15 @@ static void apm_debugfs_base_remove(void)
static struct of_device_id msm_apm_match_table[] = {
	{
		.compatible = "qcom,msm-apm",
	  .data = &msm_id[MSM8996_ID]
		.data = (void *)(uintptr_t)MSM8996_ID,
	},
	{
		.compatible = "qcom,msm8996pro-apm",
		.data = (void *)(uintptr_t)MSM8996PRO_ID,
	},
	{
		.compatible = "qcom,msmtitanium-apm",
	  .data = &msm_id[MSMTITANIUM_ID]
		.data = (void *)(uintptr_t)MSMTITANIUM_ID,
	},
	{}
};
@@ -906,11 +975,12 @@ static int msm_apm_probe(struct platform_device *pdev)
	INIT_LIST_HEAD(&ctrl->list);
	spin_lock_init(&ctrl->lock);
	ctrl->dev = dev;
	ctrl->msm_id = *(int *)match->data;
	ctrl->msm_id = (uintptr_t)match->data;
	platform_set_drvdata(pdev, ctrl);

	switch (ctrl->msm_id) {
	case MSM8996_ID:
	case MSM8996PRO_ID:
		ret = msm_apm_ctrl_devm_ioremap(pdev, ctrl);
		if (ret) {
			dev_err(dev, "Failed to add APM controller device\n");