mirror of
https://github.com/torvalds/linux.git
synced 2024-11-27 14:41:39 +00:00
Merge branch 'pm-cpufreq'
* pm-cpufreq: Revert "cpufreq: intel_pstate: Process HWP Guaranteed change notification" cpufreq: mediatek-hw: Add support for CPUFREQ HW cpufreq: Add of_perf_domain_get_sharing_cpumask dt-bindings: cpufreq: add bindings for MediaTek cpufreq HW cpufreq: Remove ready() callback cpufreq: sh: Remove sh_cpufreq_cpu_ready() cpufreq: acpi: Remove acpi_cpufreq_cpu_ready() cpufreq: qcom-hw: Set dvfs_possible_from_any_cpu cpufreq driver flag cpufreq: blocklist more Qualcomm platforms in cpufreq-dt-platdev cpufreq: qcom-cpufreq-hw: Add dcvs interrupt support cpufreq: scmi: Use .register_em() to register with energy model cpufreq: vexpress: Use .register_em() to register with energy model cpufreq: scpi: Use .register_em() to register with energy model cpufreq: qcom-cpufreq-hw: Use .register_em() to register with energy model cpufreq: omap: Use .register_em() to register with energy model cpufreq: mediatek: Use .register_em() to register with energy model cpufreq: imx6q: Use .register_em() to register with energy model cpufreq: dt: Use .register_em() to register with energy model cpufreq: Add callback to register with energy model cpufreq: vexpress: Set CPUFREQ_IS_COOLING_DEV flag
This commit is contained in:
commit
eabf9e616e
@ -75,9 +75,6 @@ And optionally
|
||||
.resume - A pointer to a per-policy resume function which is called
|
||||
with interrupts disabled and _before_ the governor is started again.
|
||||
|
||||
.ready - A pointer to a per-policy ready function which is called after
|
||||
the policy is fully initialized.
|
||||
|
||||
.attr - A pointer to a NULL-terminated list of "struct freq_attr" which
|
||||
allow to export values to sysfs.
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/cpufreq/cpufreq-mediatek-hw.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MediaTek's CPUFREQ Bindings
|
||||
|
||||
maintainers:
|
||||
- Hector Yuan <hector.yuan@mediatek.com>
|
||||
|
||||
description:
|
||||
CPUFREQ HW is a hardware engine used by MediaTek SoCs to
|
||||
manage frequency in hardware. It is capable of controlling
|
||||
frequency for multiple clusters.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: mediatek,cpufreq-hw
|
||||
|
||||
reg:
|
||||
minItems: 1
|
||||
maxItems: 2
|
||||
description:
|
||||
Addresses and sizes for the memory of the HW bases in
|
||||
each frequency domain. Each entry corresponds to
|
||||
a register bank for each frequency domain present.
|
||||
|
||||
"#performance-domain-cells":
|
||||
description:
|
||||
Number of cells in a performance domain specifier.
|
||||
Set const to 1 here for nodes providing multiple
|
||||
performance domains.
|
||||
const: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- "#performance-domain-cells"
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
cpus {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
cpu0: cpu@0 {
|
||||
device_type = "cpu";
|
||||
compatible = "arm,cortex-a55";
|
||||
enable-method = "psci";
|
||||
performance-domains = <&performance 0>;
|
||||
reg = <0x000>;
|
||||
};
|
||||
};
|
||||
|
||||
/* ... */
|
||||
|
||||
soc {
|
||||
#address-cells = <2>;
|
||||
#size-cells = <2>;
|
||||
|
||||
performance: performance-controller@11bc00 {
|
||||
compatible = "mediatek,cpufreq-hw";
|
||||
reg = <0 0x0011bc10 0 0x120>, <0 0x0011bd30 0 0x120>;
|
||||
|
||||
#performance-domain-cells = <1>;
|
||||
};
|
||||
};
|
@ -80,8 +80,6 @@ CPUfreq核心层注册一个cpufreq_driver结构体。
|
||||
.resume - 一个指向per-policy恢复函数的指针,该函数在关中断且在调节器再一次开始前被
|
||||
调用。
|
||||
|
||||
.ready - 一个指向per-policy准备函数的指针,该函数在策略完全初始化之后被调用。
|
||||
|
||||
.attr - 一个指向NULL结尾的"struct freq_attr"列表的指针,该函数允许导出值到
|
||||
sysfs。
|
||||
|
||||
|
@ -149,6 +149,7 @@ void topology_set_freq_scale(const struct cpumask *cpus, unsigned long cur_freq,
|
||||
}
|
||||
|
||||
DEFINE_PER_CPU(unsigned long, cpu_scale) = SCHED_CAPACITY_SCALE;
|
||||
EXPORT_PER_CPU_SYMBOL_GPL(cpu_scale);
|
||||
|
||||
void topology_set_cpu_scale(unsigned int cpu, unsigned long capacity)
|
||||
{
|
||||
@ -165,6 +166,7 @@ void topology_set_thermal_pressure(const struct cpumask *cpus,
|
||||
for_each_cpu(cpu, cpus)
|
||||
WRITE_ONCE(per_cpu(thermal_pressure, cpu), th_pressure);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(topology_set_thermal_pressure);
|
||||
|
||||
static ssize_t cpu_capacity_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
|
@ -133,6 +133,18 @@ config ARM_MEDIATEK_CPUFREQ
|
||||
help
|
||||
This adds the CPUFreq driver support for MediaTek SoCs.
|
||||
|
||||
config ARM_MEDIATEK_CPUFREQ_HW
|
||||
tristate "MediaTek CPUFreq HW driver"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
default m
|
||||
help
|
||||
Support for the CPUFreq HW driver.
|
||||
Some MediaTek chipsets have a HW engine to offload the steps
|
||||
necessary for changing the frequency of the CPUs. Firmware loaded
|
||||
in this engine exposes a programming interface to the OS.
|
||||
The driver implements the cpufreq interface for this HW engine.
|
||||
Say Y if you want to support CPUFreq HW.
|
||||
|
||||
config ARM_OMAP2PLUS_CPUFREQ
|
||||
bool "TI OMAP2+"
|
||||
depends on ARCH_OMAP2PLUS
|
||||
|
@ -56,6 +56,7 @@ obj-$(CONFIG_ARM_IMX6Q_CPUFREQ) += imx6q-cpufreq.o
|
||||
obj-$(CONFIG_ARM_IMX_CPUFREQ_DT) += imx-cpufreq-dt.o
|
||||
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
|
||||
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-cpufreq.o
|
||||
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ_HW) += mediatek-cpufreq-hw.o
|
||||
obj-$(CONFIG_MACH_MVEBU_V7) += mvebu-cpufreq.o
|
||||
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
|
||||
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-cpufreq.o
|
||||
|
@ -889,6 +889,9 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
|
||||
policy->fast_switch_possible = !acpi_pstate_strict &&
|
||||
!(policy_is_shared(policy) && policy->shared_type != CPUFREQ_SHARED_TYPE_ANY);
|
||||
|
||||
if (perf->states[0].core_frequency * 1000 != freq_table[0].frequency)
|
||||
pr_warn(FW_WARN "P-state 0 is not max freq\n");
|
||||
|
||||
return result;
|
||||
|
||||
err_unreg:
|
||||
@ -918,16 +921,6 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void acpi_cpufreq_cpu_ready(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct acpi_processor_performance *perf = per_cpu_ptr(acpi_perf_data,
|
||||
policy->cpu);
|
||||
unsigned int freq = policy->freq_table[0].frequency;
|
||||
|
||||
if (perf->states[0].core_frequency * 1000 != freq)
|
||||
pr_warn(FW_WARN "P-state 0 is not max freq\n");
|
||||
}
|
||||
|
||||
static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct acpi_cpufreq_data *data = policy->driver_data;
|
||||
@ -955,7 +948,6 @@ static struct cpufreq_driver acpi_cpufreq_driver = {
|
||||
.bios_limit = acpi_processor_get_bios_limit,
|
||||
.init = acpi_cpufreq_cpu_init,
|
||||
.exit = acpi_cpufreq_cpu_exit,
|
||||
.ready = acpi_cpufreq_cpu_ready,
|
||||
.resume = acpi_cpufreq_resume,
|
||||
.name = "acpi-cpufreq",
|
||||
.attr = acpi_cpufreq_attr,
|
||||
|
@ -137,11 +137,15 @@ static const struct of_device_id blocklist[] __initconst = {
|
||||
{ .compatible = "qcom,apq8096", },
|
||||
{ .compatible = "qcom,msm8996", },
|
||||
{ .compatible = "qcom,qcs404", },
|
||||
{ .compatible = "qcom,sa8155p" },
|
||||
{ .compatible = "qcom,sc7180", },
|
||||
{ .compatible = "qcom,sc7280", },
|
||||
{ .compatible = "qcom,sc8180x", },
|
||||
{ .compatible = "qcom,sdm845", },
|
||||
{ .compatible = "qcom,sm6350", },
|
||||
{ .compatible = "qcom,sm8150", },
|
||||
{ .compatible = "qcom,sm8250", },
|
||||
{ .compatible = "qcom,sm8350", },
|
||||
|
||||
{ .compatible = "st,stih407", },
|
||||
{ .compatible = "st,stih410", },
|
||||
|
@ -143,8 +143,6 @@ static int cpufreq_init(struct cpufreq_policy *policy)
|
||||
cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
|
||||
}
|
||||
|
||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
||||
|
||||
return 0;
|
||||
|
||||
out_clk_put:
|
||||
@ -184,6 +182,7 @@ static struct cpufreq_driver dt_cpufreq_driver = {
|
||||
.exit = cpufreq_exit,
|
||||
.online = cpufreq_online,
|
||||
.offline = cpufreq_offline,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.name = "cpufreq-dt",
|
||||
.attr = cpufreq_dt_attr,
|
||||
.suspend = cpufreq_generic_suspend,
|
||||
|
@ -1491,6 +1491,19 @@ static int cpufreq_online(unsigned int cpu)
|
||||
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
||||
list_add(&policy->policy_list, &cpufreq_policy_list);
|
||||
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
||||
|
||||
/*
|
||||
* Register with the energy model before
|
||||
* sched_cpufreq_governor_change() is called, which will result
|
||||
* in rebuilding of the sched domains, which should only be done
|
||||
* once the energy model is properly initialized for the policy
|
||||
* first.
|
||||
*
|
||||
* Also, this should be called before the policy is registered
|
||||
* with cooling framework.
|
||||
*/
|
||||
if (cpufreq_driver->register_em)
|
||||
cpufreq_driver->register_em(policy);
|
||||
}
|
||||
|
||||
ret = cpufreq_init_policy(policy);
|
||||
@ -1504,10 +1517,6 @@ static int cpufreq_online(unsigned int cpu)
|
||||
|
||||
kobject_uevent(&policy->kobj, KOBJ_ADD);
|
||||
|
||||
/* Callback for handling stuff after policy is ready */
|
||||
if (cpufreq_driver->ready)
|
||||
cpufreq_driver->ready(policy);
|
||||
|
||||
if (cpufreq_thermal_control_enabled(cpufreq_driver))
|
||||
policy->cdev = of_cpufreq_cooling_register(policy);
|
||||
|
||||
|
@ -192,7 +192,6 @@ static int imx6q_cpufreq_init(struct cpufreq_policy *policy)
|
||||
policy->clk = clks[ARM].clk;
|
||||
cpufreq_generic_init(policy, freq_table, transition_latency);
|
||||
policy->suspend_freq = max_freq;
|
||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -204,6 +203,7 @@ static struct cpufreq_driver imx6q_cpufreq_driver = {
|
||||
.target_index = imx6q_set_target,
|
||||
.get = cpufreq_generic_get,
|
||||
.init = imx6q_cpufreq_init,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.name = "imx6q-cpufreq",
|
||||
.attr = cpufreq_generic_attr,
|
||||
.suspend = cpufreq_generic_suspend,
|
||||
|
@ -32,7 +32,6 @@
|
||||
#include <asm/cpu_device_id.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/intel-family.h>
|
||||
#include "../drivers/thermal/intel/thermal_interrupt.h"
|
||||
|
||||
#define INTEL_PSTATE_SAMPLING_INTERVAL (10 * NSEC_PER_MSEC)
|
||||
|
||||
@ -220,7 +219,6 @@ struct global_params {
|
||||
* @sched_flags: Store scheduler flags for possible cross CPU update
|
||||
* @hwp_boost_min: Last HWP boosted min performance
|
||||
* @suspended: Whether or not the driver has been suspended.
|
||||
* @hwp_notify_work: workqueue for HWP notifications.
|
||||
*
|
||||
* This structure stores per CPU instance data for all CPUs.
|
||||
*/
|
||||
@ -259,7 +257,6 @@ struct cpudata {
|
||||
unsigned int sched_flags;
|
||||
u32 hwp_boost_min;
|
||||
bool suspended;
|
||||
struct delayed_work hwp_notify_work;
|
||||
};
|
||||
|
||||
static struct cpudata **all_cpu_data;
|
||||
@ -1628,40 +1625,6 @@ static void intel_pstate_sysfs_hide_hwp_dynamic_boost(void)
|
||||
|
||||
/************************** sysfs end ************************/
|
||||
|
||||
static void intel_pstate_notify_work(struct work_struct *work)
|
||||
{
|
||||
mutex_lock(&intel_pstate_driver_lock);
|
||||
cpufreq_update_policy(smp_processor_id());
|
||||
wrmsrl(MSR_HWP_STATUS, 0);
|
||||
mutex_unlock(&intel_pstate_driver_lock);
|
||||
}
|
||||
|
||||
void notify_hwp_interrupt(void)
|
||||
{
|
||||
unsigned int this_cpu = smp_processor_id();
|
||||
struct cpudata *cpudata;
|
||||
u64 value;
|
||||
|
||||
if (!hwp_active || !boot_cpu_has(X86_FEATURE_HWP_NOTIFY))
|
||||
return;
|
||||
|
||||
rdmsrl(MSR_HWP_STATUS, value);
|
||||
if (!(value & 0x01))
|
||||
return;
|
||||
|
||||
cpudata = all_cpu_data[this_cpu];
|
||||
schedule_delayed_work_on(this_cpu, &cpudata->hwp_notify_work, msecs_to_jiffies(10));
|
||||
}
|
||||
|
||||
static void intel_pstate_enable_hwp_interrupt(struct cpudata *cpudata)
|
||||
{
|
||||
/* Enable HWP notification interrupt for guaranteed performance change */
|
||||
if (boot_cpu_has(X86_FEATURE_HWP_NOTIFY)) {
|
||||
INIT_DELAYED_WORK(&cpudata->hwp_notify_work, intel_pstate_notify_work);
|
||||
wrmsrl_on_cpu(cpudata->cpu, MSR_HWP_INTERRUPT, 0x01);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_pstate_hwp_enable(struct cpudata *cpudata)
|
||||
{
|
||||
/* First disable HWP notification interrupt as we don't process them */
|
||||
@ -1671,8 +1634,6 @@ static void intel_pstate_hwp_enable(struct cpudata *cpudata)
|
||||
wrmsrl_on_cpu(cpudata->cpu, MSR_PM_ENABLE, 0x1);
|
||||
if (cpudata->epp_default == -EINVAL)
|
||||
cpudata->epp_default = intel_pstate_get_epp(cpudata, 0);
|
||||
|
||||
intel_pstate_enable_hwp_interrupt(cpudata);
|
||||
}
|
||||
|
||||
static int atom_get_min_pstate(void)
|
||||
|
308
drivers/cpufreq/mediatek-cpufreq-hw.c
Normal file
308
drivers/cpufreq/mediatek-cpufreq-hw.c
Normal file
@ -0,0 +1,308 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2020 MediaTek Inc.
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/energy_model.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define LUT_MAX_ENTRIES 32U
|
||||
#define LUT_FREQ GENMASK(11, 0)
|
||||
#define LUT_ROW_SIZE 0x4
|
||||
#define CPUFREQ_HW_STATUS BIT(0)
|
||||
#define SVS_HW_STATUS BIT(1)
|
||||
#define POLL_USEC 1000
|
||||
#define TIMEOUT_USEC 300000
|
||||
|
||||
enum {
|
||||
REG_FREQ_LUT_TABLE,
|
||||
REG_FREQ_ENABLE,
|
||||
REG_FREQ_PERF_STATE,
|
||||
REG_FREQ_HW_STATE,
|
||||
REG_EM_POWER_TBL,
|
||||
REG_FREQ_LATENCY,
|
||||
|
||||
REG_ARRAY_SIZE,
|
||||
};
|
||||
|
||||
struct mtk_cpufreq_data {
|
||||
struct cpufreq_frequency_table *table;
|
||||
void __iomem *reg_bases[REG_ARRAY_SIZE];
|
||||
int nr_opp;
|
||||
};
|
||||
|
||||
static const u16 cpufreq_mtk_offsets[REG_ARRAY_SIZE] = {
|
||||
[REG_FREQ_LUT_TABLE] = 0x0,
|
||||
[REG_FREQ_ENABLE] = 0x84,
|
||||
[REG_FREQ_PERF_STATE] = 0x88,
|
||||
[REG_FREQ_HW_STATE] = 0x8c,
|
||||
[REG_EM_POWER_TBL] = 0x90,
|
||||
[REG_FREQ_LATENCY] = 0x110,
|
||||
};
|
||||
|
||||
static int __maybe_unused
|
||||
mtk_cpufreq_get_cpu_power(unsigned long *mW,
|
||||
unsigned long *KHz, struct device *cpu_dev)
|
||||
{
|
||||
struct mtk_cpufreq_data *data;
|
||||
struct cpufreq_policy *policy;
|
||||
int i;
|
||||
|
||||
policy = cpufreq_cpu_get_raw(cpu_dev->id);
|
||||
if (!policy)
|
||||
return 0;
|
||||
|
||||
data = policy->driver_data;
|
||||
|
||||
for (i = 0; i < data->nr_opp; i++) {
|
||||
if (data->table[i].frequency < *KHz)
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
|
||||
*KHz = data->table[i].frequency;
|
||||
*mW = readl_relaxed(data->reg_bases[REG_EM_POWER_TBL] +
|
||||
i * LUT_ROW_SIZE) / 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_cpufreq_hw_target_index(struct cpufreq_policy *policy,
|
||||
unsigned int index)
|
||||
{
|
||||
struct mtk_cpufreq_data *data = policy->driver_data;
|
||||
|
||||
writel_relaxed(index, data->reg_bases[REG_FREQ_PERF_STATE]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int mtk_cpufreq_hw_get(unsigned int cpu)
|
||||
{
|
||||
struct mtk_cpufreq_data *data;
|
||||
struct cpufreq_policy *policy;
|
||||
unsigned int index;
|
||||
|
||||
policy = cpufreq_cpu_get_raw(cpu);
|
||||
if (!policy)
|
||||
return 0;
|
||||
|
||||
data = policy->driver_data;
|
||||
|
||||
index = readl_relaxed(data->reg_bases[REG_FREQ_PERF_STATE]);
|
||||
index = min(index, LUT_MAX_ENTRIES - 1);
|
||||
|
||||
return data->table[index].frequency;
|
||||
}
|
||||
|
||||
static unsigned int mtk_cpufreq_hw_fast_switch(struct cpufreq_policy *policy,
|
||||
unsigned int target_freq)
|
||||
{
|
||||
struct mtk_cpufreq_data *data = policy->driver_data;
|
||||
unsigned int index;
|
||||
|
||||
index = cpufreq_table_find_index_dl(policy, target_freq);
|
||||
|
||||
writel_relaxed(index, data->reg_bases[REG_FREQ_PERF_STATE]);
|
||||
|
||||
return policy->freq_table[index].frequency;
|
||||
}
|
||||
|
||||
static int mtk_cpu_create_freq_table(struct platform_device *pdev,
|
||||
struct mtk_cpufreq_data *data)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
u32 temp, i, freq, prev_freq = 0;
|
||||
void __iomem *base_table;
|
||||
|
||||
data->table = devm_kcalloc(dev, LUT_MAX_ENTRIES + 1,
|
||||
sizeof(*data->table), GFP_KERNEL);
|
||||
if (!data->table)
|
||||
return -ENOMEM;
|
||||
|
||||
base_table = data->reg_bases[REG_FREQ_LUT_TABLE];
|
||||
|
||||
for (i = 0; i < LUT_MAX_ENTRIES; i++) {
|
||||
temp = readl_relaxed(base_table + (i * LUT_ROW_SIZE));
|
||||
freq = FIELD_GET(LUT_FREQ, temp) * 1000;
|
||||
|
||||
if (freq == prev_freq)
|
||||
break;
|
||||
|
||||
data->table[i].frequency = freq;
|
||||
|
||||
dev_dbg(dev, "index=%d freq=%d\n", i, data->table[i].frequency);
|
||||
|
||||
prev_freq = freq;
|
||||
}
|
||||
|
||||
data->table[i].frequency = CPUFREQ_TABLE_END;
|
||||
data->nr_opp = i;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_cpu_resources_init(struct platform_device *pdev,
|
||||
struct cpufreq_policy *policy,
|
||||
const u16 *offsets)
|
||||
{
|
||||
struct mtk_cpufreq_data *data;
|
||||
struct device *dev = &pdev->dev;
|
||||
void __iomem *base;
|
||||
int ret, i;
|
||||
int index;
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
index = of_perf_domain_get_sharing_cpumask(policy->cpu, "performance-domains",
|
||||
"#performance-domain-cells",
|
||||
policy->cpus);
|
||||
if (index < 0)
|
||||
return index;
|
||||
|
||||
base = devm_platform_ioremap_resource(pdev, index);
|
||||
if (IS_ERR(base))
|
||||
return PTR_ERR(base);
|
||||
|
||||
for (i = REG_FREQ_LUT_TABLE; i < REG_ARRAY_SIZE; i++)
|
||||
data->reg_bases[i] = base + offsets[i];
|
||||
|
||||
ret = mtk_cpu_create_freq_table(pdev, data);
|
||||
if (ret) {
|
||||
dev_info(dev, "Domain-%d failed to create freq table\n", index);
|
||||
return ret;
|
||||
}
|
||||
|
||||
policy->freq_table = data->table;
|
||||
policy->driver_data = data;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct platform_device *pdev = cpufreq_get_driver_data();
|
||||
int sig, pwr_hw = CPUFREQ_HW_STATUS | SVS_HW_STATUS;
|
||||
struct mtk_cpufreq_data *data;
|
||||
unsigned int latency;
|
||||
int ret;
|
||||
|
||||
/* Get the bases of cpufreq for domains */
|
||||
ret = mtk_cpu_resources_init(pdev, policy, platform_get_drvdata(pdev));
|
||||
if (ret) {
|
||||
dev_info(&pdev->dev, "CPUFreq resource init failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
data = policy->driver_data;
|
||||
|
||||
latency = readl_relaxed(data->reg_bases[REG_FREQ_LATENCY]) * 1000;
|
||||
if (!latency)
|
||||
latency = CPUFREQ_ETERNAL;
|
||||
|
||||
policy->cpuinfo.transition_latency = latency;
|
||||
policy->fast_switch_possible = true;
|
||||
|
||||
/* HW should be in enabled state to proceed now */
|
||||
writel_relaxed(0x1, data->reg_bases[REG_FREQ_ENABLE]);
|
||||
if (readl_poll_timeout(data->reg_bases[REG_FREQ_HW_STATE], sig,
|
||||
(sig & pwr_hw) == pwr_hw, POLL_USEC,
|
||||
TIMEOUT_USEC)) {
|
||||
if (!(sig & CPUFREQ_HW_STATUS)) {
|
||||
pr_info("cpufreq hardware of CPU%d is not enabled\n",
|
||||
policy->cpu);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
pr_info("SVS of CPU%d is not enabled\n", policy->cpu);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct mtk_cpufreq_data *data = policy->driver_data;
|
||||
|
||||
/* HW should be in paused state now */
|
||||
writel_relaxed(0x0, data->reg_bases[REG_FREQ_ENABLE]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_cpufreq_register_em(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct em_data_callback em_cb = EM_DATA_CB(mtk_cpufreq_get_cpu_power);
|
||||
struct mtk_cpufreq_data *data = policy->driver_data;
|
||||
|
||||
em_dev_register_perf_domain(get_cpu_device(policy->cpu), data->nr_opp,
|
||||
&em_cb, policy->cpus, true);
|
||||
}
|
||||
|
||||
static struct cpufreq_driver cpufreq_mtk_hw_driver = {
|
||||
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK |
|
||||
CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
||||
CPUFREQ_IS_COOLING_DEV,
|
||||
.verify = cpufreq_generic_frequency_table_verify,
|
||||
.target_index = mtk_cpufreq_hw_target_index,
|
||||
.get = mtk_cpufreq_hw_get,
|
||||
.init = mtk_cpufreq_hw_cpu_init,
|
||||
.exit = mtk_cpufreq_hw_cpu_exit,
|
||||
.register_em = mtk_cpufreq_register_em,
|
||||
.fast_switch = mtk_cpufreq_hw_fast_switch,
|
||||
.name = "mtk-cpufreq-hw",
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
static int mtk_cpufreq_hw_driver_probe(struct platform_device *pdev)
|
||||
{
|
||||
const void *data;
|
||||
int ret;
|
||||
|
||||
data = of_device_get_match_data(&pdev->dev);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
platform_set_drvdata(pdev, (void *) data);
|
||||
cpufreq_mtk_hw_driver.driver_data = pdev;
|
||||
|
||||
ret = cpufreq_register_driver(&cpufreq_mtk_hw_driver);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "CPUFreq HW driver failed to register\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_cpufreq_hw_driver_remove(struct platform_device *pdev)
|
||||
{
|
||||
return cpufreq_unregister_driver(&cpufreq_mtk_hw_driver);
|
||||
}
|
||||
|
||||
static const struct of_device_id mtk_cpufreq_hw_match[] = {
|
||||
{ .compatible = "mediatek,cpufreq-hw", .data = &cpufreq_mtk_offsets },
|
||||
{}
|
||||
};
|
||||
|
||||
static struct platform_driver mtk_cpufreq_hw_driver = {
|
||||
.probe = mtk_cpufreq_hw_driver_probe,
|
||||
.remove = mtk_cpufreq_hw_driver_remove,
|
||||
.driver = {
|
||||
.name = "mtk-cpufreq-hw",
|
||||
.of_match_table = mtk_cpufreq_hw_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(mtk_cpufreq_hw_driver);
|
||||
|
||||
MODULE_AUTHOR("Hector Yuan <hector.yuan@mediatek.com>");
|
||||
MODULE_DESCRIPTION("Mediatek cpufreq-hw driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@ -448,8 +448,6 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
|
||||
policy->driver_data = info;
|
||||
policy->clk = info->cpu_clk;
|
||||
|
||||
dev_pm_opp_of_register_em(info->cpu_dev, policy->cpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -471,6 +469,7 @@ static struct cpufreq_driver mtk_cpufreq_driver = {
|
||||
.get = cpufreq_generic_get,
|
||||
.init = mtk_cpufreq_init,
|
||||
.exit = mtk_cpufreq_exit,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.name = "mtk-cpufreq",
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
@ -131,7 +131,6 @@ static int omap_cpu_init(struct cpufreq_policy *policy)
|
||||
|
||||
/* FIXME: what's the actual transition time? */
|
||||
cpufreq_generic_init(policy, freq_table, 300 * 1000);
|
||||
dev_pm_opp_of_register_em(mpu_dev, policy->cpus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -150,6 +149,7 @@ static struct cpufreq_driver omap_driver = {
|
||||
.get = cpufreq_generic_get,
|
||||
.init = omap_cpu_init,
|
||||
.exit = omap_cpu_exit,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.name = "omap",
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
@ -7,12 +7,14 @@
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interconnect.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#define LUT_MAX_ENTRIES 40U
|
||||
#define LUT_SRC GENMASK(31, 30)
|
||||
@ -22,10 +24,13 @@
|
||||
#define CLK_HW_DIV 2
|
||||
#define LUT_TURBO_IND 1
|
||||
|
||||
#define HZ_PER_KHZ 1000
|
||||
|
||||
struct qcom_cpufreq_soc_data {
|
||||
u32 reg_enable;
|
||||
u32 reg_freq_lut;
|
||||
u32 reg_volt_lut;
|
||||
u32 reg_current_vote;
|
||||
u32 reg_perf_state;
|
||||
u8 lut_row_size;
|
||||
};
|
||||
@ -34,6 +39,16 @@ struct qcom_cpufreq_data {
|
||||
void __iomem *base;
|
||||
struct resource *res;
|
||||
const struct qcom_cpufreq_soc_data *soc_data;
|
||||
|
||||
/*
|
||||
* Mutex to synchronize between de-init sequence and re-starting LMh
|
||||
* polling/interrupts
|
||||
*/
|
||||
struct mutex throttle_lock;
|
||||
int throttle_irq;
|
||||
bool cancel_throttle;
|
||||
struct delayed_work throttle_work;
|
||||
struct cpufreq_policy *policy;
|
||||
};
|
||||
|
||||
static unsigned long cpu_hw_rate, xo_rate;
|
||||
@ -251,10 +266,92 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned int qcom_lmh_get_throttle_freq(struct qcom_cpufreq_data *data)
|
||||
{
|
||||
unsigned int val = readl_relaxed(data->base + data->soc_data->reg_current_vote);
|
||||
|
||||
return (val & 0x3FF) * 19200;
|
||||
}
|
||||
|
||||
static void qcom_lmh_dcvs_notify(struct qcom_cpufreq_data *data)
|
||||
{
|
||||
unsigned long max_capacity, capacity, freq_hz, throttled_freq;
|
||||
struct cpufreq_policy *policy = data->policy;
|
||||
int cpu = cpumask_first(policy->cpus);
|
||||
struct device *dev = get_cpu_device(cpu);
|
||||
struct dev_pm_opp *opp;
|
||||
unsigned int freq;
|
||||
|
||||
/*
|
||||
* Get the h/w throttled frequency, normalize it using the
|
||||
* registered opp table and use it to calculate thermal pressure.
|
||||
*/
|
||||
freq = qcom_lmh_get_throttle_freq(data);
|
||||
freq_hz = freq * HZ_PER_KHZ;
|
||||
|
||||
opp = dev_pm_opp_find_freq_floor(dev, &freq_hz);
|
||||
if (IS_ERR(opp) && PTR_ERR(opp) == -ERANGE)
|
||||
dev_pm_opp_find_freq_ceil(dev, &freq_hz);
|
||||
|
||||
throttled_freq = freq_hz / HZ_PER_KHZ;
|
||||
|
||||
/* Update thermal pressure */
|
||||
|
||||
max_capacity = arch_scale_cpu_capacity(cpu);
|
||||
capacity = mult_frac(max_capacity, throttled_freq, policy->cpuinfo.max_freq);
|
||||
|
||||
/* Don't pass boost capacity to scheduler */
|
||||
if (capacity > max_capacity)
|
||||
capacity = max_capacity;
|
||||
|
||||
arch_set_thermal_pressure(policy->cpus, max_capacity - capacity);
|
||||
|
||||
/*
|
||||
* In the unlikely case policy is unregistered do not enable
|
||||
* polling or h/w interrupt
|
||||
*/
|
||||
mutex_lock(&data->throttle_lock);
|
||||
if (data->cancel_throttle)
|
||||
goto out;
|
||||
|
||||
/*
|
||||
* If h/w throttled frequency is higher than what cpufreq has requested
|
||||
* for, then stop polling and switch back to interrupt mechanism.
|
||||
*/
|
||||
if (throttled_freq >= qcom_cpufreq_hw_get(cpu))
|
||||
enable_irq(data->throttle_irq);
|
||||
else
|
||||
mod_delayed_work(system_highpri_wq, &data->throttle_work,
|
||||
msecs_to_jiffies(10));
|
||||
|
||||
out:
|
||||
mutex_unlock(&data->throttle_lock);
|
||||
}
|
||||
|
||||
static void qcom_lmh_dcvs_poll(struct work_struct *work)
|
||||
{
|
||||
struct qcom_cpufreq_data *data;
|
||||
|
||||
data = container_of(work, struct qcom_cpufreq_data, throttle_work.work);
|
||||
qcom_lmh_dcvs_notify(data);
|
||||
}
|
||||
|
||||
static irqreturn_t qcom_lmh_dcvs_handle_irq(int irq, void *data)
|
||||
{
|
||||
struct qcom_cpufreq_data *c_data = data;
|
||||
|
||||
/* Disable interrupt and enable polling */
|
||||
disable_irq_nosync(c_data->throttle_irq);
|
||||
qcom_lmh_dcvs_notify(c_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct qcom_cpufreq_soc_data qcom_soc_data = {
|
||||
.reg_enable = 0x0,
|
||||
.reg_freq_lut = 0x110,
|
||||
.reg_volt_lut = 0x114,
|
||||
.reg_current_vote = 0x704,
|
||||
.reg_perf_state = 0x920,
|
||||
.lut_row_size = 32,
|
||||
};
|
||||
@ -274,6 +371,51 @@ static const struct of_device_id qcom_cpufreq_hw_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match);
|
||||
|
||||
static int qcom_cpufreq_hw_lmh_init(struct cpufreq_policy *policy, int index)
|
||||
{
|
||||
struct qcom_cpufreq_data *data = policy->driver_data;
|
||||
struct platform_device *pdev = cpufreq_get_driver_data();
|
||||
char irq_name[15];
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* Look for LMh interrupt. If no interrupt line is specified /
|
||||
* if there is an error, allow cpufreq to be enabled as usual.
|
||||
*/
|
||||
data->throttle_irq = platform_get_irq(pdev, index);
|
||||
if (data->throttle_irq <= 0)
|
||||
return data->throttle_irq == -EPROBE_DEFER ? -EPROBE_DEFER : 0;
|
||||
|
||||
data->cancel_throttle = false;
|
||||
data->policy = policy;
|
||||
|
||||
mutex_init(&data->throttle_lock);
|
||||
INIT_DEFERRABLE_WORK(&data->throttle_work, qcom_lmh_dcvs_poll);
|
||||
|
||||
snprintf(irq_name, sizeof(irq_name), "dcvsh-irq-%u", policy->cpu);
|
||||
ret = request_threaded_irq(data->throttle_irq, NULL, qcom_lmh_dcvs_handle_irq,
|
||||
IRQF_ONESHOT, irq_name, data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Error registering %s: %d\n", irq_name, ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void qcom_cpufreq_hw_lmh_exit(struct qcom_cpufreq_data *data)
|
||||
{
|
||||
if (data->throttle_irq <= 0)
|
||||
return;
|
||||
|
||||
mutex_lock(&data->throttle_lock);
|
||||
data->cancel_throttle = true;
|
||||
mutex_unlock(&data->throttle_lock);
|
||||
|
||||
cancel_delayed_work_sync(&data->throttle_work);
|
||||
free_irq(data->throttle_irq, data);
|
||||
}
|
||||
|
||||
static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct platform_device *pdev = cpufreq_get_driver_data();
|
||||
@ -348,6 +490,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
||||
}
|
||||
|
||||
policy->driver_data = data;
|
||||
policy->dvfs_possible_from_any_cpu = true;
|
||||
|
||||
ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy);
|
||||
if (ret) {
|
||||
@ -362,14 +505,16 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
||||
goto error;
|
||||
}
|
||||
|
||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
||||
|
||||
if (policy_has_boost_freq(policy)) {
|
||||
ret = cpufreq_enable_boost_support();
|
||||
if (ret)
|
||||
dev_warn(cpu_dev, "failed to enable boost: %d\n", ret);
|
||||
}
|
||||
|
||||
ret = qcom_cpufreq_hw_lmh_init(policy, index);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
return 0;
|
||||
error:
|
||||
kfree(data);
|
||||
@ -389,6 +534,7 @@ static int qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy)
|
||||
|
||||
dev_pm_opp_remove_all_dynamic(cpu_dev);
|
||||
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
|
||||
qcom_cpufreq_hw_lmh_exit(data);
|
||||
kfree(policy->freq_table);
|
||||
kfree(data);
|
||||
iounmap(base);
|
||||
@ -412,6 +558,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
|
||||
.get = qcom_cpufreq_hw_get,
|
||||
.init = qcom_cpufreq_hw_cpu_init,
|
||||
.exit = qcom_cpufreq_hw_cpu_exit,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.fast_switch = qcom_cpufreq_hw_fast_switch,
|
||||
.name = "qcom-cpufreq-hw",
|
||||
.attr = qcom_cpufreq_hw_attr,
|
||||
|
@ -22,7 +22,9 @@
|
||||
|
||||
struct scmi_data {
|
||||
int domain_id;
|
||||
int nr_opp;
|
||||
struct device *cpu_dev;
|
||||
cpumask_var_t opp_shared_cpus;
|
||||
};
|
||||
|
||||
static struct scmi_protocol_handle *ph;
|
||||
@ -123,9 +125,6 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
struct device *cpu_dev;
|
||||
struct scmi_data *priv;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
|
||||
cpumask_var_t opp_shared_cpus;
|
||||
bool power_scale_mw;
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
@ -133,9 +132,15 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!zalloc_cpumask_var(&opp_shared_cpus, GFP_KERNEL))
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!zalloc_cpumask_var(&priv->opp_shared_cpus, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_priv;
|
||||
}
|
||||
|
||||
/* Obtain CPUs that share SCMI performance controls */
|
||||
ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
|
||||
if (ret) {
|
||||
@ -148,14 +153,14 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
* The OPP 'sharing cpus' info may come from DT through an empty opp
|
||||
* table and opp-shared.
|
||||
*/
|
||||
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, opp_shared_cpus);
|
||||
if (ret || !cpumask_weight(opp_shared_cpus)) {
|
||||
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->opp_shared_cpus);
|
||||
if (ret || !cpumask_weight(priv->opp_shared_cpus)) {
|
||||
/*
|
||||
* Either opp-table is not set or no opp-shared was found.
|
||||
* Use the CPU mask from SCMI to designate CPUs sharing an OPP
|
||||
* table.
|
||||
*/
|
||||
cpumask_copy(opp_shared_cpus, policy->cpus);
|
||||
cpumask_copy(priv->opp_shared_cpus, policy->cpus);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -180,7 +185,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
goto out_free_opp;
|
||||
}
|
||||
|
||||
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, opp_shared_cpus);
|
||||
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, priv->opp_shared_cpus);
|
||||
if (ret) {
|
||||
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
|
||||
__func__, ret);
|
||||
@ -188,21 +193,13 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
goto out_free_opp;
|
||||
}
|
||||
|
||||
power_scale_mw = perf_ops->power_scale_mw_get(ph);
|
||||
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb,
|
||||
opp_shared_cpus, power_scale_mw);
|
||||
}
|
||||
|
||||
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto out_free_opp;
|
||||
priv->nr_opp = nr_opp;
|
||||
}
|
||||
|
||||
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_priv;
|
||||
goto out_free_opp;
|
||||
}
|
||||
|
||||
priv->cpu_dev = cpu_dev;
|
||||
@ -223,17 +220,16 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
policy->fast_switch_possible =
|
||||
perf_ops->fast_switch_possible(ph, cpu_dev);
|
||||
|
||||
free_cpumask_var(opp_shared_cpus);
|
||||
return 0;
|
||||
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
|
||||
out_free_opp:
|
||||
dev_pm_opp_remove_all_dynamic(cpu_dev);
|
||||
|
||||
out_free_cpumask:
|
||||
free_cpumask_var(opp_shared_cpus);
|
||||
free_cpumask_var(priv->opp_shared_cpus);
|
||||
|
||||
out_free_priv:
|
||||
kfree(priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -244,11 +240,33 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy)
|
||||
|
||||
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
|
||||
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
|
||||
free_cpumask_var(priv->opp_shared_cpus);
|
||||
kfree(priv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void scmi_cpufreq_register_em(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
|
||||
bool power_scale_mw = perf_ops->power_scale_mw_get(ph);
|
||||
struct scmi_data *priv = policy->driver_data;
|
||||
|
||||
/*
|
||||
* This callback will be called for each policy, but we don't need to
|
||||
* register with EM every time. Despite not being part of the same
|
||||
* policy, some CPUs may still share their perf-domains, and a CPU from
|
||||
* another policy may already have registered with EM on behalf of CPUs
|
||||
* of this policy.
|
||||
*/
|
||||
if (!priv->nr_opp)
|
||||
return;
|
||||
|
||||
em_dev_register_perf_domain(get_cpu_device(policy->cpu), priv->nr_opp,
|
||||
&em_cb, priv->opp_shared_cpus,
|
||||
power_scale_mw);
|
||||
}
|
||||
|
||||
static struct cpufreq_driver scmi_cpufreq_driver = {
|
||||
.name = "scmi",
|
||||
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
||||
@ -261,6 +279,7 @@ static struct cpufreq_driver scmi_cpufreq_driver = {
|
||||
.get = scmi_cpufreq_get_rate,
|
||||
.init = scmi_cpufreq_init,
|
||||
.exit = scmi_cpufreq_exit,
|
||||
.register_em = scmi_cpufreq_register_em,
|
||||
};
|
||||
|
||||
static int scmi_cpufreq_probe(struct scmi_device *sdev)
|
||||
|
@ -163,8 +163,6 @@ static int scpi_cpufreq_init(struct cpufreq_policy *policy)
|
||||
|
||||
policy->fast_switch_possible = false;
|
||||
|
||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_cpufreq_table:
|
||||
@ -200,6 +198,7 @@ static struct cpufreq_driver scpi_cpufreq_driver = {
|
||||
.init = scpi_cpufreq_init,
|
||||
.exit = scpi_cpufreq_exit,
|
||||
.target_index = scpi_cpufreq_set_target,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
};
|
||||
|
||||
static int scpi_cpufreq_probe(struct platform_device *pdev)
|
||||
|
@ -145,16 +145,6 @@ static int sh_cpufreq_cpu_exit(struct cpufreq_policy *policy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sh_cpufreq_cpu_ready(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct device *dev = get_cpu_device(policy->cpu);
|
||||
|
||||
dev_info(dev, "CPU Frequencies - Minimum %u.%03u MHz, "
|
||||
"Maximum %u.%03u MHz.\n",
|
||||
policy->min / 1000, policy->min % 1000,
|
||||
policy->max / 1000, policy->max % 1000);
|
||||
}
|
||||
|
||||
static struct cpufreq_driver sh_cpufreq_driver = {
|
||||
.name = "sh",
|
||||
.flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
|
||||
@ -163,7 +153,6 @@ static struct cpufreq_driver sh_cpufreq_driver = {
|
||||
.verify = sh_cpufreq_verify,
|
||||
.init = sh_cpufreq_cpu_init,
|
||||
.exit = sh_cpufreq_cpu_exit,
|
||||
.ready = sh_cpufreq_cpu_ready,
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/cpu_cooling.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
@ -47,7 +46,6 @@ static bool bL_switching_enabled;
|
||||
#define ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
|
||||
#define VIRT_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq >> 1 : freq)
|
||||
|
||||
static struct thermal_cooling_device *cdev[MAX_CLUSTERS];
|
||||
static struct clk *clk[MAX_CLUSTERS];
|
||||
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
|
||||
static atomic_t cluster_usage[MAX_CLUSTERS + 1];
|
||||
@ -442,8 +440,6 @@ static int ve_spc_cpufreq_init(struct cpufreq_policy *policy)
|
||||
policy->freq_table = freq_table[cur_cluster];
|
||||
policy->cpuinfo.transition_latency = 1000000; /* 1 ms */
|
||||
|
||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
||||
|
||||
if (is_bL_switching_enabled())
|
||||
per_cpu(cpu_last_req_freq, policy->cpu) =
|
||||
clk_get_cpu_rate(policy->cpu);
|
||||
@ -457,11 +453,6 @@ static int ve_spc_cpufreq_exit(struct cpufreq_policy *policy)
|
||||
struct device *cpu_dev;
|
||||
int cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
if (cur_cluster < MAX_CLUSTERS) {
|
||||
cpufreq_cooling_unregister(cdev[cur_cluster]);
|
||||
cdev[cur_cluster] = NULL;
|
||||
}
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
pr_err("%s: failed to get cpu%d device\n", __func__,
|
||||
@ -473,17 +464,6 @@ static int ve_spc_cpufreq_exit(struct cpufreq_policy *policy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ve_spc_cpufreq_ready(struct cpufreq_policy *policy)
|
||||
{
|
||||
int cur_cluster = cpu_to_cluster(policy->cpu);
|
||||
|
||||
/* Do not register a cpu_cooling device if we are in IKS mode */
|
||||
if (cur_cluster >= MAX_CLUSTERS)
|
||||
return;
|
||||
|
||||
cdev[cur_cluster] = of_cpufreq_cooling_register(policy);
|
||||
}
|
||||
|
||||
static struct cpufreq_driver ve_spc_cpufreq_driver = {
|
||||
.name = "vexpress-spc",
|
||||
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
||||
@ -493,7 +473,7 @@ static struct cpufreq_driver ve_spc_cpufreq_driver = {
|
||||
.get = ve_spc_cpufreq_get_rate,
|
||||
.init = ve_spc_cpufreq_init,
|
||||
.exit = ve_spc_cpufreq_exit,
|
||||
.ready = ve_spc_cpufreq_ready,
|
||||
.register_em = cpufreq_register_em_with_opp,
|
||||
.attr = cpufreq_generic_attr,
|
||||
};
|
||||
|
||||
@ -553,6 +533,9 @@ static int ve_spc_cpufreq_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < MAX_CLUSTERS; i++)
|
||||
mutex_init(&cluster_lock[i]);
|
||||
|
||||
if (!is_bL_switching_enabled())
|
||||
ve_spc_cpufreq_driver.flags |= CPUFREQ_IS_COOLING_DEV;
|
||||
|
||||
ret = cpufreq_register_driver(&ve_spc_cpufreq_driver);
|
||||
if (ret) {
|
||||
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
|
||||
|
@ -9,10 +9,14 @@
|
||||
#define _LINUX_CPUFREQ_H
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/kobject.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/pm_opp.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sysfs.h>
|
||||
@ -365,14 +369,17 @@ struct cpufreq_driver {
|
||||
int (*suspend)(struct cpufreq_policy *policy);
|
||||
int (*resume)(struct cpufreq_policy *policy);
|
||||
|
||||
/* Will be called after the driver is fully initialized */
|
||||
void (*ready)(struct cpufreq_policy *policy);
|
||||
|
||||
struct freq_attr **attr;
|
||||
|
||||
/* platform specific boost support code */
|
||||
bool boost_enabled;
|
||||
int (*set_boost)(struct cpufreq_policy *policy, int state);
|
||||
|
||||
/*
|
||||
* Set by drivers that want to register with the energy model after the
|
||||
* policy is properly initialized, but before the governor is started.
|
||||
*/
|
||||
void (*register_em)(struct cpufreq_policy *policy);
|
||||
};
|
||||
|
||||
/* flags */
|
||||
@ -995,6 +1002,55 @@ static inline int cpufreq_table_count_valid_entries(const struct cpufreq_policy
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static inline int parse_perf_domain(int cpu, const char *list_name,
|
||||
const char *cell_name)
|
||||
{
|
||||
struct device_node *cpu_np;
|
||||
struct of_phandle_args args;
|
||||
int ret;
|
||||
|
||||
cpu_np = of_cpu_device_node_get(cpu);
|
||||
if (!cpu_np)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_parse_phandle_with_args(cpu_np, list_name, cell_name, 0,
|
||||
&args);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
of_node_put(cpu_np);
|
||||
|
||||
return args.args[0];
|
||||
}
|
||||
|
||||
static inline int of_perf_domain_get_sharing_cpumask(int pcpu, const char *list_name,
|
||||
const char *cell_name, struct cpumask *cpumask)
|
||||
{
|
||||
int target_idx;
|
||||
int cpu, ret;
|
||||
|
||||
ret = parse_perf_domain(pcpu, list_name, cell_name);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
target_idx = ret;
|
||||
cpumask_set_cpu(pcpu, cpumask);
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
if (cpu == pcpu)
|
||||
continue;
|
||||
|
||||
ret = parse_perf_domain(pcpu, list_name, cell_name);
|
||||
if (ret < 0)
|
||||
continue;
|
||||
|
||||
if (target_idx == ret)
|
||||
cpumask_set_cpu(cpu, cpumask);
|
||||
}
|
||||
|
||||
return target_idx;
|
||||
}
|
||||
#else
|
||||
static inline int cpufreq_boost_trigger_state(int state)
|
||||
{
|
||||
@ -1014,6 +1070,12 @@ static inline bool policy_has_boost_freq(struct cpufreq_policy *policy)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int of_perf_domain_get_sharing_cpumask(int pcpu, const char *list_name,
|
||||
const char *cell_name, struct cpumask *cpumask)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL)
|
||||
@ -1035,7 +1097,6 @@ void arch_set_freq_scale(const struct cpumask *cpus,
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* the following are really really optional */
|
||||
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
|
||||
extern struct freq_attr cpufreq_freq_attr_scaling_boost_freqs;
|
||||
@ -1046,4 +1107,10 @@ unsigned int cpufreq_generic_get(unsigned int cpu);
|
||||
void cpufreq_generic_init(struct cpufreq_policy *policy,
|
||||
struct cpufreq_frequency_table *table,
|
||||
unsigned int transition_latency);
|
||||
|
||||
static inline void cpufreq_register_em_with_opp(struct cpufreq_policy *policy)
|
||||
{
|
||||
dev_pm_opp_of_register_em(get_cpu_device(policy->cpu),
|
||||
policy->related_cpus);
|
||||
}
|
||||
#endif /* _LINUX_CPUFREQ_H */
|
||||
|
Loading…
Reference in New Issue
Block a user