On some systems (eg Tegra30) latencies for a given C state are not equal for
all CPUs. Therefore we introduce a new per CPU structure which contains
those parameters.

--

This patch doesn't update all cpuidle device registrations. I will do that
in a next version after agreement on the exact data structure to be
introduced.

Changes in v2:

* separated state latecny registration from device registration

Signed-off-by: Peter De Schrijver <[email protected]>
---
 arch/arm/mach-tegra/cpuidle.c      |   21 +++++++++++-----
 drivers/cpuidle/cpuidle.c          |   44 +++++++++++++++++++++++++++++++----
 drivers/cpuidle/driver.c           |   25 --------------------
 drivers/cpuidle/governors/ladder.c |   21 +++++++++-------
 drivers/cpuidle/governors/menu.c   |    7 +++--
 drivers/cpuidle/sysfs.c            |   34 ++++++++++++++++++---------
 include/linux/cpuidle.h            |   20 +++++++++------
 7 files changed, 104 insertions(+), 68 deletions(-)

diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index d83a8c0..84e3674 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -37,20 +37,26 @@ static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
 struct cpuidle_driver tegra_idle_driver = {
        .name = "tegra_idle",
        .owner = THIS_MODULE,
+       .power_specified = 1,
        .state_count = 1,
        .states = {
                [0] = {
-                       .enter                  = tegra_idle_enter_lp3,
-                       .exit_latency           = 10,
-                       .target_residency       = 10,
-                       .power_usage            = 600,
-                       .flags                  = CPUIDLE_FLAG_TIME_VALID,
-                       .name                   = "LP3",
-                       .desc                   = "CPU flow-controlled",
+                       .enter  = tegra_idle_enter_lp3,
+                       .name   = "LP3",
+                       .desc   = "CPU flow-controlled",
                },
        },
 };
 
+struct cpuidle_state_parameters tegra_idle_parameters[] = {
+       [0] = {
+               .exit_latency           = 10,
+               .target_residency       = 10,
+               .power_usage            = 600,
+               .flags                  = CPUIDLE_FLAG_TIME_VALID,
+       },
+};
+
 static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device);
 
 static int tegra_idle_enter_lp3(struct cpuidle_device *dev,
@@ -101,6 +107,7 @@ static int __init tegra_cpuidle_init(void)
                                cpu);
                        return ret;
                }
+               cpuidle_register_stateinfo(dev, tegra_idle_parameters);
        }
        return 0;
 }
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 588b44a..32e3a1b 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -90,8 +90,9 @@ int cpuidle_play_dead(void)
        for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
                struct cpuidle_state *s = &drv->states[i];
 
-               if (s->power_usage < power_usage && s->enter_dead) {
-                       power_usage = s->power_usage;
+               if (dev->state_parameters[i].power_usage < power_usage
+                               && s->enter_dead) {
+                       power_usage = dev->state_parameters[i].power_usage;
                        dead_state = i;
                }
        }
@@ -402,9 +403,6 @@ int cpuidle_register_device(struct cpuidle_device *dev)
                return ret;
        }
 
-       cpuidle_enable_device(dev);
-       cpuidle_install_idle_handler();
-
        mutex_unlock(&cpuidle_lock);
 
        return 0;
@@ -441,6 +439,42 @@ void cpuidle_unregister_device(struct cpuidle_device *dev)
 
 EXPORT_SYMBOL_GPL(cpuidle_unregister_device);
 
+void cpuidle_register_stateinfo(struct cpuidle_device *dev,
+                               struct cpuidle_state_parameters *info)
+{
+       struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
+       int i;
+
+       mutex_lock(&cpuidle_lock);
+
+       dev->state_parameters = info;
+
+       /*
+        * cpuidle driver should set the drv->power_specified bit
+        * before registering if the driver provides
+        * power_usage numbers.
+        *
+        * If power_specified is not set,
+        * we fill in power_usage with decreasing values as the
+        * cpuidle code has an implicit assumption that state Cn
+        * uses less power than C(n-1).
+        *
+        * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
+        * an power value of -1.  So we use -2, -3, etc, for other
+        * c-states.
+        */
+       if (!cpuidle_driver->power_specified) {
+               for (i = CPUIDLE_DRIVER_STATE_START;
+                               i < cpuidle_driver->state_count; i++)
+                       dev->state_parameters[i].power_usage = -1 - i;
+       }
+
+       cpuidle_enable_device(dev);
+       cpuidle_install_idle_handler();
+
+       mutex_unlock(&cpuidle_lock);
+}
+
 #ifdef CONFIG_SMP
 
 static void smp_callback(void *v)
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 40cd3f3..d81c6db 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -17,30 +17,6 @@
 static struct cpuidle_driver *cpuidle_curr_driver;
 DEFINE_SPINLOCK(cpuidle_driver_lock);
 
-static void __cpuidle_register_driver(struct cpuidle_driver *drv)
-{
-       int i;
-       /*
-        * cpuidle driver should set the drv->power_specified bit
-        * before registering if the driver provides
-        * power_usage numbers.
-        *
-        * If power_specified is not set,
-        * we fill in power_usage with decreasing values as the
-        * cpuidle code has an implicit assumption that state Cn
-        * uses less power than C(n-1).
-        *
-        * With CONFIG_ARCH_HAS_CPU_RELAX, C0 is already assigned
-        * an power value of -1.  So we use -2, -3, etc, for other
-        * c-states.
-        */
-       if (!drv->power_specified) {
-               for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++)
-                       drv->states[i].power_usage = -1 - i;
-       }
-}
-
-
 /**
  * cpuidle_register_driver - registers a driver
  * @drv: the driver
@@ -58,7 +34,6 @@ int cpuidle_register_driver(struct cpuidle_driver *drv)
                spin_unlock(&cpuidle_driver_lock);
                return -EBUSY;
        }
-       __cpuidle_register_driver(drv);
        cpuidle_curr_driver = drv;
        spin_unlock(&cpuidle_driver_lock);
 
diff --git a/drivers/cpuidle/governors/ladder.c 
b/drivers/cpuidle/governors/ladder.c
index b6a09ea..2e7ea8c 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -79,9 +79,9 @@ static int ladder_select_state(struct cpuidle_driver *drv,
 
        last_state = &ldev->states[last_idx];
 
-       if (drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) {
+       if (dev->state_parameters[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) {
                last_residency = cpuidle_get_last_residency(dev) - \
-                                        drv->states[last_idx].exit_latency;
+                                dev->state_parameters[last_idx].exit_latency;
        }
        else
                last_residency = last_state->threshold.promotion_time + 1;
@@ -89,7 +89,7 @@ static int ladder_select_state(struct cpuidle_driver *drv,
        /* consider promotion */
        if (last_idx < drv->state_count - 1 &&
            last_residency > last_state->threshold.promotion_time &&
-           drv->states[last_idx + 1].exit_latency <= latency_req) {
+           dev->state_parameters[last_idx + 1].exit_latency <= latency_req) {
                last_state->stats.promotion_count++;
                last_state->stats.demotion_count = 0;
                if (last_state->stats.promotion_count >= 
last_state->threshold.promotion_count) {
@@ -100,11 +100,12 @@ static int ladder_select_state(struct cpuidle_driver *drv,
 
        /* consider demotion */
        if (last_idx > CPUIDLE_DRIVER_STATE_START &&
-           drv->states[last_idx].exit_latency > latency_req) {
+           dev->state_parameters[last_idx].exit_latency > latency_req) {
                int i;
 
                for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) {
-                       if (drv->states[i].exit_latency <= latency_req)
+                       if (dev->state_parameters[i].exit_latency <=
+                                       latency_req)
                                break;
                }
                ladder_do_selection(ldev, last_idx, i);
@@ -136,12 +137,12 @@ static int ladder_enable_device(struct cpuidle_driver 
*drv,
        int i;
        struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
        struct ladder_device_state *lstate;
-       struct cpuidle_state *state;
+       struct cpuidle_state_parameters *state_parameters;
 
        ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START;
 
        for (i = 0; i < drv->state_count; i++) {
-               state = &drv->states[i];
+               state_parameters = &dev->state_parameters[i];
                lstate = &ldev->states[i];
 
                lstate->stats.promotion_count = 0;
@@ -151,9 +152,11 @@ static int ladder_enable_device(struct cpuidle_driver *drv,
                lstate->threshold.demotion_count = DEMOTION_COUNT;
 
                if (i < drv->state_count - 1)
-                       lstate->threshold.promotion_time = state->exit_latency;
+                       lstate->threshold.promotion_time =
+                               state_parameters->exit_latency;
                if (i > 0)
-                       lstate->threshold.demotion_time = state->exit_latency;
+                       lstate->threshold.demotion_time =
+                               state_parameters->exit_latency;
        }
 
        return 0;
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index 0633575..e9bbc20 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -281,7 +281,7 @@ static int menu_select(struct cpuidle_driver *drv, struct 
cpuidle_device *dev)
         * unless the timer is happening really really soon.
         */
        if (data->expected_us > 5 &&
-               drv->states[CPUIDLE_DRIVER_STATE_START].disable == 0)
+               dev->state_parameters[CPUIDLE_DRIVER_STATE_START].disable == 0)
                data->last_state_idx = CPUIDLE_DRIVER_STATE_START;
 
        /*
@@ -289,7 +289,7 @@ static int menu_select(struct cpuidle_driver *drv, struct 
cpuidle_device *dev)
         * our constraints.
         */
        for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
-               struct cpuidle_state *s = &drv->states[i];
+               struct cpuidle_state_parameters *s = &dev->state_parameters[i];
 
                if (s->disable)
                        continue;
@@ -336,7 +336,8 @@ static void menu_update(struct cpuidle_driver *drv, struct 
cpuidle_device *dev)
        struct menu_device *data = &__get_cpu_var(menu_devices);
        int last_idx = data->last_state_idx;
        unsigned int last_idle_us = cpuidle_get_last_residency(dev);
-       struct cpuidle_state *target = &drv->states[last_idx];
+       struct cpuidle_state_parameters *target =
+                       &dev->state_parameters[last_idx];
        unsigned int measured_us;
        u64 new_factor;
 
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index 88032b4..7b17413 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -215,9 +215,10 @@ static struct kobj_type ktype_cpuidle = {
 
 struct cpuidle_state_attr {
        struct attribute attr;
-       ssize_t (*show)(struct cpuidle_state *, \
-                                       struct cpuidle_state_usage *, char *);
-       ssize_t (*store)(struct cpuidle_state *, const char *, size_t);
+       ssize_t (*show)(struct cpuidle_state *, struct cpuidle_state_usage *,
+                               struct cpuidle_state_parameters *, char *);
+       ssize_t (*store)(struct cpuidle_state_parameters *, const char *,
+                               size_t);
 };
 
 #define define_one_state_ro(_name, show) \
@@ -228,13 +229,15 @@ static struct cpuidle_state_attr attr_##_name = 
__ATTR(_name, 0644, show, store)
 
 #define define_show_state_function(_name) \
 static ssize_t show_state_##_name(struct cpuidle_state *state, \
-                        struct cpuidle_state_usage *state_usage, char *buf) \
+                        struct cpuidle_state_usage *state_usage, \
+                        struct cpuidle_state_parameters *state_parameters, \
+                        char *buf) \
 { \
-       return sprintf(buf, "%u\n", state->_name);\
+       return sprintf(buf, "%u\n", state_parameters->_name);\
 }
 
 #define define_store_state_function(_name) \
-static ssize_t store_state_##_name(struct cpuidle_state *state, \
+static ssize_t store_state_##_name(struct cpuidle_state_parameters *state, \
                const char *buf, size_t size) \
 { \
        long value; \
@@ -253,14 +256,18 @@ static ssize_t store_state_##_name(struct cpuidle_state 
*state, \
 
 #define define_show_state_ull_function(_name) \
 static ssize_t show_state_##_name(struct cpuidle_state *state, \
-                       struct cpuidle_state_usage *state_usage, char *buf) \
+                       struct cpuidle_state_usage *state_usage, \
+                       struct cpuidle_state_parameters *state_parameters, \
+                       char *buf) \
 { \
        return sprintf(buf, "%llu\n", state_usage->_name);\
 }
 
 #define define_show_state_str_function(_name) \
 static ssize_t show_state_##_name(struct cpuidle_state *state, \
-                       struct cpuidle_state_usage *state_usage, char *buf) \
+                       struct cpuidle_state_usage *state_usage, \
+                       struct cpuidle_state_parameters *state_parameters, \
+                       char *buf) \
 { \
        if (state->_name[0] == '\0')\
                return sprintf(buf, "<null>\n");\
@@ -298,6 +305,7 @@ static struct attribute *cpuidle_state_default_attrs[] = {
 #define kobj_to_state_obj(k) container_of(k, struct cpuidle_state_kobj, kobj)
 #define kobj_to_state(k) (kobj_to_state_obj(k)->state)
 #define kobj_to_state_usage(k) (kobj_to_state_obj(k)->state_usage)
+#define kobj_to_state_parameters(k) (kobj_to_state_obj(k)->state_parameters)
 #define attr_to_stateattr(a) container_of(a, struct cpuidle_state_attr, attr)
 static ssize_t cpuidle_state_show(struct kobject * kobj,
        struct attribute * attr ,char * buf)
@@ -305,10 +313,12 @@ static ssize_t cpuidle_state_show(struct kobject * kobj,
        int ret = -EIO;
        struct cpuidle_state *state = kobj_to_state(kobj);
        struct cpuidle_state_usage *state_usage = kobj_to_state_usage(kobj);
+       struct cpuidle_state_parameters *state_parameters =
+                               kobj_to_state_parameters(kobj);
        struct cpuidle_state_attr * cattr = attr_to_stateattr(attr);
 
        if (cattr->show)
-               ret = cattr->show(state, state_usage, buf);
+               ret = cattr->show(state, state_usage, state_parameters, buf);
 
        return ret;
 }
@@ -317,11 +327,12 @@ static ssize_t cpuidle_state_store(struct kobject *kobj,
        struct attribute *attr, const char *buf, size_t size)
 {
        int ret = -EIO;
-       struct cpuidle_state *state = kobj_to_state(kobj);
+       struct cpuidle_state_parameters *state_parameters =
+                               kobj_to_state_parameters(kobj);
        struct cpuidle_state_attr *cattr = attr_to_stateattr(attr);
 
        if (cattr->store)
-               ret = cattr->store(state, buf, size);
+               ret = cattr->store(state_parameters, buf, size);
 
        return ret;
 }
@@ -369,6 +380,7 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device)
                        goto error_state;
                kobj->state = &drv->states[i];
                kobj->state_usage = &device->states_usage[i];
+               kobj->state_parameters = &device->state_parameters[i];
                init_completion(&kobj->kobj_unregister);
 
                ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, 
&device->kobj,
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 6c26a3d..13f6532 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -42,12 +42,6 @@ struct cpuidle_state {
        char            name[CPUIDLE_NAME_LEN];
        char            desc[CPUIDLE_DESC_LEN];
 
-       unsigned int    flags;
-       unsigned int    exit_latency; /* in US */
-       int             power_usage; /* in mW */
-       unsigned int    target_residency; /* in US */
-       unsigned int    disable;
-
        int (*enter)    (struct cpuidle_device *dev,
                        struct cpuidle_driver *drv,
                        int index);
@@ -55,6 +49,14 @@ struct cpuidle_state {
        int (*enter_dead) (struct cpuidle_device *dev, int index);
 };
 
+struct cpuidle_state_parameters {
+       unsigned int    flags;
+       unsigned int    exit_latency; /* in US */
+       int             power_usage; /* in mW */
+       unsigned int    target_residency; /* in US */
+       unsigned int    disable;
+};
+
 /* Idle State Flags */
 #define CPUIDLE_FLAG_TIME_VALID        (0x01) /* is residency time measurable? 
*/
 
@@ -83,6 +85,7 @@ cpuidle_set_statedata(struct cpuidle_state_usage *st_usage, 
void *data)
 struct cpuidle_state_kobj {
        struct cpuidle_state *state;
        struct cpuidle_state_usage *state_usage;
+       struct cpuidle_state_parameters *state_parameters;
        struct completion kobj_unregister;
        struct kobject kobj;
 };
@@ -96,7 +99,7 @@ struct cpuidle_device {
        int                     state_count;
        struct cpuidle_state_usage      states_usage[CPUIDLE_STATE_MAX];
        struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
-
+       struct cpuidle_state_parameters *state_parameters;
        struct list_head        device_list;
        struct kobject          kobj;
        struct completion       kobj_unregister;
@@ -140,7 +143,8 @@ struct cpuidle_driver *cpuidle_get_driver(void);
 extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
 extern int cpuidle_register_device(struct cpuidle_device *dev);
 extern void cpuidle_unregister_device(struct cpuidle_device *dev);
-
+extern void cpuidle_register_stateinfo(struct cpuidle_device *dev,
+                               struct cpuidle_state_parameters *info);
 extern void cpuidle_pause_and_lock(void);
 extern void cpuidle_resume_and_unlock(void);
 extern int cpuidle_enable_device(struct cpuidle_device *dev);
-- 
1.7.7.rc0.72.g4b5ea.dirty

--
To unsubscribe from this list: send the line "unsubscribe linux-tegra" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to