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

Commit 3895b977 authored by Raja Mallik's avatar Raja Mallik Committed by Sundara Vinayagam
Browse files

clk: msm: Add clock support for msm8909 target



Port RPM, GCC and A7 clock controller code for msm8909 to support
peripheral clocks and CPU clock.

Add minor fixes and cleanups for code.

Snapshot is taken from msm-3.18 kernel version @ commit bf9148fbb54a75
("ARM: dts: msm: disable internal pull up for 8909w BG")

Change-Id: I2abc93edb7ee6d0462a522b25af829ff22ecdb48
Signed-off-by: default avatarSundara Vinayagam <sundvi@codeaurora.org>
parent 513494c8
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -26,6 +26,13 @@ obj-$(CONFIG_ARCH_MDM9607) +=clock-gcc-mdm9607.o

# MDM9650
obj-$(CONFIG_ARCH_MDM9650)      += clock-gcc-mdm9650.o

# MSM8909
obj-$(CONFIG_ARCH_MSM8909)	+= clock-rpm-8909.o
obj-$(CONFIG_ARCH_MSM8909)	+= clock-gcc-8909.o

# ACPU clock
obj-$(CONFIG_ARCH_MSM8909)	+= clock-a7.o
endif

obj-y				+= mdss/
+506 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2013-2018, 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/module.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/cpu.h>
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/clk/msm-clock-generic.h>
#include <linux/of_platform.h>
#include <linux/pm_opp.h>
#include <soc/qcom/clock-local2.h>
#include <dt-bindings/clock/msm-clocks-a7.h>

#include "clock.h"

DEFINE_VDD_REGS_INIT(vdd_cpu, 1);

static struct mux_div_clk a7ssmux = {
	.ops = &rcg_mux_div_ops,
	.safe_freq = 300000000,
	.data = {
		.max_div = 32,
		.min_div = 2,
		.is_half_divider = true,
	},
	.c = {
		.dbg_name = "a7ssmux",
		.ops = &clk_ops_mux_div_clk,
		.vdd_class = &vdd_cpu,
		CLK_INIT(a7ssmux.c),
	},
	.parents = (struct clk_src[8]) {},
	.div_mask = BM(4, 0),
	.src_mask = BM(10, 8) >> 8,
	.src_shift = 8,
	.en_mask = 1,
};

static struct clk_lookup clock_tbl_a7[] = {
	CLK_LIST(a7ssmux),
	CLK_LOOKUP_OF("cpu0_clk",	a7ssmux, "fe805664.qcom,pm"),
	CLK_LOOKUP_OF("cpu1_clk",	a7ssmux, "fe805664.qcom,pm"),
	CLK_LOOKUP_OF("cpu2_clk",	a7ssmux, "fe805664.qcom,pm"),
	CLK_LOOKUP_OF("cpu3_clk",	a7ssmux, "fe805664.qcom,pm"),
	CLK_LOOKUP_OF("cpu0_clk",   a7ssmux, "8600664.qcom,pm"),
	CLK_LOOKUP_OF("cpu1_clk",   a7ssmux, "8600664.qcom,pm"),
	CLK_LOOKUP_OF("cpu2_clk",   a7ssmux, "8600664.qcom,pm"),
	CLK_LOOKUP_OF("cpu3_clk",   a7ssmux, "8600664.qcom,pm"),
};

static void print_opp_table(int a7_cpu)
{
	struct dev_pm_opp *oppfmax, *oppfmin;
	unsigned long apc0_fmax = a7ssmux.c.fmax[a7ssmux.c.num_fmax - 1];
	unsigned long apc0_fmin = a7ssmux.c.fmax[1];

	rcu_read_lock();
	oppfmax = dev_pm_opp_find_freq_exact(get_cpu_device(a7_cpu), apc0_fmax,
						true);
	oppfmin = dev_pm_opp_find_freq_exact(get_cpu_device(a7_cpu), apc0_fmin,
						true);

	/* One time information during boot. */
	pr_info("clock_cpu: a7: OPP voltage for %lu: %ld\n", apc0_fmin,
			dev_pm_opp_get_voltage(oppfmin));
	pr_info("clock_cpu: a7: OPP voltage for %lu: %ld\n", apc0_fmax,
			dev_pm_opp_get_voltage(oppfmax));

	rcu_read_unlock();
}

static int add_opp(struct clk *c, struct device *cpudev, struct device *vregdev,
			unsigned long max_rate)
{
	unsigned long rate = 0;
	int level;
	long ret, uv, corner;

	while (1) {
		ret = clk_round_rate(c, rate + 1);
		if (ret < 0) {
			pr_warn("clock-cpu: round_rate failed at %lu\n", rate);
			return ret;
		}

		rate = ret;

		level = find_vdd_level(c, rate);
		if (level <= 0) {
			pr_warn("clock-cpu: no uv for %lu.\n", rate);
			return -EINVAL;
		}

		uv = corner = c->vdd_class->vdd_uv[level];

		/*
		 * Populate both CPU and regulator devices with the
		 * freq-to-corner OPP table to maintain backward
		 * compatibility.
		 */
		ret = dev_pm_opp_add(cpudev, rate, corner);
		if (ret) {
			pr_warn("clock-cpu: couldn't add OPP for %lu\n",
					rate);
			return ret;
		}

		ret = dev_pm_opp_add(vregdev, rate, corner);
		if (ret) {
			pr_warn("clock-cpu: couldn't add OPP for %lu\n",
					rate);
			return ret;
		}

		if (rate >= max_rate)
			break;
	}

	return 0;
}

static void populate_opp_table(struct platform_device *pdev)
{
	struct platform_device *apc_dev;
	struct device_node *apc_node;
	unsigned long apc_fmax;
	int cpu, a7_cpu = 0;

	apc_node = of_parse_phandle(pdev->dev.of_node, "cpu-vdd-supply", 0);
	if (!apc_node) {
		pr_err("can't find the apc0 dt node.\n");
		return;
	}

	apc_dev = of_find_device_by_node(apc_node);
	if (!apc_dev) {
		pr_err("can't find the apc0 device node.\n");
		return;
	}

	apc_fmax = a7ssmux.c.fmax[a7ssmux.c.num_fmax - 1];

	for_each_possible_cpu(cpu) {
		a7_cpu = cpu;
		if (!get_cpu_device(cpu)) {
			pr_err("can't find cpu device for attaching OPPs\n");
			return;
		}

		WARN(add_opp(&a7ssmux.c, get_cpu_device(cpu),
					&apc_dev->dev, apc_fmax),
				"Failed to add OPP levels for A7\n");
	}

	/* One time print during bootup */
	pr_info("clock-a7: OPP tables populated (cpu %d)\n", a7_cpu);

	print_opp_table(a7_cpu);
}

static int of_get_fmax_vdd_class(struct platform_device *pdev, struct clk *c,
								char *prop_name)
{
	struct device_node *of = pdev->dev.of_node;
	int prop_len, i;
	struct clk_vdd_class *vdd = c->vdd_class;
	u32 *array;

	if (!of_find_property(of, 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;
	vdd->level_votes = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
					GFP_KERNEL);
	if (!vdd->level_votes)
		return -ENOMEM;

	vdd->vdd_uv = devm_kzalloc(&pdev->dev, prop_len * sizeof(int),
					GFP_KERNEL);
	if (!vdd->vdd_uv)
		return -ENOMEM;

	c->fmax = devm_kzalloc(&pdev->dev, prop_len * sizeof(unsigned long),
					GFP_KERNEL);
	if (!c->fmax)
		return -ENOMEM;

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

	of_property_read_u32_array(of, prop_name, array, prop_len * 2);
	for (i = 0; i < prop_len; i++) {
		c->fmax[i] = array[2 * i];
		vdd->vdd_uv[i] = array[2 * i + 1];
	}

	devm_kfree(&pdev->dev, array);
	vdd->num_levels = prop_len;
	vdd->cur_level = prop_len;
	vdd->use_max_uV = true;
	c->num_fmax = prop_len;
	return 0;
}

static void get_speed_bin(struct platform_device *pdev, int *bin, int *version)
{
	struct resource *res;
	void __iomem *base;
	u32 pte_efuse, redundant_sel, valid;

	*bin = 0;
	*version = 0;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
	if (!res) {
		dev_info(&pdev->dev,
			 "No speed/PVS binning available. Defaulting to 0!\n");
		return;
	}

	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (!base) {
		dev_warn(&pdev->dev,
			 "Unable to read efuse data. Defaulting to 0!\n");
		return;
	}

	pte_efuse = readl_relaxed(base);
	devm_iounmap(&pdev->dev, base);

	redundant_sel = (pte_efuse >> 24) & 0x7;
	*bin = pte_efuse & 0x7;
	valid = (pte_efuse >> 3) & 0x1;
	*version = (pte_efuse >> 4) & 0x3;

	if (redundant_sel == 1)
		*bin = (pte_efuse >> 27) & 0x7;

	if (!valid) {
		dev_info(&pdev->dev, "Speed bin not set. Defaulting to 0!\n");
		*bin = 0;
	} else {
		dev_info(&pdev->dev, "Speed bin: %d\n", *bin);
	}

	dev_info(&pdev->dev, "PVS version: %d\n", *version);

}

static void get_speed_bin_b(struct platform_device *pdev, int *bin,
								int *version)
{
	struct resource *res;
	void __iomem *base;
	u32 pte_efuse, shift = 2, mask = 0x7;

	*bin = 0;
	*version = 0;

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse1");
	if (res) {
		base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
		if (base) {
			pte_efuse = readl_relaxed(base);
			devm_iounmap(&pdev->dev, base);

			*version = (pte_efuse >> 18) & 0x3;
			if (!(*version)) {
				*bin = (pte_efuse >> 23) & 0x3;
				if (*bin) {
					dev_info(&pdev->dev, "Speed bin: %d PVS Version: %d\n",
						*bin, *version);
					return;
				}
			}
		} else {
			dev_warn(&pdev->dev,
				"Unable to read efuse1 data. Defaulting to 0!\n");
			return;
		}
	}

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "efuse");
	if (!res) {
		dev_info(&pdev->dev,
				"No speed/PVS binning available. Defaulting to 0!\n");
		return;
	}
	base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (!base) {
		dev_warn(&pdev->dev,
			 "Unable to read efuse data. Defaulting to 0!\n");
		return;
	}

	pte_efuse = readl_relaxed(base);
	devm_iounmap(&pdev->dev, base);

	*bin = (pte_efuse >> shift) & mask;

	dev_info(&pdev->dev, "Speed bin: %d PVS Version: %d\n", *bin,
								*version);
}

static int of_get_clk_src(struct platform_device *pdev, struct clk_src *parents)
{
	struct device_node *of = pdev->dev.of_node;
	int num_parents, i, j, index;
	struct clk *c;
	char clk_name[] = "clk-x";

	num_parents = of_property_count_strings(of, "clock-names");
	if (num_parents <= 0 || num_parents > 8) {
		dev_err(&pdev->dev, "missing clock-names\n");
		return -EINVAL;
	}

	j = 0;
	for (i = 0; i < 8; i++) {
		snprintf(clk_name, ARRAY_SIZE(clk_name), "clk-%d", i);
		index = of_property_match_string(of, "clock-names", clk_name);
		if (IS_ERR_VALUE(index))
			continue;

		parents[j].sel = i;
		parents[j].src = c = devm_clk_get(&pdev->dev, clk_name);
		if (IS_ERR(c)) {
			if (c != ERR_PTR(-EPROBE_DEFER))
				dev_err(&pdev->dev, "clk_get: %s\n fail",
						clk_name);
			return PTR_ERR(c);
		}
		j++;
	}

	return num_parents;
}

static struct platform_device *cpu_clock_a7_dev;

static int clock_a7_probe(struct platform_device *pdev)
{
	struct resource *res;
	int speed_bin = 0, version = 0, rc, cpu;
	unsigned long rate, aux_rate;
	struct clk *aux_clk, *main_pll;
	char prop_name[] = "qcom,speedX-bin-vX";
	const void *prop;
	bool compat_bin = false;
	bool compat_bin2 = false;
	bool opp_enable;

	compat_bin = of_device_is_compatible(pdev->dev.of_node,
						"qcom,clock-a53-8916");
	compat_bin2 = of_device_is_compatible(pdev->dev.of_node,
						"qcom,clock-a7-mdm9607");

	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "rcg-base");
	if (!res) {
		dev_err(&pdev->dev, "missing rcg-base\n");
		return -EINVAL;
	}
	a7ssmux.base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (!a7ssmux.base) {
		dev_err(&pdev->dev, "ioremap failed for rcg-base\n");
		return -ENOMEM;
	}

	vdd_cpu.regulator[0] = devm_regulator_get(&pdev->dev, "cpu-vdd");
	if (IS_ERR(vdd_cpu.regulator[0])) {
		if (PTR_ERR(vdd_cpu.regulator[0]) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "unable to get regulator\n");
		return PTR_ERR(vdd_cpu.regulator[0]);
	}

	rc = of_get_clk_src(pdev, a7ssmux.parents);
	if (IS_ERR_VALUE(rc))
		return rc;

	a7ssmux.num_parents = rc;

	/* Override the existing safe operating frequency */
	prop = of_get_property(pdev->dev.of_node, "qcom,safe-freq", NULL);
	if (prop)
		a7ssmux.safe_freq = of_read_ulong(prop, 1);

	if (compat_bin || compat_bin2)
		get_speed_bin_b(pdev, &speed_bin, &version);
	else
		get_speed_bin(pdev, &speed_bin, &version);

	snprintf(prop_name, ARRAY_SIZE(prop_name),
			"qcom,speed%d-bin-v%d", speed_bin, version);
	rc = of_get_fmax_vdd_class(pdev, &a7ssmux.c, prop_name);
	if (rc) {
		/* Fall back to most conservative PVS table */
		dev_err(&pdev->dev, "Unable to load voltage plan %s!\n",
								prop_name);
		rc = of_get_fmax_vdd_class(pdev, &a7ssmux.c,
						"qcom,speed0-bin-v0");
		if (rc) {
			dev_err(&pdev->dev,
					"Unable to load safe voltage plan\n");
			return rc;
		}
		dev_info(&pdev->dev, "Safe voltage plan loaded.\n");
	}

	rc = of_msm_clock_register(pdev->dev.of_node,
			clock_tbl_a7, ARRAY_SIZE(clock_tbl_a7));
	if (rc) {
		dev_err(&pdev->dev, "msm_clock_register failed\n");
		return rc;
	}

	/* Force a PLL reconfiguration */
	aux_clk = a7ssmux.parents[0].src;
	main_pll = a7ssmux.parents[1].src;

	aux_rate = clk_get_rate(aux_clk);
	rate = clk_get_rate(&a7ssmux.c);
	clk_set_rate(&a7ssmux.c, aux_rate);
	clk_set_rate(main_pll, clk_round_rate(main_pll, 1));
	clk_set_rate(&a7ssmux.c, rate);

	/*
	 * We don't want the CPU clocks to be turned off at late init
	 * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the
	 * refcount of these clocks. Any cpufreq/hotplug manager can assume
	 * that the clocks have already been prepared and enabled by the time
	 * they take over.
	 */
	get_online_cpus();
	for_each_online_cpu(cpu)
		WARN(clk_prepare_enable(&a7ssmux.c),
			"Unable to turn on CPU clock");
	put_online_cpus();

	opp_enable = of_property_read_bool(pdev->dev.of_node,
						"qcom,enable-opp");
	if (opp_enable)
		cpu_clock_a7_dev = pdev;

	return 0;
}

static const struct of_device_id clock_a7_match_table[] = {
	{.compatible = "qcom,clock-a53-8916"},
	{.compatible = "qcom,clock-a7-9650"},
	{.compatible = "qcom,clock-a7-mdm9607"},
	{.compatible = "qcom,clock-a7-sdx20"},
	{}
};

static struct platform_driver clock_a7_driver = {
	.probe = clock_a7_probe,
	.driver = {
		.name = "clock-a7",
		.of_match_table = clock_a7_match_table,
		.owner = THIS_MODULE,
	},
};

static int __init clock_a7_init(void)
{
	return platform_driver_register(&clock_a7_driver);
}
arch_initcall(clock_a7_init);

/* CPU devices are not currently available in arch_initcall */
static int __init cpu_clock_a7_init_opp(void)
{
	if (cpu_clock_a7_dev)
		populate_opp_table(cpu_clock_a7_dev);
	return 0;
}
module_init(cpu_clock_a7_init_opp);
+2908 −0

File added.

Preview size limit exceeded, changes collapsed.

+298 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2014, 2017-2018, 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.
 */

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/ctype.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <soc/qcom/clock-local2.h>
#include <soc/qcom/clock-rpm.h>
#include <soc/qcom/clock-voter.h>
#include <soc/qcom/rpm-smd.h>

#include <linux/clk/msm-clock-generic.h>

#include <dt-bindings/clock/msm-clocks-8909.h>

#include "clock.h"

#define GCC_DEBUG_CLK_CTL	0x74000
#define RPM_MISC_CLK_TYPE	0x306b6c63
#define RPM_BUS_CLK_TYPE	0x316b6c63
#define RPM_MEM_CLK_TYPE	0x326b6c63
#define RPM_SMD_KEY_ENABLE	0x62616E45
#define RPM_QPIC_CLK_TYPE	0x63697071

#define CXO_ID			0x0
#define QDSS_ID			0x1
#define BUS_SCALING		0x2

#define PCNOC_ID		0x0
#define SNOC_ID			0x1
#define BIMC_ID			0x0
#define QPIC_ID			0x0

/* XO clock */
#define BB_CLK1_ID		1
#define BB_CLK2_ID		2
#define BB_CLK3_ID		3
#define RF_CLK2_ID		5

static void __iomem *virt_base;

/* SMD clocks */
DEFINE_CLK_RPM_SMD(pcnoc_clk, pcnoc_a_clk, RPM_BUS_CLK_TYPE, PCNOC_ID, NULL);
DEFINE_CLK_RPM_SMD(snoc_clk, snoc_a_clk, RPM_BUS_CLK_TYPE, SNOC_ID, NULL);
DEFINE_CLK_RPM_SMD(bimc_clk, bimc_a_clk, RPM_MEM_CLK_TYPE, BIMC_ID, NULL);
DEFINE_CLK_RPM_SMD(qpic_clk, qpic_a_clk, RPM_QPIC_CLK_TYPE, QPIC_ID, NULL);

DEFINE_CLK_RPM_SMD_BRANCH(xo_clk_src, xo_a_clk_src,
				RPM_MISC_CLK_TYPE, CXO_ID, 19200000);

DEFINE_CLK_RPM_SMD_QDSS(qdss_clk, qdss_a_clk, RPM_MISC_CLK_TYPE, QDSS_ID);

/* SMD_XO_BUFFER */
DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk1, bb_clk1_a, BB_CLK1_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk2, bb_clk2_a, BB_CLK2_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER(bb_clk3, bb_clk3_a, BB_CLK3_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER(rf_clk2, rf_clk2_a, RF_CLK2_ID);

DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk1_pin, bb_clk1_a_pin, BB_CLK1_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk2_pin, bb_clk2_a_pin, BB_CLK2_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(bb_clk3_pin, bb_clk3_a_pin, BB_CLK3_ID);
DEFINE_CLK_RPM_SMD_XO_BUFFER_PINCTRL(rf_clk2_pin, rf_clk2_a_pin, RF_CLK2_ID);

/* Voter clocks */
static DEFINE_CLK_VOTER(pcnoc_msmbus_clk, &pcnoc_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_msmbus_clk,  &snoc_clk.c,  LONG_MAX);
static DEFINE_CLK_VOTER(bimc_msmbus_clk,  &bimc_clk.c,  LONG_MAX);
static DEFINE_CLK_VOTER(snoc_mm_msmbus_clk,  &snoc_clk.c,  LONG_MAX);

static DEFINE_CLK_VOTER(pcnoc_msmbus_a_clk, &pcnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_msmbus_a_clk,  &snoc_a_clk.c,  LONG_MAX);
static DEFINE_CLK_VOTER(bimc_msmbus_a_clk,  &bimc_a_clk.c,  LONG_MAX);
static DEFINE_CLK_VOTER(pcnoc_keepalive_a_clk, &pcnoc_a_clk.c, LONG_MAX);
static DEFINE_CLK_VOTER(snoc_mm_msmbus_a_clk,  &snoc_a_clk.c,  LONG_MAX);

static DEFINE_CLK_VOTER(pcnoc_usb_a_clk, &pcnoc_a_clk.c,  LONG_MAX);
static DEFINE_CLK_VOTER(snoc_usb_a_clk,  &snoc_a_clk.c,  LONG_MAX);
static DEFINE_CLK_VOTER(bimc_usb_a_clk,  &bimc_a_clk.c,  LONG_MAX);

/* Branch Voter clocks */
static DEFINE_CLK_BRANCH_VOTER(xo_gcc, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_otg_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_lpm_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_pil_pronto_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_pil_mss_clk, &xo_clk_src.c);
static DEFINE_CLK_BRANCH_VOTER(xo_wlan_clk, &xo_clk_src.c);

static struct mux_clk rpm_debug_mux = {
	.ops = &mux_reg_ops,
	.offset = GCC_DEBUG_CLK_CTL,
	.mask = 0x1FF,
	.en_offset = GCC_DEBUG_CLK_CTL,
	.en_mask = BIT(16),
	.base = &virt_base,
	MUX_SRC_LIST(
	{&snoc_clk.c,  0x0000},
	{&pcnoc_clk.c, 0x0008},
	/* BIMC_CLK is 2x clock to the BIMC Core as well as DDR, while the
	 * axi clock is for the BIMC AXI interface. The AXI clock is 1/2 of
	 * the BIMC Clock. measure the gcc_bimc_apss_axi_clk.
	 */
	{&bimc_clk.c,  0x0155},
	),
	.c = {
		.dbg_name = "rpm_debug_mux",
		.ops = &clk_ops_gen_mux,
		.flags = CLKFLAG_NO_RATE_CACHE,
		CLK_INIT(rpm_debug_mux.c),
	},
};

/* Lookup Table */
static struct clk_lookup msm_clocks_rpm[] = {
	CLK_LIST(xo_clk_src),
	CLK_LIST(xo_a_clk_src),
	CLK_LIST(xo_otg_clk),
	CLK_LIST(xo_lpm_clk),
	CLK_LIST(xo_pil_mss_clk),
	CLK_LIST(xo_pil_pronto_clk),
	CLK_LIST(xo_wlan_clk),

	CLK_LIST(snoc_msmbus_clk),
	CLK_LIST(snoc_msmbus_a_clk),
	CLK_LIST(snoc_mm_msmbus_clk),
	CLK_LIST(snoc_mm_msmbus_a_clk),
	CLK_LIST(pcnoc_msmbus_clk),
	CLK_LIST(pcnoc_msmbus_a_clk),
	CLK_LIST(bimc_msmbus_clk),
	CLK_LIST(bimc_msmbus_a_clk),
	CLK_LIST(pcnoc_keepalive_a_clk),

	CLK_LIST(pcnoc_usb_a_clk),
	CLK_LIST(snoc_usb_a_clk),
	CLK_LIST(bimc_usb_a_clk),

	/* CoreSight clocks */
	CLK_LIST(qdss_clk),
	CLK_LIST(qdss_a_clk),

	CLK_LIST(snoc_clk),
	CLK_LIST(pcnoc_clk),
	CLK_LIST(bimc_clk),
	CLK_LIST(snoc_a_clk),
	CLK_LIST(pcnoc_a_clk),
	CLK_LIST(bimc_a_clk),
	CLK_LIST(qpic_clk),
	CLK_LIST(qpic_a_clk),

	CLK_LIST(bb_clk1),
	CLK_LIST(bb_clk2),
	CLK_LIST(rf_clk2),

	CLK_LIST(bb_clk1_pin),
	CLK_LIST(bb_clk2_pin),
	CLK_LIST(rf_clk2_pin),

	/* RPM debug Mux*/
	CLK_LIST(rpm_debug_mux),
};

/* Lookup Table for MSM8909w-PM660 */
static struct clk_lookup msm_clocks_rpm_8909_pm660[] = {
	CLK_LIST(xo_clk_src),
	CLK_LIST(xo_a_clk_src),
	CLK_LIST(xo_otg_clk),
	CLK_LIST(xo_lpm_clk),
	CLK_LIST(xo_pil_mss_clk),
	CLK_LIST(xo_pil_pronto_clk),
	CLK_LIST(xo_wlan_clk),

	CLK_LIST(snoc_msmbus_clk),
	CLK_LIST(snoc_msmbus_a_clk),
	CLK_LIST(snoc_mm_msmbus_clk),
	CLK_LIST(snoc_mm_msmbus_a_clk),
	CLK_LIST(pcnoc_msmbus_clk),
	CLK_LIST(pcnoc_msmbus_a_clk),
	CLK_LIST(bimc_msmbus_clk),
	CLK_LIST(bimc_msmbus_a_clk),
	CLK_LIST(pcnoc_keepalive_a_clk),

	CLK_LIST(pcnoc_usb_a_clk),
	CLK_LIST(snoc_usb_a_clk),
	CLK_LIST(bimc_usb_a_clk),

	/* CoreSight clocks */
	CLK_LIST(qdss_clk),
	CLK_LIST(qdss_a_clk),

	CLK_LIST(snoc_clk),
	CLK_LIST(pcnoc_clk),
	CLK_LIST(bimc_clk),
	CLK_LIST(snoc_a_clk),
	CLK_LIST(pcnoc_a_clk),
	CLK_LIST(bimc_a_clk),
	CLK_LIST(qpic_clk),
	CLK_LIST(qpic_a_clk),

	CLK_LIST(bb_clk1),
	CLK_LIST(bb_clk2),
	CLK_LIST(bb_clk3),
	CLK_LIST(rf_clk2),

	CLK_LIST(bb_clk1_pin),
	CLK_LIST(bb_clk2_pin),
	CLK_LIST(bb_clk3_pin),
	CLK_LIST(rf_clk2_pin),

	/* RPM debug Mux*/
	CLK_LIST(rpm_debug_mux),
};

static int msm_rpmcc_8909_probe(struct platform_device *pdev)
{
	struct resource *res;
	int ret, is_8909_pm660 = 0;

	ret = enable_rpm_scaling();
	if (ret)
		return ret;

	is_8909_pm660 = of_device_is_compatible(pdev->dev.of_node,
						"qcom,rpmcc-8909-pm660");

	res =  platform_get_resource_byname(pdev, IORESOURCE_MEM, "cc_base");
	if (!res) {
		dev_err(&pdev->dev, "Unable to get register base\n");
		return -ENOMEM;
	}

	virt_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
	if (!virt_base) {
		dev_err(&pdev->dev, "Failed to map CC registers\n");
		return -ENOMEM;
	}

	if (is_8909_pm660)
		ret = of_msm_clock_register(pdev->dev.of_node,
				msm_clocks_rpm_8909_pm660,
				ARRAY_SIZE(msm_clocks_rpm_8909_pm660));
	else
		ret = of_msm_clock_register(pdev->dev.of_node, msm_clocks_rpm,
					ARRAY_SIZE(msm_clocks_rpm));

	if (ret) {
		dev_err(&pdev->dev, "Unable to register RPM clocks\n");
		return ret;
	}

	/*
	 *  Hold an active set vote for PCNOC AHB source. Sleep set vote is 0.
	 */
	clk_set_rate(&pcnoc_keepalive_a_clk.c, 19200000);
	clk_prepare_enable(&pcnoc_keepalive_a_clk.c);

	clk_prepare_enable(&xo_a_clk_src.c);

	dev_info(&pdev->dev, "Registered RPM clocks.\n");

	return 0;
}

static const struct of_device_id msm_clk_rpm_match_table[] = {
	{ .compatible = "qcom,rpmcc-8909" },
	{ .compatible = "qcom,rpmcc-8909-pm660" },
	{}
};

static struct platform_driver msm_clock_rpm_driver = {
	.probe = msm_rpmcc_8909_probe,
	.driver = {
		.name = "qcom,rpmcc-8909",
		.of_match_table = msm_clk_rpm_match_table,
		.owner = THIS_MODULE,
	},
};

static int __init msm_rpmcc_8909_init(void)
{
	return platform_driver_register(&msm_clock_rpm_driver);
}
arch_initcall(msm_rpmcc_8909_init);
+237 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading