[PATCH v6 1/9] cpuidle: Add common time keeping and irq enabling

Rob Lee rob.lee at linaro.org
Wed Feb 29 08:33:39 EST 2012


Hello Deepthi,

On Wed, Feb 29, 2012 at 5:13 AM, Deepthi Dharwar
<deepthi at linux.vnet.ibm.com> wrote:
> Hi Rob,
>
>
> On 02/29/2012 08:41 AM, Robert Lee wrote:
>
>> Make necessary changes to implement time keeping and irq enabling
>> in the core cpuidle code.  This will allow the removal of these
>> functionalities from various platform cpuidle implementations whose
>> timekeeping and irq enabling follows the form in this common code.
>>
>> Signed-off-by: Robert Lee <rob.lee at linaro.org>
>> ---
>>  arch/arm/include/asm/cpuidle.h |   14 ++++++
>>  drivers/cpuidle/cpuidle.c      |   90 ++++++++++++++++++++++++++++++++--------
>>  include/linux/cpuidle.h        |   13 ++++++
>>  3 files changed, 99 insertions(+), 18 deletions(-)
>>  create mode 100644 arch/arm/include/asm/cpuidle.h
>>
>> diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
>> new file mode 100644
>> index 0000000..1d2075b
>> --- /dev/null
>> +++ b/arch/arm/include/asm/cpuidle.h
>> @@ -0,0 +1,14 @@
>> +#ifndef __ASM_ARM_CPUIDLE_H
>> +#define __ASM_ARM_CPUIDLE_H
>> +
>> +/* Common ARM WFI state */
>> +#define CPUIDLE_ARM_WFI_STATE {\
>> +     .enter                  = cpuidle_simple_enter,\
>> +     .exit_latency           = 1,\
>> +     .target_residency       = 1,\
>> +     .flags                  = CPUIDLE_FLAG_TIME_VALID,\
>> +     .name                   = "WFI",\
>> +     .desc                   = "ARM core clock gating (WFI)",\
>> +}
>> +
>> +#endif
>> diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
>> index 59f4261..00b02f5 100644
>> --- a/drivers/cpuidle/cpuidle.c
>> +++ b/drivers/cpuidle/cpuidle.c
>> @@ -18,6 +18,7 @@
>>  #include <linux/ktime.h>
>>  #include <linux/hrtimer.h>
>>  #include <linux/module.h>
>> +#include <asm/proc-fns.h>
>>  #include <trace/events/power.h>
>>
>>  #include "cpuidle.h"
>> @@ -53,6 +54,24 @@ static void cpuidle_kick_cpus(void) {}
>>
>>  static int __cpuidle_register_device(struct cpuidle_device *dev);
>>
>> +static inline int cpuidle_enter(struct cpuidle_device *dev,
>> +                             struct cpuidle_driver *drv, int index)
>> +{
>> +     struct cpuidle_state *target_state = &drv->states[index];
>> +     return target_state->enter(dev, drv, index);
>> +}
>> +
>> +static inline int cpuidle_enter_tk(struct cpuidle_device *dev,
>> +                            struct cpuidle_driver *drv, int index)
>> +{
>> +     return cpuidle_wrap_enter(dev, drv, index, cpuidle_enter);
>> +}
>> +
>> +typedef int (*cpuidle_enter_t)(struct cpuidle_device *dev,
>> +                            struct cpuidle_driver *drv, int index);
>> +
>> +static cpuidle_enter_t cpuidle_enter_ops;
>> +
>>  /**
>>   * cpuidle_idle_call - the main idle loop
>>   *
>> @@ -63,7 +82,6 @@ int cpuidle_idle_call(void)
>>  {
>>       struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
>>       struct cpuidle_driver *drv = cpuidle_get_driver();
>> -     struct cpuidle_state *target_state;
>>       int next_state, entered_state;
>>
>>       if (off)
>> @@ -92,12 +110,10 @@ int cpuidle_idle_call(void)
>>               return 0;
>>       }
>>
>> -     target_state = &drv->states[next_state];
>> -
>>       trace_power_start(POWER_CSTATE, next_state, dev->cpu);
>>       trace_cpu_idle(next_state, dev->cpu);
>>
>> -     entered_state = target_state->enter(dev, drv, next_state);
>> +     entered_state = cpuidle_enter_ops(dev, drv, next_state);
>>
>>       trace_power_end(dev->cpu);
>>       trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu);
>> @@ -110,7 +126,8 @@ int cpuidle_idle_call(void)
>>               dev->states_usage[entered_state].time +=
>>                               (unsigned long long)dev->last_residency;
>>               dev->states_usage[entered_state].usage++;
>> -     }
>> +     } else
>> +             dev->last_residency = 0;
>>
>>       /* give the governor an opportunity to reflect on the outcome */
>>       if (cpuidle_curr_governor->reflect)
>> @@ -164,20 +181,29 @@ void cpuidle_resume_and_unlock(void)
>>
>>  EXPORT_SYMBOL_GPL(cpuidle_resume_and_unlock);
>>
>> -#ifdef CONFIG_ARCH_HAS_CPU_RELAX
>> -static int poll_idle(struct cpuidle_device *dev,
>> -             struct cpuidle_driver *drv, int index)
>> +/**
>> + * cpuidle_wrap_enter - performs timekeeping and irqen around enter function
>> + * @dev: pointer to a valid cpuidle_device object
>> + * @drv: pointer to a valid cpuidle_driver object
>> + * @index: index of the target cpuidle state.
>> + */
>> +int cpuidle_wrap_enter(struct cpuidle_device *dev,
>> +                             struct cpuidle_driver *drv, int index,
>> +                             int (*enter)(struct cpuidle_device *dev,
>> +                                     struct cpuidle_driver *drv, int index))
>>  {
>> -     ktime_t t1, t2;
>> +     ktime_t time_start, time_end;
>>       s64 diff;
>>
>> -     t1 = ktime_get();
>> +     time_start = ktime_get();
>> +
>> +     index = enter(dev, drv, index);
>> +
>> +     time_end = ktime_get();
>> +
>>       local_irq_enable();
>> -     while (!need_resched())
>> -             cpu_relax();
>>
>> -     t2 = ktime_get();
>> -     diff = ktime_to_us(ktime_sub(t2, t1));
>> +     diff = ktime_to_us(ktime_sub(time_end, time_start));
>>       if (diff > INT_MAX)
>>               diff = INT_MAX;
>>
>> @@ -186,6 +212,31 @@ static int poll_idle(struct cpuidle_device *dev,
>>       return index;
>>  }
>>
>> +int cpuidle_simple_enter(struct cpuidle_device *dev,
>> +             struct cpuidle_driver *drv, int index)
>> +{
>> +     cpu_do_idle();
>> +
>> +     return index;
>> +}
>
>
> The enter routines are always part of  the specific driver code rather
> than in the generic cpuidle framework code.
> The above function is arm driver specific enter routine, and should be
> in arch specific file.
> Right now, this causes a build break on Powerpc and Intel boxes.
>

Ok, I can move this out on next version.

> drivers/cpuidle/cpuidle.c:21:26: error: asm/proc-fns.h: No such file or
> directory
> drivers/cpuidle/cpuidle.c: In function ‘cpuidle_simple_enter’:
> drivers/cpuidle/cpuidle.c:218: error: implicit declaration of function
> ‘cpu_do_idle’
> make[2]: *** [drivers/cpuidle/cpuidle.o] Error 1
> make[2]: *** Waiting for unfinished jobs....
> make[1]: *** [drivers/cpuidle] Error 2
> make[1]: *** Waiting for unfinished jobs....
>
>
>> +
>> +#ifdef CONFIG_ARCH_HAS_CPU_RELAX
>> +static inline int __poll_idle(struct cpuidle_device *dev,
>> +             struct cpuidle_driver *drv, int index)
>> +{
>> +     while (!need_resched())
>> +             cpu_relax();
>> +
>> +     return index;
>> +}
>> +
>> +static int poll_idle(struct cpuidle_device *dev,
>> +             struct cpuidle_driver *drv, int index)
>> +{
>> +     return cpuidle_wrap_enter(dev,  drv, index,
>> +                             __poll_idle);
>> +}
>> +
>>  static void poll_idle_init(struct cpuidle_driver *drv)
>>  {
>>       struct cpuidle_state *state = &drv->states[0];
>> @@ -195,7 +246,6 @@ static void poll_idle_init(struct cpuidle_driver *drv)
>>       state->exit_latency = 0;
>>       state->target_residency = 0;
>>       state->power_usage = -1;
>> -     state->flags = 0;
>
>
> Also, why is that these flags have been eliminated ?
>

This shouldn't be there.  Will fix this in next version.

>>       state->enter = poll_idle;
>>  }
>>  #else
>> @@ -212,10 +262,11 @@ static void poll_idle_init(struct cpuidle_driver *drv) {}
>>  int cpuidle_enable_device(struct cpuidle_device *dev)
>>  {
>>       int ret, i;
>> +     struct cpuidle_driver *drv = cpuidle_get_driver();
>>
>>       if (dev->enabled)
>>               return 0;
>> -     if (!cpuidle_get_driver() || !cpuidle_curr_governor)
>> +     if (!drv || !cpuidle_curr_governor)
>>               return -EIO;
>>       if (!dev->state_count)
>>               return -EINVAL;
>> @@ -226,13 +277,16 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
>>                       return ret;
>>       }
>>
>> -     poll_idle_init(cpuidle_get_driver());
>> +     cpuidle_enter_ops = drv->en_core_tk_irqen ?
>> +             cpuidle_enter_tk : cpuidle_enter;
>> +
>> +     poll_idle_init(drv);
>>
>>       if ((ret = cpuidle_add_state_sysfs(dev)))
>>               return ret;
>>
>>       if (cpuidle_curr_governor->enable &&
>> -         (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev)))
>> +         (ret = cpuidle_curr_governor->enable(drv, dev)))
>>               goto fail_sysfs;
>>
>>       for (i = 0; i < dev->state_count; i++) {
>> diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
>> index 712abcc..c91b018 100644
>> --- a/include/linux/cpuidle.h
>> +++ b/include/linux/cpuidle.h
>> @@ -15,6 +15,7 @@
>>  #include <linux/list.h>
>>  #include <linux/kobject.h>
>>  #include <linux/completion.h>
>> +#include <linux/hrtimer.h>
>>
>>  #define CPUIDLE_STATE_MAX    8
>>  #define CPUIDLE_NAME_LEN     16
>> @@ -122,6 +123,8 @@ struct cpuidle_driver {
>>       struct module           *owner;
>>
>>       unsigned int            power_specified:1;
>> +     /* set to 1 to use the core cpuidle time keeping (for all states). */
>> +     unsigned int            en_core_tk_irqen:1;
>>       struct cpuidle_state    states[CPUIDLE_STATE_MAX];
>>       int                     state_count;
>>       int                     safe_state_index;
>> @@ -140,6 +143,13 @@ extern void cpuidle_pause_and_lock(void);
>>  extern void cpuidle_resume_and_unlock(void);
>>  extern int cpuidle_enable_device(struct cpuidle_device *dev);
>>  extern void cpuidle_disable_device(struct cpuidle_device *dev);
>> +extern int cpuidle_simple_enter(struct cpuidle_device *dev,
>> +             struct cpuidle_driver *drv, int index);
>> +
>> +extern int cpuidle_wrap_enter(struct cpuidle_device *dev,
>> +                             struct cpuidle_driver *drv, int index,
>> +                             int (*enter)(struct cpuidle_device *dev,
>> +                                     struct cpuidle_driver *drv, int index));
>>
>>  #else
>>  static inline void disable_cpuidle(void) { }
>> @@ -157,6 +167,9 @@ static inline void cpuidle_resume_and_unlock(void) { }
>>  static inline int cpuidle_enable_device(struct cpuidle_device *dev)
>>  {return -ENODEV; }
>>  static inline void cpuidle_disable_device(struct cpuidle_device *dev) { }
>> +static inline int cpuidle_simple_enter(struct cpuidle_device *dev,
>> +             struct cpuidle_driver *drv, int index)
>> +{return -ENODEV; }
>>
>>  #endif
>>
>
>
> I am for generic time keeping framework, this would remove
> loads of redundant code for the same in all the backend drivers.
>
> Cheers,
> Deepthi
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pm" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



More information about the linux-arm-kernel mailing list