From: Oleksandr Tyshchenko <olekstysh@gmail.com>
To: xen-devel@lists.xenproject.org
Cc: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>,
Stefano Stabellini <sstabellini@kernel.org>,
Julien Grall <julien.grall@linaro.org>
Subject: [RFC PATCH 28/31] xen/arm: Introduce SCPI based CPUFreq driver
Date: Thu, 9 Nov 2017 19:10:18 +0200 [thread overview]
Message-ID: <1510247421-24094-29-git-send-email-olekstysh@gmail.com> (raw)
In-Reply-To: <1510247421-24094-1-git-send-email-olekstysh@gmail.com>
From: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
This patch adds a CPUFreq driver for controlling CPUs DVFS feature
provided by System Control Processor (SCP) using SCPI protocol
for inter-processor communication.
The important point is that unlike Linux Xen doesn't have
clock infrastructure and clocks for the CPUs (DVFS clocks)
provided by SCP are managed by this driver directly using
DVFS operations over power domains the controlled CPUs are part of.
Non-arch specific driver code is mostly borrowed from
the x86 ACPI CPUFreq.
Most important TODOs regarding the whole patch series:
1. Handle devm in the direct ported code. Currently, in case of any
errors previously allocated resources are left unfreed.
2. Thermal management integration.
3. Don't pass CPUFreq related nodes to dom0. Xen owns SCPI completely.
4. Handle CPU_TURBO frequencies.
Signed-off-by: Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
CC: Stefano Stabellini <sstabellini@kernel.org>
CC: Julien Grall <julien.grall@linaro.org>
---
xen/arch/arm/cpufreq/scpi_cpufreq.c | 328 ++++++++++++++++++++++++++++++++++++
1 file changed, 328 insertions(+)
create mode 100644 xen/arch/arm/cpufreq/scpi_cpufreq.c
diff --git a/xen/arch/arm/cpufreq/scpi_cpufreq.c b/xen/arch/arm/cpufreq/scpi_cpufreq.c
new file mode 100644
index 0000000..bcd8889
--- /dev/null
+++ b/xen/arch/arm/cpufreq/scpi_cpufreq.c
@@ -0,0 +1,328 @@
+/*
+ * xen/arch/arm/cpufreq/scpi_cpufreq.c
+ *
+ * SCPI based CPUFreq driver
+ *
+ * Based on Xen arch/x86/acpi/cpufreq/cpufreq.c
+ *
+ * Oleksandr Tyshchenko <oleksandr_tyshchenko@epam.com>
+ * Copyright (c) 2017 EPAM Systems.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <xen/types.h>
+#include <xen/delay.h>
+#include <xen/cpumask.h>
+#include <xen/sched.h>
+#include <xen/xmalloc.h>
+#include <xen/err.h>
+#include <xen/cpufreq.h>
+#include <asm/bug.h>
+#include <asm/percpu.h>
+
+#include "scpi_protocol.h"
+
+extern struct device *get_cpu_device(unsigned int cpu);
+
+struct scpi_cpufreq_data
+{
+ struct processor_performance *perf;
+ struct cpufreq_frequency_table *freq_table;
+ struct scpi_dvfs_info *info; /* DVFS capabilities of the CPU's power domain */
+ int domain; /* power domain id this CPU belongs to */
+};
+
+static struct scpi_cpufreq_data *cpufreq_driver_data[NR_CPUS];
+
+static struct cpufreq_driver scpi_cpufreq_driver;
+
+static struct scpi_ops *scpi_ops;
+
+static unsigned int scpi_cpufreq_get(unsigned int cpu)
+{
+ struct scpi_cpufreq_data *data;
+ struct cpufreq_policy *policy;
+ const struct scpi_opp *opp;
+ int idx;
+
+ if ( cpu >= nr_cpu_ids || !cpu_online(cpu) )
+ return 0;
+
+ policy = per_cpu(cpufreq_cpu_policy, cpu);
+ if ( !policy || !(data = cpufreq_driver_data[policy->cpu]) ||
+ !data->info )
+ return 0;
+
+ idx = scpi_ops->dvfs_get_idx(data->domain);
+ if ( idx < 0 )
+ return 0;
+
+ opp = data->info->opps + idx;
+
+ /* Convert Hz -> kHz */
+ return opp->freq / 1000;
+}
+
+static int scpi_cpufreq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq, unsigned int relation)
+{
+ struct scpi_cpufreq_data *data = cpufreq_driver_data[policy->cpu];
+ struct processor_performance *perf;
+ struct cpufreq_freqs freqs;
+ cpumask_t online_policy_cpus;
+ unsigned int next_state = 0; /* Index into freq_table */
+ unsigned int next_perf_state = 0; /* Index into perf table */
+ unsigned int j;
+ int result;
+ const struct scpi_opp *opp;
+ int idx, max_opp;
+
+ if ( unlikely(!data) || !data->perf || !data->freq_table || !data->info )
+ return -ENODEV;
+
+ perf = data->perf;
+ result = cpufreq_frequency_table_target(policy,
+ data->freq_table,
+ target_freq,
+ relation, &next_state);
+ if ( unlikely(result) )
+ return -ENODEV;
+
+ cpumask_and(&online_policy_cpus, &cpu_online_map, policy->cpus);
+
+ next_perf_state = data->freq_table[next_state].index;
+ if ( perf->state == next_perf_state )
+ {
+ if ( unlikely(policy->resume) )
+ policy->resume = 0;
+ else
+ return 0;
+ }
+
+ /* Convert MHz -> kHz */
+ freqs.old = perf->states[perf->state].core_frequency * 1000;
+ freqs.new = data->freq_table[next_state].frequency;
+
+ /* Find corresponding index */
+ max_opp = data->info->count;
+ opp = data->info->opps;
+ for ( idx = 0; idx < max_opp; idx++, opp++ )
+ {
+ /* Compare in kHz */
+ if ( opp->freq / 1000 == freqs.new )
+ break;
+ }
+ if ( idx == max_opp )
+ return -EINVAL;
+
+ result = scpi_ops->dvfs_set_idx(data->domain, idx);
+ if ( result < 0 )
+ return result;
+
+ for_each_cpu( j, &online_policy_cpus )
+ cpufreq_statistic_update(j, perf->state, next_perf_state);
+
+ perf->state = next_perf_state;
+ policy->cur = freqs.new;
+
+ return result;
+}
+
+static int scpi_cpufreq_verify(struct cpufreq_policy *policy)
+{
+ struct scpi_cpufreq_data *data;
+ struct processor_performance *perf;
+
+ if ( !policy || !(data = cpufreq_driver_data[policy->cpu]) ||
+ !processor_pminfo[policy->cpu] )
+ return -EINVAL;
+
+ perf = &processor_pminfo[policy->cpu]->perf;
+
+ /* Convert MHz -> kHz */
+ cpufreq_verify_within_limits(policy, 0,
+ perf->states[perf->platform_limit].core_frequency * 1000);
+
+ return cpufreq_frequency_table_verify(policy, data->freq_table);
+}
+
+static int scpi_cpufreq_cpu_init(struct cpufreq_policy *policy)
+{
+ unsigned int i;
+ unsigned int valid_states = 0;
+ unsigned int curr_state, curr_freq;
+ struct scpi_cpufreq_data *data;
+ int result;
+ struct processor_performance *perf;
+ struct device *cpu_dev;
+ struct scpi_dvfs_info *info;
+ int domain;
+
+ cpu_dev = get_cpu_device(policy->cpu);
+ if ( !cpu_dev )
+ return -ENODEV;
+
+ data = xzalloc(struct scpi_cpufreq_data);
+ if ( !data )
+ return -ENOMEM;
+
+ cpufreq_driver_data[policy->cpu] = data;
+
+ data->perf = &processor_pminfo[policy->cpu]->perf;
+
+ perf = data->perf;
+ policy->shared_type = perf->shared_type;
+
+ data->freq_table = xmalloc_array(struct cpufreq_frequency_table,
+ (perf->state_count + 1));
+ if ( !data->freq_table )
+ {
+ result = -ENOMEM;
+ goto err_unreg;
+ }
+
+ /* Detect transition latency */
+ policy->cpuinfo.transition_latency = 0;
+ for ( i = 0; i < perf->state_count; i++ )
+ {
+ /* Compare in ns */
+ if ( perf->states[i].transition_latency * 1000 >
+ policy->cpuinfo.transition_latency )
+ /* Convert us -> ns */
+ policy->cpuinfo.transition_latency =
+ perf->states[i].transition_latency * 1000;
+ }
+
+ policy->governor = cpufreq_opt_governor ? : CPUFREQ_DEFAULT_GOVERNOR;
+
+ /* Initialize frequency table */
+ for ( i = 0; i < perf->state_count; i++ )
+ {
+ /* Compare in MHz */
+ if ( i > 0 && perf->states[i].core_frequency >=
+ data->freq_table[valid_states - 1].frequency / 1000 )
+ continue;
+
+ data->freq_table[valid_states].index = i;
+ /* Convert MHz -> kHz */
+ data->freq_table[valid_states].frequency =
+ perf->states[i].core_frequency * 1000;
+ valid_states++;
+ }
+ data->freq_table[valid_states].frequency = CPUFREQ_TABLE_END;
+ perf->state = 0;
+
+ result = cpufreq_frequency_table_cpuinfo(policy, data->freq_table);
+ if ( result )
+ goto err_freqfree;
+
+ /* Fill in fields needed for frequency changing */
+ domain = scpi_ops->device_domain_id(cpu_dev);
+ if ( domain < 0 )
+ {
+ result = domain;
+ goto err_freqfree;
+ }
+ data->domain = domain;
+
+ info = scpi_ops->dvfs_get_info(domain);
+ if ( IS_ERR(info) )
+ {
+ result = PTR_ERR(info);
+ goto err_freqfree;
+ }
+ data->info = info;
+
+ /* Retrieve current frequency */
+ curr_freq = scpi_cpufreq_get(policy->cpu);
+
+ /* Find corresponding state */
+ curr_state = 0;
+ for ( i = 0; data->freq_table[i].frequency != CPUFREQ_TABLE_END; i++ )
+ {
+ if ( curr_freq == data->freq_table[i].frequency )
+ {
+ curr_state = i;
+ break;
+ }
+ }
+
+ /* Update fields with actual values */
+ policy->cur = curr_freq;
+ perf->state = data->freq_table[curr_state].index;
+
+ /*
+ * the first call to ->target() should result in us actually
+ * writing something to the appropriate registers.
+ */
+ policy->resume = 1;
+
+ return result;
+
+err_freqfree:
+ xfree(data->freq_table);
+err_unreg:
+ xfree(data);
+ cpufreq_driver_data[policy->cpu] = NULL;
+
+ return result;
+}
+
+static int scpi_cpufreq_cpu_exit(struct cpufreq_policy *policy)
+{
+ struct scpi_cpufreq_data *data = cpufreq_driver_data[policy->cpu];
+
+ if ( data )
+ {
+ xfree(data->freq_table);
+ xfree(data);
+ cpufreq_driver_data[policy->cpu] = NULL;
+ }
+
+ return 0;
+}
+
+static struct cpufreq_driver scpi_cpufreq_driver = {
+ .name = "scpi-cpufreq",
+
+ .verify = scpi_cpufreq_verify,
+ .target = scpi_cpufreq_target,
+ .get = scpi_cpufreq_get,
+ .init = scpi_cpufreq_cpu_init,
+ .exit = scpi_cpufreq_cpu_exit,
+};
+
+int __init scpi_cpufreq_register_driver(void)
+{
+ scpi_ops = get_scpi_ops();
+ if ( !scpi_ops )
+ return -ENXIO;
+
+ return cpufreq_register_driver(&scpi_cpufreq_driver);
+}
+
+int cpufreq_cpu_init(unsigned int cpuid)
+{
+ return cpufreq_add_cpu(cpuid);
+}
+
+/*
+ * Local variables:
+ * mode: C
+ * c-file-style: "BSD"
+ * c-basic-offset: 4
+ * tab-width: 4
+ * indent-tabs-mode: nil
+ * End:
+ */
--
2.7.4
_______________________________________________
Xen-devel mailing list
Xen-devel@lists.xen.org
https://lists.xen.org/xen-devel
next prev parent reply other threads:[~2017-11-09 17:11 UTC|newest]
Thread overview: 108+ messages / expand[flat|nested] mbox.gz Atom feed top
2017-11-09 17:09 [RFC PATCH 00/31] CPUFreq on ARM Oleksandr Tyshchenko
2017-11-09 17:09 ` [RFC PATCH 01/31] cpufreq: move cpufreq.h file to the xen/include/xen location Oleksandr Tyshchenko
2017-12-02 0:35 ` Stefano Stabellini
2017-11-09 17:09 ` [RFC PATCH 02/31] pm: move processor_perf.h " Oleksandr Tyshchenko
2017-12-02 0:41 ` Stefano Stabellini
2017-11-09 17:09 ` [RFC PATCH 03/31] pmstat: move pmstat.c file to the xen/drivers/pm/stat.c location Oleksandr Tyshchenko
2017-12-02 0:47 ` Stefano Stabellini
2018-05-07 15:36 ` Jan Beulich
2018-05-18 11:14 ` Oleksandr Tyshchenko
2018-05-18 11:35 ` Jan Beulich
2018-05-18 14:13 ` Oleksandr Tyshchenko
2018-05-18 14:21 ` Jan Beulich
2017-11-09 17:09 ` [RFC PATCH 04/31] cpufreq: make turbo settings to be configurable Oleksandr Tyshchenko
2017-12-02 1:06 ` Stefano Stabellini
2017-12-02 17:25 ` Oleksandr Tyshchenko
2017-12-04 11:58 ` Andre Przywara
2017-12-05 15:23 ` Oleksandr Tyshchenko
2017-12-04 22:18 ` Stefano Stabellini
2017-12-05 11:13 ` Oleksandr Tyshchenko
2017-12-05 19:24 ` Stefano Stabellini
2017-12-06 11:28 ` Oleksandr Tyshchenko
2018-05-07 15:39 ` Jan Beulich
2018-05-18 14:36 ` Oleksandr Tyshchenko
2018-05-18 14:41 ` Jan Beulich
2017-11-09 17:09 ` [RFC PATCH 05/31] pmstat: make pmstat functions more generalizable Oleksandr Tyshchenko
2017-12-02 1:21 ` Stefano Stabellini
2017-12-04 16:21 ` Oleksandr Tyshchenko
2017-12-04 22:30 ` Stefano Stabellini
2017-11-09 17:09 ` [RFC PATCH 06/31] cpufreq: make cpufreq driver " Oleksandr Tyshchenko
2017-12-02 1:37 ` Stefano Stabellini
2017-12-04 19:34 ` Oleksandr Tyshchenko
2017-12-04 22:46 ` Stefano Stabellini
2017-12-05 19:29 ` Oleksandr Tyshchenko
2017-12-05 20:48 ` Stefano Stabellini
2017-12-06 7:54 ` Jan Beulich
2017-12-06 23:44 ` Stefano Stabellini
2017-12-07 8:45 ` Jan Beulich
2017-12-07 20:31 ` Oleksandr Tyshchenko
2017-12-08 8:07 ` Jan Beulich
2017-12-08 12:16 ` Oleksandr Tyshchenko
2017-11-09 17:09 ` [RFC PATCH 07/31] xenpm: Clarify xenpm usage Oleksandr Tyshchenko
2017-11-09 17:13 ` Wei Liu
2017-12-02 1:28 ` Stefano Stabellini
2017-11-09 17:09 ` [RFC PATCH 08/31] xen/device-tree: Add dt_count_phandle_with_args helper Oleksandr Tyshchenko
2017-11-09 17:09 ` [RFC PATCH 09/31] xen/device-tree: Add dt_property_for_each_string macros Oleksandr Tyshchenko
2017-12-04 23:24 ` Stefano Stabellini
2017-12-05 14:19 ` Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 10/31] xen/device-tree: Add dt_property_read_u32_index helper Oleksandr Tyshchenko
2017-12-04 23:29 ` Stefano Stabellini
2017-11-09 17:10 ` [RFC PATCH 11/31] xen/device-tree: Add dt_property_count_elems_of_size helper Oleksandr Tyshchenko
2017-12-04 23:29 ` Stefano Stabellini
2017-11-09 17:10 ` [RFC PATCH 12/31] xen/device-tree: Add dt_property_read_string_helper and friends Oleksandr Tyshchenko
2017-12-04 23:29 ` Stefano Stabellini
2017-11-09 17:10 ` [RFC PATCH 13/31] xen/arm: Add driver_data field to struct device Oleksandr Tyshchenko
2017-12-04 23:31 ` Stefano Stabellini
2017-12-05 11:26 ` Julien Grall
2017-12-05 12:57 ` Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 14/31] xen/arm: Add DEVICE_MAILBOX device class Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 15/31] xen/arm: Store device-tree node per cpu Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 16/31] arm: add SMC wrapper that is compatible with SMCCC Oleksandr Tyshchenko
2017-12-05 2:30 ` Stefano Stabellini
2017-12-05 15:33 ` Volodymyr Babchuk
2017-12-05 17:21 ` Stefano Stabellini
2017-12-05 14:58 ` Julien Grall
2017-12-05 17:08 ` Volodymyr Babchuk
2017-12-05 17:08 ` Julien Grall
2017-12-05 17:20 ` Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 17/31] xen/arm: Add ARM System Control and Power Interface (SCPI) protocol Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 18/31] xen/arm: Add mailbox infrastructure Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 19/31] xen/arm: Introduce ARM SMC based mailbox Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 20/31] xen/arm: Add common header file wrappers.h Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 21/31] xen/arm: Add rxdone_auto flag to mbox_controller structure Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 22/31] xen/arm: Add Xen changes to SCPI protocol Oleksandr Tyshchenko
2017-12-05 21:20 ` Stefano Stabellini
2017-12-05 21:41 ` Julien Grall
2017-12-06 10:08 ` Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 23/31] xen/arm: Add Xen changes to mailbox infrastructure Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 24/31] xen/arm: Add Xen changes to ARM SMC based mailbox Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 25/31] xen/arm: Use non-blocking mode for SCPI protocol Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 26/31] xen/arm: Don't set txdone_poll flag for ARM SMC mailbox Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 27/31] cpufreq: hack: perf->states isn't a real guest handle on ARM Oleksandr Tyshchenko
2017-12-05 21:34 ` Stefano Stabellini
2017-11-09 17:10 ` Oleksandr Tyshchenko [this message]
2017-11-09 17:10 ` [RFC PATCH 29/31] xen/arm: Introduce CPUFreq Interface component Oleksandr Tyshchenko
2017-12-05 22:25 ` Stefano Stabellini
2017-12-06 10:54 ` Oleksandr Tyshchenko
2017-12-07 1:40 ` Stefano Stabellini
2017-11-09 17:10 ` [RFC PATCH 30/31] xen/arm: Build CPUFreq components Oleksandr Tyshchenko
2017-11-09 17:10 ` [RFC PATCH 31/31] xen/arm: Enable CPUFreq on ARM Oleksandr Tyshchenko
2017-11-09 17:18 ` [RFC PATCH 00/31] " Andrii Anisov
2017-11-13 19:40 ` Oleksandr Tyshchenko
2017-11-13 15:21 ` Andre Przywara
2017-11-13 19:40 ` Oleksandr Tyshchenko
2017-11-14 10:49 ` Andre Przywara
2017-11-14 20:46 ` Oleksandr Tyshchenko
2017-11-15 3:03 ` Jassi Brar
2017-11-15 13:28 ` Andre Przywara
2017-11-15 15:18 ` Jassi Brar
2017-11-15 14:28 ` Andre Przywara
2017-11-16 14:57 ` Oleksandr Tyshchenko
2017-11-16 17:04 ` Andre Przywara
2017-11-17 14:01 ` Julien Grall
2017-11-17 18:36 ` Oleksandr Tyshchenko
2017-11-17 14:55 ` Oleksandr Tyshchenko
2017-11-17 16:41 ` Andre Przywara
2017-11-17 17:22 ` Oleksandr Tyshchenko
2017-12-05 22:26 ` Stefano Stabellini
2017-12-06 10:10 ` Oleksandr Tyshchenko
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1510247421-24094-29-git-send-email-olekstysh@gmail.com \
--to=olekstysh@gmail.com \
--cc=julien.grall@linaro.org \
--cc=oleksandr_tyshchenko@epam.com \
--cc=sstabellini@kernel.org \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).