summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acpi/acpi_processor.c34
-rw-r--r--drivers/acpi/ec.c10
-rw-r--r--drivers/base/arch_topology.c15
-rw-r--r--drivers/base/power/domain.c71
-rw-r--r--drivers/base/power/runtime.c5
-rw-r--r--drivers/cpufreq/Kconfig.arm2
-rw-r--r--drivers/cpufreq/armada-37xx-cpufreq.c6
-rw-r--r--drivers/cpufreq/cpufreq-dt-platdev.c1
-rw-r--r--drivers/cpufreq/cpufreq-dt.c296
-rw-r--r--drivers/cpufreq/cpufreq.c45
-rw-r--r--drivers/cpufreq/cpufreq_stats.c105
-rw-r--r--drivers/cpufreq/imx6q-cpufreq.c10
-rw-r--r--drivers/cpufreq/qcom-cpufreq-hw.c144
-rw-r--r--drivers/cpufreq/s5pv210-cpufreq.c31
-rw-r--r--drivers/cpufreq/scmi-cpufreq.c12
-rw-r--r--drivers/cpufreq/scpi-cpufreq.c6
-rw-r--r--drivers/cpufreq/sti-cpufreq.c6
-rw-r--r--drivers/cpufreq/tegra186-cpufreq.c30
-rw-r--r--drivers/cpufreq/vexpress-spc-cpufreq.c12
-rw-r--r--drivers/cpuidle/cpuidle-psci-domain.c59
-rw-r--r--drivers/cpuidle/cpuidle-tegra.c34
-rw-r--r--drivers/cpuidle/cpuidle.c1
-rw-r--r--drivers/cpuidle/sysfs.c3
-rw-r--r--drivers/devfreq/devfreq-event.c14
-rw-r--r--drivers/devfreq/devfreq.c57
-rw-r--r--drivers/devfreq/exynos-bus.c7
-rw-r--r--drivers/devfreq/rk3399_dmc.c2
-rw-r--r--drivers/devfreq/tegra30-devfreq.c8
-rw-r--r--drivers/firmware/psci/psci.c12
-rw-r--r--drivers/memory/samsung/exynos5422-dmc.c6
-rw-r--r--drivers/opp/core.c231
-rw-r--r--drivers/opp/cpu.c2
-rw-r--r--drivers/opp/of.c112
-rw-r--r--drivers/opp/opp.h5
-rw-r--r--drivers/pci/pci-acpi.c10
-rw-r--r--drivers/power/avs/qcom-cpr.c8
-rw-r--r--drivers/powercap/idle_inject.c1
-rw-r--r--drivers/soc/samsung/exynos-asv.c2
38 files changed, 812 insertions, 603 deletions
diff --git a/drivers/acpi/acpi_processor.c b/drivers/acpi/acpi_processor.c
index b51ddf3bb616..412a9725cc1e 100644
--- a/drivers/acpi/acpi_processor.c
+++ b/drivers/acpi/acpi_processor.c
@@ -798,22 +798,34 @@ int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
memset(&cx, 0, sizeof(cx));
element = &cst->package.elements[i];
- if (element->type != ACPI_TYPE_PACKAGE)
+ if (element->type != ACPI_TYPE_PACKAGE) {
+ acpi_handle_info(handle, "_CST C%d type(%x) is not package, skip...\n",
+ i, element->type);
continue;
+ }
- if (element->package.count != 4)
+ if (element->package.count != 4) {
+ acpi_handle_info(handle, "_CST C%d package count(%d) is not 4, skip...\n",
+ i, element->package.count);
continue;
+ }
obj = &element->package.elements[0];
- if (obj->type != ACPI_TYPE_BUFFER)
+ if (obj->type != ACPI_TYPE_BUFFER) {
+ acpi_handle_info(handle, "_CST C%d package element[0] type(%x) is not buffer, skip...\n",
+ i, obj->type);
continue;
+ }
reg = (struct acpi_power_register *)obj->buffer.pointer;
obj = &element->package.elements[1];
- if (obj->type != ACPI_TYPE_INTEGER)
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(handle, "_CST C[%d] package element[1] type(%x) is not integer, skip...\n",
+ i, obj->type);
continue;
+ }
cx.type = obj->integer.value;
/*
@@ -850,6 +862,8 @@ int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
cx.entry_method = ACPI_CSTATE_HALT;
snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI HLT");
} else {
+ acpi_handle_info(handle, "_CST C%d declares FIXED_HARDWARE C-state but not supported in hardware, skip...\n",
+ i);
continue;
}
} else if (reg->space_id == ACPI_ADR_SPACE_SYSTEM_IO) {
@@ -857,6 +871,8 @@ int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
snprintf(cx.desc, ACPI_CX_DESC_LEN, "ACPI IOPORT 0x%x",
cx.address);
} else {
+ acpi_handle_info(handle, "_CST C%d space_id(%x) neither FIXED_HARDWARE nor SYSTEM_IO, skip...\n",
+ i, reg->space_id);
continue;
}
@@ -864,14 +880,20 @@ int acpi_processor_evaluate_cst(acpi_handle handle, u32 cpu,
cx.valid = 1;
obj = &element->package.elements[2];
- if (obj->type != ACPI_TYPE_INTEGER)
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(handle, "_CST C%d package element[2] type(%x) not integer, skip...\n",
+ i, obj->type);
continue;
+ }
cx.latency = obj->integer.value;
obj = &element->package.elements[3];
- if (obj->type != ACPI_TYPE_INTEGER)
+ if (obj->type != ACPI_TYPE_INTEGER) {
+ acpi_handle_info(handle, "_CST C%d package element[3] type(%x) not integer, skip...\n",
+ i, obj->type);
continue;
+ }
memcpy(&info->states[++last_index], &cx, sizeof(cx));
}
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index fcddda3d6712..e0cb1bcfffb2 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -2011,20 +2011,16 @@ bool acpi_ec_dispatch_gpe(void)
if (acpi_any_gpe_status_set(first_ec->gpe))
return true;
- if (ec_no_wakeup)
- return false;
-
/*
* Dispatch the EC GPE in-band, but do not report wakeup in any case
* to allow the caller to process events properly after that.
*/
ret = acpi_dispatch_gpe(NULL, first_ec->gpe);
- if (ret == ACPI_INTERRUPT_HANDLED) {
+ if (ret == ACPI_INTERRUPT_HANDLED)
pm_pr_dbg("ACPI EC GPE dispatched\n");
- /* Flush the event and query workqueues. */
- acpi_ec_flush_work();
- }
+ /* Flush the event and query workqueues. */
+ acpi_ec_flush_work();
return false;
}
diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c
index 75f72d684294..c1a9e2fb634e 100644
--- a/drivers/base/arch_topology.c
+++ b/drivers/base/arch_topology.c
@@ -21,18 +21,27 @@
#include <linux/sched.h>
#include <linux/smp.h>
-__weak bool arch_freq_counters_available(struct cpumask *cpus)
+bool topology_scale_freq_invariant(void)
+{
+ return cpufreq_supports_freq_invariance() ||
+ arch_freq_counters_available(cpu_online_mask);
+}
+
+__weak bool arch_freq_counters_available(const struct cpumask *cpus)
{
return false;
}
DEFINE_PER_CPU(unsigned long, freq_scale) = SCHED_CAPACITY_SCALE;
-void arch_set_freq_scale(struct cpumask *cpus, unsigned long cur_freq,
- unsigned long max_freq)
+void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq,
+ unsigned long max_freq)
{
unsigned long scale;
int i;
+ if (WARN_ON_ONCE(!cur_freq || !max_freq))
+ return;
+
/*
* If the use of counters for FIE is enabled, just return as we don't
* want to update the scale factor with information from CPUFREQ.
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index 2cb5e04cf86c..05bb4d4401b2 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -123,7 +123,7 @@ static const struct genpd_lock_ops genpd_spin_ops = {
#define genpd_lock_interruptible(p) p->lock_ops->lock_interruptible(p)
#define genpd_unlock(p) p->lock_ops->unlock(p)
-#define genpd_status_on(genpd) (genpd->status == GPD_STATE_ACTIVE)
+#define genpd_status_on(genpd) (genpd->status == GENPD_STATE_ON)
#define genpd_is_irq_safe(genpd) (genpd->flags & GENPD_FLAG_IRQ_SAFE)
#define genpd_is_always_on(genpd) (genpd->flags & GENPD_FLAG_ALWAYS_ON)
#define genpd_is_active_wakeup(genpd) (genpd->flags & GENPD_FLAG_ACTIVE_WAKEUP)
@@ -222,7 +222,7 @@ static void genpd_update_accounting(struct generic_pm_domain *genpd)
* out of off and so update the idle time and vice
* versa.
*/
- if (genpd->status == GPD_STATE_ACTIVE) {
+ if (genpd->status == GENPD_STATE_ON) {
int state_idx = genpd->state_idx;
genpd->states[state_idx].idle_time =
@@ -497,6 +497,7 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
struct pm_domain_data *pdd;
struct gpd_link *link;
unsigned int not_suspended = 0;
+ int ret;
/*
* Do not try to power off the domain in the following situations:
@@ -544,26 +545,15 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
if (!genpd->gov)
genpd->state_idx = 0;
- if (genpd->power_off) {
- int ret;
-
- if (atomic_read(&genpd->sd_count) > 0)
- return -EBUSY;
+ /* Don't power off, if a child domain is waiting to power on. */
+ if (atomic_read(&genpd->sd_count) > 0)
+ return -EBUSY;
- /*
- * If sd_count > 0 at this point, one of the subdomains hasn't
- * managed to call genpd_power_on() for the parent yet after
- * incrementing it. In that case genpd_power_on() will wait
- * for us to drop the lock, so we can call .power_off() and let
- * the genpd_power_on() restore power for us (this shouldn't
- * happen very often).
- */
- ret = _genpd_power_off(genpd, true);
- if (ret)
- return ret;
- }
+ ret = _genpd_power_off(genpd, true);
+ if (ret)
+ return ret;
- genpd->status = GPD_STATE_POWER_OFF;
+ genpd->status = GENPD_STATE_OFF;
genpd_update_accounting(genpd);
list_for_each_entry(link, &genpd->child_links, child_node) {
@@ -616,7 +606,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
if (ret)
goto err;
- genpd->status = GPD_STATE_ACTIVE;
+ genpd->status = GENPD_STATE_ON;
genpd_update_accounting(genpd);
return 0;
@@ -961,7 +951,7 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
if (_genpd_power_off(genpd, false))
return;
- genpd->status = GPD_STATE_POWER_OFF;
+ genpd->status = GENPD_STATE_OFF;
list_for_each_entry(link, &genpd->child_links, child_node) {
genpd_sd_counter_dec(link->parent);
@@ -1007,8 +997,7 @@ static void genpd_sync_power_on(struct generic_pm_domain *genpd, bool use_lock,
}
_genpd_power_on(genpd, false);
-
- genpd->status = GPD_STATE_ACTIVE;
+ genpd->status = GENPD_STATE_ON;
}
/**
@@ -1287,7 +1276,7 @@ static int genpd_restore_noirq(struct device *dev)
* so make it appear as powered off to genpd_sync_power_on(),
* so that it tries to power it on in case it was really off.
*/
- genpd->status = GPD_STATE_POWER_OFF;
+ genpd->status = GENPD_STATE_OFF;
genpd_sync_power_on(genpd, true, 0);
genpd_unlock(genpd);
@@ -1777,7 +1766,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
genpd->gov = gov;
INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
atomic_set(&genpd->sd_count, 0);
- genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
+ genpd->status = is_off ? GENPD_STATE_OFF : GENPD_STATE_ON;
genpd->device_count = 0;
genpd->max_off_time_ns = -1;
genpd->max_off_time_changed = true;
@@ -2044,8 +2033,9 @@ int of_genpd_add_provider_simple(struct device_node *np,
if (genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table(&genpd->dev);
if (ret) {
- dev_err(&genpd->dev, "Failed to add OPP table: %d\n",
- ret);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&genpd->dev, "Failed to add OPP table: %d\n",
+ ret);
goto unlock;
}
@@ -2054,7 +2044,7 @@ int of_genpd_add_provider_simple(struct device_node *np,
* state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table(&genpd->dev);
- WARN_ON(!genpd->opp_table);
+ WARN_ON(IS_ERR(genpd->opp_table));
}
ret = genpd_add_provider(np, genpd_xlate_simple, genpd);
@@ -2111,8 +2101,9 @@ int of_genpd_add_provider_onecell(struct device_node *np,
if (genpd->set_performance_state) {
ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i);
if (ret) {
- dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n",
- i, ret);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n",
+ i, ret);
goto error;
}
@@ -2121,7 +2112,7 @@ int of_genpd_add_provider_onecell(struct device_node *np,
* performance state.
*/
genpd->opp_table = dev_pm_opp_get_opp_table_indexed(&genpd->dev, i);
- WARN_ON(!genpd->opp_table);
+ WARN_ON(IS_ERR(genpd->opp_table));
}
genpd->provider = &np->fwnode;
@@ -2802,8 +2793,8 @@ static int genpd_summary_one(struct seq_file *s,
struct generic_pm_domain *genpd)
{
static const char * const status_lookup[] = {
- [GPD_STATE_ACTIVE] = "on",
- [GPD_STATE_POWER_OFF] = "off"
+ [GENPD_STATE_ON] = "on",
+ [GENPD_STATE_OFF] = "off"
};
struct pm_domain_data *pm_data;
const char *kobj_path;
@@ -2881,8 +2872,8 @@ static int summary_show(struct seq_file *s, void *data)
static int status_show(struct seq_file *s, void *data)
{
static const char * const status_lookup[] = {
- [GPD_STATE_ACTIVE] = "on",
- [GPD_STATE_POWER_OFF] = "off"
+ [GENPD_STATE_ON] = "on",
+ [GENPD_STATE_OFF] = "off"
};
struct generic_pm_domain *genpd = s->private;
@@ -2895,7 +2886,7 @@ static int status_show(struct seq_file *s, void *data)
if (WARN_ON_ONCE(genpd->status >= ARRAY_SIZE(status_lookup)))
goto exit;
- if (genpd->status == GPD_STATE_POWER_OFF)
+ if (genpd->status == GENPD_STATE_OFF)
seq_printf(s, "%s-%u\n", status_lookup[genpd->status],
genpd->state_idx);
else
@@ -2938,7 +2929,7 @@ static int idle_states_show(struct seq_file *s, void *data)
ktime_t delta = 0;
s64 msecs;
- if ((genpd->status == GPD_STATE_POWER_OFF) &&
+ if ((genpd->status == GENPD_STATE_OFF) &&
(genpd->state_idx == i))
delta = ktime_sub(ktime_get(), genpd->accounting_time);
@@ -2961,7 +2952,7 @@ static int active_time_show(struct seq_file *s, void *data)
if (ret)
return -ERESTARTSYS;
- if (genpd->status == GPD_STATE_ACTIVE)
+ if (genpd->status == GENPD_STATE_ON)
delta = ktime_sub(ktime_get(), genpd->accounting_time);
seq_printf(s, "%lld ms\n", ktime_to_ms(
@@ -2984,7 +2975,7 @@ static int total_idle_time_show(struct seq_file *s, void *data)
for (i = 0; i < genpd->state_count; i++) {
- if ((genpd->status == GPD_STATE_POWER_OFF) &&
+ if ((genpd->status == GENPD_STATE_OFF) &&
(genpd->state_idx == i))
delta = ktime_sub(ktime_get(), genpd->accounting_time);
diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c
index 8143210a5c54..6f605f7820bb 100644
--- a/drivers/base/power/runtime.c
+++ b/drivers/base/power/runtime.c
@@ -291,8 +291,7 @@ static int rpm_get_suppliers(struct device *dev)
device_links_read_lock_held()) {
int retval;
- if (!(link->flags & DL_FLAG_PM_RUNTIME) ||
- READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND)
+ if (!(link->flags & DL_FLAG_PM_RUNTIME))
continue;
retval = pm_runtime_get_sync(link->supplier);
@@ -312,8 +311,6 @@ static void rpm_put_suppliers(struct device *dev)
list_for_each_entry_rcu(link, &dev->links.suppliers, c_node,
device_links_read_lock_held()) {
- if (READ_ONCE(link->status) == DL_STATE_SUPPLIER_UNBIND)
- continue;
while (refcount_dec_not_one(&link->rpm_active))
pm_runtime_put(link->supplier);
diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm
index cb72fb507d57..bf5830eb664f 100644
--- a/drivers/cpufreq/Kconfig.arm
+++ b/drivers/cpufreq/Kconfig.arm
@@ -283,7 +283,7 @@ config ARM_SPEAR_CPUFREQ
config ARM_STI_CPUFREQ
tristate "STi CPUFreq support"
- depends on SOC_STIH407
+ depends on CPUFREQ_DT && SOC_STIH407
help
This driver uses the generic OPP framework to match the running
platform with a predefined set of suitable values. If not provided
diff --git a/drivers/cpufreq/armada-37xx-cpufreq.c b/drivers/cpufreq/armada-37xx-cpufreq.c
index df1c941260d1..b4af4094309b 100644
--- a/drivers/cpufreq/armada-37xx-cpufreq.c
+++ b/drivers/cpufreq/armada-37xx-cpufreq.c
@@ -484,6 +484,12 @@ remove_opp:
/* late_initcall, to guarantee the driver is loaded after A37xx clock driver */
late_initcall(armada37xx_cpufreq_driver_init);
+static const struct of_device_id __maybe_unused armada37xx_cpufreq_of_match[] = {
+ { .compatible = "marvell,armada-3700-nb-pm" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, armada37xx_cpufreq_of_match);
+
MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
MODULE_DESCRIPTION("Armada 37xx cpufreq driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c
index 7d01df7bfa6c..3776d960f405 100644
--- a/drivers/cpufreq/cpufreq-dt-platdev.c
+++ b/drivers/cpufreq/cpufreq-dt-platdev.c
@@ -137,6 +137,7 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "st,stih407", },
{ .compatible = "st,stih410", },
+ { .compatible = "st,stih418", },
{ .compatible = "sigma,tango4", },
diff --git a/drivers/cpufreq/cpufreq-dt.c b/drivers/cpufreq/cpufreq-dt.c
index 944d7b45afe9..e363ae04aac6 100644
--- a/drivers/cpufreq/cpufreq-dt.c
+++ b/drivers/cpufreq/cpufreq-dt.c
@@ -13,6 +13,7 @@
#include <linux/cpufreq.h>
#include <linux/cpumask.h>
#include <linux/err.h>
+#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_opp.h>
@@ -24,32 +25,41 @@
#include "cpufreq-dt.h"
struct private_data {
- struct opp_table *opp_table;
+ struct list_head node;
+
+ cpumask_var_t cpus;
struct device *cpu_dev;
- const char *reg_name;
+ struct opp_table *opp_table;
+ struct opp_table *reg_opp_table;
bool have_static_opps;
};
+static LIST_HEAD(priv_list);
+
static struct freq_attr *cpufreq_dt_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
NULL, /* Extra space for boost-attr if required */
NULL,
};
+static struct private_data *cpufreq_dt_find_data(int cpu)
+{
+ struct private_data *priv;
+
+ list_for_each_entry(priv, &priv_list, node) {
+ if (cpumask_test_cpu(cpu, priv->cpus))
+ return priv;
+ }
+
+ return 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;
+ return dev_pm_opp_set_rate(priv->cpu_dev, freq * 1000);
}
/*
@@ -90,83 +100,24 @@ node_put:
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) {
- /*
- * If cpu's clk node is present, but clock is not yet
- * registered, we should try defering probe.
- */
- 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;
- }
-
- clk_put(cpu_clk);
-
- ret = dev_pm_opp_of_find_icc_paths(cpu_dev, NULL);
- if (ret)
- return ret;
-
- 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);
+ priv = cpufreq_dt_find_data(policy->cpu);
+ if (!priv) {
+ pr_err("failed to find data for cpu%d\n", policy->cpu);
return -ENODEV;
}
+ cpu_dev = priv->cpu_dev;
+ cpumask_copy(policy->cpus, priv->cpus);
+
cpu_clk = clk_get(cpu_dev, NULL);
if (IS_ERR(cpu_clk)) {
ret = PTR_ERR(cpu_clk);
@@ -174,45 +125,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)
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.
@@ -232,31 +144,17 @@ static int cpufreq_init(struct cpufreq_policy *policy)
*/
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;
+ dev_err(cpu_dev, "OPP table can't be empty\n");
+ ret = -ENODEV;
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;
@@ -288,11 +186,6 @@ out_free_cpufreq_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;
@@ -320,12 +213,7 @@ static int cpufreq_exit(struct cpufreq_policy *policy)
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;
}
@@ -344,21 +232,119 @@ static struct cpufreq_driver dt_cpufreq_driver = {
.suspend = cpufreq_generic_suspend,
};
-static int dt_cpufreq_probe(struct platform_device *pdev)
+static int dt_cpufreq_early_init(struct device *dev, int cpu)
{
- struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev);
+ struct private_data *priv;
+ struct device *cpu_dev;
+ const char *reg_name;
int ret;
+ /* Check if this CPU is already covered by some other policy */
+ if (cpufreq_dt_find_data(cpu))
+ return 0;
+
+ cpu_dev = get_cpu_device(cpu);
+ if (!cpu_dev)
+ return -EPROBE_DEFER;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ if (!alloc_cpumask_var(&priv->cpus, GFP_KERNEL))
+ return -ENOMEM;
+
+ priv->cpu_dev = cpu_dev;
+
+ /* Try to get OPP table early to ensure resources are available */
+ priv->opp_table = dev_pm_opp_get_opp_table(cpu_dev);
+ if (IS_ERR(priv->opp_table)) {
+ ret = PTR_ERR(priv->opp_table);
+ if (ret != -EPROBE_DEFER)
+ dev_err(cpu_dev, "failed to get OPP table: %d\n", ret);
+ goto free_cpumask;
+ }
+
/*
- * 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 ?
+ * OPP layer will be taking care of regulators now, but it needs to know
+ * the name of the regulator first.
*/
- ret = resources_available();
- if (ret)
- return ret;
+ reg_name = find_supply_name(cpu_dev);
+ if (reg_name) {
+ priv->reg_opp_table = dev_pm_opp_set_regulators(cpu_dev,
+ &reg_name, 1);
+ if (IS_ERR(priv->reg_opp_table)) {
+ ret = PTR_ERR(priv->reg_opp_table);
+ if (ret != -EPROBE_DEFER)
+ dev_err(cpu_dev, "failed to set regulators: %d\n",
+ ret);
+ goto put_table;
+ }
+ }
+
+ /* Find OPP sharing information so we can fill pri->cpus here */
+ /* Get OPP-sharing information from "operating-points-v2" bindings */
+ ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->cpus);
+ if (ret) {
+ if (ret != -ENOENT)
+ goto put_reg;
+
+ /*
+ * operating-points-v2 not supported, fallback to all CPUs share
+ * OPP for backward compatibility if the platform hasn't set
+ * sharing CPUs.
+ */
+ if (dev_pm_opp_get_sharing_cpus(cpu_dev, priv->cpus)) {
+ cpumask_setall(priv->cpus);
+
+ /*
+ * OPP tables are initialized only for cpu, do it for
+ * others as well.
+ */
+ ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->cpus);
+ if (ret)
+ dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
+ __func__, ret);
+ }
+ }
+
+ list_add(&priv->node, &priv_list);
+ return 0;
+
+put_reg:
+ if (priv->reg_opp_table)
+ dev_pm_opp_put_regulators(priv->reg_opp_table);
+put_table:
+ dev_pm_opp_put_opp_table(priv->opp_table);
+free_cpumask:
+ free_cpumask_var(priv->cpus);
+ return ret;
+}
+
+static void dt_cpufreq_release(void)
+{
+ struct private_data *priv, *tmp;
+
+ list_for_each_entry_safe(priv, tmp, &priv_list, node) {
+ if (priv->reg_opp_table)
+ dev_pm_opp_put_regulators(priv->reg_opp_table);
+ dev_pm_opp_put_opp_table(priv->opp_table);
+ free_cpumask_var(priv->cpus);
+ list_del(&priv->node);
+ }
+}
+
+static int dt_cpufreq_probe(struct platform_device *pdev)
+{
+ struct cpufreq_dt_platform_data *data = dev_get_platdata(&pdev->dev);
+ int ret, cpu;
+
+ /* Request resources early so we can return in case of -EPROBE_DEFER */
+ for_each_possible_cpu(cpu) {
+ ret = dt_cpufreq_early_init(&pdev->dev, cpu);
+ if (ret)
+ goto err;
+ }
if (data) {
if (data->have_governor_per_policy)
@@ -374,15 +360,21 @@ static int dt_cpufreq_probe(struct platform_device *pdev)
}
ret = cpufreq_register_driver(&dt_cpufreq_driver);
- if (ret)
+ if (ret) {
dev_err(&pdev->dev, "failed register driver: %d\n", ret);
+ goto err;
+ }
+ return 0;
+err:
+ dt_cpufreq_release