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

Commit 8786b3d5 authored by Shefali Jain's avatar Shefali Jain
Browse files

clk: msm: clock-cpu: Add snapshot of CPU driver



This is snapshot of the CPU driver as of msm-3.18
'commit c0b3f609196f ("ARM: dts: msm: Remove GPU
mempool for msm8909")'.

Change-Id: I89d717fea2c4fe20e236ba244b57b1ef7ef207e1
Signed-off-by: default avatarShefali Jain <shefjain@codeaurora.org>
parent aeedb24a
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
Qualcomm Technologies MSM8953 CPU clock tree

clock-cpu-8953 is a device that represents the MSM8953 CPU subystem clock
tree. It lists the various power supplies that need to be scaled when the
clocks are scaled and also other HW specific parameters like fmax tables etc.

The root clock generator could have the ramp controller in built.
Ramp control will allow programming the sequence ID for pulse swallowing,
enable sequence and for linking sequence IDs.

Required properties:
- compatible:		Must be "qcom,clock-cpu-8953".

- reg:			Pairs of physical base addresses and region sizes of
			memory mapped registers.
- reg-names:		Names of the bases for the above registers. Expected
			bases are:
			"c0-pll", "c1-pll", "c0-mux", "c1-mux", "cci-mux",
			"efuse", "perf_base"(optional), "rcgwr-c0-base(optional)",
			"rcgwr-c1-base(optional)".
- clocks:		The clocks sources used by the cluster/cci mux.
- clock-names:		Name of the clocks for the above clocks.
- vdd-mx-supply:	The regulator powering all the PLLs of clusters & cci.
- vdd-cl-supply:	The regulator powering the clusters & cci.
- qcom,speedX-bin-vY-ZZZ:
			A table of CPU frequency (Hz) to voltage (corner)
			mapping that represents the max frequency possible
			for each supported voltage level for a CPU. 'X' is
			the speed bin into which the device falls into - a
			bin will have unique frequency-voltage relationships.
			'Y' is the characterization version, implying that
			characterization (deciding what speed bin a device
			falls into) methods and/or encoding may change. The
			values 'X' and 'Y' are read from efuse registers, and
			the right table is picked from multiple possible tables.
			'ZZZ' can be cl for(c0 & c1) or cci depending on whether
			the table for the clusters or cci.

Optional Properties:
- qcom,enable-qos:      Boolean property to indicate the pm qos is required
			during set rate of the cluster clocks, which would not
			allow the cluster cores to go to low power mode.

Example:
	clock_cpu: qcom,cpu-clock-8953@b116000 {
		compatible = "qcom,cpu-clock-8953";
		reg =   <0xb114000  0x68>,
			<0xb014000  0x68>,
			<0xb116000  0x400>,
			<0xb111050  0x08>,
			<0xb011050  0x08>,
			<0xb1d1050  0x08>,
			<0x00a412c  0x08>;
		reg-names = "rcgwr-c0-base", "rcgwr-c1-base",
			    "c0-pll", "c0-mux", "c1-mux",
			    "cci-mux", "efuse";
		vdd-mx-supply = <&pm8953_s7_level_ao>;
		vdd-cl-supply = <&apc_vreg_corner>;
		clocks = <&clock_gcc clk_xo_a_clk_src>;
		clock-names = "xo_a";
		qcom,num-clusters = <2>;
		qcom,speed0-bin-v0-cl =
			<          0 0>,
			< 2208000000 7>;
		qcom,speed0-bin-v0-cci =
			<          0 0>,
			<  883200000 7>;
		#address-cells = <1>;
		#size-cells = <1>;
	};
+2 −0
Original line number Diff line number Diff line
@@ -14,4 +14,6 @@ obj-$(CONFIG_MSM_CLK_CONTROLLER_V2) += msm-clock-controller.o

ifeq ($(CONFIG_COMMON_CLK_MSM), y)
obj-$(CONFIG_ARCH_MSM8953)	+= clock-gcc-8953.o
obj-$(CONFIG_ARCH_MSM8953)	+= clock-cpu-8953.o
obj-$(CONFIG_ARCH_MSM8953)	+= clock-rcgwr.o
endif
+989 −0

File added.

Preview size limit exceeded, changes collapsed.

+572 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, 2017 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) "%s: " fmt, __func__

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/bitops.h>
#include <linux/io.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/clk.h>
#include <linux/clk/msm-clk-provider.h>
#include <linux/clk/msm-clock-generic.h>
#include <linux/debugfs.h>

#define CMD_RCGR_REG		0x0
#define CMD_UPDATE_EN		BIT(0)
/* Async_clk_en */
#define CMD_ROOT_EN		BIT(1)

struct rcgwr {
	void __iomem *base;
	void __iomem *rcg_base;
	int *dfs_sid_offset;
	int *dfs_sid_value;
	int  dfs_sid_len;
	int *link_sid_offset;
	int *link_sid_value;
	int  link_sid_len;
	int *lmh_sid_offset;
	int *lmh_sid_value;
	int  lmh_sid_len;
	bool inited;
};

static struct rcgwr **rcgwr;
static struct platform_device *cpu_clock_dev;
static u32 num_clusters;

#define DFS_SID_1_2		0x10
#define DFS_SID_3_4		0x14
#define DFS_SID_5_6		0x18
#define DFS_SID_7_8		0x1C
#define DFS_SID_9_10		0x20
#define DFS_SID_11_12		0x24
#define DFS_SID_13_14		0x28
#define DFS_SID_15		0x2C
#define LMH_SID_1_2		0x30
#define LMH_SID_3_4		0x34
#define LMH_SID_5		0x38
#define DCVS_CFG_CTL		0x50
#define LMH_CFG_CTL		0x54
#define RC_CFG_CTL		0x58
#define RC_CFG_DBG		0x5C
#define RC_CFG_UPDATE		0x60

#define RC_CFG_UPDATE_EN_BIT	8
#define RC_CFG_ACK_BIT		16

#define UPDATE_CHECK_MAX_LOOPS  500

#define DFS_SID_START		0xE
#define LMH_SID_START		0x6
#define DCVS_CONFIG		0x2
#define LINK_SID		0x3

/* Sequence for enable */
static int ramp_en[] = { 0x800, 0xC00, 0x400};

static int check_rcg_config(void __iomem *base)
{
	u32 cmd_rcgr_regval, count;

	cmd_rcgr_regval = readl_relaxed(base + CMD_RCGR_REG);
	cmd_rcgr_regval |= CMD_ROOT_EN;
	writel_relaxed(cmd_rcgr_regval, (base + CMD_RCGR_REG));

	for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
		cmd_rcgr_regval = readl_relaxed(base + CMD_RCGR_REG);
		cmd_rcgr_regval &= CMD_UPDATE_EN;
		if (!(cmd_rcgr_regval)) {
			pr_debug("cmd_rcgr state on update bit cleared 0x%x, cmd 0x%x\n",
					readl_relaxed(base + CMD_RCGR_REG),
					cmd_rcgr_regval);
			return 0;
		}
		udelay(1);
	}

	WARN_ON(count == 0);

	return -EINVAL;
}

static int rc_config_update(void __iomem *base, u32 rc_value, u32 rc_ack_bit)
{
	u32 count, ret = 0, regval;

	regval = readl_relaxed(base + RC_CFG_UPDATE);
	regval |= rc_value;
	writel_relaxed(regval, base + RC_CFG_UPDATE);
	regval |= BIT(RC_CFG_UPDATE_EN_BIT);
	writel_relaxed(regval, base + RC_CFG_UPDATE);

	/* Poll for update ack */
	for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
		regval = readl_relaxed((base + RC_CFG_UPDATE))
						  >> RC_CFG_ACK_BIT;
		if (regval == BIT(rc_ack_bit)) {
			ret = 0;
			break;
		}
		udelay(1);
	}
	WARN_ON(count == 0);

	/* Clear RC_CFG_UPDATE_EN */
	writel_relaxed(0 << RC_CFG_UPDATE_EN_BIT, (base + RC_CFG_UPDATE));
	/* Poll for update ack */
	for (count = UPDATE_CHECK_MAX_LOOPS; count > 0; count--) {
		regval = readl_relaxed((base + RC_CFG_UPDATE))
						>> RC_CFG_ACK_BIT;
		if (!regval)
			return ret;
		udelay(1);
	}
	WARN_ON(count == 0);

	return -EINVAL;
}


static int ramp_control_enable(struct platform_device *pdev,
		struct rcgwr *rcgwr)
{
	int i = 0, ret = 0;

	for (i = 0; i < ARRAY_SIZE(ramp_en); i++) {
		ret = check_rcg_config(rcgwr->rcg_base);
		if (ret) {
			dev_err(&pdev->dev, "Failed to update config!!!\n");
			return ret;
		}
		writel_relaxed(ramp_en[i], rcgwr->base + DCVS_CFG_CTL);
		ret = rc_config_update(rcgwr->base, DCVS_CONFIG, DCVS_CONFIG);
		if (ret) {
			dev_err(&pdev->dev,
				"Failed to config update for 0x2 and ACK 0x4\n");
			break;
		}
	}

	return ret;
}

static int ramp_down_disable(struct platform_device *pdev,
		struct rcgwr *rcgwr)
{
	int ret = 0;

	ret = check_rcg_config(rcgwr->rcg_base);
	if (ret) {
		dev_err(&pdev->dev, "Failed to update config!!!\n");
		return ret;
	}

	writel_relaxed(0x200, rcgwr->base + DCVS_CFG_CTL);
	ret = rc_config_update(rcgwr->base, DCVS_CONFIG, DCVS_CONFIG);
	if (ret)
		dev_err(&pdev->dev,
			"Failed to config update for 0x2 and ACK 0x4\n");

	return ret;
}

static int ramp_control_disable(struct platform_device *pdev,
		struct rcgwr *rcgwr)
{
	int ret = 0;

	if (!rcgwr->inited)
		return 0;

	ret = check_rcg_config(rcgwr->rcg_base);
	if  (ret) {
		dev_err(&pdev->dev, "Failed to update config!!!\n");
		return ret;
	}

	writel_relaxed(0x0, rcgwr->base + DCVS_CFG_CTL);

	ret = rc_config_update(rcgwr->base, DCVS_CONFIG, DCVS_CONFIG);
	if (ret)
		dev_err(&pdev->dev,
			"Failed to config update for 0x2 and ACK 0x4\n");

	rcgwr->inited = false;

	return ret;
}

static int ramp_link_sid(struct platform_device *pdev, struct rcgwr *rcgwr)
{
	int ret = 0, i;

	if (!rcgwr->link_sid_len) {
		pr_err("Use Default Link SID\n");
		return 0;
	}

	ret = check_rcg_config(rcgwr->rcg_base);
	if  (ret) {
		dev_err(&pdev->dev, "Failed to update config!!!\n");
		return ret;
	}

	for (i = 0; i < rcgwr->link_sid_len; i++)
		writel_relaxed(rcgwr->link_sid_value[i],
				rcgwr->base + rcgwr->link_sid_offset[i]);

	ret = rc_config_update(rcgwr->base, LINK_SID, LINK_SID);
	if (ret)
		dev_err(&pdev->dev,
			"Failed to config update for 0x3 and ACK 0x8\n");

	return ret;
}

static int ramp_lmh_sid(struct platform_device *pdev, struct rcgwr *rcgwr)
{
	int ret = 0, i, j;

	if (!rcgwr->lmh_sid_len) {
		pr_err("Use Default LMH SID\n");
		return 0;
	}

	ret = check_rcg_config(rcgwr->rcg_base);
	if  (ret) {
		dev_err(&pdev->dev, "Failed to update config!!!\n");
		return ret;
	}

	for (i = 0; i < rcgwr->lmh_sid_len; i++)
		writel_relaxed(rcgwr->lmh_sid_value[i],
				rcgwr->base + rcgwr->lmh_sid_offset[i]);

	for (i = LMH_SID_START, j = 0; j < rcgwr->lmh_sid_len; i--, j++) {
		ret = rc_config_update(rcgwr->base, i, i);
		if (ret) {
			dev_err(&pdev->dev,
			"Failed to update config for DFSSID-0x%x and ack 0x%lx\n",
					i, BIT(i));
			break;
		}
	}

	return ret;
}

static int ramp_dfs_sid(struct platform_device *pdev, struct rcgwr *rcgwr)
{
	int ret = 0, i, j;

	if (!rcgwr->dfs_sid_len) {
		pr_err("Use Default DFS SID\n");
		return 0;
	}

	ret = check_rcg_config(rcgwr->rcg_base);
	if  (ret) {
		dev_err(&pdev->dev, "Failed to update config!!!\n");
		return ret;
	}

	for (i = 0; i < rcgwr->dfs_sid_len; i++)
		writel_relaxed(rcgwr->dfs_sid_value[i],
				rcgwr->base + rcgwr->dfs_sid_offset[i]);

	for (i = DFS_SID_START, j = 0; j < rcgwr->dfs_sid_len; i--, j++) {
		ret = rc_config_update(rcgwr->base, i, i);
		if (ret) {
			dev_err(&pdev->dev,
				"Failed to update config for DFSSID-0x%x and ack 0x%lx\n",
					i, BIT(i));
			break;
		}
	}

	return ret;
}

static int parse_dt_rcgwr(struct platform_device *pdev, char *prop_name,
				int **off, int **val, int *len)
{
	struct device_node *node = pdev->dev.of_node;
	int prop_len, i;
	u32 *array;

	if (!of_find_property(node, prop_name, &prop_len)) {
		dev_err(&pdev->dev, "missing %s\n", prop_name);
		return -EINVAL;
	}

	prop_len /= sizeof(u32);
	if (prop_len % 2) {
		dev_err(&pdev->dev, "bad length %d\n", prop_len);
		return -EINVAL;
	}

	prop_len /= 2;

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

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

	array = devm_kzalloc(&pdev->dev,
			prop_len * sizeof(u32) * 2, GFP_KERNEL);
	if (!array)
		return -ENOMEM;

	of_property_read_u32_array(node, prop_name, array, prop_len * 2);
	for (i = 0; i < prop_len; i++) {
		*(*off + i) = array[i * 2];
		*(*val + i) = array[2 * i + 1];
	}

	*len = prop_len;

	return 0;
}

static int rcgwr_init_bases(struct platform_device *pdev, struct rcgwr *rcgwr,
		const char *name)
{
	struct resource *res;
	char rcg_name[] = "rcgwr-xxx-base";
	char rcg_mux[] = "xxx-mux";

	snprintf(rcg_name, ARRAY_SIZE(rcg_name), "rcgwr-%s-base", name);
	res = platform_get_resource_byname(pdev,
					IORESOURCE_MEM, rcg_name);
	if (!res) {
		dev_err(&pdev->dev, "missing %s\n", rcg_name);
		return -EINVAL;
	}

	rcgwr->base = devm_ioremap(&pdev->dev, res->start,
						resource_size(res));
	if (!rcgwr->base) {
		dev_err(&pdev->dev, "ioremap failed for %s\n",
					rcg_name);
		return -ENOMEM;
	}

	snprintf(rcg_mux, ARRAY_SIZE(rcg_mux), "%s-mux", name);
	res = platform_get_resource_byname(pdev,
					IORESOURCE_MEM, rcg_mux);
	if (!res) {
		dev_err(&pdev->dev, "missing %s\n", rcg_mux);
		return -EINVAL;
	}

	rcgwr->rcg_base = devm_ioremap(&pdev->dev, res->start,
						resource_size(res));
	if (!rcgwr->rcg_base) {
		dev_err(&pdev->dev, "ioremap failed for %s\n",
					rcg_name);
		return -ENOMEM;
	}

	return 0;
}

/*
 * Disable the RCG ramp controller.
 */
int clock_rcgwr_disable(struct platform_device *pdev)
{
	int i, ret = 0;

	for (i = 0; i < num_clusters; i++) {
		if (!rcgwr[i])
			return -ENOMEM;
		ret = ramp_control_disable(pdev, rcgwr[i]);
		if (ret)
			dev_err(&pdev->dev,
			"Ramp controller disable failed for Cluster-%d\n", i);
	}

	return ret;
}

static int clock_rcgwr_disable_set(void *data, u64 val)
{
	if (val) {
		pr_err("Enabling not supported!!\n");
		return -EINVAL;
	} else
		return clock_rcgwr_disable(cpu_clock_dev);
}

DEFINE_SIMPLE_ATTRIBUTE(rcgwr_enable_fops, NULL,
			clock_rcgwr_disable_set, "%lld\n");

static int clock_debug_enable_show(struct seq_file *m, void *v)
{
	int i = 0;

	seq_puts(m, "Cluster\t\tEnable\n");

	for (i = 0; i < num_clusters; i++)
		seq_printf(m, "%d\t\t%d\n", i, rcgwr[i]->inited);

	return 0;
}

static int clock_debug_open(struct inode *inode, struct file *file)
{
	return single_open(file, clock_debug_enable_show, inode->i_private);
}

static const struct file_operations rcgwr_enable_show = {
	.owner		= THIS_MODULE,
	.open		= clock_debug_open,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
};

/*
 * Program the DFS Sequence ID.
 * Program the Link Sequence ID.
 * Enable RCG with ramp controller.
 */
int clock_rcgwr_init(struct platform_device *pdev)
{
	int ret = 0, i;
	char link_sid[] = "qcom,link-sid-xxx";
	char dfs_sid[]  = "qcom,dfs-sid-xxx";
	char lmh_sid[]  = "qcom,lmh-sid-xxx";
	char ramp_dis[] = "qcom,ramp-dis-xxx";
	char names[] = "cxxx";
	struct dentry *debugfs_base;

	ret = of_property_read_u32(pdev->dev.of_node, "qcom,num-clusters",
						&num_clusters);
	if (ret)
		panic("Cannot read num-clusters from dt (ret:%d)\n", ret);

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

	for (i = 0; i < num_clusters; i++) {
		rcgwr[i] = devm_kzalloc(&pdev->dev, sizeof(struct rcgwr),
				GFP_KERNEL);
		if (!rcgwr[i])
			goto fail_mem;

		snprintf(names, ARRAY_SIZE(names), "c%d", i);

		ret = rcgwr_init_bases(pdev, rcgwr[i], names);
		if (ret) {
			dev_err(&pdev->dev, "Failed to init_bases for RCGwR\n");
			goto fail_mem;
		}

		snprintf(dfs_sid, ARRAY_SIZE(dfs_sid),
					"qcom,dfs-sid-%s", names);
		ret = parse_dt_rcgwr(pdev, dfs_sid, &(rcgwr[i]->dfs_sid_offset),
			&(rcgwr[i]->dfs_sid_value), &(rcgwr[i]->dfs_sid_len));
		if (ret)
			dev_err(&pdev->dev,
				"No DFS SID tables found for Cluster-%d\n", i);

		snprintf(link_sid, ARRAY_SIZE(link_sid),
					"qcom,link-sid-%s", names);
		ret = parse_dt_rcgwr(pdev, link_sid,
			&(rcgwr[i]->link_sid_offset),
			&(rcgwr[i]->link_sid_value), &(rcgwr[i]->link_sid_len));
		if (ret)
			dev_err(&pdev->dev,
				"No Link SID tables found for Cluster-%d\n", i);

		snprintf(lmh_sid, ARRAY_SIZE(lmh_sid),
					"qcom,lmh-sid-%s", names);
		ret = parse_dt_rcgwr(pdev, lmh_sid,
			&(rcgwr[i]->lmh_sid_offset),
			&(rcgwr[i]->lmh_sid_value), &(rcgwr[i]->lmh_sid_len));
		if (ret)
			dev_err(&pdev->dev,
				"No LMH SID tables found for Cluster-%d\n", i);

		ret = ramp_lmh_sid(pdev, rcgwr[i]);
		if (ret)
			goto fail_mem;

		ret = ramp_dfs_sid(pdev, rcgwr[i]);
		if (ret)
			goto fail_mem;

		ret = ramp_link_sid(pdev, rcgwr[i]);
		if (ret)
			goto fail_mem;

		ret = ramp_control_enable(pdev, rcgwr[i]);
		if (ret)
			goto fail_mem;

		snprintf(ramp_dis, ARRAY_SIZE(ramp_dis),
					"qcom,ramp-dis-%s", names);
		if (of_property_read_bool(pdev->dev.of_node, ramp_dis)) {
			ret = ramp_down_disable(pdev, rcgwr[i]);
			if (ret)
				goto fail_mem;
		}

		rcgwr[i]->inited = true;
	}

	cpu_clock_dev = pdev;

	debugfs_base = debugfs_create_dir("rcgwr", NULL);
	if (debugfs_base) {
		if (!debugfs_create_file("enable", 0444, debugfs_base, NULL,
				&rcgwr_enable_fops)) {
			pr_err("Unable to create `enable` debugfs entry\n");
			debugfs_remove(debugfs_base);
		}

		if (!debugfs_create_file("status", 0444, debugfs_base, NULL,
					&rcgwr_enable_show)) {
			pr_err("Unable to create `status` debugfs entry\n");
			debugfs_remove_recursive(debugfs_base);
		}
	} else
		pr_err("Unable to create debugfs dir\n");

	pr_info("RCGwR  Init Completed\n");

	return ret;

fail_mem:
	--i;
	for (; i >= 0 ; i--) {
		devm_kfree(&pdev->dev, rcgwr[i]);
		rcgwr[i] = NULL;
	}
	devm_kfree(&pdev->dev, rcgwr);
	panic("RCGwR failed to Initialize\n");
}