From patchwork Mon Apr 11 03:57:43 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chanwoo Choi X-Patchwork-Id: 8796461 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id 6437AC0553 for ; Mon, 11 Apr 2016 04:00:46 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 2B1C320268 for ; Mon, 11 Apr 2016 04:00:45 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CDBA520254 for ; Mon, 11 Apr 2016 04:00:43 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752199AbcDKD6O (ORCPT ); Sun, 10 Apr 2016 23:58:14 -0400 Received: from mailout4.samsung.com ([203.254.224.34]:40232 "EHLO mailout4.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752108AbcDKD6M (ORCPT ); Sun, 10 Apr 2016 23:58:12 -0400 Received: from epcpsbgr3.samsung.com (u143.gpu120.samsung.co.kr [203.254.230.143]) by mailout4.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0O5G02LGXB0OUD50@mailout4.samsung.com>; Mon, 11 Apr 2016 12:58:00 +0900 (KST) Received: from epcpsbgm2new.samsung.com ( [172.20.52.112]) by epcpsbgr3.samsung.com (EPCPMTA) with SMTP id 47.3B.04781.8C02B075; Mon, 11 Apr 2016 12:58:00 +0900 (KST) X-AuditID: cbfee68f-f79c86d0000012ad-71-570b20c8f5bf Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm2new.samsung.com (EPCPMTA) with SMTP id ED.19.06699.8C02B075; Mon, 11 Apr 2016 12:58:00 +0900 (KST) Received: from chan.10.32.193.11 ([10.113.62.212]) by mmp2.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTPA id <0O5G00DWZB0M9H20@mmp2.samsung.com>; Mon, 11 Apr 2016 12:57:59 +0900 (KST) From: Chanwoo Choi To: myungjoo.ham@samsung.com, kyungmin.park@samsung.com, k.kozlowski@samsung.com, kgene@kernel.org, s.nawrocki@samsung.com, tomasz.figa@gmail.com Cc: rjw@rjwysocki.net, robh+dt@kernel.org, pawel.moll@arm.com, mark.rutland@arm.com, ijc+devicetree@hellion.org.uk, galak@codeaurora.org, linux@arm.linux.org.uk, linux.amoon@gmail.com, m.reichl@fivetechno.de, tjakobi@math.uni-bielefeld.de, inki.dae@samsung.com, cw00.choi@samsung.com, linux-kernel@vger.kernel.org, linux-pm@vger.kernel.org, linux-samsung-soc@vger.kernel.org, linux-arm-kernel@lists.infradead.org, devicetree@vger.kernel.org Subject: [PATCH v9 05/20] PM / devfreq: Add new passive governor Date: Mon, 11 Apr 2016 12:57:43 +0900 Message-id: <1460347078-15175-6-git-send-email-cw00.choi@samsung.com> X-Mailer: git-send-email 1.8.0 In-reply-to: <1460347078-15175-1-git-send-email-cw00.choi@samsung.com> References: <1460347078-15175-1-git-send-email-cw00.choi@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrFIsWRmVeSWpSXmKPExsWyRsSkQPeEAne4wfot5hbXvzxntZh/5Byr Rf+bhawW516tZLSYdH8Ci8XrF4YW/Y9fM1ucbXrDbrHp8TVWi8u75rBZfO49wmgx4/w+Jot1 G2+xW9y+zGvx8sgPRoul1y8yWdxuXMFmMWH6WhaLM6cvsVq07j3CbnH4TTurRdvqD6wWq3b9 YXQQ91gzbw2jR0tzD5vH5b5eJo9bd+o9ds66y+6xcvkXNo9NqzrZPDYvqff4d4zdY8vVdhaP vi2rGD0+b5IL4InisklJzcksSy3St0vgyvi4/i1zwZrwiqZDe1kaGJ+7dzFyckgImEh8PvOX GcIWk7hwbz1bFyMXh5DACkaJ4wsPMcIU3T7dzA6RmMUoMaXlOxOE84VRYsXaNUwgVWwCWhL7 X9wAaxcRmMoo8flUGwuIwyxwhFli6sYbQFUcHMICDhI3/nqBNLAIqEp8avvLDmLzCrhKnJox nwlinZzEhz2PwOKcAm4Sn9a3s4K0CgHVPPtcBzJSQuAIh8S8QxNYIOYISHybfIgFpEZCQFZi 0wGodyQlDq64wTKBUXgBI8MqRtHUguSC4qT0ImO94sTc4tK8dL3k/NxNjMAoPv3vWf8OxrsH rA8xCnAwKvHwOlzjChdiTSwrrsw9xGgKtGEis5Rocj4wVeSVxBsamxlZmJqYGhuZW5opifMu lPoZLCSQnliSmp2aWpBaFF9UmpNafIiRiYNTqoFRzax3631tr/2zM7sX/fTn/bzWTeVNQSLf 165XGW/46+aeS/xWJ1vWnjJhfWTu6Tu8Lx95zv7jY/VlpfDp2XfyLubu8SmsnDWjfV3tiq9L ZnYzHjxjVVzUvqjCccvLfZyFaqX7Ai8neB380Gxwa/9PyZ/dCZ0311fl3tt/ncPg47ey6xoK U7QZlViKMxINtZiLihMBcTXHON0CAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprOKsWRmVeSWpSXmKPExsVy+t9jQd0TCtzhBtNO6Vlc//Kc1WL+kXOs Fv1vFrJanHu1ktFi0v0JLBavXxha9D9+zWxxtukNu8Wmx9dYLS7vmsNm8bn3CKPFjPP7mCzW bbzFbnH7Mq/FyyM/GC2WXr/IZHG7cQWbxYTpa1kszpy+xGrRuvcIu8XhN+2sFm2rP7BarNr1 h9FB3GPNvDWMHi3NPWwel/t6mTxu3an32DnrLrvHyuVf2Dw2repk89i8pN7j3zF2jy1X21k8 +rasYvT4vEkugCeqgdEmIzUxJbVIITUvOT8lMy/dVsk7ON453tTMwFDX0NLCXEkhLzE31VbJ xSdA1y0zB+hrJYWyxJxSoFBAYnGxkr4dpgmhIW66FjCNEbq+IUFwPUYGaCBhDWPGx/VvmQvW hFc0HdrL0sD43L2LkZNDQsBE4vbpZnYIW0ziwr31bF2MXBxCArMYJaa0fGeCcL4wSqxYu4YJ pIpNQEti/4sbYFUiAlMZJT6famMBcZgFjjBLTN14A6iKg0NYwEHixl8vkAYWAVWJT21/wVbw CrhKnJoxnwlinZzEhz2PwOKcAm4Sn9a3s4K0CgHVPPtcN4GRdwEjwypGidSC5ILipPRco7zU cr3ixNzi0rx0veT83E2M4ETxTHoH4+Fd7ocYBTgYlXh4X1zmChdiTSwrrsw9xCjBwawkwvtA ljtciDclsbIqtSg/vqg0J7X4EKMp0F0TmaVEk/OBSSyvJN7Q2MTMyNLI3NDCyNhcSZz38f91 YUIC6YklqdmpqQWpRTB9TBycUg2MyTtUZtUeMdrIw/lWsNzLdRm/vHDyoy/1P4S486ZqCOp/ uXfRrqi8+t4lw2judBMx/uc5nR/f3pe5t/kRpzVboMRSIUbN81elTgs9NlSqu8gtJv2/94va im/W4TuCHZRWfuXdVGwwq8fI5Ep5s39nmKR8clmgbt2ZovBC68q4g99mbumYc1eJpTgj0VCL uag4EQCHRXkcKgMAAA== DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-CFilter-Loop: Reflected Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-7.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This patch adds the new passive governor for DEVFREQ framework. The following governors are already present and used for DVFS (Dynamic Voltage and Frequency Scaling) drivers. The following governors are independently used for one device driver which don't give the influence to other device drviers and also don't receive the effect from other device drivers. - ondemand / performance / powersave / userspace The passive governor depends on operation of parent driver with specific governos extremely and is not able to decide the new frequency by oneself. According to the decided new frequency of parent driver with governor, the passive governor uses it to decide the appropriate frequency for own device driver. The passive governor must need the following information from device tree: - the source clock and OPP tables - the instance of parent device For exameple, there are one more devfreq device drivers which need to change their source clock according to their utilization on runtime. But, they share the same power line (e.g., regulator). So, specific device driver is operated as parent with ondemand governor and then the rest device driver with passive governor is influenced by parent device. Suggested-by: Myungjoo Ham Signed-off-by: Chanwoo Choi [tjakobi: Reported RCU locking issue and cw00.choi fix it] Reported-by: Tobias Jakobi [linux.amoon: Reported possible recursive locking and cw00.choi fix it] Reported-by: Anand Moon Signed-off-by: MyungJoo Ham Acked-by: Krzysztof Kozlowski --- drivers/devfreq/Kconfig | 8 ++ drivers/devfreq/Makefile | 1 + drivers/devfreq/governor_passive.c | 207 +++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 33 ++++++ 4 files changed, 249 insertions(+) create mode 100644 drivers/devfreq/governor_passive.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index cedda8f1e5eb..d260cd0219f6 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -64,6 +64,14 @@ config DEVFREQ_GOV_USERSPACE Otherwise, the governor does not change the frequency given at the initialization. +config DEVFREQ_GOV_PASSIVE + tristate "Passive" + help + Sets the frequency based on the frequency of its parent devfreq + device. This governor does not change the frequency by itself + through sysfs entries. The passive governor recommends that + devfreq device uses the OPP table to get the frequency/voltage. + comment "DEVFREQ Drivers" config ARM_EXYNOS_BUS_DEVFREQ diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 8af8aaf922a8..2633087d5c63 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND) += governor_simpleondemand.o obj-$(CONFIG_DEVFREQ_GOV_PERFORMANCE) += governor_performance.o obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o +obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c new file mode 100644 index 000000000000..a4b0b02ee797 --- /dev/null +++ b/drivers/devfreq/governor_passive.c @@ -0,0 +1,207 @@ +/* + * linux/drivers/devfreq/governor_passive.c + * + * Copyright (C) 2016 Samsung Electronics + * Author: Chanwoo Choi + * Author: MyungJoo Ham + * + * 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. + */ + +#include +#include +#include +#include "governor.h" + +static int devfreq_passive_get_target_freq(struct devfreq *devfreq, + unsigned long *freq) +{ + struct devfreq_passive_data *p_data + = (struct devfreq_passive_data *)devfreq->data; + struct devfreq *parent_devfreq = (struct devfreq *)p_data->parent; + unsigned long child_freq = ULONG_MAX; + struct dev_pm_opp *opp; + int i, count, ret = 0; + + /* + * If the devfreq device with passive governor has the specific method + * to determine the next frequency, should use the get_target_freq() + * of struct devfreq_passive_data. + */ + if (p_data->get_target_freq) { + ret = p_data->get_target_freq(devfreq, freq); + goto out; + } + + /* + * If the parent and passive devfreq device uses the OPP table, + * get the next frequency by using the OPP table. + */ + + /* + * - parent devfreq device uses the governors except for passive. + * - passive devfreq device uses the passive governor. + * + * Each devfreq has the OPP table. After deciding the new frequency + * from the governor of parent devfreq device, the passive governor + * need to get the index of new frequency on OPP table of parent + * device. And then the index is used for getting the suitable + * new frequency for passive devfreq device. + */ + if (!devfreq->profile || !devfreq->profile->freq_table + || devfreq->profile->max_state <= 0) + return -EINVAL; + + /* + * The passive governor have to get the correct frequency from OPP + * list of parent device. Because in this case, *freq is temporary + * value which is decided by ondemand governor. + */ + rcu_read_lock(); + opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0); + rcu_read_unlock(); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); + goto out; + } + + /* + * Get the OPP table's index of decided freqeuncy by governor + * of parent device. + */ + for (i = 0; i < parent_devfreq->profile->max_state; i++) + if (parent_devfreq->profile->freq_table[i] == *freq) + break; + + if (i == parent_devfreq->profile->max_state) { + ret = -EINVAL; + goto out; + } + + /* Get the suitable frequency by using index of parent device. */ + if (i < devfreq->profile->max_state) { + child_freq = devfreq->profile->freq_table[i]; + } else { + count = devfreq->profile->max_state; + child_freq = devfreq->profile->freq_table[count - 1]; + } + + /* Return the suitable frequency for passive device. */ + *freq = child_freq; + +out: + return ret; +} + +static int update_devfreq_passive(struct devfreq *devfreq, unsigned long freq) +{ + int ret; + + if (!devfreq->governor) + return -EINVAL; + + mutex_lock_nested(&devfreq->lock, SINGLE_DEPTH_NESTING); + + ret = devfreq->governor->get_target_freq(devfreq, &freq); + if (ret < 0) + goto out; + + ret = devfreq->profile->target(devfreq->dev.parent, &freq, 0); + if (ret < 0) + goto out; + + devfreq->previous_freq = freq; + +out: + mutex_unlock(&devfreq->lock); + + return 0; +} + +static int devfreq_passive_notifier_call(struct notifier_block *nb, + unsigned long event, void *ptr) +{ + struct devfreq_passive_data *data + = container_of(nb, struct devfreq_passive_data, nb); + struct devfreq *devfreq = (struct devfreq *)data->this; + struct devfreq *parent = (struct devfreq *)data->parent; + struct devfreq_freqs *freqs = (struct devfreq_freqs *)ptr; + unsigned long freq = freqs->new; + + switch (event) { + case DEVFREQ_PRECHANGE: + if (parent->previous_freq > freq) + update_devfreq_passive(devfreq, freq); + break; + case DEVFREQ_POSTCHANGE: + if (parent->previous_freq < freq) + update_devfreq_passive(devfreq, freq); + break; + } + + return NOTIFY_DONE; +} + +static int devfreq_passive_event_handler(struct devfreq *devfreq, + unsigned int event, void *data) +{ + struct device *dev = devfreq->dev.parent; + struct devfreq_passive_data *p_data + = (struct devfreq_passive_data *)devfreq->data; + struct devfreq *parent = (struct devfreq *)p_data->parent; + struct notifier_block *nb = &p_data->nb; + int ret = 0; + + if (!parent) + return -EPROBE_DEFER; + + switch (event) { + case DEVFREQ_GOV_START: + if (!p_data->this) + p_data->this = devfreq; + + nb->notifier_call = devfreq_passive_notifier_call; + ret = devm_devfreq_register_notifier(dev, parent, nb, + DEVFREQ_TRANSITION_NOTIFIER); + break; + case DEVFREQ_GOV_STOP: + devm_devfreq_unregister_notifier(dev, parent, nb, + DEVFREQ_TRANSITION_NOTIFIER); + break; + default: + break; + } + + return ret; +} + +static struct devfreq_governor devfreq_passive = { + .name = "passive", + .get_target_freq = devfreq_passive_get_target_freq, + .event_handler = devfreq_passive_event_handler, +}; + +static int __init devfreq_passive_init(void) +{ + return devfreq_add_governor(&devfreq_passive); +} +subsys_initcall(devfreq_passive_init); + +static void __exit devfreq_passive_exit(void) +{ + int ret; + + ret = devfreq_remove_governor(&devfreq_passive); + if (ret) + pr_err("%s: failed remove governor %d\n", __func__, ret); + + return; +} +module_exit(devfreq_passive_exit); + +MODULE_AUTHOR("Chanwoo Choi "); +MODULE_AUTHOR("MyungJoo Ham "); +MODULE_DESCRIPTION("DEVFREQ Passive governor"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 98c699304e12..2de4e2eea180 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -272,6 +272,39 @@ struct devfreq_simple_ondemand_data { }; #endif +#if IS_ENABLED(CONFIG_DEVFREQ_GOV_PASSIVE) +/** + * struct devfreq_passive_data - void *data fed to struct devfreq + * and devfreq_add_device + * @parent: the devfreq instance of parent device. + * @get_target_freq: Optional callback, Returns desired operating frequency + * for the device using passive governor. That is called + * when passive governor should decide the next frequency + * by using the new frequency of parent devfreq device + * using governors except for passive governor. + * If the devfreq device has the specific method to decide + * the next frequency, should use this callback. + * @this: the devfreq instance of own device. + * @nb: the notifier block for DEVFREQ_TRANSITION_NOTIFIER list + * + * The devfreq_passive_data have to set the devfreq instance of parent + * device with governors except for the passive governor. But, don't need to + * initialize the 'this' and 'nb' field because the devfreq core will handle + * them. + */ +struct devfreq_passive_data { + /* Should set the devfreq instance of parent device */ + struct devfreq *parent; + + /* Optional callback to decide the next frequency of passvice device */ + int (*get_target_freq)(struct devfreq *this, unsigned long *freq); + + /* For passive governor's internal use. Don't need to set them */ + struct devfreq *this; + struct notifier_block nb; +}; +#endif + #else /* !CONFIG_PM_DEVFREQ */ static inline struct devfreq *devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile,