From patchwork Fri Apr 8 04:24:54 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Chanwoo Choi X-Patchwork-Id: 8780331 Return-Path: X-Original-To: patchwork-linux-samsung-soc@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 1D1C59F659 for ; Fri, 8 Apr 2016 04:30:19 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CDCA620204 for ; Fri, 8 Apr 2016 04:30:17 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 174CD2026F for ; Fri, 8 Apr 2016 04:30:15 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756283AbcDHE37 (ORCPT ); Fri, 8 Apr 2016 00:29:59 -0400 Received: from mailout2.samsung.com ([203.254.224.25]:45419 "EHLO mailout2.samsung.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752274AbcDHEZT (ORCPT ); Fri, 8 Apr 2016 00:25:19 -0400 Received: from epcpsbgr2.samsung.com (u142.gpu120.samsung.co.kr [203.254.230.142]) by mailout2.samsung.com (Oracle Communications Messaging Server 7.0.5.31.0 64bit (built May 5 2014)) with ESMTP id <0O5A00C6PSA0Y120@mailout2.samsung.com>; Fri, 08 Apr 2016 13:25:12 +0900 (KST) Received: from epcpsbgm2new.samsung.com ( [172.20.52.115]) by epcpsbgr2.samsung.com (EPCPMTA) with SMTP id F6.72.04785.8A237075; Fri, 8 Apr 2016 13:25:12 +0900 (KST) X-AuditID: cbfee68e-f79d96d0000012b1-da-570732a84bfd Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm2new.samsung.com (EPCPMTA) with SMTP id 64.D6.06699.8A237075; Fri, 8 Apr 2016 13:25:12 +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 <0O5A00DKDS9YHX20@mmp2.samsung.com>; Fri, 08 Apr 2016 13:25:11 +0900 (KST) From: Chanwoo Choi To: myungjoo.ham@samsung.com, kyungmin.park@samsung.com, k.kozlowski@samsung.com, kgene@kernel.org 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 v8 05/20] PM / devfreq: Add new passive governor Date: Fri, 08 Apr 2016 13:24:54 +0900 Message-id: <1460089509-16260-6-git-send-email-cw00.choi@samsung.com> X-Mailer: git-send-email 1.8.0 In-reply-to: <1460089509-16260-1-git-send-email-cw00.choi@samsung.com> References: <1460089509-16260-1-git-send-email-cw00.choi@samsung.com> X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFjrAIsWRmVeSWpSXmKPExsWyRsSkWHeFEXu4waw/BhbXvzxntZh/5Byr Rf+bhawW516tZLSYdH8Ci8XrF4YW/Y9fM1ucbXrDbrHp8TVWi8u75rBZfO49wmgx4/w+Jot1 G2+xW9y+zGvx8sgPRoul1y8yWdxuXMFmMWH6WhaLM6cvsVq07j3CbtG2+gOrg6jHmnlrGD1a mnvYPC739TJ53LpT77Fz1l12j5XLv7B5bFrVyeaxeUm9x79j7B5brrazePRtWcXo8XmTXABP FJdNSmpOZllqkb5dAlfGqdnr2QqmhVWs+nqSqYHxoVsXIyeHhICJxIMPU9ggbDGJC/fWA9lc HEICKxglXjxbzN7FyAFWNPVKKkR8FqPEq587WSGcL4wSXw5eAOtmE9CS2P/iBpgtIpAi8fjh SbBJzAJHmCWmbrzBBJIQFnCQeLN1J1gRi4CqRNOhr6wgNq+Aq8SNSaeZIM6Qk/iw5xE7iM0p 4CYx+cokZhBbCKRm83RmkKESAjs4JDpXTYEaJCDxbfIhFohTZSU2HWCGmCMpcXDFDZYJjMIL GBlWMYqmFiQXFCelFxnpFSfmFpfmpesl5+duYgRG7Ol/z/p2MN48YH2IUYCDUYmH98J7tnAh 1sSy4srcQ4ymQBsmMkuJJucD00JeSbyhsZmRhamJqbGRuaWZkjhvgtTPYCGB9MSS1OzU1ILU ovii0pzU4kOMTBycUg2MyTKdq18V7zjAyHpdqf/r4YxXq6LO3VSPbL5lPlvE8mfOYefqmsbH J77OtFywYKJLIjv3+82lPYeOpSQwGqx/2Xu9yWWVX2/GU+/YvOKHs+oqmOfmGja7W+37ZhN2 oSZkfcQCuZi9luVTeLSuZh65vM0pc1e905x5qkuuiCyP+1D15NiCrQbnlViKMxINtZiLihMB Kp/tb9MCAAA= X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFuplleLIzCtJLcpLzFFi42I5/e+xoO4KI/Zwg1MLNCyuf3nOajH/yDlW i/43C1ktzr1ayWgx6f4EFovXLwwt+h+/ZrY42/SG3WLT42usFpd3zWGz+Nx7hNFixvl9TBbr Nt5it7h9mdfi5ZEfjBZLr19ksrjduILNYsL0tSwWZ05fYrVo3XuE3aJt9QdWB1GPNfPWMHq0 NPeweVzu62XyuHWn3mPnrLvsHiuXf2Hz2LSqk81j85J6j3/H2D22XG1n8ejbsorR4/MmuQCe qAZGm4zUxJTUIoXUvOT8lMy8dFsl7+B453hTMwNDXUNLC3MlhbzE3FRbJRefAF23zBygX5UU yhJzSoFCAYnFxUr6dpgmhIa46VrANEbo+oYEwfUYGaCBhDWMGadmr2crmBZWserrSaYGxodu XYwcHBICJhJTr6R2MXICmWISF+6tZ+ti5OIQEpjFKPHq505WCOcLo8SXgxfYQKrYBLQk9r+4 AWaLCKRIPH54EqyDWeAIs8TUjTeYQBLCAg4Sb7buBCtiEVCVaDr0lRXE5hVwlbgx6TQTxDo5 iQ97HrGD2JwCbhKTr0xiBrGFQGo2T2eewMi7gJFhFaNEakFyQXFSeq5RXmq5XnFibnFpXrpe cn7uJkZwUngmvYPx8C73Q4wCHIxKPLwX3rOFC7EmlhVX5h5ilOBgVhLhddVnDxfiTUmsrEot yo8vKs1JLT7EaAp02ERmKdHkfGDCyiuJNzQ2MTOyNDI3tDAyNlcS5338f12YkEB6Yklqdmpq QWoRTB8TB6dUAyP/Mjen9sfz5obJnrg46eLF+m1/Dsw0vp3wqll+130nlxX2mcnmRQZfFVN+ /uEzDQ5N2PSFqXl3z+7t+hukt/cebPmjpFqq9+Ve8cXfR+KO/dUoPXr75vxGy4TD2zV0f316 uOP1Eut9eZf+et2dEf1LM6vifa9acZbOxC13nnYcfu3891XNgppjSizFGYmGWsxFxYkAjlT6 iSADAAA= DLP-Filter: Pass X-MTR: 20000000000000000@CPGS X-CFilter-Loop: Reflected Sender: linux-samsung-soc-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-samsung-soc@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 Signed-off-by: MyungJoo Ham --- 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..28a9ae32d330 --- /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(&devfreq->lock); + + 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,