Merge back earlier cpufreq material for v4.1.

This commit is contained in:
Rafael J. Wysocki 2015-04-10 12:00:36 +02:00
commit 38fc7839ba
7 changed files with 207 additions and 74 deletions

View File

@ -293,5 +293,13 @@ config SH_CPU_FREQ
If unsure, say N.
endif
config QORIQ_CPUFREQ
tristate "CPU frequency scaling driver for Freescale QorIQ SoCs"
depends on OF && COMMON_CLK && (PPC_E500MC || ARM)
select CLK_QORIQ
help
This adds the CPUFreq driver support for Freescale QorIQ SoCs
which are capable of changing the CPU's frequency dynamically.
endif
endmenu

View File

@ -108,6 +108,15 @@ config ARM_HIGHBANK_CPUFREQ
If in doubt, say N.
config ARM_HISI_ACPU_CPUFREQ
tristate "Hisilicon ACPU CPUfreq driver"
depends on ARCH_HISI && CPUFREQ_DT
select PM_OPP
help
This enables the hisilicon ACPU CPUfreq driver.
If in doubt, say N.
config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6 cpufreq support"
depends on ARCH_MXC

View File

@ -23,15 +23,6 @@ config CPU_FREQ_MAPLE
This adds support for frequency switching on Maple 970FX
Evaluation Board and compatible boards (IBM JS2x blades).
config PPC_CORENET_CPUFREQ
tristate "CPU frequency scaling driver for Freescale E500MC SoCs"
depends on PPC_E500MC && OF && COMMON_CLK
select CLK_QORIQ
help
This adds the CPUFreq driver support for Freescale e500mc,
e5500 and e6500 series SoCs which are capable of changing
the CPU's frequency dynamically.
config CPU_FREQ_PMAC
bool "Support for Apple PowerBooks"
depends on ADB_PMU && PPC32

View File

@ -59,6 +59,7 @@ arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS4X12_CPUFREQ) += exynos4x12-cpufreq.o
arm-exynos-cpufreq-$(CONFIG_ARM_EXYNOS5250_CPUFREQ) += exynos5250-cpufreq.o
obj-$(CONFIG_ARM_EXYNOS5440_CPUFREQ) += exynos5440-cpufreq.o
obj-$(CONFIG_ARM_HIGHBANK_CPUFREQ) += highbank-cpufreq.o
obj-$(CONFIG_ARM_HISI_ACPU_CPUFREQ) += hisi-acpu-cpufreq.o
obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
obj-$(CONFIG_ARM_INTEGRATOR) += integrator-cpufreq.o
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
@ -85,7 +86,7 @@ obj-$(CONFIG_CPU_FREQ_CBE) += ppc-cbe-cpufreq.o
ppc-cbe-cpufreq-y += ppc_cbe_cpufreq_pervasive.o ppc_cbe_cpufreq.o
obj-$(CONFIG_CPU_FREQ_CBE_PMI) += ppc_cbe_cpufreq_pmi.o
obj-$(CONFIG_CPU_FREQ_MAPLE) += maple-cpufreq.o
obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o
obj-$(CONFIG_QORIQ_CPUFREQ) += qoriq-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o
obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o
obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o

View File

@ -0,0 +1,42 @@
/*
* Hisilicon Platforms Using ACPU CPUFreq Support
*
* Copyright (c) 2015 Hisilicon Limited.
* Copyright (c) 2015 Linaro Limited.
*
* Leo Yan <leo.yan@linaro.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed "as is" WITHOUT ANY WARRANTY of any
* kind, whether express or implied; 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) KBUILD_MODNAME ": " fmt
#include <linux/err.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
static int __init hisi_acpu_cpufreq_driver_init(void)
{
struct platform_device *pdev;
if (!of_machine_is_compatible("hisilicon,hi6220"))
return -ENODEV;
pdev = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);
return PTR_ERR_OR_ZERO(pdev);
}
module_init(hisi_acpu_cpufreq_driver_init);
MODULE_AUTHOR("Leo Yan <leo.yan@linaro.org>");
MODULE_DESCRIPTION("Hisilicon acpu cpufreq driver");
MODULE_LICENSE("GPL v2");

View File

@ -34,9 +34,13 @@
#include <asm/smp.h> /* Required for cpu_sibling_mask() in UP configs */
#define POWERNV_MAX_PSTATES 256
#define PMSR_PSAFE_ENABLE (1UL << 30)
#define PMSR_SPR_EM_DISABLE (1UL << 31)
#define PMSR_MAX(x) ((x >> 32) & 0xFF)
#define PMSR_LP(x) ((x >> 48) & 0xFF)
static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1];
static bool rebooting;
static bool rebooting, throttled;
/*
* Note: The set of pstates consists of contiguous integers, the
@ -294,6 +298,44 @@ static inline unsigned int get_nominal_index(void)
return powernv_pstate_info.max - powernv_pstate_info.nominal;
}
static void powernv_cpufreq_throttle_check(unsigned int cpu)
{
unsigned long pmsr;
int pmsr_pmax, pmsr_lp;
pmsr = get_pmspr(SPRN_PMSR);
/* Check for Pmax Capping */
pmsr_pmax = (s8)PMSR_MAX(pmsr);
if (pmsr_pmax != powernv_pstate_info.max) {
throttled = true;
pr_info("CPU %d Pmax is reduced to %d\n", cpu, pmsr_pmax);
pr_info("Max allowed Pstate is capped\n");
}
/*
* Check for Psafe by reading LocalPstate
* or check if Psafe_mode_active is set in PMSR.
*/
pmsr_lp = (s8)PMSR_LP(pmsr);
if ((pmsr_lp < powernv_pstate_info.min) ||
(pmsr & PMSR_PSAFE_ENABLE)) {
throttled = true;
pr_info("Pstate set to safe frequency\n");
}
/* Check if SPR_EM_DISABLE is set in PMSR */
if (pmsr & PMSR_SPR_EM_DISABLE) {
throttled = true;
pr_info("Frequency Control disabled from OS\n");
}
if (throttled) {
pr_info("PMSR = %16lx\n", pmsr);
pr_crit("CPU Frequency could be throttled\n");
}
}
/*
* powernv_cpufreq_target_index: Sets the frequency corresponding to
* the cpufreq table entry indexed by new_index on the cpus in the
@ -307,6 +349,9 @@ static int powernv_cpufreq_target_index(struct cpufreq_policy *policy,
if (unlikely(rebooting) && new_index != get_nominal_index())
return 0;
if (!throttled)
powernv_cpufreq_throttle_check(smp_processor_id());
freq_data.pstate_id = powernv_freqs[new_index].driver_data;
/*

View File

@ -1,7 +1,7 @@
/*
* Copyright 2013 Freescale Semiconductor, Inc.
*
* CPU Frequency Scaling driver for Freescale PowerPC corenet SoCs.
* CPU Frequency Scaling driver for Freescale QorIQ SoCs.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@ -20,12 +20,11 @@
#include <linux/of.h>
#include <linux/slab.h>
#include <linux/smp.h>
#include <sysdev/fsl_soc.h>
#include <asm/smp.h> /* for get_hard_smp_processor_id() in UP configs */
/**
* struct cpu_data - per CPU data struct
* struct cpu_data
* @parent: the parent node of cpu clock
* @table: frequency table
*/
@ -69,18 +68,79 @@ static const struct soc_data sdata[] = {
static u32 min_cpufreq;
static const u32 *fmask;
static DEFINE_PER_CPU(struct cpu_data *, cpu_data);
/* cpumask in a cluster */
static DEFINE_PER_CPU(cpumask_var_t, cpu_mask);
#ifndef CONFIG_SMP
static inline const struct cpumask *cpu_core_mask(int cpu)
#if defined(CONFIG_ARM)
static int get_cpu_physical_id(int cpu)
{
return cpumask_of(0);
return topology_core_id(cpu);
}
#else
static int get_cpu_physical_id(int cpu)
{
return get_hard_smp_processor_id(cpu);
}
#endif
static u32 get_bus_freq(void)
{
struct device_node *soc;
u32 sysfreq;
soc = of_find_node_by_type(NULL, "soc");
if (!soc)
return 0;
if (of_property_read_u32(soc, "bus-frequency", &sysfreq))
sysfreq = 0;
of_node_put(soc);
return sysfreq;
}
static struct device_node *cpu_to_clk_node(int cpu)
{
struct device_node *np, *clk_np;
if (!cpu_present(cpu))
return NULL;
np = of_get_cpu_node(cpu, NULL);
if (!np)
return NULL;
clk_np = of_parse_phandle(np, "clocks", 0);
if (!clk_np)
return NULL;
of_node_put(np);
return clk_np;
}
/* traverse cpu nodes to get cpu mask of sharing clock wire */
static void set_affected_cpus(struct cpufreq_policy *policy)
{
struct device_node *np, *clk_np;
struct cpumask *dstp = policy->cpus;
int i;
np = cpu_to_clk_node(policy->cpu);
if (!np)
return;
for_each_present_cpu(i) {
clk_np = cpu_to_clk_node(i);
if (!clk_np)
continue;
if (clk_np == np)
cpumask_set_cpu(i, dstp);
of_node_put(clk_np);
}
of_node_put(np);
}
/* reduce the duplicated frequencies in frequency table */
static void freq_table_redup(struct cpufreq_frequency_table *freq_table,
int count)
@ -107,6 +167,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
int i, j, ind;
unsigned int freq, max_freq;
struct cpufreq_frequency_table table;
for (i = 0; i < count - 1; i++) {
max_freq = freq_table[i].frequency;
ind = i;
@ -131,7 +192,7 @@ static void freq_table_sort(struct cpufreq_frequency_table *freq_table,
}
}
static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
static int qoriq_cpufreq_cpu_init(struct cpufreq_policy *policy)
{
struct device_node *np;
int i, count, ret;
@ -147,10 +208,8 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
return -ENODEV;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
pr_err("%s: no memory\n", __func__);
if (!data)
goto err_np;
}
policy->clk = of_clk_get(np, 0);
if (IS_ERR(policy->clk)) {
@ -172,7 +231,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
}
if (fmask)
mask = fmask[get_hard_smp_processor_id(cpu)];
mask = fmask[get_cpu_physical_id(cpu)];
else
mask = 0x0;
@ -203,13 +262,12 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
data->table = table;
/* update ->cpus if we have cluster, no harm if not */
cpumask_copy(policy->cpus, per_cpu(cpu_mask, cpu));
for_each_cpu(i, per_cpu(cpu_mask, cpu))
per_cpu(cpu_data, i) = data;
set_affected_cpus(policy);
policy->driver_data = data;
/* Minimum transition latency is 12 platform clocks */
u64temp = 12ULL * NSEC_PER_SEC;
do_div(u64temp, fsl_get_sys_freq());
do_div(u64temp, get_bus_freq());
policy->cpuinfo.transition_latency = u64temp + 1;
of_node_put(np);
@ -221,7 +279,7 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
err_node:
of_node_put(data->parent);
err_nomem2:
per_cpu(cpu_data, cpu) = NULL;
policy->driver_data = NULL;
kfree(data);
err_np:
of_node_put(np);
@ -229,43 +287,40 @@ static int corenet_cpufreq_cpu_init(struct cpufreq_policy *policy)
return -ENODEV;
}
static int __exit corenet_cpufreq_cpu_exit(struct cpufreq_policy *policy)
static int __exit qoriq_cpufreq_cpu_exit(struct cpufreq_policy *policy)
{
struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
unsigned int cpu;
struct cpu_data *data = policy->driver_data;
of_node_put(data->parent);
kfree(data->table);
kfree(data);
for_each_cpu(cpu, per_cpu(cpu_mask, policy->cpu))
per_cpu(cpu_data, cpu) = NULL;
policy->driver_data = NULL;
return 0;
}
static int corenet_cpufreq_target(struct cpufreq_policy *policy,
static int qoriq_cpufreq_target(struct cpufreq_policy *policy,
unsigned int index)
{
struct clk *parent;
struct cpu_data *data = per_cpu(cpu_data, policy->cpu);
struct cpu_data *data = policy->driver_data;
parent = of_clk_get(data->parent, data->table[index].driver_data);
return clk_set_parent(policy->clk, parent);
}
static struct cpufreq_driver ppc_corenet_cpufreq_driver = {
.name = "ppc_cpufreq",
static struct cpufreq_driver qoriq_cpufreq_driver = {
.name = "qoriq_cpufreq",
.flags = CPUFREQ_CONST_LOOPS,
.init = corenet_cpufreq_cpu_init,
.exit = __exit_p(corenet_cpufreq_cpu_exit),
.init = qoriq_cpufreq_cpu_init,
.exit = __exit_p(qoriq_cpufreq_cpu_exit),
.verify = cpufreq_generic_frequency_table_verify,
.target_index = corenet_cpufreq_target,
.target_index = qoriq_cpufreq_target,
.get = cpufreq_generic_get,
.attr = cpufreq_generic_attr,
};
static const struct of_device_id node_matches[] __initdata = {
static const struct of_device_id node_matches[] __initconst = {
{ .compatible = "fsl,p2041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p3041-clockgen", .data = &sdata[0], },
{ .compatible = "fsl,p5020-clockgen", .data = &sdata[1], },
@ -275,61 +330,43 @@ static const struct of_device_id node_matches[] __initdata = {
{}
};
static int __init ppc_corenet_cpufreq_init(void)
static int __init qoriq_cpufreq_init(void)
{
int ret;
struct device_node *np;
const struct of_device_id *match;
const struct soc_data *data;
unsigned int cpu;
np = of_find_matching_node(NULL, node_matches);
if (!np)
return -ENODEV;
for_each_possible_cpu(cpu) {
if (!alloc_cpumask_var(&per_cpu(cpu_mask, cpu), GFP_KERNEL))
goto err_mask;
cpumask_copy(per_cpu(cpu_mask, cpu), cpu_core_mask(cpu));
}
match = of_match_node(node_matches, np);
data = match->data;
if (data) {
if (data->flag)
fmask = data->freq_mask;
min_cpufreq = fsl_get_sys_freq();
min_cpufreq = get_bus_freq();
} else {
min_cpufreq = fsl_get_sys_freq() / 2;
min_cpufreq = get_bus_freq() / 2;
}
of_node_put(np);
ret = cpufreq_register_driver(&ppc_corenet_cpufreq_driver);
ret = cpufreq_register_driver(&qoriq_cpufreq_driver);
if (!ret)
pr_info("Freescale PowerPC corenet CPU frequency scaling driver\n");
pr_info("Freescale QorIQ CPU frequency scaling driver\n");
return ret;
err_mask:
for_each_possible_cpu(cpu)
free_cpumask_var(per_cpu(cpu_mask, cpu));
return -ENOMEM;
}
module_init(ppc_corenet_cpufreq_init);
module_init(qoriq_cpufreq_init);
static void __exit ppc_corenet_cpufreq_exit(void)
static void __exit qoriq_cpufreq_exit(void)
{
unsigned int cpu;
for_each_possible_cpu(cpu)
free_cpumask_var(per_cpu(cpu_mask, cpu));
cpufreq_unregister_driver(&ppc_corenet_cpufreq_driver);
cpufreq_unregister_driver(&qoriq_cpufreq_driver);
}
module_exit(ppc_corenet_cpufreq_exit);
module_exit(qoriq_cpufreq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tang Yuantian <Yuantian.Tang@freescale.com>");
MODULE_DESCRIPTION("cpufreq driver for Freescale e500mc series SoCs");
MODULE_DESCRIPTION("cpufreq driver for Freescale QorIQ series SoCs");