mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-16 13:34:30 +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
|
.resume - A pointer to a per-policy resume function which is called
|
||||||
with interrupts disabled and _before_ the governor is started again.
|
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
|
.attr - A pointer to a NULL-terminated list of "struct freq_attr" which
|
||||||
allow to export values to sysfs.
|
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恢复函数的指针,该函数在关中断且在调节器再一次开始前被
|
.resume - 一个指向per-policy恢复函数的指针,该函数在关中断且在调节器再一次开始前被
|
||||||
调用。
|
调用。
|
||||||
|
|
||||||
.ready - 一个指向per-policy准备函数的指针,该函数在策略完全初始化之后被调用。
|
|
||||||
|
|
||||||
.attr - 一个指向NULL结尾的"struct freq_attr"列表的指针,该函数允许导出值到
|
.attr - 一个指向NULL结尾的"struct freq_attr"列表的指针,该函数允许导出值到
|
||||||
sysfs。
|
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;
|
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)
|
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)
|
for_each_cpu(cpu, cpus)
|
||||||
WRITE_ONCE(per_cpu(thermal_pressure, cpu), th_pressure);
|
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,
|
static ssize_t cpu_capacity_show(struct device *dev,
|
||||||
struct device_attribute *attr,
|
struct device_attribute *attr,
|
||||||
|
@ -133,6 +133,18 @@ config ARM_MEDIATEK_CPUFREQ
|
|||||||
help
|
help
|
||||||
This adds the CPUFreq driver support for MediaTek SoCs.
|
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
|
config ARM_OMAP2PLUS_CPUFREQ
|
||||||
bool "TI OMAP2+"
|
bool "TI OMAP2+"
|
||||||
depends on ARCH_OMAP2PLUS
|
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_IMX_CPUFREQ_DT) += imx-cpufreq-dt.o
|
||||||
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
|
obj-$(CONFIG_ARM_KIRKWOOD_CPUFREQ) += kirkwood-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_MEDIATEK_CPUFREQ) += mediatek-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_MACH_MVEBU_V7) += mvebu-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
|
obj-$(CONFIG_ARM_OMAP2PLUS_CPUFREQ) += omap-cpufreq.o
|
||||||
obj-$(CONFIG_ARM_PXA2xx_CPUFREQ) += pxa2xx-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->fast_switch_possible = !acpi_pstate_strict &&
|
||||||
!(policy_is_shared(policy) && policy->shared_type != CPUFREQ_SHARED_TYPE_ANY);
|
!(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;
|
return result;
|
||||||
|
|
||||||
err_unreg:
|
err_unreg:
|
||||||
@ -918,16 +921,6 @@ static int acpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
|
|||||||
return 0;
|
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)
|
static int acpi_cpufreq_resume(struct cpufreq_policy *policy)
|
||||||
{
|
{
|
||||||
struct acpi_cpufreq_data *data = policy->driver_data;
|
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,
|
.bios_limit = acpi_processor_get_bios_limit,
|
||||||
.init = acpi_cpufreq_cpu_init,
|
.init = acpi_cpufreq_cpu_init,
|
||||||
.exit = acpi_cpufreq_cpu_exit,
|
.exit = acpi_cpufreq_cpu_exit,
|
||||||
.ready = acpi_cpufreq_cpu_ready,
|
|
||||||
.resume = acpi_cpufreq_resume,
|
.resume = acpi_cpufreq_resume,
|
||||||
.name = "acpi-cpufreq",
|
.name = "acpi-cpufreq",
|
||||||
.attr = acpi_cpufreq_attr,
|
.attr = acpi_cpufreq_attr,
|
||||||
|
@ -137,11 +137,15 @@ static const struct of_device_id blocklist[] __initconst = {
|
|||||||
{ .compatible = "qcom,apq8096", },
|
{ .compatible = "qcom,apq8096", },
|
||||||
{ .compatible = "qcom,msm8996", },
|
{ .compatible = "qcom,msm8996", },
|
||||||
{ .compatible = "qcom,qcs404", },
|
{ .compatible = "qcom,qcs404", },
|
||||||
|
{ .compatible = "qcom,sa8155p" },
|
||||||
{ .compatible = "qcom,sc7180", },
|
{ .compatible = "qcom,sc7180", },
|
||||||
{ .compatible = "qcom,sc7280", },
|
{ .compatible = "qcom,sc7280", },
|
||||||
{ .compatible = "qcom,sc8180x", },
|
{ .compatible = "qcom,sc8180x", },
|
||||||
{ .compatible = "qcom,sdm845", },
|
{ .compatible = "qcom,sdm845", },
|
||||||
|
{ .compatible = "qcom,sm6350", },
|
||||||
{ .compatible = "qcom,sm8150", },
|
{ .compatible = "qcom,sm8150", },
|
||||||
|
{ .compatible = "qcom,sm8250", },
|
||||||
|
{ .compatible = "qcom,sm8350", },
|
||||||
|
|
||||||
{ .compatible = "st,stih407", },
|
{ .compatible = "st,stih407", },
|
||||||
{ .compatible = "st,stih410", },
|
{ .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;
|
cpufreq_dt_attr[1] = &cpufreq_freq_attr_scaling_boost_freqs;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_clk_put:
|
out_clk_put:
|
||||||
@ -184,6 +182,7 @@ static struct cpufreq_driver dt_cpufreq_driver = {
|
|||||||
.exit = cpufreq_exit,
|
.exit = cpufreq_exit,
|
||||||
.online = cpufreq_online,
|
.online = cpufreq_online,
|
||||||
.offline = cpufreq_offline,
|
.offline = cpufreq_offline,
|
||||||
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
.name = "cpufreq-dt",
|
.name = "cpufreq-dt",
|
||||||
.attr = cpufreq_dt_attr,
|
.attr = cpufreq_dt_attr,
|
||||||
.suspend = cpufreq_generic_suspend,
|
.suspend = cpufreq_generic_suspend,
|
||||||
|
@ -1491,6 +1491,19 @@ static int cpufreq_online(unsigned int cpu)
|
|||||||
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
write_lock_irqsave(&cpufreq_driver_lock, flags);
|
||||||
list_add(&policy->policy_list, &cpufreq_policy_list);
|
list_add(&policy->policy_list, &cpufreq_policy_list);
|
||||||
write_unlock_irqrestore(&cpufreq_driver_lock, flags);
|
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);
|
ret = cpufreq_init_policy(policy);
|
||||||
@ -1504,10 +1517,6 @@ static int cpufreq_online(unsigned int cpu)
|
|||||||
|
|
||||||
kobject_uevent(&policy->kobj, KOBJ_ADD);
|
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))
|
if (cpufreq_thermal_control_enabled(cpufreq_driver))
|
||||||
policy->cdev = of_cpufreq_cooling_register(policy);
|
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;
|
policy->clk = clks[ARM].clk;
|
||||||
cpufreq_generic_init(policy, freq_table, transition_latency);
|
cpufreq_generic_init(policy, freq_table, transition_latency);
|
||||||
policy->suspend_freq = max_freq;
|
policy->suspend_freq = max_freq;
|
||||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -204,6 +203,7 @@ static struct cpufreq_driver imx6q_cpufreq_driver = {
|
|||||||
.target_index = imx6q_set_target,
|
.target_index = imx6q_set_target,
|
||||||
.get = cpufreq_generic_get,
|
.get = cpufreq_generic_get,
|
||||||
.init = imx6q_cpufreq_init,
|
.init = imx6q_cpufreq_init,
|
||||||
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
.name = "imx6q-cpufreq",
|
.name = "imx6q-cpufreq",
|
||||||
.attr = cpufreq_generic_attr,
|
.attr = cpufreq_generic_attr,
|
||||||
.suspend = cpufreq_generic_suspend,
|
.suspend = cpufreq_generic_suspend,
|
||||||
|
@ -32,7 +32,6 @@
|
|||||||
#include <asm/cpu_device_id.h>
|
#include <asm/cpu_device_id.h>
|
||||||
#include <asm/cpufeature.h>
|
#include <asm/cpufeature.h>
|
||||||
#include <asm/intel-family.h>
|
#include <asm/intel-family.h>
|
||||||
#include "../drivers/thermal/intel/thermal_interrupt.h"
|
|
||||||
|
|
||||||
#define INTEL_PSTATE_SAMPLING_INTERVAL (10 * NSEC_PER_MSEC)
|
#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
|
* @sched_flags: Store scheduler flags for possible cross CPU update
|
||||||
* @hwp_boost_min: Last HWP boosted min performance
|
* @hwp_boost_min: Last HWP boosted min performance
|
||||||
* @suspended: Whether or not the driver has been suspended.
|
* @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.
|
* This structure stores per CPU instance data for all CPUs.
|
||||||
*/
|
*/
|
||||||
@ -259,7 +257,6 @@ struct cpudata {
|
|||||||
unsigned int sched_flags;
|
unsigned int sched_flags;
|
||||||
u32 hwp_boost_min;
|
u32 hwp_boost_min;
|
||||||
bool suspended;
|
bool suspended;
|
||||||
struct delayed_work hwp_notify_work;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct cpudata **all_cpu_data;
|
static struct cpudata **all_cpu_data;
|
||||||
@ -1628,40 +1625,6 @@ static void intel_pstate_sysfs_hide_hwp_dynamic_boost(void)
|
|||||||
|
|
||||||
/************************** sysfs end ************************/
|
/************************** 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)
|
static void intel_pstate_hwp_enable(struct cpudata *cpudata)
|
||||||
{
|
{
|
||||||
/* First disable HWP notification interrupt as we don't process them */
|
/* 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);
|
wrmsrl_on_cpu(cpudata->cpu, MSR_PM_ENABLE, 0x1);
|
||||||
if (cpudata->epp_default == -EINVAL)
|
if (cpudata->epp_default == -EINVAL)
|
||||||
cpudata->epp_default = intel_pstate_get_epp(cpudata, 0);
|
cpudata->epp_default = intel_pstate_get_epp(cpudata, 0);
|
||||||
|
|
||||||
intel_pstate_enable_hwp_interrupt(cpudata);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int atom_get_min_pstate(void)
|
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->driver_data = info;
|
||||||
policy->clk = info->cpu_clk;
|
policy->clk = info->cpu_clk;
|
||||||
|
|
||||||
dev_pm_opp_of_register_em(info->cpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,6 +469,7 @@ static struct cpufreq_driver mtk_cpufreq_driver = {
|
|||||||
.get = cpufreq_generic_get,
|
.get = cpufreq_generic_get,
|
||||||
.init = mtk_cpufreq_init,
|
.init = mtk_cpufreq_init,
|
||||||
.exit = mtk_cpufreq_exit,
|
.exit = mtk_cpufreq_exit,
|
||||||
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
.name = "mtk-cpufreq",
|
.name = "mtk-cpufreq",
|
||||||
.attr = cpufreq_generic_attr,
|
.attr = cpufreq_generic_attr,
|
||||||
};
|
};
|
||||||
|
@ -131,7 +131,6 @@ static int omap_cpu_init(struct cpufreq_policy *policy)
|
|||||||
|
|
||||||
/* FIXME: what's the actual transition time? */
|
/* FIXME: what's the actual transition time? */
|
||||||
cpufreq_generic_init(policy, freq_table, 300 * 1000);
|
cpufreq_generic_init(policy, freq_table, 300 * 1000);
|
||||||
dev_pm_opp_of_register_em(mpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -150,6 +149,7 @@ static struct cpufreq_driver omap_driver = {
|
|||||||
.get = cpufreq_generic_get,
|
.get = cpufreq_generic_get,
|
||||||
.init = omap_cpu_init,
|
.init = omap_cpu_init,
|
||||||
.exit = omap_cpu_exit,
|
.exit = omap_cpu_exit,
|
||||||
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
.name = "omap",
|
.name = "omap",
|
||||||
.attr = cpufreq_generic_attr,
|
.attr = cpufreq_generic_attr,
|
||||||
};
|
};
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interconnect.h>
|
#include <linux/interconnect.h>
|
||||||
|
#include <linux/interrupt.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/of_address.h>
|
#include <linux/of_address.h>
|
||||||
#include <linux/of_platform.h>
|
#include <linux/of_platform.h>
|
||||||
#include <linux/pm_opp.h>
|
#include <linux/pm_opp.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#define LUT_MAX_ENTRIES 40U
|
#define LUT_MAX_ENTRIES 40U
|
||||||
#define LUT_SRC GENMASK(31, 30)
|
#define LUT_SRC GENMASK(31, 30)
|
||||||
@ -22,10 +24,13 @@
|
|||||||
#define CLK_HW_DIV 2
|
#define CLK_HW_DIV 2
|
||||||
#define LUT_TURBO_IND 1
|
#define LUT_TURBO_IND 1
|
||||||
|
|
||||||
|
#define HZ_PER_KHZ 1000
|
||||||
|
|
||||||
struct qcom_cpufreq_soc_data {
|
struct qcom_cpufreq_soc_data {
|
||||||
u32 reg_enable;
|
u32 reg_enable;
|
||||||
u32 reg_freq_lut;
|
u32 reg_freq_lut;
|
||||||
u32 reg_volt_lut;
|
u32 reg_volt_lut;
|
||||||
|
u32 reg_current_vote;
|
||||||
u32 reg_perf_state;
|
u32 reg_perf_state;
|
||||||
u8 lut_row_size;
|
u8 lut_row_size;
|
||||||
};
|
};
|
||||||
@ -34,6 +39,16 @@ struct qcom_cpufreq_data {
|
|||||||
void __iomem *base;
|
void __iomem *base;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
const struct qcom_cpufreq_soc_data *soc_data;
|
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;
|
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 = {
|
static const struct qcom_cpufreq_soc_data qcom_soc_data = {
|
||||||
.reg_enable = 0x0,
|
.reg_enable = 0x0,
|
||||||
.reg_freq_lut = 0x110,
|
.reg_freq_lut = 0x110,
|
||||||
.reg_volt_lut = 0x114,
|
.reg_volt_lut = 0x114,
|
||||||
|
.reg_current_vote = 0x704,
|
||||||
.reg_perf_state = 0x920,
|
.reg_perf_state = 0x920,
|
||||||
.lut_row_size = 32,
|
.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);
|
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)
|
static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = cpufreq_get_driver_data();
|
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->driver_data = data;
|
||||||
|
policy->dvfs_possible_from_any_cpu = true;
|
||||||
|
|
||||||
ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy);
|
ret = qcom_cpufreq_hw_read_lut(cpu_dev, policy);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
@ -362,14 +505,16 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
if (policy_has_boost_freq(policy)) {
|
if (policy_has_boost_freq(policy)) {
|
||||||
ret = cpufreq_enable_boost_support();
|
ret = cpufreq_enable_boost_support();
|
||||||
if (ret)
|
if (ret)
|
||||||
dev_warn(cpu_dev, "failed to enable boost: %d\n", 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;
|
return 0;
|
||||||
error:
|
error:
|
||||||
kfree(data);
|
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_remove_all_dynamic(cpu_dev);
|
||||||
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
|
dev_pm_opp_of_cpumask_remove_table(policy->related_cpus);
|
||||||
|
qcom_cpufreq_hw_lmh_exit(data);
|
||||||
kfree(policy->freq_table);
|
kfree(policy->freq_table);
|
||||||
kfree(data);
|
kfree(data);
|
||||||
iounmap(base);
|
iounmap(base);
|
||||||
@ -412,6 +558,7 @@ static struct cpufreq_driver cpufreq_qcom_hw_driver = {
|
|||||||
.get = qcom_cpufreq_hw_get,
|
.get = qcom_cpufreq_hw_get,
|
||||||
.init = qcom_cpufreq_hw_cpu_init,
|
.init = qcom_cpufreq_hw_cpu_init,
|
||||||
.exit = qcom_cpufreq_hw_cpu_exit,
|
.exit = qcom_cpufreq_hw_cpu_exit,
|
||||||
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
.fast_switch = qcom_cpufreq_hw_fast_switch,
|
.fast_switch = qcom_cpufreq_hw_fast_switch,
|
||||||
.name = "qcom-cpufreq-hw",
|
.name = "qcom-cpufreq-hw",
|
||||||
.attr = qcom_cpufreq_hw_attr,
|
.attr = qcom_cpufreq_hw_attr,
|
||||||
|
@ -22,7 +22,9 @@
|
|||||||
|
|
||||||
struct scmi_data {
|
struct scmi_data {
|
||||||
int domain_id;
|
int domain_id;
|
||||||
|
int nr_opp;
|
||||||
struct device *cpu_dev;
|
struct device *cpu_dev;
|
||||||
|
cpumask_var_t opp_shared_cpus;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct scmi_protocol_handle *ph;
|
static struct scmi_protocol_handle *ph;
|
||||||
@ -123,9 +125,6 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
|||||||
struct device *cpu_dev;
|
struct device *cpu_dev;
|
||||||
struct scmi_data *priv;
|
struct scmi_data *priv;
|
||||||
struct cpufreq_frequency_table *freq_table;
|
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);
|
cpu_dev = get_cpu_device(policy->cpu);
|
||||||
if (!cpu_dev) {
|
if (!cpu_dev) {
|
||||||
@ -133,9 +132,15 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!zalloc_cpumask_var(&opp_shared_cpus, GFP_KERNEL))
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
return -ENOMEM;
|
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 */
|
/* Obtain CPUs that share SCMI performance controls */
|
||||||
ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
|
ret = scmi_get_sharing_cpus(cpu_dev, policy->cpus);
|
||||||
if (ret) {
|
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
|
* The OPP 'sharing cpus' info may come from DT through an empty opp
|
||||||
* table and opp-shared.
|
* table and opp-shared.
|
||||||
*/
|
*/
|
||||||
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, opp_shared_cpus);
|
ret = dev_pm_opp_of_get_sharing_cpus(cpu_dev, priv->opp_shared_cpus);
|
||||||
if (ret || !cpumask_weight(opp_shared_cpus)) {
|
if (ret || !cpumask_weight(priv->opp_shared_cpus)) {
|
||||||
/*
|
/*
|
||||||
* Either opp-table is not set or no opp-shared was found.
|
* Either opp-table is not set or no opp-shared was found.
|
||||||
* Use the CPU mask from SCMI to designate CPUs sharing an OPP
|
* Use the CPU mask from SCMI to designate CPUs sharing an OPP
|
||||||
* table.
|
* 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;
|
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) {
|
if (ret) {
|
||||||
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
|
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n",
|
||||||
__func__, ret);
|
__func__, ret);
|
||||||
@ -188,21 +193,13 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
|||||||
goto out_free_opp;
|
goto out_free_opp;
|
||||||
}
|
}
|
||||||
|
|
||||||
power_scale_mw = perf_ops->power_scale_mw_get(ph);
|
priv->nr_opp = nr_opp;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", 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;
|
priv->cpu_dev = cpu_dev;
|
||||||
@ -223,17 +220,16 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
|||||||
policy->fast_switch_possible =
|
policy->fast_switch_possible =
|
||||||
perf_ops->fast_switch_possible(ph, cpu_dev);
|
perf_ops->fast_switch_possible(ph, cpu_dev);
|
||||||
|
|
||||||
free_cpumask_var(opp_shared_cpus);
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_priv:
|
|
||||||
kfree(priv);
|
|
||||||
|
|
||||||
out_free_opp:
|
out_free_opp:
|
||||||
dev_pm_opp_remove_all_dynamic(cpu_dev);
|
dev_pm_opp_remove_all_dynamic(cpu_dev);
|
||||||
|
|
||||||
out_free_cpumask:
|
out_free_cpumask:
|
||||||
free_cpumask_var(opp_shared_cpus);
|
free_cpumask_var(priv->opp_shared_cpus);
|
||||||
|
|
||||||
|
out_free_priv:
|
||||||
|
kfree(priv);
|
||||||
|
|
||||||
return ret;
|
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_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
|
||||||
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
|
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
|
||||||
|
free_cpumask_var(priv->opp_shared_cpus);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
|
|
||||||
return 0;
|
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 = {
|
static struct cpufreq_driver scmi_cpufreq_driver = {
|
||||||
.name = "scmi",
|
.name = "scmi",
|
||||||
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
||||||
@ -261,6 +279,7 @@ static struct cpufreq_driver scmi_cpufreq_driver = {
|
|||||||
.get = scmi_cpufreq_get_rate,
|
.get = scmi_cpufreq_get_rate,
|
||||||
.init = scmi_cpufreq_init,
|
.init = scmi_cpufreq_init,
|
||||||
.exit = scmi_cpufreq_exit,
|
.exit = scmi_cpufreq_exit,
|
||||||
|
.register_em = scmi_cpufreq_register_em,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int scmi_cpufreq_probe(struct scmi_device *sdev)
|
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;
|
policy->fast_switch_possible = false;
|
||||||
|
|
||||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free_cpufreq_table:
|
out_free_cpufreq_table:
|
||||||
@ -200,6 +198,7 @@ static struct cpufreq_driver scpi_cpufreq_driver = {
|
|||||||
.init = scpi_cpufreq_init,
|
.init = scpi_cpufreq_init,
|
||||||
.exit = scpi_cpufreq_exit,
|
.exit = scpi_cpufreq_exit,
|
||||||
.target_index = scpi_cpufreq_set_target,
|
.target_index = scpi_cpufreq_set_target,
|
||||||
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int scpi_cpufreq_probe(struct platform_device *pdev)
|
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;
|
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 = {
|
static struct cpufreq_driver sh_cpufreq_driver = {
|
||||||
.name = "sh",
|
.name = "sh",
|
||||||
.flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
|
.flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING,
|
||||||
@ -163,7 +153,6 @@ static struct cpufreq_driver sh_cpufreq_driver = {
|
|||||||
.verify = sh_cpufreq_verify,
|
.verify = sh_cpufreq_verify,
|
||||||
.init = sh_cpufreq_cpu_init,
|
.init = sh_cpufreq_cpu_init,
|
||||||
.exit = sh_cpufreq_cpu_exit,
|
.exit = sh_cpufreq_cpu_exit,
|
||||||
.ready = sh_cpufreq_cpu_ready,
|
|
||||||
.attr = cpufreq_generic_attr,
|
.attr = cpufreq_generic_attr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include <linux/cpu.h>
|
#include <linux/cpu.h>
|
||||||
#include <linux/cpufreq.h>
|
#include <linux/cpufreq.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
#include <linux/cpu_cooling.h>
|
|
||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/mutex.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 ACTUAL_FREQ(cluster, freq) ((cluster == A7_CLUSTER) ? freq << 1 : freq)
|
||||||
#define VIRT_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 clk *clk[MAX_CLUSTERS];
|
||||||
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
|
static struct cpufreq_frequency_table *freq_table[MAX_CLUSTERS + 1];
|
||||||
static atomic_t cluster_usage[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->freq_table = freq_table[cur_cluster];
|
||||||
policy->cpuinfo.transition_latency = 1000000; /* 1 ms */
|
policy->cpuinfo.transition_latency = 1000000; /* 1 ms */
|
||||||
|
|
||||||
dev_pm_opp_of_register_em(cpu_dev, policy->cpus);
|
|
||||||
|
|
||||||
if (is_bL_switching_enabled())
|
if (is_bL_switching_enabled())
|
||||||
per_cpu(cpu_last_req_freq, policy->cpu) =
|
per_cpu(cpu_last_req_freq, policy->cpu) =
|
||||||
clk_get_cpu_rate(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;
|
struct device *cpu_dev;
|
||||||
int cur_cluster = cpu_to_cluster(policy->cpu);
|
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);
|
cpu_dev = get_cpu_device(policy->cpu);
|
||||||
if (!cpu_dev) {
|
if (!cpu_dev) {
|
||||||
pr_err("%s: failed to get cpu%d device\n", __func__,
|
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;
|
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 = {
|
static struct cpufreq_driver ve_spc_cpufreq_driver = {
|
||||||
.name = "vexpress-spc",
|
.name = "vexpress-spc",
|
||||||
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
.flags = CPUFREQ_HAVE_GOVERNOR_PER_POLICY |
|
||||||
@ -493,7 +473,7 @@ static struct cpufreq_driver ve_spc_cpufreq_driver = {
|
|||||||
.get = ve_spc_cpufreq_get_rate,
|
.get = ve_spc_cpufreq_get_rate,
|
||||||
.init = ve_spc_cpufreq_init,
|
.init = ve_spc_cpufreq_init,
|
||||||
.exit = ve_spc_cpufreq_exit,
|
.exit = ve_spc_cpufreq_exit,
|
||||||
.ready = ve_spc_cpufreq_ready,
|
.register_em = cpufreq_register_em_with_opp,
|
||||||
.attr = cpufreq_generic_attr,
|
.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++)
|
for (i = 0; i < MAX_CLUSTERS; i++)
|
||||||
mutex_init(&cluster_lock[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);
|
ret = cpufreq_register_driver(&ve_spc_cpufreq_driver);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
|
pr_info("%s: Failed registering platform driver: %s, err: %d\n",
|
||||||
|
@ -9,10 +9,14 @@
|
|||||||
#define _LINUX_CPUFREQ_H
|
#define _LINUX_CPUFREQ_H
|
||||||
|
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
|
#include <linux/cpu.h>
|
||||||
#include <linux/cpumask.h>
|
#include <linux/cpumask.h>
|
||||||
#include <linux/completion.h>
|
#include <linux/completion.h>
|
||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/notifier.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/pm_qos.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/sysfs.h>
|
#include <linux/sysfs.h>
|
||||||
@ -365,14 +369,17 @@ struct cpufreq_driver {
|
|||||||
int (*suspend)(struct cpufreq_policy *policy);
|
int (*suspend)(struct cpufreq_policy *policy);
|
||||||
int (*resume)(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;
|
struct freq_attr **attr;
|
||||||
|
|
||||||
/* platform specific boost support code */
|
/* platform specific boost support code */
|
||||||
bool boost_enabled;
|
bool boost_enabled;
|
||||||
int (*set_boost)(struct cpufreq_policy *policy, int state);
|
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 */
|
/* flags */
|
||||||
@ -995,6 +1002,55 @@ static inline int cpufreq_table_count_valid_entries(const struct cpufreq_policy
|
|||||||
|
|
||||||
return count;
|
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
|
#else
|
||||||
static inline int cpufreq_boost_trigger_state(int state)
|
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;
|
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
|
#endif
|
||||||
|
|
||||||
#if defined(CONFIG_ENERGY_MODEL) && defined(CONFIG_CPU_FREQ_GOV_SCHEDUTIL)
|
#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
|
#endif
|
||||||
|
|
||||||
/* the following are really really optional */
|
/* the following are really really optional */
|
||||||
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
|
extern struct freq_attr cpufreq_freq_attr_scaling_available_freqs;
|
||||||
extern struct freq_attr cpufreq_freq_attr_scaling_boost_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,
|
void cpufreq_generic_init(struct cpufreq_policy *policy,
|
||||||
struct cpufreq_frequency_table *table,
|
struct cpufreq_frequency_table *table,
|
||||||
unsigned int transition_latency);
|
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 */
|
#endif /* _LINUX_CPUFREQ_H */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user