From patchwork Mon Sep 23 15:51:08 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11157191 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 98D9C14ED for ; Mon, 23 Sep 2019 15:51:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7771F205F4 for ; Mon, 23 Sep 2019 15:51:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S2393894AbfIWPvS (ORCPT ); Mon, 23 Sep 2019 11:51:18 -0400 Received: from inva020.nxp.com ([92.121.34.13]:59314 "EHLO inva020.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S2394071AbfIWPvS (ORCPT ); Mon, 23 Sep 2019 11:51:18 -0400 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 2B89C1A09B2; Mon, 23 Sep 2019 17:51:16 +0200 (CEST) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 1E1621A0321; Mon, 23 Sep 2019 17:51:16 +0200 (CEST) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 6D0EF20613; Mon, 23 Sep 2019 17:51:15 +0200 (CEST) From: Leonard Crestez To: Matthias Kaehlcke , MyungJoo Ham , Kyungmin Park Cc: Chanwoo Choi , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Abel Vesa , Jacky Bai , Viresh Kumar , Lukasz Luba , linux-pm@vger.kernel.org, linux-arm-kernel@lists.infradead.org, NXP Linux Team Subject: [PATCH v6 5/6] PM / devfreq: Add PM QoS support Date: Mon, 23 Sep 2019 18:51:08 +0300 Message-Id: <55807c847f072690f3ec904168023484767dc603.1569252537.git.leonard.crestez@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Register notifiers with the PM QoS framework in order to respond to requests for DEV_PM_QOS_MIN_FREQUENCY and DEV_PM_QOS_MAX_FREQUENCY. Signed-off-by: Leonard Crestez --- drivers/devfreq/devfreq.c | 71 +++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 5 +++ 2 files changed, 76 insertions(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b6acb827fee5..a4c7dde17a06 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -22,17 +22,20 @@ #include #include #include #include #include +#include #include "governor.h" #define HZ_PER_KHZ 1000 #define CREATE_TRACE_POINTS #include +#define HZ_PER_KHZ 1000 + static struct class *devfreq_class; /* * devfreq core provides delayed work based load monitoring helper * functions. Governors can use these or can implement their own @@ -123,10 +126,16 @@ static void devfreq_get_freq_range(struct devfreq *devfreq, } else { *min_freq = freq_table[devfreq->profile->max_state - 1]; *max_freq = freq_table[0]; } + /* constraints from PM QoS: */ + *min_freq = max(*min_freq, HZ_PER_KHZ * (unsigned long)dev_pm_qos_read_value( + devfreq->dev.parent, DEV_PM_QOS_MIN_FREQUENCY)); + *max_freq = min(*max_freq, HZ_PER_KHZ * (unsigned long)dev_pm_qos_read_value( + devfreq->dev.parent, DEV_PM_QOS_MAX_FREQUENCY)); + /* constraints from sysfs: */ *min_freq = max(*min_freq, devfreq->min_freq); *max_freq = min(*max_freq, devfreq->max_freq); /* constraints from OPP interface: */ @@ -604,10 +613,49 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, mutex_unlock(&devfreq->lock); return ret; } +/** + * devfreq_qos_notifier_call() - Common handler for qos freq changes. + * @devfreq: the devfreq instance. + */ +static int devfreq_qos_notifier_call(struct devfreq *devfreq) +{ + int ret; + + mutex_lock(&devfreq->lock); + ret = update_devfreq(devfreq); + mutex_unlock(&devfreq->lock); + + return ret; +} + +/** + * devfreq_qos_min_notifier_call() - Callback for qos min_freq changes. + * @nb: Should to be devfreq->nb_min + */ +static int devfreq_qos_min_notifier_call(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + struct devfreq *devfreq = container_of(nb, struct devfreq, nb_min); + + return devfreq_qos_notifier_call(devfreq); +} + +/** + * devfreq_qos_max_notifier_call() - Callback for qos min_freq changes. + * @nb: Should to be devfreq->nb_max + */ +static int devfreq_qos_max_notifier_call(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + struct devfreq *devfreq = container_of(nb, struct devfreq, nb_max); + + return devfreq_qos_notifier_call(devfreq); +} + /** * devfreq_dev_release() - Callback for struct device to release the device. * @dev: the devfreq device * * Remove devfreq from the list and release its resources. @@ -618,10 +666,15 @@ static void devfreq_dev_release(struct device *dev) mutex_lock(&devfreq_list_lock); list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); + dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, + DEV_PM_QOS_MAX_FREQUENCY); + dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min, + DEV_PM_QOS_MIN_FREQUENCY); + if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); kfree(devfreq->time_in_state); kfree(devfreq->trans_table); @@ -731,10 +784,27 @@ struct devfreq *devfreq_add_device(struct device *dev, if (err) { put_device(&devfreq->dev); goto err_out; } + /* + * Register notifiers for updates to min_freq/max_freq after device is + * initialized (and we can handle notifications) but before the governor + * is started (which should do an initial enforcement of constraints) + */ + devfreq->nb_min.notifier_call = devfreq_qos_min_notifier_call; + err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, + DEV_PM_QOS_MIN_FREQUENCY); + if (err) + goto err_devfreq; + + devfreq->nb_max.notifier_call = devfreq_qos_max_notifier_call; + err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max, + DEV_PM_QOS_MAX_FREQUENCY); + if (err) + goto err_devfreq; + mutex_lock(&devfreq_list_lock); governor = try_then_request_governor(devfreq->governor_name); if (IS_ERR(governor)) { dev_err(dev, "%s: Unable to find governor for the device\n", @@ -758,10 +828,11 @@ struct devfreq *devfreq_add_device(struct device *dev, return devfreq; err_init: mutex_unlock(&devfreq_list_lock); +err_devfreq: devfreq_remove_device(devfreq); return ERR_PTR(err); err_dev: /* diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index c3cbc15fdf08..dac0dffeabb4 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -134,10 +134,12 @@ struct devfreq_dev_profile { * @total_trans: Number of devfreq transitions * @trans_table: Statistics of devfreq transitions * @time_in_state: Statistics of devfreq states * @last_stat_updated: The last time stat updated * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier + * @nb_min: Notifier block for DEV_PM_QOS_MIN_FREQUENCY + * @nb_max: Notifier block for DEV_PM_QOS_MAX_FREQUENCY * * This structure stores the devfreq information for a give device. * * Note that when a governor accesses entries in struct devfreq in its * functions except for the context of callbacks defined in struct @@ -176,10 +178,13 @@ struct devfreq { unsigned int *trans_table; unsigned long *time_in_state; unsigned long last_stat_updated; struct srcu_notifier_head transition_notifier_list; + + struct notifier_block nb_min; + struct notifier_block nb_max; }; struct devfreq_freqs { unsigned long old; unsigned long new;