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

Commit 36d54d2f authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "clk: qcom: Add support for root clock generator with ramp controller"

parents 38086338 080feff9
Loading
Loading
Loading
Loading
+42 −0
Original line number Diff line number Diff line
Qualcomm CPU clock Ramp Controller

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.

This will use the cpu clock device node.

Required properties:
- 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:
			"rcgwr-cX-base" (X: 0 or 1 based on the number of
			clusters)
- qcom,num-clusters:	Number of clusters which support RCGwR.

Optional Properties:
- qcom,lmh-sid-cX:	List of LMH SID offset and value to be programmed for
			each cluster (X: 0 or 1)
- qcom,link-sid-cX:	List of Link SID offset and value to be programmed for
			each cluster (X: 0 or 1)
- qcom,dfs-sid-cX:	List of DFS SID offset and value to be programmed for
			each cluster (X: 0 or 1)
- qcom,ramp-dis-cX:	Boolean property to disable ramp down for each cluster
			(X: 0 or 1)

Example:
        clock_cpu: qcom,cpu-clock@b016000 {
                compatible = "qcom,cpu-clock";
		qcom,num-clusters = <2>;
		qcom,lmh-sid-c0  = < 0x30 0x077706db>,
				   < 0x34 0x05550249>,
				   < 0x38 0x00000111>;
		qcom,lmh-sid-c1  = < 0x30 0x077706db>,
				   < 0x34 0x05550249>,
				   < 0x38 0x00000111>;
                reg = <0xb114000 0x68>,
		      <0xb014000 0x68>;
                reg-names = "rcgwr-c0-base", "rcgwr-c1-base";
                #clock-cells = <1>;
};
+1 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ obj-$(CONFIG_ARCH_MSM8916) += clock-a7.o
obj-$(CONFIG_ARCH_MSM8916)	+= clock-cpu-8939.o
obj-$(CONFIG_ARCH_MSM8916)	+= clock-gcc-8952.o
obj-$(CONFIG_ARCH_MSM8916)	+= clock-gcc-titanium.o
obj-$(CONFIG_ARCH_MSM8916)	+= clock-rcgwr.o


obj-y				+= gdsc.o
+572 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2015, 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);
	}

	BUG_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);
	}
	BUG_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);
	}
	BUG_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)
		BUG();

	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", S_IRUGO, 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");
}
+4 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <linux/of.h>
#include <linux/device.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/regulator/consumer.h>
#include <linux/seq_file.h>
@@ -214,6 +215,9 @@ int msm_clock_register(struct clk_lookup *table, size_t size);
int of_msm_clock_register(struct device_node *np, struct clk_lookup *table,
				size_t size);

int clock_rcgwr_init(struct platform_device *pdev);
int clock_rcgwr_disable(struct platform_device *pdev);

extern struct clk dummy_clk;
extern struct clk_ops clk_ops_dummy;