From patchwork Wed May 11 07:58:43 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: MyungJoo Ham X-Patchwork-Id: 775862 Received: from smtp1.linux-foundation.org (smtp1.linux-foundation.org [140.211.169.13]) by demeter2.kernel.org (8.14.4/8.14.3) with ESMTP id p4B84w7W031486 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Wed, 11 May 2011 08:05:19 GMT Received: from daredevil.linux-foundation.org (localhost [127.0.0.1]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id p4B83Vca021659; Wed, 11 May 2011 01:03:31 -0700 Received: from mailout3.samsung.com (mailout3.samsung.com [203.254.224.33]) by smtp1.linux-foundation.org (8.14.2/8.13.5/Debian-3ubuntu1.1) with ESMTP id p4B7wkMq021337 for ; Wed, 11 May 2011 00:59:24 -0700 Received: from epcpsbgm2.samsung.com (mailout3.samsung.com [203.254.224.33]) by mailout3.samsung.com (Oracle Communications Messaging Exchange Server 7u4-19.01 64bit (built Sep 7 2010)) with ESMTP id <0LL0005GPU5VCQA0@mailout3.samsung.com> for linux-pm@lists.linux-foundation.org; Wed, 11 May 2011 16:58:44 +0900 (KST) X-AuditID: cbfee61b-b7bb4ae000002c54-b5-4dca41b311e9 Received: from epmmp2 ( [203.254.227.17]) by epcpsbgm2.samsung.com (MMPCPMTA) with SMTP id 77.29.11348.3B14ACD4; Wed, 11 May 2011 16:58:44 +0900 (KST) Received: from TNRNDGASPAPP1.tn.corp.samsungelectronics.net ([165.213.149.150]) by mmp2.samsung.com (iPlanet Messaging Server 5.2 Patch 2 (built Jul 14 2004)) with ESMTPA id <0LL000MOVU5W32@mmp2.samsung.com> for linux-pm@lists.linux-foundation.org; Wed, 11 May 2011 16:58:44 +0900 (KST) Received: from localhost.localdomain ([165.213.219.116]) by TNRNDGASPAPP1.tn.corp.samsungelectronics.net with Microsoft SMTPSVC(6.0.3790.4675); Wed, 11 May 2011 16:58:43 +0900 Date: Wed, 11 May 2011 16:58:43 +0900 From: MyungJoo Ham In-reply-to: <1305100723-29161-1-git-send-email-myungjoo.ham@samsung.com> To: linux-pm@lists.linux-foundation.org Message-id: <1305100723-29161-3-git-send-email-myungjoo.ham@samsung.com> X-Mailer: git-send-email 1.7.4.1 References: <1305100723-29161-1-git-send-email-myungjoo.ham@samsung.com> X-OriginalArrivalTime: 11 May 2011 07:58:43.0692 (UTC) FILETIME=[3F8FD2C0:01CC0FB1] X-Brightmail-Tracker: AAAAAA== Received-SPF: pass (localhost is always allowed.) X-Spam-Status: No, hits=-12.155 required=5 tests=AWL, BAYES_00, OSDL_HEADER_SUBJECT_BRACKETED, SAMSUNG_WEBMAIL_OSDL X-Spam-Checker-Version: SpamAssassin 3.2.4-osdl_revision__1.47__ X-MIMEDefang-Filter: lf$Revision: 1.188 $ X-Scanned-By: MIMEDefang 2.63 on 140.211.169.21 Cc: Nishanth Menon , Len Brown , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, Kyungmin Park , Thomas Gleixner Subject: [linux-pm] [PATCH v2 3/3] PM / DEVFREQ: add sysfs interface (including user tickling) X-BeenThere: linux-pm@lists.linux-foundation.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Linux power management List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: linux-pm-bounces@lists.linux-foundation.org Errors-To: linux-pm-bounces@lists.linux-foundation.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.6 (demeter2.kernel.org [140.211.167.43]); Wed, 11 May 2011 08:05:19 +0000 (UTC) 1. System-wide sysfs interface - tickle_all R: number of tickle_all execution W: tickle all devfreq devices - min_interval R: devfreq monitoring base interval in ms - monitoring R: shows whether devfreq monitoring is active or not. 2. Device specific sysfs interface - tickle R: number of tickle execution for the device W: tickle the device Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park --- drivers/base/power/devfreq.c | 191 ++++++++++++++++++++++++++++++++++++----- include/linux/devfreq.h | 3 + 2 files changed, 170 insertions(+), 24 deletions(-) diff --git a/drivers/base/power/devfreq.c b/drivers/base/power/devfreq.c index 251d761..ba1b606 100644 --- a/drivers/base/power/devfreq.c +++ b/drivers/base/power/devfreq.c @@ -40,6 +40,9 @@ static LIST_HEAD(devfreq_list); /* Exclusive access to devfreq_list and its elements */ static DEFINE_MUTEX(devfreq_list_lock); +static struct kobject *devfreq_kobj; +static struct attribute_group dev_attr_group; + /** * find_device_devfreq() - find devfreq struct using device pointer * @dev: device pointer used to lookup device DEVFREQ. @@ -211,6 +214,8 @@ int devfreq_add_device(struct device *dev, struct devfreq_dev_profile *profile, queue_delayed_work(devfreq_wq, &devfreq_work, msecs_to_jiffies(DEVFREQ_INTERVAL)); } + + sysfs_update_group(&dev->kobj, &dev_attr_group); out: mutex_unlock(&devfreq_list_lock); @@ -237,6 +242,8 @@ int devfreq_remove_device(struct device *dev) return -EINVAL; } + sysfs_remove_group(&dev->kobj, &dev_attr_group); + list_del(&devfreq->node); kfree(devfreq); @@ -286,6 +293,38 @@ out: return err; } +int _devfreq_tickle_device(struct devfreq *df, unsigned long delay) +{ + int err = 0; + unsigned long freq; + struct opp *opp; + + freq = df->profile->max_freq; + opp = opp_find_freq_floor(df->dev, &freq); + if (IS_ERR(opp)) + return PTR_ERR(opp); + + freq = opp_get_freq(opp); + if (df->previous_freq != freq) { + err = df->profile->target(df->dev, opp); + if (!err) + df->previous_freq = freq; + } + if (err) { + dev_err(df->dev, "%s: Cannot set frequency.\n", __func__); + } else { + df->tickle = delay; + df->num_tickle++; + } + + if (devfreq_wq && !monitoring) { + monitoring = true; + queue_delayed_work(devfreq_wq, &devfreq_work, + msecs_to_jiffies(DEVFREQ_INTERVAL)); + } + + return err; +} /** * devfreq_tickle_device() - Guarantee maximum operation speed for a while * instaneously. @@ -301,43 +340,133 @@ out: int devfreq_tickle_device(struct device *dev, unsigned long duration_ms) { struct devfreq *devfreq; - struct opp *opp; - unsigned long freq; int err = 0; + unsigned long delay; /* in num DEVFREQ_INTERVAL */ mutex_lock(&devfreq_list_lock); devfreq = find_device_devfreq(dev); - if (!IS_ERR(devfreq)) { - freq = devfreq->profile->max_freq; - opp = opp_find_freq_floor(devfreq->dev, &freq); - freq = opp_get_freq(opp); - if (devfreq->previous_freq != freq) { - err = devfreq->profile->target(devfreq->dev, opp); - if (!err) - devfreq->previous_freq = freq; - } - if (err) - dev_err(dev, "%s: Cannot set frequency.\n", __func__); - else - devfreq->tickle = DIV_ROUND_UP(duration_ms, - DEVFREQ_INTERVAL); + delay = DIV_ROUND_UP(duration_ms, DEVFREQ_INTERVAL); + + if (IS_ERR(devfreq)) + err = PTR_ERR(devfreq); + else + err = _devfreq_tickle_device(devfreq, delay); + + mutex_unlock(&devfreq_list_lock); + + return err; +} + +static int num_tickle_all; +static ssize_t tickle_all(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int duration; + struct devfreq *tmp; + unsigned long delay; + + sscanf(buf, "%d", &duration); + if (duration < DEVFREQ_INTERVAL) + duration = DEVFREQ_INTERVAL; + + if (unlikely(IS_ERR_OR_NULL(dev))) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; } - if (devfreq_wq && !monitoring) { - monitoring = true; - queue_delayed_work(devfreq_wq, &devfreq_work, - msecs_to_jiffies(DEVFREQ_INTERVAL)); + delay = DIV_ROUND_UP(duration, DEVFREQ_INTERVAL); + + mutex_lock(&devfreq_list_lock); + list_for_each_entry(tmp, &devfreq_list, node) { + _devfreq_tickle_device(tmp, delay); } mutex_unlock(&devfreq_list_lock); - if (IS_ERR(devfreq)) { - dev_err(dev, "%s: Cannot find devfreq.\n", __func__); - err = PTR_ERR(devfreq); + num_tickle_all++; + return count; +} + +static ssize_t show_num_tickle_all(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", num_tickle_all); +} + +static ssize_t show_min_interval(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", DEVFREQ_INTERVAL); +} + +static ssize_t show_monitoring(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", monitoring ? 1 : 0); +} + +static DEVICE_ATTR(tickle_all, 0644, show_num_tickle_all, tickle_all); +static DEVICE_ATTR(min_interval, 0444, show_min_interval, NULL); +static DEVICE_ATTR(monitoring, 0444, show_monitoring, NULL); +static struct attribute *devfreq_entries[] = { + &dev_attr_tickle_all.attr, + &dev_attr_min_interval.attr, + &dev_attr_monitoring.attr, + NULL, +}; +static struct attribute_group devfreq_attr_group = { + .name = "devfreq", + .attrs = devfreq_entries, +}; + +static ssize_t tickle(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int duration; + struct devfreq *df; + unsigned long delay; + + sscanf(buf, "%d", &duration); + if (duration < DEVFREQ_INTERVAL) + duration = DEVFREQ_INTERVAL; + + if (unlikely(IS_ERR_OR_NULL(dev))) { + pr_err("%s: Invalid parameters\n", __func__); + return -EINVAL; } - return err; + delay = DIV_ROUND_UP(duration, DEVFREQ_INTERVAL); + + mutex_lock(&devfreq_list_lock); + df = find_device_devfreq(dev); + _devfreq_tickle_device(df, delay); + mutex_unlock(&devfreq_list_lock); + + return count; } +static ssize_t show_num_tickle(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df; + + df = find_device_devfreq(dev); + + if (!IS_ERR(df)) + return sprintf(buf, "%d\n", df->num_tickle); + + return PTR_ERR(df); +} + +static DEVICE_ATTR(tickle, 0644, show_num_tickle, tickle); +static struct attribute *dev_entries[] = { + &dev_attr_tickle.attr, + NULL, +}; +static struct attribute_group dev_attr_group = { + .name = NULL, + .attrs = dev_entries, +}; + static int __init devfreq_init(void) { mutex_lock(&devfreq_list_lock); @@ -345,6 +474,20 @@ static int __init devfreq_init(void) monitoring = false; devfreq_wq = create_freezable_workqueue("devfreq_wq"); INIT_DELAYED_WORK_DEFERRABLE(&devfreq_work, devfreq_monitor); + + /* Create sysfs */ +#ifdef CONFIG_PM + devfreq_kobj = kobject_create_and_add("devfreq", power_kobj); + if (!devfreq_kobj) { + pr_err("Unable to create DEVFREQ kobject.\n"); + goto out; + } + if (sysfs_create_group(devfreq_kobj, &devfreq_attr_group)) { + pr_err("Unable to create DEVFREQ sysfs entries.\n"); + goto out; + } +#endif +out: mutex_unlock(&devfreq_list_lock); devfreq_monitor(&devfreq_work.work); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index ec41ba6..f6e38ee 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -59,6 +59,7 @@ struct devfreq_governor { * at each executino of devfreq_monitor, tickle is decremented. * User may tickle a device-devfreq in order to set maximum * frequency instaneously with some guaranteed duration. + * @num_tickle number of tickle calls. * * This structure stores the DEVFREQ information for a give device. */ @@ -72,6 +73,8 @@ struct devfreq { unsigned long previous_freq; unsigned int next_polling; unsigned int tickle; + + unsigned int num_tickle; }; #if defined(CONFIG_PM_DEVFREQ)