Merge branch 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull ARM cpufreq updates for 5.11-rc1 from Viresh Kumar: "This contains the following updates: - Fix imx's NVMEM_IMX_OCOTP dependency (Arnd Bergmann). - Add support for mt8167 and blacklist mt8516 (Fabien Parent). - Some ->get() callback related cleanups to the tegra194 driver and some optimizations in tegra186 driver (Jon Hunter and Sumit Gupta). - Power scale improvements to arm_scmi driver (Lukasz Luba). - Add missing MODULE_DEVICE_TABLE and MODULE_ALIAS to several drivers (Pali Rohár). - Fix error path in mediatek driver (Qinglang Miao). - Fix memleak in ST's cpufreq driver (Yangtao Li)." * 'cpufreq/arm/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: (22 commits) cpufreq: arm_scmi: Discover the power scale in performance protocol firmware: arm_scmi: Add power_scale_mw_get() interface cpufreq: tegra194: Rename tegra194_get_speed_common function cpufreq: tegra194: Remove unnecessary frequency calculation cpufreq: tegra186: Simplify cluster information lookup cpufreq: tegra186: Fix sparse 'incorrect type in assignment' warning cpufreq: imx: fix NVMEM_IMX_OCOTP dependency cpufreq: vexpress-spc: Add missing MODULE_ALIAS cpufreq: scpi: Add missing MODULE_ALIAS cpufreq: loongson1: Add missing MODULE_ALIAS cpufreq: sun50i: Add missing MODULE_DEVICE_TABLE cpufreq: st: Add missing MODULE_DEVICE_TABLE cpufreq: qcom: Add missing MODULE_DEVICE_TABLE cpufreq: mediatek: Add missing MODULE_DEVICE_TABLE cpufreq: highbank: Add missing MODULE_DEVICE_TABLE cpufreq: ap806: Add missing MODULE_DEVICE_TABLE cpufreq: mediatek: add missing platform_driver_unregister() on error in mtk_cpufreq_driver_init cpufreq: tegra194: get consistent cpuinfo_cur_freq cpufreq: blacklist mt8516 in cpufreq-dt-platdev cpufreq: mediatek: Add support for mt8167 ...
This commit is contained in:
Коммит
30c768829a
|
@ -94,7 +94,7 @@ config ARM_IMX6Q_CPUFREQ
|
|||
tristate "Freescale i.MX6 cpufreq support"
|
||||
depends on ARCH_MXC
|
||||
depends on REGULATOR_ANATOP
|
||||
select NVMEM_IMX_OCOTP
|
||||
depends on NVMEM_IMX_OCOTP || COMPILE_TEST
|
||||
select PM_OPP
|
||||
help
|
||||
This adds cpufreq driver support for Freescale i.MX6 series SoCs.
|
||||
|
|
|
@ -204,6 +204,12 @@ static void __exit armada_8k_cpufreq_exit(void)
|
|||
}
|
||||
module_exit(armada_8k_cpufreq_exit);
|
||||
|
||||
static const struct of_device_id __maybe_unused armada_8k_cpufreq_of_match[] = {
|
||||
{ .compatible = "marvell,ap806-cpu-clock" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, armada_8k_cpufreq_of_match);
|
||||
|
||||
MODULE_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
|
||||
MODULE_DESCRIPTION("Armada 8K cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -119,10 +119,12 @@ static const struct of_device_id blacklist[] __initconst = {
|
|||
{ .compatible = "mediatek,mt2712", },
|
||||
{ .compatible = "mediatek,mt7622", },
|
||||
{ .compatible = "mediatek,mt7623", },
|
||||
{ .compatible = "mediatek,mt8167", },
|
||||
{ .compatible = "mediatek,mt817x", },
|
||||
{ .compatible = "mediatek,mt8173", },
|
||||
{ .compatible = "mediatek,mt8176", },
|
||||
{ .compatible = "mediatek,mt8183", },
|
||||
{ .compatible = "mediatek,mt8516", },
|
||||
|
||||
{ .compatible = "nvidia,tegra20", },
|
||||
{ .compatible = "nvidia,tegra30", },
|
||||
|
|
|
@ -101,6 +101,13 @@ out_put_node:
|
|||
}
|
||||
module_init(hb_cpufreq_driver_init);
|
||||
|
||||
static const struct of_device_id __maybe_unused hb_cpufreq_of_match[] = {
|
||||
{ .compatible = "calxeda,highbank" },
|
||||
{ .compatible = "calxeda,ecx-2000" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, hb_cpufreq_of_match);
|
||||
|
||||
MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>");
|
||||
MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -216,6 +216,7 @@ static struct platform_driver ls1x_cpufreq_platdrv = {
|
|||
|
||||
module_platform_driver(ls1x_cpufreq_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:ls1x-cpufreq");
|
||||
MODULE_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
|
||||
MODULE_DESCRIPTION("Loongson1 CPUFreq driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -532,6 +532,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
|
|||
{ .compatible = "mediatek,mt2712", },
|
||||
{ .compatible = "mediatek,mt7622", },
|
||||
{ .compatible = "mediatek,mt7623", },
|
||||
{ .compatible = "mediatek,mt8167", },
|
||||
{ .compatible = "mediatek,mt817x", },
|
||||
{ .compatible = "mediatek,mt8173", },
|
||||
{ .compatible = "mediatek,mt8176", },
|
||||
|
@ -540,6 +541,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
|
|||
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, mtk_cpufreq_machines);
|
||||
|
||||
static int __init mtk_cpufreq_driver_init(void)
|
||||
{
|
||||
|
@ -572,6 +574,7 @@ static int __init mtk_cpufreq_driver_init(void)
|
|||
pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
pr_err("failed to register mtk-cpufreq platform device\n");
|
||||
platform_driver_unregister(&mtk_cpufreq_platdrv);
|
||||
return PTR_ERR(pdev);
|
||||
}
|
||||
|
||||
|
|
|
@ -464,6 +464,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
|
|||
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, qcom_cpufreq_match_list);
|
||||
|
||||
/*
|
||||
* Since the driver depends on smem and nvmem drivers, which may
|
||||
|
|
|
@ -126,6 +126,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
|||
struct scmi_data *priv;
|
||||
struct cpufreq_frequency_table *freq_table;
|
||||
struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
|
||||
bool power_scale_mw;
|
||||
|
||||
cpu_dev = get_cpu_device(policy->cpu);
|
||||
if (!cpu_dev) {
|
||||
|
@ -189,7 +190,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
|
|||
policy->fast_switch_possible =
|
||||
handle->perf_ops->fast_switch_possible(handle, cpu_dev);
|
||||
|
||||
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus);
|
||||
power_scale_mw = handle->perf_ops->power_scale_mw_get(handle);
|
||||
em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus,
|
||||
power_scale_mw);
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -233,6 +233,7 @@ static struct platform_driver scpi_cpufreq_platdrv = {
|
|||
};
|
||||
module_platform_driver(scpi_cpufreq_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:scpi-cpufreq");
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
|
|
@ -223,7 +223,8 @@ use_defaults:
|
|||
opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
|
||||
if (IS_ERR(opp_table)) {
|
||||
dev_err(dev, "Failed to set supported hardware\n");
|
||||
return PTR_ERR(opp_table);
|
||||
ret = PTR_ERR(opp_table);
|
||||
goto err_put_prop_name;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
|
||||
|
@ -232,6 +233,10 @@ use_defaults:
|
|||
version[0], version[1], version[2]);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_prop_name:
|
||||
dev_pm_opp_put_prop_name(opp_table);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sti_cpufreq_fetch_syscon_registers(void)
|
||||
|
@ -292,6 +297,13 @@ register_cpufreq_dt:
|
|||
}
|
||||
module_init(sti_cpufreq_init);
|
||||
|
||||
static const struct of_device_id __maybe_unused sti_cpufreq_of_match[] = {
|
||||
{ .compatible = "st,stih407" },
|
||||
{ .compatible = "st,stih410" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sti_cpufreq_of_match);
|
||||
|
||||
MODULE_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
|
||||
MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
|
||||
MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");
|
||||
|
|
|
@ -167,6 +167,7 @@ static const struct of_device_id sun50i_cpufreq_match_list[] = {
|
|||
{ .compatible = "allwinner,sun50i-h6" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
|
||||
|
||||
static const struct of_device_id *sun50i_cpufreq_match_node(void)
|
||||
{
|
||||
|
|
|
@ -12,35 +12,52 @@
|
|||
#include <soc/tegra/bpmp.h>
|
||||
#include <soc/tegra/bpmp-abi.h>
|
||||
|
||||
#define EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4)
|
||||
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
|
||||
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
|
||||
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
|
||||
#define TEGRA186_NUM_CLUSTERS 2
|
||||
#define EDVD_OFFSET_A57(core) ((SZ_64K * 6) + (0x20 + (core) * 0x4))
|
||||
#define EDVD_OFFSET_DENVER(core) ((SZ_64K * 7) + (0x20 + (core) * 0x4))
|
||||
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0
|
||||
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff
|
||||
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16
|
||||
|
||||
struct tegra186_cpufreq_cluster_info {
|
||||
unsigned long offset;
|
||||
int cpus[4];
|
||||
struct tegra186_cpufreq_cpu {
|
||||
unsigned int bpmp_cluster_id;
|
||||
unsigned int edvd_offset;
|
||||
};
|
||||
|
||||
#define NO_CPU -1
|
||||
static const struct tegra186_cpufreq_cluster_info tegra186_clusters[] = {
|
||||
/* Denver cluster */
|
||||
static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
|
||||
/* CPU0 - A57 Cluster */
|
||||
{
|
||||
.offset = SZ_64K * 7,
|
||||
.cpus = { 1, 2, NO_CPU, NO_CPU },
|
||||
.bpmp_cluster_id = 0,
|
||||
},
|
||||
/* A57 cluster */
|
||||
{
|
||||
.offset = SZ_64K * 6,
|
||||
.cpus = { 0, 3, 4, 5 },
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(0)
|
||||
},
|
||||
/* CPU1 - Denver Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 0,
|
||||
.edvd_offset = EDVD_OFFSET_DENVER(0)
|
||||
},
|
||||
/* CPU2 - Denver Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 0,
|
||||
.edvd_offset = EDVD_OFFSET_DENVER(1)
|
||||
},
|
||||
/* CPU3 - A57 Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(1)
|
||||
},
|
||||
/* CPU4 - A57 Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(2)
|
||||
},
|
||||
/* CPU5 - A57 Cluster */
|
||||
{
|
||||
.bpmp_cluster_id = 1,
|
||||
.edvd_offset = EDVD_OFFSET_A57(3)
|
||||
},
|
||||
};
|
||||
|
||||
struct tegra186_cpufreq_cluster {
|
||||
const struct tegra186_cpufreq_cluster_info *info;
|
||||
struct cpufreq_frequency_table *table;
|
||||
u32 ref_clk_khz;
|
||||
u32 div;
|
||||
|
@ -48,36 +65,18 @@ struct tegra186_cpufreq_cluster {
|
|||
|
||||
struct tegra186_cpufreq_data {
|
||||
void __iomem *regs;
|
||||
|
||||
size_t num_clusters;
|
||||
struct tegra186_cpufreq_cluster *clusters;
|
||||
const struct tegra186_cpufreq_cpu *cpus;
|
||||
};
|
||||
|
||||
static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < data->num_clusters; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
const struct tegra186_cpufreq_cluster_info *info =
|
||||
cluster->info;
|
||||
int core;
|
||||
|
||||
for (core = 0; core < ARRAY_SIZE(info->cpus); core++) {
|
||||
if (info->cpus[core] == policy->cpu)
|
||||
break;
|
||||
}
|
||||
if (core == ARRAY_SIZE(info->cpus))
|
||||
continue;
|
||||
|
||||
policy->driver_data =
|
||||
data->regs + info->offset + EDVD_CORE_VOLT_FREQ(core);
|
||||
policy->freq_table = cluster->table;
|
||||
break;
|
||||
}
|
||||
unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
|
||||
|
||||
policy->freq_table = data->clusters[cluster].table;
|
||||
policy->cpuinfo.transition_latency = 300 * 1000;
|
||||
policy->driver_data = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -85,11 +84,12 @@ static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
|
|||
static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
unsigned int index)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct cpufreq_frequency_table *tbl = policy->freq_table + index;
|
||||
void __iomem *edvd_reg = policy->driver_data;
|
||||
unsigned int edvd_offset = data->cpus[policy->cpu].edvd_offset;
|
||||
u32 edvd_val = tbl->driver_data;
|
||||
|
||||
writel(edvd_val, edvd_reg);
|
||||
writel(edvd_val, data->regs + edvd_offset);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -97,35 +97,22 @@ static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
|
|||
static unsigned int tegra186_cpufreq_get(unsigned int cpu)
|
||||
{
|
||||
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct tegra186_cpufreq_cluster *cluster;
|
||||
struct cpufreq_policy *policy;
|
||||
void __iomem *edvd_reg;
|
||||
unsigned int i, freq = 0;
|
||||
unsigned int edvd_offset, cluster_id;
|
||||
u32 ndiv;
|
||||
|
||||
policy = cpufreq_cpu_get(cpu);
|
||||
if (!policy)
|
||||
return 0;
|
||||
|
||||
edvd_reg = policy->driver_data;
|
||||
ndiv = readl(edvd_reg) & EDVD_CORE_VOLT_FREQ_F_MASK;
|
||||
|
||||
for (i = 0; i < data->num_clusters; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
int core;
|
||||
|
||||
for (core = 0; core < ARRAY_SIZE(cluster->info->cpus); core++) {
|
||||
if (cluster->info->cpus[core] != policy->cpu)
|
||||
continue;
|
||||
|
||||
freq = (cluster->ref_clk_khz * ndiv) / cluster->div;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
edvd_offset = data->cpus[policy->cpu].edvd_offset;
|
||||
ndiv = readl(data->regs + edvd_offset) & EDVD_CORE_VOLT_FREQ_F_MASK;
|
||||
cluster_id = data->cpus[policy->cpu].bpmp_cluster_id;
|
||||
cluster = &data->clusters[cluster_id];
|
||||
cpufreq_cpu_put(policy);
|
||||
|
||||
return freq;
|
||||
return (cluster->ref_clk_khz * ndiv) / cluster->div;
|
||||
}
|
||||
|
||||
static struct cpufreq_driver tegra186_cpufreq_driver = {
|
||||
|
@ -141,7 +128,7 @@ static struct cpufreq_driver tegra186_cpufreq_driver = {
|
|||
|
||||
static struct cpufreq_frequency_table *init_vhint_table(
|
||||
struct platform_device *pdev, struct tegra_bpmp *bpmp,
|
||||
struct tegra186_cpufreq_cluster *cluster)
|
||||
struct tegra186_cpufreq_cluster *cluster, unsigned int cluster_id)
|
||||
{
|
||||
struct cpufreq_frequency_table *table;
|
||||
struct mrq_cpu_vhint_request req;
|
||||
|
@ -160,7 +147,7 @@ static struct cpufreq_frequency_table *init_vhint_table(
|
|||
|
||||
memset(&req, 0, sizeof(req));
|
||||
req.addr = phys;
|
||||
req.cluster_id = cluster->info->bpmp_cluster_id;
|
||||
req.cluster_id = cluster_id;
|
||||
|
||||
memset(&msg, 0, sizeof(msg));
|
||||
msg.mrq = MRQ_CPU_VHINT;
|
||||
|
@ -234,12 +221,12 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
|||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
data->clusters = devm_kcalloc(&pdev->dev, ARRAY_SIZE(tegra186_clusters),
|
||||
data->clusters = devm_kcalloc(&pdev->dev, TEGRA186_NUM_CLUSTERS,
|
||||
sizeof(*data->clusters), GFP_KERNEL);
|
||||
if (!data->clusters)
|
||||
return -ENOMEM;
|
||||
|
||||
data->num_clusters = ARRAY_SIZE(tegra186_clusters);
|
||||
data->cpus = tegra186_cpus;
|
||||
|
||||
bpmp = tegra_bpmp_get(&pdev->dev);
|
||||
if (IS_ERR(bpmp))
|
||||
|
@ -251,11 +238,10 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
|
|||
goto put_bpmp;
|
||||
}
|
||||
|
||||
for (i = 0; i < data->num_clusters; i++) {
|
||||
for (i = 0; i < TEGRA186_NUM_CLUSTERS; i++) {
|
||||
struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
|
||||
|
||||
cluster->info = &tegra186_clusters[i];
|
||||
cluster->table = init_vhint_table(pdev, bpmp, cluster);
|
||||
cluster->table = init_vhint_table(pdev, bpmp, cluster, i);
|
||||
if (IS_ERR(cluster->table)) {
|
||||
err = PTR_ERR(cluster->table);
|
||||
goto put_bpmp;
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
#define KHZ 1000
|
||||
#define REF_CLK_MHZ 408 /* 408 MHz */
|
||||
#define US_DELAY 500
|
||||
#define US_DELAY_MIN 2
|
||||
#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)
|
||||
#define MAX_CNT ~0U
|
||||
|
||||
|
@ -44,7 +43,6 @@ struct tegra194_cpufreq_data {
|
|||
|
||||
struct tegra_cpu_ctr {
|
||||
u32 cpu;
|
||||
u32 delay;
|
||||
u32 coreclk_cnt, last_coreclk_cnt;
|
||||
u32 refclk_cnt, last_refclk_cnt;
|
||||
};
|
||||
|
@ -112,7 +110,7 @@ static void tegra_read_counters(struct work_struct *work)
|
|||
val = read_freq_feedback();
|
||||
c->last_refclk_cnt = lower_32_bits(val);
|
||||
c->last_coreclk_cnt = upper_32_bits(val);
|
||||
udelay(c->delay);
|
||||
udelay(US_DELAY);
|
||||
val = read_freq_feedback();
|
||||
c->refclk_cnt = lower_32_bits(val);
|
||||
c->coreclk_cnt = upper_32_bits(val);
|
||||
|
@ -139,7 +137,7 @@ static void tegra_read_counters(struct work_struct *work)
|
|||
* @cpu - logical cpu whose freq to be updated
|
||||
* Returns freq in KHz on success, 0 if cpu is offline
|
||||
*/
|
||||
static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
|
||||
static unsigned int tegra194_calculate_speed(u32 cpu)
|
||||
{
|
||||
struct read_counters_work read_counters_work;
|
||||
struct tegra_cpu_ctr c;
|
||||
|
@ -153,7 +151,6 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
|
|||
* interrupts enabled.
|
||||
*/
|
||||
read_counters_work.c.cpu = cpu;
|
||||
read_counters_work.c.delay = delay;
|
||||
INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters);
|
||||
queue_work_on(cpu, read_counters_wq, &read_counters_work.work);
|
||||
flush_work(&read_counters_work.work);
|
||||
|
@ -180,9 +177,61 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
|
|||
return (rate_mhz * KHZ); /* in KHz */
|
||||
}
|
||||
|
||||
static void get_cpu_ndiv(void *ndiv)
|
||||
{
|
||||
u64 ndiv_val;
|
||||
|
||||
asm volatile("mrs %0, s3_0_c15_c0_4" : "=r" (ndiv_val) : );
|
||||
|
||||
*(u64 *)ndiv = ndiv_val;
|
||||
}
|
||||
|
||||
static void set_cpu_ndiv(void *data)
|
||||
{
|
||||
struct cpufreq_frequency_table *tbl = data;
|
||||
u64 ndiv_val = (u64)tbl->driver_data;
|
||||
|
||||
asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
|
||||
}
|
||||
|
||||
static unsigned int tegra194_get_speed(u32 cpu)
|
||||
{
|
||||
return tegra194_get_speed_common(cpu, US_DELAY);
|
||||
struct tegra194_cpufreq_data *data = cpufreq_get_driver_data();
|
||||
struct cpufreq_frequency_table *pos;
|
||||
unsigned int rate;
|
||||
u64 ndiv;
|
||||
int ret;
|
||||
u32 cl;
|
||||
|
||||
smp_call_function_single(cpu, get_cpu_cluster, &cl, true);
|
||||
|
||||
/* reconstruct actual cpu freq using counters */
|
||||
rate = tegra194_calculate_speed(cpu);
|
||||
|
||||
/* get last written ndiv value */
|
||||
ret = smp_call_function_single(cpu, get_cpu_ndiv, &ndiv, true);
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return rate;
|
||||
|
||||
/*
|
||||
* If the reconstructed frequency has acceptable delta from
|
||||
* the last written value, then return freq corresponding
|
||||
* to the last written ndiv value from freq_table. This is
|
||||
* done to return consistent value.
|
||||
*/
|
||||
cpufreq_for_each_valid_entry(pos, data->tables[cl]) {
|
||||
if (pos->driver_data != ndiv)
|
||||
continue;
|
||||
|
||||
if (abs(pos->frequency - rate) > 115200) {
|
||||
pr_warn("cpufreq: cpu%d,cur:%u,set:%u,set ndiv:%llu\n",
|
||||
cpu, rate, pos->frequency, ndiv);
|
||||
} else {
|
||||
rate = pos->frequency;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
||||
|
@ -196,9 +245,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
|||
if (cl >= data->num_clusters)
|
||||
return -EINVAL;
|
||||
|
||||
/* boot freq */
|
||||
policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN);
|
||||
|
||||
/* set same policy for all cpus in a cluster */
|
||||
for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++)
|
||||
cpumask_set_cpu(cpu, policy->cpus);
|
||||
|
@ -209,14 +255,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void set_cpu_ndiv(void *data)
|
||||
{
|
||||
struct cpufreq_frequency_table *tbl = data;
|
||||
u64 ndiv_val = (u64)tbl->driver_data;
|
||||
|
||||
asm volatile("msr s3_0_c15_c0_4, %0" : : "r" (ndiv_val));
|
||||
}
|
||||
|
||||
static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
|
||||
unsigned int index)
|
||||
{
|
||||
|
|
|
@ -591,6 +591,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = {
|
|||
};
|
||||
module_platform_driver(ve_spc_cpufreq_platdrv);
|
||||
|
||||
MODULE_ALIAS("platform:vexpress-spc-cpufreq");
|
||||
MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>");
|
||||
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
|
||||
MODULE_DESCRIPTION("Vexpress SPC ARM big LITTLE cpufreq driver");
|
||||
|
|
|
@ -750,6 +750,13 @@ static bool scmi_fast_switch_possible(const struct scmi_handle *handle,
|
|||
return dom->fc_info && dom->fc_info->level_set_addr;
|
||||
}
|
||||
|
||||
static bool scmi_power_scale_mw_get(const struct scmi_handle *handle)
|
||||
{
|
||||
struct scmi_perf_info *pi = handle->perf_priv;
|
||||
|
||||
return pi->power_scale_mw;
|
||||
}
|
||||
|
||||
static const struct scmi_perf_ops perf_ops = {
|
||||
.limits_set = scmi_perf_limits_set,
|
||||
.limits_get = scmi_perf_limits_get,
|
||||
|
@ -762,6 +769,7 @@ static const struct scmi_perf_ops perf_ops = {
|
|||
.freq_get = scmi_dvfs_freq_get,
|
||||
.est_power_get = scmi_dvfs_est_power_get,
|
||||
.fast_switch_possible = scmi_fast_switch_possible,
|
||||
.power_scale_mw_get = scmi_power_scale_mw_get,
|
||||
};
|
||||
|
||||
static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,
|
||||
|
|
|
@ -1335,7 +1335,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
|
|||
goto failed;
|
||||
}
|
||||
|
||||
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus);
|
||||
ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true);
|
||||
if (ret)
|
||||
goto failed;
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ struct em_perf_state {
|
|||
* em_perf_domain - Performance domain
|
||||
* @table: List of performance states, in ascending order
|
||||
* @nr_perf_states: Number of performance states
|
||||
* @milliwatts: Flag indicating the power values are in milli-Watts
|
||||
* or some other scale.
|
||||
* @cpus: Cpumask covering the CPUs of the domain. It's here
|
||||
* for performance reasons to avoid potential cache
|
||||
* misses during energy calculations in the scheduler
|
||||
|
@ -43,6 +45,7 @@ struct em_perf_state {
|
|||
struct em_perf_domain {
|
||||
struct em_perf_state *table;
|
||||
int nr_perf_states;
|
||||
int milliwatts;
|
||||
unsigned long cpus[];
|
||||
};
|
||||
|
||||
|
@ -79,7 +82,8 @@ struct em_data_callback {
|
|||
struct em_perf_domain *em_cpu_get(int cpu);
|
||||
struct em_perf_domain *em_pd_get(struct device *dev);
|
||||
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
struct em_data_callback *cb, cpumask_t *span);
|
||||
struct em_data_callback *cb, cpumask_t *span,
|
||||
bool milliwatts);
|
||||
void em_dev_unregister_perf_domain(struct device *dev);
|
||||
|
||||
/**
|
||||
|
@ -186,7 +190,8 @@ struct em_data_callback {};
|
|||
|
||||
static inline
|
||||
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
struct em_data_callback *cb, cpumask_t *span)
|
||||
struct em_data_callback *cb, cpumask_t *span,
|
||||
bool milliwatts)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,7 @@ struct scmi_perf_ops {
|
|||
unsigned long *rate, unsigned long *power);
|
||||
bool (*fast_switch_possible)(const struct scmi_handle *handle,
|
||||
struct device *dev);
|
||||
bool (*power_scale_mw_get)(const struct scmi_handle *handle);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -52,6 +52,17 @@ static int em_debug_cpus_show(struct seq_file *s, void *unused)
|
|||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(em_debug_cpus);
|
||||
|
||||
static int em_debug_units_show(struct seq_file *s, void *unused)
|
||||
{
|
||||
struct em_perf_domain *pd = s->private;
|
||||
char *units = pd->milliwatts ? "milliWatts" : "bogoWatts";
|
||||
|
||||
seq_printf(s, "%s\n", units);
|
||||
|
||||
return 0;
|
||||
}
|
||||
DEFINE_SHOW_ATTRIBUTE(em_debug_units);
|
||||
|
||||
static void em_debug_create_pd(struct device *dev)
|
||||
{
|
||||
struct dentry *d;
|
||||
|
@ -64,6 +75,8 @@ static void em_debug_create_pd(struct device *dev)
|
|||
debugfs_create_file("cpus", 0444, d, dev->em_pd->cpus,
|
||||
&em_debug_cpus_fops);
|
||||
|
||||
debugfs_create_file("units", 0444, d, dev->em_pd, &em_debug_units_fops);
|
||||
|
||||
/* Create a sub-directory for each performance state */
|
||||
for (i = 0; i < dev->em_pd->nr_perf_states; i++)
|
||||
em_debug_create_ps(&dev->em_pd->table[i], d);
|
||||
|
@ -250,17 +263,24 @@ EXPORT_SYMBOL_GPL(em_cpu_get);
|
|||
* @cpus : Pointer to cpumask_t, which in case of a CPU device is
|
||||
* obligatory. It can be taken from i.e. 'policy->cpus'. For other
|
||||
* type of devices this should be set to NULL.
|
||||
* @milliwatts : Flag indicating that the power values are in milliWatts or
|
||||
* in some other scale. It must be set properly.
|
||||
*
|
||||
* Create Energy Model tables for a performance domain using the callbacks
|
||||
* defined in cb.
|
||||
*
|
||||
* The @milliwatts is important to set with correct value. Some kernel
|
||||
* sub-systems might rely on this flag and check if all devices in the EM are
|
||||
* using the same scale.
|
||||
*
|
||||
* If multiple clients register the same performance domain, all but the first
|
||||
* registration will be ignored.
|
||||
*
|
||||
* Return 0 on success
|
||||
*/
|
||||
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
||||
struct em_data_callback *cb, cpumask_t *cpus)
|
||||
struct em_data_callback *cb, cpumask_t *cpus,
|
||||
bool milliwatts)
|
||||
{
|
||||
unsigned long cap, prev_cap = 0;
|
||||
int cpu, ret;
|
||||
|
@ -313,6 +333,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
|
|||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
dev->em_pd->milliwatts = milliwatts;
|
||||
|
||||
em_debug_create_pd(dev);
|
||||
dev_info(dev, "EM: created perf domain\n");
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче