From patchwork Thu Nov 9 17:10:18 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Oleksandr Tyshchenko X-Patchwork-Id: 10051355 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 8C3BB60381 for ; Thu, 9 Nov 2017 17:13:15 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 72EC12B038 for ; Thu, 9 Nov 2017 17:13:15 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 6713C2B051; Thu, 9 Nov 2017 17:13:15 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-3.6 required=2.0 tests=BAYES_00, DKIM_ADSP_CUSTOM_MED, DKIM_SIGNED, FREEMAIL_FROM, RCVD_IN_DNSWL_MED, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 73EAF2B038 for ; Thu, 9 Nov 2017 17:13:14 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eCqM6-0004vy-Po; Thu, 09 Nov 2017 17:11:06 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1eCqM5-0004qQ-P7 for xen-devel@lists.xenproject.org; Thu, 09 Nov 2017 17:11:05 +0000 Received: from [85.158.143.35] by server-1.bemta-6.messagelabs.com id 43/73-04165-92C840A5; Thu, 09 Nov 2017 17:11:05 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrMIsWRWlGSWpSXmKPExsVyMfS6o65GD0u UwfoGcYvvWyYzOTB6HP5whSWAMYo1My8pvyKBNWPfnlaWgutBFbf//WRtYDxg38XIxSEkMJNR 4v2qXqYuRk4OFoGXLBIznzmCJCQE+lklrmyZzt7FyAHkZEm8mWEEYaZJLNnLAVIuIVAh0bH/E RuILSSgJPF652YmiJmzmSRmzzjNCpJgEzCQ2P/uCTuILQJUdG/VZLAiZoF+RokZc/6DFQkLuE ucb4GYxCKgKnHy02dGEJtXwEXi+uS1bBDb5CRunutkBrE5geJvPl6H2uwscfH1cbYJjIILGBl WMWoUpxaVpRbpGpnqJRVlpmeU5CZm5ugaGpjp5aYWFyemp+YkJhXrJefnbmIEBhwDEOxgXLUg 8BCjJAeTkiivlCVLlBBfUn5KZUZicUZ8UWlOavEhRhkODiUJXu9uoJxgUWp6akVaZg4w9GHSE hw8SiK877qA0rzFBYm5xZnpEKlTjK4cF+5c+sPEcWDCFRC55xaQfDbzdQMzx7SrrU3MQix5+X mpUuK8ViCzBUCaM0rz4EbD4vYSo6yUMC8j0LFCPAWpRbmZJajyrxjFORiVhHm3g5zAk5lXAnf BK6DjmICOi2YHO64kESEl1cBYVmYa+n8mz8VbKnWX/RU7EqUe/1v/Vn/lISet5RPa/Z6zdCS8 dsq8oX1m4lQlU37hTrvNEXPfTTL6tjo3NemIlLGAJu+Wz1/CBbtULjlMqv59vOjU8gh2oTvmc ax3rjA+EuXtmPDi5fb95gd08rhaAzls/TLzfZ/sv2xqNvHZyhfMW9+c3rdQiaU4I9FQi7moOB EA4MtYttYCAAA= X-Env-Sender: olekstysh@gmail.com X-Msg-Ref: server-4.tower-21.messagelabs.com!1510247463!74828483!1 X-Originating-IP: [209.85.215.65] X-SpamReason: No, hits=0.0 required=7.0 tests= X-StarScan-Received: X-StarScan-Version: 9.4.45; banners=-,-,- X-VirusChecked: Checked Received: (qmail 44818 invoked from network); 9 Nov 2017 17:11:04 -0000 Received: from mail-lf0-f65.google.com (HELO mail-lf0-f65.google.com) (209.85.215.65) by server-4.tower-21.messagelabs.com with AES128-GCM-SHA256 encrypted SMTP; 9 Nov 2017 17:11:04 -0000 Received: by mail-lf0-f65.google.com with SMTP id e143so8035300lfg.12 for ; Thu, 09 Nov 2017 09:11:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=bSLUeY9iZ0p+6GfB14uQN98sAvU98jYWCkN3Bpr/V3M=; b=EgOXFuS8pVJf8R7h6BUszBfKDcJiJxZVZlUE1oNJ+MGAKMmaw+0Voj8ad4iiE7BKV9 UqoJ2q5PByFyekrkWYfcczeisqlTvnwvVM3HGQT8P2Pa/RrQ2S+AP4oa5hezfIlZNyNo J5iGD2GfLOIOnJpfeSTTE1+hUZftHiONOTvMRJ0X02mLXN2DekisBv7qOss7fI3E6ZZU 0ZkPWW/xx84mBsrBBtkHfzHI9/rVjdWZqnsB31GzH2f/8XLcQaMtai8TolbcJroAEeKJ JtSAR6sSre6nM+ZGmUdzgpfV9/v27LiIMbCit/3UBncil4aGTrB6nmELnArP2vcuMKV9 8+RA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=bSLUeY9iZ0p+6GfB14uQN98sAvU98jYWCkN3Bpr/V3M=; b=XKv5JbPzE0+7tgK6L80ta5Et6D9SpAj0q/X4hl43SCki1JgQt8LNSrtiOQevucvYnp vVHPhKyhFEBZzZtjifk/nLQsNguaswM25VyyBLHMbcaCIt8ijbqWRQHmfhckUuhEqh6I 57VijXw+luN5npErJxbga2W3GbGkcwWEkclSRawylYdV7c0j45ISg5PNWPElgPyXAz1U 4aucr354KWYM1H/xtWoSDAXva1zSbhgDcB1INJJKI9vbNMqz29GGJ2Z48D/qPf3pCsM5 MgxCkL/8PceYixH4WdwkkzkFjZ1PVfVyn9zkwHlNVJkiafyEH+tcDpEHd5mfpSddkUUi uuPw== X-Gm-Message-State: AJaThX57Af/JRUmx9vj0bYFnFEeiKYr7qiyOMaecQEQKNfhxR3I5+vQm WeFxyqiFEYV0e1mBmkglkirmkg== X-Google-Smtp-Source: AGs4zMZMXGdIBE9MMVLBLQ5Nb3EQxnQ0orpQvlVIlpDz4amY+YrxwXELCosKp0fbOSK4cjdpUgnO+Q== X-Received: by 10.25.217.66 with SMTP id q63mr515016lfg.176.1510247463327; Thu, 09 Nov 2017 09:11:03 -0800 (PST) Received: from otyshchenko.kyiv.epam.com (ll-53.209.223.85.sovam.net.ua. [85.223.209.53]) by smtp.gmail.com with ESMTPSA id x90sm1394299ljb.86.2017.11.09.09.11.02 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 09 Nov 2017 09:11:02 -0800 (PST) From: Oleksandr Tyshchenko To: xen-devel@lists.xenproject.org Date: Thu, 9 Nov 2017 19:10:18 +0200 Message-Id: <1510247421-24094-29-git-send-email-olekstysh@gmail.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1510247421-24094-1-git-send-email-olekstysh@gmail.com> References: <1510247421-24094-1-git-send-email-olekstysh@gmail.com> Cc: Oleksandr Tyshchenko , Stefano Stabellini , Julien Grall Subject: [Xen-devel] [RFC PATCH 28/31] xen/arm: Introduce SCPI based CPUFreq driver X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP From: Oleksandr Tyshchenko 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 CC: Stefano Stabellini CC: Julien Grall --- 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 + * 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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: + */