]> git.karo-electronics.de Git - linux-beck.git/commitdiff
PM / devfreq: Add sysfs node for representing frequency transition information.
authorJonghwa Lee <jonghwa3.lee@samsung.com>
Thu, 23 Aug 2012 11:00:46 +0000 (20:00 +0900)
committerMyungJoo Ham <myungjoo.ham@samsung.com>
Tue, 20 Nov 2012 07:05:44 +0000 (16:05 +0900)
This patch adds sysfs node which can be used to get information of frequency
transition. It represents transition table which contains total number of transition of
each freqeuncy state and time spent. It is inspired CPUFREQ's status driver.

Signed-off-by: Jonghwa Lee <jonghwa3.lee@samsung.com>
[Added Documentation/ABI entry, updated kernel-doc, and resolved merge conflict]
Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
Documentation/ABI/testing/sysfs-class-devfreq
drivers/devfreq/devfreq.c
include/linux/devfreq.h

index e672ccb02e7fb180f1871ed6a932adb87fbb3716..40f98a9428dce6f3380170e60b6f72e8257c1543 100644 (file)
@@ -44,6 +44,17 @@ Description:
                (/sys/class/devfreq/.../central_polling is 0), this value
                may be useless.
 
+What:          /sys/class/devfreq/.../trans_stat
+Date:          October 2012
+Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
+Descrtiption:
+               This ABI shows the statistics of devfreq behavior on a
+               specific device. It shows the time spent in each state and
+               the number of transitions between states.
+               In order to activate this ABI, the devfreq target device
+               driver should provide the list of available frequencies
+               with its profile.
+
 What:          /sys/class/devfreq/.../userspace/set_freq
 Date:          September 2011
 Contact:       MyungJoo Ham <myungjoo.ham@samsung.com>
index c44e562bdfe08ffa2f084aab0570d162f285a924..bf6de38190cf5ff19a867e2515b4cb47e9ed515b 100644 (file)
@@ -66,6 +66,51 @@ static struct devfreq *find_device_devfreq(struct device *dev)
        return ERR_PTR(-ENODEV);
 }
 
+/**
+ * devfreq_get_freq_level() - Lookup freq_table for the frequency
+ * @devfreq:   the devfreq instance
+ * @freq:      the target frequency
+ */
+static int devfreq_get_freq_level(struct devfreq *devfreq, unsigned long freq)
+{
+       int lev;
+
+       for (lev = 0; lev < devfreq->profile->max_state; lev++)
+               if (freq == devfreq->profile->freq_table[lev])
+                       return lev;
+
+       return -EINVAL;
+}
+
+/**
+ * devfreq_update_status() - Update statistics of devfreq behavior
+ * @devfreq:   the devfreq instance
+ * @freq:      the update target frequency
+ */
+static int devfreq_update_status(struct devfreq *devfreq, unsigned long freq)
+{
+       int lev, prev_lev;
+       unsigned long cur_time;
+
+       lev = devfreq_get_freq_level(devfreq, freq);
+       if (lev < 0)
+               return lev;
+
+       cur_time = jiffies;
+       devfreq->time_in_state[lev] +=
+                        cur_time - devfreq->last_stat_updated;
+       if (freq != devfreq->previous_freq) {
+               prev_lev = devfreq_get_freq_level(devfreq,
+                                               devfreq->previous_freq);
+               devfreq->trans_table[(prev_lev *
+                               devfreq->profile->max_state) + lev]++;
+               devfreq->total_trans++;
+       }
+       devfreq->last_stat_updated = cur_time;
+
+       return 0;
+}
+
 /* Load monitoring helper functions for governors use */
 
 /**
@@ -112,6 +157,11 @@ int update_devfreq(struct devfreq *devfreq)
        if (err)
                return err;
 
+       if (devfreq->profile->freq_table)
+               if (devfreq_update_status(devfreq, freq))
+                       dev_err(&devfreq->dev,
+                               "Couldn't update frequency transition information.\n");
+
        devfreq->previous_freq = freq;
        return err;
 }
@@ -378,6 +428,15 @@ struct devfreq *devfreq_add_device(struct device *dev,
        devfreq->data = data;
        devfreq->nb.notifier_call = devfreq_notifier_call;
 
+       devfreq->trans_table =  devm_kzalloc(dev, sizeof(unsigned int) *
+                                               devfreq->profile->max_state *
+                                               devfreq->profile->max_state,
+                                               GFP_KERNEL);
+       devfreq->time_in_state = devm_kzalloc(dev, sizeof(unsigned int) *
+                                               devfreq->profile->max_state,
+                                               GFP_KERNEL);
+       devfreq->last_stat_updated = jiffies;
+
        dev_set_name(&devfreq->dev, dev_name(dev));
        err = device_register(&devfreq->dev);
        if (err) {
@@ -601,6 +660,47 @@ static ssize_t show_available_freqs(struct device *d,
        return count;
 }
 
+static ssize_t show_trans_table(struct device *dev, struct device_attribute *attr,
+                               char *buf)
+{
+       struct devfreq *devfreq = to_devfreq(dev);
+       ssize_t len;
+       int i, j, err;
+       unsigned int max_state = devfreq->profile->max_state;
+
+       err = devfreq_update_status(devfreq, devfreq->previous_freq);
+       if (err)
+               return 0;
+
+       len = sprintf(buf, "   From  :   To\n");
+       len += sprintf(buf + len, "         :");
+       for (i = 0; i < max_state; i++)
+               len += sprintf(buf + len, "%8u",
+                               devfreq->profile->freq_table[i]);
+
+       len += sprintf(buf + len, "   time(ms)\n");
+
+       for (i = 0; i < max_state; i++) {
+               if (devfreq->profile->freq_table[i]
+                                       == devfreq->previous_freq) {
+                       len += sprintf(buf + len, "*");
+               } else {
+                       len += sprintf(buf + len, " ");
+               }
+               len += sprintf(buf + len, "%8u:",
+                               devfreq->profile->freq_table[i]);
+               for (j = 0; j < max_state; j++)
+                       len += sprintf(buf + len, "%8u",
+                               devfreq->trans_table[(i * max_state) + j]);
+               len += sprintf(buf + len, "%10u\n",
+                       jiffies_to_msecs(devfreq->time_in_state[i]));
+       }
+
+       len += sprintf(buf + len, "Total transition : %u\n",
+                                       devfreq->total_trans);
+       return len;
+}
+
 static struct device_attribute devfreq_attrs[] = {
        __ATTR(governor, S_IRUGO, show_governor, NULL),
        __ATTR(cur_freq, S_IRUGO, show_freq, NULL),
@@ -610,6 +710,7 @@ static struct device_attribute devfreq_attrs[] = {
               store_polling_interval),
        __ATTR(min_freq, S_IRUGO | S_IWUSR, show_min_freq, store_min_freq),
        __ATTR(max_freq, S_IRUGO | S_IWUSR, show_max_freq, store_max_freq),
+       __ATTR(trans_stat, S_IRUGO, show_trans_table, NULL),
        { },
 };
 
index 1461fb2355ad4f1470ee8c3fa00614287a79b052..bc35c4aee6a38b420fffc9ece7a5fa94dd0f439c 100644 (file)
@@ -73,6 +73,8 @@ struct devfreq_dev_status {
  *                     from devfreq_remove_device() call. If the user
  *                     has registered devfreq->nb at a notifier-head,
  *                     this is the time to unregister it.
+ * @freq_table:        Optional list of frequencies to support statistics.
+ * @max_state: The size of freq_table.
  */
 struct devfreq_dev_profile {
        unsigned long initial_freq;
@@ -83,6 +85,9 @@ struct devfreq_dev_profile {
                              struct devfreq_dev_status *stat);
        int (*get_cur_freq)(struct device *dev, unsigned long *freq);
        void (*exit)(struct device *dev);
+
+       unsigned int *freq_table;
+       unsigned int max_state;
 };
 
 /**
@@ -127,6 +132,10 @@ struct devfreq_governor {
  * @min_freq:  Limit minimum frequency requested by user (0: none)
  * @max_freq:  Limit maximum frequency requested by user (0: none)
  * @stop_polling:       devfreq polling status of a device.
+ * @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
  *
  * This structure stores the devfreq information for a give device.
  *
@@ -153,6 +162,12 @@ struct devfreq {
        unsigned long min_freq;
        unsigned long max_freq;
        bool stop_polling;
+
+       /* information for device freqeuncy transition */
+       unsigned int total_trans;
+       unsigned int *trans_table;
+       unsigned long *time_in_state;
+       unsigned long last_stat_updated;
 };
 
 #if defined(CONFIG_PM_DEVFREQ)