SUPERH platform development
 help / color / mirror / Atom feed
* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
From: Kevin Hilman @ 2011-09-29  0:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei
In-Reply-To: <201109272150.42732.rjw@sisk.pl>

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Tuesday, September 27, 2011, Rafael J. Wysocki wrote:
>> On Tuesday, September 27, 2011, Kevin Hilman wrote:
>> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> > 
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
>> > > domain callbacks under power.lock if power.irq_safe is set for the
>> > > given device.  This is inconsistent with that rpm_idle() does after
>> > > commit 02b2677 (PM / Runtime: Allow _put_sync() from
>> > > interrupts-disabled context) and is problematic for subsystems and PM
>> > > domains wanting to use power.lock for synchronization in their
>> > > runtime PM callbacks.
>> > >
>> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > 
>> > The part described here looks right, and is much better for consistency.
>> > 
>> > Reviewed-by: Kevin Hilman <khilman@ti.com>
>> > 
>> > but...
>> > 
>> > [...]
>> > 
>> > > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>> > >  			goto out;
>> > >  		}
>> > >  
>> > > +		if (dev->power.irq_safe) {
>> > > +			spin_unlock(&dev->power.lock);
>> > > +
>> > > +			cpu_relax();
>> > > +
>> > > +			spin_lock(&dev->power.lock);
>> > > +			goto repeat;
>> > > +		}
>> > > +
>> > 
>> > 
>> > ... AFAICT, this isn't directly related to the problem described in the
>> > changelog (or at least I didn't find it obvious),
>> 
>> It is related.  Whether or not it's obvious, I'm not sure. :-)
>> 
>> The problem is that after the changes in __rpm_callback() another CPU may start
>> executing the same routine for the same device if dev->power.irq_safe is set
>> (previously, it would block on the dev's power.lock) and it may see
>> dev->power.runtime_status = RPM_RESUMING or
>> dev->power.runtime_status = RPM_SUSPENDING, while previously, it wouldn't
>> reach the relevant code.  Thus we have to modify that code to take
>> the dev->power.irq_safe case into account.
>> 
>> > and probably deserves a comment in the code as well.
>> 
>> Well, the comment in the code would explain why the commit did what it did,
>> but it wouldn't be very useful afterwards IMHO.
>> 
>> Perhaps I'll simply add some explanation to the changelog.
>
> Below is the patch with the new changelog, for completness.

Thanks, it's much clearer now.

Kevin

> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set
>
> The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> domain callbacks under power.lock if power.irq_safe is set for the
> given device.  This is inconsistent with that rpm_idle() does after
> commit 02b2677 (PM / Runtime: Allow _put_sync() from
> interrupts-disabled context) and is problematic for subsystems and PM
> domains wanting to use power.lock for synchronization in their
> runtime PM callbacks.
>
> This change requires the code checking if the device's runtime PM
> status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take
> the power.irq_safe set case into account (that code wasn't reachable
> before with power.irq_safe set, because it's executed with the
> device's power.lock held).
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Reviewed-by: Ming Lei <tom.leiming@gmail.com>
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> ---
>  drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
>  1 file changed, 46 insertions(+), 22 deletions(-)
>
> Index: linux/drivers/base/power/runtime.c
> =================================> --- linux.orig/drivers/base/power/runtime.c
> +++ linux/drivers/base/power/runtime.c
> @@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
>  }
>  
>  /**
> + * __rpm_callback - Run a given runtime PM callback for a given device.
> + * @cb: Runtime PM callback to run.
> + * @dev: Device to run the callback for.
> + */
> +static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
> +	__releases(&dev->power.lock) __acquires(&dev->power.lock)
> +{
> +	int retval;
> +
> +	if (dev->power.irq_safe)
> +		spin_unlock(&dev->power.lock);
> +	else
> +		spin_unlock_irq(&dev->power.lock);
> +
> +	retval = cb(dev);
> +
> +	if (dev->power.irq_safe)
> +		spin_lock(&dev->power.lock);
> +	else
> +		spin_lock_irq(&dev->power.lock);
> +
> +	return retval;
> +}
> +
> +/**
>   * rpm_idle - Notify device bus type if the device can be suspended.
>   * @dev: Device to notify the bus type about.
>   * @rpmflags: Flag bits.
> @@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
>  	else
>  		callback = NULL;
>  
> -	if (callback) {
> -		if (dev->power.irq_safe)
> -			spin_unlock(&dev->power.lock);
> -		else
> -			spin_unlock_irq(&dev->power.lock);
> -
> -		callback(dev);
> -
> -		if (dev->power.irq_safe)
> -			spin_lock(&dev->power.lock);
> -		else
> -			spin_lock_irq(&dev->power.lock);
> -	}
> +	if (callback)
> +		__rpm_callback(callback, dev);
>  
>  	dev->power.idle_notification = false;
>  	wake_up_all(&dev->power.wait_queue);
> @@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
>   * @dev: Device to run the callback for.
>   */
>  static int rpm_callback(int (*cb)(struct device *), struct device *dev)
> -	__releases(&dev->power.lock) __acquires(&dev->power.lock)
>  {
>  	int retval;
>  
>  	if (!cb)
>  		return -ENOSYS;
>  
> -	if (dev->power.irq_safe) {
> -		retval = cb(dev);
> -	} else {
> -		spin_unlock_irq(&dev->power.lock);
> -
> -		retval = cb(dev);
> +	retval = __rpm_callback(cb, dev);
>  
> -		spin_lock_irq(&dev->power.lock);
> -	}
>  	dev->power.runtime_error = retval;
>  	return retval != -EACCES ? retval : -EIO;
>  }
> @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +
>  		/* Wait for the other suspend running in parallel with us. */
>  		for (;;) {
>  			prepare_to_wait(&dev->power.wait_queue, &wait,
> @@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
>  			goto out;
>  		}
>  
> +		if (dev->power.irq_safe) {
> +			spin_unlock(&dev->power.lock);
> +
> +			cpu_relax();
> +
> +			spin_lock(&dev->power.lock);
> +			goto repeat;
> +		}
> +
>  		/* Wait for the operation carried out in parallel with us. */
>  		for (;;) {
>  			prepare_to_wait(&dev->power.wait_queue, &wait,
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply

* 群发软件+买家搜索机+最新广交会买家、海
From: 仅10元每天 @ 2011-09-28 20:56 UTC (permalink / raw)
  To: linux-sh
In-Reply-To: <20111213084743353137@amidress.com>

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 17288 bytes --]

������������������������������������+���������������������������������������������+109������������������������������������������������������������������������������������������������������������������������������������������������,B2B������������������������������������500������������������

������������������8������������������(������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)��������� 
1���������2011������������������109��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 
2������������������������������������������������������������������������,���������451660������������������������������������ 
3���������2008���������,2009���������,2010��������� ������������������+������������������������������������������������������������������������������������������103 104 105 106 107 108 ��������������������������� ���������120.6������������������������������������
4���������2010������������������������������������������������������������������������PPAI��������������������������������������������� PPAI Members Directory������������������������������������������������������������������������������������������
5���������2010���������������������������������������������������������������������������������������������������������������������(���������������������������������������������������������������)������������������7.2������������������������������������������������������������������������������������������������������������
6���������60.8������������������������������������������������������B2B���������������������������������������������
7���������2009���������������������������������������������������������������piers��������������������������� 1���������������������������
8���������������������������������������������������������������������������������������������������������������������������������������������������������

��������� 500���������������������������������������������������������������������������������Email. 

���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������

������������������������������������������������������QQ: 1339625218   ������������������������������������������������������������������������: 1339625218@qq.com
������������������������������������������������������QQ: 1339625218   ������������������������������������������������������������������������: 1339625218@qq.com
������������������������������������������������������QQ: 1339625218   ������������������������������������������������������������������������: 1339625218@qq.com
 

������������������������������������������������������������������������������������������������������������,���������������������������������������������������������������,������������������������������������������������������������������������,��������������������������������������������������������������������������������������������������� 

���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������




������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
1 ������������������������������������
2 ������������������������������������������������������������������������
3 ������������������
4 ������������������������������������
5 ������������������
6 ���������������������������������������������������������������
7 ������������������������������������
8 ���������������������������������������������
9 ������������������
10 ������������������
11 ������������������������������������
12 ������������������������������������
13 ������������������
14 ������������������
15 ������������������������������������������������������
16 ������������������������������������
17 ������������������������������������
18 ������������������
19 ������������������������������������
20 ���������������������������������������������������������������
21 ������������������������������������
22 ���������������������������������������������
23 ���������������������������
24 ������������������������������������
25 ������������������
26 ������������������
27 ������������������
28 ������������������
29 ������������������
30 ������������������
31 ������������������������������������
32 ���������
33 ������������������������������������
34 ������������������
35 ������������������������������������
36 ������������������
37 ������������������������������������
38 ������������������������������������
39 ���������������������������
40 ���������


���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������
���������������������������������������������������������������������������������������������������������������{.n���+���������������������+%���������������������\x17������w������{.n���+������������{���������������{ay���\x1d���������������,j\a������f���������h���������������������������z_������\x03(���������������������j"������\x1a���^[m������������\a������G������������������?���������������&���������~������

^ permalink raw reply

* Re: [PATCH 3/3] net: sh_eth: move the asm/sh_eth.h to
From: David Miller @ 2011-09-28 17:43 UTC (permalink / raw)
  To: yoshihiro.shimoda.uh; +Cc: lethal, netdev, linux-sh
In-Reply-To: <4E82D178.9020808@renesas.com>

From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Date: Wed, 28 Sep 2011 16:49:12 +0900

> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>

Applied.

^ permalink raw reply

* Re: [PATCH 2/3] sh: modify prototype in sh_eth.h
From: David Miller @ 2011-09-28 17:43 UTC (permalink / raw)
  To: yoshihiro.shimoda.uh; +Cc: lethal, linux-sh, netdev
In-Reply-To: <4E82D171.6060200@renesas.com>

From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Date: Wed, 28 Sep 2011 16:49:05 +0900

> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>

Applied.

^ permalink raw reply

* Re: [PATCH 1/3] net: sh_eth: use ioremap()
From: David Miller @ 2011-09-28 17:43 UTC (permalink / raw)
  To: yoshihiro.shimoda.uh; +Cc: netdev, linux-sh
In-Reply-To: <4E82D16A.8090105@renesas.com>

From: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Date: Wed, 28 Sep 2011 16:48:58 +0900

> This patch also changes writel/readl to iowrite32/ioread32.
> 
> Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>

Applied.

^ permalink raw reply

* [PATCH] ARM: mach-shmobile: sh73a0 GPIO IRQ support
From: Magnus Damm @ 2011-09-28  7:55 UTC (permalink / raw)
  To: linux-sh

From: Magnus Damm <damm@opensource.se>

This patch adds support for sh73a0 GPIO IRQs by making use
of the PFC GPIO IRQ feature. Only IRQ pins are  supported
at this time. In the future when PINT interrupts also are
supported properly we can easily extend the table with such
information. Also, the sh73a0 is currently making use of
the GIC for external interrupt which is rather unflexible
when it comes to triggering configuration at this point.

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 arch/arm/mach-shmobile/pfc-sh73a0.c |   41 +++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

--- 0006/arch/arm/mach-shmobile/pfc-sh73a0.c
+++ work/arch/arm/mach-shmobile/pfc-sh73a0.c	2011-09-28 16:31:42.000000000 +0900
@@ -22,6 +22,7 @@
 #include <linux/kernel.h>
 #include <linux/gpio.h>
 #include <mach/sh73a0.h>
+#include <mach/irqs.h>
 
 #define _1(fn, pfx, sfx) fn(pfx, sfx)
 
@@ -2765,6 +2766,43 @@ static struct pinmux_data_reg pinmux_dat
 	{ },
 };
 
+#define EXT_IRQ(n) gic_spi((n) + 1) /* GIC SPI starting from 1 for IRQ0 */
+
+static struct pinmux_irq pinmux_irqs[] = {
+	PINMUX_IRQ(EXT_IRQ(19), PORT9_FN0),
+	PINMUX_IRQ(EXT_IRQ(1), PORT10_FN0),
+	PINMUX_IRQ(EXT_IRQ(0), PORT11_FN0),
+	PINMUX_IRQ(EXT_IRQ(18), PORT13_FN0),
+	PINMUX_IRQ(EXT_IRQ(20), PORT14_FN0),
+	PINMUX_IRQ(EXT_IRQ(21), PORT15_FN0),
+	PINMUX_IRQ(EXT_IRQ(31), PORT26_FN0),
+	PINMUX_IRQ(EXT_IRQ(30), PORT27_FN0),
+	PINMUX_IRQ(EXT_IRQ(29), PORT28_FN0),
+	PINMUX_IRQ(EXT_IRQ(22), PORT40_FN0),
+	PINMUX_IRQ(EXT_IRQ(23), PORT53_FN0),
+	PINMUX_IRQ(EXT_IRQ(10), PORT54_FN0),
+	PINMUX_IRQ(EXT_IRQ(9), PORT56_FN0),
+	PINMUX_IRQ(EXT_IRQ(26), PORT115_FN0),
+	PINMUX_IRQ(EXT_IRQ(27), PORT116_FN0),
+	PINMUX_IRQ(EXT_IRQ(28), PORT117_FN0),
+	PINMUX_IRQ(EXT_IRQ(24), PORT118_FN0),
+	PINMUX_IRQ(EXT_IRQ(6), PORT147_FN0),
+	PINMUX_IRQ(EXT_IRQ(2), PORT149_FN0),
+	PINMUX_IRQ(EXT_IRQ(7), PORT150_FN0),
+	PINMUX_IRQ(EXT_IRQ(12), PORT156_FN0),
+	PINMUX_IRQ(EXT_IRQ(4), PORT159_FN0),
+	PINMUX_IRQ(EXT_IRQ(25), PORT164_FN0),
+	PINMUX_IRQ(EXT_IRQ(8), PORT223_FN0),
+	PINMUX_IRQ(EXT_IRQ(3), PORT224_FN0),
+	PINMUX_IRQ(EXT_IRQ(5), PORT227_FN0),
+	PINMUX_IRQ(EXT_IRQ(17), PORT234_FN0),
+	PINMUX_IRQ(EXT_IRQ(11), PORT238_FN0),
+	PINMUX_IRQ(EXT_IRQ(13), PORT239_FN0),
+	PINMUX_IRQ(EXT_IRQ(16), PORT249_FN0),
+	PINMUX_IRQ(EXT_IRQ(14), PORT251_FN0),
+	PINMUX_IRQ(EXT_IRQ(9), PORT308_FN0),
+};
+
 static struct pinmux_info sh73a0_pinmux_info = {
 	.name = "sh73a0_pfc",
 	.reserved_id = PINMUX_RESERVED,
@@ -2785,6 +2823,9 @@ static struct pinmux_info sh73a0_pinmux_
 
 	.gpio_data = pinmux_data,
 	.gpio_data_size = ARRAY_SIZE(pinmux_data),
+
+	.gpio_irq = pinmux_irqs,
+	.gpio_irq_size = ARRAY_SIZE(pinmux_irqs),
 };
 
 void sh73a0_pinmux_init(void)

^ permalink raw reply

* [PATCH] sh: pfc: Add GPIO IRQ support
From: Magnus Damm @ 2011-09-28  7:50 UTC (permalink / raw)
  To: linux-sh

From: Magnus Damm <damm@opensource.se>

Add GPIO IRQ support to the shared PFC code in drivers/sh/pfc.c

The enums pointed out by a certain GPIO will be matched against
a table for IRQ to enum mappings.

Only the shared PFC code is updated by this patch. SoC specific
changes are also needed to allow platforms to make use of this
feature.

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 Applies on top of "sh: pfc: Remove unused gpio_in_use member"

 drivers/sh/pfc.c       |   27 +++++++++++++++++++++++++++
 include/linux/sh_pfc.h |   11 +++++++++++
 2 files changed, 38 insertions(+)

--- 0001/drivers/sh/pfc.c
+++ work/drivers/sh/pfc.c	2011-09-28 15:02:21.000000000 +0900
@@ -577,6 +577,32 @@ static void sh_gpio_set(struct gpio_chip
 	sh_gpio_set_value(chip_to_pinmux(chip), offset, value);
 }
 
+static int sh_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+	struct pinmux_info *gpioc = chip_to_pinmux(chip);
+	pinmux_enum_t enum_id;
+	pinmux_enum_t *enum_ids;
+	int i, k, pos;
+
+	pos = 0;
+	enum_id = 0;
+	while (1) {
+		pos = get_gpio_enum_id(gpioc, offset, pos, &enum_id);
+		if (pos <= 0 || !enum_id)
+			break;
+
+		for (i = 0; i < gpioc->gpio_irq_size; i++) {
+			enum_ids = gpioc->gpio_irq[i].enum_ids;
+			for (k = 0; enum_ids[k]; k++) {
+				if (enum_ids[k] = enum_id)
+					return gpioc->gpio_irq[i].irq;
+			}
+		}
+	}
+
+	return -ENOSYS;
+}
+
 int register_pinmux(struct pinmux_info *pip)
 {
 	struct gpio_chip *chip = &pip->chip;
@@ -592,6 +618,7 @@ int register_pinmux(struct pinmux_info *
 	chip->get = sh_gpio_get;
 	chip->direction_output = sh_gpio_direction_output;
 	chip->set = sh_gpio_set;
+	chip->to_irq = sh_gpio_to_irq;
 
 	WARN_ON(pip->first_gpio != 0); /* needs testing */
 
--- 0004/include/linux/sh_pfc.h
+++ work/include/linux/sh_pfc.h	2011-09-28 15:01:46.000000000 +0900
@@ -61,6 +61,14 @@ struct pinmux_data_reg {
 	.reg = r, .reg_width = r_width,	\
 	.enum_ids = (pinmux_enum_t [r_width]) \
 
+struct pinmux_irq {
+	int irq;
+	pinmux_enum_t *enum_ids;
+};
+
+#define PINMUX_IRQ(irq_nr, ids...)			   \
+	{ .irq = irq_nr, .enum_ids = (pinmux_enum_t []) { ids, 0 } }	\
+
 struct pinmux_range {
 	pinmux_enum_t begin;
 	pinmux_enum_t end;
@@ -87,6 +95,9 @@ struct pinmux_info {
 	pinmux_enum_t *gpio_data;
 	unsigned int gpio_data_size;
 
+	struct pinmux_irq *gpio_irq;
+	unsigned int gpio_irq_size;
+
 	struct gpio_chip chip;
 };
 

^ permalink raw reply

* [PATCH] sh: modify the asm/sh_eth.h to linux/sh_eth.h in some boards
From: Yoshihiro Shimoda @ 2011-09-28  7:49 UTC (permalink / raw)
  To: linux-sh

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
 - This patch depends on the following patch:
  - net: sh_eth: move the asm/sh_eth.h to include/linux/
 - This patch doesn't contain board-sh7757lcr.c, because
   it needs more modification.

 arch/sh/boards/board-espt.c           |    2 +-
 arch/sh/boards/mach-ecovec24/setup.c  |    2 +-
 arch/sh/boards/mach-se/7724/setup.c   |    2 +-
 arch/sh/boards/mach-sh7763rdp/setup.c |    2 +-
 4 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/arch/sh/boards/board-espt.c b/arch/sh/boards/board-espt.c
index 9da92ac..b3ae9d3 100644
--- a/arch/sh/boards/board-espt.c
+++ b/arch/sh/boards/board-espt.c
@@ -13,9 +13,9 @@
 #include <linux/interrupt.h>
 #include <linux/mtd/physmap.h>
 #include <linux/io.h>
+#include <linux/sh_eth.h>
 #include <asm/machvec.h>
 #include <asm/sizes.h>
-#include <asm/sh_eth.h>

 /* NOR Flash */
 static struct mtd_partition espt_nor_flash_partitions[] = {
diff --git a/arch/sh/boards/mach-ecovec24/setup.c b/arch/sh/boards/mach-ecovec24/setup.c
index b24d69d..7f74985 100644
--- a/arch/sh/boards/mach-ecovec24/setup.c
+++ b/arch/sh/boards/mach-ecovec24/setup.c
@@ -28,13 +28,13 @@
 #include <linux/spi/mmc_spi.h>
 #include <linux/input.h>
 #include <linux/input/sh_keysc.h>
+#include <linux/sh_eth.h>
 #include <video/sh_mobile_lcdc.h>
 #include <sound/sh_fsi.h>
 #include <media/sh_mobile_ceu.h>
 #include <media/tw9910.h>
 #include <media/mt9t112.h>
 #include <asm/heartbeat.h>
-#include <asm/sh_eth.h>
 #include <asm/clock.h>
 #include <asm/suspend.h>
 #include <cpu/sh7724.h>
diff --git a/arch/sh/boards/mach-se/7724/setup.c b/arch/sh/boards/mach-se/7724/setup.c
index d007567..b747c0a 100644
--- a/arch/sh/boards/mach-se/7724/setup.c
+++ b/arch/sh/boards/mach-se/7724/setup.c
@@ -23,12 +23,12 @@
 #include <linux/input.h>
 #include <linux/input/sh_keysc.h>
 #include <linux/usb/r8a66597.h>
+#include <linux/sh_eth.h>
 #include <video/sh_mobile_lcdc.h>
 #include <media/sh_mobile_ceu.h>
 #include <sound/sh_fsi.h>
 #include <asm/io.h>
 #include <asm/heartbeat.h>
-#include <asm/sh_eth.h>
 #include <asm/clock.h>
 #include <asm/suspend.h>
 #include <cpu/sh7724.h>
diff --git a/arch/sh/boards/mach-sh7763rdp/setup.c b/arch/sh/boards/mach-sh7763rdp/setup.c
index f3d828f..dd036f1 100644
--- a/arch/sh/boards/mach-sh7763rdp/setup.c
+++ b/arch/sh/boards/mach-sh7763rdp/setup.c
@@ -17,8 +17,8 @@
 #include <linux/mtd/physmap.h>
 #include <linux/fb.h>
 #include <linux/io.h>
+#include <linux/sh_eth.h>
 #include <mach/sh7763rdp.h>
-#include <asm/sh_eth.h>
 #include <asm/sh7760fb.h>

 /* NOR Flash */
-- 
1.7.1

^ permalink raw reply related

* [PATCH 3/3] net: sh_eth: move the asm/sh_eth.h to include/linux/
From: Yoshihiro Shimoda @ 2011-09-28  7:49 UTC (permalink / raw)
  To: davem, Paul Mundt; +Cc: netdev, SH-Linux

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
 This patch is for net-next.

 arch/sh/include/asm/sh_eth.h          |   25 -------------------------
 drivers/net/ethernet/renesas/sh_eth.c |    1 +
 drivers/net/ethernet/renesas/sh_eth.h |    8 --------
 include/linux/sh_eth.h                |   25 +++++++++++++++++++++++++
 4 files changed, 26 insertions(+), 33 deletions(-)
 delete mode 100644 arch/sh/include/asm/sh_eth.h
 create mode 100644 include/linux/sh_eth.h

diff --git a/arch/sh/include/asm/sh_eth.h b/arch/sh/include/asm/sh_eth.h
deleted file mode 100644
index 2076acf..0000000
--- a/arch/sh/include/asm/sh_eth.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef __ASM_SH_ETH_H__
-#define __ASM_SH_ETH_H__
-
-#include <linux/phy.h>
-
-enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN};
-enum {
-	SH_ETH_REG_GIGABIT,
-	SH_ETH_REG_FAST_SH4,
-	SH_ETH_REG_FAST_SH3_SH2
-};
-
-struct sh_eth_plat_data {
-	int phy;
-	int edmac_endian;
-	int register_type;
-	phy_interface_t phy_interface;
-	void (*set_mdio_gate)(void *addr);
-
-	unsigned char mac_addr[6];
-	unsigned no_ether_link:1;
-	unsigned ether_link_active_low:1;
-};
-
-#endif
diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 38ccda5..6aa0704 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -35,6 +35,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/ethtool.h>
+#include <linux/sh_eth.h>

 #include "sh_eth.h"

diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index 78e586e..47877b1 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -23,14 +23,6 @@
 #ifndef __SH_ETH_H__
 #define __SH_ETH_H__

-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/spinlock.h>
-#include <linux/netdevice.h>
-#include <linux/phy.h>
-
-#include <asm/sh_eth.h>
-
 #define CARDNAME	"sh-eth"
 #define TX_TIMEOUT	(5*HZ)
 #define TX_RING_SIZE	64	/* Tx ring size */
diff --git a/include/linux/sh_eth.h b/include/linux/sh_eth.h
new file mode 100644
index 0000000..2076acf
--- /dev/null
+++ b/include/linux/sh_eth.h
@@ -0,0 +1,25 @@
+#ifndef __ASM_SH_ETH_H__
+#define __ASM_SH_ETH_H__
+
+#include <linux/phy.h>
+
+enum {EDMAC_LITTLE_ENDIAN, EDMAC_BIG_ENDIAN};
+enum {
+	SH_ETH_REG_GIGABIT,
+	SH_ETH_REG_FAST_SH4,
+	SH_ETH_REG_FAST_SH3_SH2
+};
+
+struct sh_eth_plat_data {
+	int phy;
+	int edmac_endian;
+	int register_type;
+	phy_interface_t phy_interface;
+	void (*set_mdio_gate)(void *addr);
+
+	unsigned char mac_addr[6];
+	unsigned no_ether_link:1;
+	unsigned ether_link_active_low:1;
+};
+
+#endif
-- 
1.7.1


^ permalink raw reply related

* [PATCH 2/3] sh: modify prototype in sh_eth.h
From: Yoshihiro Shimoda @ 2011-09-28  7:49 UTC (permalink / raw)
  To: Paul Mundt; +Cc: SH-Linux, netdev

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
 arch/sh/include/asm/sh_eth.h |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/sh/include/asm/sh_eth.h b/arch/sh/include/asm/sh_eth.h
index 0f325da..2076acf 100644
--- a/arch/sh/include/asm/sh_eth.h
+++ b/arch/sh/include/asm/sh_eth.h
@@ -15,7 +15,7 @@ struct sh_eth_plat_data {
 	int edmac_endian;
 	int register_type;
 	phy_interface_t phy_interface;
-	void (*set_mdio_gate)(unsigned long addr);
+	void (*set_mdio_gate)(void *addr);

 	unsigned char mac_addr[6];
 	unsigned no_ether_link:1;
-- 
1.7.1


^ permalink raw reply related

* [PATCH 1/3] net: sh_eth: use ioremap()
From: Yoshihiro Shimoda @ 2011-09-28  7:48 UTC (permalink / raw)
  To: davem; +Cc: netdev, SH-Linux

This patch also changes writel/readl to iowrite32/ioread32.

Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
---
 This patch is for net-next.

 drivers/net/ethernet/renesas/sh_eth.c |   38 ++++++++++++++++++++------------
 drivers/net/ethernet/renesas/sh_eth.h |    9 ++++---
 2 files changed, 29 insertions(+), 18 deletions(-)

diff --git a/drivers/net/ethernet/renesas/sh_eth.c b/drivers/net/ethernet/renesas/sh_eth.c
index 4479a45..38ccda5 100644
--- a/drivers/net/ethernet/renesas/sh_eth.c
+++ b/drivers/net/ethernet/renesas/sh_eth.c
@@ -155,18 +155,18 @@ static void sh_eth_chip_reset_giga(struct net_device *ndev)

 	/* save MAHR and MALR */
 	for (i = 0; i < 2; i++) {
-		malr[i] = readl(GIGA_MALR(i));
-		mahr[i] = readl(GIGA_MAHR(i));
+		malr[i] = ioread32((void *)GIGA_MALR(i));
+		mahr[i] = ioread32((void *)GIGA_MAHR(i));
 	}

 	/* reset device */
-	writel(ARSTR_ARSTR, SH_GIGA_ETH_BASE + 0x1800);
+	iowrite32(ARSTR_ARSTR, (void *)(SH_GIGA_ETH_BASE + 0x1800));
 	mdelay(1);

 	/* restore MAHR and MALR */
 	for (i = 0; i < 2; i++) {
-		writel(malr[i], GIGA_MALR(i));
-		writel(mahr[i], GIGA_MAHR(i));
+		iowrite32(malr[i], (void *)GIGA_MALR(i));
+		iowrite32(mahr[i], (void *)GIGA_MAHR(i));
 	}
 }

@@ -515,9 +515,9 @@ static unsigned long sh_eth_get_edtrr_trns(struct sh_eth_private *mdp)
 }

 struct bb_info {
-	void (*set_gate)(unsigned long addr);
+	void (*set_gate)(void *addr);
 	struct mdiobb_ctrl ctrl;
-	u32 addr;
+	void *addr;
 	u32 mmd_msk;/* MMD */
 	u32 mdo_msk;
 	u32 mdi_msk;
@@ -525,21 +525,21 @@ struct bb_info {
 };

 /* PHY bit set */
-static void bb_set(u32 addr, u32 msk)
+static void bb_set(void *addr, u32 msk)
 {
-	writel(readl(addr) | msk, addr);
+	iowrite32(ioread32(addr) | msk, addr);
 }

 /* PHY bit clear */
-static void bb_clr(u32 addr, u32 msk)
+static void bb_clr(void *addr, u32 msk)
 {
-	writel((readl(addr) & ~msk), addr);
+	iowrite32((ioread32(addr) & ~msk), addr);
 }

 /* PHY bit read */
-static int bb_read(u32 addr, u32 msk)
+static int bb_read(void *addr, u32 msk)
 {
-	return (readl(addr) & msk) != 0;
+	return (ioread32(addr) & msk) != 0;
 }

 /* Data I/O pin control */
@@ -1680,7 +1680,7 @@ static int sh_mdio_init(struct net_device *ndev, int id,
 	}

 	/* bitbang init */
-	bitbang->addr = ndev->base_addr + mdp->reg_offset[PIR];
+	bitbang->addr = mdp->addr + mdp->reg_offset[PIR];
 	bitbang->set_gate = pd->set_mdio_gate;
 	bitbang->mdi_msk = 0x08;
 	bitbang->mdo_msk = 0x04;
@@ -1812,6 +1812,13 @@ static int sh_eth_drv_probe(struct platform_device *pdev)
 	ether_setup(ndev);

 	mdp = netdev_priv(ndev);
+	mdp->addr = ioremap(res->start, resource_size(res));
+	if (mdp->addr = NULL) {
+		ret = -ENOMEM;
+		dev_err(&pdev->dev, "ioremap failed.\n");
+		goto out_release;
+	}
+
 	spin_lock_init(&mdp->lock);
 	mdp->pdev = pdev;
 	pm_runtime_enable(&pdev->dev);
@@ -1892,6 +1899,8 @@ out_unregister:

 out_release:
 	/* net_dev free */
+	if (mdp && mdp->addr)
+		iounmap(mdp->addr);
 	if (mdp && mdp->tsu_addr)
 		iounmap(mdp->tsu_addr);
 	if (ndev)
@@ -1910,6 +1919,7 @@ static int sh_eth_drv_remove(struct platform_device *pdev)
 	sh_mdio_release(ndev);
 	unregister_netdev(ndev);
 	pm_runtime_disable(&pdev->dev);
+	iounmap(mdp->addr);
 	free_netdev(ndev);
 	platform_set_drvdata(pdev, NULL);

diff --git a/drivers/net/ethernet/renesas/sh_eth.h b/drivers/net/ethernet/renesas/sh_eth.h
index c3048a6..78e586e 100644
--- a/drivers/net/ethernet/renesas/sh_eth.h
+++ b/drivers/net/ethernet/renesas/sh_eth.h
@@ -762,6 +762,7 @@ struct sh_eth_private {
 	struct platform_device *pdev;
 	struct sh_eth_cpu_data *cd;
 	const u16 *reg_offset;
+	void __iomem *addr;
 	void __iomem *tsu_addr;
 	dma_addr_t rx_desc_dma;
 	dma_addr_t tx_desc_dma;
@@ -811,7 +812,7 @@ static inline void sh_eth_write(struct net_device *ndev, unsigned long data,
 {
 	struct sh_eth_private *mdp = netdev_priv(ndev);

-	writel(data, ndev->base_addr + mdp->reg_offset[enum_index]);
+	iowrite32(data, mdp->addr + mdp->reg_offset[enum_index]);
 }

 static inline unsigned long sh_eth_read(struct net_device *ndev,
@@ -819,19 +820,19 @@ static inline unsigned long sh_eth_read(struct net_device *ndev,
 {
 	struct sh_eth_private *mdp = netdev_priv(ndev);

-	return readl(ndev->base_addr + mdp->reg_offset[enum_index]);
+	return ioread32(mdp->addr + mdp->reg_offset[enum_index]);
 }

 static inline void sh_eth_tsu_write(struct sh_eth_private *mdp,
 				unsigned long data, int enum_index)
 {
-	writel(data, mdp->tsu_addr + mdp->reg_offset[enum_index]);
+	iowrite32(data, mdp->tsu_addr + mdp->reg_offset[enum_index]);
 }

 static inline unsigned long sh_eth_tsu_read(struct sh_eth_private *mdp,
 					int enum_index)
 {
-	return readl(mdp->tsu_addr + mdp->reg_offset[enum_index]);
+	return ioread32(mdp->tsu_addr + mdp->reg_offset[enum_index]);
 }

 #endif	/* #ifndef __SH_ETH_H__ */
-- 
1.7.1


^ permalink raw reply related

* [PATCH] sh: pfc: Remove unused gpio_in_use member
From: Magnus Damm @ 2011-09-28  7:47 UTC (permalink / raw)
  To: linux-sh

From: Magnus Damm <damm@opensource.se>

Remove unused member gpio_in_use from struct pinmux_info.

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 include/linux/sh_pfc.h |    1 -
 1 file changed, 1 deletion(-)

--- 0001/include/linux/sh_pfc.h
+++ work/include/linux/sh_pfc.h	2011-09-28 14:01:29.000000000 +0900
@@ -87,7 +87,6 @@ struct pinmux_info {
 	pinmux_enum_t *gpio_data;
 	unsigned int gpio_data_size;
 
-	unsigned long *gpio_in_use;
 	struct gpio_chip chip;
 };
 

^ permalink raw reply

* Re: [PATCH] dma: shdma: transfer based runtime PM
From: Vinod Koul @ 2011-09-28  4:35 UTC (permalink / raw)
  To: Guennadi Liakhovetski; +Cc: linux-kernel, linux-sh, Dan Williams, Paul Mundt
In-Reply-To: <Pine.LNX.4.64.1109251612320.11831@axis700.grange>

On Sun, 2011-09-25 at 16:19 +0200, Guennadi Liakhovetski wrote:
> Hi Vinod
> 
> On Mon, 5 Sep 2011, Vinod Koul wrote:
> 
> [snip]
> 
> > This patch fails to apply for me, can you please rebase it to me tree
> > and resend, I will apply it later this week
> 
> Sorry, it didn't apply, because I apparently forgot to send out this 
> patch:
> 
> http://marc.info/?l=linux-kernel&m\x131695995404173&w=2
> 
> which I've also cc'ed to you a couple of minutes ago. With that patch 
> applied first, the patch in question
> 
> http://marc.info/?l=linux-kernel&m\x131386369726846&w=2
> 
> still applies cleanly on top of the "next" branch of your slave-dma tree.
I have applied both, Thanks


-- 
~Vinod


^ permalink raw reply

* [GIT PULL] Power management regression fix for 3.1
From: Rafael J. Wysocki @ 2011-09-27 21:42 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: LKML, Linux PM mailing list, Linux-sh list,
	Russell King - ARM Linux, Guennadi Liakhovetski

Hi Linus,

Please pull one power management regression fix for 3.1 on top of commit
f9d81f61c84aca693bc353dfef4b8c36c2e5e1b5 (ptrace: PTRACE_LISTEN forgets to
unlock ->siglock) from

git://github.com/rjwysocki/linux-pm.git pm-fixes

It fixes a locking bug introduced by one of the previous PM commits,
as described in the changelog.

Thanks!


 drivers/base/power/clock_ops.c |   75 ++++++++++++++++++++-------------------
 1 files changed, 38 insertions(+), 37 deletions(-)

---------------

Rafael J. Wysocki (1):
      PM / Clocks: Do not acquire a mutex under a spinlock


^ permalink raw reply

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
From: Rafael J. Wysocki @ 2011-09-27 19:50 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei
In-Reply-To: <201109271916.33616.rjw@sisk.pl>

On Tuesday, September 27, 2011, Rafael J. Wysocki wrote:
> On Tuesday, September 27, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >
> > > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > > domain callbacks under power.lock if power.irq_safe is set for the
> > > given device.  This is inconsistent with that rpm_idle() does after
> > > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > > interrupts-disabled context) and is problematic for subsystems and PM
> > > domains wanting to use power.lock for synchronization in their
> > > runtime PM callbacks.
> > >
> > > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > 
> > The part described here looks right, and is much better for consistency.
> > 
> > Reviewed-by: Kevin Hilman <khilman@ti.com>
> > 
> > but...
> > 
> > [...]
> > 
> > > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
> > >  			goto out;
> > >  		}
> > >  
> > > +		if (dev->power.irq_safe) {
> > > +			spin_unlock(&dev->power.lock);
> > > +
> > > +			cpu_relax();
> > > +
> > > +			spin_lock(&dev->power.lock);
> > > +			goto repeat;
> > > +		}
> > > +
> > 
> > 
> > ... AFAICT, this isn't directly related to the problem described in the
> > changelog (or at least I didn't find it obvious),
> 
> It is related.  Whether or not it's obvious, I'm not sure. :-)
> 
> The problem is that after the changes in __rpm_callback() another CPU may start
> executing the same routine for the same device if dev->power.irq_safe is set
> (previously, it would block on the dev's power.lock) and it may see
> dev->power.runtime_status = RPM_RESUMING or
> dev->power.runtime_status = RPM_SUSPENDING, while previously, it wouldn't
> reach the relevant code.  Thus we have to modify that code to take
> the dev->power.irq_safe case into account.
> 
> > and probably deserves a comment in the code as well.
> 
> Well, the comment in the code would explain why the commit did what it did,
> but it wouldn't be very useful afterwards IMHO.
> 
> Perhaps I'll simply add some explanation to the changelog.

Below is the patch with the new changelog, for completness.

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Runtime: Don't run callbacks under lock for power.irq_safe set

The rpm_suspend() and rpm_resume() routines execute subsystem or PM
domain callbacks under power.lock if power.irq_safe is set for the
given device.  This is inconsistent with that rpm_idle() does after
commit 02b2677 (PM / Runtime: Allow _put_sync() from
interrupts-disabled context) and is problematic for subsystems and PM
domains wanting to use power.lock for synchronization in their
runtime PM callbacks.

This change requires the code checking if the device's runtime PM
status is RPM_SUSPENDING or RPM_RESUMING to be modified too, to take
the power.irq_safe set case into account (that code wasn't reachable
before with power.irq_safe set, because it's executed with the
device's power.lock held).

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Reviewed-by: Ming Lei <tom.leiming@gmail.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 drivers/base/power/runtime.c |   68 +++++++++++++++++++++++++++++--------------
 1 file changed, 46 insertions(+), 22 deletions(-)

Index: linux/drivers/base/power/runtime.c
=================================--- linux.orig/drivers/base/power/runtime.c
+++ linux/drivers/base/power/runtime.c
@@ -155,6 +155,31 @@ static int rpm_check_suspend_allowed(str
 }
 
 /**
+ * __rpm_callback - Run a given runtime PM callback for a given device.
+ * @cb: Runtime PM callback to run.
+ * @dev: Device to run the callback for.
+ */
+static int __rpm_callback(int (*cb)(struct device *), struct device *dev)
+	__releases(&dev->power.lock) __acquires(&dev->power.lock)
+{
+	int retval;
+
+	if (dev->power.irq_safe)
+		spin_unlock(&dev->power.lock);
+	else
+		spin_unlock_irq(&dev->power.lock);
+
+	retval = cb(dev);
+
+	if (dev->power.irq_safe)
+		spin_lock(&dev->power.lock);
+	else
+		spin_lock_irq(&dev->power.lock);
+
+	return retval;
+}
+
+/**
  * rpm_idle - Notify device bus type if the device can be suspended.
  * @dev: Device to notify the bus type about.
  * @rpmflags: Flag bits.
@@ -225,19 +250,8 @@ static int rpm_idle(struct device *dev,
 	else
 		callback = NULL;
 
-	if (callback) {
-		if (dev->power.irq_safe)
-			spin_unlock(&dev->power.lock);
-		else
-			spin_unlock_irq(&dev->power.lock);
-
-		callback(dev);
-
-		if (dev->power.irq_safe)
-			spin_lock(&dev->power.lock);
-		else
-			spin_lock_irq(&dev->power.lock);
-	}
+	if (callback)
+		__rpm_callback(callback, dev);
 
 	dev->power.idle_notification = false;
 	wake_up_all(&dev->power.wait_queue);
@@ -252,22 +266,14 @@ static int rpm_idle(struct device *dev,
  * @dev: Device to run the callback for.
  */
 static int rpm_callback(int (*cb)(struct device *), struct device *dev)
-	__releases(&dev->power.lock) __acquires(&dev->power.lock)
 {
 	int retval;
 
 	if (!cb)
 		return -ENOSYS;
 
-	if (dev->power.irq_safe) {
-		retval = cb(dev);
-	} else {
-		spin_unlock_irq(&dev->power.lock);
-
-		retval = cb(dev);
+	retval = __rpm_callback(cb, dev);
 
-		spin_lock_irq(&dev->power.lock);
-	}
 	dev->power.runtime_error = retval;
 	return retval != -EACCES ? retval : -EIO;
 }
@@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the other suspend running in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,
@@ -496,6 +511,15 @@ static int rpm_resume(struct device *dev
 			goto out;
 		}
 
+		if (dev->power.irq_safe) {
+			spin_unlock(&dev->power.lock);
+
+			cpu_relax();
+
+			spin_lock(&dev->power.lock);
+			goto repeat;
+		}
+
 		/* Wait for the operation carried out in parallel with us. */
 		for (;;) {
 			prepare_to_wait(&dev->power.wait_queue, &wait,

^ permalink raw reply

* Re: [PATCH 2/3] PM / Runtime: Don't run callbacks under lock for power.irq_safe set
From: Rafael J. Wysocki @ 2011-09-27 17:16 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, LKML, Linux-sh list, Magnus Damm,
	jean.pihet, Ming Lei
In-Reply-To: <87oby66ejr.fsf@ti.com>

On Tuesday, September 27, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > The rpm_suspend() and rpm_resume() routines execute subsystem or PM
> > domain callbacks under power.lock if power.irq_safe is set for the
> > given device.  This is inconsistent with that rpm_idle() does after
> > commit 02b2677 (PM / Runtime: Allow _put_sync() from
> > interrupts-disabled context) and is problematic for subsystems and PM
> > domains wanting to use power.lock for synchronization in their
> > runtime PM callbacks.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> 
> The part described here looks right, and is much better for consistency.
> 
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> 
> but...
> 
> [...]
> 
> > @@ -347,6 +353,15 @@ static int rpm_suspend(struct device *de
> >  			goto out;
> >  		}
> >  
> > +		if (dev->power.irq_safe) {
> > +			spin_unlock(&dev->power.lock);
> > +
> > +			cpu_relax();
> > +
> > +			spin_lock(&dev->power.lock);
> > +			goto repeat;
> > +		}
> > +
> 
> 
> ... AFAICT, this isn't directly related to the problem described in the
> changelog (or at least I didn't find it obvious),

It is related.  Whether or not it's obvious, I'm not sure. :-)

The problem is that after the changes in __rpm_callback() another CPU may start
executing the same routine for the same device if dev->power.irq_safe is set
(previously, it would block on the dev's power.lock) and it may see
dev->power.runtime_status = RPM_RESUMING or
dev->power.runtime_status = RPM_SUSPENDING, while previously, it wouldn't
reach the relevant code.  Thus we have to modify that code to take
the dev->power.irq_safe case into account.

> and probably deserves a comment in the code as well.

Well, the comment in the code would explain why the commit did what it did,
but it wouldn't be very useful afterwards IMHO.

Perhaps I'll simply add some explanation to the changelog.

Thanks,
Rafael

^ permalink raw reply

* Re: [PATCH V7 0/4] cpuidle: Global registration of idle states with per-cpu statistics
From: Kevin Hilman @ 2011-09-27 17:12 UTC (permalink / raw)
  To: Deepthi Dharwar
  Cc: venki, ak, len.brown, peterz, santosh.shilimkar, arjan, lenb,
	linux-sh, linux-kernel, linux-acpi, linux-pm, linux-omap,
	linux-arm-kernel
In-Reply-To: <20110927100202.16043.58669.stgit@localhost6.localdomain6>

Deepthi Dharwar <deepthi@linux.vnet.ibm.com> writes:

> Version 6 of this patch series dated 22nd Sept 2011 was 
> Acked-by: Arjan van de Ven <arjan@linux.intel.com>
> Acked-by: Kevin Hilman <khilman@ti.com> for OMAP specific parts.
> Reviewed-by: Kevin Hilman <khilman@ti.com>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>

minor: I think this should be a Tested-by from Jean.  Since he was not on
the delivery path, it should not be a sign-off.

Kevin

^ permalink raw reply

* [PATCH] ARM: sh7372 ap4evb NOR Flash USB boot fix
From: Bastian Hecht @ 2011-09-27 11:23 UTC (permalink / raw)
  To: linux-sh

Always use CS0 shadow area for NOR flash instead of regular CS0
memory area on ap4evb.

When booting from CS0 NOR Flash the regular CS0 memory area is
available, but when booting via USB the MASK ROM gets mapped to
0x0 which gets in the way for the NOR Flash. Always using CS0
shadow area works well for both NOR Flash boot and USB boot.

Signed-off-by: Bastian Hecht <hechtb@gmail.com>
---

diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c
index 1d9080c..3b3cd0c 100644
--- a/arch/arm/mach-shmobile/board-ap4evb.c
+++ b/arch/arm/mach-shmobile/board-ap4evb.c
@@ -198,8 +198,8 @@ static struct physmap_flash_data nor_flash_data = {
 
 static struct resource nor_flash_resources[] = {
 	[0]	= {
-		.start	= 0x00000000,
-		.end	= 0x08000000 - 1,
+		.start	= 0x20000000, /* CS0 shadow instead of regular CS0 */
+		.end	= 0x28000000 - 1, /* needed by USB MASK ROM boot */		
 		.flags	= IORESOURCE_MEM,
 	}
 };

^ permalink raw reply related

* [PATCH V7 4/4] cpuidle: Single/Global registration of idle states
From: Deepthi Dharwar @ 2011-09-27 10:16 UTC (permalink / raw)
  To: khilman, venki, ak, len.brown, peterz, santosh.shilimkar, arjan,
	lenb
  Cc: linux-sh, linux-kernel, linux-acpi, linux-pm, linux-omap,
	linux-arm-kernel
In-Reply-To: <20110927100202.16043.58669.stgit@localhost6.localdomain6>

This patch makes the cpuidle_states structure global (single copy)
instead of per-cpu. The statistics needed on per-cpu basis
by the governor are kept per-cpu. This simplifies the cpuidle
subsystem as state registration is done by single cpu only.
Having single copy of cpuidle_states saves memory. Rare case
of asymmetric C-states can be handled within the cpuidle driver
and architectures such as POWER do not have asymmetric C-states.

Having single/global registration for all the idle states,
dynamic C-state transitions on x86 are handled by
the boot cpu. Here, the boot cpu  would disable all the devices,
re-populate the states and later enable all the devices,
irrespective of the cpu that would receive the notification first.

Reference:
https://lkml.org/lkml/2011/4/25/83

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-at91/cpuidle.c          |   31 +++--
 arch/arm/mach-davinci/cpuidle.c       |   39 ++++---
 arch/arm/mach-exynos4/cpuidle.c       |   23 ++--
 arch/arm/mach-kirkwood/cpuidle.c      |   30 +++--
 arch/arm/mach-omap2/cpuidle34xx.c     |   73 ++++++++-----
 arch/sh/kernel/cpu/shmobile/cpuidle.c |   18 ++-
 drivers/acpi/processor_driver.c       |   20 +--
 drivers/acpi/processor_idle.c         |  191 +++++++++++++++++++++++++++++----
 drivers/cpuidle/cpuidle.c             |   45 ++------
 drivers/cpuidle/driver.c              |   25 ++++
 drivers/cpuidle/governors/ladder.c    |   28 +++--
 drivers/cpuidle/governors/menu.c      |   20 ++-
 drivers/cpuidle/sysfs.c               |    3 -
 drivers/idle/intel_idle.c             |   80 +++++++++++---
 include/acpi/processor.h              |    1 
 include/linux/cpuidle.h               |   19 ++-
 16 files changed, 439 insertions(+), 207 deletions(-)

diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c
index 4696a0d..93178f6 100644
--- a/arch/arm/mach-at91/cpuidle.c
+++ b/arch/arm/mach-at91/cpuidle.c
@@ -33,6 +33,7 @@ static struct cpuidle_driver at91_idle_driver = {
 
 /* Actual code that puts the SoC in different idle states */
 static int at91_enter_idle(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv,
 			       int index)
 {
 	struct timeval before, after;
@@ -64,27 +65,29 @@ static int at91_enter_idle(struct cpuidle_device *dev,
 static int at91_init_cpuidle(void)
 {
 	struct cpuidle_device *device;
-
-	cpuidle_register_driver(&at91_idle_driver);
+	struct cpuidle_driver *driver = &at91_idle_driver;
 
 	device = &per_cpu(at91_cpuidle_device, smp_processor_id());
 	device->state_count = AT91_MAX_STATES;
+	driver->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");
+	driver->states[0].enter = at91_enter_idle;
+	driver->states[0].exit_latency = 1;
+	driver->states[0].target_residency = 10000;
+	driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
+	strcpy(driver->states[0].name, "WFI");
+	strcpy(driver->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");
+	driver->states[1].enter = at91_enter_idle;
+	driver->states[1].exit_latency = 10;
+	driver->states[1].target_residency = 10000;
+	driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
+	strcpy(driver->states[1].name, "RAM_SR");
+	strcpy(driver->states[1].desc, "WFI and RAM Self Refresh");
+
+	cpuidle_register_driver(&at91_idle_driver);
 
 	if (cpuidle_register_device(device)) {
 		printk(KERN_ERR "at91_init_cpuidle: Failed registering\n");
diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c
index f2d2f34..dbeeccd 100644
--- a/arch/arm/mach-davinci/cpuidle.c
+++ b/arch/arm/mach-davinci/cpuidle.c
@@ -78,6 +78,7 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = {
 
 /* Actual code that puts the SoC in different idle states */
 static int davinci_enter_idle(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
 						int index)
 {
 	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
@@ -109,6 +110,7 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)
 {
 	int ret;
 	struct cpuidle_device *device;
+	struct cpuidle_driver *driver = &davinci_idle_driver;
 	struct davinci_cpuidle_config *pdata = pdev->dev.platform_data;
 
 	device = &per_cpu(davinci_cpuidle_device, smp_processor_id());
@@ -120,32 +122,33 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)
 
 	ddr2_reg_base = pdata->ddr2_ctlr_base;
 
-	ret = cpuidle_register_driver(&davinci_idle_driver);
-	if (ret) {
-		dev_err(&pdev->dev, "failed to register driver\n");
-		return ret;
-	}
-
 	/* Wait for interrupt state */
-	device->states[0].enter = davinci_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");
+	driver->states[0].enter = davinci_enter_idle;
+	driver->states[0].exit_latency = 1;
+	driver->states[0].target_residency = 10000;
+	driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
+	strcpy(driver->states[0].name, "WFI");
+	strcpy(driver->states[0].desc, "Wait for interrupt");
 
 	/* Wait for interrupt and DDR self refresh state */
-	device->states[1].enter = davinci_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, "DDR SR");
-	strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
+	driver->states[1].enter = davinci_enter_idle;
+	driver->states[1].exit_latency = 10;
+	driver->states[1].target_residency = 10000;
+	driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
+	strcpy(driver->states[1].name, "DDR SR");
+	strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");
 	if (pdata->ddr2_pdown)
 		davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN;
 	cpuidle_set_statedata(&device->states_usage[1], &davinci_states[1]);
 
 	device->state_count = DAVINCI_CPUIDLE_MAX_STATES;
+	driver->state_count = DAVINCI_CPUIDLE_MAX_STATES;
+
+	ret = cpuidle_register_driver(&davinci_idle_driver);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to register driver\n");
+		return ret;
+	}
 
 	ret = cpuidle_register_device(device);
 	if (ret) {
diff --git a/arch/arm/mach-exynos4/cpuidle.c b/arch/arm/mach-exynos4/cpuidle.c
index ea026e7..35f6502 100644
--- a/arch/arm/mach-exynos4/cpuidle.c
+++ b/arch/arm/mach-exynos4/cpuidle.c
@@ -16,6 +16,7 @@
 #include <asm/proc-fns.h>
 
 static int exynos4_enter_idle(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv,
 			      int index);
 
 static struct cpuidle_state exynos4_cpuidle_set[] = {
@@ -37,6 +38,7 @@ static struct cpuidle_driver exynos4_idle_driver = {
 };
 
 static int exynos4_enter_idle(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
 			      int index)
 {
 	struct timeval before, after;
@@ -60,22 +62,23 @@ static int __init exynos4_init_cpuidle(void)
 {
 	int i, max_cpuidle_state, cpu_id;
 	struct cpuidle_device *device;
-
+	struct cpuidle_driver *drv = &exynos4_idle_driver;
+
+	/* Setup cpuidle driver */
+	drv->state_count = (sizeof(exynos4_cpuidle_set) /
+				       sizeof(struct cpuidle_state));
+	max_cpuidle_state = drv->state_count;
+	for (i = 0; i < max_cpuidle_state; i++) {
+		memcpy(&drv->states[i], &exynos4_cpuidle_set[i],
+				sizeof(struct cpuidle_state));
+	}
 	cpuidle_register_driver(&exynos4_idle_driver);
 
 	for_each_cpu(cpu_id, cpu_online_mask) {
 		device = &per_cpu(exynos4_cpuidle_device, cpu_id);
 		device->cpu = cpu_id;
 
-		device->state_count = (sizeof(exynos4_cpuidle_set) /
-					       sizeof(struct cpuidle_state));
-
-		max_cpuidle_state = device->state_count;
-
-		for (i = 0; i < max_cpuidle_state; i++) {
-			memcpy(&device->states[i], &exynos4_cpuidle_set[i],
-					sizeof(struct cpuidle_state));
-		}
+		device->state_count = drv->state_count;
 
 		if (cpuidle_register_device(device)) {
 			printk(KERN_ERR "CPUidle register device failed\n,");
diff --git a/arch/arm/mach-kirkwood/cpuidle.c b/arch/arm/mach-kirkwood/cpuidle.c
index 358dd80..ffd690d 100644
--- a/arch/arm/mach-kirkwood/cpuidle.c
+++ b/arch/arm/mach-kirkwood/cpuidle.c
@@ -32,6 +32,7 @@ static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device);
 
 /* Actual code that puts the SoC in different idle states */
 static int kirkwood_enter_idle(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
 			       int index)
 {
 	struct timeval before, after;
@@ -68,28 +69,29 @@ static int kirkwood_enter_idle(struct cpuidle_device *dev,
 static int kirkwood_init_cpuidle(void)
 {
 	struct cpuidle_device *device;
-
-	cpuidle_register_driver(&kirkwood_idle_driver);
+	struct cpuidle_driver *driver = &kirkwood_idle_driver;
 
 	device = &per_cpu(kirkwood_cpuidle_device, smp_processor_id());
 	device->state_count = KIRKWOOD_MAX_STATES;
+	driver->state_count = KIRKWOOD_MAX_STATES;
 
 	/* Wait for interrupt state */
-	device->states[0].enter = kirkwood_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");
+	driver->states[0].enter = kirkwood_enter_idle;
+	driver->states[0].exit_latency = 1;
+	driver->states[0].target_residency = 10000;
+	driver->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
+	strcpy(driver->states[0].name, "WFI");
+	strcpy(driver->states[0].desc, "Wait for interrupt");
 
 	/* Wait for interrupt and DDR self refresh state */
-	device->states[1].enter = kirkwood_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, "DDR SR");
-	strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
+	driver->states[1].enter = kirkwood_enter_idle;
+	driver->states[1].exit_latency = 10;
+	driver->states[1].target_residency = 10000;
+	driver->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
+	strcpy(driver->states[1].name, "DDR SR");
+	strcpy(driver->states[1].desc, "WFI and DDR Self Refresh");
 
+	cpuidle_register_driver(&kirkwood_idle_driver);
 	if (cpuidle_register_device(device)) {
 		printk(KERN_ERR "kirkwood_init_cpuidle: Failed registering\n");
 		return -EIO;
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index d3fce7b..1fe35c2 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -88,12 +88,14 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
 /**
  * omap3_enter_idle - Programs OMAP3 to enter the specified state
  * @dev: cpuidle device
+ * @drv: cpuidle driver
  * @index: the index of state to be entered
  *
  * Called from the CPUidle framework to program the device to the
  * specified target state selected by the governor.
  */
 static int omap3_enter_idle(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
 				int index)
 {
 	struct omap3_idle_statedata *cx @@ -148,6 +150,7 @@ return_sleep_time:
 /**
  * next_valid_state - Find next valid C-state
  * @dev: cpuidle device
+ * @drv: cpuidle driver
  * @index: Index of currently selected c-state
  *
  * If the state corresponding to index is valid, index is returned back
@@ -158,10 +161,11 @@ return_sleep_time:
  * if it satisfies the enable_off_mode condition.
  */
 static int next_valid_state(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv,
 				int index)
 {
 	struct cpuidle_state_usage *curr_usage = &dev->states_usage[index];
-	struct cpuidle_state *curr = &dev->states[index];
+	struct cpuidle_state *curr = &drv->states[index];
 	struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr_usage);
 	u32 mpu_deepest_state = PWRDM_POWER_RET;
 	u32 core_deepest_state = PWRDM_POWER_RET;
@@ -188,7 +192,7 @@ static int next_valid_state(struct cpuidle_device *dev,
 
 		/* Reach the current state starting at highest C-state */
 		for (; idx >= 0; idx--) {
-			if (&dev->states[idx] = curr) {
+			if (&drv->states[idx] = curr) {
 				next_index = idx;
 				break;
 			}
@@ -224,12 +228,14 @@ static int next_valid_state(struct cpuidle_device *dev,
 /**
  * omap3_enter_idle_bm - Checks for any bus activity
  * @dev: cpuidle device
+ * @drv: cpuidle driver
  * @index: array index of target state to be programmed
  *
  * This function checks for any pending activity and then programs
  * the device to the specified or a safer state.
  */
 static int omap3_enter_idle_bm(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
 			       int index)
 {
 	int new_state_idx;
@@ -238,7 +244,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	int ret;
 
 	if (!omap3_can_sleep()) {
-		new_state_idx = dev->safe_state_index;
+		new_state_idx = drv->safe_state_index;
 		goto select_state;
 	}
 
@@ -248,7 +254,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	 */
 	cam_state = pwrdm_read_pwrst(cam_pd);
 	if (cam_state = PWRDM_POWER_ON) {
-		new_state_idx = dev->safe_state_index;
+		new_state_idx = drv->safe_state_index;
 		goto select_state;
 	}
 
@@ -275,10 +281,10 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	if (per_next_state != per_saved_state)
 		pwrdm_set_next_pwrst(per_pd, per_next_state);
 
-	new_state_idx = next_valid_state(dev, index);
+	new_state_idx = next_valid_state(dev, drv, index);
 
 select_state:
-	ret = omap3_enter_idle(dev, new_state_idx);
+	ret = omap3_enter_idle(dev, drv, new_state_idx);
 
 	/* Restore original PER state if it was modified */
 	if (per_next_state != per_saved_state)
@@ -311,22 +317,30 @@ struct cpuidle_driver omap3_idle_driver = {
 	.owner = 	THIS_MODULE,
 };
 
-/* Helper to fill the C-state common data and register the driver_data */
-static inline struct omap3_idle_statedata *_fill_cstate(
-					struct cpuidle_device *dev,
+/* Helper to fill the C-state common data*/
+static inline void _fill_cstate(struct cpuidle_driver *drv,
 					int idx, const char *descr)
 {
-	struct omap3_idle_statedata *cx = &omap3_idle_data[idx];
-	struct cpuidle_state *state = &dev->states[idx];
-	struct cpuidle_state_usage *state_usage = &dev->states_usage[idx];
+	struct cpuidle_state *state = &drv->states[idx];
 
 	state->exit_latency	= cpuidle_params_table[idx].exit_latency;
 	state->target_residency	= cpuidle_params_table[idx].target_residency;
 	state->flags		= CPUIDLE_FLAG_TIME_VALID;
 	state->enter		= omap3_enter_idle_bm;
-	cx->valid		= cpuidle_params_table[idx].valid;
 	sprintf(state->name, "C%d", idx + 1);
 	strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
+
+}
+
+/* Helper to register the driver_data */
+static inline struct omap3_idle_statedata *_fill_cstate_usage(
+					struct cpuidle_device *dev,
+					int idx)
+{
+	struct omap3_idle_statedata *cx = &omap3_idle_data[idx];
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[idx];
+
+	cx->valid		= cpuidle_params_table[idx].valid;
 	cpuidle_set_statedata(state_usage, cx);
 
 	return cx;
@@ -341,6 +355,7 @@ static inline struct omap3_idle_statedata *_fill_cstate(
 int __init omap3_idle_init(void)
 {
 	struct cpuidle_device *dev;
+	struct cpuidle_driver *drv = &omap3_idle_driver;
 	struct omap3_idle_statedata *cx;
 
 	mpu_pd = pwrdm_lookup("mpu_pwrdm");
@@ -348,45 +363,52 @@ int __init omap3_idle_init(void)
 	per_pd = pwrdm_lookup("per_pwrdm");
 	cam_pd = pwrdm_lookup("cam_pwrdm");
 
-	cpuidle_register_driver(&omap3_idle_driver);
+
+	drv->safe_state_index = -1;
 	dev = &per_cpu(omap3_idle_dev, smp_processor_id());
-	dev->safe_state_index = -1;
 
 	/* C1 . MPU WFI + Core active */
-	cx = _fill_cstate(dev, 0, "MPU ON + CORE ON");
-	(&dev->states[0])->enter = omap3_enter_idle;
-	dev->safe_state_index = 0;
+	_fill_cstate(drv, 0, "MPU ON + CORE ON");
+	(&drv->states[0])->enter = omap3_enter_idle;
+	drv->safe_state_index = 0;
+	cx = _fill_cstate_usage(dev, 0);
 	cx->valid = 1;	/* C1 is always valid */
 	cx->mpu_state = PWRDM_POWER_ON;
 	cx->core_state = PWRDM_POWER_ON;
 
 	/* C2 . MPU WFI + Core inactive */
-	cx = _fill_cstate(dev, 1, "MPU ON + CORE ON");
+	_fill_cstate(drv, 1, "MPU ON + CORE ON");
+	cx = _fill_cstate_usage(dev, 1);
 	cx->mpu_state = PWRDM_POWER_ON;
 	cx->core_state = PWRDM_POWER_ON;
 
 	/* C3 . MPU CSWR + Core inactive */
-	cx = _fill_cstate(dev, 2, "MPU RET + CORE ON");
+	_fill_cstate(drv, 2, "MPU RET + CORE ON");
+	cx = _fill_cstate_usage(dev, 2);
 	cx->mpu_state = PWRDM_POWER_RET;
 	cx->core_state = PWRDM_POWER_ON;
 
 	/* C4 . MPU OFF + Core inactive */
-	cx = _fill_cstate(dev, 3, "MPU OFF + CORE ON");
+	_fill_cstate(drv, 3, "MPU OFF + CORE ON");
+	cx = _fill_cstate_usage(dev, 3);
 	cx->mpu_state = PWRDM_POWER_OFF;
 	cx->core_state = PWRDM_POWER_ON;
 
 	/* C5 . MPU RET + Core RET */
-	cx = _fill_cstate(dev, 4, "MPU RET + CORE RET");
+	_fill_cstate(drv, 4, "MPU RET + CORE RET");
+	cx = _fill_cstate_usage(dev, 4);
 	cx->mpu_state = PWRDM_POWER_RET;
 	cx->core_state = PWRDM_POWER_RET;
 
 	/* C6 . MPU OFF + Core RET */
-	cx = _fill_cstate(dev, 5, "MPU OFF + CORE RET");
+	_fill_cstate(drv, 5, "MPU OFF + CORE RET");
+	cx = _fill_cstate_usage(dev, 5);
 	cx->mpu_state = PWRDM_POWER_OFF;
 	cx->core_state = PWRDM_POWER_RET;
 
 	/* C7 . MPU OFF + Core OFF */
-	cx = _fill_cstate(dev, 6, "MPU OFF + CORE OFF");
+	_fill_cstate(drv, 6, "MPU OFF + CORE OFF");
+	cx = _fill_cstate_usage(dev, 6);
 	/*
 	 * Erratum i583: implementation for ES rev < Es1.2 on 3630. We cannot
 	 * enable OFF mode in a stable form for previous revisions.
@@ -400,6 +422,9 @@ int __init omap3_idle_init(void)
 	cx->mpu_state = PWRDM_POWER_OFF;
 	cx->core_state = PWRDM_POWER_OFF;
 
+	drv->state_count = OMAP3_NUM_STATES;
+	cpuidle_register_driver(&omap3_idle_driver);
+
 	dev->state_count = OMAP3_NUM_STATES;
 	if (cpuidle_register_device(dev)) {
 		printk(KERN_ERR "%s: CPUidle register device failed\n",
diff --git a/arch/sh/kernel/cpu/shmobile/cpuidle.c b/arch/sh/kernel/cpu/shmobile/cpuidle.c
index 7be50d4c..ad1012a 100644
--- a/arch/sh/kernel/cpu/shmobile/cpuidle.c
+++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c
@@ -25,6 +25,7 @@ static unsigned long cpuidle_mode[] = {
 };
 
 static int cpuidle_sleep_enter(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
 				int index)
 {
 	unsigned long allowed_mode = arch_hwblk_sleep_mode();
@@ -64,19 +65,19 @@ static struct cpuidle_driver cpuidle_driver = {
 void sh_mobile_setup_cpuidle(void)
 {
 	struct cpuidle_device *dev = &cpuidle_dev;
+	struct cpuidle_driver *drv = &cpuidle_driver;
 	struct cpuidle_state *state;
 	int i;
 
-	cpuidle_register_driver(&cpuidle_driver);
 
 	for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
-		dev->states[i].name[0] = '\0';
-		dev->states[i].desc[0] = '\0';
+		drv->states[i].name[0] = '\0';
+		drv->states[i].desc[0] = '\0';
 	}
 
 	i = CPUIDLE_DRIVER_STATE_START;
 
-	state = &dev->states[i++];
+	state = &drv->states[i++];
 	snprintf(state->name, CPUIDLE_NAME_LEN, "C1");
 	strncpy(state->desc, "SuperH Sleep Mode", CPUIDLE_DESC_LEN);
 	state->exit_latency = 1;
@@ -86,10 +87,10 @@ void sh_mobile_setup_cpuidle(void)
 	state->flags |= CPUIDLE_FLAG_TIME_VALID;
 	state->enter = cpuidle_sleep_enter;
 
-	dev->safe_state_index = i-1;
+	drv->safe_state_index = i-1;
 
 	if (sh_mobile_sleep_supported & SUSP_SH_SF) {
-		state = &dev->states[i++];
+		state = &drv->states[i++];
 		snprintf(state->name, CPUIDLE_NAME_LEN, "C2");
 		strncpy(state->desc, "SuperH Sleep Mode [SF]",
 			CPUIDLE_DESC_LEN);
@@ -102,7 +103,7 @@ void sh_mobile_setup_cpuidle(void)
 	}
 
 	if (sh_mobile_sleep_supported & SUSP_SH_STANDBY) {
-		state = &dev->states[i++];
+		state = &drv->states[i++];
 		snprintf(state->name, CPUIDLE_NAME_LEN, "C3");
 		strncpy(state->desc, "SuperH Mobile Standby Mode [SF]",
 			CPUIDLE_DESC_LEN);
@@ -114,7 +115,10 @@ void sh_mobile_setup_cpuidle(void)
 		state->enter = cpuidle_sleep_enter;
 	}
 
+	drv->state_count = i;
 	dev->state_count = i;
 
+	cpuidle_register_driver(&cpuidle_driver);
+
 	cpuidle_register_device(dev);
 }
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index a4e0f1b..9d7bc9f 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -426,7 +426,7 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
 
 	if (action = CPU_ONLINE && pr) {
 		acpi_processor_ppc_has_changed(pr, 0);
-		acpi_processor_cst_has_changed(pr);
+		acpi_processor_hotplug(pr);
 		acpi_processor_reevaluate_tstate(pr, action);
 		acpi_processor_tstate_has_changed(pr);
 	}
@@ -503,8 +503,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
 	acpi_processor_get_throttling_info(pr);
 	acpi_processor_get_limit_info(pr);
 
-
-	if (cpuidle_get_driver() = &acpi_idle_driver)
+	if (!cpuidle_get_driver() || cpuidle_get_driver() = &acpi_idle_driver)
 		acpi_processor_power_init(pr, device);
 
 	pr->cdev = thermal_cooling_device_register("Processor", device,
@@ -800,17 +799,9 @@ static int __init acpi_processor_init(void)
 
 	memset(&errata, 0, sizeof(errata));
 
-	if (!cpuidle_register_driver(&acpi_idle_driver)) {
-		printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
-			acpi_idle_driver.name);
-	} else {
-		printk(KERN_DEBUG "ACPI: acpi_idle yielding to %s\n",
-			cpuidle_get_driver()->name);
-	}
-
 	result = acpi_bus_register_driver(&acpi_processor_driver);
 	if (result < 0)
-		goto out_cpuidle;
+		return result;
 
 	acpi_processor_install_hotplug_notify();
 
@@ -821,11 +812,6 @@ static int __init acpi_processor_init(void)
 	acpi_processor_throttling_init();
 
 	return 0;
-
-out_cpuidle:
-	cpuidle_unregister_driver(&acpi_idle_driver);
-
-	return result;
 }
 
 static void __exit acpi_processor_exit(void)
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index b98c752..24fe3af 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -741,11 +741,13 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
 /**
  * acpi_idle_enter_c1 - enters an ACPI C1 state-type
  * @dev: the target CPU
+ * @drv: cpuidle driver containing cpuidle state info
  * @index: index of target state
  *
  * This is equivalent to the HALT instruction.
  */
-static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index)
+static int acpi_idle_enter_c1(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int index)
 {
 	ktime_t  kt1, kt2;
 	s64 idle_time;
@@ -787,9 +789,11 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index)
 /**
  * acpi_idle_enter_simple - enters an ACPI state without BM handling
  * @dev: the target CPU
+ * @drv: cpuidle driver with cpuidle state information
  * @index: the index of suggested state
  */
-static int acpi_idle_enter_simple(struct cpuidle_device *dev, int index)
+static int acpi_idle_enter_simple(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int index)
 {
 	struct acpi_processor *pr;
 	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
@@ -869,11 +873,13 @@ static DEFINE_SPINLOCK(c3_lock);
 /**
  * acpi_idle_enter_bm - enters C3 with proper BM handling
  * @dev: the target CPU
+ * @drv: cpuidle driver containing state data
  * @index: the index of suggested state
  *
  * If BM is detected, the deepest non-C3 idle state is entered instead.
  */
-static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index)
+static int acpi_idle_enter_bm(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int index)
 {
 	struct acpi_processor *pr;
 	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
@@ -896,9 +902,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index)
 	}
 
 	if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
-		if (dev->safe_state_index >= 0) {
-			return dev->states[dev->safe_state_index].enter(dev,
-						dev->safe_state_index);
+		if (drv->safe_state_index >= 0) {
+			return drv->states[drv->safe_state_index].enter(dev,
+						drv, drv->safe_state_index);
 		} else {
 			local_irq_disable();
 			acpi_safe_halt();
@@ -993,14 +999,15 @@ struct cpuidle_driver acpi_idle_driver = {
 };
 
 /**
- * acpi_processor_setup_cpuidle - prepares and configures CPUIDLE
+ * acpi_processor_setup_cpuidle_cx - prepares and configures CPUIDLE
+ * device i.e. per-cpu data
+ *
  * @pr: the ACPI processor
  */
-static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
+static int acpi_processor_setup_cpuidle_cx(struct acpi_processor *pr)
 {
 	int i, count = CPUIDLE_DRIVER_STATE_START;
 	struct acpi_processor_cx *cx;
-	struct cpuidle_state *state;
 	struct cpuidle_state_usage *state_usage;
 	struct cpuidle_device *dev = &pr->power.dev;
 
@@ -1012,18 +1019,12 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 	}
 
 	dev->cpu = pr->id;
-	dev->safe_state_index = -1;
-	for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
-		dev->states[i].name[0] = '\0';
-		dev->states[i].desc[0] = '\0';
-	}
 
 	if (max_cstate = 0)
 		max_cstate = 1;
 
 	for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
 		cx = &pr->power.states[i];
-		state = &dev->states[count];
 		state_usage = &dev->states_usage[count];
 
 		if (!cx->valid)
@@ -1035,8 +1036,64 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 		    !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
 			continue;
 #endif
+
 		cpuidle_set_statedata(state_usage, cx);
 
+		count++;
+		if (count = CPUIDLE_STATE_MAX)
+			break;
+	}
+
+	dev->state_count = count;
+
+	if (!count)
+		return -EINVAL;
+
+	return 0;
+}
+
+/**
+ * acpi_processor_setup_cpuidle states- prepares and configures cpuidle
+ * global state data i.e. idle routines
+ *
+ * @pr: the ACPI processor
+ */
+static int acpi_processor_setup_cpuidle_states(struct acpi_processor *pr)
+{
+	int i, count = CPUIDLE_DRIVER_STATE_START;
+	struct acpi_processor_cx *cx;
+	struct cpuidle_state *state;
+	struct cpuidle_driver *drv = &acpi_idle_driver;
+
+	if (!pr->flags.power_setup_done)
+		return -EINVAL;
+
+	if (pr->flags.power = 0)
+		return -EINVAL;
+
+	drv->safe_state_index = -1;
+	for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
+		drv->states[i].name[0] = '\0';
+		drv->states[i].desc[0] = '\0';
+	}
+
+	if (max_cstate = 0)
+		max_cstate = 1;
+
+	for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
+		cx = &pr->power.states[i];
+
+		if (!cx->valid)
+			continue;
+
+#ifdef CONFIG_HOTPLUG_CPU
+		if ((cx->type != ACPI_STATE_C1) && (num_online_cpus() > 1) &&
+		    !pr->flags.has_cst &&
+		    !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
+			continue;
+#endif
+
+		state = &drv->states[count];
 		snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
 		strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
 		state->exit_latency = cx->latency;
@@ -1049,13 +1106,13 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 				state->flags |= CPUIDLE_FLAG_TIME_VALID;
 
 			state->enter = acpi_idle_enter_c1;
-			dev->safe_state_index = count;
+			drv->safe_state_index = count;
 			break;
 
 			case ACPI_STATE_C2:
 			state->flags |= CPUIDLE_FLAG_TIME_VALID;
 			state->enter = acpi_idle_enter_simple;
-			dev->safe_state_index = count;
+			drv->safe_state_index = count;
 			break;
 
 			case ACPI_STATE_C3:
@@ -1071,7 +1128,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 			break;
 	}
 
-	dev->state_count = count;
+	drv->state_count = count;
 
 	if (!count)
 		return -EINVAL;
@@ -1079,7 +1136,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 	return 0;
 }
 
-int acpi_processor_cst_has_changed(struct acpi_processor *pr)
+int acpi_processor_hotplug(struct acpi_processor *pr)
 {
 	int ret = 0;
 
@@ -1100,7 +1157,7 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
 	cpuidle_disable_device(&pr->power.dev);
 	acpi_processor_get_power_info(pr);
 	if (pr->flags.power) {
-		acpi_processor_setup_cpuidle(pr);
+		acpi_processor_setup_cpuidle_cx(pr);
 		ret = cpuidle_enable_device(&pr->power.dev);
 	}
 	cpuidle_resume_and_unlock();
@@ -1108,10 +1165,72 @@ int acpi_processor_cst_has_changed(struct acpi_processor *pr)
 	return ret;
 }
 
+int acpi_processor_cst_has_changed(struct acpi_processor *pr)
+{
+	int cpu;
+	struct acpi_processor *_pr;
+
+	if (disabled_by_idle_boot_param())
+		return 0;
+
+	if (!pr)
+		return -EINVAL;
+
+	if (nocst)
+		return -ENODEV;
+
+	if (!pr->flags.power_setup_done)
+		return -ENODEV;
+
+	/*
+	 * FIXME:  Design the ACPI notification to make it once per
+	 * system instead of once per-cpu.  This condition is a hack
+	 * to make the code that updates C-States be called once.
+	 */
+
+	if (smp_processor_id() = 0 &&
+			cpuidle_get_driver() = &acpi_idle_driver) {
+
+		cpuidle_pause_and_lock();
+		/* Protect against cpu-hotplug */
+		get_online_cpus();
+
+		/* Disable all cpuidle devices */
+		for_each_online_cpu(cpu) {
+			_pr = per_cpu(processors, cpu);
+			if (!_pr || !_pr->flags.power_setup_done)
+				continue;
+			cpuidle_disable_device(&_pr->power.dev);
+		}
+
+		/* Populate Updated C-state information */
+		acpi_processor_setup_cpuidle_states(pr);
+
+		/* Enable all cpuidle devices */
+		for_each_online_cpu(cpu) {
+			_pr = per_cpu(processors, cpu);
+			if (!_pr || !_pr->flags.power_setup_done)
+				continue;
+			acpi_processor_get_power_info(_pr);
+			if (_pr->flags.power) {
+				acpi_processor_setup_cpuidle_cx(_pr);
+				cpuidle_enable_device(&_pr->power.dev);
+			}
+		}
+		put_online_cpus();
+		cpuidle_resume_and_unlock();
+	}
+
+	return 0;
+}
+
+static int acpi_processor_registered;
+
 int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
 			      struct acpi_device *device)
 {
 	acpi_status status = 0;
+	int retval;
 	static int first_run;
 
 	if (disabled_by_idle_boot_param())
@@ -1148,9 +1267,26 @@ int __cpuinit acpi_processor_power_init(struct acpi_processor *pr,
 	 * platforms that only support C1.
 	 */
 	if (pr->flags.power) {
-		acpi_processor_setup_cpuidle(pr);
-		if (cpuidle_register_device(&pr->power.dev))
-			return -EIO;
+		/* Register acpi_idle_driver if not already registered */
+		if (!acpi_processor_registered) {
+			acpi_processor_setup_cpuidle_states(pr);
+			retval = cpuidle_register_driver(&acpi_idle_driver);
+			if (retval)
+				return retval;
+			printk(KERN_DEBUG "ACPI: %s registered with cpuidle\n",
+					acpi_idle_driver.name);
+		}
+		/* Register per-cpu cpuidle_device. Cpuidle driver
+		 * must already be registered before registering device
+		 */
+		acpi_processor_setup_cpuidle_cx(pr);
+		retval = cpuidle_register_device(&pr->power.dev);
+		if (retval) {
+			if (acpi_processor_registered = 0)
+				cpuidle_unregister_driver(&acpi_idle_driver);
+			return retval;
+		}
+		acpi_processor_registered++;
 	}
 	return 0;
 }
@@ -1161,8 +1297,13 @@ int acpi_processor_power_exit(struct acpi_processor *pr,
 	if (disabled_by_idle_boot_param())
 		return 0;
 
-	cpuidle_unregister_device(&pr->power.dev);
-	pr->flags.power_setup_done = 0;
+	if (pr->flags.power) {
+		cpuidle_unregister_device(&pr->power.dev);
+		acpi_processor_registered--;
+		if (acpi_processor_registered = 0)
+			cpuidle_unregister_driver(&acpi_idle_driver);
+	}
 
+	pr->flags.power_setup_done = 0;
 	return 0;
 }
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 7127e92..7a57b11 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -61,6 +61,7 @@ static int __cpuidle_register_device(struct cpuidle_device *dev);
 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;
 
@@ -84,18 +85,18 @@ int cpuidle_idle_call(void)
 #endif
 
 	/* ask the governor for the next state */
-	next_state = cpuidle_curr_governor->select(dev);
+	next_state = cpuidle_curr_governor->select(drv, dev);
 	if (need_resched()) {
 		local_irq_enable();
 		return 0;
 	}
 
-	target_state = &dev->states[next_state];
+	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, next_state);
+	entered_state = target_state->enter(dev, drv, next_state);
 
 	trace_power_end(dev->cpu);
 	trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu);
@@ -163,7 +164,8 @@ 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, int index)
+static int poll_idle(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int index)
 {
 	ktime_t	t1, t2;
 	s64 diff;
@@ -183,12 +185,9 @@ static int poll_idle(struct cpuidle_device *dev, int index)
 	return index;
 }
 
-static void poll_idle_init(struct cpuidle_device *dev)
+static void poll_idle_init(struct cpuidle_driver *drv)
 {
-	struct cpuidle_state *state = &dev->states[0];
-	struct cpuidle_state_usage *state_usage = &dev->states_usage[0];
-
-	cpuidle_set_statedata(state_usage, NULL);
+	struct cpuidle_state *state = &drv->states[0];
 
 	snprintf(state->name, CPUIDLE_NAME_LEN, "POLL");
 	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
@@ -199,7 +198,7 @@ static void poll_idle_init(struct cpuidle_device *dev)
 	state->enter = poll_idle;
 }
 #else
-static void poll_idle_init(struct cpuidle_device *dev) {}
+static void poll_idle_init(struct cpuidle_driver *drv) {}
 #endif /* CONFIG_ARCH_HAS_CPU_RELAX */
 
 /**
@@ -226,13 +225,13 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
 			return ret;
 	}
 
-	poll_idle_init(dev);
+	poll_idle_init(cpuidle_get_driver());
 
 	if ((ret = cpuidle_add_state_sysfs(dev)))
 		return ret;
 
 	if (cpuidle_curr_governor->enable &&
-	    (ret = cpuidle_curr_governor->enable(dev)))
+	    (ret = cpuidle_curr_governor->enable(cpuidle_get_driver(), dev)))
 		goto fail_sysfs;
 
 	for (i = 0; i < dev->state_count; i++) {
@@ -273,7 +272,7 @@ void cpuidle_disable_device(struct cpuidle_device *dev)
 	dev->enabled = 0;
 
 	if (cpuidle_curr_governor->disable)
-		cpuidle_curr_governor->disable(dev);
+		cpuidle_curr_governor->disable(cpuidle_get_driver(), dev);
 
 	cpuidle_remove_state_sysfs(dev);
 	enabled_devices--;
@@ -301,26 +300,6 @@ static int __cpuidle_register_device(struct cpuidle_device *dev)
 
 	init_completion(&dev->kobj_unregister);
 
-	/*
-	 * cpuidle driver should set the dev->power_specified bit
-	 * before registering the device if the driver provides
-	 * power_usage numbers.
-	 *
-	 * For those devices whose ->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 (!dev->power_specified) {
-		int i;
-		for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++)
-			dev->states[i].power_usage = -1 - i;
-	}
-
 	per_cpu(cpuidle_devices, dev->cpu) = dev;
 	list_add(&dev->device_list, &cpuidle_detected_devices);
 	if ((ret = cpuidle_add_sysfs(sys_dev))) {
diff --git a/drivers/cpuidle/driver.c b/drivers/cpuidle/driver.c
index 3f7e3ce..284d7af 100644
--- a/drivers/cpuidle/driver.c
+++ b/drivers/cpuidle/driver.c
@@ -17,6 +17,30 @@
 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
@@ -34,6 +58,7 @@ 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 6a686a7..ef6b9e4 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -60,9 +60,11 @@ static inline void ladder_do_selection(struct ladder_device *ldev,
 
 /**
  * ladder_select_state - selects the next state to enter
+ * @drv: cpuidle driver
  * @dev: the CPU
  */
-static int ladder_select_state(struct cpuidle_device *dev)
+static int ladder_select_state(struct cpuidle_driver *drv,
+				struct cpuidle_device *dev)
 {
 	struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
 	struct ladder_device_state *last_state;
@@ -77,15 +79,17 @@ static int ladder_select_state(struct cpuidle_device *dev)
 
 	last_state = &ldev->states[last_idx];
 
-	if (dev->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID)
-		last_residency = cpuidle_get_last_residency(dev) - dev->states[last_idx].exit_latency;
+	if (drv->states[last_idx].flags & CPUIDLE_FLAG_TIME_VALID) {
+		last_residency = cpuidle_get_last_residency(dev) - \
+					 drv->states[last_idx].exit_latency;
+	}
 	else
 		last_residency = last_state->threshold.promotion_time + 1;
 
 	/* consider promotion */
-	if (last_idx < dev->state_count - 1 &&
+	if (last_idx < drv->state_count - 1 &&
 	    last_residency > last_state->threshold.promotion_time &&
-	    dev->states[last_idx + 1].exit_latency <= latency_req) {
+	    drv->states[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) {
@@ -96,11 +100,11 @@ static int ladder_select_state(struct cpuidle_device *dev)
 
 	/* consider demotion */
 	if (last_idx > CPUIDLE_DRIVER_STATE_START &&
-	    dev->states[last_idx].exit_latency > latency_req) {
+	    drv->states[last_idx].exit_latency > latency_req) {
 		int i;
 
 		for (i = last_idx - 1; i > CPUIDLE_DRIVER_STATE_START; i--) {
-			if (dev->states[i].exit_latency <= latency_req)
+			if (drv->states[i].exit_latency <= latency_req)
 				break;
 		}
 		ladder_do_selection(ldev, last_idx, i);
@@ -123,9 +127,11 @@ static int ladder_select_state(struct cpuidle_device *dev)
 
 /**
  * ladder_enable_device - setup for the governor
+ * @drv: cpuidle driver
  * @dev: the CPU
  */
-static int ladder_enable_device(struct cpuidle_device *dev)
+static int ladder_enable_device(struct cpuidle_driver *drv,
+				struct cpuidle_device *dev)
 {
 	int i;
 	struct ladder_device *ldev = &per_cpu(ladder_devices, dev->cpu);
@@ -134,8 +140,8 @@ static int ladder_enable_device(struct cpuidle_device *dev)
 
 	ldev->last_state_idx = CPUIDLE_DRIVER_STATE_START;
 
-	for (i = 0; i < dev->state_count; i++) {
-		state = &dev->states[i];
+	for (i = 0; i < drv->state_count; i++) {
+		state = &drv->states[i];
 		lstate = &ldev->states[i];
 
 		lstate->stats.promotion_count = 0;
@@ -144,7 +150,7 @@ static int ladder_enable_device(struct cpuidle_device *dev)
 		lstate->threshold.promotion_count = PROMOTION_COUNT;
 		lstate->threshold.demotion_count = DEMOTION_COUNT;
 
-		if (i < dev->state_count - 1)
+		if (i < drv->state_count - 1)
 			lstate->threshold.promotion_time = state->exit_latency;
 		if (i > 0)
 			lstate->threshold.demotion_time = state->exit_latency;
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index af724e8..bcbe881 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -182,7 +182,7 @@ static inline int performance_multiplier(void)
 
 static DEFINE_PER_CPU(struct menu_device, menu_devices);
 
-static void menu_update(struct cpuidle_device *dev);
+static void menu_update(struct cpuidle_driver *drv, struct cpuidle_device *dev);
 
 /* This implements DIV_ROUND_CLOSEST but avoids 64 bit division */
 static u64 div_round64(u64 dividend, u32 divisor)
@@ -228,9 +228,10 @@ static void detect_repeating_patterns(struct menu_device *data)
 
 /**
  * menu_select - selects the next idle state to enter
+ * @drv: cpuidle driver containing state data
  * @dev: the CPU
  */
-static int menu_select(struct cpuidle_device *dev)
+static int menu_select(struct cpuidle_driver *drv, struct cpuidle_device *dev)
 {
 	struct menu_device *data = &__get_cpu_var(menu_devices);
 	int latency_req = pm_qos_request(PM_QOS_CPU_DMA_LATENCY);
@@ -240,7 +241,7 @@ static int menu_select(struct cpuidle_device *dev)
 	struct timespec t;
 
 	if (data->needs_update) {
-		menu_update(dev);
+		menu_update(drv, dev);
 		data->needs_update = 0;
 	}
 
@@ -285,8 +286,8 @@ static int menu_select(struct cpuidle_device *dev)
 	 * Find the idle state with the lowest power while satisfying
 	 * our constraints.
 	 */
-	for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) {
-		struct cpuidle_state *s = &dev->states[i];
+	for (i = CPUIDLE_DRIVER_STATE_START; i < drv->state_count; i++) {
+		struct cpuidle_state *s = &drv->states[i];
 
 		if (s->target_residency > data->predicted_us)
 			continue;
@@ -323,14 +324,15 @@ static void menu_reflect(struct cpuidle_device *dev, int index)
 
 /**
  * menu_update - attempts to guess what happened after entry
+ * @drv: cpuidle driver containing state data
  * @dev: the CPU
  */
-static void menu_update(struct cpuidle_device *dev)
+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 = &dev->states[last_idx];
+	struct cpuidle_state *target = &drv->states[last_idx];
 	unsigned int measured_us;
 	u64 new_factor;
 
@@ -384,9 +386,11 @@ static void menu_update(struct cpuidle_device *dev)
 
 /**
  * menu_enable_device - scans a CPU's states and does setup
+ * @drv: cpuidle driver
  * @dev: the CPU
  */
-static int menu_enable_device(struct cpuidle_device *dev)
+static int menu_enable_device(struct cpuidle_driver *drv,
+				struct cpuidle_device *dev)
 {
 	struct menu_device *data = &per_cpu(menu_devices, dev->cpu);
 
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index 8a1ace1..1e756e1 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -322,13 +322,14 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device)
 {
 	int i, ret = -ENOMEM;
 	struct cpuidle_state_kobj *kobj;
+	struct cpuidle_driver *drv = cpuidle_get_driver();
 
 	/* state statistics */
 	for (i = 0; i < device->state_count; i++) {
 		kobj = kzalloc(sizeof(struct cpuidle_state_kobj), GFP_KERNEL);
 		if (!kobj)
 			goto error_state;
-		kobj->state = &device->states[i];
+		kobj->state = &drv->states[i];
 		kobj->state_usage = &device->states_usage[i];
 		init_completion(&kobj->kobj_unregister);
 
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index 3aa8d4c..5be9d59 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -81,7 +81,8 @@ static unsigned int mwait_substates;
 static unsigned int lapic_timer_reliable_states = (1 << 1);	 /* Default to only C1 */
 
 static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
-static int intel_idle(struct cpuidle_device *dev, int index);
+static int intel_idle(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv, int index);
 
 static struct cpuidle_state *cpuidle_state_table;
 
@@ -227,13 +228,15 @@ static int get_driver_data(int cstate)
 /**
  * intel_idle
  * @dev: cpuidle_device
+ * @drv: cpuidle driver
  * @index: index of cpuidle state
  *
  */
-static int intel_idle(struct cpuidle_device *dev, int index)
+static int intel_idle(struct cpuidle_device *dev,
+		struct cpuidle_driver *drv, int index)
 {
 	unsigned long ecx = 1; /* break on interrupt flag */
-	struct cpuidle_state *state = &dev->states[index];
+	struct cpuidle_state *state = &drv->states[index];
 	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
 	unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage);
 	unsigned int cstate;
@@ -420,6 +423,60 @@ static void intel_idle_cpuidle_devices_uninit(void)
 	return;
 }
 /*
+ * intel_idle_cpuidle_driver_init()
+ * allocate, initialize cpuidle_states
+ */
+static int intel_idle_cpuidle_driver_init(void)
+{
+	int cstate;
+	struct cpuidle_driver *drv = &intel_idle_driver;
+
+	drv->state_count = 1;
+
+	for (cstate = 1; cstate < MWAIT_MAX_NUM_CSTATES; ++cstate) {
+		int num_substates;
+
+		if (cstate > max_cstate) {
+			printk(PREFIX "max_cstate %d reached\n",
+				max_cstate);
+			break;
+		}
+
+		/* does the state exist in CPUID.MWAIT? */
+		num_substates = (mwait_substates >> ((cstate) * 4))
+					& MWAIT_SUBSTATE_MASK;
+		if (num_substates = 0)
+			continue;
+		/* is the state not enabled? */
+		if (cpuidle_state_table[cstate].enter = NULL) {
+			/* does the driver not know about the state? */
+			if (*cpuidle_state_table[cstate].name = '\0')
+				pr_debug(PREFIX "unaware of model 0x%x"
+					" MWAIT %d please"
+					" contact lenb@kernel.org",
+				boot_cpu_data.x86_model, cstate);
+			continue;
+		}
+
+		if ((cstate > 2) &&
+			!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
+			mark_tsc_unstable("TSC halts in idle"
+					" states deeper than C2");
+
+		drv->states[drv->state_count] =	/* structure copy */
+			cpuidle_state_table[cstate];
+
+		drv->state_count += 1;
+	}
+
+	if (auto_demotion_disable_flags)
+		smp_call_function(auto_demotion_disable, NULL, 1);
+
+	return 0;
+}
+
+
+/*
  * intel_idle_cpuidle_devices_init()
  * allocate, initialize, register cpuidle_devices
  */
@@ -453,23 +510,9 @@ static int intel_idle_cpuidle_devices_init(void)
 				continue;
 			/* is the state not enabled? */
 			if (cpuidle_state_table[cstate].enter = NULL) {
-				/* does the driver not know about the state? */
-				if (*cpuidle_state_table[cstate].name = '\0')
-					pr_debug(PREFIX "unaware of model 0x%x"
-						" MWAIT %d please"
-						" contact lenb@kernel.org",
-					boot_cpu_data.x86_model, cstate);
 				continue;
 			}
 
-			if ((cstate > 2) &&
-				!boot_cpu_has(X86_FEATURE_NONSTOP_TSC))
-				mark_tsc_unstable("TSC halts in idle"
-					" states deeper than C2");
-
-			dev->states[dev->state_count] =	/* structure copy */
-				cpuidle_state_table[cstate];
-
 			dev->states_usage[dev->state_count].driver_data  				(void *)get_driver_data(cstate);
 
@@ -484,8 +527,6 @@ static int intel_idle_cpuidle_devices_init(void)
 			return -EIO;
 		}
 	}
-	if (auto_demotion_disable_flags)
-		smp_call_function(auto_demotion_disable, NULL, 1);
 
 	return 0;
 }
@@ -503,6 +544,7 @@ static int __init intel_idle_init(void)
 	if (retval)
 		return retval;
 
+	intel_idle_cpuidle_driver_init();
 	retval = cpuidle_register_driver(&intel_idle_driver);
 	if (retval) {
 		printk(KERN_DEBUG PREFIX "intel_idle yielding to %s",
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 67055f1..610f6fb 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -329,6 +329,7 @@ extern void acpi_processor_throttling_init(void);
 int acpi_processor_power_init(struct acpi_processor *pr,
 			      struct acpi_device *device);
 int acpi_processor_cst_has_changed(struct acpi_processor *pr);
+int acpi_processor_hotplug(struct acpi_processor *pr);
 int acpi_processor_power_exit(struct acpi_processor *pr,
 			      struct acpi_device *device);
 int acpi_processor_suspend(struct acpi_device * device, pm_message_t state);
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 0156540..c904188 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -22,6 +22,7 @@
 #define CPUIDLE_DESC_LEN	32
 
 struct cpuidle_device;
+struct cpuidle_driver;
 
 
 /****************************
@@ -45,6 +46,7 @@ struct cpuidle_state {
 	unsigned int	target_residency; /* in US */
 
 	int (*enter)	(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv,
 			int index);
 };
 
@@ -83,12 +85,10 @@ struct cpuidle_state_kobj {
 struct cpuidle_device {
 	unsigned int		registered:1;
 	unsigned int		enabled:1;
-	unsigned int		power_specified:1;
 	unsigned int		cpu;
 
 	int			last_residency;
 	int			state_count;
-	struct cpuidle_state	states[CPUIDLE_STATE_MAX];
 	struct cpuidle_state_usage	states_usage[CPUIDLE_STATE_MAX];
 	struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
 
@@ -96,7 +96,6 @@ struct cpuidle_device {
 	struct kobject		kobj;
 	struct completion	kobj_unregister;
 	void			*governor_data;
-	int			safe_state_index;
 };
 
 DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
@@ -120,6 +119,11 @@ static inline int cpuidle_get_last_residency(struct cpuidle_device *dev)
 struct cpuidle_driver {
 	char			name[CPUIDLE_NAME_LEN];
 	struct module 		*owner;
+
+	unsigned int		power_specified:1;
+	struct cpuidle_state	states[CPUIDLE_STATE_MAX];
+	int			state_count;
+	int			safe_state_index;
 };
 
 #ifdef CONFIG_CPU_IDLE
@@ -166,10 +170,13 @@ struct cpuidle_governor {
 	struct list_head 	governor_list;
 	unsigned int		rating;
 
-	int  (*enable)		(struct cpuidle_device *dev);
-	void (*disable)		(struct cpuidle_device *dev);
+	int  (*enable)		(struct cpuidle_driver *drv,
+					struct cpuidle_device *dev);
+	void (*disable)		(struct cpuidle_driver *drv,
+					struct cpuidle_device *dev);
 
-	int  (*select)		(struct cpuidle_device *dev);
+	int  (*select)		(struct cpuidle_driver *drv,
+					struct cpuidle_device *dev);
 	void (*reflect)		(struct cpuidle_device *dev, int index);
 
 	struct module 		*owner;


^ permalink raw reply related

* [PATCH V7 3/4] cpuidle: Split cpuidle_state structure and move
From: Deepthi Dharwar @ 2011-09-27 10:15 UTC (permalink / raw)
  To: khilman, venki, ak, len.brown, peterz, santosh.shilimkar, arjan,
	lenb
  Cc: linux-sh, linux-kernel, linux-acpi, linux-pm, linux-omap,
	linux-arm-kernel
In-Reply-To: <20110927100202.16043.58669.stgit@localhost6.localdomain6>

This is the first step towards global registration of cpuidle
states. The statistics used primarily by the governor are per-cpu
and have to be split from rest of the fields inside cpuidle_state,
which would be made global i.e. single copy. The driver_data field
is also per-cpu and moved.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-davinci/cpuidle.c   |    5 ++--
 arch/arm/mach-omap2/cpuidle34xx.c |   13 ++++++----
 drivers/acpi/processor_idle.c     |   25 ++++++++++----------
 drivers/cpuidle/cpuidle.c         |   11 +++++----
 drivers/cpuidle/sysfs.c           |   19 ++++++++++-----
 drivers/idle/intel_idle.c         |   46 +++++++++++++++++++++++++++----------
 include/linux/cpuidle.h           |   25 ++++++++++++--------
 7 files changed, 90 insertions(+), 54 deletions(-)

diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c
index ca8582a..f2d2f34 100644
--- a/arch/arm/mach-davinci/cpuidle.c
+++ b/arch/arm/mach-davinci/cpuidle.c
@@ -80,7 +80,8 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = {
 static int davinci_enter_idle(struct cpuidle_device *dev,
 						int index)
 {
-	struct davinci_ops *ops = cpuidle_get_statedata(&dev->states[index]);
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+	struct davinci_ops *ops = cpuidle_get_statedata(state_usage);
 	struct timeval before, after;
 	int idle_time;
 
@@ -142,7 +143,7 @@ static int __init davinci_cpuidle_probe(struct platform_device *pdev)
 	strcpy(device->states[1].desc, "WFI and DDR Self Refresh");
 	if (pdata->ddr2_pdown)
 		davinci_states[1].flags |= DAVINCI_CPUIDLE_FLAGS_DDR2_PWDN;
-	cpuidle_set_statedata(&device->states[1], &davinci_states[1]);
+	cpuidle_set_statedata(&device->states_usage[1], &davinci_states[1]);
 
 	device->state_count = DAVINCI_CPUIDLE_MAX_STATES;
 
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 58425c7..d3fce7b 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -97,7 +97,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
 				int index)
 {
 	struct omap3_idle_statedata *cx -			cpuidle_get_statedata(&dev->states[index]);
+			cpuidle_get_statedata(&dev->states_usage[index]);
 	struct timespec ts_preidle, ts_postidle, ts_idle;
 	u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
 	int idle_time;
@@ -160,8 +160,9 @@ return_sleep_time:
 static int next_valid_state(struct cpuidle_device *dev,
 				int index)
 {
+	struct cpuidle_state_usage *curr_usage = &dev->states_usage[index];
 	struct cpuidle_state *curr = &dev->states[index];
-	struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr);
+	struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr_usage);
 	u32 mpu_deepest_state = PWRDM_POWER_RET;
 	u32 core_deepest_state = PWRDM_POWER_RET;
 	int next_index = -1;
@@ -202,7 +203,7 @@ static int next_valid_state(struct cpuidle_device *dev,
 		 */
 		idx--;
 		for (; idx >= 0; idx--) {
-			cx = cpuidle_get_statedata(&dev->states[idx]);
+			cx = cpuidle_get_statedata(&dev->states_usage[idx]);
 			if ((cx->valid) &&
 			    (cx->mpu_state >= mpu_deepest_state) &&
 			    (cx->core_state >= core_deepest_state)) {
@@ -231,7 +232,6 @@ static int next_valid_state(struct cpuidle_device *dev,
 static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 			       int index)
 {
-	struct cpuidle_state *state = &dev->states[index];
 	int new_state_idx;
 	u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
 	struct omap3_idle_statedata *cx;
@@ -264,7 +264,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	 * Prevent PER off if CORE is not in retention or off as this
 	 * would disable PER wakeups completely.
 	 */
-	cx = cpuidle_get_statedata(state);
+	cx = cpuidle_get_statedata(&dev->states_usage[index]);
 	core_next_state = cx->core_state;
 	per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
 	if ((per_next_state = PWRDM_POWER_OFF) &&
@@ -318,6 +318,7 @@ static inline struct omap3_idle_statedata *_fill_cstate(
 {
 	struct omap3_idle_statedata *cx = &omap3_idle_data[idx];
 	struct cpuidle_state *state = &dev->states[idx];
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[idx];
 
 	state->exit_latency	= cpuidle_params_table[idx].exit_latency;
 	state->target_residency	= cpuidle_params_table[idx].target_residency;
@@ -326,7 +327,7 @@ static inline struct omap3_idle_statedata *_fill_cstate(
 	cx->valid		= cpuidle_params_table[idx].valid;
 	sprintf(state->name, "C%d", idx + 1);
 	strncpy(state->desc, descr, CPUIDLE_DESC_LEN);
-	cpuidle_set_statedata(state, cx);
+	cpuidle_set_statedata(state_usage, cx);
 
 	return cx;
 }
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 9cd08ce..b98c752 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -745,14 +745,13 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
  *
  * This is equivalent to the HALT instruction.
  */
-static int acpi_idle_enter_c1(struct cpuidle_device *dev,
-				int index)
+static int acpi_idle_enter_c1(struct cpuidle_device *dev, int index)
 {
 	ktime_t  kt1, kt2;
 	s64 idle_time;
 	struct acpi_processor *pr;
-	struct cpuidle_state *state = &dev->states[index];
-	struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+	struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
 
 	pr = __this_cpu_read(processors);
 	dev->last_residency = 0;
@@ -790,12 +789,11 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
  * @dev: the target CPU
  * @index: the index of suggested state
  */
-static int acpi_idle_enter_simple(struct cpuidle_device *dev,
-				int index)
+static int acpi_idle_enter_simple(struct cpuidle_device *dev, int index)
 {
 	struct acpi_processor *pr;
-	struct cpuidle_state *state = &dev->states[index];
-	struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+	struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
 	ktime_t  kt1, kt2;
 	s64 idle_time_ns;
 	s64 idle_time;
@@ -875,12 +873,11 @@ static DEFINE_SPINLOCK(c3_lock);
  *
  * If BM is detected, the deepest non-C3 idle state is entered instead.
  */
-static int acpi_idle_enter_bm(struct cpuidle_device *dev,
-				int index)
+static int acpi_idle_enter_bm(struct cpuidle_device *dev, int index)
 {
 	struct acpi_processor *pr;
-	struct cpuidle_state *state = &dev->states[index];
-	struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+	struct acpi_processor_cx *cx = cpuidle_get_statedata(state_usage);
 	ktime_t  kt1, kt2;
 	s64 idle_time_ns;
 	s64 idle_time;
@@ -1004,6 +1001,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 	int i, count = CPUIDLE_DRIVER_STATE_START;
 	struct acpi_processor_cx *cx;
 	struct cpuidle_state *state;
+	struct cpuidle_state_usage *state_usage;
 	struct cpuidle_device *dev = &pr->power.dev;
 
 	if (!pr->flags.power_setup_done)
@@ -1026,6 +1024,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 	for (i = 1; i < ACPI_PROCESSOR_MAX_POWER && i <= max_cstate; i++) {
 		cx = &pr->power.states[i];
 		state = &dev->states[count];
+		state_usage = &dev->states_usage[count];
 
 		if (!cx->valid)
 			continue;
@@ -1036,7 +1035,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 		    !(acpi_gbl_FADT.flags & ACPI_FADT_C2_MP_SUPPORTED))
 			continue;
 #endif
-		cpuidle_set_statedata(state, cx);
+		cpuidle_set_statedata(state_usage, cx);
 
 		snprintf(state->name, CPUIDLE_NAME_LEN, "C%d", i);
 		strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index f66bcf9..7127e92 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -105,9 +105,9 @@ int cpuidle_idle_call(void)
 		/* This can be moved to within driver enter routine
 		 * but that results in multiple copies of same code.
 		 */
-		dev->states[entered_state].time ++		dev->states_usage[entered_state].time + 				(unsigned long long)dev->last_residency;
-		dev->states[entered_state].usage++;
+		dev->states_usage[entered_state].usage++;
 	}
 
 	/* give the governor an opportunity to reflect on the outcome */
@@ -186,8 +186,9 @@ static int poll_idle(struct cpuidle_device *dev, int index)
 static void poll_idle_init(struct cpuidle_device *dev)
 {
 	struct cpuidle_state *state = &dev->states[0];
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[0];
 
-	cpuidle_set_statedata(state, NULL);
+	cpuidle_set_statedata(state_usage, NULL);
 
 	snprintf(state->name, CPUIDLE_NAME_LEN, "POLL");
 	snprintf(state->desc, CPUIDLE_DESC_LEN, "CPUIDLE CORE POLL IDLE");
@@ -235,8 +236,8 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
 		goto fail_sysfs;
 
 	for (i = 0; i < dev->state_count; i++) {
-		dev->states[i].usage = 0;
-		dev->states[i].time = 0;
+		dev->states_usage[i].usage = 0;
+		dev->states_usage[i].time = 0;
 	}
 	dev->last_residency = 0;
 
diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c
index be7917ec..8a1ace1 100644
--- a/drivers/cpuidle/sysfs.c
+++ b/drivers/cpuidle/sysfs.c
@@ -216,7 +216,8 @@ static struct kobj_type ktype_cpuidle = {
 
 struct cpuidle_state_attr {
 	struct attribute attr;
-	ssize_t (*show)(struct cpuidle_state *, char *);
+	ssize_t (*show)(struct cpuidle_state *, \
+					struct cpuidle_state_usage *, char *);
 	ssize_t (*store)(struct cpuidle_state *, const char *, size_t);
 };
 
@@ -224,19 +225,22 @@ struct cpuidle_state_attr {
 static struct cpuidle_state_attr attr_##_name = __ATTR(_name, 0444, show, NULL)
 
 #define define_show_state_function(_name) \
-static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
+static ssize_t show_state_##_name(struct cpuidle_state *state, \
+			 struct cpuidle_state_usage *state_usage, char *buf) \
 { \
 	return sprintf(buf, "%u\n", state->_name);\
 }
 
 #define define_show_state_ull_function(_name) \
-static ssize_t show_state_##_name(struct cpuidle_state *state, char *buf) \
+static ssize_t show_state_##_name(struct cpuidle_state *state, \
+			struct cpuidle_state_usage *state_usage, char *buf) \
 { \
-	return sprintf(buf, "%llu\n", state->_name);\
+	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, char *buf) \
+static ssize_t show_state_##_name(struct cpuidle_state *state, \
+			struct cpuidle_state_usage *state_usage, char *buf) \
 { \
 	if (state->_name[0] = '\0')\
 		return sprintf(buf, "<null>\n");\
@@ -269,16 +273,18 @@ 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 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)
 {
 	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_attr * cattr = attr_to_stateattr(attr);
 
 	if (cattr->show)
-		ret = cattr->show(state, buf);
+		ret = cattr->show(state, state_usage, buf);
 
 	return ret;
 }
@@ -323,6 +329,7 @@ int cpuidle_add_state_sysfs(struct cpuidle_device *device)
 		if (!kobj)
 			goto error_state;
 		kobj->state = &device->states[i];
+		kobj->state_usage = &device->states_usage[i];
 		init_completion(&kobj->kobj_unregister);
 
 		ret = kobject_init_and_add(&kobj->kobj, &ktype_state_cpuidle, &device->kobj,
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index a1c888d..3aa8d4c 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -109,7 +109,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C1 */
 		.name = "C1-NHM",
 		.desc = "MWAIT 0x00",
-		.driver_data = (void *) 0x00,
 		.flags = CPUIDLE_FLAG_TIME_VALID,
 		.exit_latency = 3,
 		.target_residency = 6,
@@ -117,7 +116,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C2 */
 		.name = "C3-NHM",
 		.desc = "MWAIT 0x10",
-		.driver_data = (void *) 0x10,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 20,
 		.target_residency = 80,
@@ -125,7 +123,6 @@ static struct cpuidle_state nehalem_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C3 */
 		.name = "C6-NHM",
 		.desc = "MWAIT 0x20",
-		.driver_data = (void *) 0x20,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 200,
 		.target_residency = 800,
@@ -137,7 +134,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C1 */
 		.name = "C1-SNB",
 		.desc = "MWAIT 0x00",
-		.driver_data = (void *) 0x00,
 		.flags = CPUIDLE_FLAG_TIME_VALID,
 		.exit_latency = 1,
 		.target_residency = 1,
@@ -145,7 +141,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C2 */
 		.name = "C3-SNB",
 		.desc = "MWAIT 0x10",
-		.driver_data = (void *) 0x10,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 80,
 		.target_residency = 211,
@@ -153,7 +148,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C3 */
 		.name = "C6-SNB",
 		.desc = "MWAIT 0x20",
-		.driver_data = (void *) 0x20,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 104,
 		.target_residency = 345,
@@ -161,7 +155,6 @@ static struct cpuidle_state snb_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C4 */
 		.name = "C7-SNB",
 		.desc = "MWAIT 0x30",
-		.driver_data = (void *) 0x30,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 109,
 		.target_residency = 345,
@@ -173,7 +166,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C1 */
 		.name = "C1-ATM",
 		.desc = "MWAIT 0x00",
-		.driver_data = (void *) 0x00,
 		.flags = CPUIDLE_FLAG_TIME_VALID,
 		.exit_latency = 1,
 		.target_residency = 4,
@@ -181,7 +173,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C2 */
 		.name = "C2-ATM",
 		.desc = "MWAIT 0x10",
-		.driver_data = (void *) 0x10,
 		.flags = CPUIDLE_FLAG_TIME_VALID,
 		.exit_latency = 20,
 		.target_residency = 80,
@@ -190,7 +181,6 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C4 */
 		.name = "C4-ATM",
 		.desc = "MWAIT 0x30",
-		.driver_data = (void *) 0x30,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 100,
 		.target_residency = 400,
@@ -199,13 +189,41 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
 	{ /* MWAIT C6 */
 		.name = "C6-ATM",
 		.desc = "MWAIT 0x52",
-		.driver_data = (void *) 0x52,
 		.flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED,
 		.exit_latency = 140,
 		.target_residency = 560,
 		.enter = &intel_idle },
 };
 
+static int get_driver_data(int cstate)
+{
+	int driver_data;
+	switch (cstate) {
+
+	case 1:	/* MWAIT C1 */
+		driver_data = 0x00;
+		break;
+	case 2:	/* MWAIT C2 */
+		driver_data = 0x10;
+		break;
+	case 3:	/* MWAIT C3 */
+		driver_data = 0x20;
+		break;
+	case 4:	/* MWAIT C4 */
+		driver_data = 0x30;
+		break;
+	case 5:	/* MWAIT C5 */
+		driver_data = 0x40;
+		break;
+	case 6:	/* MWAIT C6 */
+		driver_data = 0x52;
+		break;
+	default:
+		driver_data = 0x00;
+	}
+	return driver_data;
+}
+
 /**
  * intel_idle
  * @dev: cpuidle_device
@@ -216,7 +234,8 @@ static int intel_idle(struct cpuidle_device *dev, int index)
 {
 	unsigned long ecx = 1; /* break on interrupt flag */
 	struct cpuidle_state *state = &dev->states[index];
-	unsigned long eax = (unsigned long)cpuidle_get_statedata(state);
+	struct cpuidle_state_usage *state_usage = &dev->states_usage[index];
+	unsigned long eax = (unsigned long)cpuidle_get_statedata(state_usage);
 	unsigned int cstate;
 	ktime_t kt_before, kt_after;
 	s64 usec_delta;
@@ -451,6 +470,9 @@ static int intel_idle_cpuidle_devices_init(void)
 			dev->states[dev->state_count] =	/* structure copy */
 				cpuidle_state_table[cstate];
 
+			dev->states_usage[dev->state_count].driver_data +				(void *)get_driver_data(cstate);
+
 			dev->state_count += 1;
 		}
 
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index c6d85cf..0156540 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -28,19 +28,22 @@ struct cpuidle_device;
  * CPUIDLE DEVICE INTERFACE *
  ****************************/
 
+struct cpuidle_state_usage {
+	void		*driver_data;
+
+	unsigned long long	usage;
+	unsigned long long	time; /* in US */
+};
+
 struct cpuidle_state {
 	char		name[CPUIDLE_NAME_LEN];
 	char		desc[CPUIDLE_DESC_LEN];
-	void		*driver_data;
 
 	unsigned int	flags;
 	unsigned int	exit_latency; /* in US */
 	unsigned int	power_usage; /* in mW */
 	unsigned int	target_residency; /* in US */
 
-	unsigned long long	usage;
-	unsigned long long	time; /* in US */
-
 	int (*enter)	(struct cpuidle_device *dev,
 			int index);
 };
@@ -52,26 +55,27 @@ struct cpuidle_state {
 
 /**
  * cpuidle_get_statedata - retrieves private driver state data
- * @state: the state
+ * @st_usage: the state usage statistics
  */
-static inline void * cpuidle_get_statedata(struct cpuidle_state *state)
+static inline void *cpuidle_get_statedata(struct cpuidle_state_usage *st_usage)
 {
-	return state->driver_data;
+	return st_usage->driver_data;
 }
 
 /**
  * cpuidle_set_statedata - stores private driver state data
- * @state: the state
+ * @st_usage: the state usage statistics
  * @data: the private data
  */
 static inline void
-cpuidle_set_statedata(struct cpuidle_state *state, void *data)
+cpuidle_set_statedata(struct cpuidle_state_usage *st_usage, void *data)
 {
-	state->driver_data = data;
+	st_usage->driver_data = data;
 }
 
 struct cpuidle_state_kobj {
 	struct cpuidle_state *state;
+	struct cpuidle_state_usage *state_usage;
 	struct completion kobj_unregister;
 	struct kobject kobj;
 };
@@ -85,6 +89,7 @@ struct cpuidle_device {
 	int			last_residency;
 	int			state_count;
 	struct cpuidle_state	states[CPUIDLE_STATE_MAX];
+	struct cpuidle_state_usage	states_usage[CPUIDLE_STATE_MAX];
 	struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
 
 	struct list_head 	device_list;


^ permalink raw reply related

* [PATCH V7 2/4] cpuidle: Remove CPUIDLE_FLAG_IGNORE and dev->prepare()
From: Deepthi Dharwar @ 2011-09-27 10:15 UTC (permalink / raw)
  To: khilman, venki, ak, len.brown, peterz, santosh.shilimkar, arjan,
	lenb
  Cc: linux-sh, linux-kernel, linux-acpi, linux-pm, linux-omap,
	linux-arm-kernel
In-Reply-To: <20110927100202.16043.58669.stgit@localhost6.localdomain6>

The cpuidle_device->prepare() mechanism causes updates to the
cpuidle_state[].flags, setting and clearing CPUIDLE_FLAG_IGNORE
to tell the governor not to chose a state on a per-cpu basis at
run-time. State demotion is now handled by the driver and it returns
the actual state entered. Hence, this mechanism is not required.
Also this removes per-cpu flags from cpuidle_state enabling
it to be made global.

Reference:
https://lkml.org/lkml/2011/3/25/52

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
 drivers/cpuidle/cpuidle.c        |   10 ----------
 drivers/cpuidle/governors/menu.c |    2 --
 include/linux/cpuidle.h          |    3 ---
 3 files changed, 0 insertions(+), 15 deletions(-)

diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 88bd121..f66bcf9 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -83,16 +83,6 @@ int cpuidle_idle_call(void)
 	hrtimer_peek_ahead_timers();
 #endif
 
-	/*
-	 * Call the device's prepare function before calling the
-	 * governor's select function.  ->prepare gives the device's
-	 * cpuidle driver a chance to update any dynamic information
-	 * of its cpuidle states for the current idle period, e.g.
-	 * state availability, latencies, residencies, etc.
-	 */
-	if (dev->prepare)
-		dev->prepare(dev);
-
 	/* ask the governor for the next state */
 	next_state = cpuidle_curr_governor->select(dev);
 	if (need_resched()) {
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index e4b200c..af724e8 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -288,8 +288,6 @@ static int menu_select(struct cpuidle_device *dev)
 	for (i = CPUIDLE_DRIVER_STATE_START; i < dev->state_count; i++) {
 		struct cpuidle_state *s = &dev->states[i];
 
-		if (s->flags & CPUIDLE_FLAG_IGNORE)
-			continue;
 		if (s->target_residency > data->predicted_us)
 			continue;
 		if (s->exit_latency > latency_req)
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 8da811b..c6d85cf 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -47,7 +47,6 @@ struct cpuidle_state {
 
 /* Idle State Flags */
 #define CPUIDLE_FLAG_TIME_VALID	(0x01) /* is residency time measurable? */
-#define CPUIDLE_FLAG_IGNORE	(0x100) /* ignore during this idle period */
 
 #define CPUIDLE_DRIVER_FLAGS_MASK (0xFFFF0000)
 
@@ -93,8 +92,6 @@ struct cpuidle_device {
 	struct completion	kobj_unregister;
 	void			*governor_data;
 	int			safe_state_index;
-
-	int (*prepare)		(struct cpuidle_device *dev);
 };
 
 DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);


^ permalink raw reply related

* [PATCH V7 1/4] cpuidle: Move dev->last_residency update to driver
From: Deepthi Dharwar @ 2011-09-27 10:14 UTC (permalink / raw)
  To: khilman, venki, ak, len.brown, peterz, santosh.shilimkar, arjan,
	lenb
  Cc: linux-sh, linux-kernel, linux-acpi, linux-pm, linux-omap,
	linux-arm-kernel
In-Reply-To: <20110927100202.16043.58669.stgit@localhost6.localdomain6>

Cpuidle governor only suggests the state to enter using the
governor->select() interface, but allows the low level driver to
override the recommended state. The actual entered state
may be different because of software or hardware demotion. Software
demotion is done by the back-end cpuidle driver and can be accounted
correctly. Current cpuidle code uses last_state field to capture the
actual state entered and based on that updates the statistics for the
state entered.

Ideally the driver enter routine should update the counters,
and it should return the state actually entered rather than the time
spent there. The generic cpuidle code should simply handle where
the counters live in the sysfs namespace, not updating the counters.

Reference:
https://lkml.org/lkml/2011/3/25/52

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Kevin Hilman <khilman@ti.com>
---
 arch/arm/mach-at91/cpuidle.c          |   10 +++-
 arch/arm/mach-davinci/cpuidle.c       |    9 +++-
 arch/arm/mach-exynos4/cpuidle.c       |    7 ++-
 arch/arm/mach-kirkwood/cpuidle.c      |   12 ++++-
 arch/arm/mach-omap2/cpuidle34xx.c     |   67 +++++++++++++++++------------
 arch/sh/kernel/cpu/shmobile/cpuidle.c |   12 +++--
 drivers/acpi/processor_idle.c         |   75 ++++++++++++++++++++++-----------
 drivers/cpuidle/cpuidle.c             |   32 +++++++-------
 drivers/cpuidle/governors/ladder.c    |   13 ++++++
 drivers/cpuidle/governors/menu.c      |    7 ++-
 drivers/idle/intel_idle.c             |   12 ++++-
 include/linux/cpuidle.h               |    7 +--
 12 files changed, 164 insertions(+), 99 deletions(-)

diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c
index 1cfeac1..4696a0d 100644
--- a/arch/arm/mach-at91/cpuidle.c
+++ b/arch/arm/mach-at91/cpuidle.c
@@ -33,7 +33,7 @@ static struct cpuidle_driver at91_idle_driver = {
 
 /* Actual code that puts the SoC in different idle states */
 static int at91_enter_idle(struct cpuidle_device *dev,
-			       struct cpuidle_state *state)
+			       int index)
 {
 	struct timeval before, after;
 	int idle_time;
@@ -41,10 +41,10 @@ static int at91_enter_idle(struct cpuidle_device *dev,
 
 	local_irq_disable();
 	do_gettimeofday(&before);
-	if (state = &dev->states[0])
+	if (index = 0)
 		/* Wait for interrupt state */
 		cpu_do_idle();
-	else if (state = &dev->states[1]) {
+	else if (index = 1) {
 		asm("b 1f; .align 5; 1:");
 		asm("mcr p15, 0, r0, c7, c10, 4");	/* drain write buffer */
 		saved_lpr = sdram_selfrefresh_enable();
@@ -55,7 +55,9 @@ static int at91_enter_idle(struct cpuidle_device *dev,
 	local_irq_enable();
 	idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
 			(after.tv_usec - before.tv_usec);
-	return idle_time;
+
+	dev->last_residency = idle_time;
+	return index;
 }
 
 /* Initialize CPU idle by registering the idle states */
diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c
index bd59f31..ca8582a 100644
--- a/arch/arm/mach-davinci/cpuidle.c
+++ b/arch/arm/mach-davinci/cpuidle.c
@@ -78,9 +78,9 @@ static struct davinci_ops davinci_states[DAVINCI_CPUIDLE_MAX_STATES] = {
 
 /* Actual code that puts the SoC in different idle states */
 static int davinci_enter_idle(struct cpuidle_device *dev,
-						struct cpuidle_state *state)
+						int index)
 {
-	struct davinci_ops *ops = cpuidle_get_statedata(state);
+	struct davinci_ops *ops = cpuidle_get_statedata(&dev->states[index]);
 	struct timeval before, after;
 	int idle_time;
 
@@ -98,7 +98,10 @@ static int davinci_enter_idle(struct cpuidle_device *dev,
 	local_irq_enable();
 	idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
 			(after.tv_usec - before.tv_usec);
-	return idle_time;
+
+	dev->last_residency = idle_time;
+
+	return index;
 }
 
 static int __init davinci_cpuidle_probe(struct platform_device *pdev)
diff --git a/arch/arm/mach-exynos4/cpuidle.c b/arch/arm/mach-exynos4/cpuidle.c
index bf7e96f..ea026e7 100644
--- a/arch/arm/mach-exynos4/cpuidle.c
+++ b/arch/arm/mach-exynos4/cpuidle.c
@@ -16,7 +16,7 @@
 #include <asm/proc-fns.h>
 
 static int exynos4_enter_idle(struct cpuidle_device *dev,
-			      struct cpuidle_state *state);
+			      int index);
 
 static struct cpuidle_state exynos4_cpuidle_set[] = {
 	[0] = {
@@ -37,7 +37,7 @@ static struct cpuidle_driver exynos4_idle_driver = {
 };
 
 static int exynos4_enter_idle(struct cpuidle_device *dev,
-			      struct cpuidle_state *state)
+			      int index)
 {
 	struct timeval before, after;
 	int idle_time;
@@ -52,7 +52,8 @@ static int exynos4_enter_idle(struct cpuidle_device *dev,
 	idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
 		    (after.tv_usec - before.tv_usec);
 
-	return idle_time;
+	dev->last_residency = idle_time;
+	return index;
 }
 
 static int __init exynos4_init_cpuidle(void)
diff --git a/arch/arm/mach-kirkwood/cpuidle.c b/arch/arm/mach-kirkwood/cpuidle.c
index f68d33f..358dd80 100644
--- a/arch/arm/mach-kirkwood/cpuidle.c
+++ b/arch/arm/mach-kirkwood/cpuidle.c
@@ -32,17 +32,17 @@ static DEFINE_PER_CPU(struct cpuidle_device, kirkwood_cpuidle_device);
 
 /* Actual code that puts the SoC in different idle states */
 static int kirkwood_enter_idle(struct cpuidle_device *dev,
-			       struct cpuidle_state *state)
+			       int index)
 {
 	struct timeval before, after;
 	int idle_time;
 
 	local_irq_disable();
 	do_gettimeofday(&before);
-	if (state = &dev->states[0])
+	if (index = 0)
 		/* Wait for interrupt state */
 		cpu_do_idle();
-	else if (state = &dev->states[1]) {
+	else if (index = 1) {
 		/*
 		 * Following write will put DDR in self refresh.
 		 * Note that we have 256 cycles before DDR puts it
@@ -57,7 +57,11 @@ static int kirkwood_enter_idle(struct cpuidle_device *dev,
 	local_irq_enable();
 	idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
 			(after.tv_usec - before.tv_usec);
-	return idle_time;
+
+	/* Update last residency */
+	dev->last_residency = idle_time;
+
+	return index;
 }
 
 /* Initialize CPU idle by registering the idle states */
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 4bf6e6e..58425c7 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -88,17 +88,19 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
 /**
  * omap3_enter_idle - Programs OMAP3 to enter the specified state
  * @dev: cpuidle device
- * @state: The target state to be programmed
+ * @index: the index of state to be entered
  *
  * Called from the CPUidle framework to program the device to the
  * specified target state selected by the governor.
  */
 static int omap3_enter_idle(struct cpuidle_device *dev,
-			struct cpuidle_state *state)
+				int index)
 {
-	struct omap3_idle_statedata *cx = cpuidle_get_statedata(state);
+	struct omap3_idle_statedata *cx +			cpuidle_get_statedata(&dev->states[index]);
 	struct timespec ts_preidle, ts_postidle, ts_idle;
 	u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
+	int idle_time;
 
 	/* Used to keep track of the total time in idle */
 	getnstimeofday(&ts_preidle);
@@ -113,7 +115,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
 		goto return_sleep_time;
 
 	/* Deny idle for C1 */
-	if (state = &dev->states[0]) {
+	if (index = 0) {
 		pwrdm_for_each_clkdm(mpu_pd, _cpuidle_deny_idle);
 		pwrdm_for_each_clkdm(core_pd, _cpuidle_deny_idle);
 	}
@@ -122,7 +124,7 @@ static int omap3_enter_idle(struct cpuidle_device *dev,
 	omap_sram_idle();
 
 	/* Re-allow idle for C1 */
-	if (state = &dev->states[0]) {
+	if (index = 0) {
 		pwrdm_for_each_clkdm(mpu_pd, _cpuidle_allow_idle);
 		pwrdm_for_each_clkdm(core_pd, _cpuidle_allow_idle);
 	}
@@ -134,28 +136,35 @@ return_sleep_time:
 	local_irq_enable();
 	local_fiq_enable();
 
-	return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC;
+	idle_time = ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * \
+								USEC_PER_SEC;
+
+	/* Update cpuidle counters */
+	dev->last_residency = idle_time;
+
+	return index;
 }
 
 /**
  * next_valid_state - Find next valid C-state
  * @dev: cpuidle device
- * @state: Currently selected C-state
+ * @index: Index of currently selected c-state
  *
- * If the current state is valid, it is returned back to the caller.
- * Else, this function searches for a lower c-state which is still
- * valid.
+ * If the state corresponding to index is valid, index is returned back
+ * to the caller. Else, this function searches for a lower c-state which is
+ * still valid (as defined in omap3_power_states[]) and returns its index.
  *
  * A state is valid if the 'valid' field is enabled and
  * if it satisfies the enable_off_mode condition.
  */
-static struct cpuidle_state *next_valid_state(struct cpuidle_device *dev,
-					      struct cpuidle_state *curr)
+static int next_valid_state(struct cpuidle_device *dev,
+				int index)
 {
-	struct cpuidle_state *next = NULL;
+	struct cpuidle_state *curr = &dev->states[index];
 	struct omap3_idle_statedata *cx = cpuidle_get_statedata(curr);
 	u32 mpu_deepest_state = PWRDM_POWER_RET;
 	u32 core_deepest_state = PWRDM_POWER_RET;
+	int next_index = -1;
 
 	if (enable_off_mode) {
 		mpu_deepest_state = PWRDM_POWER_OFF;
@@ -172,20 +181,20 @@ static struct cpuidle_state *next_valid_state(struct cpuidle_device *dev,
 	if ((cx->valid) &&
 	    (cx->mpu_state >= mpu_deepest_state) &&
 	    (cx->core_state >= core_deepest_state)) {
-		return curr;
+		return index;
 	} else {
 		int idx = OMAP3_NUM_STATES - 1;
 
 		/* Reach the current state starting at highest C-state */
 		for (; idx >= 0; idx--) {
 			if (&dev->states[idx] = curr) {
-				next = &dev->states[idx];
+				next_index = idx;
 				break;
 			}
 		}
 
 		/* Should never hit this condition */
-		WARN_ON(next = NULL);
+		WARN_ON(next_index = -1);
 
 		/*
 		 * Drop to next valid state.
@@ -197,37 +206,39 @@ static struct cpuidle_state *next_valid_state(struct cpuidle_device *dev,
 			if ((cx->valid) &&
 			    (cx->mpu_state >= mpu_deepest_state) &&
 			    (cx->core_state >= core_deepest_state)) {
-				next = &dev->states[idx];
+				next_index = idx;
 				break;
 			}
 		}
 		/*
 		 * C1 is always valid.
-		 * So, no need to check for 'next=NULL' outside this loop.
+		 * So, no need to check for 'next_index = -1' outside
+		 * this loop.
 		 */
 	}
 
-	return next;
+	return next_index;
 }
 
 /**
  * omap3_enter_idle_bm - Checks for any bus activity
  * @dev: cpuidle device
- * @state: The target state to be programmed
+ * @index: array index of target state to be programmed
  *
  * This function checks for any pending activity and then programs
  * the device to the specified or a safer state.
  */
 static int omap3_enter_idle_bm(struct cpuidle_device *dev,
-			       struct cpuidle_state *state)
+			       int index)
 {
-	struct cpuidle_state *new_state;
+	struct cpuidle_state *state = &dev->states[index];
+	int new_state_idx;
 	u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
 	struct omap3_idle_statedata *cx;
 	int ret;
 
 	if (!omap3_can_sleep()) {
-		new_state = dev->safe_state;
+		new_state_idx = dev->safe_state_index;
 		goto select_state;
 	}
 
@@ -237,7 +248,7 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	 */
 	cam_state = pwrdm_read_pwrst(cam_pd);
 	if (cam_state = PWRDM_POWER_ON) {
-		new_state = dev->safe_state;
+		new_state_idx = dev->safe_state_index;
 		goto select_state;
 	}
 
@@ -264,11 +275,10 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
 	if (per_next_state != per_saved_state)
 		pwrdm_set_next_pwrst(per_pd, per_next_state);
 
-	new_state = next_valid_state(dev, state);
+	new_state_idx = next_valid_state(dev, index);
 
 select_state:
-	dev->last_state = new_state;
-	ret = omap3_enter_idle(dev, new_state);
+	ret = omap3_enter_idle(dev, new_state_idx);
 
 	/* Restore original PER state if it was modified */
 	if (per_next_state != per_saved_state)
@@ -339,11 +349,12 @@ int __init omap3_idle_init(void)
 
 	cpuidle_register_driver(&omap3_idle_driver);
 	dev = &per_cpu(omap3_idle_dev, smp_processor_id());
+	dev->safe_state_index = -1;
 
 	/* C1 . MPU WFI + Core active */
 	cx = _fill_cstate(dev, 0, "MPU ON + CORE ON");
 	(&dev->states[0])->enter = omap3_enter_idle;
-	dev->safe_state = &dev->states[0];
+	dev->safe_state_index = 0;
 	cx->valid = 1;	/* C1 is always valid */
 	cx->mpu_state = PWRDM_POWER_ON;
 	cx->core_state = PWRDM_POWER_ON;
diff --git a/arch/sh/kernel/cpu/shmobile/cpuidle.c b/arch/sh/kernel/cpu/shmobile/cpuidle.c
index e4469e72..7be50d4c 100644
--- a/arch/sh/kernel/cpu/shmobile/cpuidle.c
+++ b/arch/sh/kernel/cpu/shmobile/cpuidle.c
@@ -25,11 +25,11 @@ static unsigned long cpuidle_mode[] = {
 };
 
 static int cpuidle_sleep_enter(struct cpuidle_device *dev,
-			       struct cpuidle_state *state)
+				int index)
 {
 	unsigned long allowed_mode = arch_hwblk_sleep_mode();
 	ktime_t before, after;
-	int requested_state = state - &dev->states[0];
+	int requested_state = index;
 	int allowed_state;
 	int k;
 
@@ -46,11 +46,13 @@ static int cpuidle_sleep_enter(struct cpuidle_device *dev,
 	 */
 	k = min_t(int, allowed_state, requested_state);
 
-	dev->last_state = &dev->states[k];
 	before = ktime_get();
 	sh_mobile_call_standby(cpuidle_mode[k]);
 	after = ktime_get();
-	return ktime_to_ns(ktime_sub(after, before)) >> 10;
+
+	dev->last_residency = (int)ktime_to_ns(ktime_sub(after, before)) >> 10;
+
+	return k;
 }
 
 static struct cpuidle_device cpuidle_dev;
@@ -84,7 +86,7 @@ void sh_mobile_setup_cpuidle(void)
 	state->flags |= CPUIDLE_FLAG_TIME_VALID;
 	state->enter = cpuidle_sleep_enter;
 
-	dev->safe_state = state;
+	dev->safe_state_index = i-1;
 
 	if (sh_mobile_sleep_supported & SUSP_SH_SF) {
 		state = &dev->states[i++];
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 431ab11..9cd08ce 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -741,22 +741,24 @@ static inline void acpi_idle_do_entry(struct acpi_processor_cx *cx)
 /**
  * acpi_idle_enter_c1 - enters an ACPI C1 state-type
  * @dev: the target CPU
- * @state: the state data
+ * @index: index of target state
  *
  * This is equivalent to the HALT instruction.
  */
 static int acpi_idle_enter_c1(struct cpuidle_device *dev,
-			      struct cpuidle_state *state)
+				int index)
 {
 	ktime_t  kt1, kt2;
 	s64 idle_time;
 	struct acpi_processor *pr;
+	struct cpuidle_state *state = &dev->states[index];
 	struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
 
 	pr = __this_cpu_read(processors);
+	dev->last_residency = 0;
 
 	if (unlikely(!pr))
-		return 0;
+		return -EINVAL;
 
 	local_irq_disable();
 
@@ -764,7 +766,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
 	if (acpi_idle_suspend) {
 		local_irq_enable();
 		cpu_relax();
-		return 0;
+		return -EINVAL;
 	}
 
 	lapic_timer_state_broadcast(pr, cx, 1);
@@ -773,37 +775,46 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
 	kt2 = ktime_get_real();
 	idle_time =  ktime_to_us(ktime_sub(kt2, kt1));
 
+	/* Update device last_residency*/
+	dev->last_residency = (int)idle_time;
+
 	local_irq_enable();
 	cx->usage++;
 	lapic_timer_state_broadcast(pr, cx, 0);
 
-	return idle_time;
+	return index;
 }
 
 /**
  * acpi_idle_enter_simple - enters an ACPI state without BM handling
  * @dev: the target CPU
- * @state: the state data
+ * @index: the index of suggested state
  */
 static int acpi_idle_enter_simple(struct cpuidle_device *dev,
-				  struct cpuidle_state *state)
+				int index)
 {
 	struct acpi_processor *pr;
+	struct cpuidle_state *state = &dev->states[index];
 	struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
 	ktime_t  kt1, kt2;
 	s64 idle_time_ns;
 	s64 idle_time;
 
 	pr = __this_cpu_read(processors);
+	dev->last_residency = 0;
 
 	if (unlikely(!pr))
-		return 0;
-
-	if (acpi_idle_suspend)
-		return(acpi_idle_enter_c1(dev, state));
+		return -EINVAL;
 
 	local_irq_disable();
 
+	if (acpi_idle_suspend) {
+		local_irq_enable();
+		cpu_relax();
+		return -EINVAL;
+	}
+
+
 	if (cx->entry_method != ACPI_CSTATE_FFH) {
 		current_thread_info()->status &= ~TS_POLLING;
 		/*
@@ -815,7 +826,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 		if (unlikely(need_resched())) {
 			current_thread_info()->status |= TS_POLLING;
 			local_irq_enable();
-			return 0;
+			return -EINVAL;
 		}
 	}
 
@@ -837,6 +848,9 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 	idle_time = idle_time_ns;
 	do_div(idle_time, NSEC_PER_USEC);
 
+	/* Update device last_residency*/
+	dev->last_residency = (int)idle_time;
+
 	/* Tell the scheduler how much we idled: */
 	sched_clock_idle_wakeup_event(idle_time_ns);
 
@@ -848,7 +862,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 
 	lapic_timer_state_broadcast(pr, cx, 0);
 	cx->time += idle_time;
-	return idle_time;
+	return index;
 }
 
 static int c3_cpu_count;
@@ -857,14 +871,15 @@ static DEFINE_SPINLOCK(c3_lock);
 /**
  * acpi_idle_enter_bm - enters C3 with proper BM handling
  * @dev: the target CPU
- * @state: the state data
+ * @index: the index of suggested state
  *
  * If BM is detected, the deepest non-C3 idle state is entered instead.
  */
 static int acpi_idle_enter_bm(struct cpuidle_device *dev,
-			      struct cpuidle_state *state)
+				int index)
 {
 	struct acpi_processor *pr;
+	struct cpuidle_state *state = &dev->states[index];
 	struct acpi_processor_cx *cx = cpuidle_get_statedata(state);
 	ktime_t  kt1, kt2;
 	s64 idle_time_ns;
@@ -872,22 +887,26 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 
 
 	pr = __this_cpu_read(processors);
+	dev->last_residency = 0;
 
 	if (unlikely(!pr))
-		return 0;
+		return -EINVAL;
 
-	if (acpi_idle_suspend)
-		return(acpi_idle_enter_c1(dev, state));
+
+	if (acpi_idle_suspend) {
+		cpu_relax();
+		return -EINVAL;
+	}
 
 	if (!cx->bm_sts_skip && acpi_idle_bm_check()) {
-		if (dev->safe_state) {
-			dev->last_state = dev->safe_state;
-			return dev->safe_state->enter(dev, dev->safe_state);
+		if (dev->safe_state_index >= 0) {
+			return dev->states[dev->safe_state_index].enter(dev,
+						dev->safe_state_index);
 		} else {
 			local_irq_disable();
 			acpi_safe_halt();
 			local_irq_enable();
-			return 0;
+			return -EINVAL;
 		}
 	}
 
@@ -904,7 +923,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 		if (unlikely(need_resched())) {
 			current_thread_info()->status |= TS_POLLING;
 			local_irq_enable();
-			return 0;
+			return -EINVAL;
 		}
 	}
 
@@ -954,6 +973,9 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 	idle_time = idle_time_ns;
 	do_div(idle_time, NSEC_PER_USEC);
 
+	/* Update device last_residency*/
+	dev->last_residency = (int)idle_time;
+
 	/* Tell the scheduler how much we idled: */
 	sched_clock_idle_wakeup_event(idle_time_ns);
 
@@ -965,7 +987,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 
 	lapic_timer_state_broadcast(pr, cx, 0);
 	cx->time += idle_time;
-	return idle_time;
+	return index;
 }
 
 struct cpuidle_driver acpi_idle_driver = {
@@ -992,6 +1014,7 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 	}
 
 	dev->cpu = pr->id;
+	dev->safe_state_index = -1;
 	for (i = 0; i < CPUIDLE_STATE_MAX; i++) {
 		dev->states[i].name[0] = '\0';
 		dev->states[i].desc[0] = '\0';
@@ -1027,13 +1050,13 @@ static int acpi_processor_setup_cpuidle(struct acpi_processor *pr)
 				state->flags |= CPUIDLE_FLAG_TIME_VALID;
 
 			state->enter = acpi_idle_enter_c1;
-			dev->safe_state = state;
+			dev->safe_state_index = count;
 			break;
 
 			case ACPI_STATE_C2:
 			state->flags |= CPUIDLE_FLAG_TIME_VALID;
 			state->enter = acpi_idle_enter_simple;
-			dev->safe_state = state;
+			dev->safe_state_index = count;
 			break;
 
 			case ACPI_STATE_C3:
diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index d4c5423..88bd121 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -62,7 +62,7 @@ int cpuidle_idle_call(void)
 {
 	struct cpuidle_device *dev = __this_cpu_read(cpuidle_devices);
 	struct cpuidle_state *target_state;
-	int next_state;
+	int next_state, entered_state;
 
 	if (off)
 		return -ENODEV;
@@ -102,26 +102,27 @@ int cpuidle_idle_call(void)
 
 	target_state = &dev->states[next_state];
 
-	/* enter the state and update stats */
-	dev->last_state = target_state;
-
 	trace_power_start(POWER_CSTATE, next_state, dev->cpu);
 	trace_cpu_idle(next_state, dev->cpu);
 
-	dev->last_residency = target_state->enter(dev, target_state);
+	entered_state = target_state->enter(dev, next_state);
 
 	trace_power_end(dev->cpu);
 	trace_cpu_idle(PWR_EVENT_EXIT, dev->cpu);
 
-	if (dev->last_state)
-		target_state = dev->last_state;
-
-	target_state->time += (unsigned long long)dev->last_residency;
-	target_state->usage++;
+	if (entered_state >= 0) {
+		/* Update cpuidle counters */
+		/* This can be moved to within driver enter routine
+		 * but that results in multiple copies of same code.
+		 */
+		dev->states[entered_state].time ++				(unsigned long long)dev->last_residency;
+		dev->states[entered_state].usage++;
+	}
 
 	/* give the governor an opportunity to reflect on the outcome */
 	if (cpuidle_curr_governor->reflect)
-		cpuidle_curr_governor->reflect(dev);
+		cpuidle_curr_governor->reflect(dev, entered_state);
 
 	return 0;
 }
@@ -172,11 +173,10 @@ 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_state *st)
+static int poll_idle(struct cpuidle_device *dev, int index)
 {
 	ktime_t	t1, t2;
 	s64 diff;
-	int ret;
 
 	t1 = ktime_get();
 	local_irq_enable();
@@ -188,8 +188,9 @@ static int poll_idle(struct cpuidle_device *dev, struct cpuidle_state *st)
 	if (diff > INT_MAX)
 		diff = INT_MAX;
 
-	ret = (int) diff;
-	return ret;
+	dev->last_residency = (int) diff;
+
+	return index;
 }
 
 static void poll_idle_init(struct cpuidle_device *dev)
@@ -248,7 +249,6 @@ int cpuidle_enable_device(struct cpuidle_device *dev)
 		dev->states[i].time = 0;
 	}
 	dev->last_residency = 0;
-	dev->last_state = NULL;
 
 	smp_wmb();
 
diff --git a/drivers/cpuidle/governors/ladder.c b/drivers/cpuidle/governors/ladder.c
index 12c9890..6a686a7 100644
--- a/drivers/cpuidle/governors/ladder.c
+++ b/drivers/cpuidle/governors/ladder.c
@@ -153,11 +153,24 @@ static int ladder_enable_device(struct cpuidle_device *dev)
 	return 0;
 }
 
+/**
+ * ladder_reflect - update the correct last_state_idx
+ * @dev: the CPU
+ * @index: the index of actual state entered
+ */
+static void ladder_reflect(struct cpuidle_device *dev, int index)
+{
+	struct ladder_device *ldev = &__get_cpu_var(ladder_devices);
+	if (index > 0)
+		ldev->last_state_idx = index;
+}
+
 static struct cpuidle_governor ladder_governor = {
 	.name =		"ladder",
 	.rating =	10,
 	.enable =	ladder_enable_device,
 	.select =	ladder_select_state,
+	.reflect =	ladder_reflect,
 	.owner =	THIS_MODULE,
 };
 
diff --git a/drivers/cpuidle/governors/menu.c b/drivers/cpuidle/governors/menu.c
index c47f3d0..e4b200c 100644
--- a/drivers/cpuidle/governors/menu.c
+++ b/drivers/cpuidle/governors/menu.c
@@ -310,14 +310,17 @@ static int menu_select(struct cpuidle_device *dev)
 /**
  * menu_reflect - records that data structures need update
  * @dev: the CPU
+ * @index: the index of actual entered state
  *
  * NOTE: it's important to be fast here because this operation will add to
  *       the overall exit latency.
  */
-static void menu_reflect(struct cpuidle_device *dev)
+static void menu_reflect(struct cpuidle_device *dev, int index)
 {
 	struct menu_device *data = &__get_cpu_var(menu_devices);
-	data->needs_update = 1;
+	data->last_state_idx = index;
+	if (index >= 0)
+		data->needs_update = 1;
 }
 
 /**
diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c
index a46dddf..a1c888d 100644
--- a/drivers/idle/intel_idle.c
+++ b/drivers/idle/intel_idle.c
@@ -81,7 +81,7 @@ static unsigned int mwait_substates;
 static unsigned int lapic_timer_reliable_states = (1 << 1);	 /* Default to only C1 */
 
 static struct cpuidle_device __percpu *intel_idle_cpuidle_devices;
-static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state);
+static int intel_idle(struct cpuidle_device *dev, int index);
 
 static struct cpuidle_state *cpuidle_state_table;
 
@@ -209,12 +209,13 @@ static struct cpuidle_state atom_cstates[MWAIT_MAX_NUM_CSTATES] = {
 /**
  * intel_idle
  * @dev: cpuidle_device
- * @state: cpuidle state
+ * @index: index of cpuidle state
  *
  */
-static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
+static int intel_idle(struct cpuidle_device *dev, int index)
 {
 	unsigned long ecx = 1; /* break on interrupt flag */
+	struct cpuidle_state *state = &dev->states[index];
 	unsigned long eax = (unsigned long)cpuidle_get_statedata(state);
 	unsigned int cstate;
 	ktime_t kt_before, kt_after;
@@ -256,7 +257,10 @@ static int intel_idle(struct cpuidle_device *dev, struct cpuidle_state *state)
 	if (!(lapic_timer_reliable_states & (1 << (cstate))))
 		clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &cpu);
 
-	return usec_delta;
+	/* Update cpuidle counters */
+	dev->last_residency = (int)usec_delta;
+
+	return index;
 }
 
 static void __setup_broadcast_timer(void *arg)
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index b51629e..8da811b 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -42,7 +42,7 @@ struct cpuidle_state {
 	unsigned long long	time; /* in US */
 
 	int (*enter)	(struct cpuidle_device *dev,
-			 struct cpuidle_state *state);
+			int index);
 };
 
 /* Idle State Flags */
@@ -87,13 +87,12 @@ struct cpuidle_device {
 	int			state_count;
 	struct cpuidle_state	states[CPUIDLE_STATE_MAX];
 	struct cpuidle_state_kobj *kobjs[CPUIDLE_STATE_MAX];
-	struct cpuidle_state	*last_state;
 
 	struct list_head 	device_list;
 	struct kobject		kobj;
 	struct completion	kobj_unregister;
 	void			*governor_data;
-	struct cpuidle_state	*safe_state;
+	int			safe_state_index;
 
 	int (*prepare)		(struct cpuidle_device *dev);
 };
@@ -169,7 +168,7 @@ struct cpuidle_governor {
 	void (*disable)		(struct cpuidle_device *dev);
 
 	int  (*select)		(struct cpuidle_device *dev);
-	void (*reflect)		(struct cpuidle_device *dev);
+	void (*reflect)		(struct cpuidle_device *dev, int index);
 
 	struct module 		*owner;
 };


^ permalink raw reply related

* [PATCH V7 0/4] cpuidle: Global registration of idle states with
From: Deepthi Dharwar @ 2011-09-27 10:14 UTC (permalink / raw)
  To: khilman, venki, ak, len.brown, peterz, santosh.shilimkar, arjan,
	lenb
  Cc: linux-sh, linux-kernel, linux-acpi, linux-pm, linux-omap,
	linux-arm-kernel

Version 6 of this patch series dated 22nd Sept 2011 was 
Acked-by: Arjan van de Ven <arjan@linux.intel.com>
Acked-by: Kevin Hilman <khilman@ti.com> for OMAP specific parts.
Reviewed-by: Kevin Hilman <khilman@ti.com>
Signed-off-by: Jean Pihet <j-pihet@ti.com>

Hi Len, 
Can you please queue this series for the next mainline merge window by merging
it into your development tree and also linux-next for further testing.

I also tested it, by applying these patches on your ACPI tree hosted @github.

Thanks
-Deepthi


The following patch series implements global registration of cpuidle
states, and also has the necessary data structure changes to
accommodate the per-cpu writable members of the cpuidle_states
structure.

This patch series had been in discussion earlier and
following are the links to the previous discussions.

v1 --> https://lkml.org/lkml/2011/3/22/161
v2 --> https://lkml.org/lkml/2011/1/13/98
v3 --> https://lkml.org/lkml/2011/2/8/73
v4 --> https://lkml.org/lkml/2011/4/28/312
v5 --> https://lkml.org/lkml/2011/6/6/259
v6 --> https://lkml.org/lkml/2011/9/22/58

Changes from previous version (V6):

   Removed the RFC tag.
    
Tests done:

    1. Compile tested for ARM using the following configs: da8xx_omapl_defconfig,
    exynos4_defconfig, kirkwood_defconfig, omap2plus_defconfig, 
    at91rm9200_defconfig
      
    2. Boot tested on x86 nehalem with multiple C-states for both intel_idle
    and acpi_idle drivers.

    3. Boot tested on T60p thinkpad that has T2600 cpu with multiple C-states. 
    Also tested the case when there is dynamic changes in C-states 
    AC <-> Battery Power switch.

Brief description of the patches:

Core change in this series is to split the cpuidle_device structure 
into two parts, i.e  global and per-cpu basis. 

The per-cpu pieces are mostly generic statistics that can be independent 
of current running driver. As a result of these changes, there is single 
copy of cpuidle_states structure and single registration done by one 
cpu. The low level driver is free to set per-cpu driver data on
each cpu if needed using the cpuidle_set_statedata() as the case
today. Only in very rare cases asymmetric C-states exist which can be 
handled within the cpuidle driver. Most architectures do not have 
asymmetric C-states.

First two patches in the series facilitate splitting of cpuidle_states
and cpuidle_device structure and next two patches do the actual split,
change the API's and make existing code follow the changed API.

[1/4] - Move the idle residency accounting part from cpuidle.c to
the respective low level drivers, so that the accounting can
be accurately maintained if the driver decides to demote the
chosen (suggested) by the governor.

[2/4] - removes the cpuidle_device()->prepare API since is is not
widely used and the only use case was to allow software
demotion using CPUIDLE_FLAG_IGNORE flag.  Both these
functions can be absorbed within the cpuidle back-end
driver ad hence deprecating the prepare routine and the
CPUIDLE_FLAG_IGNORE flag.

    - Ref: https://lkml.org/lkml/2011/3/25/52

[3/4] - Splits the usage statistics (read/write) part out of
cpuidle_state structure, so that the states can become read
only and hence made global.

[4/4] - Most APIs will now need to pass pointer to both global
cpuidle_driver and per-cpu cpuidle_device structure.

 arch/arm/mach-at91/cpuidle.c          |   41 +++--
 arch/arm/mach-davinci/cpuidle.c       |   51 ++++---
 arch/arm/mach-exynos4/cpuidle.c       |   30 ++--
 arch/arm/mach-kirkwood/cpuidle.c      |   42 +++---
 arch/arm/mach-omap2/cpuidle34xx.c     |  133 +++++++++++------
 arch/sh/kernel/cpu/shmobile/cpuidle.c |   28 ++--
 drivers/acpi/processor_driver.c       |   20 ---
 drivers/acpi/processor_idle.c         |  251 +++++++++++++++++++++++++++------
 drivers/cpuidle/cpuidle.c             |   86 ++++-------
 drivers/cpuidle/driver.c              |   25 +++
 drivers/cpuidle/governors/ladder.c    |   41 ++++-
 drivers/cpuidle/governors/menu.c      |   29 ++--
 drivers/cpuidle/sysfs.c               |   22 ++-
 drivers/idle/intel_idle.c             |  130 +++++++++++++----
 include/acpi/processor.h              |    1 
 include/linux/cpuidle.h               |   52 ++++---
 16 files changed, 650 insertions(+), 332 deletions(-)


-- 

-Deepthi

^ permalink raw reply

* [PATCH] ARM: mach-shmobile: sh7372 Mackerel NOR Flash USB boot fix
From: Magnus Damm @ 2011-09-27  9:29 UTC (permalink / raw)
  To: linux-sh

From: Magnus Damm <damm@opensource.se>

Always use CS0 shadow area for NOR flash instead of regular CS0
memory area on Mackerel.

When booting from CS0 NOR Flash the regular CS0 memory area is
available, but when booting via USB the MASK ROM gets mapped to
0x0 which gets in the way for the NOR Flash. Always using CS0
shadow area works well for both NOR Flash boot and USB boot.

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 arch/arm/mach-shmobile/board-mackerel.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

--- 0001/arch/arm/mach-shmobile/board-mackerel.c
+++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-09-22 21:30:39.000000000 +0900
@@ -272,8 +272,8 @@ static struct physmap_flash_data nor_fla
 
 static struct resource nor_flash_resources[] = {
 	[0]	= {
-		.start	= 0x00000000,
-		.end	= 0x08000000 - 1,
+		.start	= 0x20000000, /* CS0 shadow instead of regular CS0 */
+		.end	= 0x28000000 - 1, /* needed by USB MASK ROM boot */
 		.flags	= IORESOURCE_MEM,
 	}
 };

^ permalink raw reply

* [PATCH] ARM: mach-shmobile: USB boot tool "r_usb_boot"
From: Magnus Damm @ 2011-09-27  9:22 UTC (permalink / raw)
  To: linux-sh

From: Magnus Damm <damm@opensource.se>

Add a Renesas specific USB boot tool "r_usb_boot" to the directory
Documentation/arm/SH-Mobile/.

This reusable tool is designed ot runs on the Linux host machine,
connecting to the MASK ROM of the target SoC. The USB boot tool is
used to upload and start code on the SoC. The code is uploaded to
internal SRAM and executed from there.

The USB boot tool is specific to certain SoC models but independent
of board. At this point sh7372 has been tested on AP4EVB and Mackerel
boards and sh73a0 SoC support has been tested on Kota2.

A common use case is to upload a board-specific minimal kernel image
to re-program the NOR Flash or perhaps use kexec to boot a full kernel.

Signed-off-by: Magnus Damm <damm@opensource.se>
---

 Documentation/arm/SH-Mobile/r_usb_boot.c |  544 ++++++++++++++++++++++++++++++
 1 file changed, 544 insertions(+)

--- /dev/null
+++ work/Documentation/arm/SH-Mobile/r_usb_boot.c	2011-09-27 18:08:23.000000000 +0900
@@ -0,0 +1,544 @@
+/*
+ * USB boot tool for SH-Mobile ARM SoCs
+ *
+ * Boots a stand alone Linux kernel directly from the MASK ROM.
+ * Limited to the size of the MERAM block (1.5 MiB SRAM)
+ *
+ * Tested on sh7372 and sh73a0.
+ *
+ * Copyright (C) 2011  Renesas Solutions Corp.
+ * Copyright (C) 2011  Magnus Damm
+ *
+ * Requires libusb, use the following line to build:
+ * $ gcc `pkg-config libusb-1.0 --libs --cflags` r_usb_boot.c -o r_usb_boot
+ *
+ * This program is designed to run on a Linux host computer that is
+ * connected to the target hardware via USB. The target hardware
+ * needs to have USB boot support built into the MASK ROM and the
+ * boot mode pins (MDn) should select "USB Development mode".
+ *
+ * Usage:
+ * # r_usb_boot zImage
+ *
+ * The kernel must include System RAM setup code among other things, as
+ * an example see arch/arm/mach-shmobile/include/mach/head-ap4evb.txt
+ * Important kernel configuration parameters include:
+ *
+ * CONFIG_ZBOOT_ROM=y
+ * CONFIG_ZBOOT_ROM_TEXT=0x0
+ * CONFIG_ZBOOT_ROM_BSS=0xe5580000 (Needs to match MERAM base address)
+ * CONFIG_CMDLINE="..." (This depends on your board)
+ * CONFIG_CMDLINE_FORCE=y
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libusb.h"
+
+#define HUGECHUNK 0x40000
+#define MAX_FILESIZE (HUGECHUNK * 5) /* 1.5 MiB MERAM, 256 KiB reserved */
+
+struct device_info {
+	char *name;
+	unsigned short vid;
+	unsigned short pid;
+
+	enum { FORMAT_VRL4, FORMAT_VRL5 } format;
+	unsigned int small_chunksize;
+	unsigned int large_chunksize;
+
+} supported_devices[] = {
+	{
+		"sh7372",
+		0x045b, 0x0055,
+		FORMAT_VRL4,
+		64, 4096,
+	},
+	{
+		"sh73a0",
+		0x045b, 0x0060,
+		FORMAT_VRL5,
+		64, 16384,
+	},
+};
+
+#define TIMEOUT 5000
+#define MAX_CHUNK 16384
+
+/* ARM code that copies data in memory */
+static unsigned char mover_code_data[] = {
+	0x02, 0x00, 0x00, 0xea, /* b after_size */
+	0x00, 0x00, 0x00, 0x00, /* src */
+	0x00, 0x00, 0x00, 0x00, /* dst */
+	0x00, 0x00, 0x00, 0x00, /* size */
+
+	0x14, 0x20, 0x1f, 0xe5, /* ldr r2, src */
+	0x14, 0x30, 0x1f, 0xe5, /* ldr r3, dst */
+	0x14, 0x00, 0x1f, 0xe5, /* ldr r0, len */
+
+	0x04, 0x10, 0x92, 0xe4, /* ldr r1,[r2],#4 */
+	0x04, 0x10, 0x83, 0xe4, /* str r1,[r3],#4 */
+	0x04, 0x00, 0x50, 0xe2, /* subs r0,r0,#4 */
+	0xfb, 0xff, 0xff, 0x1a, /* bne before_ldr_r1 */
+
+	0x0e, 0xf0, 0xa0, 0xe1, /* mov	pc, lr */
+};
+
+static int get_hw_version(libusb_device_handle *h)
+{
+	unsigned char get_version[] = { 0xaf, 0x00 };
+	unsigned char data[0x20];
+	int r, r2;
+
+	/* Get Version */
+	r = libusb_bulk_transfer(h, 0x02,
+				 get_version, sizeof(get_version),
+				 &r2, TIMEOUT);
+	if (r)
+		return -1;
+
+	/* Version Information */
+	r = libusb_bulk_transfer(h, 0x81,
+				 data, sizeof(data),
+				 &r2, TIMEOUT);
+
+	if (!r && r2 && (data[0] = 0xbf) && (data[1] = (r2 - 2))) {
+		data[r2] = '\0';
+		printf("Hardware Version Information %s\n", &data[2]);
+		return 0;
+	}
+
+	return -1;
+}
+
+static int status_request_response(libusb_device_handle *h)
+{
+	unsigned char data_out[3] = { 0xa1, 0x01, 0x00 };
+	unsigned char data_in[0x40];
+	int r, r2;
+
+	/* Status Request */
+	r = libusb_bulk_transfer(h, 0x02,
+				 data_out, sizeof(data_out),
+				 &r2, TIMEOUT);
+	if (r || (r2 != sizeof(data_out))) {
+		fprintf(stderr, "Status Request send error\n");
+		return -1;
+	}
+
+	/* Status Response */
+	r = libusb_bulk_transfer(h, 0x81,
+				 data_in, sizeof(data_in),
+				 &r2, TIMEOUT);
+
+	if (r || (r2 != 3) || (data_in[0] != 0xb0)) {
+		fprintf(stderr, "Status is not ok with %d %d 0x%02x 0x%02x\n",
+			r, r2, data_in[0], data_in[2]);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int setup_clocks(libusb_device_handle *h)
+{
+	unsigned char initialize[] = { 0xa0, 0x01, 0x00 };
+	unsigned char data[0x20];
+	int r, r2;
+
+	/* Initialize */
+	r = libusb_bulk_transfer(h, 0x02,
+				 initialize, sizeof(initialize),
+				 &r2, TIMEOUT);
+	if (r)
+		return -1;
+
+	/* Command Response */
+	r = libusb_bulk_transfer(h, 0x81,
+				 data, sizeof(data),
+				 &r2, TIMEOUT);
+	if (r || (r2 != 3) || (data[0] != 0xb0) || (data[2] != 0x00))
+		return -1;
+
+	/* Status Request + Status Response */
+	return status_request_response(h);
+}
+
+static int do_start(libusb_device_handle *h)
+{
+	unsigned char program_start[] = { 0xa5, 0x00 };
+	unsigned char data[0x10];
+	int r, r2;
+
+	/* Program Start */
+	r = libusb_bulk_transfer(h, 0x02,
+				 program_start, sizeof(program_start),
+				 &r2, TIMEOUT);
+	if (r)
+		return -1;
+
+	/* Command Response */
+	r = libusb_bulk_transfer(h, 0x81,
+				 data, sizeof(data),
+				 &r2, TIMEOUT);
+	if (r || (r2 != 3) || (data[0] != 0xb0) || (data[1] != 1)
+	    || (data[2] != 0)) {
+		fprintf(stderr, "Unexpected Program Start Response 0x%02x\n",
+		       data[2]);
+		return -1;
+	}
+	return 0;
+}
+
+static int do_download(libusb_device_handle *h,
+		       int my_chunksize,
+		       unsigned int base_addr,
+		       unsigned char *buf, int no_bytes)
+{
+	unsigned char data[MAX_CHUNK + 7];
+	int k,  r, r2;
+	unsigned int crc, allcrc;
+	int bytes_to_go;
+	int seq;
+	int chunksize;
+
+	allcrc = 0;
+	seq = 0;
+	chunksize = my_chunksize < MAX_CHUNK ? my_chunksize : MAX_CHUNK;
+
+	bytes_to_go = (no_bytes + chunksize - 1) & ~(chunksize - 1);
+
+	/* Download Start */
+	data[0] = 0xa2;
+	data[1] = 0x08;
+	data[2] = (base_addr >> 24) & 0xff;
+	data[3] = (base_addr >> 16) & 0xff;
+	data[4] = (base_addr >> 8) & 0xff;
+	data[5] = base_addr & 0xff;
+	data[6] = (bytes_to_go >> 24) & 0xff;
+	data[7] = (bytes_to_go >> 16) & 0xff;
+	data[8] = (bytes_to_go >> 8) & 0xff;
+	data[9] = bytes_to_go & 0xff;
+	r = libusb_bulk_transfer(h, 0x02,
+				 data, 10,
+				 &r2, TIMEOUT);
+	if (r)
+		return -1;
+
+	/* Download Response */
+	r = libusb_bulk_transfer(h, 0x81,
+				 data, sizeof(data),
+				 &r2, TIMEOUT);
+	if (r || (r2 != 4) || (data[0] != 0xb1))
+		return -1;
+
+	if (data[2] || data[3]) {
+		fprintf(stderr, "Download Start Response is 0x%02x 0x%02x\n",
+		       data[2], data[3]);
+		return -1;
+	}
+
+	/* Status Request + Status Response */
+	status_request_response(h);
+
+	while (bytes_to_go > 0) {
+
+		k = chunksize + 4;
+
+		/* Download Data */
+		data[0] = 0xa3;
+		data[1] = (k >> 8) & 0xff;
+		data[2] = k & 0xff;
+		data[3] = (seq >> 8) & 0xff;
+		data[4] = seq & 0xff;
+
+		crc = 0;
+		for (k = 0; k < chunksize; k++) {
+			crc += buf[k];
+			allcrc += buf[k];
+			data[5 + k] = buf[k];
+		}
+		crc = ~(crc - 1);
+
+		data[chunksize + 5] = (crc >> 8) & 0xff;
+		data[chunksize + 6] = crc & 0xff;
+		r = libusb_bulk_transfer(h, 0x02,
+					 data, chunksize + 7,
+					 &r2, TIMEOUT);
+		if (r)
+			return -1;
+
+		/* Download Response */
+		r = libusb_bulk_transfer(h, 0x81,
+					 data, sizeof(data),
+					 &r2, TIMEOUT);
+		if (r || (r2 != 4) || (data[0] != 0xb1))
+			return -1;
+
+		if (data[2] || data[3]) {
+			fprintf(stderr, "Download Response 0x%02x 0x%02x\n",
+			       data[2], data[3]);
+			return -1;
+		}
+
+		/* Status Request + Status Response */
+		status_request_response(h);
+
+		/* process next chunk */
+		buf += chunksize;
+		bytes_to_go -= chunksize;
+		seq++;
+	}
+
+	/* Download End Confirm */
+	data[0] = 0xa4;
+	data[1] = 0x02;
+
+	allcrc = ~(allcrc - 1);
+	data[2] = (allcrc >> 8) & 0xff;
+	data[3] = allcrc & 0xff;
+
+	r = libusb_bulk_transfer(h, 0x02,
+				 data, 4,
+				 &r2, TIMEOUT);
+	if (r)
+		return -1;
+
+	/* Download End Status */
+	r = libusb_bulk_transfer(h, 0x81,
+				 data, sizeof(data),
+				 &r2, TIMEOUT);
+	if (r || (r2 != 5) || (data[0] != 0xb2))
+		return -1;
+
+	if (data[2]) {
+		fprintf(stderr, "Download End Status is 0x%02x 0x%02x 0x%02x\n",
+		       data[2], data[3], data[4]);
+		return -1;
+	}
+
+	return 0;
+}
+
+/* VRL5 header */
+static unsigned char vrl5_header_file_data[340] = {
+	0x10, 0xf0, 0x9f, 0xe5,
+	0x56, 0x00, 0x00, 0x00,
+	0x20, 0x41, 0x35, 0x47,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00,
+	0x01, 0x00, 0x00, 0x00,
+};
+
+/* VRL4 header */
+static unsigned char vrl4_header_file_data[340] = {
+	0x00, 0x00, 0x00, 0xea,
+	0x56, 0x00, 0x00, 0x00,
+	0x08, 0xf0, 0x9f, 0xe5,
+	0x00, 0x00, 0x00, 0x00,
+	0x00, 0x00,
+	0x00, 0x00,
+	0x01, 0x00, 0x00, 0x00,
+};
+
+static int vrl_header(libusb_device_handle *h,
+		      struct device_info *di,
+		      unsigned int start_addr)
+{
+	unsigned char header_buf[340];
+
+	switch (di->format) {
+	case FORMAT_VRL4:
+		memcpy(header_buf, vrl4_header_file_data, sizeof(header_buf));
+		break;
+	case FORMAT_VRL5:
+		memcpy(header_buf, vrl5_header_file_data, sizeof(header_buf));
+		break;
+	default:
+		return -1;
+	}
+
+	/* Fill in start address */
+	header_buf[24] = start_addr & 0xff;
+	header_buf[25] = (start_addr >> 8) & 0xff;
+	header_buf[26] = (start_addr >> 16) & 0xff;
+	header_buf[27] = (start_addr >> 24) & 0xff;
+
+	/* Download VRL header for file at 0xe55b0000 */
+	return do_download(h, di->small_chunksize,
+			   0xe55b0000, header_buf, sizeof(header_buf));
+}
+
+static int run_mover_code(libusb_device_handle *h,
+			  struct device_info *di,
+			  unsigned int src_addr,
+			  unsigned int dst_addr,
+			  unsigned int no_bytes)
+{
+	int ret;
+
+	printf("Moving 0x%08x bytes from 0x%08x to 0x%08x\n",
+	       no_bytes, src_addr, dst_addr);
+
+	mover_code_data[4] = src_addr & 0xff;
+	mover_code_data[5] = (src_addr >> 8) & 0xff;
+	mover_code_data[6] = (src_addr >> 16) & 0xff;
+	mover_code_data[7] = (src_addr >> 24) & 0xff;
+	mover_code_data[8] = dst_addr & 0xff;
+	mover_code_data[9] = (dst_addr >> 8) & 0xff;
+	mover_code_data[10] = (dst_addr >> 16) & 0xff;
+	mover_code_data[11] = (dst_addr >> 24) & 0xff;
+	mover_code_data[12] = no_bytes & 0xff;
+	mover_code_data[13] = (no_bytes >> 8) & 0xff;
+	mover_code_data[14] = (no_bytes >> 16) & 0xff;
+	mover_code_data[15] = (no_bytes >> 24) & 0xff;
+
+	/* Begin loading the headerless code */
+	do_download(h, di->small_chunksize, 0xe55b8000,
+		    mover_code_data, sizeof(mover_code_data));
+
+	/* Download VRL header pointing to 0xe55b8000 */
+	vrl_header(h, di, 0xe55b8000);
+
+	/* Start the mover code */
+	ret = do_start(h);
+
+	/* Check that the code made it back:
+	 * Status Request + Status Response */
+	if (ret = 0)
+		status_request_response(h);
+
+	return ret;
+}
+
+static int rolf(libusb_device_handle *h,
+		struct device_info *di,
+		char *filename)
+{
+	unsigned char file_buf[HUGECHUNK];
+	int k, ret;
+	int fd, filesize;
+
+	fd = open(filename, O_RDONLY);
+	if (fd < 0) {
+		fprintf(stderr, "unable to open file.\n");
+		return -1;
+	}
+
+	filesize = lseek(fd, 0, SEEK_END);
+	if (filesize = -1) {
+		fprintf(stderr, "unable to seek file.\n");
+		return -1;
+	}
+
+	if (filesize > MAX_FILESIZE) {
+		fprintf(stderr, "filesize %d exceed maximum supported %d.\n",
+			filesize, MAX_FILESIZE);
+		return -1;
+	}
+
+	if (setup_clocks(h) != 0)
+		return -1;
+
+	/* split file in HUGCHUNK size blocks - download top first */
+	for (k = ((filesize + HUGECHUNK - 1) / HUGECHUNK) - 1; k >= 0; k--) {
+
+		printf("Loading 0x%08x bytes to 0x%08x\n",
+		       HUGECHUNK, 0xe55c0000);
+
+		lseek(fd, HUGECHUNK * k, SEEK_SET);
+		read(fd, file_buf, HUGECHUNK);
+
+		/* Download file data chunk */
+		do_download(h, di->large_chunksize,
+			    0xe55c0000, file_buf, HUGECHUNK);
+
+		/* need to move all file data except first block */
+		if (k) {
+			ret = run_mover_code(h, di, 0xe55c0000,
+					     0xe55c0000 + (HUGECHUNK * k),
+					     HUGECHUNK);
+			if (ret != 0)
+				return ret;
+		}
+	}
+
+	/* Download VRL header pointing to 0xe55c0000 */
+	vrl_header(h, di, 0xe55c0000);
+
+	printf("Starting program at 0x%08x\n", 0xe55c0000);
+
+	/* Start the data in the file */
+	do_start(h);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct libusb_device_handle *h;
+	int k;
+	int ret;
+
+	ret = libusb_init(NULL);
+	if (ret < 0) {
+		fprintf(stderr, "Unable to init libusb\n");
+		exit(1);
+	}
+
+	for (k = 0;
+	     k < sizeof(supported_devices) / sizeof(supported_devices[0]);
+	     k++) {
+
+		h = libusb_open_device_with_vid_pid(NULL,
+						    supported_devices[k].vid,
+						    supported_devices[k].pid);
+
+		if (h) {
+			printf("Detected %s\n",
+			       supported_devices[k].name);
+			break;
+		}
+	}
+
+	if (!h) {
+		fprintf(stderr, "Unable to locate device\n");
+		goto out;
+	}
+
+	/* ttyACM is assigned by default, disconnect that driver */
+	libusb_detach_kernel_driver(h, 0);
+
+	ret = libusb_claim_interface(h, 1);
+	if (ret < 0) {
+		fprintf(stderr, "Unable to claim interface\n");
+		goto out;
+	}
+
+	if (get_hw_version(h) != 0)
+		goto out2;
+
+	if (argc > 1)
+		rolf(h, &supported_devices[k], argv[1]);
+
+ out2:
+	libusb_release_interface(h, 0);
+ out:
+	libusb_close(h);
+	libusb_exit(NULL);
+
+	return ret >= 0 ? 0 : 1;
+}

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox