2
0
mirror of https://github.com/edk2-porting/linux-next.git synced 2025-01-19 02:54:00 +08:00

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:
Rafael J. Wysocki 2020-12-14 20:29:50 +01:00
commit 30c768829a
19 changed files with 192 additions and 94 deletions

View File

@ -94,7 +94,7 @@ config ARM_IMX6Q_CPUFREQ
tristate "Freescale i.MX6 cpufreq support" tristate "Freescale i.MX6 cpufreq support"
depends on ARCH_MXC depends on ARCH_MXC
depends on REGULATOR_ANATOP depends on REGULATOR_ANATOP
select NVMEM_IMX_OCOTP depends on NVMEM_IMX_OCOTP || COMPILE_TEST
select PM_OPP select PM_OPP
help help
This adds cpufreq driver support for Freescale i.MX6 series SoCs. This adds cpufreq driver support for Freescale i.MX6 series SoCs.

View File

@ -204,6 +204,12 @@ static void __exit armada_8k_cpufreq_exit(void)
} }
module_exit(armada_8k_cpufreq_exit); 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_AUTHOR("Gregory Clement <gregory.clement@bootlin.com>");
MODULE_DESCRIPTION("Armada 8K cpufreq driver"); MODULE_DESCRIPTION("Armada 8K cpufreq driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -119,10 +119,12 @@ static const struct of_device_id blacklist[] __initconst = {
{ .compatible = "mediatek,mt2712", }, { .compatible = "mediatek,mt2712", },
{ .compatible = "mediatek,mt7622", }, { .compatible = "mediatek,mt7622", },
{ .compatible = "mediatek,mt7623", }, { .compatible = "mediatek,mt7623", },
{ .compatible = "mediatek,mt8167", },
{ .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt817x", },
{ .compatible = "mediatek,mt8173", }, { .compatible = "mediatek,mt8173", },
{ .compatible = "mediatek,mt8176", }, { .compatible = "mediatek,mt8176", },
{ .compatible = "mediatek,mt8183", }, { .compatible = "mediatek,mt8183", },
{ .compatible = "mediatek,mt8516", },
{ .compatible = "nvidia,tegra20", }, { .compatible = "nvidia,tegra20", },
{ .compatible = "nvidia,tegra30", }, { .compatible = "nvidia,tegra30", },

View File

@ -101,6 +101,13 @@ out_put_node:
} }
module_init(hb_cpufreq_driver_init); 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_AUTHOR("Mark Langsdorf <mark.langsdorf@calxeda.com>");
MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver"); MODULE_DESCRIPTION("Calxeda Highbank cpufreq driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -216,6 +216,7 @@ static struct platform_driver ls1x_cpufreq_platdrv = {
module_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_AUTHOR("Kelvin Cheung <keguang.zhang@gmail.com>");
MODULE_DESCRIPTION("Loongson1 CPUFreq driver"); MODULE_DESCRIPTION("Loongson1 CPUFreq driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -532,6 +532,7 @@ static const struct of_device_id mtk_cpufreq_machines[] __initconst = {
{ .compatible = "mediatek,mt2712", }, { .compatible = "mediatek,mt2712", },
{ .compatible = "mediatek,mt7622", }, { .compatible = "mediatek,mt7622", },
{ .compatible = "mediatek,mt7623", }, { .compatible = "mediatek,mt7623", },
{ .compatible = "mediatek,mt8167", },
{ .compatible = "mediatek,mt817x", }, { .compatible = "mediatek,mt817x", },
{ .compatible = "mediatek,mt8173", }, { .compatible = "mediatek,mt8173", },
{ .compatible = "mediatek,mt8176", }, { .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) 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); pdev = platform_device_register_simple("mtk-cpufreq", -1, NULL, 0);
if (IS_ERR(pdev)) { if (IS_ERR(pdev)) {
pr_err("failed to register mtk-cpufreq platform device\n"); pr_err("failed to register mtk-cpufreq platform device\n");
platform_driver_unregister(&mtk_cpufreq_platdrv);
return PTR_ERR(pdev); return PTR_ERR(pdev);
} }

View File

@ -464,6 +464,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst = {
{ .compatible = "qcom,msm8960", .data = &match_data_krait }, { .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 * Since the driver depends on smem and nvmem drivers, which may

View File

@ -126,6 +126,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
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); struct em_data_callback em_cb = EM_DATA_CB(scmi_get_cpu_power);
bool power_scale_mw;
cpu_dev = get_cpu_device(policy->cpu); cpu_dev = get_cpu_device(policy->cpu);
if (!cpu_dev) { if (!cpu_dev) {
@ -189,7 +190,9 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy)
policy->fast_switch_possible = policy->fast_switch_possible =
handle->perf_ops->fast_switch_possible(handle, cpu_dev); 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; return 0;

View File

@ -233,6 +233,7 @@ static struct platform_driver scpi_cpufreq_platdrv = {
}; };
module_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_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver"); MODULE_DESCRIPTION("ARM SCPI CPUFreq interface driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -223,7 +223,8 @@ use_defaults:
opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS); opp_table = dev_pm_opp_set_supported_hw(dev, version, VERSION_ELEMENTS);
if (IS_ERR(opp_table)) { if (IS_ERR(opp_table)) {
dev_err(dev, "Failed to set supported hardware\n"); 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", dev_dbg(dev, "pcode: %d major: %d minor: %d substrate: %d\n",
@ -232,6 +233,10 @@ use_defaults:
version[0], version[1], version[2]); version[0], version[1], version[2]);
return 0; return 0;
err_put_prop_name:
dev_pm_opp_put_prop_name(opp_table);
return ret;
} }
static int sti_cpufreq_fetch_syscon_registers(void) static int sti_cpufreq_fetch_syscon_registers(void)
@ -292,6 +297,13 @@ register_cpufreq_dt:
} }
module_init(sti_cpufreq_init); 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_DESCRIPTION("STMicroelectronics CPUFreq/OPP driver");
MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>"); MODULE_AUTHOR("Ajitpal Singh <ajitpal.singh@st.com>");
MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>"); MODULE_AUTHOR("Lee Jones <lee.jones@linaro.org>");

View File

@ -167,6 +167,7 @@ static const struct of_device_id sun50i_cpufreq_match_list[] = {
{ .compatible = "allwinner,sun50i-h6" }, { .compatible = "allwinner,sun50i-h6" },
{} {}
}; };
MODULE_DEVICE_TABLE(of, sun50i_cpufreq_match_list);
static const struct of_device_id *sun50i_cpufreq_match_node(void) static const struct of_device_id *sun50i_cpufreq_match_node(void)
{ {

View File

@ -12,35 +12,52 @@
#include <soc/tegra/bpmp.h> #include <soc/tegra/bpmp.h>
#include <soc/tegra/bpmp-abi.h> #include <soc/tegra/bpmp-abi.h>
#define EDVD_CORE_VOLT_FREQ(core) (0x20 + (core) * 0x4) #define TEGRA186_NUM_CLUSTERS 2
#define EDVD_CORE_VOLT_FREQ_F_SHIFT 0 #define EDVD_OFFSET_A57(core) ((SZ_64K * 6) + (0x20 + (core) * 0x4))
#define EDVD_CORE_VOLT_FREQ_F_MASK 0xffff #define EDVD_OFFSET_DENVER(core) ((SZ_64K * 7) + (0x20 + (core) * 0x4))
#define EDVD_CORE_VOLT_FREQ_V_SHIFT 16 #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 { struct tegra186_cpufreq_cpu {
unsigned long offset;
int cpus[4];
unsigned int bpmp_cluster_id; unsigned int bpmp_cluster_id;
unsigned int edvd_offset;
}; };
#define NO_CPU -1 static const struct tegra186_cpufreq_cpu tegra186_cpus[] = {
static const struct tegra186_cpufreq_cluster_info tegra186_clusters[] = { /* CPU0 - A57 Cluster */
/* Denver 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, .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 { struct tegra186_cpufreq_cluster {
const struct tegra186_cpufreq_cluster_info *info;
struct cpufreq_frequency_table *table; struct cpufreq_frequency_table *table;
u32 ref_clk_khz; u32 ref_clk_khz;
u32 div; u32 div;
@ -48,36 +65,18 @@ struct tegra186_cpufreq_cluster {
struct tegra186_cpufreq_data { struct tegra186_cpufreq_data {
void __iomem *regs; void __iomem *regs;
size_t num_clusters;
struct tegra186_cpufreq_cluster *clusters; struct tegra186_cpufreq_cluster *clusters;
const struct tegra186_cpufreq_cpu *cpus;
}; };
static int tegra186_cpufreq_init(struct cpufreq_policy *policy) static int tegra186_cpufreq_init(struct cpufreq_policy *policy)
{ {
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data(); struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
unsigned int i; unsigned int cluster = data->cpus[policy->cpu].bpmp_cluster_id;
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;
}
policy->freq_table = data->clusters[cluster].table;
policy->cpuinfo.transition_latency = 300 * 1000; policy->cpuinfo.transition_latency = 300 * 1000;
policy->driver_data = NULL;
return 0; 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, static int tegra186_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index) unsigned int index)
{ {
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
struct cpufreq_frequency_table *tbl = policy->freq_table + index; 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; u32 edvd_val = tbl->driver_data;
writel(edvd_val, edvd_reg); writel(edvd_val, data->regs + edvd_offset);
return 0; 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) static unsigned int tegra186_cpufreq_get(unsigned int cpu)
{ {
struct tegra186_cpufreq_data *data = cpufreq_get_driver_data(); struct tegra186_cpufreq_data *data = cpufreq_get_driver_data();
struct tegra186_cpufreq_cluster *cluster;
struct cpufreq_policy *policy; struct cpufreq_policy *policy;
void __iomem *edvd_reg; unsigned int edvd_offset, cluster_id;
unsigned int i, freq = 0;
u32 ndiv; u32 ndiv;
policy = cpufreq_cpu_get(cpu); policy = cpufreq_cpu_get(cpu);
if (!policy) if (!policy)
return 0; return 0;
edvd_reg = policy->driver_data; edvd_offset = data->cpus[policy->cpu].edvd_offset;
ndiv = readl(edvd_reg) & EDVD_CORE_VOLT_FREQ_F_MASK; ndiv = readl(data->regs + edvd_offset) & EDVD_CORE_VOLT_FREQ_F_MASK;
cluster_id = data->cpus[policy->cpu].bpmp_cluster_id;
for (i = 0; i < data->num_clusters; i++) { cluster = &data->clusters[cluster_id];
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:
cpufreq_cpu_put(policy); cpufreq_cpu_put(policy);
return freq; return (cluster->ref_clk_khz * ndiv) / cluster->div;
} }
static struct cpufreq_driver tegra186_cpufreq_driver = { 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( static struct cpufreq_frequency_table *init_vhint_table(
struct platform_device *pdev, struct tegra_bpmp *bpmp, 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 cpufreq_frequency_table *table;
struct mrq_cpu_vhint_request req; struct mrq_cpu_vhint_request req;
@ -160,7 +147,7 @@ static struct cpufreq_frequency_table *init_vhint_table(
memset(&req, 0, sizeof(req)); memset(&req, 0, sizeof(req));
req.addr = phys; req.addr = phys;
req.cluster_id = cluster->info->bpmp_cluster_id; req.cluster_id = cluster_id;
memset(&msg, 0, sizeof(msg)); memset(&msg, 0, sizeof(msg));
msg.mrq = MRQ_CPU_VHINT; msg.mrq = MRQ_CPU_VHINT;
@ -234,12 +221,12 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
if (!data) if (!data)
return -ENOMEM; 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); sizeof(*data->clusters), GFP_KERNEL);
if (!data->clusters) if (!data->clusters)
return -ENOMEM; return -ENOMEM;
data->num_clusters = ARRAY_SIZE(tegra186_clusters); data->cpus = tegra186_cpus;
bpmp = tegra_bpmp_get(&pdev->dev); bpmp = tegra_bpmp_get(&pdev->dev);
if (IS_ERR(bpmp)) if (IS_ERR(bpmp))
@ -251,11 +238,10 @@ static int tegra186_cpufreq_probe(struct platform_device *pdev)
goto put_bpmp; 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]; struct tegra186_cpufreq_cluster *cluster = &data->clusters[i];
cluster->info = &tegra186_clusters[i]; cluster->table = init_vhint_table(pdev, bpmp, cluster, i);
cluster->table = init_vhint_table(pdev, bpmp, cluster);
if (IS_ERR(cluster->table)) { if (IS_ERR(cluster->table)) {
err = PTR_ERR(cluster->table); err = PTR_ERR(cluster->table);
goto put_bpmp; goto put_bpmp;

View File

@ -21,7 +21,6 @@
#define KHZ 1000 #define KHZ 1000
#define REF_CLK_MHZ 408 /* 408 MHz */ #define REF_CLK_MHZ 408 /* 408 MHz */
#define US_DELAY 500 #define US_DELAY 500
#define US_DELAY_MIN 2
#define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ) #define CPUFREQ_TBL_STEP_HZ (50 * KHZ * KHZ)
#define MAX_CNT ~0U #define MAX_CNT ~0U
@ -44,7 +43,6 @@ struct tegra194_cpufreq_data {
struct tegra_cpu_ctr { struct tegra_cpu_ctr {
u32 cpu; u32 cpu;
u32 delay;
u32 coreclk_cnt, last_coreclk_cnt; u32 coreclk_cnt, last_coreclk_cnt;
u32 refclk_cnt, last_refclk_cnt; u32 refclk_cnt, last_refclk_cnt;
}; };
@ -112,7 +110,7 @@ static void tegra_read_counters(struct work_struct *work)
val = read_freq_feedback(); val = read_freq_feedback();
c->last_refclk_cnt = lower_32_bits(val); c->last_refclk_cnt = lower_32_bits(val);
c->last_coreclk_cnt = upper_32_bits(val); c->last_coreclk_cnt = upper_32_bits(val);
udelay(c->delay); udelay(US_DELAY);
val = read_freq_feedback(); val = read_freq_feedback();
c->refclk_cnt = lower_32_bits(val); c->refclk_cnt = lower_32_bits(val);
c->coreclk_cnt = upper_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 * @cpu - logical cpu whose freq to be updated
* Returns freq in KHz on success, 0 if cpu is offline * 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 read_counters_work read_counters_work;
struct tegra_cpu_ctr c; struct tegra_cpu_ctr c;
@ -153,7 +151,6 @@ static unsigned int tegra194_get_speed_common(u32 cpu, u32 delay)
* interrupts enabled. * interrupts enabled.
*/ */
read_counters_work.c.cpu = cpu; read_counters_work.c.cpu = cpu;
read_counters_work.c.delay = delay;
INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters); INIT_WORK_ONSTACK(&read_counters_work.work, tegra_read_counters);
queue_work_on(cpu, read_counters_wq, &read_counters_work.work); queue_work_on(cpu, read_counters_wq, &read_counters_work.work);
flush_work(&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 */ 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) 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) 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) if (cl >= data->num_clusters)
return -EINVAL; return -EINVAL;
/* boot freq */
policy->cur = tegra194_get_speed_common(policy->cpu, US_DELAY_MIN);
/* set same policy for all cpus in a cluster */ /* set same policy for all cpus in a cluster */
for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++) for (cpu = (cl * 2); cpu < ((cl + 1) * 2); cpu++)
cpumask_set_cpu(cpu, policy->cpus); cpumask_set_cpu(cpu, policy->cpus);
@ -209,14 +255,6 @@ static int tegra194_cpufreq_init(struct cpufreq_policy *policy)
return 0; 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, static int tegra194_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index) unsigned int index)
{ {

View File

@ -591,6 +591,7 @@ static struct platform_driver ve_spc_cpufreq_platdrv = {
}; };
module_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("Viresh Kumar <viresh.kumar@linaro.org>");
MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>"); MODULE_AUTHOR("Sudeep Holla <sudeep.holla@arm.com>");
MODULE_DESCRIPTION("Vexpress SPC ARM big LITTLE cpufreq driver"); MODULE_DESCRIPTION("Vexpress SPC ARM big LITTLE cpufreq driver");

View File

@ -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; 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 = { static const struct scmi_perf_ops perf_ops = {
.limits_set = scmi_perf_limits_set, .limits_set = scmi_perf_limits_set,
.limits_get = scmi_perf_limits_get, .limits_get = scmi_perf_limits_get,
@ -762,6 +769,7 @@ static const struct scmi_perf_ops perf_ops = {
.freq_get = scmi_dvfs_freq_get, .freq_get = scmi_dvfs_freq_get,
.est_power_get = scmi_dvfs_est_power_get, .est_power_get = scmi_dvfs_est_power_get,
.fast_switch_possible = scmi_fast_switch_possible, .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, static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle,

View File

@ -1335,7 +1335,7 @@ int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus)
goto failed; 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) if (ret)
goto failed; goto failed;

View File

@ -29,6 +29,8 @@ struct em_perf_state {
* em_perf_domain - Performance domain * em_perf_domain - Performance domain
* @table: List of performance states, in ascending order * @table: List of performance states, in ascending order
* @nr_perf_states: Number of performance states * @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 * @cpus: Cpumask covering the CPUs of the domain. It's here
* for performance reasons to avoid potential cache * for performance reasons to avoid potential cache
* misses during energy calculations in the scheduler * misses during energy calculations in the scheduler
@ -43,6 +45,7 @@ struct em_perf_state {
struct em_perf_domain { struct em_perf_domain {
struct em_perf_state *table; struct em_perf_state *table;
int nr_perf_states; int nr_perf_states;
int milliwatts;
unsigned long cpus[]; 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_cpu_get(int cpu);
struct em_perf_domain *em_pd_get(struct device *dev); struct em_perf_domain *em_pd_get(struct device *dev);
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, 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); void em_dev_unregister_perf_domain(struct device *dev);
/** /**
@ -186,7 +190,8 @@ struct em_data_callback {};
static inline static inline
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, 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; return -EINVAL;
} }

View File

@ -121,6 +121,7 @@ struct scmi_perf_ops {
unsigned long *rate, unsigned long *power); unsigned long *rate, unsigned long *power);
bool (*fast_switch_possible)(const struct scmi_handle *handle, bool (*fast_switch_possible)(const struct scmi_handle *handle,
struct device *dev); struct device *dev);
bool (*power_scale_mw_get)(const struct scmi_handle *handle);
}; };
/** /**

View File

@ -52,6 +52,17 @@ static int em_debug_cpus_show(struct seq_file *s, void *unused)
} }
DEFINE_SHOW_ATTRIBUTE(em_debug_cpus); 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) static void em_debug_create_pd(struct device *dev)
{ {
struct dentry *d; 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, debugfs_create_file("cpus", 0444, d, dev->em_pd->cpus,
&em_debug_cpus_fops); &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 */ /* Create a sub-directory for each performance state */
for (i = 0; i < dev->em_pd->nr_perf_states; i++) for (i = 0; i < dev->em_pd->nr_perf_states; i++)
em_debug_create_ps(&dev->em_pd->table[i], d); 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 * @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 * obligatory. It can be taken from i.e. 'policy->cpus'. For other
* type of devices this should be set to NULL. * 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 * Create Energy Model tables for a performance domain using the callbacks
* defined in cb. * 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 * If multiple clients register the same performance domain, all but the first
* registration will be ignored. * registration will be ignored.
* *
* Return 0 on success * Return 0 on success
*/ */
int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, 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; unsigned long cap, prev_cap = 0;
int cpu, ret; int cpu, ret;
@ -313,6 +333,8 @@ int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states,
if (ret) if (ret)
goto unlock; goto unlock;
dev->em_pd->milliwatts = milliwatts;
em_debug_create_pd(dev); em_debug_create_pd(dev);
dev_info(dev, "EM: created perf domain\n"); dev_info(dev, "EM: created perf domain\n");