cpufreq: ARM big LITTLE: Add generic cpufreq driver and its DT glue
big LITTLE is ARM's new Architecture focussing power/performance needs of modern world. More information about big LITTLE can be found here: http://www.arm.com/products/processors/technologies/biglittleprocessing.php http://lwn.net/Articles/481055/ In order to keep cpufreq support for all big LITTLE platforms simple/generic, this patch tries to add a generic cpufreq driver layer for all big LITTLE platforms. The driver is divided into two parts: - Core driver: Generic and shared across all big LITTLE SoC's - Glue drivers: Per platform drivers providing ops to the core driver This patch adds in a generic glue driver which would extract information from Device Tree. Future SoC's can either reuse the DT glue or write their own depending on the need. Signed-off-by: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
This commit is contained in:
parent
eb2f50ff93
commit
8a67f0ef2b
|
@ -0,0 +1,65 @@
|
||||||
|
Generic ARM big LITTLE cpufreq driver's DT glue
|
||||||
|
-----------------------------------------------
|
||||||
|
|
||||||
|
This is DT specific glue layer for generic cpufreq driver for big LITTLE
|
||||||
|
systems.
|
||||||
|
|
||||||
|
Both required and optional properties listed below must be defined
|
||||||
|
under node /cpus/cpu@x. Where x is the first cpu inside a cluster.
|
||||||
|
|
||||||
|
FIXME: Cpus should boot in the order specified in DT and all cpus for a cluster
|
||||||
|
must be present contiguously. Generic DT driver will check only node 'x' for
|
||||||
|
cpu:x.
|
||||||
|
|
||||||
|
Required properties:
|
||||||
|
- operating-points: Refer to Documentation/devicetree/bindings/power/opp.txt
|
||||||
|
for details
|
||||||
|
|
||||||
|
Optional properties:
|
||||||
|
- clock-latency: Specify the possible maximum transition latency for clock,
|
||||||
|
in unit of nanoseconds.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
|
||||||
|
cpus {
|
||||||
|
#address-cells = <1>;
|
||||||
|
#size-cells = <0>;
|
||||||
|
|
||||||
|
cpu@0 {
|
||||||
|
compatible = "arm,cortex-a15";
|
||||||
|
reg = <0>;
|
||||||
|
next-level-cache = <&L2>;
|
||||||
|
operating-points = <
|
||||||
|
/* kHz uV */
|
||||||
|
792000 1100000
|
||||||
|
396000 950000
|
||||||
|
198000 850000
|
||||||
|
>;
|
||||||
|
clock-latency = <61036>; /* two CLK32 periods */
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu@1 {
|
||||||
|
compatible = "arm,cortex-a15";
|
||||||
|
reg = <1>;
|
||||||
|
next-level-cache = <&L2>;
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu@100 {
|
||||||
|
compatible = "arm,cortex-a7";
|
||||||
|
reg = <100>;
|
||||||
|
next-level-cache = <&L2>;
|
||||||
|
operating-points = <
|
||||||
|
/* kHz uV */
|
||||||
|
792000 950000
|
||||||
|
396000 750000
|
||||||
|
198000 450000
|
||||||
|
>;
|
||||||
|
clock-latency = <61036>; /* two CLK32 periods */
|
||||||
|
};
|
||||||
|
|
||||||
|
cpu@101 {
|
||||||
|
compatible = "arm,cortex-a7";
|
||||||
|
reg = <101>;
|
||||||
|
next-level-cache = <&L2>;
|
||||||
|
};
|
||||||
|
};
|
11
MAINTAINERS
11
MAINTAINERS
|
@ -2206,6 +2206,17 @@ S: Maintained
|
||||||
F: drivers/cpufreq/
|
F: drivers/cpufreq/
|
||||||
F: include/linux/cpufreq.h
|
F: include/linux/cpufreq.h
|
||||||
|
|
||||||
|
CPU FREQUENCY DRIVERS - ARM BIG LITTLE
|
||||||
|
M: Viresh Kumar <viresh.kumar@linaro.org>
|
||||||
|
M: Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
|
||||||
|
L: cpufreq@vger.kernel.org
|
||||||
|
L: linux-pm@vger.kernel.org
|
||||||
|
W: http://www.arm.com/products/processors/technologies/biglittleprocessing.php
|
||||||
|
S: Maintained
|
||||||
|
F: drivers/cpufreq/arm_big_little.h
|
||||||
|
F: drivers/cpufreq/arm_big_little.c
|
||||||
|
F: drivers/cpufreq/arm_big_little_dt.c
|
||||||
|
|
||||||
CPUID/MSR DRIVER
|
CPUID/MSR DRIVER
|
||||||
M: "H. Peter Anvin" <hpa@zytor.com>
|
M: "H. Peter Anvin" <hpa@zytor.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
|
@ -2,6 +2,18 @@
|
||||||
# ARM CPU Frequency scaling drivers
|
# ARM CPU Frequency scaling drivers
|
||||||
#
|
#
|
||||||
|
|
||||||
|
config ARM_BIG_LITTLE_CPUFREQ
|
||||||
|
tristate
|
||||||
|
depends on ARM_CPU_TOPOLOGY
|
||||||
|
|
||||||
|
config ARM_DT_BL_CPUFREQ
|
||||||
|
tristate "Generic ARM big LITTLE CPUfreq driver probed via DT"
|
||||||
|
select ARM_BIG_LITTLE_CPUFREQ
|
||||||
|
depends on OF && HAVE_CLK
|
||||||
|
help
|
||||||
|
This enables the Generic CPUfreq driver for ARM big.LITTLE platform.
|
||||||
|
This gets frequency tables from DT.
|
||||||
|
|
||||||
config ARM_OMAP2PLUS_CPUFREQ
|
config ARM_OMAP2PLUS_CPUFREQ
|
||||||
bool "TI OMAP2+"
|
bool "TI OMAP2+"
|
||||||
depends on ARCH_OMAP2PLUS
|
depends on ARCH_OMAP2PLUS
|
||||||
|
|
|
@ -44,6 +44,11 @@ obj-$(CONFIG_X86_INTEL_PSTATE) += intel_pstate.o
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# ARM SoC drivers
|
# ARM SoC drivers
|
||||||
|
obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
|
||||||
|
# big LITTLE per platform glues. Keep DT_BL_CPUFREQ as the last entry in all big
|
||||||
|
# LITTLE drivers, so that it is probed last.
|
||||||
|
obj-$(CONFIG_ARM_DT_BL_CPUFREQ) += arm_big_little_dt.o
|
||||||
|
|
||||||
obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o
|
obj-$(CONFIG_UX500_SOC_DB8500) += dbx500-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
|
obj-$(CONFIG_ARM_S3C2416_CPUFREQ) += s3c2416-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
|
obj-$(CONFIG_ARM_S3C64XX_CPUFREQ) += s3c64xx-cpufreq.o
|
||||||
|
|
|
@ -0,0 +1,282 @@
|
||||||
|
/*
|
||||||
|
* ARM big.LITTLE Platforms CPUFreq support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 ARM Ltd.
|
||||||
|
* Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Linaro.
|
||||||
|
* Viresh Kumar <viresh.kumar@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/clk.h>
|
||||||
|
#include <linux/cpu.h>
|
||||||
|
#include <linux/cpufreq.h>
|
||||||
|
#include <linux/cpumask.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/of_platform.h>
|
||||||
|
#include <linux/opp.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/topology.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#include "arm_big_little.h"
|
||||||
|
|
||||||
|
/* Currently we support only two clusters */
|
||||||
|
#define MAX_CLUSTERS 2
|
||||||
|
|
||||||
|
static struct cpufreq_arm_bL_ops *arm_bL_ops;
|
||||||
|
static struct clk *clk[MAX_CLUSTERS];
|
||||||
|
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS];
|
||||||
|
static atomic_t cluster_usage[MAX_CLUSTERS] = {ATOMIC_INIT(0), ATOMIC_INIT(0)};
|
||||||
|
|
||||||
|
static int cpu_to_cluster(int cpu)
|
||||||
|
{
|
||||||
|
return topology_physical_package_id(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int bL_cpufreq_get(unsigned int cpu)
|
||||||
|
{
|
||||||
|
u32 cur_cluster = cpu_to_cluster(cpu);
|
||||||
|
|
||||||
|
return clk_get_rate(clk[cur_cluster]) / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Validate policy frequency range */
|
||||||
|
static int bL_cpufreq_verify_policy(struct cpufreq_policy *policy)
|
||||||
|
{
|
||||||
|
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||||
|
|
||||||
|
return cpufreq_frequency_table_verify(policy, freq_table[cur_cluster]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set clock frequency */
|
||||||
|
static int bL_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||||
|
unsigned int target_freq, unsigned int relation)
|
||||||
|
{
|
||||||
|
struct cpufreq_freqs freqs;
|
||||||
|
u32 cpu = policy->cpu, freq_tab_idx, cur_cluster;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
cur_cluster = cpu_to_cluster(policy->cpu);
|
||||||
|
|
||||||
|
freqs.old = bL_cpufreq_get(policy->cpu);
|
||||||
|
|
||||||
|
/* Determine valid target frequency using freq_table */
|
||||||
|
cpufreq_frequency_table_target(policy, freq_table[cur_cluster],
|
||||||
|
target_freq, relation, &freq_tab_idx);
|
||||||
|
freqs.new = freq_table[cur_cluster][freq_tab_idx].frequency;
|
||||||
|
|
||||||
|
freqs.cpu = policy->cpu;
|
||||||
|
|
||||||
|
pr_debug("%s: cpu: %d, cluster: %d, oldfreq: %d, target freq: %d, new freq: %d\n",
|
||||||
|
__func__, cpu, cur_cluster, freqs.old, target_freq,
|
||||||
|
freqs.new);
|
||||||
|
|
||||||
|
if (freqs.old == freqs.new)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for_each_cpu(freqs.cpu, policy->cpus)
|
||||||
|
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
|
||||||
|
|
||||||
|
ret = clk_set_rate(clk[cur_cluster], freqs.new * 1000);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("clk_set_rate failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
policy->cur = freqs.new;
|
||||||
|
|
||||||
|
for_each_cpu(freqs.cpu, policy->cpus)
|
||||||
|
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_cluster_clk_and_freq_table(struct device *cpu_dev)
|
||||||
|
{
|
||||||
|
u32 cluster = cpu_to_cluster(cpu_dev->id);
|
||||||
|
|
||||||
|
if (!atomic_dec_return(&cluster_usage[cluster])) {
|
||||||
|
clk_put(clk[cluster]);
|
||||||
|
opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
|
||||||
|
dev_dbg(cpu_dev, "%s: cluster: %d\n", __func__, cluster);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int get_cluster_clk_and_freq_table(struct device *cpu_dev)
|
||||||
|
{
|
||||||
|
u32 cluster = cpu_to_cluster(cpu_dev->id);
|
||||||
|
char name[14] = "cpu-cluster.";
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (atomic_inc_return(&cluster_usage[cluster]) != 1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ret = arm_bL_ops->init_opp_table(cpu_dev);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(cpu_dev, "%s: init_opp_table failed, cpu: %d, err: %d\n",
|
||||||
|
__func__, cpu_dev->id, ret);
|
||||||
|
goto atomic_dec;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = opp_init_cpufreq_table(cpu_dev, &freq_table[cluster]);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(cpu_dev, "%s: failed to init cpufreq table, cpu: %d, err: %d\n",
|
||||||
|
__func__, cpu_dev->id, ret);
|
||||||
|
goto atomic_dec;
|
||||||
|
}
|
||||||
|
|
||||||
|
name[12] = cluster + '0';
|
||||||
|
clk[cluster] = clk_get_sys(name, NULL);
|
||||||
|
if (!IS_ERR(clk[cluster])) {
|
||||||
|
dev_dbg(cpu_dev, "%s: clk: %p & freq table: %p, cluster: %d\n",
|
||||||
|
__func__, clk[cluster], freq_table[cluster],
|
||||||
|
cluster);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_err(cpu_dev, "%s: Failed to get clk for cpu: %d, cluster: %d\n",
|
||||||
|
__func__, cpu_dev->id, cluster);
|
||||||
|
ret = PTR_ERR(clk[cluster]);
|
||||||
|
opp_free_cpufreq_table(cpu_dev, &freq_table[cluster]);
|
||||||
|
|
||||||
|
atomic_dec:
|
||||||
|
atomic_dec(&cluster_usage[cluster]);
|
||||||
|
dev_err(cpu_dev, "%s: Failed to get data for cluster: %d\n", __func__,
|
||||||
|
cluster);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Per-CPU initialization */
|
||||||
|
static int bL_cpufreq_init(struct cpufreq_policy *policy)
|
||||||
|
{
|
||||||
|
u32 cur_cluster = cpu_to_cluster(policy->cpu);
|
||||||
|
struct device *cpu_dev;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
cpu_dev = get_cpu_device(policy->cpu);
|
||||||
|
if (!cpu_dev) {
|
||||||
|
pr_err("%s: failed to get cpu%d device\n", __func__,
|
||||||
|
policy->cpu);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = get_cluster_clk_and_freq_table(cpu_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
ret = cpufreq_frequency_table_cpuinfo(policy, freq_table[cur_cluster]);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(cpu_dev, "CPU %d, cluster: %d invalid freq table\n",
|
||||||
|
policy->cpu, cur_cluster);
|
||||||
|
put_cluster_clk_and_freq_table(cpu_dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpufreq_frequency_table_get_attr(freq_table[cur_cluster], policy->cpu);
|
||||||
|
|
||||||
|
if (arm_bL_ops->get_transition_latency)
|
||||||
|
policy->cpuinfo.transition_latency =
|
||||||
|
arm_bL_ops->get_transition_latency(cpu_dev);
|
||||||
|
else
|
||||||
|
policy->cpuinfo.transition_latency = CPUFREQ_ETERNAL;
|
||||||
|
|
||||||
|
policy->cur = bL_cpufreq_get(policy->cpu);
|
||||||
|
|
||||||
|
cpumask_copy(policy->cpus, topology_core_cpumask(policy->cpu));
|
||||||
|
|
||||||
|
dev_info(cpu_dev, "CPU %d initialized\n", policy->cpu);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bL_cpufreq_exit(struct cpufreq_policy *policy)
|
||||||
|
{
|
||||||
|
struct device *cpu_dev;
|
||||||
|
|
||||||
|
cpu_dev = get_cpu_device(policy->cpu);
|
||||||
|
if (!cpu_dev) {
|
||||||
|
pr_err("%s: failed to get cpu%d device\n", __func__,
|
||||||
|
policy->cpu);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
put_cluster_clk_and_freq_table(cpu_dev);
|
||||||
|
dev_dbg(cpu_dev, "%s: Exited, cpu: %d\n", __func__, policy->cpu);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Export freq_table to sysfs */
|
||||||
|
static struct freq_attr *bL_cpufreq_attr[] = {
|
||||||
|
&cpufreq_freq_attr_scaling_available_freqs,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cpufreq_driver bL_cpufreq_driver = {
|
||||||
|
.name = "arm-big-little",
|
||||||
|
.flags = CPUFREQ_STICKY,
|
||||||
|
.verify = bL_cpufreq_verify_policy,
|
||||||
|
.target = bL_cpufreq_set_target,
|
||||||
|
.get = bL_cpufreq_get,
|
||||||
|
.init = bL_cpufreq_init,
|
||||||
|
.exit = bL_cpufreq_exit,
|
||||||
|
.have_multiple_policies = true,
|
||||||
|
.attr = bL_cpufreq_attr,
|
||||||
|
};
|
||||||
|
|
||||||
|
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (arm_bL_ops) {
|
||||||
|
pr_debug("%s: Already registered: %s, exiting\n", __func__,
|
||||||
|
arm_bL_ops->name);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ops || !strlen(ops->name) || !ops->init_opp_table) {
|
||||||
|
pr_err("%s: Invalid arm_bL_ops, exiting\n", __func__);
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
arm_bL_ops = ops;
|
||||||
|
|
||||||
|
ret = cpufreq_register_driver(&bL_cpufreq_driver);
|
||||||
|
if (ret) {
|
||||||
|
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
|
||||||
|
__func__, ops->name, ret);
|
||||||
|
arm_bL_ops = NULL;
|
||||||
|
} else {
|
||||||
|
pr_info("%s: Registered platform driver: %s\n", __func__,
|
||||||
|
ops->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(bL_cpufreq_register);
|
||||||
|
|
||||||
|
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops)
|
||||||
|
{
|
||||||
|
if (arm_bL_ops != ops) {
|
||||||
|
pr_err("%s: Registered with: %s, can't unregister, exiting\n",
|
||||||
|
__func__, arm_bL_ops->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cpufreq_unregister_driver(&bL_cpufreq_driver);
|
||||||
|
pr_info("%s: Un-registered platform driver: %s\n", __func__,
|
||||||
|
arm_bL_ops->name);
|
||||||
|
arm_bL_ops = NULL;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(bL_cpufreq_unregister);
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* ARM big.LITTLE platform's CPUFreq header file
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 ARM Ltd.
|
||||||
|
* Sudeep KarkadaNagesha <sudeep.karkadanagesha@arm.com>
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Linaro.
|
||||||
|
* Viresh Kumar <viresh.kumar@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.
|
||||||
|
*/
|
||||||
|
#ifndef CPUFREQ_ARM_BIG_LITTLE_H
|
||||||
|
#define CPUFREQ_ARM_BIG_LITTLE_H
|
||||||
|
|
||||||
|
#include <linux/cpufreq.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
struct cpufreq_arm_bL_ops {
|
||||||
|
char name[CPUFREQ_NAME_LEN];
|
||||||
|
int (*get_transition_latency)(struct device *cpu_dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This must set opp table for cpu_dev in a similar way as done by
|
||||||
|
* of_init_opp_table().
|
||||||
|
*/
|
||||||
|
int (*init_opp_table)(struct device *cpu_dev);
|
||||||
|
};
|
||||||
|
|
||||||
|
int bL_cpufreq_register(struct cpufreq_arm_bL_ops *ops);
|
||||||
|
void bL_cpufreq_unregister(struct cpufreq_arm_bL_ops *ops);
|
||||||
|
|
||||||
|
#endif /* CPUFREQ_ARM_BIG_LITTLE_H */
|
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* Generic big.LITTLE CPUFreq Interface driver
|
||||||
|
*
|
||||||
|
* It provides necessary ops to arm_big_little cpufreq driver and gets
|
||||||
|
* Frequency information from Device Tree. Freq table in DT must be in KHz.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 Linaro.
|
||||||
|
* Viresh Kumar <viresh.kumar@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/cpufreq.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
#include <linux/opp.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include "arm_big_little.h"
|
||||||
|
|
||||||
|
static int dt_init_opp_table(struct device *cpu_dev)
|
||||||
|
{
|
||||||
|
struct device_node *np = NULL;
|
||||||
|
int count = 0, ret;
|
||||||
|
|
||||||
|
for_each_child_of_node(of_find_node_by_path("/cpus"), np) {
|
||||||
|
if (count++ != cpu_dev->id)
|
||||||
|
continue;
|
||||||
|
if (!of_get_property(np, "operating-points", NULL))
|
||||||
|
return -ENODATA;
|
||||||
|
|
||||||
|
cpu_dev->of_node = np;
|
||||||
|
|
||||||
|
ret = of_init_opp_table(cpu_dev);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int dt_get_transition_latency(struct device *cpu_dev)
|
||||||
|
{
|
||||||
|
struct device_node *np = NULL;
|
||||||
|
u32 transition_latency = CPUFREQ_ETERNAL;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for_each_child_of_node(of_find_node_by_path("/cpus"), np) {
|
||||||
|
if (count++ != cpu_dev->id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
of_property_read_u32(np, "clock-latency", &transition_latency);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct cpufreq_arm_bL_ops dt_bL_ops = {
|
||||||
|
.name = "dt-bl",
|
||||||
|
.get_transition_latency = dt_get_transition_latency,
|
||||||
|
.init_opp_table = dt_init_opp_table,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int generic_bL_init(void)
|
||||||
|
{
|
||||||
|
return bL_cpufreq_register(&dt_bL_ops);
|
||||||
|
}
|
||||||
|
module_init(generic_bL_init);
|
||||||
|
|
||||||
|
static void generic_bL_exit(void)
|
||||||
|
{
|
||||||
|
return bL_cpufreq_unregister(&dt_bL_ops);
|
||||||
|
}
|
||||||
|
module_exit(generic_bL_exit);
|
||||||
|
|
||||||
|
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
|
||||||
|
MODULE_DESCRIPTION("Generic ARM big LITTLE cpufreq driver via DT");
|
||||||
|
MODULE_LICENSE("GPL");
|
Loading…
Reference in New Issue