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

Commit 6df4b3c2 authored by Tirupathi Reddy's avatar Tirupathi Reddy
Browse files

power: qcom: apm: Add APM controller support for msmtitanium



Add APM controller register programming sequence for msmtitanium
target.

Change-Id: I8a78901d58a696d52e15ac083d05b0b705e83866
Signed-off-by: default avatarTirupathi Reddy <tirupath@codeaurora.org>
parent 7427cd00
Loading
Loading
Loading
Loading
+17 −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"
- compatible:	"qcom,msm-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.
@@ -21,6 +21,18 @@ Optional properties
				clock sources to GPLL0 before the APM switch begins and to
				switch back to the original clock source after the APM switch
				completes.
- qcom,apm-post-halt-delay:	The APM controller post halt delay counter value that SW needs
				to program one time before starting the APM HW controller for
				msmtitanium target.
- qcom,apm-halt-clk-delay:	The APM controller halt clock delay counter value that SW
				needs to program one time before starting the APM HW controller
				for msmtitanium target.
- qcom,apm-resume-clk-delay:	The APM controller resume clock delay counter value that SW
				needs to program one time before starting the APM HW controller
				for msmtitanium target.
- qcom,apm-sel-switch-delay:	The APM controller switch selection delay counter value that SW
				needs to program one time before starting the APM HW controller
				for msmtitanium target.

MSM APM Users

@@ -50,6 +62,10 @@ Example:
				"apc1-cpu1-spm",
				"apc0-l2-spm",
				"apc1-l2-spm";
		qcom,apm-post-halt-delay = <0x2>;
		qcom,apm-halt-clk-delay = <0x11>;
		qcom,apm-resume-clk-delay = <0x10>;
		qcom,apm-sel-switch-delay = <0x01>;

	foo_user {
		...
+284 −11
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@

#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/of_device.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/kernel.h>
@@ -86,6 +87,13 @@ enum {
	CLOCK_ASSERT_TOGGLE,
};

enum {
	MSM8996_ID,
	MSMTITANIUM_ID,
};

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

struct msm_apm_ctrl_dev {
	struct list_head	list;
	struct device		*dev;
@@ -99,6 +107,7 @@ struct msm_apm_ctrl_dev {
	bool			clk_src_override;
	u32			version;
	struct dentry		*debugfs;
	u32			msm_id;
};

#if defined(CONFIG_DEBUG_FS)
@@ -231,6 +240,117 @@ free_events:
	return ret;
}

/* Titanium register offset definition */
#define MSMTITANIUM_APM_DLY_CNTR	0x2ac

/* Register field shift definitions */
#define APM_CTL_SEL_SWITCH_DLY_SHIFT	0
#define APM_CTL_RESUME_CLK_DLY_SHIFT	8
#define APM_CTL_HALT_CLK_DLY_SHIFT	16
#define APM_CTL_POST_HALT_DLY_SHIFT	24

/* Register field mask definitions */
#define APM_CTL_SEL_SWITCH_DLY_MASK	GENMASK(7, 0)
#define APM_CTL_RESUME_CLK_DLY_MASK	GENMASK(15, 8)
#define APM_CTL_HALT_CLK_DLY_MASK	GENMASK(23, 16)
#define APM_CTL_POST_HALT_DLY_MASK	GENMASK(31, 24)

/*
 * Get the resources associated with the msmtitanium APM controller from
 * device tree, remap all I/O addresses, and program the initial
 * register configuration required for the titanium APM controller device.
 */
static int msmtitanium_apm_ctrl_init(struct platform_device *pdev,
				     struct msm_apm_ctrl_dev *ctrl)
{
	struct device *dev = &pdev->dev;
	struct resource *res;
	u32 delay_counter, val = 0, regval = 0;
	int rc = 0;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pm-apcc-glb");
	if (!res) {
		dev_err(dev, "Missing PM APCC Global register physical address\n");
		return -ENODEV;
	}
	ctrl->reg_base = devm_ioremap(dev, res->start, resource_size(res));
	if (!ctrl->reg_base) {
		dev_err(dev, "Failed to map PM APCC Global registers\n");
		return -ENOMEM;
	}

	/*
	 * Initial APM register configuration required before starting
	 * APM HW controller.
	 */
	regval = readl_relaxed(ctrl->reg_base + MSMTITANIUM_APM_DLY_CNTR);
	val = regval;

	if (of_find_property(dev->of_node, "qcom,apm-post-halt-delay", NULL)) {
		rc = of_property_read_u32(dev->of_node,
				"qcom,apm-post-halt-delay", &delay_counter);
		if (rc < 0) {
			dev_err(dev, "apm-post-halt-delay read failed, rc = %d",
				rc);
			return rc;
		}

		val &= ~APM_CTL_POST_HALT_DLY_MASK;
		val |= (delay_counter << APM_CTL_POST_HALT_DLY_SHIFT)
			& APM_CTL_POST_HALT_DLY_MASK;
	}

	if (of_find_property(dev->of_node, "qcom,apm-halt-clk-delay", NULL)) {
		rc = of_property_read_u32(dev->of_node,
				"qcom,apm-halt-clk-delay", &delay_counter);
		if (rc < 0) {
			dev_err(dev, "apm-halt-clk-delay read failed, rc = %d",
				rc);
			return rc;
		}

		val &= ~APM_CTL_HALT_CLK_DLY_MASK;
		val |= (delay_counter << APM_CTL_HALT_CLK_DLY_SHIFT)
			& APM_CTL_HALT_CLK_DLY_MASK;
	}

	if (of_find_property(dev->of_node, "qcom,apm-resume-clk-delay", NULL)) {
		rc = of_property_read_u32(dev->of_node,
				"qcom,apm-resume-clk-delay", &delay_counter);
		if (rc < 0) {
			dev_err(dev, "apm-resume-clk-delay read failed, rc = %d",
				rc);
			return rc;
		}

		val &= ~APM_CTL_RESUME_CLK_DLY_MASK;
		val |= (delay_counter << APM_CTL_RESUME_CLK_DLY_SHIFT)
			& APM_CTL_RESUME_CLK_DLY_MASK;
	}

	if (of_find_property(dev->of_node, "qcom,apm-sel-switch-delay", NULL)) {
		rc = of_property_read_u32(dev->of_node,
				"qcom,apm-sel-switch-delay", &delay_counter);
		if (rc < 0) {
			dev_err(dev, "apm-sel-switch-delay read failed, rc = %d",
				rc);
			return rc;
		}

		val &= ~APM_CTL_SEL_SWITCH_DLY_MASK;
		val |= (delay_counter << APM_CTL_SEL_SWITCH_DLY_SHIFT)
			& APM_CTL_SEL_SWITCH_DLY_MASK;
	}

	if (val != regval) {
		writel_relaxed(val, ctrl->reg_base + MSMTITANIUM_APM_DLY_CNTR);
		/* make sure write completes before return */
		mb();
	}

	return rc;
}

static int msm_apm_secure_clock_source_override(
			struct msm_apm_ctrl_dev *ctrl_dev, bool enable)
{
@@ -248,7 +368,7 @@ static int msm_apm_secure_clock_source_override(
	return 0;
}

static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
static int msm8996_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
	int i, timeout = MSM_APM_SWITCH_TIMEOUT_US;
	u32 regval;
@@ -344,7 +464,7 @@ static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
	return ret;
}

static int msm_apm_switch_to_apcc(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;
@@ -440,6 +560,130 @@ static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
	return ret;
}

/* Titanium register value definitions */
#define MSMTITANIUM_APM_MX_MODE_VAL            0x00
#define MSMTITANIUM_APM_APCC_MODE_VAL          0x02
#define MSMTITANIUM_APM_MX_DONE_VAL            0x00
#define MSMTITANIUM_APM_APCC_DONE_VAL          0x02

/* Titanium register offset definitions */
#define MSMTITANIUM_APCC_APM_MODE              0x000002a8
#define MSMTITANIUM_APCC_APM_CTL_STS           0x000002b0

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

	spin_lock_irqsave(&ctrl_dev->lock, flags);

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

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

	while (timeout > 0) {
		regval = readl_relaxed(ctrl_dev->reg_base +
					MSMTITANIUM_APCC_APM_CTL_STS);
		if ((regval & MSM_APM_CTL_STS_MASK) ==
				MSMTITANIUM_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);
	} else {
		ctrl_dev->supply = MSM_APM_SUPPLY_MX;
		dev_dbg(ctrl_dev->dev, "APM supply switched to MX\n");
	}

	spin_unlock_irqrestore(&ctrl_dev->lock, flags);

	return ret;
}

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

	spin_lock_irqsave(&ctrl_dev->lock, flags);

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

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

	while (timeout > 0) {
		regval = readl_relaxed(ctrl_dev->reg_base +
					MSMTITANIUM_APCC_APM_CTL_STS);
		if ((regval & MSM_APM_CTL_STS_MASK) ==
				MSMTITANIUM_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);
	} else {
		ctrl_dev->supply = MSM_APM_SUPPLY_APCC;
		dev_dbg(ctrl_dev->dev, "APM supply switched to APCC\n");
	}

	spin_unlock_irqrestore(&ctrl_dev->lock, flags);

	return ret;
}

static int msm_apm_switch_to_mx(struct msm_apm_ctrl_dev *ctrl_dev)
{
	int ret = 0;

	switch (ctrl_dev->msm_id) {
	case MSM8996_ID:
		ret = msm8996_apm_switch_to_mx(ctrl_dev);
		break;
	case MSMTITANIUM_ID:
		ret = msmtitanium_apm_switch_to_mx(ctrl_dev);
		break;
	}

	return ret;
}

static int msm_apm_switch_to_apcc(struct msm_apm_ctrl_dev *ctrl_dev)
{
	int ret = 0;

	switch (ctrl_dev->msm_id) {
	case MSM8996_ID:
		ret = msm8996_apm_switch_to_apcc(ctrl_dev);
		break;
	case MSMTITANIUM_ID:
		ret = msmtitanium_apm_switch_to_apcc(ctrl_dev);
		break;
	}

	return ret;
}

/**
 * msm_apm_get_supply() - Returns the supply that is currently
 *			powering the memory arrays
@@ -623,10 +867,23 @@ static void apm_debugfs_base_remove(void)

#endif

static struct of_device_id msm_apm_match_table[] = {
	{
	  .compatible = "qcom,msm-apm",
	  .data = &msm_id[MSM8996_ID]
	},
	{
	  .compatible = "qcom,msmtitanium-apm",
	  .data = &msm_id[MSMTITANIUM_ID]
	},
	{}
};

static int msm_apm_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct msm_apm_ctrl_dev *ctrl;
	const struct of_device_id *match;
	int ret = 0;

	dev_dbg(dev, "probing MSM Array Power Mux driver\n");
@@ -636,6 +893,10 @@ static int msm_apm_probe(struct platform_device *pdev)
		return -ENODEV;
	}

	match = of_match_device(msm_apm_match_table, dev);
	if (!match)
		return -ENODEV;

	ctrl = devm_kzalloc(dev, sizeof(*ctrl), GFP_KERNEL);
	if (!ctrl) {
		dev_err(dev, "MSM APM controller memory allocation failed\n");
@@ -645,13 +906,30 @@ 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;
	platform_set_drvdata(pdev, ctrl);

	switch (ctrl->msm_id) {
	case MSM8996_ID:
		ret = msm_apm_ctrl_devm_ioremap(pdev, ctrl);
		if (ret) {
			dev_err(dev, "Failed to add APM controller device\n");
			return ret;
		}
		break;
	case MSMTITANIUM_ID:
		ret = msmtitanium_apm_ctrl_init(pdev, ctrl);
		if (ret) {
			dev_err(dev, "Failed to initialize APM controller device: ret=%d\n",
				ret);
			return ret;
		}
		break;
	default:
		dev_err(dev, "unable to add APM controller device for msm_id:%d\n",
			ctrl->msm_id);
		return -ENODEV;
	}

	apm_debugfs_init(ctrl);
	mutex_lock(&apm_ctrl_list_mutex);
@@ -678,11 +956,6 @@ static int msm_apm_remove(struct platform_device *pdev)
	return 0;
}

static struct of_device_id msm_apm_match_table[] = {
	{ .compatible = MSM_APM_DRIVER_NAME, },
	{}
};

static struct platform_driver msm_apm_driver = {
	.driver		= {
		.name		= MSM_APM_DRIVER_NAME,