linux/drivers/cpufreq/cpufreq-dt.c

395 lines
9.3 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Freescale Semiconductor, Inc.
*
* Copyright (C) 2014 Linaro.
* Viresh Kumar <viresh.kumar@linaro.org>
*/
#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/err.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_opp.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/thermal.h>
#include "cpufreq-dt.h"
struct private_data {
struct opp_table *opp_table;
struct device *cpu_dev;
const char *reg_name;
bool have_static_opps;
};
static struct freq_attr *cpufreq_dt_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL, /* Extra space for boost-attr if required */
NULL,
};
static int set_target(struct cpufreq_policy *policy, unsigned int index)
{
struct private_data *priv = policy->driver_data;
unsigned long freq = policy->freq_table[index].frequency;
int ret;
ret = dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
if (!ret) {
arch_set_freq_scale(policy->related_cpus, freq,
policy->cpuinfo.max_freq);
}
return ret;
}
/*
* An earlier version of opp-v1 bindings used to name the regulator
* "cpu0-supply", we still need to handle that for backwards compatibility.
*/
static const char *find_supply_name(struct device *dev)
{
struct device_node *np;
struct property *pp;
int cpu = dev->id;
const char *name = NULL;
np = of_node_get(dev->of_node);
/* This must be valid for sure */
if (WARN_ON(!np))
return NULL;
/* Try "cpu0" for older DTs */
if (!cpu) {
pp = of_find_property(np, "cpu0-supply", NULL);
if (pp) {
name = "cpu0";
goto node_put;
}
}
pp = of_find_property(np, "cpu-supply", NULL);
if (pp) {
name = "cpu";
goto node_put;
}
dev_dbg(dev, "no regulator for cpu%d\n", cpu);
node_put:
of_node_put(np);
return name;
}
static int resources_available(void)
{
struct device *cpu_dev;
struct regulator *cpu_reg;
struct clk *cpu_clk;
int ret = 0;
const char *name;
cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
pr_err("failed to get cpu0 device\n");
return -ENODEV;
}
cpu_clk = clk_get(cpu_dev, NULL);
ret = PTR_ERR_OR_ZERO(cpu_clk);
if (ret) {
cpufreq: cpufreq-cpu0: defer probe when regulator is not ready With commit 1e4b545, regulator_get will now return -EPROBE_DEFER when the cpu0-supply node is present, but the regulator is not yet registered. It is possible for this to occur when the regulator registration by itself might be defered due to some dependent interface not yet instantiated. For example: an regulator which uses I2C and GPIO might need both systems available before proceeding, in this case, the regulator might defer it's registration. However, the cpufreq-cpu0 driver assumes that any un-successful return result is equivalent of failure. When the regulator_get returns failure other than -EPROBE_DEFER, it makes sense to assume that supply node is not present and proceed with the assumption that only clock control is necessary in the platform. With this change, we can now handle the following conditions: a) cpu0-supply binding is not present, regulator_get will return appropriate error result, resulting in cpufreq-cpu0 driver controlling just the clock. b) cpu0-supply binding is present, regulator_get returns -EPROBE_DEFER, we retry resulting in cpufreq-cpu0 driver registering later once the regulator is available. c) cpu0-supply binding is present, regulator_get returns -EPROBE_DEFER, however, regulator never registers, we retry until cpufreq-cpu0 driver fails to register pointing at device tree information bug. However, in this case, the fact that cpufreq-cpu0 operates with clock only when the DT binding clearly indicates need of a supply is a bug of it's own. d) cpu0-supply gets an regulator at probe - cpufreq-cpu0 driver controls both the clock and regulator Signed-off-by: Nishanth Menon <nm@ti.com> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-05-01 13:38:12 +00:00
/*
* If cpu's clk node is present, but clock is not yet
* registered, we should try defering probe.
cpufreq: cpufreq-cpu0: defer probe when regulator is not ready With commit 1e4b545, regulator_get will now return -EPROBE_DEFER when the cpu0-supply node is present, but the regulator is not yet registered. It is possible for this to occur when the regulator registration by itself might be defered due to some dependent interface not yet instantiated. For example: an regulator which uses I2C and GPIO might need both systems available before proceeding, in this case, the regulator might defer it's registration. However, the cpufreq-cpu0 driver assumes that any un-successful return result is equivalent of failure. When the regulator_get returns failure other than -EPROBE_DEFER, it makes sense to assume that supply node is not present and proceed with the assumption that only clock control is necessary in the platform. With this change, we can now handle the following conditions: a) cpu0-supply binding is not present, regulator_get will return appropriate error result, resulting in cpufreq-cpu0 driver controlling just the clock. b) cpu0-supply binding is present, regulator_get returns -EPROBE_DEFER, we retry resulting in cpufreq-cpu0 driver registering later once the regulator is available. c) cpu0-supply binding is present, regulator_get returns -EPROBE_DEFER, however, regulator never registers, we retry until cpufreq-cpu0 driver fails to register pointing at device tree information bug. However, in this case, the fact that cpufreq-cpu0 operates with clock only when the DT binding clearly indicates need of a supply is a bug of it's own. d) cpu0-supply gets an regulator at probe - cpufreq-cpu0 driver controls both the clock and regulator Signed-off-by: Nishanth Menon <nm@ti.com> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-05-01 13:38:12 +00:00
*/
if (ret == -EPROBE_DEFER)
dev_dbg(cpu_dev, "clock not ready, retry\n");
else
dev_err(cpu_dev, "failed to get clock: %d\n", ret);
return ret;
cpufreq: cpufreq-cpu0: defer probe when regulator is not ready With commit 1e4b545, regulator_get will now return -EPROBE_DEFER when the cpu0-supply node is present, but the regulator is not yet registered. It is possible for this to occur when the regulator registration by itself might be defered due to some dependent interface not yet instantiated. For example: an regulator which uses I2C and GPIO might need both systems available before proceeding, in this case, the regulator might defer it's registration. However, the cpufreq-cpu0 driver assumes that any un-successful return result is equivalent of failure. When the regulator_get returns failure other than -EPROBE_DEFER, it makes sense to assume that supply node is not present and proceed with the assumption that only clock control is necessary in the platform. With this change, we can now handle the following conditions: a) cpu0-supply binding is not present, regulator_get will return appropriate error result, resulting in cpufreq-cpu0 driver controlling just the clock. b) cpu0-supply binding is present, regulator_get returns -EPROBE_DEFER, we retry resulting in cpufreq-cpu0 driver registering later once the regulator is available. c) cpu0-supply binding is present, regulator_get returns -EPROBE_DEFER, however, regulator never registers, we retry until cpufreq-cpu0 driver fails to register pointing at device tree information bug. However, in this case, the fact that cpufreq-cpu0 operates with clock only when the DT binding clearly indicates need of a supply is a bug of it's own. d) cpu0-supply gets an regulator at probe - cpufreq-cpu0 driver controls both the clock and regulator Signed-off-by: Nishanth Menon <nm@ti.com> Acked-by: Shawn Guo <shawn.guo@linaro.org> Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2013-05-01 13:38:12 +00:00
}
clk_put(cpu_clk);
name = find_supply_name(cpu_dev);
/* Platform doesn't require regulator */
if (!name)
return 0;
cpu_reg = regulator_get_optional(cpu_dev, name);
ret = PTR_ERR_OR_ZERO(cpu_reg);
if (ret) {
/*
* If cpu's regulator supply node is present, but regulator is
* not yet registered, we should try defering probe.
*/
if (ret == -EPROBE_DEFER)
dev_dbg(cpu_dev, "cpu0 regulator not ready, retry\n");
else
dev_dbg(cpu_dev, "no regulator for cpu0: %d\n", ret);
return ret;
}
regulator_put(cpu_reg);
return 0;
}
static int cpufreq_init(struct cpufreq_policy *policy)
{
struct cpufreq_frequency_table *freq_table;
struct opp_table *opp_table = NULL;
struct private_data *priv;
struct device *cpu_dev;
struct clk *cpu_clk;
unsigned int transition_latency;
bool fallback = false;
const char *name;
int ret;
cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) {
pr_err("failed to get cpu%d device\n", policy->cpu);
return -ENODEV;
}
cpu_clk = clk_get(cpu_dev, NULL);
if (IS_ERR(cpu_clk)) {
ret = PTR_ERR(cpu_clk);
dev_err(cpu_dev, "%s: failed to get clk: %d\n", __func__, ret);
return ret;
}
/* Get OPP-sharing information from "operating-points-v2" bindings */
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, policy->cpus);
if (ret) {
if (ret != -ENOENT)
goto out_put_clk;
/*
* operating-points-v2 not supported, fallback to old method of
* finding shared-OPPs for backward compatibility if the
* platform hasn't set sharing CPUs.
*/
if (dev_pm_opp_get_sharing_cpus(cpu_dev, policy->cpus))
fallback = true;
}
/*
* OPP layer will be taking care of regulators now, but it needs to know
* the name of the regulator first.
*/
name = find_supply_name(cpu_dev);
if (name) {
opp_table = dev_pm_opp_set_regulators(cpu_dev, &name, 1);
if (IS_ERR(opp_table)) {
ret = PTR_ERR(opp_table);
dev_err(cpu_dev, "Failed to set regulator for cpu%d: %d\n",
policy->cpu, ret);
goto out_put_clk;
}
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
ret = -ENOMEM;
goto out_put_regulator;
}
priv->reg_name = name;
priv->opp_table = opp_table;
/*
* Initialize OPP tables for all policy->cpus. They will be shared by
* all CPUs which have marked their CPUs shared with OPP bindings.
*
* For platforms not using operating-points-v2 bindings, we do this
* before updating policy->cpus. Otherwise, we will end up creating
* duplicate OPPs for policy->cpus.
*
* OPPs might be populated at runtime, don't check for error here
*/
if (!dev_pm_opp_of_cpumask_add_table(policy->cpus))
priv->have_static_opps = true;
/*
* But we need OPP table to function so if it is not there let's
* give platform code chance to provide it for us.
*/
ret = dev_pm_opp_get_opp_count(cpu_dev);
if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
goto out_free_opp;
}
if (fallback) {
cpumask_setall(policy->cpus);
/*
* OPP tables are initialized only for policy->cpu, do it for
* others as well.
*/
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
if (ret)
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
__func__, ret);
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_free_opp;
}
priv->cpu_dev = cpu_dev;
policy->driver_data = priv;
policy->clk = cpu_clk;
policy->freq_table = freq_table;
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(cpu_dev) / 1000;
/* Support turbo/boost mode */
if (policy_has_boost_freq(policy)) {
/* This gets disabled by core on driver unregister */
ret = cpufreq_enable_boost_support();
if (ret)
goto out_free_cpufreq_table;
cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
}
transition_latency = dev_pm_opp_get_max_transition_latency(cpu_dev);
if (!transition_latency)
transition_latency = CPUFREQ_ETERNAL;
policy->cpuinfo.transition_latency = transition_latency;
policy->dvfs_possible_from_any_cpu = true;
dev_pm_opp_of_register_em(policy->cpus);
return 0;
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_opp:
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(policy->cpus);
kfree(priv);
out_put_regulator:
if (name)
dev_pm_opp_put_regulators(opp_table);
out_put_clk:
clk_put(cpu_clk);
return ret;
}
static int cpufreq_online(struct cpufreq_policy *policy)
{
/* We did light-weight tear down earlier, nothing to do here */
return 0;
}
static int cpufreq_offline(struct cpufreq_policy *policy)
{
/*
* Preserve policy->driver_data and don't free resources on light-weight
* tear down.
*/
return 0;
}
static int cpufreq_exit(struct cpufreq_policy *policy)
{
struct private_data *priv = policy->driver_data;
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
if (priv->have_static_opps)
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
if (priv->reg_name)
dev_pm_opp_put_regulators(priv->opp_table);
clk_put(policy->clk);
kfree(priv);
return 0;
}
static struct cpufreq_driver dt_cpufreq_driver = {
.flags = CPUFREQ_STICKY | CPUFREQ_NEED_INITIAL_FREQ_CHECK |
CPUFREQ_IS_COOLING_DEV,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = set_target,
.get = cpufreq_generic_get,
.init = cpufreq_init,
.exit = cpufreq_exit,
.online = cpufreq_online,
.offline = cpufreq_offline,
.name = "cpufreq-dt",
.attr = cpufreq_dt_attr,
.suspend = cpufreq_generic_suspend,
};
static int dt_cpufreq_probe(struct platform_device *pdev)
{
struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev);
int ret;
/*
* All per-cluster (CPUs sharing clock/voltages) initialization is done
* from ->init(). In probe(), we just need to make sure that clk and
* regulators are available. Else defer probe and retry.
*
* FIXME: Is checking this only for CPU0 sufficient ?
*/
ret = resources_available();
if (ret)
return ret;
if (data) {
if (data->have_governor_per_policy)
dt_cpufreq_driver.flags |= CPUFREQ_HAVE_GOVERNOR_PER_POLICY;
dt_cpufreq_driver.resume = data->resume;
if (data->suspend)
dt_cpufreq_driver.suspend = data->suspend;
}
ret = cpufreq_register_driver(&dt_cpufreq_driver);
if (ret)
dev_err(&pdev->dev, "failed register driver: %d\n", ret);
return ret;
}
static int dt_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&dt_cpufreq_driver);
return 0;
}
static struct platform_driver dt_cpufreq_platdrv = {
.driver = {
.name = "cpufreq-dt",
},
.probe = dt_cpufreq_probe,
.remove = dt_cpufreq_remove,
};
module_platform_driver(dt_cpufreq_platdrv);
MODULE_ALIAS("platform:cpufreq-dt");
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
MODULE_AUTHOR("Shawn Guo <shawn.guo@linaro.org>");
MODULE_DESCRIPTION("Generic cpufreq driver");
MODULE_LICENSE("GPL");