Merge branch 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm into pm-cpufreq
Pull ARM cpufreq driver updates for v5.1 from Viresh Kumar: "This pull request contains following changes: - New Armada 8k cpufreq driver (Gregory CLEMENT). - qcom driver cleanups (Amit Kucheria, Taniya Das, Yangtao Li). - s5pv210 driver cleanup (Paweł Chmiel). - tegra driver cleanup (Yangtao Li). - Minor update to MAINTAINERS file (Baruch Siach)." * 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: cpufreq: qcom-hw: Register an Energy Model cpufreq: qcom: Read voltage LUT and populate OPP cpufreq: qcom-hw: Move to device_initcall cpufreq: tegra124: add missing of_node_put() cpufreq: qcom-kryo: make some variables static MAINTAINERS: Update the active pm tree for ARM cpufreq: ap806: add cpufreq driver for Armada 8K MAINTAINERS: add new entries for Armada 8K cpufreq driver cpufreq: s5pv210: Defer probe if getting regulators fail MAINTAINERS: use common indentation PM / OPP: Introduce a power estimation helper PM / OPP: Remove unused parameter of _generic_set_opp_clk_only()
This commit is contained in:
commit
b53c7348ca
13
MAINTAINERS
13
MAINTAINERS
|
@ -1737,6 +1737,7 @@ F: arch/arm/configs/mvebu_*_defconfig
|
|||
F: arch/arm/mach-mvebu/
|
||||
F: arch/arm64/boot/dts/marvell/armada*
|
||||
F: drivers/cpufreq/armada-37xx-cpufreq.c
|
||||
F: drivers/cpufreq/armada-8k-cpufreq.c
|
||||
F: drivers/cpufreq/mvebu-cpufreq.c
|
||||
F: drivers/irqchip/irq-armada-370-xp.c
|
||||
F: drivers/irqchip/irq-mvebu-*
|
||||
|
@ -3957,7 +3958,7 @@ M: Viresh Kumar <viresh.kumar@linaro.org>
|
|||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm.git
|
||||
T: git git://git.linaro.org/people/vireshk/linux.git (For ARM Updates)
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm.git (For ARM Updates)
|
||||
B: https://bugzilla.kernel.org
|
||||
F: Documentation/admin-guide/pm/cpufreq.rst
|
||||
F: Documentation/admin-guide/pm/intel_pstate.rst
|
||||
|
@ -12596,11 +12597,11 @@ F: Documentation/media/v4l-drivers/qcom_camss.rst
|
|||
F: drivers/media/platform/qcom/camss/
|
||||
|
||||
QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096
|
||||
M: Ilia Lin <ilia.lin@gmail.com>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
|
||||
F: drivers/cpufreq/qcom-cpufreq-kryo.c
|
||||
M: Ilia Lin <ilia.lin@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/opp/kryo-cpufreq.txt
|
||||
F: drivers/cpufreq/qcom-cpufreq-kryo.c
|
||||
|
||||
QUALCOMM EMAC GIGABIT ETHERNET DRIVER
|
||||
M: Timur Tabi <timur@kernel.org>
|
||||
|
|
|
@ -25,6 +25,17 @@ config ARM_ARMADA_37XX_CPUFREQ
|
|||
This adds the CPUFreq driver support for Marvell Armada 37xx SoCs.
|
||||
The Armada 37xx PMU supports 4 frequency and VDD levels.
|
||||
|
||||
config ARM_ARMADA_8K_CPUFREQ
|
||||
tristate "Armada 8K CPUFreq driver"
|
||||
depends on ARCH_MVEBU && CPUFREQ_DT
|
||||
help
|
||||
This enables the CPUFreq driver support for Marvell
|
||||
Armada8k SOCs.
|
||||
Armada8K device has the AP806 which supports scaling
|
||||
to any full integer divider.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
# big LITTLE core layer and glue drivers
|
||||
config ARM_BIG_LITTLE_CPUFREQ
|
||||
tristate "Generic ARM big LITTLE CPUfreq driver"
|
||||
|
|
|
@ -50,6 +50,7 @@ obj-$(CONFIG_X86_SFI_CPUFREQ) += sfi-cpufreq.o
|
|||
obj-$(CONFIG_ARM_BIG_LITTLE_CPUFREQ) += arm_big_little.o
|
||||
|
||||
obj-$(CONFIG_ARM_ARMADA_37XX_CPUFREQ) += armada-37xx-cpufreq.o
|
||||
obj-$(CONFIG_ARM_ARMADA_8K_CPUFREQ) += armada-8k-cpufreq.o
|
||||
obj-$(CONFIG_ARM_BRCMSTB_AVS_CPUFREQ) += brcmstb-avs-cpufreq.o
|
||||
obj-$(CONFIG_ACPI_CPPC_CPUFREQ) += cppc_cpufreq.o
|
||||
obj-$(CONFIG_ARCH_DAVINCI) += davinci-cpufreq.o
|
||||
|
|
204
drivers/cpufreq/armada-8k-cpufreq.c
Normal file
204
drivers/cpufreq/armada-8k-cpufreq.c
Normal file
|
@ -0,0 +1,204 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* CPUFreq support for Armada 8K
|
||||
*
|
||||
* Copyright (C) 2018 Marvell
|
||||
*
|
||||
* Omri Itach <omrii@marvell.com>
|
||||
* Gregory Clement <gregory.clement@bootlin.com>
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#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>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
/*
|
||||
* Setup the opps list with the divider for the max frequency, that
|
||||
* will be filled at runtime.
|
||||
*/
|
||||
static const int opps_div[] __initconst = {1, 2, 3, 4};
|
||||
|
||||
static struct platform_device *armada_8k_pdev;
|
||||
|
||||
struct freq_table {
|
||||
struct device *cpu_dev;
|
||||
unsigned int freq[ARRAY_SIZE(opps_div)];
|
||||
};
|
||||
|
||||
/* If the CPUs share the same clock, then they are in the same cluster. */
|
||||
static void __init armada_8k_get_sharing_cpus(struct clk *cur_clk,
|
||||
struct cpumask *cpumask)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct device *cpu_dev;
|
||||
struct clk *clk;
|
||||
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (!cpu_dev) {
|
||||
pr_warn("Failed to get cpu%d device\n", cpu);
|
||||
continue;
|
||||
}
|
||||
|
||||
clk = clk_get(cpu_dev, 0);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_warn("Cannot get clock for CPU %d\n", cpu);
|
||||
} else {
|
||||
if (clk_is_match(clk, cur_clk))
|
||||
cpumask_set_cpu(cpu, cpumask);
|
||||
|
||||
clk_put(clk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int __init armada_8k_add_opp(struct clk *clk, struct device *cpu_dev,
|
||||
struct freq_table *freq_tables,
|
||||
int opps_index)
|
||||
{
|
||||
unsigned int cur_frequency;
|
||||
unsigned int freq;
|
||||
int i, ret;
|
||||
|
||||
/* Get nominal (current) CPU frequency. */
|
||||
cur_frequency = clk_get_rate(clk);
|
||||
if (!cur_frequency) {
|
||||
dev_err(cpu_dev, "Failed to get clock rate for this CPU\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
freq_tables[opps_index].cpu_dev = cpu_dev;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(opps_div); i++) {
|
||||
freq = cur_frequency / opps_div[i];
|
||||
|
||||
ret = dev_pm_opp_add(cpu_dev, freq, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
freq_tables[opps_index].freq[i] = freq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void armada_8k_cpufreq_free_table(struct freq_table *freq_tables)
|
||||
{
|
||||
int opps_index, nb_cpus = num_possible_cpus();
|
||||
|
||||
for (opps_index = 0 ; opps_index <= nb_cpus; opps_index++) {
|
||||
int i;
|
||||
|
||||
/* If cpu_dev is NULL then we reached the end of the array */
|
||||
if (!freq_tables[opps_index].cpu_dev)
|
||||
break;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(opps_div); i++) {
|
||||
/*
|
||||
* A 0Hz frequency is not valid, this meant
|
||||
* that it was not yet initialized so there is
|
||||
* no more opp to free
|
||||
*/
|
||||
if (freq_tables[opps_index].freq[i] == 0)
|
||||
break;
|
||||
|
||||
dev_pm_opp_remove(freq_tables[opps_index].cpu_dev,
|
||||
freq_tables[opps_index].freq[i]);
|
||||
}
|
||||
}
|
||||
|
||||
kfree(freq_tables);
|
||||
}
|
||||
|
||||
static int __init armada_8k_cpufreq_init(void)
|
||||
{
|
||||
int ret = 0, opps_index = 0, cpu, nb_cpus;
|
||||
struct freq_table *freq_tables;
|
||||
struct device_node *node;
|
||||
struct cpumask cpus;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "marvell,ap806-cpu-clock");
|
||||
if (!node || !of_device_is_available(node))
|
||||
return -ENODEV;
|
||||
|
||||
nb_cpus = num_possible_cpus();
|
||||
freq_tables = kcalloc(nb_cpus, sizeof(*freq_tables), GFP_KERNEL);
|
||||
cpumask_copy(&cpus, cpu_possible_mask);
|
||||
|
||||
/*
|
||||
* For each CPU, this loop registers the operating points
|
||||
* supported (which are the nominal CPU frequency and full integer
|
||||
* divisions of it).
|
||||
*/
|
||||
for_each_cpu(cpu, &cpus) {
|
||||
struct cpumask shared_cpus;
|
||||
struct device *cpu_dev;
|
||||
struct clk *clk;
|
||||
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
|
||||
if (!cpu_dev) {
|
||||
pr_err("Cannot get CPU %d\n", cpu);
|
||||
continue;
|
||||
}
|
||||
|
||||
clk = clk_get(cpu_dev, 0);
|
||||
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("Cannot get clock for CPU %d\n", cpu);
|
||||
ret = PTR_ERR(clk);
|
||||
goto remove_opp;
|
||||
}
|
||||
|
||||
ret = armada_8k_add_opp(clk, cpu_dev, freq_tables, opps_index);
|
||||
if (ret) {
|
||||
clk_put(clk);
|
||||
goto remove_opp;
|
||||
}
|
||||
|
||||
opps_index++;
|
||||
cpumask_clear(&shared_cpus);
|
||||
armada_8k_get_sharing_cpus(clk, &shared_cpus);
|
||||
dev_pm_opp_set_sharing_cpus(cpu_dev, &shared_cpus);
|
||||
cpumask_andnot(&cpus, &cpus, &shared_cpus);
|
||||
clk_put(clk);
|
||||
}
|
||||
|
||||
armada_8k_pdev = platform_device_register_simple("cpufreq-dt", -1,
|
||||
NULL, 0);
|
||||
ret = PTR_ERR_OR_ZERO(armada_8k_pdev);
|
||||
if (ret)
|
||||
goto remove_opp;
|
||||
|
||||
platform_set_drvdata(armada_8k_pdev, freq_tables);
|
||||
|
||||
return 0;
|
||||
|
||||
remove_opp:
|
||||
armada_8k_cpufreq_free_table(freq_tables);
|
||||
return ret;
|
||||
}
|
||||
module_init(armada_8k_cpufreq_init);
|
||||
|
||||
static void __exit armada_8k_cpufreq_exit(void)
|
||||
{
|
||||
struct freq_table *freq_tables = platform_get_drvdata(armada_8k_pdev);
|
||||
|
||||
platform_device_unregister(armada_8k_pdev);
|
||||
armada_8k_cpufreq_free_table(freq_tables);
|
||||
}
|
||||
module_exit(armada_8k_cpufreq_exit);
|
||||
|
||||
MODULE_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Armada 8K cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -10,18 +10,21 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define LUT_MAX_ENTRIES 40U
|
||||
#define LUT_SRC GENMASK(31, 30)
|
||||
#define LUT_L_VAL GENMASK(7, 0)
|
||||
#define LUT_CORE_COUNT GENMASK(18, 16)
|
||||
#define LUT_VOLT GENMASK(11, 0)
|
||||
#define LUT_ROW_SIZE 32
|
||||
#define CLK_HW_DIV 2
|
||||
|
||||
/* Register offsets */
|
||||
#define REG_ENABLE 0x0
|
||||
#define REG_LUT_TABLE 0x110
|
||||
#define REG_FREQ_LUT 0x110
|
||||
#define REG_VOLT_LUT 0x114
|
||||
#define REG_PERF_STATE 0x920
|
||||
|
||||
static unsigned long cpu_hw_rate, xo_rate;
|
||||
|
@ -70,11 +73,12 @@ static unsigned int qcom_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
|
|||
return policy->freq_table[index].frequency;
|
||||
}
|
||||
|
||||
static int qcom_cpufreq_hw_read_lut(struct device *dev,
|
||||
static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev,
|
||||
struct cpufreq_policy *policy,
|
||||
void __iomem *base)
|
||||
{
|
||||
u32 data, src, lval, i, core_count, prev_cc = 0, prev_freq = 0, freq;
|
||||
u32 volt;
|
||||
unsigned int max_cores = cpumask_weight(policy->cpus);
|
||||
struct cpufreq_frequency_table *table;
|
||||
|
||||
|
@ -83,23 +87,28 @@ static int qcom_cpufreq_hw_read_lut(struct device *dev,
|
|||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
|
||||
data = readl_relaxed(base + REG_LUT_TABLE + i * LUT_ROW_SIZE);
|
||||
data = readl_relaxed(base + REG_FREQ_LUT +
|
||||
i * LUT_ROW_SIZE);
|
||||
src = FIELD_GET(LUT_SRC, data);
|
||||
lval = FIELD_GET(LUT_L_VAL, data);
|
||||
core_count = FIELD_GET(LUT_CORE_COUNT, data);
|
||||
|
||||
data = readl_relaxed(base + REG_VOLT_LUT +
|
||||
i * LUT_ROW_SIZE);
|
||||
volt = FIELD_GET(LUT_VOLT, data) * 1000;
|
||||
|
||||
if (src)
|
||||
freq = xo_rate * lval / 1000;
|
||||
else
|
||||
freq = cpu_hw_rate / 1000;
|
||||
|
||||
/* Ignore boosts in the middle of the table */
|
||||
if (core_count != max_cores) {
|
||||
table[i].frequency = CPUFREQ_ENTRY_INVALID;
|
||||
} else {
|
||||
if (freq != prev_freq && core_count == max_cores) {
|
||||
table[i].frequency = freq;
|
||||
dev_dbg(dev, "index=%d freq=%d, core_count %d\n", i,
|
||||
dev_pm_opp_add(cpu_dev, freq * 1000, volt);
|
||||
dev_dbg(cpu_dev, "index=%d freq=%d, core_count %d\n", i,
|
||||
freq, core_count);
|
||||
} else {
|
||||
table[i].frequency = CPUFREQ_ENTRY_INVALID;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -116,6 +125,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *dev,
|
|||
if (prev_cc != max_cores) {
|
||||
prev->frequency = prev_freq;
|
||||
prev->flags = CPUFREQ_BOOST_FREQ;
|
||||
dev_pm_opp_add(cpu_dev, prev_freq * 1000, volt);
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -127,6 +137,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *dev,
|
|||
|
||||
table[i].frequency = CPUFREQ_TABLE_END;
|
||||
policy->freq_table = table;
|
||||
dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -159,10 +170,18 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
|||
struct device *dev = &global_pdev->dev;
|
||||
struct of_phandle_args args;
|
||||
struct device_node *cpu_np;
|
||||
struct device *cpu_dev;
|
||||
struct resource *res;
|
||||
void __iomem *base;
|
||||
int ret, index;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
cpu_np = of_cpu_device_node_get(policy->cpu);
|
||||
if (!cpu_np)
|
||||
return -EINVAL;
|
||||
|
@ -199,12 +218,21 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
|||
|
||||
policy->driver_data = base + REG_PERF_STATE;
|
||||
|
||||
ret = qcom_cpufreq_hw_read_lut(dev, policy, base);
|
||||
ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy, base);
|
||||
if (ret) {
|
||||
dev_err(dev, "Domain-%d failed to read LUT\n", index);
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = dev_pm_opp_get_opp_count(cpu_dev);
|
||||
if (ret <= 0) {
|
||||
dev_err(cpu_dev, "Failed to add OPPs\n");
|
||||
ret = -ENODEV;
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev_pm_opp_of_register_em(policy->cpus);
|
||||
|
||||
policy->fast_switch_possible = true;
|
||||
|
||||
return 0;
|
||||
|
@ -215,8 +243,10 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
|||
|
||||
static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct device *cpu_dev = get_cpu_device(policy->cpu);
|
||||
void __iomem *base = policy->driver_data - REG_PERF_STATE;
|
||||
|
||||
dev_pm_opp_remove_all_dynamic(cpu_dev);
|
||||
kfree(policy->freq_table);
|
||||
devm_iounmap(&global_pdev->dev, base);
|
||||
|
||||
|
@ -297,7 +327,7 @@ static int __init qcom_cpufreq_hw_init(void)
|
|||
{
|
||||
return platform_driver_register(&qcom_cpufreq_hw_driver);
|
||||
}
|
||||
subsys_initcall(qcom_cpufreq_hw_init);
|
||||
device_initcall(qcom_cpufreq_hw_init);
|
||||
|
||||
static void __exit qcom_cpufreq_hw_exit(void)
|
||||
{
|
||||
|
|
|
@ -42,7 +42,7 @@ enum _msm8996_version {
|
|||
NUM_OF_MSM8996_VERSIONS,
|
||||
};
|
||||
|
||||
struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;
|
||||
static struct platform_device *cpufreq_dt_pdev, *kryo_cpufreq_pdev;
|
||||
|
||||
static enum _msm8996_version qcom_cpufreq_kryo_get_msm_id(void)
|
||||
{
|
||||
|
|
|
@ -584,7 +584,7 @@ static struct notifier_block s5pv210_cpufreq_reboot_notifier = {
|
|||
static int s5pv210_cpufreq_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np;
|
||||
int id;
|
||||
int id, result = 0;
|
||||
|
||||
/*
|
||||
* HACK: This is a temporary workaround to get access to clock
|
||||
|
@ -594,18 +594,39 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev)
|
|||
* this whole driver as soon as S5PV210 gets migrated to use
|
||||
* cpufreq-dt driver.
|
||||
*/
|
||||
arm_regulator = regulator_get(NULL, "vddarm");
|
||||
if (IS_ERR(arm_regulator)) {
|
||||
if (PTR_ERR(arm_regulator) == -EPROBE_DEFER)
|
||||
pr_debug("vddarm regulator not ready, defer\n");
|
||||
else
|
||||
pr_err("failed to get regulator vddarm\n");
|
||||
return PTR_ERR(arm_regulator);
|
||||
}
|
||||
|
||||
int_regulator = regulator_get(NULL, "vddint");
|
||||
if (IS_ERR(int_regulator)) {
|
||||
if (PTR_ERR(int_regulator) == -EPROBE_DEFER)
|
||||
pr_debug("vddint regulator not ready, defer\n");
|
||||
else
|
||||
pr_err("failed to get regulator vddint\n");
|
||||
result = PTR_ERR(int_regulator);
|
||||
goto err_int_regulator;
|
||||
}
|
||||
|
||||
np = of_find_compatible_node(NULL, NULL, "samsung,s5pv210-clock");
|
||||
if (!np) {
|
||||
pr_err("%s: failed to find clock controller DT node\n",
|
||||
__func__);
|
||||
return -ENODEV;
|
||||
result = -ENODEV;
|
||||
goto err_clock;
|
||||
}
|
||||
|
||||
clk_base = of_iomap(np, 0);
|
||||
of_node_put(np);
|
||||
if (!clk_base) {
|
||||
pr_err("%s: failed to map clock registers\n", __func__);
|
||||
return -EFAULT;
|
||||
result = -EFAULT;
|
||||
goto err_clock;
|
||||
}
|
||||
|
||||
for_each_compatible_node(np, NULL, "samsung,s5pv210-dmc") {
|
||||
|
@ -614,7 +635,8 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev)
|
|||
pr_err("%s: failed to get alias of dmc node '%pOFn'\n",
|
||||
__func__, np);
|
||||
of_node_put(np);
|
||||
return id;
|
||||
result = id;
|
||||
goto err_clk_base;
|
||||
}
|
||||
|
||||
dmc_base[id] = of_iomap(np, 0);
|
||||
|
@ -622,33 +644,40 @@ static int s5pv210_cpufreq_probe(struct platform_device *pdev)
|
|||
pr_err("%s: failed to map dmc%d registers\n",
|
||||
__func__, id);
|
||||
of_node_put(np);
|
||||
return -EFAULT;
|
||||
result = -EFAULT;
|
||||
goto err_dmc;
|
||||
}
|
||||
}
|
||||
|
||||
for (id = 0; id < ARRAY_SIZE(dmc_base); ++id) {
|
||||
if (!dmc_base[id]) {
|
||||
pr_err("%s: failed to find dmc%d node\n", __func__, id);
|
||||
return -ENODEV;
|
||||
result = -ENODEV;
|
||||
goto err_dmc;
|
||||
}
|
||||
}
|
||||
|
||||
arm_regulator = regulator_get(NULL, "vddarm");
|
||||
if (IS_ERR(arm_regulator)) {
|
||||
pr_err("failed to get regulator vddarm\n");
|
||||
return PTR_ERR(arm_regulator);
|
||||
}
|
||||
|
||||
int_regulator = regulator_get(NULL, "vddint");
|
||||
if (IS_ERR(int_regulator)) {
|
||||
pr_err("failed to get regulator vddint\n");
|
||||
regulator_put(arm_regulator);
|
||||
return PTR_ERR(int_regulator);
|
||||
}
|
||||
|
||||
register_reboot_notifier(&s5pv210_cpufreq_reboot_notifier);
|
||||
|
||||
return cpufreq_register_driver(&s5pv210_driver);
|
||||
|
||||
err_dmc:
|
||||
for (id = 0; id < ARRAY_SIZE(dmc_base); ++id)
|
||||
if (dmc_base[id]) {
|
||||
iounmap(dmc_base[id]);
|
||||
dmc_base[id] = NULL;
|
||||
}
|
||||
|
||||
err_clk_base:
|
||||
iounmap(clk_base);
|
||||
|
||||
err_clock:
|
||||
regulator_put(int_regulator);
|
||||
|
||||
err_int_regulator:
|
||||
regulator_put(arm_regulator);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static struct platform_driver s5pv210_cpufreq_platdrv = {
|
||||
|
|
|
@ -134,6 +134,8 @@ static int tegra124_cpufreq_probe(struct platform_device *pdev)
|
|||
|
||||
platform_set_drvdata(pdev, priv);
|
||||
|
||||
of_node_put(np);
|
||||
|
||||
return 0;
|
||||
|
||||
out_switch_to_pllx:
|
||||
|
|
|
@ -533,9 +533,8 @@ static int _set_opp_voltage(struct device *dev, struct regulator *reg,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
_generic_set_opp_clk_only(struct device *dev, struct clk *clk,
|
||||
unsigned long old_freq, unsigned long freq)
|
||||
static inline int _generic_set_opp_clk_only(struct device *dev, struct clk *clk,
|
||||
unsigned long freq)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -572,7 +571,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
|
|||
}
|
||||
|
||||
/* Change frequency */
|
||||
ret = _generic_set_opp_clk_only(dev, opp_table->clk, old_freq, freq);
|
||||
ret = _generic_set_opp_clk_only(dev, opp_table->clk, freq);
|
||||
if (ret)
|
||||
goto restore_voltage;
|
||||
|
||||
|
@ -586,7 +585,7 @@ static int _generic_set_opp_regulator(const struct opp_table *opp_table,
|
|||
return 0;
|
||||
|
||||
restore_freq:
|
||||
if (_generic_set_opp_clk_only(dev, opp_table->clk, freq, old_freq))
|
||||
if (_generic_set_opp_clk_only(dev, opp_table->clk, old_freq))
|
||||
dev_err(dev, "%s: failed to restore old-freq (%lu Hz)\n",
|
||||
__func__, old_freq);
|
||||
restore_voltage:
|
||||
|
@ -759,7 +758,7 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
|||
opp->supplies);
|
||||
} else {
|
||||
/* Only frequency scaling */
|
||||
ret = _generic_set_opp_clk_only(dev, clk, old_freq, freq);
|
||||
ret = _generic_set_opp_clk_only(dev, clk, freq);
|
||||
}
|
||||
|
||||
/* Scaling down? Configure required OPPs after frequency */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <linux/pm_domain.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/energy_model.h>
|
||||
|
||||
#include "opp.h"
|
||||
|
||||
|
@ -1047,3 +1048,101 @@ struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
|
|||
return of_node_get(opp->np);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node);
|
||||
|
||||
/*
|
||||
* Callback function provided to the Energy Model framework upon registration.
|
||||
* This computes the power estimated by @CPU at @kHz if it is the frequency
|
||||
* of an existing OPP, or at the frequency of the first OPP above @kHz otherwise
|
||||
* (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled
|
||||
* frequency and @mW to the associated power. The power is estimated as
|
||||
* P = C * V^2 * f with C being the CPU's capacitance and V and f respectively
|
||||
* the voltage and frequency of the OPP.
|
||||
*
|
||||
* Returns -ENODEV if the CPU device cannot be found, -EINVAL if the power
|
||||
* calculation failed because of missing parameters, 0 otherwise.
|
||||
*/
|
||||
static int __maybe_unused _get_cpu_power(unsigned long *mW, unsigned long *kHz,
|
||||
int cpu)
|
||||
{
|
||||
struct device *cpu_dev;
|
||||
struct dev_pm_opp *opp;
|
||||
struct device_node *np;
|
||||
unsigned long mV, Hz;
|
||||
u32 cap;
|
||||
u64 tmp;
|
||||
int ret;
|
||||
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (!cpu_dev)
|
||||
return -ENODEV;
|
||||
|
||||
np = of_node_get(cpu_dev->of_node);
|
||||
if (!np)
|
||||
return -EINVAL;
|
||||
|
||||
ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap);
|
||||
of_node_put(np);
|
||||
if (ret)
|
||||
return -EINVAL;
|
||||
|
||||
Hz = *kHz * 1000;
|
||||
opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz);
|
||||
if (IS_ERR(opp))
|
||||
return -EINVAL;
|
||||
|
||||
mV = dev_pm_opp_get_voltage(opp) / 1000;
|
||||
dev_pm_opp_put(opp);
|
||||
if (!mV)
|
||||
return -EINVAL;
|
||||
|
||||
tmp = (u64)cap * mV * mV * (Hz / 1000000);
|
||||
do_div(tmp, 1000000000);
|
||||
|
||||
*mW = (unsigned long)tmp;
|
||||
*kHz = Hz / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_opp_of_register_em() - Attempt to register an Energy Model
|
||||
* @cpus : CPUs for which an Energy Model has to be registered
|
||||
*
|
||||
* This checks whether the "dynamic-power-coefficient" devicetree property has
|
||||
* been specified, and tries to register an Energy Model with it if it has.
|
||||
*/
|
||||
void dev_pm_opp_of_register_em(struct cpumask *cpus)
|
||||
{
|
||||
struct em_data_callback em_cb = EM_DATA_CB(_get_cpu_power);
|
||||
int ret, nr_opp, cpu = cpumask_first(cpus);
|
||||
struct device *cpu_dev;
|
||||
struct device_node *np;
|
||||
u32 cap;
|
||||
|
||||
cpu_dev = get_cpu_device(cpu);
|
||||
if (!cpu_dev)
|
||||
return;
|
||||
|
||||
nr_opp = dev_pm_opp_get_opp_count(cpu_dev);
|
||||
if (nr_opp <= 0)
|
||||
return;
|
||||
|
||||
np = of_node_get(cpu_dev->of_node);
|
||||
if (!np)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Register an EM only if the 'dynamic-power-coefficient' property is
|
||||
* set in devicetree. It is assumed the voltage values are known if that
|
||||
* property is set since it is useless otherwise. If voltages are not
|
||||
* known, just let the EM registration fail with an error to alert the
|
||||
* user about the inconsistent configuration.
|
||||
*/
|
||||
ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap);
|
||||
of_node_put(np);
|
||||
if (ret || !cap)
|
||||
return;
|
||||
|
||||
em_register_perf_domain(cpus, nr_opp, &em_cb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em);
|
||||
|
|
|
@ -327,6 +327,7 @@ int dev_pm_opp_of_get_sharing_cpus(struct device *cpu_dev, struct cpumask *cpuma
|
|||
struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev);
|
||||
struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp);
|
||||
int of_get_required_opp_performance_state(struct device_node *np, int index);
|
||||
void dev_pm_opp_of_register_em(struct cpumask *cpus);
|
||||
#else
|
||||
static inline int dev_pm_opp_of_add_table(struct device *dev)
|
||||
{
|
||||
|
@ -365,6 +366,11 @@ static inline struct device_node *dev_pm_opp_get_of_node(struct dev_pm_opp *opp)
|
|||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void dev_pm_opp_of_register_em(struct cpumask *cpus)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int of_get_required_opp_performance_state(struct device_node *np, int index)
|
||||
{
|
||||
return -ENOTSUPP;
|
||||
|
|
Loading…
Reference in New Issue
Block a user