[07/15] ARM: cpuidle: add init/exit routine

Message ID 1364234140-514-8-git-send-email-daniel.lezcano@linaro.org
State New
Headers show

Commit Message

Daniel Lezcano March 25, 2013, 5:55 p.m.
The init and exit routine for most of the drivers are the same,
that is register the driver and register the device.

Provide a common function to do that in the cpuidle driver for ARM,
so we can get rid of a lot of code duplication in the different SOC
cpuidle drivers.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 arch/arm/include/asm/cpuidle.h |    4 +++
 arch/arm/kernel/cpuidle.c      |   57 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 60 insertions(+), 1 deletion(-)

Comments

Andrew Lunn March 25, 2013, 6:10 p.m. | #1
On Mon, Mar 25, 2013 at 06:55:32PM +0100, Daniel Lezcano wrote:
> The init and exit routine for most of the drivers are the same,
> that is register the driver and register the device.
> 
> Provide a common function to do that in the cpuidle driver for ARM,
> so we can get rid of a lot of code duplication in the different SOC
> cpuidle drivers.

Hi Daniel

Please could you add a comment in the code about which piece is
specific to ARM, because its not obvious to me. Its not like there is
a reference to WFI for example. It looks like this code could go in
drivers/cpuidle/cpuidle.c

  Thanks
	Andrew

> 
> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
> ---
>  arch/arm/include/asm/cpuidle.h |    4 +++
>  arch/arm/kernel/cpuidle.c      |   57 +++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 60 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
> index 7367787..83a38ac 100644
> --- a/arch/arm/include/asm/cpuidle.h
> +++ b/arch/arm/include/asm/cpuidle.h
> @@ -4,6 +4,10 @@
>  extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
>  				    struct cpuidle_driver *drv, int index);
>  
> +extern int arm_cpuidle_init(struct cpuidle_driver *drv);
> +
> +extern void arm_cpuidle_exit(struct cpuidle_driver *drv);
> +
>  /* Common ARM WFI state */
>  #define ARM_CPUIDLE_WFI_STATE_PWR(p) {\
>  	.enter                  = arm_cpuidle_simple_enter,\
> diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c
> index 89545f6..13cfe3e 100644
> --- a/arch/arm/kernel/cpuidle.c
> +++ b/arch/arm/kernel/cpuidle.c
> @@ -9,13 +9,68 @@
>   * http://www.gnu.org/copyleft/gpl.html
>   */
>  
> +#include <linux/module.h>
>  #include <linux/cpuidle.h>
>  #include <asm/proc-fns.h>
>  
> +static DEFINE_PER_CPU(struct cpuidle_device, cpuidle_device);
> +
>  int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
> -		struct cpuidle_driver *drv, int index)
> +			     struct cpuidle_driver *drv, int index)
>  {
>  	cpu_do_idle();
>  
>  	return index;
>  }
> +
> +int __init arm_cpuidle_init(struct cpuidle_driver *drv)
> +{
> +       int ret, cpu;
> +       struct cpuidle_device *device;
> +
> +       ret = cpuidle_register_driver(drv);
> +       if (ret) {
> +               printk(KERN_ERR "failed to register idle driver '%s'\n",
> +                       drv->name);
> +               return ret;
> +       }
> +
> +       for_each_online_cpu(cpu) {
> +
> +               device = &per_cpu(cpuidle_device, cpu);
> +               device->cpu = cpu;
> +               ret = cpuidle_register_device(device);
> +               if (ret) {
> +                       printk(KERN_ERR "Failed to register cpuidle "
> +                              "device for cpu%d\n", cpu);
> +                       goto out_unregister;
> +               }
> +       }
> +
> +out:
> +       return ret;
> +
> +out_unregister:
> +       for_each_online_cpu(cpu) {
> +               device = &per_cpu(cpuidle_device, cpu);
> +               cpuidle_unregister_device(device);
> +       }
> +
> +       cpuidle_unregister_driver(drv);
> +       goto out;
> +}
> +EXPORT_SYMBOL_GPL(arm_cpuidle_init);
> +
> +void __exit arm_cpuidle_exit(struct cpuidle_driver *drv)
> +{
> +	int cpu;
> +	struct cpuidle_device *device;
> +
> +	for_each_online_cpu(cpu) {
> +		device = &per_cpu(cpuidle_device, cpu);
> +		cpuidle_unregister_device(device);
> +	}
> +
> +	cpuidle_unregister_driver(drv);
> +}
> +EXPORT_SYMBOL_GPL(arm_cpuidle_exit);
> -- 
> 1.7.9.5
>
Daniel Lezcano March 25, 2013, 6:33 p.m. | #2
On 03/25/2013 07:10 PM, Andrew Lunn wrote:
> On Mon, Mar 25, 2013 at 06:55:32PM +0100, Daniel Lezcano wrote:
>> The init and exit routine for most of the drivers are the same,
>> that is register the driver and register the device.
>>
>> Provide a common function to do that in the cpuidle driver for ARM,
>> so we can get rid of a lot of code duplication in the different SOC
>> cpuidle drivers.
> 
> Hi Daniel
> 
> Please could you add a comment in the code about which piece is
> specific to ARM, because its not obvious to me. Its not like there is
> a reference to WFI for example. It looks like this code could go in
> drivers/cpuidle/cpuidle.c

Yes, I agree. At the first glance, the code, as it is, could go in this
file but more ARM specific code will be moved to this ARM generic code
driver like device tree description and couple idle states. The init
function would be more arch specific then.

For this reason, I think it is reasonable to move to
arm/kernel/cpuidle.c rather than drivers/cpuidle/cpuidle.c first.

In the future, when all the ARM cpuidle driver will be fully
consolidated, that will be easier to identify the common parts across
the different arch and then move them to the generic framework.

>> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
>> ---
>>  arch/arm/include/asm/cpuidle.h |    4 +++
>>  arch/arm/kernel/cpuidle.c      |   57 +++++++++++++++++++++++++++++++++++++++-
>>  2 files changed, 60 insertions(+), 1 deletion(-)
>>
>> diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
>> index 7367787..83a38ac 100644
>> --- a/arch/arm/include/asm/cpuidle.h
>> +++ b/arch/arm/include/asm/cpuidle.h
>> @@ -4,6 +4,10 @@
>>  extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
>>  				    struct cpuidle_driver *drv, int index);
>>  
>> +extern int arm_cpuidle_init(struct cpuidle_driver *drv);
>> +
>> +extern void arm_cpuidle_exit(struct cpuidle_driver *drv);
>> +
>>  /* Common ARM WFI state */
>>  #define ARM_CPUIDLE_WFI_STATE_PWR(p) {\
>>  	.enter                  = arm_cpuidle_simple_enter,\
>> diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c
>> index 89545f6..13cfe3e 100644
>> --- a/arch/arm/kernel/cpuidle.c
>> +++ b/arch/arm/kernel/cpuidle.c
>> @@ -9,13 +9,68 @@
>>   * http://www.gnu.org/copyleft/gpl.html
>>   */
>>  
>> +#include <linux/module.h>
>>  #include <linux/cpuidle.h>
>>  #include <asm/proc-fns.h>
>>  
>> +static DEFINE_PER_CPU(struct cpuidle_device, cpuidle_device);
>> +
>>  int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
>> -		struct cpuidle_driver *drv, int index)
>> +			     struct cpuidle_driver *drv, int index)
>>  {
>>  	cpu_do_idle();
>>  
>>  	return index;
>>  }
>> +
>> +int __init arm_cpuidle_init(struct cpuidle_driver *drv)
>> +{
>> +       int ret, cpu;
>> +       struct cpuidle_device *device;
>> +
>> +       ret = cpuidle_register_driver(drv);
>> +       if (ret) {
>> +               printk(KERN_ERR "failed to register idle driver '%s'\n",
>> +                       drv->name);
>> +               return ret;
>> +       }
>> +
>> +       for_each_online_cpu(cpu) {
>> +
>> +               device = &per_cpu(cpuidle_device, cpu);
>> +               device->cpu = cpu;
>> +               ret = cpuidle_register_device(device);
>> +               if (ret) {
>> +                       printk(KERN_ERR "Failed to register cpuidle "
>> +                              "device for cpu%d\n", cpu);
>> +                       goto out_unregister;
>> +               }
>> +       }
>> +
>> +out:
>> +       return ret;
>> +
>> +out_unregister:
>> +       for_each_online_cpu(cpu) {
>> +               device = &per_cpu(cpuidle_device, cpu);
>> +               cpuidle_unregister_device(device);
>> +       }
>> +
>> +       cpuidle_unregister_driver(drv);
>> +       goto out;
>> +}
>> +EXPORT_SYMBOL_GPL(arm_cpuidle_init);
>> +
>> +void __exit arm_cpuidle_exit(struct cpuidle_driver *drv)
>> +{
>> +	int cpu;
>> +	struct cpuidle_device *device;
>> +
>> +	for_each_online_cpu(cpu) {
>> +		device = &per_cpu(cpuidle_device, cpu);
>> +		cpuidle_unregister_device(device);
>> +	}
>> +
>> +	cpuidle_unregister_driver(drv);
>> +}
>> +EXPORT_SYMBOL_GPL(arm_cpuidle_exit);
>> -- 
>> 1.7.9.5
>>
Andrew Lunn March 25, 2013, 7:09 p.m. | #3
> > Please could you add a comment in the code about which piece is
> > specific to ARM, because its not obvious to me. Its not like there is
> > a reference to WFI for example. It looks like this code could go in
> > drivers/cpuidle/cpuidle.c
> 
> Yes, I agree. At the first glance, the code, as it is, could go in this
> file but more ARM specific code will be moved to this ARM generic code
> driver like device tree description and couple idle states. The init
> function would be more arch specific then.

Hi Daniel

There was a discussion about device tree bindings when i posted
the kirkwood cpuidle driver, now in drivers/cpuidle/cpuidle-kirkwood.c.

The conclusion was that pseudo devices, like cpuidle, do not have DT
bindings. They can check of_machine_is_compatible(), like
cpuidle-calxeda.c does, or they are platform drivers, which is what
cpuidle-kirkwood.c is.

Even if DT binding was allowed, it again should not be ARM specific.

Are coupled idle states ARM specific?

     Andrew
Daniel Lezcano March 25, 2013, 7:20 p.m. | #4
On 03/25/2013 08:09 PM, Andrew Lunn wrote:
>>> Please could you add a comment in the code about which piece is
>>> specific to ARM, because its not obvious to me. Its not like there is
>>> a reference to WFI for example. It looks like this code could go in
>>> drivers/cpuidle/cpuidle.c
>>
>> Yes, I agree. At the first glance, the code, as it is, could go in this
>> file but more ARM specific code will be moved to this ARM generic code
>> driver like device tree description and couple idle states. The init
>> function would be more arch specific then.
> 
> Hi Daniel
> 
> There was a discussion about device tree bindings when i posted
> the kirkwood cpuidle driver, now in drivers/cpuidle/cpuidle-kirkwood.c.
> 
> The conclusion was that pseudo devices, like cpuidle, do not have DT
> bindings. They can check of_machine_is_compatible(), like
> cpuidle-calxeda.c does, or they are platform drivers, which is what
> cpuidle-kirkwood.c is.
> 
> Even if DT binding was allowed, it again should not be ARM specific.

If the DT binding was allowed, I *may* not be ARM specific but will
certainly used only by the ARM drivers as the x86 platform uses ACPI or
static tables.

> Are coupled idle states ARM specific?

Well the code is not arch specific but today the idle coupling is ARM
specific because it is the only arch using this kind of synchronization.
There is also a last man standing algorithm common to ux500 and imx
(maybe exynos soon) I would like to merge into this ARM driver.

AFAIK, the cluster shutdown is managed by the hardware on x86 so there
is no need for this.
Amit Kucheria March 25, 2013, 7:31 p.m. | #5
On Tue, Mar 26, 2013 at 12:50 AM, Daniel Lezcano
<daniel.lezcano@linaro.org> wrote:
> On 03/25/2013 08:09 PM, Andrew Lunn wrote:
>>>> Please could you add a comment in the code about which piece is
>>>> specific to ARM, because its not obvious to me. Its not like there is
>>>> a reference to WFI for example. It looks like this code could go in
>>>> drivers/cpuidle/cpuidle.c
>>>
>>> Yes, I agree. At the first glance, the code, as it is, could go in this
>>> file but more ARM specific code will be moved to this ARM generic code
>>> driver like device tree description and couple idle states. The init
>>> function would be more arch specific then.
>>
>> Hi Daniel
>>
>> There was a discussion about device tree bindings when i posted
>> the kirkwood cpuidle driver, now in drivers/cpuidle/cpuidle-kirkwood.c.
>>
>> The conclusion was that pseudo devices, like cpuidle, do not have DT
>> bindings. They can check of_machine_is_compatible(), like
>> cpuidle-calxeda.c does, or they are platform drivers, which is what
>> cpuidle-kirkwood.c is.
>>
>> Even if DT binding was allowed, it again should not be ARM specific.
>
> If the DT binding was allowed, I *may* not be ARM specific but will
> certainly used only by the ARM drivers as the x86 platform uses ACPI or
> static tables.
>
>> Are coupled idle states ARM specific?
>
> Well the code is not arch specific but today the idle coupling is ARM
> specific because it is the only arch using this kind of synchronization.
> There is also a last man standing algorithm common to ux500 and imx
> (maybe exynos soon) I would like to merge into this ARM driver.

Nico has developed a last man standing algorithm[1] for big.LITTLE
TC2. That too needs to be considered during this consolidation. While
it was developed for multi-cluster configurations, I don't see what it
shouldn't work here too.

[1] http://lwn.net/Articles/539082/
Daniel Lezcano March 25, 2013, 7:46 p.m. | #6
On 03/25/2013 08:31 PM, Amit Kucheria wrote:
> On Tue, Mar 26, 2013 at 12:50 AM, Daniel Lezcano
> <daniel.lezcano@linaro.org> wrote:
>> On 03/25/2013 08:09 PM, Andrew Lunn wrote:
>>>>> Please could you add a comment in the code about which piece is
>>>>> specific to ARM, because its not obvious to me. Its not like there is
>>>>> a reference to WFI for example. It looks like this code could go in
>>>>> drivers/cpuidle/cpuidle.c
>>>>
>>>> Yes, I agree. At the first glance, the code, as it is, could go in this
>>>> file but more ARM specific code will be moved to this ARM generic code
>>>> driver like device tree description and couple idle states. The init
>>>> function would be more arch specific then.
>>>
>>> Hi Daniel
>>>
>>> There was a discussion about device tree bindings when i posted
>>> the kirkwood cpuidle driver, now in drivers/cpuidle/cpuidle-kirkwood.c.
>>>
>>> The conclusion was that pseudo devices, like cpuidle, do not have DT
>>> bindings. They can check of_machine_is_compatible(), like
>>> cpuidle-calxeda.c does, or they are platform drivers, which is what
>>> cpuidle-kirkwood.c is.
>>>
>>> Even if DT binding was allowed, it again should not be ARM specific.
>>
>> If the DT binding was allowed, I *may* not be ARM specific but will
>> certainly used only by the ARM drivers as the x86 platform uses ACPI or
>> static tables.
>>
>>> Are coupled idle states ARM specific?
>>
>> Well the code is not arch specific but today the idle coupling is ARM
>> specific because it is the only arch using this kind of synchronization.
>> There is also a last man standing algorithm common to ux500 and imx
>> (maybe exynos soon) I would like to merge into this ARM driver.
> 
> Nico has developed a last man standing algorithm[1] for big.LITTLE
> TC2. That too needs to be considered during this consolidation. While
> it was developed for multi-cluster configurations, I don't see what it
> shouldn't work here too.
> 
> [1] http://lwn.net/Articles/539082/

I had it in mind when answering but I didn't mention it.

Thanks Amit for the pointer.

  -- Daniel
Nicolas Pitre March 25, 2013, 8:28 p.m. | #7
On Mon, 25 Mar 2013, Daniel Lezcano wrote:

> On 03/25/2013 07:10 PM, Andrew Lunn wrote:
> > On Mon, Mar 25, 2013 at 06:55:32PM +0100, Daniel Lezcano wrote:
> >> The init and exit routine for most of the drivers are the same,
> >> that is register the driver and register the device.
> >>
> >> Provide a common function to do that in the cpuidle driver for ARM,
> >> so we can get rid of a lot of code duplication in the different SOC
> >> cpuidle drivers.
> > 
> > Hi Daniel
> > 
> > Please could you add a comment in the code about which piece is
> > specific to ARM, because its not obvious to me. Its not like there is
> > a reference to WFI for example. It looks like this code could go in
> > drivers/cpuidle/cpuidle.c
> 
> Yes, I agree. At the first glance, the code, as it is, could go in this
> file but more ARM specific code will be moved to this ARM generic code
> driver like device tree description and couple idle states. The init
> function would be more arch specific then.
> 
> For this reason, I think it is reasonable to move to
> arm/kernel/cpuidle.c rather than drivers/cpuidle/cpuidle.c first.

No.

Please move things under drivers/cpuidle/ upfront.

We do want drivers to be gathered according to their purpose and 
subsystem, not according to the architecture they belong to.

This is a simple maintenance optimization to do so.

The reason is when someone wishes to improve the subsystem then that 
someone who might know nothing about the ARM or other architectures 
won't have to look for obscure drivers buried into arch specific 
directories.  When things are gathered under a common directory it is 
then much easier to perform wide ranging changes to a subsystem.

And for those who do know the ARM architecture and wish to modify the 
ARM driver it is not that hard to locate the driver once.

The "downside" for you might be that the activity under drivers/cpuidle/ 
is more closely scrutinized by more people.  But that isn't a bad thing 
either.

> In the future, when all the ARM cpuidle driver will be fully
> consolidated, that will be easier to identify the common parts across
> the different arch and then move them to the generic framework.

Nothing prevents you from doing that consolidation work right in 
drivers/cpuidle/.


Nicolas
Andrew Lunn March 25, 2013, 9:42 p.m. | #8
> If the DT binding was allowed, I *may* not be ARM specific but will
> certainly used only by the ARM drivers as the x86 platform uses ACPI or
> static tables.

And powerpc? Its powerpc that created DT, as far as i understand.

arch/powerpc/platforms/pseries/processor_idle.c

static int pseries_idle_devices_init(void)
{
        int i;
        struct cpuidle_driver *drv = &pseries_idle_driver;
        struct cpuidle_device *dev;

        pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
        if (pseries_cpuidle_devices == NULL)
                return -ENOMEM;

        for_each_possible_cpu(i) {
                dev = per_cpu_ptr(pseries_cpuidle_devices, i);
                dev->state_count = drv->state_count;
                dev->cpu = i;
                if (cpuidle_register_device(dev)) {
                        printk(KERN_DEBUG \
                                "cpuidle_register_device %d failed!\n", i);
                        return -EIO;
                }
        }

        return 0;
}


This looks pretty similar to the code you are consolidating. Can your
'ARM' code be made to work on powerpc?

     Andrew
Daniel Lezcano March 25, 2013, 9:53 p.m. | #9
On 03/25/2013 09:28 PM, Nicolas Pitre wrote:
> On Mon, 25 Mar 2013, Daniel Lezcano wrote:
> 
>> On 03/25/2013 07:10 PM, Andrew Lunn wrote:
>>> On Mon, Mar 25, 2013 at 06:55:32PM +0100, Daniel Lezcano wrote:
>>>> The init and exit routine for most of the drivers are the same,
>>>> that is register the driver and register the device.
>>>>
>>>> Provide a common function to do that in the cpuidle driver for ARM,
>>>> so we can get rid of a lot of code duplication in the different SOC
>>>> cpuidle drivers.
>>>
>>> Hi Daniel
>>>
>>> Please could you add a comment in the code about which piece is
>>> specific to ARM, because its not obvious to me. Its not like there is
>>> a reference to WFI for example. It looks like this code could go in
>>> drivers/cpuidle/cpuidle.c
>>
>> Yes, I agree. At the first glance, the code, as it is, could go in this
>> file but more ARM specific code will be moved to this ARM generic code
>> driver like device tree description and couple idle states. The init
>> function would be more arch specific then.
>>
>> For this reason, I think it is reasonable to move to
>> arm/kernel/cpuidle.c rather than drivers/cpuidle/cpuidle.c first.
> 
> No.
> 
> Please move things under drivers/cpuidle/ upfront.
>
> We do want drivers to be gathered according to their purpose and 
> subsystem, not according to the architecture they belong to.
> 
> This is a simple maintenance optimization to do so.
> 
> The reason is when someone wishes to improve the subsystem then that 
> someone who might know nothing about the ARM or other architectures 
> won't have to look for obscure drivers buried into arch specific 
> directories.  When things are gathered under a common directory it is 
> then much easier to perform wide ranging changes to a subsystem.
> 
> And for those who do know the ARM architecture and wish to modify the 
> ARM driver it is not that hard to locate the driver once.
> 
> The "downside" for you might be that the activity under drivers/cpuidle/ 
> is more closely scrutinized by more people.  But that isn't a bad thing 
> either.
> 
>> In the future, when all the ARM cpuidle driver will be fully
>> consolidated, that will be easier to identify the common parts across
>> the different arch and then move them to the generic framework.
> 
> Nothing prevents you from doing that consolidation work right in 
> drivers/cpuidle/.

I fully agree with you but I think there is a misunderstanding.

The idea is to consolidate the ARM code in the ARM cpuidle driver which
is mostly empty. The init/exit functions could falsely lead someone to
think they could be moved in the generic framework but IMO it is too
soon because the code consolidation will bring some arch specificity and
in this case we will going back and forth. I want to avoid that.

Let me consolidate the ARM driver, cleanup the headers to split the arch
specific code from the rest and then move this driver to
drivers/cpuidle. This is just a question of a week.
Nicolas Pitre March 25, 2013, 9:57 p.m. | #10
On Mon, 25 Mar 2013, Daniel Lezcano wrote:

> On 03/25/2013 09:28 PM, Nicolas Pitre wrote:
> > On Mon, 25 Mar 2013, Daniel Lezcano wrote:
> > 
> >> In the future, when all the ARM cpuidle driver will be fully
> >> consolidated, that will be easier to identify the common parts across
> >> the different arch and then move them to the generic framework.
> > 
> > Nothing prevents you from doing that consolidation work right in 
> > drivers/cpuidle/.
> 
> I fully agree with you but I think there is a misunderstanding.
> 
> The idea is to consolidate the ARM code in the ARM cpuidle driver which
> is mostly empty. The init/exit functions could falsely lead someone to
> think they could be moved in the generic framework but IMO it is too
> soon because the code consolidation will bring some arch specificity and
> in this case we will going back and forth. I want to avoid that.
> 
> Let me consolidate the ARM driver, cleanup the headers to split the arch
> specific code from the rest and then move this driver to
> drivers/cpuidle. This is just a question of a week.

OK.


Nicolas
Daniel Lezcano March 25, 2013, 10:09 p.m. | #11
On 03/25/2013 10:42 PM, Andrew Lunn wrote:
>> If the DT binding was allowed, I *may* not be ARM specific but will
>> certainly used only by the ARM drivers as the x86 platform uses ACPI or
>> static tables.
> 
> And powerpc? Its powerpc that created DT, as far as i understand.
> 
> arch/powerpc/platforms/pseries/processor_idle.c
> 
> static int pseries_idle_devices_init(void)
> {
>         int i;
>         struct cpuidle_driver *drv = &pseries_idle_driver;
>         struct cpuidle_device *dev;
> 
>         pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
>         if (pseries_cpuidle_devices == NULL)
>                 return -ENOMEM;
> 
>         for_each_possible_cpu(i) {
>                 dev = per_cpu_ptr(pseries_cpuidle_devices, i);
>                 dev->state_count = drv->state_count;
>                 dev->cpu = i;
>                 if (cpuidle_register_device(dev)) {
>                         printk(KERN_DEBUG \
>                                 "cpuidle_register_device %d failed!\n", i);
>                         return -EIO;
>                 }
>         }
> 
>         return 0;
> }
> 
> 
> This looks pretty similar to the code you are consolidating. Can your
> 'ARM' code be made to work on powerpc?

Yes, it is very similar. I am aware of this code and the cpuidle code of
all others archs but for now there are *18* cpuidle drivers for ARM I
would like to consolidate in priority into a single one. When all of
them will be factored out, I will recheck with the other arch. That will
be easier to consolidate four archs: x86, arm, sh and powerpc.

May be in the meantime, someone will cleanup the non-ARM drivers and
make my life easier :)

In any case, I keep in mind there are other arch using cpuidle.

Thanks
  -- Daniel

Patch

diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h
index 7367787..83a38ac 100644
--- a/arch/arm/include/asm/cpuidle.h
+++ b/arch/arm/include/asm/cpuidle.h
@@ -4,6 +4,10 @@ 
 extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
 				    struct cpuidle_driver *drv, int index);
 
+extern int arm_cpuidle_init(struct cpuidle_driver *drv);
+
+extern void arm_cpuidle_exit(struct cpuidle_driver *drv);
+
 /* Common ARM WFI state */
 #define ARM_CPUIDLE_WFI_STATE_PWR(p) {\
 	.enter                  = arm_cpuidle_simple_enter,\
diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c
index 89545f6..13cfe3e 100644
--- a/arch/arm/kernel/cpuidle.c
+++ b/arch/arm/kernel/cpuidle.c
@@ -9,13 +9,68 @@ 
  * http://www.gnu.org/copyleft/gpl.html
  */
 
+#include <linux/module.h>
 #include <linux/cpuidle.h>
 #include <asm/proc-fns.h>
 
+static DEFINE_PER_CPU(struct cpuidle_device, cpuidle_device);
+
 int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
-		struct cpuidle_driver *drv, int index)
+			     struct cpuidle_driver *drv, int index)
 {
 	cpu_do_idle();
 
 	return index;
 }
+
+int __init arm_cpuidle_init(struct cpuidle_driver *drv)
+{
+       int ret, cpu;
+       struct cpuidle_device *device;
+
+       ret = cpuidle_register_driver(drv);
+       if (ret) {
+               printk(KERN_ERR "failed to register idle driver '%s'\n",
+                       drv->name);
+               return ret;
+       }
+
+       for_each_online_cpu(cpu) {
+
+               device = &per_cpu(cpuidle_device, cpu);
+               device->cpu = cpu;
+               ret = cpuidle_register_device(device);
+               if (ret) {
+                       printk(KERN_ERR "Failed to register cpuidle "
+                              "device for cpu%d\n", cpu);
+                       goto out_unregister;
+               }
+       }
+
+out:
+       return ret;
+
+out_unregister:
+       for_each_online_cpu(cpu) {
+               device = &per_cpu(cpuidle_device, cpu);
+               cpuidle_unregister_device(device);
+       }
+
+       cpuidle_unregister_driver(drv);
+       goto out;
+}
+EXPORT_SYMBOL_GPL(arm_cpuidle_init);
+
+void __exit arm_cpuidle_exit(struct cpuidle_driver *drv)
+{
+	int cpu;
+	struct cpuidle_device *device;
+
+	for_each_online_cpu(cpu) {
+		device = &per_cpu(cpuidle_device, cpu);
+		cpuidle_unregister_device(device);
+	}
+
+	cpuidle_unregister_driver(drv);
+}
+EXPORT_SYMBOL_GPL(arm_cpuidle_exit);