From patchwork Wed Jul 29 15:12:05 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Luba X-Patchwork-Id: 11691173 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DF92013B6 for ; Wed, 29 Jul 2020 15:12:24 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id D06C820829 for ; Wed, 29 Jul 2020 15:12:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726449AbgG2PMY (ORCPT ); Wed, 29 Jul 2020 11:12:24 -0400 Received: from foss.arm.com ([217.140.110.172]:53390 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726365AbgG2PMX (ORCPT ); Wed, 29 Jul 2020 11:12:23 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 2D067D6E; Wed, 29 Jul 2020 08:12:23 -0700 (PDT) Received: from e123648.arm.com (unknown [10.37.12.35]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 0CA0D3F66E; Wed, 29 Jul 2020 08:12:20 -0700 (PDT) From: Lukasz Luba To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, cristian.marussi@arm.com, viresh.kumar@linaro.org, lukasz.luba@arm.com, rjw@rjwysocki.net Subject: [PATCH 1/4] cpufreq: Add support for statistics read from drivers Date: Wed, 29 Jul 2020 16:12:05 +0100 Message-Id: <20200729151208.27737-2-lukasz.luba@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200729151208.27737-1-lukasz.luba@arm.com> References: <20200729151208.27737-1-lukasz.luba@arm.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The cpufreq statistics are not collected when the 'fast switch' is in use. The information can be retrieved from firmware by the cpufreq driver, but the cpufreq interfaces must be extended in order to support it. Introduce callback in cpufreq driver structure and additional flag in cpufreq policy to provide support for statistics maintained in firmware. Since the cpufreq driver structure can be shared by many policy objects, the flag 'has_driver_stats' in policy makes sure that the framework will only use the statistics from cpufreq driver for actually supported CPUs. Signed-off-by: Lukasz Luba --- drivers/cpufreq/cpufreq.c | 22 +++++++++++++++++++ drivers/cpufreq/cpufreq_stats.c | 38 +++++++++++++++++++++------------ include/linux/cpufreq.h | 32 +++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 14 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 17c1c3becd92..83098205e739 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2543,6 +2543,28 @@ void cpufreq_update_limits(unsigned int cpu) } EXPORT_SYMBOL_GPL(cpufreq_update_limits); +#ifdef CONFIG_CPU_FREQ_STAT +/** + * cpufreq_update_statistics - Update statistics for a given policy. + * @policy: struct cpufreq_policy into which the current cpufreq_policy + * is written + * + * Invoke the driver's ->get_statistics callback if present to fetch + * newest statistics from the driver. + */ +int cpufreq_update_statistics(struct cpufreq_policy *policy) +{ + if (!cpufreq_driver || !policy) + return -EINVAL; + + if (cpufreq_driver->get_statistics) + return cpufreq_driver->get_statistics(policy); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(cpufreq_update_statistics); +#endif + /********************************************************************* * BOOST * *********************************************************************/ diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index 94d959a8e954..d2d975b3cc6d 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -11,19 +11,6 @@ #include #include - -struct cpufreq_stats { - unsigned int total_trans; - unsigned long long last_time; - unsigned int max_state; - unsigned int state_num; - unsigned int last_index; - u64 *time_in_state; - spinlock_t lock; - unsigned int *freq_table; - unsigned int *trans_table; -}; - static void cpufreq_stats_update(struct cpufreq_stats *stats) { unsigned long long cur_time = get_jiffies_64(); @@ -50,13 +37,36 @@ static ssize_t show_total_trans(struct cpufreq_policy *policy, char *buf) } cpufreq_freq_attr_ro(total_trans); +static ssize_t cpufreq_stats_present_driver_data(struct cpufreq_policy *policy, char *buf) +{ + struct cpufreq_stats *stats = policy->stats; + ssize_t len = 0; + int i, ret; + + spin_lock(&stats->lock); + ret = cpufreq_update_statistics(policy); + spin_unlock(&stats->lock); + + if (ret) + return 0; + + for (i = 0; i < stats->state_num; i++) { + len += sprintf(buf + len, "%u %llu\n", + stats->freq_table[i], + stats->time_in_state[i]); + } + return len; +} + static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf) { struct cpufreq_stats *stats = policy->stats; ssize_t len = 0; int i; - if (policy->fast_switch_enabled) + if (policy->has_driver_stats) + return cpufreq_stats_present_driver_data(policy, buf); + else if (policy->fast_switch_enabled) return 0; spin_lock(&stats->lock); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index e62b022cb07e..21074703e08c 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -138,6 +138,14 @@ struct cpufreq_policy { /* cpufreq-stats */ struct cpufreq_stats *stats; + /* + * Flag indicating that cpufreq statistics can be taken from device driver. + * The statistics are collected by firmware and the driver is able to retrieve + * them. It is useful when the 'fast switch' is used or the firmware changes + * frequency independently due to e.g. thermal issues. + */ + bool has_driver_stats; + /* For cpufreq driver's internal use */ void *driver_data; @@ -169,6 +177,18 @@ struct cpufreq_freqs { u8 flags; /* flags of cpufreq_driver, see below. */ }; +struct cpufreq_stats { + unsigned int total_trans; + unsigned long long last_time; + unsigned int max_state; + unsigned int state_num; + unsigned int last_index; + u64 *time_in_state; + spinlock_t lock; + unsigned int *freq_table; + unsigned int *trans_table; +}; + /* Only for ACPI */ #define CPUFREQ_SHARED_TYPE_NONE (0) /* None */ #define CPUFREQ_SHARED_TYPE_HW (1) /* HW does needed coordination */ @@ -245,11 +265,17 @@ void cpufreq_stats_create_table(struct cpufreq_policy *policy); void cpufreq_stats_free_table(struct cpufreq_policy *policy); void cpufreq_stats_record_transition(struct cpufreq_policy *policy, unsigned int new_freq); +int cpufreq_update_statistics(struct cpufreq_policy *policy); #else static inline void cpufreq_stats_create_table(struct cpufreq_policy *policy) { } static inline void cpufreq_stats_free_table(struct cpufreq_policy *policy) { } static inline void cpufreq_stats_record_transition(struct cpufreq_policy *policy, unsigned int new_freq) { } +static int __maybe_unused +cpufreq_update_statistics(struct cpufreq_policy *policy) +{ + return -EINVAL; +} #endif /* CONFIG_CPU_FREQ_STAT */ /********************************************************************* @@ -350,6 +376,12 @@ struct cpufreq_driver { /* Called to update policy limits on firmware notifications. */ void (*update_limits)(unsigned int cpu); + /* + * Optional, used when driver can fetch statistics from firmware. + * This callback function cannot sleep. + */ + int (*get_statistics)(struct cpufreq_policy *policy); + /* optional */ int (*bios_limit)(int cpu, unsigned int *limit); From patchwork Wed Jul 29 15:12:06 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Luba X-Patchwork-Id: 11691175 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 3210C14E3 for ; Wed, 29 Jul 2020 15:12:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 1FD6820829 for ; Wed, 29 Jul 2020 15:12:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726480AbgG2PMa (ORCPT ); Wed, 29 Jul 2020 11:12:30 -0400 Received: from foss.arm.com ([217.140.110.172]:53404 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726950AbgG2PM0 (ORCPT ); Wed, 29 Jul 2020 11:12:26 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id C20DC30E; Wed, 29 Jul 2020 08:12:25 -0700 (PDT) Received: from e123648.arm.com (unknown [10.37.12.35]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 99BD63F66E; Wed, 29 Jul 2020 08:12:23 -0700 (PDT) From: Lukasz Luba To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, cristian.marussi@arm.com, viresh.kumar@linaro.org, lukasz.luba@arm.com, rjw@rjwysocki.net Subject: [PATCH 2/4] scmi: perf: Extend protocol to support performance statistics Date: Wed, 29 Jul 2020 16:12:06 +0100 Message-Id: <20200729151208.27737-3-lukasz.luba@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200729151208.27737-1-lukasz.luba@arm.com> References: <20200729151208.27737-1-lukasz.luba@arm.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The firmware is able to maintain performance statistics and share with OS via shared memory. The memory region can be interpreted by the SCMI perf protocol after receiving and mapping the proper addresses from FW. This patch aims to provide needed infrastructure and setup necessary mechanisms in the protocol layer. It also extends API functions for the upper layer (cpufreq, devfreq) with a new callback, which allows to retrieve the statistics for a particular performance domain. The new structure scmi_perf_domain_stats added in the header works as a glue for these two layers. The data processing code for the shared memory is aligned with SCMI v2 specification (DEN0056B) and handles the required endianness. It can be changed in future not disturbing the upper layer. Signed-off-by: Lukasz Luba Reported-by: kernel test robot --- drivers/firmware/arm_scmi/perf.c | 210 +++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 11 ++ 2 files changed, 221 insertions(+) diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 3e1e87012c95..761067bb6237 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -19,6 +19,9 @@ #include "common.h" #include "notify.h" +#define PERF_DOMAIN_STATS_OFFSETS_BASE 0x10 +#define PERF_DOMAIN_COUNT_BASE 0x8 + enum scmi_performance_protocol_cmd { PERF_DOMAIN_ATTRIBUTES = 0x3, PERF_DESCRIBE_LEVELS = 0x4, @@ -32,11 +35,27 @@ enum scmi_performance_protocol_cmd { }; struct scmi_opp { + u32 id; u32 perf; u32 power; u32 trans_latency_us; }; +struct scmi_perf_level_raw_stats { + __le32 perf_level_id; + __le32 reserved; + __le64 usage_count; + __le64 total_residency_us; +}; + +struct scmi_perf_domain_raw_stats { + __le16 perf_level_count; + __le16 curr_perf_level_id; + __le32 extended_stats_offset; + __le64 ts_last_change_us; + struct scmi_perf_level_raw_stats perf_level[]; +}; + struct scmi_msg_resp_perf_attributes { __le16 num_domains; __le16 flags; @@ -161,13 +180,26 @@ struct perf_dom_info { struct scmi_fc_info *fc_info; }; +struct scmi_perf_domain_stats_desc { + void __iomem *addr; + int *opp_map; + int size; +}; + +struct scmi_perf_stats_desc { + uint16_t domain_count; + struct scmi_perf_domain_stats_desc *domain_stats; +}; + struct scmi_perf_info { u32 version; int num_domains; bool power_scale_mw; u64 stats_addr; u32 stats_size; + void __iomem *stats_virt_addr; struct perf_dom_info *dom_info; + struct scmi_perf_stats_desc *stats_desc; }; static enum scmi_performance_protocol_cmd evt_2_cmd[] = { @@ -175,6 +207,55 @@ static enum scmi_performance_protocol_cmd evt_2_cmd[] = { PERF_NOTIFY_LEVEL, }; +static int scmi_perf_stats_init(const struct scmi_handle *handle, + struct scmi_perf_info *pi) +{ + struct scmi_perf_domain_stats_desc *domain_stats; + int i, domain_count; + __le32 offset; + + domain_count = le16_to_cpu(ioread16(pi->stats_virt_addr + + PERF_DOMAIN_COUNT_BASE)); + + pi->stats_desc = devm_kzalloc(handle->dev, + sizeof(struct scmi_perf_stats_desc), + GFP_KERNEL); + if (!pi->stats_desc) + return -ENOMEM; + + pi->stats_desc->domain_stats = devm_kzalloc(handle->dev, domain_count * + sizeof(struct scmi_perf_domain_stats_desc), + GFP_KERNEL); + if (!pi->stats_desc->domain_stats) + return -ENOMEM; + + pi->stats_desc->domain_count = domain_count; + + for (i = 0; i < domain_count; i++) { + int stats_size; + __le16 opp_count; + + offset = ioread32(pi->stats_virt_addr + + PERF_DOMAIN_STATS_OFFSETS_BASE + i * 4); + if (!offset) + continue; + + domain_stats = &pi->stats_desc->domain_stats[i]; + + domain_stats->addr = pi->stats_virt_addr + le32_to_cpu(offset); + + /* The first field is the performance level count. */ + opp_count = le16_to_cpu(ioread16(domain_stats->addr)); + stats_size = sizeof(struct scmi_perf_domain_raw_stats); + stats_size += sizeof(struct scmi_perf_level_raw_stats) * + opp_count; + + domain_stats->size = stats_size; + } + + return 0; +} + static int scmi_perf_attributes_get(const struct scmi_handle *handle, struct scmi_perf_info *pi) { @@ -198,6 +279,14 @@ static int scmi_perf_attributes_get(const struct scmi_handle *handle, pi->stats_addr = le32_to_cpu(attr->stats_addr_low) | (u64)le32_to_cpu(attr->stats_addr_high) << 32; pi->stats_size = le32_to_cpu(attr->stats_size); + if (pi->stats_addr && pi->stats_size) { + pi->stats_virt_addr = devm_ioremap(handle->dev, + pi->stats_addr, pi->stats_size); + if (pi->stats_virt_addr) + ret = scmi_perf_stats_init(handle, pi); + else + ret = -ENOMEM; + } } scmi_xfer_put(handle, t); @@ -298,6 +387,7 @@ scmi_perf_describe_levels_get(const struct scmi_handle *handle, u32 domain, opp->power = le32_to_cpu(level_info->opp[cnt].power); opp->trans_latency_us = le16_to_cpu (level_info->opp[cnt].transition_latency_us); + opp->id = tot_opp_cnt + cnt; dev_dbg(handle->dev, "Level %d Power %d Latency %dus\n", opp->perf, opp->power, opp->trans_latency_us); @@ -748,6 +838,125 @@ static bool scmi_fast_switch_possible(const struct scmi_handle *handle, return dom->fc_info && dom->fc_info->level_set_addr; } +static int scmi_dvfs_setup_opps_mapping(const struct scmi_handle *handle, + u32 domain_id) +{ + struct scmi_perf_domain_stats_desc *domain_stats; + struct scmi_perf_info *pi = handle->perf_priv; + struct perf_dom_info *dom; + struct scmi_opp *opp; + int idx, *mapping; + + dom = pi->dom_info + domain_id; + if (!dom) + return -EIO; + + mapping = devm_kzalloc(handle->dev, sizeof(int) * dom->opp_count, + GFP_KERNEL); + if (!mapping) + return -ENOMEM; + + /* Construct LUT with FW OPP ids as an index */ + for (opp = dom->opp, idx = 0; idx < dom->opp_count; idx++, opp++) + mapping[opp->id] = idx; + + domain_stats = &pi->stats_desc->domain_stats[domain_id]; + domain_stats->opp_map = mapping; + + return 0; +} + +static int +scmi_dvfs_stats_get(const struct scmi_handle *handle, u32 domain_id, + struct scmi_perf_domain_stats *stats) +{ + struct scmi_perf_domain_stats_desc *domain_stats; + struct scmi_perf_domain_raw_stats *raw_stats[2]; + struct scmi_perf_info *pi = handle->perf_priv; + struct scmi_perf_level_raw_stats *perf; + int i, index, ret = -EINVAL; + struct perf_dom_info *dom; + u64 transition_count = 0; + struct scmi_opp *opp; + + if (!stats) + return -EINVAL; + + if (!pi->stats_virt_addr || !pi->stats_desc || + !pi->stats_desc->domain_stats) + return -ENOENT; + + if (pi->stats_desc->domain_count <= domain_id || + !pi->stats_desc->domain_stats[domain_id].addr) + return -ENOENT; + + dom = pi->dom_info + domain_id; + if (!dom) + return -EIO; + + domain_stats = &pi->stats_desc->domain_stats[domain_id]; + + if (!domain_stats->opp_map) { + ret = scmi_dvfs_setup_opps_mapping(handle, domain_id); + if (ret) + return ret; + } + + raw_stats[0] = vmalloc(domain_stats->size); + if (!raw_stats[0]) + return -ENOMEM; + + raw_stats[1] = vmalloc(domain_stats->size); + if (!raw_stats[1]) { + vfree(raw_stats[0]); + return -ENOMEM; + } + + /* + * Let's try 10 times. If two consecutive reads are the same - done. + * This approach is aligned with SCMI v2 specification. + */ + for (i = 0; i < 10; i++) { + memcpy_fromio(raw_stats[0], domain_stats->addr, + domain_stats->size); + memcpy_fromio(raw_stats[1], domain_stats->addr, + domain_stats->size); + if (!memcmp(raw_stats[0], raw_stats[1], domain_stats->size)) { + ret = 0; + break; + } + } + + if (ret) + goto free_buf; + + for (i = 0; i < dom->opp_count; i++) { + perf = &raw_stats[0]->perf_level[i]; + + transition_count += __le64_to_cpu(perf->usage_count); + stats->time_in_state[i] = + __le64_to_cpu(perf->total_residency_us); + + /* Speed-up and initialize the frequencies only once. */ + if (stats->freq_table[i] == 0) { + index = domain_stats->opp_map[i]; + opp = &dom->opp[index]; + stats->freq_table[i] = opp->perf * dom->mult_factor; + } + } + + stats->total_trans = transition_count; + + stats->last_index = __le16_to_cpu(raw_stats[0]->curr_perf_level_id); + stats->last_time = __le64_to_cpu(raw_stats[0]->ts_last_change_us); + +free_buf: + vfree(raw_stats[1]); + vfree(raw_stats[0]); + + return ret; +} + static struct scmi_perf_ops perf_ops = { .limits_set = scmi_perf_limits_set, .limits_get = scmi_perf_limits_get, @@ -760,6 +969,7 @@ static 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, + .statistics_get = scmi_dvfs_stats_get, }; static int scmi_perf_set_notify_enabled(const struct scmi_handle *handle, diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 7e5dd7d1e221..3316ff4f9d34 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -55,6 +55,15 @@ struct scmi_clock_info { }; }; +struct scmi_perf_domain_stats { + unsigned long long last_time; + unsigned long long last_index; + unsigned int total_trans; + unsigned int state_num; + u64 *time_in_state; + unsigned int *freq_table; +}; + struct scmi_handle; /** @@ -121,6 +130,8 @@ struct scmi_perf_ops { unsigned long *rate, unsigned long *power); bool (*fast_switch_possible)(const struct scmi_handle *handle, struct device *dev); + int (*statistics_get)(const struct scmi_handle *handle, u32 domain_id, + struct scmi_perf_domain_stats *stats); }; /** From patchwork Wed Jul 29 15:12:07 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Luba X-Patchwork-Id: 11691179 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B72A914E3 for ; Wed, 29 Jul 2020 15:12:40 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A95072083B for ; Wed, 29 Jul 2020 15:12:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726984AbgG2PMg (ORCPT ); Wed, 29 Jul 2020 11:12:36 -0400 Received: from foss.arm.com ([217.140.110.172]:53434 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726958AbgG2PMe (ORCPT ); Wed, 29 Jul 2020 11:12:34 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 3F9EDD6E; Wed, 29 Jul 2020 08:12:28 -0700 (PDT) Received: from e123648.arm.com (unknown [10.37.12.35]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 252B03F66E; Wed, 29 Jul 2020 08:12:25 -0700 (PDT) From: Lukasz Luba To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, cristian.marussi@arm.com, viresh.kumar@linaro.org, lukasz.luba@arm.com, rjw@rjwysocki.net Subject: [PATCH 3/4] cpufreq: scmi: Move scmi_cpufreq_driver structure to the top Date: Wed, 29 Jul 2020 16:12:07 +0100 Message-Id: <20200729151208.27737-4-lukasz.luba@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200729151208.27737-1-lukasz.luba@arm.com> References: <20200729151208.27737-1-lukasz.luba@arm.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Move the scmi_cpufreq_driver to top of the file in order to prepare for upcoming changes, which will extend it based on discoverable functionality. Signed-off-by: Lukasz Luba --- drivers/cpufreq/scmi-cpufreq.c | 36 +++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index fb42e3390377..fe95350eb844 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -24,6 +24,28 @@ struct scmi_data { struct device *cpu_dev; }; +static unsigned int scmi_cpufreq_get_rate(unsigned int cpu); +static int scmi_cpufreq_set_target(struct cpufreq_policy *policy, + unsigned int index); +static unsigned int scmi_cpufreq_fast_switch(struct cpufreq_policy *policy, + unsigned int target_freq); +static int scmi_cpufreq_init(struct cpufreq_policy *policy); +static int scmi_cpufreq_exit(struct cpufreq_policy *policy); + +static struct cpufreq_driver scmi_cpufreq_driver = { + .name = "scmi", + .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | + CPUFREQ_NEED_INITIAL_FREQ_CHECK | + CPUFREQ_IS_COOLING_DEV, + .verify = cpufreq_generic_frequency_table_verify, + .attr = cpufreq_generic_attr, + .target_index = scmi_cpufreq_set_target, + .fast_switch = scmi_cpufreq_fast_switch, + .get = scmi_cpufreq_get_rate, + .init = scmi_cpufreq_init, + .exit = scmi_cpufreq_exit, +}; + static const struct scmi_handle *handle; static unsigned int scmi_cpufreq_get_rate(unsigned int cpu) @@ -219,20 +241,6 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy) return 0; } -static struct cpufreq_driver scmi_cpufreq_driver = { - .name = "scmi", - .flags = CPUFREQ_STICKY | CPUFREQ_HAVE_GOVERNOR_PER_POLICY | - CPUFREQ_NEED_INITIAL_FREQ_CHECK | - CPUFREQ_IS_COOLING_DEV, - .verify = cpufreq_generic_frequency_table_verify, - .attr = cpufreq_generic_attr, - .target_index = scmi_cpufreq_set_target, - .fast_switch = scmi_cpufreq_fast_switch, - .get = scmi_cpufreq_get_rate, - .init = scmi_cpufreq_init, - .exit = scmi_cpufreq_exit, -}; - static int scmi_cpufreq_probe(struct scmi_device *sdev) { int ret; From patchwork Wed Jul 29 15:12:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lukasz Luba X-Patchwork-Id: 11691177 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0D6D613B6 for ; Wed, 29 Jul 2020 15:12:36 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id F3C0720829 for ; Wed, 29 Jul 2020 15:12:35 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726950AbgG2PMc (ORCPT ); Wed, 29 Jul 2020 11:12:32 -0400 Received: from foss.arm.com ([217.140.110.172]:53422 "EHLO foss.arm.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726958AbgG2PMb (ORCPT ); Wed, 29 Jul 2020 11:12:31 -0400 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id A7E931063; Wed, 29 Jul 2020 08:12:30 -0700 (PDT) Received: from e123648.arm.com (unknown [10.37.12.35]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 9F1BD3F66E; Wed, 29 Jul 2020 08:12:28 -0700 (PDT) From: Lukasz Luba To: linux-kernel@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-pm@vger.kernel.org Cc: sudeep.holla@arm.com, cristian.marussi@arm.com, viresh.kumar@linaro.org, lukasz.luba@arm.com, rjw@rjwysocki.net Subject: [PATCH 4/4] cpufreq: scmi: Read statistics from FW shared memory Date: Wed, 29 Jul 2020 16:12:08 +0100 Message-Id: <20200729151208.27737-5-lukasz.luba@arm.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20200729151208.27737-1-lukasz.luba@arm.com> References: <20200729151208.27737-1-lukasz.luba@arm.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Add support for reading CPUFreq statistics from the firmware. The setup code initializes needed structures per domain basis. The driver callback scmi_cpufreq_stats_get is called by the CPUFreq framework and invokes SCMI performance protocol in order to retrieve latest statistics. Signed-off-by: Lukasz Luba --- drivers/cpufreq/scmi-cpufreq.c | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index fe95350eb844..67ae63171dd0 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -22,6 +22,7 @@ struct scmi_data { int domain_id; struct device *cpu_dev; + struct scmi_perf_domain_stats *stats; }; static unsigned int scmi_cpufreq_get_rate(unsigned int cpu); @@ -48,6 +49,34 @@ static struct cpufreq_driver scmi_cpufreq_driver = { static const struct scmi_handle *handle; +static int scmi_cpufreq_stats_get(struct cpufreq_policy *policy) +{ + struct scmi_data *priv = policy->driver_data; + struct cpufreq_stats *stats = policy->stats; + int i, ret; + + /* + * Since the driver callback is global and can be set for all + * policy objects, there is a need to check if that policy has + * statistics in the shared memory. + */ + if (!policy->has_driver_stats) + return -EINVAL; + + ret = handle->perf_ops->statistics_get(handle, priv->domain_id, priv->stats); + if (ret) + return ret; + + for (i = 0; i < priv->stats->state_num; i++) + stats->time_in_state[i] = priv->stats->time_in_state[i]; + + stats->total_trans = priv->stats->total_trans; + stats->last_index = priv->stats->last_index; + stats->last_time = priv->stats->last_time; + + return ret; +} + static unsigned int scmi_cpufreq_get_rate(unsigned int cpu) { struct cpufreq_policy *policy = cpufreq_cpu_get_raw(cpu); @@ -147,6 +176,45 @@ scmi_get_cpu_power(unsigned long *power, unsigned long *KHz, return 0; } +static int scmi_cpufreq_stats_init(struct cpufreq_policy *policy, + struct scmi_data *priv, int nr_opp) +{ + struct scmi_perf_domain_stats *stats; + int ret; + + stats = kzalloc(sizeof(struct scmi_perf_domain_stats), GFP_KERNEL); + if (!stats) + return -ENOMEM; + + stats->time_in_state = kcalloc(nr_opp, sizeof(u64), GFP_KERNEL); + if (!stats->time_in_state) { + kfree(stats); + return -ENOMEM; + } + + stats->freq_table = kcalloc(nr_opp, sizeof(unsigned int), GFP_KERNEL); + if (!stats->freq_table) { + kfree(stats->time_in_state); + kfree(stats); + return -ENOMEM; + } + + priv->stats = stats; + priv->stats->state_num = nr_opp; + policy->has_driver_stats = true; + scmi_cpufreq_driver.get_statistics = scmi_cpufreq_stats_get; + + ret = handle->perf_ops->statistics_get(handle, priv->domain_id, stats); + if (ret) { + kfree(stats->freq_table); + kfree(stats->time_in_state); + kfree(stats); + policy->has_driver_stats = false; + } + + return ret; +} + static int scmi_cpufreq_init(struct cpufreq_policy *policy) { int ret, nr_opp; @@ -209,6 +277,10 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) /* SCMI allows DVFS request for any domain from any CPU */ policy->dvfs_possible_from_any_cpu = true; + ret = scmi_cpufreq_stats_init(policy, priv, nr_opp); + if (ret) + dev_warn(cpu_dev, "failed to init statistics: %d\n", ret); + latency = handle->perf_ops->transition_latency_get(handle, cpu_dev); if (!latency) latency = CPUFREQ_ETERNAL; @@ -236,6 +308,14 @@ static int scmi_cpufreq_exit(struct cpufreq_policy *policy) dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table); dev_pm_opp_remove_all_dynamic(priv->cpu_dev); + + if (priv->stats) { + policy->has_driver_stats = false; + kfree(priv->stats->time_in_state); + kfree(priv->stats->freq_table); + kfree(priv->stats); + } + kfree(priv); return 0;