* [PATCH v2] Add cpuidle support for at91
[not found] ` <20090909151417.GA4014@pc-ras4041.res.insa>
@ 2009-09-29 14:15 ` Nicolas Ferre
2009-09-30 12:34 ` Albin Tonnerre
2009-10-22 15:47 ` Albin Tonnerre
0 siblings, 2 replies; 6+ messages in thread
From: Nicolas Ferre @ 2009-09-29 14:15 UTC (permalink / raw)
To: linux-arm-kernel
Salut Albin !
Albin Tonnerre :
> Nicolas, Andrew: ping?
Indeed it's been a long time...
> On Thu, 13 Aug 2009 21:32 +0200, Albin Tonnerre wrote :
>> This patch adds the support for cpuidle on AT91 SoCs, taken from the
>> cpuidle support in mach-kirkwood.
>> cpuidle needs sdram_selfrefresh_enable and _disable, so move their
>> definition to a separate header file instead of duplicating the code
>> already used in pm.c.
>>
>> Signed-off-by: Albin Tonnerre <albin.tonnerre@free-electrons.com>
[nicolas.ferre at atmel.com: tested on at91sam9263ek]
Tested-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Works very well and allow to save some power. After quick measures, we
run at 172mA at 12V instead of 190mA at 12V so ~2.06W and a saving of ~10% :
nice job !
This is of course in my test conditions.
Just one precision: what is the difference, entering state0 with only
the current cpu_do_idle() that is activated by default ?
>> ---
>> Changelog since V1:
>> Updated thanks to feedback from Marc Pignat:
>> - On AT91RM200, it is necessary to make sure that the Low-power mode is
>> left before entering the self-refresh mode
>> - Document the fact that restoring the low-power mode when we're not
>> sure we're out of self-refresh mode is not recommended on RM200
>>
>> arch/arm/mach-at91/Makefile | 1 +
>> arch/arm/mach-at91/cpuidle.c | 101 ++++++++++++++++++++++++++++++++++++++++++
>> arch/arm/mach-at91/pm.c | 62 ++------------------------
>> arch/arm/mach-at91/pm.h | 62 ++++++++++++++++++++++++++
>> 4 files changed, 168 insertions(+), 58 deletions(-)
>> mode change 100644 => 100755 arch/arm/mach-at91/Makefile
>> create mode 100644 arch/arm/mach-at91/cpuidle.c
>> create mode 100644 arch/arm/mach-at91/pm.h
>>
>> diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile
>> old mode 100644
>> new mode 100755
>> index c69ff23..06d189d
>> --- a/arch/arm/mach-at91/Makefile
>> +++ b/arch/arm/mach-at91/Makefile
>> @@ -67,6 +67,7 @@ obj-y += leds.o
>> # Power Management
>> obj-$(CONFIG_PM) += pm.o
>> obj-$(CONFIG_AT91_SLOW_CLOCK) += pm_slowclock.o
>> +obj-$(CONFIG_CPU_IDLE) += cpuidle.o
>>
>> ifeq ($(CONFIG_PM_DEBUG),y)
>> CFLAGS_pm.o += -DDEBUG
>> diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c
>> new file mode 100644
>> index 0000000..9146135
>> --- /dev/null
>> +++ b/arch/arm/mach-at91/cpuidle.c
>> @@ -0,0 +1,101 @@
>> +/*
>> + * based on arch/arm/mach-kirkwood/cpuidle.c
>> + *
>> + * CPU idle support for AT91 SoC
>> + *
>> + * This file is licensed under the terms of the GNU General Public
>> + * License version 2. This program is licensed "as is" without any
>> + * warranty of any kind, whether express or implied.
>> + *
>> + * The cpu idle uses wait-for-interrupt and RAM self refresh in order
>> + * to implement two idle states -
>> + * #1 wait-for-interrupt
>> + * #2 wait-for-interrupt and RAM self refresh
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/init.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/cpuidle.h>
>> +#include <asm/proc-fns.h>
>> +#include <linux/io.h>
>> +
>> +#include "pm.h"
>> +
>> +#define AT91_MAX_STATES 2
>> +
>> +static DEFINE_PER_CPU(struct cpuidle_device, at91_cpuidle_device);
>> +
>> +static struct cpuidle_driver at91_idle_driver = {
>> + .name = "at91_idle",
>> + .owner = THIS_MODULE,
>> +};
>> +
>> +/* Actual code that puts the SoC in different idle states */
>> +static int at91_enter_idle(struct cpuidle_device *dev,
>> + struct cpuidle_state *state)
>> +{
>> + struct timeval before, after;
>> + int idle_time;
>> + u32 saved_lpr;
>> +
>> + local_irq_disable();
>> + do_gettimeofday(&before);
>> + if (state == &dev->states[0])
>> + /* Wait for interrupt state */
>> + cpu_do_idle();
>> + else if (state == &dev->states[1]) {
>> + asm("b 1f; .align 5; 1:");
>> + asm("mcr p15, 0, r0, c7, c10, 4"); /* drain write buffer */
>> + saved_lpr = sdram_selfrefresh_enable();
>> + cpu_do_idle();
>> + /*
>> + * On AT91RM200, self-refresh mode is exited as soon as a memory access
>> + * is made, but we don't know for sure when that happens. However, we
>> + * need to restore the low-power mode if it was enabled before going
>> + * idle. Restoring low-power mode while still in self-refresh is "not
>> + * recommended", but seems to work.
>> + */
>> + sdram_selfrefresh_disable(saved_lpr);
>> + }
>> + do_gettimeofday(&after);
>> + local_irq_enable();
>> + idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
>> + (after.tv_usec - before.tv_usec);
>> + return idle_time;
>> +}
>> +
>> +/* Initialize CPU idle by registering the idle states */
>> +static int at91_init_cpuidle(void)
>> +{
>> + struct cpuidle_device *device;
>> +
>> + cpuidle_register_driver(&at91_idle_driver);
>> +
>> + device = &per_cpu(at91_cpuidle_device, smp_processor_id());
>> + device->state_count = AT91_MAX_STATES;
>> +
>> + /* Wait for interrupt state */
>> + device->states[0].enter = at91_enter_idle;
>> + device->states[0].exit_latency = 1;
>> + device->states[0].target_residency = 10000;
>> + device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
>> + strcpy(device->states[0].name, "WFI");
>> + strcpy(device->states[0].desc, "Wait for interrupt");
>> +
>> + /* Wait for interrupt and RAM self refresh state */
>> + device->states[1].enter = at91_enter_idle;
>> + device->states[1].exit_latency = 10;
>> + device->states[1].target_residency = 10000;
>> + device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
>> + strcpy(device->states[1].name, "RAM_SR");
>> + strcpy(device->states[1].desc, "WFI and RAM Self Refresh");
>> +
>> + if (cpuidle_register_device(device)) {
>> + printk(KERN_ERR "at91_init_cpuidle: Failed registering\n");
>> + return -EIO;
>> + }
>> + return 0;
>> +}
>> +
>> +device_initcall(at91_init_cpuidle);
>> diff --git a/arch/arm/mach-at91/pm.c b/arch/arm/mach-at91/pm.c
>> index e26c4fe..0a68c46 100644
>> --- a/arch/arm/mach-at91/pm.c
>> +++ b/arch/arm/mach-at91/pm.c
>> @@ -29,62 +29,7 @@
>> #include <mach/cpu.h>
>>
>> #include "generic.h"
>> -
>> -#ifdef CONFIG_ARCH_AT91RM9200
>> -#include <mach/at91rm9200_mc.h>
>> -
>> -/*
>> - * The AT91RM9200 goes into self-refresh mode with this command, and will
>> - * terminate self-refresh automatically on the next SDRAM access.
>> - */
>> -#define sdram_selfrefresh_enable() at91_sys_write(AT91_SDRAMC_SRR, 1)
>> -#define sdram_selfrefresh_disable() do {} while (0)
>> -
>> -#elif defined(CONFIG_ARCH_AT91CAP9)
>> -#include <mach/at91cap9_ddrsdr.h>
>> -
>> -static u32 saved_lpr;
>> -
>> -static inline void sdram_selfrefresh_enable(void)
>> -{
>> - u32 lpr;
>> -
>> - saved_lpr = at91_sys_read(AT91_DDRSDRC_LPR);
>> -
>> - lpr = saved_lpr & ~AT91_DDRSDRC_LPCB;
>> - at91_sys_write(AT91_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH);
>> -}
>> -
>> -#define sdram_selfrefresh_disable() at91_sys_write(AT91_DDRSDRC_LPR, saved_lpr)
>> -
>> -#else
>> -#include <mach/at91sam9_sdramc.h>
>> -
>> -#ifdef CONFIG_ARCH_AT91SAM9263
>> -/*
>> - * FIXME either or both the SDRAM controllers (EB0, EB1) might be in use;
>> - * handle those cases both here and in the Suspend-To-RAM support.
>> - */
>> -#define AT91_SDRAMC AT91_SDRAMC0
>> -#warning Assuming EB1 SDRAM controller is *NOT* used
>> -#endif
>> -
>> -static u32 saved_lpr;
>> -
>> -static inline void sdram_selfrefresh_enable(void)
>> -{
>> - u32 lpr;
>> -
>> - saved_lpr = at91_sys_read(AT91_SDRAMC_LPR);
>> -
>> - lpr = saved_lpr & ~AT91_SDRAMC_LPCB;
>> - at91_sys_write(AT91_SDRAMC_LPR, lpr | AT91_SDRAMC_LPCB_SELF_REFRESH);
>> -}
>> -
>> -#define sdram_selfrefresh_disable() at91_sys_write(AT91_SDRAMC_LPR, saved_lpr)
>> -
>> -#endif
>> -
>> +#include "pm.h"
>>
>> /*
>> * Show the reason for the previous system reset.
>> @@ -259,6 +204,7 @@ extern u32 at91_slow_clock_sz;
>>
>> static int at91_pm_enter(suspend_state_t state)
>> {
>> + u32 saved_lpr;
>> at91_gpio_suspend();
>> at91_irq_suspend();
>>
>> @@ -314,9 +260,9 @@ static int at91_pm_enter(suspend_state_t state)
>> */
>> asm("b 1f; .align 5; 1:");
>> asm("mcr p15, 0, r0, c7, c10, 4"); /* drain write buffer */
>> - sdram_selfrefresh_enable();
>> + saved_lpr = sdram_selfrefresh_enable();
>> asm("mcr p15, 0, r0, c7, c0, 4"); /* wait for interrupt */
>> - sdram_selfrefresh_disable();
>> + sdram_selfrefresh_disable(saved_lpr);
>> break;
>>
>> case PM_SUSPEND_ON:
>> diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
>> new file mode 100644
>> index 0000000..0b8b717
>> --- /dev/null
>> +++ b/arch/arm/mach-at91/pm.h
>> @@ -0,0 +1,62 @@
>> +#ifdef CONFIG_ARCH_AT91RM9200
>> +#include <mach/at91rm9200_mc.h>
>> +
>> +/*
>> + * The AT91RM9200 goes into self-refresh mode with this command, and will
>> + * terminate self-refresh automatically on the next SDRAM access.
>> + */
>> +
>> +static inline u32 sdram_selfrefresh_enable(void)
>> +{
>> + u32 saved_lpr = at91_sys_read(AT91_SDRAMC_LPR);
>> +
>> + at91_sys_write(AT91_SDRAMC_LPR, 0);
>> + at91_sys_write(AT91_SDRAMC_SRR, 1);
>> + return saved_lpr;
>> +}
>> +
>> +#define sdram_selfrefresh_disable(saved_lpr) do { at91_sys_write(AT91_SDRAMC_LPR, saved_lpr); } while (0)
>> +
>> +#elif defined(CONFIG_ARCH_AT91CAP9)
>> +#include <mach/at91cap9_ddrsdr.h>
>> +
>> +
>> +static inline u32 sdram_selfrefresh_enable(void)
>> +{
>> + u32 saved_lpr, lpr;
>> +
>> + saved_lpr = at91_sys_read(AT91_DDRSDRC_LPR);
>> +
>> + lpr = saved_lpr & ~AT91_DDRSDRC_LPCB;
>> + at91_sys_write(AT91_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH);
>> + return saved_lpr;
>> +}
>> +
>> +#define sdram_selfrefresh_disable(saved_lpr) at91_sys_write(AT91_DDRSDRC_LPR, saved_lpr)
>> +
>> +#else
>> +#include <mach/at91sam9_sdramc.h>
>> +
>> +#ifdef CONFIG_ARCH_AT91SAM9263
>> +/*
>> + * FIXME either or both the SDRAM controllers (EB0, EB1) might be in use;
>> + * handle those cases both here and in the Suspend-To-RAM support.
>> + */
>> +#define AT91_SDRAMC AT91_SDRAMC0
>> +#warning Assuming EB1 SDRAM controller is *NOT* used
>> +#endif
>> +
>> +static inline u32 sdram_selfrefresh_enable(void)
>> +{
>> + u32 saved_lpr, lpr;
>> +
>> + saved_lpr = at91_sys_read(AT91_SDRAMC_LPR);
>> +
>> + lpr = saved_lpr & ~AT91_SDRAMC_LPCB;
>> + at91_sys_write(AT91_SDRAMC_LPR, lpr | AT91_SDRAMC_LPCB_SELF_REFRESH);
>> + return saved_lpr;
>> +}
>> +
>> +#define sdram_selfrefresh_disable(saved_lpr) at91_sys_write(AT91_SDRAMC_LPR, saved_lpr)
>> +
>> +#endif
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] Add cpuidle support for at91
2009-09-29 14:15 ` [PATCH v2] Add cpuidle support for at91 Nicolas Ferre
@ 2009-09-30 12:34 ` Albin Tonnerre
2009-09-30 12:56 ` Nicolas Ferre
2009-10-22 15:47 ` Albin Tonnerre
1 sibling, 1 reply; 6+ messages in thread
From: Albin Tonnerre @ 2009-09-30 12:34 UTC (permalink / raw)
To: linux-arm-kernel
[Moving the discussion to l-a-k at lists.infradead.org]
Salut Nicolas !
Thanks a lot for your answer.
On Tue, 29 Sep 2009 16:15 +0200, Nicolas Ferre wrote :
> Works very well and allow to save some power. After quick measures, we
> run at 172mA at 12V instead of 190mA at 12V so ~2.06W and a saving of ~10% :
... and you haven't tried cpufreq yet :)
> nice job !
> This is of course in my test conditions.
> Just one precision: what is the difference, entering state0 with only
> the current cpu_do_idle() that is activated by default ?
Sorry, I don't get what you mean. Would you mind elaborating a bit?
Regards,
--
Albin Tonnerre, Free Electrons
Kernel, drivers and embedded Linux development,
consulting, training and support.
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 836 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20090930/35313a82/attachment.sig>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] Add cpuidle support for at91
2009-09-30 12:34 ` Albin Tonnerre
@ 2009-09-30 12:56 ` Nicolas Ferre
2009-10-02 8:46 ` Wolfram Sang
2009-10-09 10:53 ` Albin Tonnerre
0 siblings, 2 replies; 6+ messages in thread
From: Nicolas Ferre @ 2009-09-30 12:56 UTC (permalink / raw)
To: linux-arm-kernel
Albin Tonnerre :
>> Just one precision: what is the difference, entering state0 with only
>> the current cpu_do_idle() that is activated by default ?
>
> Sorry, I don't get what you mean. Would you mind elaborating a bit?
I guess that during idle time, even without the cpuidle infrastructure,
the SOC enters arch_idle() that calls cpu_do_idle().
So, now that I have your patch applied, I wonder what is the difference
between the old situation and the first state of the cpuidle table.
In other words, only state "wait-for-interrupt *and* RAM self refresh"
brings some more power saving. Indeed, cpu_do_idle() that correspond to
the "WFI" state was already the way of dealing with idle cpu, even
without your patch. Am I correct ?
Best regards,
--
Nicolas Ferre
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] Add cpuidle support for at91
2009-09-30 12:56 ` Nicolas Ferre
@ 2009-10-02 8:46 ` Wolfram Sang
2009-10-09 10:53 ` Albin Tonnerre
1 sibling, 0 replies; 6+ messages in thread
From: Wolfram Sang @ 2009-10-02 8:46 UTC (permalink / raw)
To: linux-arm-kernel
> In other words, only state "wait-for-interrupt *and* RAM self refresh"
> brings some more power saving. Indeed, cpu_do_idle() that correspond to
> the "WFI" state was already the way of dealing with idle cpu, even
> without your patch. Am I correct ?
Yes, but it has a higher exit latency. Those data is provided to the cpuidle
framework, so it can decide which state to use.
I wonder if other people can confirm a similar amount of power savings? I am
trying to implement the RAM self refresh for i.MX and don't see much
improvment. I guess I still do something wrong...
Regards,
Wolfram
--
Pengutronix e.K. | Wolfram Sang |
Industrial Linux Solutions | http://www.pengutronix.de/ |
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 197 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20091002/4f2c5efa/attachment.sig>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] Add cpuidle support for at91
2009-09-30 12:56 ` Nicolas Ferre
2009-10-02 8:46 ` Wolfram Sang
@ 2009-10-09 10:53 ` Albin Tonnerre
1 sibling, 0 replies; 6+ messages in thread
From: Albin Tonnerre @ 2009-10-09 10:53 UTC (permalink / raw)
To: linux-arm-kernel
On Wed, 30 Sep 2009 14:56 +0200, Nicolas Ferre wrote :
> Albin Tonnerre :
> >> Just one precision: what is the difference, entering state0 with only
> >> the current cpu_do_idle() that is activated by default ?
> >
> > Sorry, I don't get what you mean. Would you mind elaborating a bit?
>
> I guess that during idle time, even without the cpuidle infrastructure,
> the SOC enters arch_idle() that calls cpu_do_idle().
>
> So, now that I have your patch applied, I wonder what is the difference
> between the old situation and the first state of the cpuidle table.
>
> In other words, only state "wait-for-interrupt *and* RAM self refresh"
> brings some more power saving. Indeed, cpu_do_idle() that correspond to
> the "WFI" state was already the way of dealing with idle cpu, even
> without your patch. Am I correct ?
Indeed, only the second mode brings us some gain.
Regards,
--
Albin Tonnerre, Free Electrons
Kernel, drivers and embedded Linux development,
consulting, training and support.
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 835 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20091009/20844aa2/attachment.sig>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v2] Add cpuidle support for at91
2009-09-29 14:15 ` [PATCH v2] Add cpuidle support for at91 Nicolas Ferre
2009-09-30 12:34 ` Albin Tonnerre
@ 2009-10-22 15:47 ` Albin Tonnerre
1 sibling, 0 replies; 6+ messages in thread
From: Albin Tonnerre @ 2009-10-22 15:47 UTC (permalink / raw)
To: linux-arm-kernel
[Sending it to the right list this time, sorry for the noise]
On Tue, 29 Sep 2009 16:15 +0200, Nicolas Ferre wrote :
> > On Thu, 13 Aug 2009 21:32 +0200, Albin Tonnerre wrote :
> >> This patch adds the support for cpuidle on AT91 SoCs, taken from the
> >> cpuidle support in mach-kirkwood.
> >> cpuidle needs sdram_selfrefresh_enable and _disable, so move their
> >> definition to a separate header file instead of duplicating the code
> >> already used in pm.c.
> >>
> >> Signed-off-by: Albin Tonnerre <albin.tonnerre@free-electrons.com>
>
> [nicolas.ferre at atmel.com: tested on at91sam9263ek]
> Tested-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Andrew: what's your opinion on this? If it's OK, I'd really like to see it
pushed forward.
Regards,
--
Albin Tonnerre, Free Electrons
Kernel, drivers and embedded Linux development,
consulting, training and support.
http://free-electrons.com
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2009-10-22 15:47 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <1247680190-19674-1-git-send-email-albin.tonnerre@free-electrons.com>
[not found] ` <200908111200.58866.marc.pignat@hevs.ch>
[not found] ` <20090812165700.GC4051@pc-ras4041.res.insa>
[not found] ` <200908130858.47487.marc.pignat@hevs.ch>
[not found] ` <20090813193241.GA5985@pc-ras4041.res.insa>
[not found] ` <20090909151417.GA4014@pc-ras4041.res.insa>
2009-09-29 14:15 ` [PATCH v2] Add cpuidle support for at91 Nicolas Ferre
2009-09-30 12:34 ` Albin Tonnerre
2009-09-30 12:56 ` Nicolas Ferre
2009-10-02 8:46 ` Wolfram Sang
2009-10-09 10:53 ` Albin Tonnerre
2009-10-22 15:47 ` Albin Tonnerre
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).