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

Commit dcb879fb authored by Shaleen Agrawal's avatar Shaleen Agrawal Committed by Jonathan Avila
Browse files

cpufreq: Add snapshot of qcom-cpufreq driver



This is a snapshot of qcom-cpufreq as of msm-4.19 commit 9a1757a1cb90
("qcom-cpufreq: Add missing cpufreq registers when use common table").

Change-Id: I4458f50dff9e9d44ec56d0214a60bf26673706a6
Signed-off-by: default avatarShaleen Agrawal <shalagra@codeaurora.org>
parent a70116e1
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -338,3 +338,9 @@ config ARM_PXA2xx_CPUFREQ
	  This add the CPUFreq driver support for Intel PXA2xx SOCs.
	  This add the CPUFreq driver support for Intel PXA2xx SOCs.


	  If in doubt, say N.
	  If in doubt, say N.

config CPU_FREQ_MSM
	bool "MSM CPUFreq support"
	depends on CPU_FREQ
	help
	  This enables the CPUFreq driver for Qualcomm Technologies, Inc. CPUs.
+1 −0
Original line number Original line Diff line number Diff line
@@ -65,6 +65,7 @@ obj-$(CONFIG_ARM_IMX_CPUFREQ_DT) += imx-cpufreq-dt.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ)	+= kirkwood-cpufreq.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ)	+= kirkwood-cpufreq.o
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ)	+= mediatek-cpufreq.o
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ)	+= mediatek-cpufreq.o
obj-$(CONFIG_MACH_MVEBU_V7)		+= mvebu-cpufreq.o
obj-$(CONFIG_MACH_MVEBU_V7)		+= mvebu-cpufreq.o
obj-$(CONFIG_CPU_FREQ_MSM)              += qcom-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ)	+= omap-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)	+= pxa2xx-cpufreq.o
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ)	+= pxa2xx-cpufreq.o
obj-$(CONFIG_PXA3xx)			+= pxa3xx-cpufreq.o
obj-$(CONFIG_PXA3xx)			+= pxa3xx-cpufreq.o
+6 −10
Original line number Original line Diff line number Diff line
@@ -1130,7 +1130,8 @@ static int cpufreq_add_policy_cpu(struct cpufreq_policy *policy, unsigned int cp
	if (has_target()) {
	if (has_target()) {
		ret = cpufreq_start_governor(policy);
		ret = cpufreq_start_governor(policy);
		if (ret)
		if (ret)
			pr_err("%s: Failed to start governor\n", __func__);
			pr_err("%s: Failed to start governor for CPU%u, policy CPU%u\n",
			       __func__, cpu, policy->cpu);
	}
	}
	up_write(&policy->rwsem);
	up_write(&policy->rwsem);
	return ret;
	return ret;
@@ -2162,15 +2163,6 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy,
	pr_debug("target for CPU %u: %u kHz, relation %u, requested %u kHz\n",
	pr_debug("target for CPU %u: %u kHz, relation %u, requested %u kHz\n",
		 policy->cpu, target_freq, relation, old_target_freq);
		 policy->cpu, target_freq, relation, old_target_freq);


	/*
	 * This might look like a redundant call as we are checking it again
	 * after finding index. But it is left intentionally for cases where
	 * exactly same freq is called again and so we can save on few function
	 * calls.
	 */
	if (target_freq == policy->cur)
		return 0;

	/* Save last value to restore later on errors */
	/* Save last value to restore later on errors */
	policy->restore_freq = policy->cur;
	policy->restore_freq = policy->cur;


@@ -2430,6 +2422,10 @@ int cpufreq_set_policy(struct cpufreq_policy *policy,
	if (ret)
	if (ret)
		return ret;
		return ret;


	/* adjust if necessary - hardware incompatibility */
	blocking_notifier_call_chain(&cpufreq_policy_notifier_list,
			CPUFREQ_INCOMPATIBLE, new_policy);

	policy->min = new_policy->min;
	policy->min = new_policy->min;
	policy->max = new_policy->max;
	policy->max = new_policy->max;
	trace_cpu_frequency_limits(policy);
	trace_cpu_frequency_limits(policy);
+548 −0
Original line number Original line Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* drivers/cpufreq/qcom-cpufreq.c
 *
 * MSM architecture cpufreq driver
 *
 * Copyright (C) 2007 Google, Inc.
 * Copyright (c) 2007-2019, The Linux Foundation. All rights reserved.
 * Author: Mike A. Chan <mikechan@google.com>
 *
 */

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cpufreq.h>
#include <linux/cpu.h>
#include <linux/cpumask.h>
#include <linux/suspend.h>
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/cpu_cooling.h>
#include <trace/events/power.h>

static DEFINE_MUTEX(l2bw_lock);

static struct thermal_cooling_device *cdev[NR_CPUS];
static struct clk *cpu_clk[NR_CPUS];
static struct clk *l2_clk;
static DEFINE_PER_CPU(struct cpufreq_frequency_table *, freq_table);
static bool hotplug_ready;

struct cpufreq_suspend_t {
	struct mutex suspend_mutex;
	int device_suspended;
};

static DEFINE_PER_CPU(struct cpufreq_suspend_t, suspend_data);
static DEFINE_PER_CPU(int, cached_resolve_idx);
static DEFINE_PER_CPU(unsigned int, cached_resolve_freq);

static int set_cpu_freq(struct cpufreq_policy *policy, unsigned int new_freq,
			unsigned int index)
{
	int ret = 0;
	struct cpufreq_freqs freqs;
	unsigned long rate;

	freqs.old = policy->cur;
	freqs.new = new_freq;
	freqs.cpu = policy->cpu;

	trace_cpu_frequency_switch_start(freqs.old, freqs.new, policy->cpu);
	cpufreq_freq_transition_begin(policy, &freqs);

	rate = new_freq * 1000;
	rate = clk_round_rate(cpu_clk[policy->cpu], rate);
	ret = clk_set_rate(cpu_clk[policy->cpu], rate);
	cpufreq_freq_transition_end(policy, &freqs, ret);
	if (!ret) {
		arch_set_freq_scale(policy->related_cpus, new_freq,
				    policy->cpuinfo.max_freq);
		trace_cpu_frequency_switch_end(policy->cpu);
	}

	return ret;
}

static int msm_cpufreq_target(struct cpufreq_policy *policy,
				unsigned int target_freq,
				unsigned int relation)
{
	int ret = 0;
	int index;
	struct cpufreq_frequency_table *table;
	int first_cpu = cpumask_first(policy->related_cpus);

	mutex_lock(&per_cpu(suspend_data, policy->cpu).suspend_mutex);

	if (target_freq == policy->cur)
		goto done;

	if (per_cpu(suspend_data, policy->cpu).device_suspended) {
		pr_debug("cpufreq: cpu%d scheduling frequency change in suspend\n",
			 policy->cpu);
		ret = -EFAULT;
		goto done;
	}

	table = policy->freq_table;
	if (per_cpu(cached_resolve_freq, first_cpu) == target_freq)
		index = per_cpu(cached_resolve_idx, first_cpu);
	else
		index = cpufreq_frequency_table_target(policy, target_freq,
						       relation);

	pr_debug("CPU[%d] target %d relation %d (%d-%d) selected %d\n",
		policy->cpu, target_freq, relation,
		policy->min, policy->max, table[index].frequency);

	ret = set_cpu_freq(policy, table[index].frequency,
			   table[index].driver_data);
done:
	mutex_unlock(&per_cpu(suspend_data, policy->cpu).suspend_mutex);
	return ret;
}

static unsigned int msm_cpufreq_resolve_freq(struct cpufreq_policy *policy,
					     unsigned int target_freq)
{
	int index;
	int first_cpu = cpumask_first(policy->related_cpus);
	unsigned int freq;

	index = cpufreq_frequency_table_target(policy, target_freq,
					       CPUFREQ_RELATION_L);
	freq = policy->freq_table[index].frequency;

	per_cpu(cached_resolve_idx, first_cpu) = index;
	per_cpu(cached_resolve_freq, first_cpu) = freq;

	return freq;
}

static int msm_cpufreq_verify(struct cpufreq_policy *policy)
{
	cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
			policy->cpuinfo.max_freq);
	return 0;
}

static unsigned int msm_cpufreq_get_freq(unsigned int cpu)
{
	return clk_get_rate(cpu_clk[cpu]) / 1000;
}

static int msm_cpufreq_init(struct cpufreq_policy *policy)
{
	int cur_freq;
	int index;
	int ret = 0;
	struct cpufreq_frequency_table *table =
			per_cpu(freq_table, policy->cpu);
	int cpu;

	/*
	 * In some SoC, some cores are clocked by same source, and their
	 * frequencies can not be changed independently. Find all other
	 * CPUs that share same clock, and mark them as controlled by
	 * same policy.
	 */
	for_each_possible_cpu(cpu)
		if (cpu_clk[cpu] == cpu_clk[policy->cpu])
			cpumask_set_cpu(cpu, policy->cpus);

	policy->freq_table = table;
	ret = cpufreq_table_validate_and_sort(policy);
	if (ret) {
		pr_err("cpufreq: failed to get policy min/max\n");
		return ret;
	}

	cur_freq = clk_get_rate(cpu_clk[policy->cpu])/1000;

	index =  cpufreq_frequency_table_target(policy, cur_freq,
						CPUFREQ_RELATION_H);
	/*
	 * Call set_cpu_freq unconditionally so that when cpu is set to
	 * online, frequency limit will always be updated.
	 */
	ret = set_cpu_freq(policy, table[index].frequency,
			   table[index].driver_data);
	if (ret)
		return ret;
	pr_debug("cpufreq: cpu%d init at %d switching to %d\n",
			policy->cpu, cur_freq, table[index].frequency);
	policy->cur = table[index].frequency;
	policy->dvfs_possible_from_any_cpu = true;

	return 0;
}

static int qcom_cpufreq_dead_cpu(unsigned int cpu)
{
	/* Fail hotplug until this driver can get CPU clocks */
	if (!hotplug_ready)
		return -EINVAL;

	clk_unprepare(cpu_clk[cpu]);
	clk_unprepare(l2_clk);
	return 0;
}

static int qcom_cpufreq_up_cpu(unsigned int cpu)
{
	int rc;

	/* Fail hotplug until this driver can get CPU clocks */
	if (!hotplug_ready)
		return -EINVAL;

	rc = clk_prepare(l2_clk);
	if (rc < 0)
		return rc;
	rc = clk_prepare(cpu_clk[cpu]);
	if (rc < 0)
		clk_unprepare(l2_clk);
	return rc;
}

static int qcom_cpufreq_dying_cpu(unsigned int cpu)
{
	/* Fail hotplug until this driver can get CPU clocks */
	if (!hotplug_ready)
		return -EINVAL;

	clk_disable(cpu_clk[cpu]);
	clk_disable(l2_clk);
	return 0;
}

static int qcom_cpufreq_starting_cpu(unsigned int cpu)
{
	int rc;

	/* Fail hotplug until this driver can get CPU clocks */
	if (!hotplug_ready)
		return -EINVAL;

	rc = clk_enable(l2_clk);
	if (rc < 0)
		return rc;
	rc = clk_enable(cpu_clk[cpu]);
	if (rc < 0)
		clk_disable(l2_clk);
	return rc;
}

static int msm_cpufreq_suspend(void)
{
	int cpu;

	for_each_possible_cpu(cpu) {
		mutex_lock(&per_cpu(suspend_data, cpu).suspend_mutex);
		per_cpu(suspend_data, cpu).device_suspended = 1;
		mutex_unlock(&per_cpu(suspend_data, cpu).suspend_mutex);
	}

	return NOTIFY_DONE;
}

static int msm_cpufreq_resume(void)
{
	int cpu, ret;
	struct cpufreq_policy policy;

	for_each_possible_cpu(cpu) {
		per_cpu(suspend_data, cpu).device_suspended = 0;
	}

	/*
	 * Freq request might be rejected during suspend, resulting
	 * in policy->cur violating min/max constraint.
	 * Correct the frequency as soon as possible.
	 */
	get_online_cpus();
	for_each_online_cpu(cpu) {
		ret = cpufreq_get_policy(&policy, cpu);
		if (ret)
			continue;
		if (policy.cur <= policy.max && policy.cur >= policy.min)
			continue;
		cpufreq_update_policy(cpu);
	}
	put_online_cpus();

	return NOTIFY_DONE;
}

static int msm_cpufreq_pm_event(struct notifier_block *this,
				unsigned long event, void *ptr)
{
	switch (event) {
	case PM_POST_HIBERNATION:
	case PM_POST_SUSPEND:
		return msm_cpufreq_resume();
	case PM_HIBERNATION_PREPARE:
	case PM_SUSPEND_PREPARE:
		return msm_cpufreq_suspend();
	default:
		return NOTIFY_DONE;
	}
}

static struct notifier_block msm_cpufreq_pm_notifier = {
	.notifier_call = msm_cpufreq_pm_event,
};

static struct freq_attr *msm_freq_attr[] = {
	&cpufreq_freq_attr_scaling_available_freqs,
	NULL,
};

static void msm_cpufreq_ready(struct cpufreq_policy *policy)
{
	struct device_node *np;
	unsigned int cpu = policy->cpu;

	if (cdev[cpu])
		return;

	np = of_cpu_device_node_get(cpu);
	if (WARN_ON(!np))
		return;

	/*
	 * For now, just loading the cooling device;
	 * thermal DT code takes care of matching them.
	 */
	if (of_find_property(np, "#cooling-cells", NULL)) {
		cdev[cpu] = of_cpufreq_cooling_register(policy);
		if (IS_ERR(cdev[cpu])) {
			pr_err("running cpufreq for CPU%d without cooling dev: %ld\n",
			       cpu, PTR_ERR(cdev[cpu]));
			cdev[cpu] = NULL;
		}
	}

	of_node_put(np);
}

static struct cpufreq_driver msm_cpufreq_driver = {
	/* lps calculations are handled here. */
	.flags		= CPUFREQ_STICKY | CPUFREQ_CONST_LOOPS |
				CPUFREQ_NEED_INITIAL_FREQ_CHECK,
	.init		= msm_cpufreq_init,
	.verify		= msm_cpufreq_verify,
	.target		= msm_cpufreq_target,
	.resolve_freq	= msm_cpufreq_resolve_freq,
	.get		= msm_cpufreq_get_freq,
	.name		= "msm",
	.attr		= msm_freq_attr,
	.ready		= msm_cpufreq_ready,
};

static struct cpufreq_frequency_table *cpufreq_parse_dt(struct device *dev,
						char *tbl_name, int cpu)
{
	int ret, nf, i, j;
	u32 *data;
	struct cpufreq_frequency_table *ftbl;

	/* Parse list of usable CPU frequencies. */
	if (!of_find_property(dev->of_node, tbl_name, &nf))
		return ERR_PTR(-EINVAL);
	nf /= sizeof(*data);

	if (nf == 0)
		return ERR_PTR(-EINVAL);

	data = devm_kzalloc(dev, nf * sizeof(*data), GFP_KERNEL);
	if (!data)
		return ERR_PTR(-ENOMEM);

	ret = of_property_read_u32_array(dev->of_node, tbl_name, data, nf);
	if (ret)
		return ERR_PTR(ret);

	ftbl = devm_kzalloc(dev, (nf + 1) * sizeof(*ftbl), GFP_KERNEL);
	if (!ftbl)
		return ERR_PTR(-ENOMEM);

	j = 0;
	for (i = 0; i < nf; i++) {
		unsigned long f;

		f = clk_round_rate(cpu_clk[cpu], data[i] * 1000);
		if (IS_ERR_VALUE(f))
			break;
		f /= 1000;

		/*
		 * Don't repeat frequencies if they round up to the same clock
		 * frequency.
		 *
		 */
		if (j > 0 && f <= ftbl[j - 1].frequency)
			continue;

		ftbl[j].driver_data = j;
		ftbl[j].frequency = f;
		j++;
	}

	ftbl[j].driver_data = j;
	ftbl[j].frequency = CPUFREQ_TABLE_END;

	devm_kfree(dev, data);

	return ftbl;
}

static int msm_cpufreq_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	char clk_name[] = "cpu??_clk";
	char tbl_name[] = "qcom,cpufreq-table-??";
	struct clk *c;
	int cpu, ret;
	struct cpufreq_frequency_table *ftbl;

	l2_clk = devm_clk_get(dev, "l2_clk");
	if (IS_ERR(l2_clk))
		l2_clk = NULL;

	for_each_possible_cpu(cpu) {
		snprintf(clk_name, sizeof(clk_name), "cpu%d_clk", cpu);
		c = devm_clk_get(dev, clk_name);
		if (cpu == 0 && IS_ERR(c))
			return PTR_ERR(c);
		else if (IS_ERR(c))
			c = cpu_clk[cpu-1];
		cpu_clk[cpu] = c;
	}
	hotplug_ready = true;

	/* Use per-policy governor tunable for some targets */
	if (of_property_read_bool(dev->of_node, "qcom,governor-per-policy"))
		msm_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;

	/* Parse commong cpufreq table for all CPUs */
	ftbl = cpufreq_parse_dt(dev, "qcom,cpufreq-table", 0);
	if (!IS_ERR(ftbl)) {
		for_each_possible_cpu(cpu)
			per_cpu(freq_table, cpu) = ftbl;
		goto out_register;
	}

	/*
	 * No common table. Parse individual tables for each unique
	 * CPU clock.
	 */
	for_each_possible_cpu(cpu) {
		snprintf(tbl_name, sizeof(tbl_name),
			 "qcom,cpufreq-table-%d", cpu);
		ftbl = cpufreq_parse_dt(dev, tbl_name, cpu);

		/* CPU0 must contain freq table */
		if (cpu == 0 && IS_ERR(ftbl)) {
			dev_err(dev, "Failed to parse CPU0's freq table\n");
			return PTR_ERR(ftbl);
		}
		if (cpu == 0) {
			per_cpu(freq_table, cpu) = ftbl;
			continue;
		}

		if (cpu_clk[cpu] != cpu_clk[cpu - 1] && IS_ERR(ftbl)) {
			dev_err(dev, "Failed to parse CPU%d's freq table\n",
				cpu);
			return PTR_ERR(ftbl);
		}

		/* Use previous CPU's table if it shares same clock */
		if (cpu_clk[cpu] == cpu_clk[cpu - 1]) {
			if (!IS_ERR(ftbl)) {
				dev_warn(dev, "Conflicting tables for CPU%d\n",
					 cpu);
				devm_kfree(dev, ftbl);
			}
			ftbl = per_cpu(freq_table, cpu - 1);
		}
		per_cpu(freq_table, cpu) = ftbl;
	}

out_register:
	ret = register_pm_notifier(&msm_cpufreq_pm_notifier);
	if (ret)
		return ret;

	ret = cpufreq_register_driver(&msm_cpufreq_driver);
	if (ret)
		unregister_pm_notifier(&msm_cpufreq_pm_notifier);

	return ret;
}

static const struct of_device_id msm_cpufreq_match_table[] = {
	{ .compatible = "qcom,msm-cpufreq" },
	{}
};

static struct platform_driver msm_cpufreq_plat_driver = {
	.probe = msm_cpufreq_probe,
	.driver = {
		.name = "msm-cpufreq",
		.of_match_table = msm_cpufreq_match_table,
	},
};

static int __init msm_cpufreq_register(void)
{
	int cpu, rc;

	for_each_possible_cpu(cpu) {
		mutex_init(&(per_cpu(suspend_data, cpu).suspend_mutex));
		per_cpu(suspend_data, cpu).device_suspended = 0;
		per_cpu(cached_resolve_freq, cpu) = UINT_MAX;
	}

	rc = platform_driver_register(&msm_cpufreq_plat_driver);
	if (rc < 0) {
		/* Unblock hotplug if msm-cpufreq probe fails */
		cpuhp_remove_state_nocalls(CPUHP_QCOM_CPUFREQ_PREPARE);
		cpuhp_remove_state_nocalls(CPUHP_AP_QCOM_CPUFREQ_STARTING);
		for_each_possible_cpu(cpu)
			mutex_destroy(&(per_cpu(suspend_data, cpu).
					suspend_mutex));
		return rc;
	}

	return 0;
}

subsys_initcall(msm_cpufreq_register);

static int __init msm_cpufreq_early_register(void)
{
	int ret;

	ret = cpuhp_setup_state_nocalls(CPUHP_AP_QCOM_CPUFREQ_STARTING,
					"AP_QCOM_CPUFREQ_STARTING",
					qcom_cpufreq_starting_cpu,
					qcom_cpufreq_dying_cpu);
	if (ret)
		return ret;

	ret = cpuhp_setup_state_nocalls(CPUHP_QCOM_CPUFREQ_PREPARE,
					"QCOM_CPUFREQ_PREPARE",
					qcom_cpufreq_up_cpu,
					qcom_cpufreq_dead_cpu);
	if (!ret)
		return ret;
	cpuhp_remove_state_nocalls(CPUHP_AP_QCOM_CPUFREQ_STARTING);
	return ret;
}
core_initcall(msm_cpufreq_early_register);
+1 −0
Original line number Original line Diff line number Diff line
@@ -461,6 +461,7 @@ static inline void cpufreq_resume(void) {}
/* Policy Notifiers  */
/* Policy Notifiers  */
#define CPUFREQ_CREATE_POLICY		(0)
#define CPUFREQ_CREATE_POLICY		(0)
#define CPUFREQ_REMOVE_POLICY		(1)
#define CPUFREQ_REMOVE_POLICY		(1)
#define CPUFREQ_INCOMPATIBLE	        (2)


#ifdef CONFIG_CPU_FREQ
#ifdef CONFIG_CPU_FREQ
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
int cpufreq_register_notifier(struct notifier_block *nb, unsigned int list);
Loading