Linux Power Management development
 help / color / mirror / Atom feed
* Re: [PATCH 3/6] OMAP: PM: register to the per-device PM QoS framework
From: Kevin Hilman @ 2011-11-17 21:24 UTC (permalink / raw)
  To: jean.pihet; +Cc: linux-omap, Linux PM mailing list, Jean Pihet
In-Reply-To: <1319032263-22699-4-git-send-email-j-pihet@ti.com>

jean.pihet@newoldbits.com writes:

> From: Jean Pihet <j-pihet@ti.com>
>
> Implement the devices wake-up latency constraints using the global
> device PM QoS notification handler which applies the constraints to the
> underlying layer by calling the corresponding function at hwmod level.
>
> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using wake-up
> latency constraints on MPU, CORE and PER.
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> ---
>  arch/arm/mach-omap2/pm.c |   63 ++++++++++++++++++++++++++++++++++++++++++++++
>  1 files changed, 63 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
> index 3feb359..58b4b76 100644
> --- a/arch/arm/mach-omap2/pm.c
> +++ b/arch/arm/mach-omap2/pm.c
> @@ -11,13 +11,16 @@
>  
>  #include <linux/kernel.h>
>  #include <linux/init.h>
> +#include <linux/notifier.h>
>  #include <linux/io.h>
>  #include <linux/err.h>
>  #include <linux/opp.h>
> +#include <linux/pm_qos.h>
>  
>  #include <plat/omap-pm.h>
>  #include <plat/omap_device.h>
>  #include <plat/common.h>
> +#include <plat/omap_hwmod.h>
>  
>  #include "voltage.h"
>  #include "powerdomain.h"
> @@ -242,11 +245,71 @@ static void __init omap4_init_voltages(void)
>  	omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", iva_dev);
>  }
>  
> +/* Interface to the per-device PM QoS framework */
> +static int omap2_dev_pm_qos_handler(struct notifier_block *nb,
> +				    unsigned long new_value,
> +				    void *req)
> +{
> +	struct omap_device *od;
> +	struct omap_hwmod *oh;
> +	struct platform_device *pdev;
> +	struct dev_pm_qos_request *dev_pm_qos_req = req;
> +
> +	pr_debug("OMAP PM CONSTRAINTS: req@0x%p, new_value=%lu\n",

s/CONSTRAINTS/constraints/
another one below.

> +		 req, new_value);
> +
> +	/* Look for the platform device for the constraint target device */
> +	pdev = to_platform_device(dev_pm_qos_req->dev);
> +
> +	/* Try to catch non platform devices */

why?

> +	if (pdev->name == NULL) {
> +		pr_err("%s: Error: platform device for device %s not valid\n",
> +		       __func__, dev_name(dev_pm_qos_req->dev));
> +		return -EINVAL;
> +	}
> +
> +	/* Find the associated omap_device for dev */
> +	od = container_of(pdev, struct omap_device, pdev);

What about devices that are valid platform_devices, but not omap_devices?

> +	if (od->hwmods_cnt != 1) {
> +		pr_err("%s: Error: No unique hwmod for device %s\n",
> +		       __func__, dev_name(dev_pm_qos_req->dev));
> +		return -EINVAL;
> +	}
> +
> +	/* Find the primary omap_hwmod for dev */
> +	oh = od->hwmods[0];
> +
> +	pr_debug("OMAP PM CONSTRAINTS: req@0x%p, dev=0x%p, new_value=%lu\n",
> +		 req, dev_pm_qos_req->dev, new_value);
> +
> +	/* Apply the constraint */
> +	return omap_hwmod_set_wkup_lat_constraint(oh, dev_pm_qos_req,
> +						  new_value);
> +}
> +
> +static struct notifier_block omap2_dev_pm_qos_notifier = {
> +	.notifier_call	= omap2_dev_pm_qos_handler,
> +};
> +
> +static int __init omap2_dev_pm_qos_init(void)
> +{
> +	int ret;
> +
> +	ret = dev_pm_qos_add_global_notifier(&omap2_dev_pm_qos_notifier);
> +	if (ret)
> +		WARN(1, KERN_ERR "Cannot add global notifier for dev PM QoS\n");

minor: could use WARN_ON()

> +	return ret;
> +}
> +
>  static int __init omap2_common_pm_init(void)
>  {
>  	omap2_init_processor_devices();
>  	omap_pm_if_init();
>  
> +	/* Register to the per-device PM QoS framework */
> +	omap2_dev_pm_qos_init();
> +
>  	return 0;
>  }
>  postcore_initcall(omap2_common_pm_init);

Kevin

^ permalink raw reply

* Re: [PATCH 1/6] OMAP2+: powerdomain: control power domains next state
From: Kevin Hilman @ 2011-11-17 21:16 UTC (permalink / raw)
  To: jean.pihet; +Cc: linux-omap, Linux PM mailing list, Jean Pihet
In-Reply-To: <1319032263-22699-2-git-send-email-j-pihet@ti.com>

jean.pihet@newoldbits.com writes:

> From: Jean Pihet <j-pihet@ti.com>
>
> When a PM QoS device latency constraint is requested or removed the
> PM QoS layer notifies the underlying layer with the updated aggregated
> constraint value. The constraint is stored in the powerdomain constraints
> list and then applied to the corresponding power domain.
> The power domains get the next power state programmed directly in the
> registers via pwrdm_wakeuplat_update_pwrst.
>
> Tested on OMAP3 Beagleboard and OMAP4 Pandaboard in RET/OFF using
> wake-up latency constraints on MPU, CORE and PER.
>
> Signed-off-by: Jean Pihet <j-pihet@ti.com>
> ---
>  arch/arm/mach-omap2/powerdomain.c |  237 +++++++++++++++++++++++++++++++++++++
>  arch/arm/mach-omap2/powerdomain.h |   35 +++++-
>  2 files changed, 270 insertions(+), 2 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
> index 9af0847..351766d 100644
> --- a/arch/arm/mach-omap2/powerdomain.c
> +++ b/arch/arm/mach-omap2/powerdomain.c
> @@ -17,8 +17,10 @@
>  #include <linux/kernel.h>
>  #include <linux/types.h>
>  #include <linux/list.h>
> +#include <linux/slab.h>
>  #include <linux/errno.h>
>  #include <linux/string.h>
> +#include <linux/pm_qos.h>
>  #include <trace/events/power.h>
>  
>  #include "cm2xxx_3xxx.h"
> @@ -104,6 +106,12 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
>  	for (i = 0; i < pwrdm->banks; i++)
>  		pwrdm->ret_mem_off_counter[i] = 0;
>  
> +	/* Initialize the per-od wake-up constraints data */

This comment needs an update (they are not per-od, but per pwrdm), or
could probably be removed, since it doesn't add any value over the code.

> +	spin_lock_init(&pwrdm->wkup_lat_plist_lock);
> +	plist_head_init(&pwrdm->wkup_lat_plist_head);
> +	pwrdm->wkup_lat_next_state = PWRDM_POWER_OFF;
> +
> +	/* Initialize the pwrdm state */
>  	pwrdm_wait_transition(pwrdm);
>  	pwrdm->state = pwrdm_read_pwrst(pwrdm);
>  	pwrdm->state_counter[pwrdm->state] = 1;
> @@ -191,6 +199,158 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
>  	return 0;
>  }
>  
> +/**
> + * _pwrdm_wakeuplat_update_list - Set/update/remove a powerdomain wakeup
> + *  latency constraint from the pwrdm's constraint list
> + * @pwrdm: struct powerdomain * which the constraint applies to
> + * @cookie: constraint identifier, used for tracking.
> + * @min_latency: minimum wakeup latency constraint (in microseconds) for
> + *  the given pwrdm. The value of PM_QOS_DEV_LAT_DEFAULT_VALUE removes
> + *  the constraint.
> + * @user: pointer to the current list entry
> + * @new_user: allocated list entry, used for insertion of new constraints
> + *  in the list
> + * @free_new_user: set to non-zero if the newly allocated list entry
> + *  is unused and needs to be freed
> + * @free_node: set to non-zero if the current list entry is not in use
> + *  anymore and needs to be freed
> + *
> + * Tracks the constraints by @cookie.
> + * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
> + * constraint list.
> + * If the constraint identifier already exists in the list, the old value is
> + * overwritten.
> + * Constraint removal: Removes the identifier's entry from powerdomain's
> + * wakeup latency constraint list.
> + *
> + * Called with the pwrdm wakeup latency spinlock held.
> + *
> + * Returns 0 upon success, -EINVAL if the constraint is not existing.
> + */
> +static inline int _pwrdm_update_wakeuplat_list(
> +			struct powerdomain *pwrdm,
> +			void *cookie,
> +			long min_latency,
> +			struct pwrdm_wkup_constraints_entry *user,
> +			struct pwrdm_wkup_constraints_entry *new_user,
> +			int *free_new_user,
> +			int *free_node)
> +{
> +	struct pwrdm_wkup_constraints_entry *tmp_user;
> +
> +	/* Check if there already is a constraint for cookie */
> +	plist_for_each_entry(tmp_user, &pwrdm->wkup_lat_plist_head, node) {
> +		if (tmp_user->cookie == cookie) {
> +			user = tmp_user;
> +			break;
> +		}
> +	}
> +
> +	if (min_latency != PM_QOS_DEV_LAT_DEFAULT_VALUE) {
> +		/* If nothing to update, job done */
> +		if (user && (user->node.prio == min_latency))
> +			return 0;
> +
> +		if (!user) {
> +			/* Add new entry to the list */
> +			user = new_user;
> +			user->cookie = cookie;
> +			*free_new_user = 0;
> +		} else {
> +			/* Update existing entry */
> +			plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
> +		}
> +
> +		plist_node_init(&user->node, min_latency);
> +		plist_add(&user->node, &pwrdm->wkup_lat_plist_head);
> +	} else {
> +		if (user) {
> +			/* Remove the constraint from the list */
> +			plist_del(&user->node, &pwrdm->wkup_lat_plist_head);
> +			*free_node = 1;
> +		} else {
> +			/* Constraint not existing or list empty, do nothing */
> +			return -EINVAL;
> +		}
> +
> +	}
> +
> +	return 0;
> +}

[...]

>  /**
> + * pwrdm_set_wkup_lat_constraint - Set/update/remove a powerdomain wakeup
> + *  latency constraint and apply it
> + * @pwrdm: struct powerdomain * which the constraint applies to
> + * @cookie: constraint identifier, used for tracking.
> + * @min_latency: minimum wakeup latency constraint (in microseconds) for
> + *  the given pwrdm. The value of PM_QOS_DEV_LAT_DEFAULT_VALUE removes
> + *  the constraint.
> + *
> + * Tracks the constraints by @cookie.
> + * Constraint set/update: Adds a new entry to powerdomain's wake-up latency
> + * constraint list.
> + * If the constraint identifier already exists in the list, the old value is
> + * overwritten.
> + * Constraint removal: Removes the identifier's entry from powerdomain's
> + * wakeup latency constraint list.
> + *
> + * Applies the aggregated constraint value for the given pwrdm by calling
> + * _pwrdm_wakeuplat_update_pwrst.
> + *
> + * Returns 0 upon success, -ENOMEM in case of memory shortage, -EINVAL in
> + * case of invalid parameters, or the return value from
> + * _pwrdm_wakeuplat_update_pwrst.
> + *
> + * The caller must check the validity of the parameters.
> + */
> +int pwrdm_set_wkup_lat_constraint(struct powerdomain *pwrdm, void *cookie,
> +				  long min_latency)
> +{
> +	struct pwrdm_wkup_constraints_entry *user = NULL, *new_user = NULL;
> +	int ret = 0, free_new_user = 0, free_node = 0;
> +	long value = PM_QOS_DEV_LAT_DEFAULT_VALUE;
> +	unsigned long flags;
> +
> +	pr_debug("powerdomain: %s: pwrdm %s, cookie=0x%p, min_latency=%ld\n",
> +		 __func__, pwrdm->name, cookie, min_latency);
> +
> +	if (min_latency != PM_QOS_DEV_LAT_DEFAULT_VALUE) {
> +		new_user = kzalloc(sizeof(struct pwrdm_wkup_constraints_entry),
> +				   GFP_KERNEL);
> +		if (!new_user) {
> +			pr_err("%s: FATAL ERROR: kzalloc failed\n", __func__);
> +			return -ENOMEM;
> +		}
> +		free_new_user = 1;
> +	}
> +
> +	spin_lock_irqsave(&pwrdm->wkup_lat_plist_lock, flags);
> +
> +	/* Manage the constraints list */
> +	ret = _pwrdm_update_wakeuplat_list(pwrdm, cookie, min_latency,
> +					   user, new_user,
> +					   &free_new_user, &free_node);
> +
> +	/* Find the aggregated constraint value from the list */
> +	if (!ret)
> +		if (!plist_head_empty(&pwrdm->wkup_lat_plist_head))
> +			value = plist_first(&pwrdm->wkup_lat_plist_head)->prio;
> +
> +	spin_unlock_irqrestore(&pwrdm->wkup_lat_plist_lock, flags);
> +
> +	if (free_node)
> +		kfree(user);
> +
> +	if (free_new_user)
> +		kfree(new_user);

The alloc/free of these buffers is not terribly obvious when reading.  I
think the code/changelog needs some comments describing the logic
behind how/when these nodes are allocated and freed.

> +	/* Apply the constraint to the pwrdm */
> +	if (!ret) {
> +		pr_debug("powerdomain: %s: pwrdm %s, value=%ld\n",
> +			 __func__, pwrdm->name, value);
> +		ret = _pwrdm_wakeuplat_update_pwrst(pwrdm, value);
> +	}
> +
> +	return ret;
> +}
> +

Kevin

^ permalink raw reply

* [RFC PATCH v2 4/4] cpuidle: (POWER) Handle power_save=off
From: Deepthi Dharwar @ 2011-11-17 11:29 UTC (permalink / raw)
  To: linuxppc-dev, linux-pm, linux-pm; +Cc: linux-kernel
In-Reply-To: <20111117112815.9191.2322.stgit@localhost6.localdomain6>

This patch makes pseries_idle_driver not to be registered when
power_save=off kernel boot option is specified. The
boot_option_idle_override variable used here is similar to
its usage on x86.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
 arch/powerpc/include/asm/processor.h            |    1 +
 arch/powerpc/platforms/pseries/processor_idle.c |    4 ++++
 2 files changed, 5 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 811b7e7..b286fb7 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -382,6 +382,7 @@ static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
 }
 #endif
 
+extern unsigned long  boot_option_idle_override;
 enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
 
 #endif /* __KERNEL__ */
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index b5addd7..5f74b4e 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -260,6 +260,10 @@ static int pseries_idle_probe(void)
 		return -EPERM;
 	}
 
+	if (boot_option_idle_override != IDLE_NO_OVERRIDE) {
+		return -ENODEV;
+	}
+
 	if (!firmware_has_feature(FW_FEATURE_SPLPAR)) {
 		printk(KERN_DEBUG "Using default idle\n");
 		return -ENODEV;

^ permalink raw reply related

* [RFC PATCH v2 3/4] cpuidle: (POWER) Enable cpuidle and directly call cpuidle_idle_call() for pSeries
From: Deepthi Dharwar @ 2011-11-17 11:28 UTC (permalink / raw)
  To: linuxppc-dev, linux-pm, linux-pm; +Cc: linux-kernel
In-Reply-To: <20111117112815.9191.2322.stgit@localhost6.localdomain6>

This patch enables cpuidle for pSeries and cpuidle_idle_call() is
directly called from the idle loop. As a result pseries_idle cpuidle
driver registered with cpuidle subsystem comes into action. This patch
also removes the routines pseries_shared_idle_sleep and
pseries_dedicated_idle_sleep as they are now implemented as part of
pseries_idle cpuidle driver.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
 arch/powerpc/platforms/Kconfig         |    6 ++
 arch/powerpc/platforms/pseries/setup.c |   86 +-------------------------------
 include/linux/cpuidle.h                |    2 -
 3 files changed, 8 insertions(+), 86 deletions(-)

diff --git a/arch/powerpc/platforms/Kconfig b/arch/powerpc/platforms/Kconfig
index e458872..0d2a028 100644
--- a/arch/powerpc/platforms/Kconfig
+++ b/arch/powerpc/platforms/Kconfig
@@ -211,6 +211,12 @@ config PPC_PASEMI_CPUFREQ
 
 endmenu
 
+menu "CPUIdle driver"
+
+source "drivers/cpuidle/Kconfig"
+
+endmenu
+
 config PPC601_SYNC_FIX
 	bool "Workarounds for PPC601 bugs"
 	depends on 6xx && (PPC_PREP || PPC_PMAC)
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 9c6716a..f624e74 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -39,6 +39,7 @@
 #include <linux/irq.h>
 #include <linux/seq_file.h>
 #include <linux/root_dev.h>
+#include <linux/cpuidle.h>
 
 #include <asm/mmu.h>
 #include <asm/processor.h>
@@ -74,9 +75,6 @@ EXPORT_SYMBOL(CMO_PageSize);
 
 int fwnmi_active;  /* TRUE if an FWNMI handler is present */
 
-static void pseries_shared_idle_sleep(void);
-static void pseries_dedicated_idle_sleep(void);
-
 static struct device_node *pSeries_mpic_node;
 
 static void pSeries_show_cpuinfo(struct seq_file *m)
@@ -374,18 +372,9 @@ static void __init pSeries_setup_arch(void)
 
 	pSeries_nvram_init();
 
-	/* Choose an idle loop */
 	if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
 		vpa_init(boot_cpuid);
-		if (get_lppaca()->shared_proc) {
-			printk(KERN_DEBUG "Using shared processor idle loop\n");
-			ppc_md.power_save = pseries_shared_idle_sleep;
-		} else {
-			printk(KERN_DEBUG "Using dedicated idle loop\n");
-			ppc_md.power_save = pseries_dedicated_idle_sleep;
-		}
-	} else {
-		printk(KERN_DEBUG "Using default idle loop\n");
+		ppc_md.power_save = (void *)cpuidle_idle_call;
 	}
 
 	if (firmware_has_feature(FW_FEATURE_LPAR))
@@ -586,77 +575,6 @@ static int __init pSeries_probe(void)
 	return 1;
 }
 
-static void pseries_dedicated_idle_sleep(void)
-{ 
-	unsigned int cpu = smp_processor_id();
-	unsigned long start_snooze;
-	unsigned long in_purr, out_purr;
-	long snooze = __get_cpu_var(smt_snooze_delay);
-
-	/*
-	 * Indicate to the HV that we are idle. Now would be
-	 * a good time to find other work to dispatch.
-	 */
-	get_lppaca()->idle = 1;
-	get_lppaca()->donate_dedicated_cpu = 1;
-	in_purr = mfspr(SPRN_PURR);
-
-	/*
-	 * We come in with interrupts disabled, and need_resched()
-	 * has been checked recently.  If we should poll for a little
-	 * while, do so.
-	 */
-	if (snooze) {
-		start_snooze = get_tb() + snooze * tb_ticks_per_usec;
-		local_irq_enable();
-		set_thread_flag(TIF_POLLING_NRFLAG);
-
-		while ((snooze < 0) || (get_tb() < start_snooze)) {
-			if (need_resched() || cpu_is_offline(cpu))
-				goto out;
-			ppc64_runlatch_off();
-			HMT_low();
-			HMT_very_low();
-		}
-
-		HMT_medium();
-		clear_thread_flag(TIF_POLLING_NRFLAG);
-		smp_mb();
-		local_irq_disable();
-		if (need_resched() || cpu_is_offline(cpu))
-			goto out;
-	}
-
-	cede_processor();
-
-out:
-	HMT_medium();
-	out_purr = mfspr(SPRN_PURR);
-	get_lppaca()->wait_state_cycles += out_purr - in_purr;
-	get_lppaca()->donate_dedicated_cpu = 0;
-	get_lppaca()->idle = 0;
-}
-
-static void pseries_shared_idle_sleep(void)
-{
-	/*
-	 * Indicate to the HV that we are idle. Now would be
-	 * a good time to find other work to dispatch.
-	 */
-	get_lppaca()->idle = 1;
-
-	/*
-	 * Yield the processor to the hypervisor.  We return if
-	 * an external interrupt occurs (which are driven prior
-	 * to returning here) or if a prod occurs from another
-	 * processor. When returning here, external interrupts
-	 * are enabled.
-	 */
-	cede_processor();
-
-	get_lppaca()->idle = 0;
-}
-
 static int pSeries_pci_probe_mode(struct pci_bus *bus)
 {
 	if (firmware_has_feature(FW_FEATURE_LPAR))
diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index 7408af8..23f81de 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -130,7 +130,6 @@ struct cpuidle_driver {
 #ifdef CONFIG_CPU_IDLE
 extern void disable_cpuidle(void);
 extern int cpuidle_idle_call(void);
-
 extern int cpuidle_register_driver(struct cpuidle_driver *drv);
 struct cpuidle_driver *cpuidle_get_driver(void);
 extern void cpuidle_unregister_driver(struct cpuidle_driver *drv);
@@ -145,7 +144,6 @@ extern void cpuidle_disable_device(struct cpuidle_device *dev);
 #else
 static inline void disable_cpuidle(void) { }
 static inline int cpuidle_idle_call(void) { return -ENODEV; }
-
 static inline int cpuidle_register_driver(struct cpuidle_driver *drv)
 {return -ENODEV; }
 static inline struct cpuidle_driver *cpuidle_get_driver(void) {return NULL; }

^ permalink raw reply related

* [RFC PATCH v2 2/4] cpuidle: (POWER) cpuidle driver for pSeries
From: Deepthi Dharwar @ 2011-11-17 11:28 UTC (permalink / raw)
  To: linuxppc-dev, linux-pm, linux-pm; +Cc: linux-kernel
In-Reply-To: <20111117112815.9191.2322.stgit@localhost6.localdomain6>

This patch implements a backhand cpuidle driver for pSeries
based on pseries_dedicated_idle_loop and pseries_shared_idle_loop
routines.  The driver is built only if CONFIG_CPU_IDLE is set. This
cpuidle driver uses global registration of idle states and
not per-cpu.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
 arch/powerpc/include/asm/system.h               |    8 +
 arch/powerpc/kernel/sysfs.c                     |    2 
 arch/powerpc/platforms/pseries/Kconfig          |    9 +
 arch/powerpc/platforms/pseries/Makefile         |    1 
 arch/powerpc/platforms/pseries/processor_idle.c |  317 +++++++++++++++++++++++
 arch/powerpc/platforms/pseries/pseries.h        |    3 
 arch/powerpc/platforms/pseries/setup.c          |    3 
 arch/powerpc/platforms/pseries/smp.c            |    1 
 8 files changed, 341 insertions(+), 3 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/processor_idle.c

diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h
index ff66680..f56a0a7 100644
--- a/arch/powerpc/include/asm/system.h
+++ b/arch/powerpc/include/asm/system.h
@@ -223,6 +223,14 @@ extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask);
 extern int powersave_nap;	/* set if nap mode can be used in idle loop */
 void cpu_idle_wait(void);
 
+#ifdef CONFIG_PSERIES_IDLE
+extern void update_smt_snooze_delay(int snooze);
+extern int pseries_notify_cpuidle_add_cpu(int cpu);
+#else
+static inline void update_smt_snooze_delay(int snooze) {}
+static inline int pseries_notify_cpuidle_add_cpu(int cpu) { return 0; }
+#endif
+
 /*
  * Atomic exchange
  *
diff --git a/arch/powerpc/kernel/sysfs.c b/arch/powerpc/kernel/sysfs.c
index ce035c1..ebe5d78 100644
--- a/arch/powerpc/kernel/sysfs.c
+++ b/arch/powerpc/kernel/sysfs.c
@@ -18,6 +18,7 @@
 #include <asm/machdep.h>
 #include <asm/smp.h>
 #include <asm/pmc.h>
+#include <asm/system.h>
 
 #include "cacheinfo.h"
 
@@ -51,6 +52,7 @@ static ssize_t store_smt_snooze_delay(struct sys_device *dev,
 		return -EINVAL;
 
 	per_cpu(smt_snooze_delay, cpu->sysdev.id) = snooze;
+	update_smt_snooze_delay(snooze);
 
 	return count;
 }
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index c81f6bb..ae7b6d4 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -120,3 +120,12 @@ config DTL
 	  which are accessible through a debugfs file.
 
 	  Say N if you are unsure.
+
+config PSERIES_IDLE
+	tristate "Cpuidle driver for pSeries platforms"
+	depends on CPU_IDLE
+	depends on PPC_PSERIES
+	default y
+	help
+	  Select this option to enable processor idle state management
+	  through cpuidle subsystem.
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 3556e40..236db46 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_PHYP_DUMP)		+= phyp_dump.o
 obj-$(CONFIG_CMM)		+= cmm.o
 obj-$(CONFIG_DTL)		+= dtl.o
 obj-$(CONFIG_IO_EVENT_IRQ)	+= io_event_irq.o
+obj-$(CONFIG_PSERIES_IDLE)	+= processor_idle.o
 
 ifeq ($(CONFIG_PPC_PSERIES),y)
 obj-$(CONFIG_SUSPEND)		+= suspend.o
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
new file mode 100644
index 0000000..b5addd7
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -0,0 +1,317 @@
+/*
+ *  processor_idle - idle state cpuidle driver.
+ *  Adapted from drivers/idle/intel_idle.c and
+ *  drivers/acpi/processor_idle.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu.h>
+
+#include <asm/paca.h>
+#include <asm/reg.h>
+#include <asm/system.h>
+#include <asm/machdep.h>
+#include <asm/firmware.h>
+
+#include "plpar_wrappers.h"
+#include "pseries.h"
+
+struct cpuidle_driver pseries_idle_driver = {
+	.name =		"pseries_idle",
+	.owner =	THIS_MODULE,
+};
+
+#define MAX_IDLE_STATE_COUNT	2
+
+static int max_cstate = MAX_IDLE_STATE_COUNT - 1;
+static struct cpuidle_device __percpu *pseries_idle_cpuidle_devices;
+static struct cpuidle_state *cpuidle_state_table;
+
+void update_smt_snooze_delay(int snooze)
+{
+	struct cpuidle_driver *drv = cpuidle_get_driver();
+	if (drv)
+		drv->states[0].target_residency = snooze;
+}
+
+static inline void idle_loop_prolog(unsigned long *in_purr, ktime_t *kt_before)
+{
+
+	*kt_before = ktime_get_real();
+	*in_purr = mfspr(SPRN_PURR);
+	/*
+	 * Indicate to the HV that we are idle. Now would be
+	 * a good time to find other work to dispatch.
+	 */
+	get_lppaca()->idle = 1;
+	get_lppaca()->donate_dedicated_cpu = 1;
+}
+
+static inline  s64 idle_loop_epilog(unsigned long in_purr, ktime_t kt_before)
+{
+	get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
+	get_lppaca()->donate_dedicated_cpu = 0;
+	get_lppaca()->idle = 0;
+
+	return ktime_to_us(ktime_sub(ktime_get_real(), kt_before));
+}
+
+
+static int snooze_loop(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv,
+			int index)
+{
+	unsigned long in_purr;
+	ktime_t kt_before;
+
+	idle_loop_prolog(&in_purr, &kt_before);
+
+	local_irq_enable();
+	set_thread_flag(TIF_POLLING_NRFLAG);
+	while (!need_resched()) {
+		ppc64_runlatch_off();
+		HMT_low();
+		HMT_very_low();
+	}
+	HMT_medium();
+	clear_thread_flag(TIF_POLLING_NRFLAG);
+	smp_mb();
+	local_irq_disable();
+
+	dev->last_residency =
+		(int)idle_loop_epilog(in_purr, kt_before);
+	return index;
+}
+
+static int dedicated_cede_loop(struct cpuidle_device *dev,
+				struct cpuidle_driver *drv,
+				int index)
+{
+	unsigned long in_purr;
+	ktime_t kt_before;
+
+	idle_loop_prolog(&in_purr, &kt_before);
+
+	ppc64_runlatch_off();
+	HMT_medium();
+	cede_processor();
+
+	dev->last_residency =
+		(int)idle_loop_epilog(in_purr, kt_before);
+	return index;
+}
+
+static int shared_cede_loop(struct cpuidle_device *dev,
+			struct cpuidle_driver *drv,
+			int index)
+{
+	unsigned long in_purr;
+	ktime_t kt_before;
+
+	idle_loop_prolog(&in_purr, &kt_before);
+
+	/*
+	 * Yield the processor to the hypervisor.  We return if
+	 * an external interrupt occurs (which are driven prior
+	 * to returning here) or if a prod occurs from another
+	 * processor. When returning here, external interrupts
+	 * are enabled.
+	 */
+	cede_processor();
+
+	dev->last_residency =
+		(int)idle_loop_epilog(in_purr, kt_before);
+	return index;
+}
+
+/*
+ * States for dedicated partition case.
+ */
+static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
+	{ /* Snooze */
+		.name = "snooze",
+		.desc = "snooze",
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.exit_latency = 0,
+		.target_residency = 0,
+		.enter = &snooze_loop },
+	{ /* CEDE */
+		.name = "CEDE",
+		.desc = "CEDE",
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.exit_latency = 1,
+		.target_residency = 10,
+		.enter = &dedicated_cede_loop },
+};
+
+/*
+ * States for shared partition case.
+ */
+static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
+	{ /* Shared Cede */
+		.name = "Shared Cede",
+		.desc = "Shared Cede",
+		.flags = CPUIDLE_FLAG_TIME_VALID,
+		.exit_latency = 0,
+		.target_residency = 0,
+		.enter = &shared_cede_loop },
+};
+
+int pseries_notify_cpuidle_add_cpu(int cpu)
+{
+	struct cpuidle_device *dev =
+			per_cpu_ptr(pseries_idle_cpuidle_devices, cpu);
+	if (dev && cpuidle_get_driver()) {
+		cpuidle_disable_device(dev);
+		cpuidle_enable_device(dev);
+	}
+	return 0;
+}
+
+/*
+ * pseries_idle_cpuidle_driver_init()
+ */
+static int pseries_idle_cpuidle_driver_init(void)
+{
+	int cstate;
+	struct cpuidle_driver *drv = &pseries_idle_driver;
+
+	drv->state_count = 0;
+
+	for (cstate = 0; cstate < MAX_IDLE_STATE_COUNT; ++cstate) {
+
+		if (cstate > max_cstate)
+			break;
+
+		/* is the state not enabled? */
+		if (cpuidle_state_table[cstate].enter == NULL)
+			continue;
+
+		drv->states[drv->state_count] =	/* structure copy */
+			cpuidle_state_table[cstate];
+
+		if (cpuidle_state_table == dedicated_states)
+			drv->states[drv->state_count].target_residency =
+				__get_cpu_var(smt_snooze_delay);
+
+		drv->state_count += 1;
+	}
+
+	return 0;
+}
+
+/* pseries_idle_devices_uninit(void)
+ * unregister cpuidle devices and de-allocate memory
+ */
+static void pseries_idle_devices_uninit(void)
+{
+	int i;
+	struct cpuidle_device *dev;
+
+	for_each_possible_cpu(i) {
+		dev = per_cpu_ptr(pseries_idle_cpuidle_devices, i);
+		cpuidle_unregister_device(dev);
+	}
+
+	free_percpu(pseries_idle_cpuidle_devices);
+	return;
+}
+
+/* pseries_idle_devices_init()
+ * allocate, initialize and register cpuidle device
+ */
+static int pseries_idle_devices_init(void)
+{
+	int i;
+	struct cpuidle_driver *drv = &pseries_idle_driver;
+	struct cpuidle_device *dev;
+
+	pseries_idle_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+	if (pseries_idle_cpuidle_devices == NULL)
+		return -ENOMEM;
+
+	for_each_possible_cpu(i) {
+		dev = per_cpu_ptr(pseries_idle_cpuidle_devices, i);
+		dev->state_count = drv->state_count;
+		dev->cpu = i;
+		if (cpuidle_register_device(dev)) {
+			printk(KERN_DEBUG \
+				"cpuidle_register_device %d failed!\n", i);
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * pseries_idle_probe()
+ * Choose state table for shared versus dedicated partition
+ */
+static int pseries_idle_probe(void)
+{
+	if (max_cstate == 0) {
+		printk(KERN_DEBUG "pseries processor idle disabled.\n");
+		return -EPERM;
+	}
+
+	if (!firmware_has_feature(FW_FEATURE_SPLPAR)) {
+		printk(KERN_DEBUG "Using default idle\n");
+		return -ENODEV;
+	}
+
+	if (get_lppaca()->shared_proc)
+		cpuidle_state_table = shared_states;
+	else
+		cpuidle_state_table = dedicated_states;
+
+	return 0;
+}
+
+static int __init pseries_processor_idle_init(void)
+{
+	int retval;
+
+	retval = pseries_idle_probe();
+	if (retval)
+		return retval;
+
+	pseries_idle_cpuidle_driver_init();
+	retval = cpuidle_register_driver(&pseries_idle_driver);
+	if (retval) {
+		printk(KERN_DEBUG "Registration of pseries driver failed.\n");
+		return retval;
+	}
+
+	retval = pseries_idle_devices_init();
+	if (retval) {
+		pseries_idle_devices_uninit();
+		cpuidle_unregister_driver(&pseries_idle_driver);
+		return retval;
+	}
+
+	printk(KERN_DEBUG "pseries_idle_driver registered\n");
+
+	return 0;
+}
+
+static void __exit pseries_processor_idle_exit(void)
+{
+
+	pseries_idle_devices_uninit();
+	cpuidle_unregister_driver(&pseries_idle_driver);
+
+	return;
+}
+
+module_init(pseries_processor_idle_init);
+module_exit(pseries_processor_idle_exit);
+
+MODULE_AUTHOR("Deepthi Dharwar <deepthi@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("Cpuidle driver for POWER");
+MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index 24c7162..9a3dda0 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -57,4 +57,7 @@ extern struct device_node *dlpar_configure_connector(u32);
 extern int dlpar_attach_node(struct device_node *);
 extern int dlpar_detach_node(struct device_node *);
 
+/* Snooze Delay, pseries_idle */
+DECLARE_PER_CPU(long, smt_snooze_delay);
+
 #endif /* _PSERIES_PSERIES_H */
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c3408ca..9c6716a 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -586,9 +586,6 @@ static int __init pSeries_probe(void)
 	return 1;
 }
 
-
-DECLARE_PER_CPU(long, smt_snooze_delay);
-
 static void pseries_dedicated_idle_sleep(void)
 { 
 	unsigned int cpu = smp_processor_id();
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index 26e93fd..bbc3c42 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -148,6 +148,7 @@ static void __devinit smp_xics_setup_cpu(int cpu)
 	set_cpu_current_state(cpu, CPU_STATE_ONLINE);
 	set_default_offline_state(cpu);
 #endif
+	pseries_notify_cpuidle_add_cpu(cpu);
 }
 
 static int __devinit smp_pSeries_kick_cpu(int nr)

^ permalink raw reply related

* [RFC PATCH v2 1/4] cpuidle: (powerpc) Add cpu_idle_wait() to allow switching of idle routines
From: Deepthi Dharwar @ 2011-11-17 11:28 UTC (permalink / raw)
  To: linuxppc-dev, linux-pm, linux-pm; +Cc: linux-kernel
In-Reply-To: <20111117112815.9191.2322.stgit@localhost6.localdomain6>

This patch provides cpu_idle_wait() routine for the powerpc
platform which is required by the cpuidle subsystem. This
routine is requied to change the idle handler on SMP systems.
The equivalent routine for x86 is in arch/x86/kernel/process.c
but the powerpc implementation is different.

Signed-off-by: Deepthi Dharwar <deepthi@linux.vnet.ibm.com>
Signed-off-by: Trinabh Gupta <g.trinabh@gmail.com>
Signed-off-by: Arun R Bharadwaj <arun.r.bharadwaj@gmail.com>
---
 arch/powerpc/Kconfig                 |    4 ++++
 arch/powerpc/include/asm/processor.h |    2 ++
 arch/powerpc/include/asm/system.h    |    1 +
 arch/powerpc/kernel/idle.c           |   26 ++++++++++++++++++++++++++
 4 files changed, 33 insertions(+), 0 deletions(-)

diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig
index b177caa..87f8604 100644
--- a/arch/powerpc/Kconfig
+++ b/arch/powerpc/Kconfig
@@ -87,6 +87,10 @@ config ARCH_HAS_ILOG2_U64
 	bool
 	default y if 64BIT
 
+config ARCH_HAS_CPU_IDLE_WAIT
+	bool
+	default y
+
 config GENERIC_HWEIGHT
 	bool
 	default y
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index eb11a44..811b7e7 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -382,6 +382,8 @@ static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
 }
 #endif
 
+enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
+
 #endif /* __KERNEL__ */
 #endif /* __ASSEMBLY__ */
 #endif /* _ASM_POWERPC_PROCESSOR_H */
diff --git a/arch/powerpc/include/asm/system.h b/arch/powerpc/include/asm/system.h
index e30a13d..ff66680 100644
--- a/arch/powerpc/include/asm/system.h
+++ b/arch/powerpc/include/asm/system.h
@@ -221,6 +221,7 @@ extern unsigned long klimit;
 extern void *zalloc_maybe_bootmem(size_t size, gfp_t mask);
 
 extern int powersave_nap;	/* set if nap mode can be used in idle loop */
+void cpu_idle_wait(void);
 
 /*
  * Atomic exchange
diff --git a/arch/powerpc/kernel/idle.c b/arch/powerpc/kernel/idle.c
index 39a2baa..b478c72 100644
--- a/arch/powerpc/kernel/idle.c
+++ b/arch/powerpc/kernel/idle.c
@@ -39,9 +39,13 @@
 #define cpu_should_die()	0
 #endif
 
+unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
+EXPORT_SYMBOL(boot_option_idle_override);
+
 static int __init powersave_off(char *arg)
 {
 	ppc_md.power_save = NULL;
+	boot_option_idle_override = IDLE_POWERSAVE_OFF;
 	return 0;
 }
 __setup("powersave=off", powersave_off);
@@ -102,6 +106,28 @@ void cpu_idle(void)
 	}
 }
 
+
+/*
+ * cpu_idle_wait - Used to ensure that all the CPUs come out of the old
+ * idle loop and start using the new idle loop.
+ * Required while changing idle handler on SMP systems.
+ * Caller must have changed idle handler to the new value before the call.
+ */
+void cpu_idle_wait(void)
+{
+	int cpu;
+	smp_mb();
+
+	/* kick all the CPUs so that they exit out of old idle routine */
+	get_online_cpus();
+	for_each_online_cpu(cpu) {
+		if (cpu != smp_processor_id())
+			smp_send_reschedule(cpu);
+	}
+	put_online_cpus();
+}
+EXPORT_SYMBOL_GPL(cpu_idle_wait);
+
 int powersave_nap;
 
 #ifdef CONFIG_SYSCTL

^ permalink raw reply related

* [RFC PATCH v2 0/4] cpuidle: (POWER) cpuidle driver for pSeries
From: Deepthi Dharwar @ 2011-11-17 11:28 UTC (permalink / raw)
  To: linuxppc-dev, linux-pm, linux-pm; +Cc: linux-kernel

This patch series ports the cpuidle framework for ppc64 platform and
implements a cpuidle back-end driver for ppc64 (pSeries) platform.
Currently idle states are managed by pseries_{dedicated,shared}_idle_sleep()
routines in arch/powerpc/platforms/pseries/setup.c.  There are
two idle states (snooze and cede) that are exploited by
these routines based on simple heuristics.

Moving the idle states over to cpuidle framework can take advantage of
the advanced heuristics, tunables, and features provided by cpuidle
framework. Additional idle states like extended cede with hints would be
included and exploited using the cpuidle framework. The statistics and 
tracing infrastructure provided by the cpuidle framework also helps in 
enabling power management related tools and help tune the system and 
applications.

This series aims to maintain compatibility and functionality to
existing pSeries idle cpu management code.  There are no new functions
or idle states added as part of this series.

The previous version of this patch can be found at
https://lkml.org/lkml/2011/6/7/375

Changes from the previous version (v1):

	1] Rebased to latest 3.2-rc2

	2] Incorporated the changes from the feedback provided by Ben
	   in the previous version of this series.

	3] The first three patches in this series posted in v1 are not
	   re-posted as they were taken from "idle cleanup - v3" posted by
	   Len Brown (https://lkml.org/lkml/2011/4/2/8) which have made 
	   to the mainline in 3.1. 
	   This cleanup was a community requirement and major dependency
	   before cpuidle framework can be ported over to powerpc
	   architecture.

This patch series includes:

[1/4] - Provides arch specific cpu_idle_wait() function required by cpuidle
  subsystem.
[2/4] - pseries_idle cpuidle driver
[3/4] - Enables cpuidle for pSeries and directly calls cpuidle_idle_call()
[4/4] - Handles powersave=off kernel boot parameter and disables registration
  of pseries_idle cpuidle driver.

This series has been tested on ppc64 pSeries POWER7 system with the snooze
and cede states

--
 arch/powerpc/Kconfig                            |    4 
 arch/powerpc/include/asm/processor.h            |    3 
 arch/powerpc/include/asm/system.h               |    9 +
 arch/powerpc/kernel/idle.c                      |   26 ++
 arch/powerpc/kernel/sysfs.c                     |    2 
 arch/powerpc/platforms/Kconfig                  |    6 
 arch/powerpc/platforms/pseries/Kconfig          |    9 +
 arch/powerpc/platforms/pseries/Makefile         |    1 
 arch/powerpc/platforms/pseries/processor_idle.c |  321 +++++++++++++++++++++++
 arch/powerpc/platforms/pseries/pseries.h        |    3 
 arch/powerpc/platforms/pseries/setup.c          |   89 ------
 arch/powerpc/platforms/pseries/smp.c            |    1 
 include/linux/cpuidle.h                         |    2 
 13 files changed, 387 insertions(+), 89 deletions(-)
 create mode 100644 arch/powerpc/platforms/pseries/processor_idle.c



-Deepthi 

^ permalink raw reply

* [PATCH 6/6][resend] acpi : remove 'power' field from acpi_processor_cx
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee
In-Reply-To: <1321525600-2466-1-git-send-email-daniel.lezcano@linaro.org>

The power field is filled but never used in the code.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/acpi/processor_idle.c |    2 --
 include/acpi/processor.h      |    1 -
 2 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 4b9ebd5..e2b67e2 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -495,8 +495,6 @@ static int acpi_processor_get_power_info_cst(struct acpi_processor *pr)
 		if (obj->type != ACPI_TYPE_INTEGER)
 			continue;
 
-		cx.power = obj->integer.value;
-
 		current_count++;
 		memcpy(&(pr->power.states[current_count]), &cx, sizeof(cx));
 
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index bc3449a..bed1093 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -59,7 +59,6 @@ struct acpi_processor_cx {
 	u8 entry_method;
 	u8 index;
 	u32 latency;
-	u32 power;
 	u8 bm_sts_skip;
 	char desc[ACPI_CX_DESC_LEN];
 };
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 5/6][resend] acpi : remove 'usage' field in acpi_processor_cx
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee
In-Reply-To: <1321525600-2466-1-git-send-email-daniel.lezcano@linaro.org>

The usage field is incremented but the resulting value is never
used in the code.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/acpi/processor_idle.c |    5 -----
 include/acpi/processor.h      |    1 -
 2 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 142af72..4b9ebd5 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -778,7 +778,6 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev,
 	dev->last_residency = (int)idle_time;
 
 	local_irq_enable();
-	cx->usage++;
 	lapic_timer_state_broadcast(pr, cx, 0);
 
 	return index;
@@ -858,8 +857,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 	if (cx->entry_method != ACPI_CSTATE_FFH)
 		current_thread_info()->status |= TS_POLLING;
 
-	cx->usage++;
-
 	lapic_timer_state_broadcast(pr, cx, 0);
 	return index;
 }
@@ -983,8 +980,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 	if (cx->entry_method != ACPI_CSTATE_FFH)
 		current_thread_info()->status |= TS_POLLING;
 
-	cx->usage++;
-
 	lapic_timer_state_broadcast(pr, cx, 0);
 	return index;
 }
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 46bcf79..bc3449a 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -60,7 +60,6 @@ struct acpi_processor_cx {
 	u8 index;
 	u32 latency;
 	u32 power;
-	u32 usage;
 	u8 bm_sts_skip;
 	char desc[ACPI_CX_DESC_LEN];
 };
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 4/6][resend] acpi : remove 'time' field in acpi_processor_cx
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee
In-Reply-To: <1321525600-2466-1-git-send-email-daniel.lezcano@linaro.org>

The 'time' field is used to sum the idle time but it is never
used.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/acpi/processor_idle.c |    2 --
 include/acpi/processor.h      |    1 -
 2 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index dd87355..142af72 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -861,7 +861,6 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev,
 	cx->usage++;
 
 	lapic_timer_state_broadcast(pr, cx, 0);
-	cx->time += idle_time;
 	return index;
 }
 
@@ -987,7 +986,6 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev,
 	cx->usage++;
 
 	lapic_timer_state_broadcast(pr, cx, 0);
-	cx->time += idle_time;
 	return index;
 }
 
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 1b6e65c..46bcf79 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -61,7 +61,6 @@ struct acpi_processor_cx {
 	u32 latency;
 	u32 power;
 	u32 usage;
-	u64 time;
 	u8 bm_sts_skip;
 	char desc[ACPI_CX_DESC_LEN];
 };
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 3/6][resend] acpi : remove 'latency_ticks' in acpi_processor_cx
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee
In-Reply-To: <1321525600-2466-1-git-send-email-daniel.lezcano@linaro.org>

The latency_ticks field is used to store a value but it is never
used. It is safe to remove it.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/acpi/processor_idle.c |    2 --
 include/acpi/processor.h      |    1 -
 2 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 24fe3af..dd87355 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -595,7 +595,6 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr,
 	 */
 	cx->valid = 1;
 
-	cx->latency_ticks = cx->latency;
 	/*
 	 * On older chipsets, BM_RLD needs to be set
 	 * in order for Bus Master activity to wake the
@@ -628,7 +627,6 @@ static int acpi_processor_power_verify(struct acpi_processor *pr)
 			if (!cx->address)
 				break;
 			cx->valid = 1; 
-			cx->latency_ticks = cx->latency; /* Normalize latency */
 			break;
 
 		case ACPI_STATE_C3:
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 610f6fb..1b6e65c 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -59,7 +59,6 @@ struct acpi_processor_cx {
 	u8 entry_method;
 	u8 index;
 	u32 latency;
-	u32 latency_ticks;
 	u32 power;
 	u32 usage;
 	u64 time;
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 2/6][resend] cpuidle : remove unused 'governor_data' field
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee
In-Reply-To: <1321525600-2466-1-git-send-email-daniel.lezcano@linaro.org>

As far as I can see, this field is never used in the code.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 include/linux/cpuidle.h |    1 -
 1 files changed, 0 insertions(+), 1 deletions(-)

diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h
index c904188..4c8fd37 100644
--- a/include/linux/cpuidle.h
+++ b/include/linux/cpuidle.h
@@ -95,7 +95,6 @@ struct cpuidle_device {
 	struct list_head 	device_list;
 	struct kobject		kobj;
 	struct completion	kobj_unregister;
-	void			*governor_data;
 };
 
 DECLARE_PER_CPU(struct cpuidle_device *, cpuidle_devices);
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 1/6][resend] cpuidle : move code to prevent forward declaration
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee
In-Reply-To: <1321525600-2466-1-git-send-email-daniel.lezcano@linaro.org>

A mindless change by moving __cpuidle_register_device and cpuidle_register_device
up in the code and hence preventing the forward declaration.

Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
---
 drivers/cpuidle/cpuidle.c |  116 ++++++++++++++++++++++-----------------------
 1 files changed, 57 insertions(+), 59 deletions(-)

diff --git a/drivers/cpuidle/cpuidle.c b/drivers/cpuidle/cpuidle.c
index 7a57b11..e0e2a02 100644
--- a/drivers/cpuidle/cpuidle.c
+++ b/drivers/cpuidle/cpuidle.c
@@ -50,8 +50,6 @@ static void cpuidle_kick_cpus(void)
 static void cpuidle_kick_cpus(void) {}
 #endif
 
-static int __cpuidle_register_device(struct cpuidle_device *dev);
-
 /**
  * cpuidle_idle_call - the main idle loop
  *
@@ -202,6 +200,63 @@ static void poll_idle_init(struct cpuidle_driver *drv) {}
 #endif /* CONFIG_ARCH_HAS_CPU_RELAX */
 
 /**
+ * __cpuidle_register_device - internal register function called before register
+ * and enable routines
+ * @dev: the cpu
+ *
+ * cpuidle_lock mutex must be held before this is called
+ */
+static int __cpuidle_register_device(struct cpuidle_device *dev)
+{
+	int ret;
+	struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
+	struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
+
+	if (!sys_dev)
+		return -EINVAL;
+	if (!try_module_get(cpuidle_driver->owner))
+		return -EINVAL;
+
+	init_completion(&dev->kobj_unregister);
+
+	per_cpu(cpuidle_devices, dev->cpu) = dev;
+	list_add(&dev->device_list, &cpuidle_detected_devices);
+	if ((ret = cpuidle_add_sysfs(sys_dev))) {
+		module_put(cpuidle_driver->owner);
+		return ret;
+	}
+
+	dev->registered = 1;
+	return 0;
+}
+
+/**
+ * cpuidle_register_device - registers a CPU's idle PM feature
+ * @dev: the cpu
+ */
+int cpuidle_register_device(struct cpuidle_device *dev)
+{
+	int ret;
+
+	mutex_lock(&cpuidle_lock);
+
+	if ((ret = __cpuidle_register_device(dev))) {
+		mutex_unlock(&cpuidle_lock);
+		return ret;
+	}
+
+	cpuidle_enable_device(dev);
+	cpuidle_install_idle_handler();
+
+	mutex_unlock(&cpuidle_lock);
+
+	return 0;
+
+}
+
+EXPORT_SYMBOL_GPL(cpuidle_register_device);
+
+/**
  * cpuidle_enable_device - enables idle PM for a CPU
  * @dev: the CPU
  *
@@ -281,63 +336,6 @@ void cpuidle_disable_device(struct cpuidle_device *dev)
 EXPORT_SYMBOL_GPL(cpuidle_disable_device);
 
 /**
- * __cpuidle_register_device - internal register function called before register
- * and enable routines
- * @dev: the cpu
- *
- * cpuidle_lock mutex must be held before this is called
- */
-static int __cpuidle_register_device(struct cpuidle_device *dev)
-{
-	int ret;
-	struct sys_device *sys_dev = get_cpu_sysdev((unsigned long)dev->cpu);
-	struct cpuidle_driver *cpuidle_driver = cpuidle_get_driver();
-
-	if (!sys_dev)
-		return -EINVAL;
-	if (!try_module_get(cpuidle_driver->owner))
-		return -EINVAL;
-
-	init_completion(&dev->kobj_unregister);
-
-	per_cpu(cpuidle_devices, dev->cpu) = dev;
-	list_add(&dev->device_list, &cpuidle_detected_devices);
-	if ((ret = cpuidle_add_sysfs(sys_dev))) {
-		module_put(cpuidle_driver->owner);
-		return ret;
-	}
-
-	dev->registered = 1;
-	return 0;
-}
-
-/**
- * cpuidle_register_device - registers a CPU's idle PM feature
- * @dev: the cpu
- */
-int cpuidle_register_device(struct cpuidle_device *dev)
-{
-	int ret;
-
-	mutex_lock(&cpuidle_lock);
-
-	if ((ret = __cpuidle_register_device(dev))) {
-		mutex_unlock(&cpuidle_lock);
-		return ret;
-	}
-
-	cpuidle_enable_device(dev);
-	cpuidle_install_idle_handler();
-
-	mutex_unlock(&cpuidle_lock);
-
-	return 0;
-
-}
-
-EXPORT_SYMBOL_GPL(cpuidle_register_device);
-
-/**
  * cpuidle_unregister_device - unregisters a CPU's idle PM feature
  * @dev: the cpu
  */
-- 
1.7.4.1

^ permalink raw reply related

* [PATCH 0/6][resend] removing unused fields in the structure
From: Daniel Lezcano @ 2011-11-17 10:26 UTC (permalink / raw)
  To: lenb; +Cc: linux-acpi, linux-pm, rob.lee

 This patchset applies on the 'next' branch of the linux-lenb git tree.

resend : Cc'ed linux-pm mailing list also

Daniel Lezcano (6):
  cpuidle : move code to prevent forward declaration
  cpuidle : remove unused 'governor_data' field
  acpi : remove 'latency_ticks' in acpi_processor_cx
  acpi : remove 'time' field in acpi_processor_cx
  acpi : remove 'usage' field in acpi_processor_cx
  acpi : remove 'power' field from acpi_processor_cx

 drivers/acpi/processor_idle.c |   11 ----
 drivers/cpuidle/cpuidle.c     |  116 ++++++++++++++++++++---------------------
 include/acpi/processor.h      |    4 --
 include/linux/cpuidle.h       |    1 -
 4 files changed, 57 insertions(+), 75 deletions(-)

-- 
1.7.4.1

^ permalink raw reply

* power_supply: bq27x00: suggest to add battery_lock to bq27x00_read.
From: zhan han @ 2011-11-17  3:59 UTC (permalink / raw)
  To: linux-pm


[-- Attachment #1.1: Type: text/plain, Size: 981 bytes --]

bq27x00 could return abnormal data when bq27x00_read called by multiple
processes at the same time,
when applications access capacity, voltage and temperature frequently.
to make sure that not running bq27x00_read before previous return, add
battery_lock to avoid this:

--- bq27x00_battery.c.orig      2011-11-17 09:53:51.564690000 +0800
+++ bq27x00_battery.c   2011-11-17 10:00:26.768690000 +0800
@@ -129,10 +129,19 @@
  * Common code for BQ27x00 devices
  */

-static inline int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
+/* To avoid running multiple bq27x00_read return with abnormal data,
+   add battery_lock mutex. --H.Z
+*/
+static DEFINE_MUTEX(battery_lock);
+
+static int bq27x00_read(struct bq27x00_device_info *di, u8 reg,
                bool single)
 {
-       return di->bus.read(di, reg, single);
+       int ret;
+       mutex_lock(&battery_lock);
+       ret = di->bus.read(di, reg, single);
+       mutex_unlock(&battery_lock);
+       return ret;
 }

[-- Attachment #1.2: Type: text/html, Size: 4804 bytes --]

[-- Attachment #2: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply

* [PATCH 7/7] xen/acpi/sleep: Register to the acpi_suspend_lowlevel a callback.
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

We piggyback on "x86/acpi: Provide registration for acpi_suspend_lowlevel."
to register a Xen version of the callback. The callback does not
do anything special - except it omits the x86_acpi_suspend_lowlevel.
It does that b/c during suspend it tries to save cr8 values (which
the hypervisor does not support), and then on resume path the
cr3, cr8, idt, and gdt are all resumed which clashes with what
the hypervisor has set up for the guest.

Signed-off-by: Liang Tang <liang.tang@oracle.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 include/xen/acpi.h |   17 ++++++++++++++++-
 1 files changed, 16 insertions(+), 1 deletions(-)

diff --git a/include/xen/acpi.h b/include/xen/acpi.h
index 69a6890..832b5e5 100644
--- a/include/xen/acpi.h
+++ b/include/xen/acpi.h
@@ -44,11 +44,26 @@
 acpi_status xen_acpi_notify_hypervisor_state(u8 sleep_state,
 				     u32 pm1a_cnt, u32 pm1b_cnd);
 
+static inline int xen_acpi_suspend_lowlevel(void)
+{
+	/*
+	* Xen will save and restore CPU context, so
+	* we can skip that and just go straight to
+	* the suspend.
+	*/
+	acpi_enter_sleep_state(ACPI_STATE_S3);
+	return 0;
+}
+
+
 static inline void xen_acpi_sleep_register(void)
 {
-	if (xen_initial_domain())
+	if (xen_initial_domain()){
 		acpi_os_prepare_sleep_register(
 			&xen_acpi_notify_hypervisor_state);
+
+		acpi_suspend_lowlevel = xen_acpi_suspend_lowlevel;
+	}
 }
 #else
 static inline void xen_acpi_sleep_register(void)
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH 6/7] x86/acpi/sleep: Provide registration for acpi_suspend_lowlevel
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

Which by default will be x86_acpi_suspend_lowlevel.
This registration allows us to register another callback
if there is a need to use another platform specific callback.

Signed-off-by: Liang Tang <liang.tang@oracle.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 arch/x86/include/asm/acpi.h  |    2 +-
 arch/x86/kernel/acpi/boot.c  |    2 ++
 arch/x86/kernel/acpi/sleep.c |    4 ++--
 arch/x86/kernel/acpi/sleep.h |    2 ++
 drivers/acpi/sleep.c         |    2 ++
 5 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h
index 610001d..68cf060 100644
--- a/arch/x86/include/asm/acpi.h
+++ b/arch/x86/include/asm/acpi.h
@@ -115,7 +115,7 @@ static inline void acpi_disable_pci(void)
 }
 
 /* Low-level suspend routine. */
-extern int acpi_suspend_lowlevel(void);
+extern int (*acpi_suspend_lowlevel)(void);
 
 extern const unsigned char acpi_wakeup_code[];
 #define acpi_wakeup_address (__pa(TRAMPOLINE_SYM(acpi_wakeup_code)))
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 4558f0d..eb85e88 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -44,6 +44,7 @@
 #include <asm/mpspec.h>
 #include <asm/smp.h>
 
+#include "sleep.h" /* To include x86_acpi_suspend_lowlevel */
 static int __initdata acpi_force = 0;
 u32 acpi_rsdt_forced;
 int acpi_disabled;
@@ -552,6 +553,7 @@ static int acpi_register_gsi_ioapic(struct device *dev, u32 gsi,
 int (*__acpi_register_gsi)(struct device *dev, u32 gsi,
 			   int trigger, int polarity) = acpi_register_gsi_pic;
 
+int (*acpi_suspend_lowlevel)(void) = x86_acpi_suspend_lowlevel;
 /*
  * success: return IRQ number (>=0)
  * failure: return < 0
diff --git a/arch/x86/kernel/acpi/sleep.c b/arch/x86/kernel/acpi/sleep.c
index 103b6ab..4d2d0b1 100644
--- a/arch/x86/kernel/acpi/sleep.c
+++ b/arch/x86/kernel/acpi/sleep.c
@@ -25,12 +25,12 @@ static char temp_stack[4096];
 #endif
 
 /**
- * acpi_suspend_lowlevel - save kernel state
+ * x86_acpi_suspend_lowlevel - save kernel state
  *
  * Create an identity mapped page table and copy the wakeup routine to
  * low memory.
  */
-int acpi_suspend_lowlevel(void)
+int x86_acpi_suspend_lowlevel(void)
 {
 	struct wakeup_header *header;
 	/* address in low memory of the wakeup routine. */
diff --git a/arch/x86/kernel/acpi/sleep.h b/arch/x86/kernel/acpi/sleep.h
index 416d4be..4d3feb5 100644
--- a/arch/x86/kernel/acpi/sleep.h
+++ b/arch/x86/kernel/acpi/sleep.h
@@ -13,3 +13,5 @@ extern unsigned long acpi_copy_wakeup_routine(unsigned long);
 extern void wakeup_long64(void);
 
 extern void do_suspend_lowlevel(void);
+
+extern int x86_acpi_suspend_lowlevel(void);
diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c
index 6d9a3ab..b8b26e8 100644
--- a/drivers/acpi/sleep.c
+++ b/drivers/acpi/sleep.c
@@ -254,6 +254,8 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
 		break;
 
 	case ACPI_STATE_S3:
+		if (!acpi_suspend_lowlevel)
+			return -ENODEV;
 		error = acpi_suspend_lowlevel();
 		if (error)
 			return error;
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH 5/7] xen/pci: Utilize the restore_msi_irqs hook.
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

From: Tang Liang <liang.tang@oracle.com>

to make a hypercall to restore the vectors in the MSI/MSI-X
configuration space.

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 arch/x86/pci/xen.c              |   27 +++++++++++++++++++++++++++
 include/xen/interface/physdev.h |    7 +++++++
 2 files changed, 34 insertions(+), 0 deletions(-)

diff --git a/arch/x86/pci/xen.c b/arch/x86/pci/xen.c
index 492ade8..249a5ae 100644
--- a/arch/x86/pci/xen.c
+++ b/arch/x86/pci/xen.c
@@ -324,6 +324,32 @@ static int xen_initdom_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
 out:
 	return ret;
 }
+
+static void xen_initdom_restore_msi_irqs(struct pci_dev *dev, int irq)
+{
+	int ret = 0;
+
+	if (pci_seg_supported) {
+		struct physdev_pci_device restore_ext;
+
+		restore_ext.seg = pci_domain_nr(dev->bus);
+		restore_ext.bus = dev->bus->number;
+		restore_ext.devfn = dev->devfn;
+		ret = HYPERVISOR_physdev_op(PHYSDEVOP_restore_msi_ext,
+					&restore_ext);
+		if (ret == -ENOSYS)
+			pci_seg_supported = false;
+		WARN(ret && ret != -ENOSYS, "restore_msi_ext -> %d\n", ret);
+	}
+	if (!pci_seg_supported) {
+		struct physdev_restore_msi restore;
+
+		restore.bus = dev->bus->number;
+		restore.devfn = dev->devfn;
+		ret = HYPERVISOR_physdev_op(PHYSDEVOP_restore_msi, &restore);
+		WARN(ret && ret != -ENOSYS, "restore_msi -> %d\n", ret);
+	}
+}
 #endif
 
 static void xen_teardown_msi_irqs(struct pci_dev *dev)
@@ -446,6 +472,7 @@ int __init pci_xen_initial_domain(void)
 #ifdef CONFIG_PCI_MSI
 	x86_msi.setup_msi_irqs = xen_initdom_setup_msi_irqs;
 	x86_msi.teardown_msi_irq = xen_teardown_msi_irq;
+	x86_msi.restore_msi_irqs = xen_initdom_restore_msi_irqs;
 #endif
 	xen_setup_acpi_sci();
 	__acpi_register_gsi = acpi_register_gsi_xen;
diff --git a/include/xen/interface/physdev.h b/include/xen/interface/physdev.h
index c1080d9..0c28989 100644
--- a/include/xen/interface/physdev.h
+++ b/include/xen/interface/physdev.h
@@ -145,6 +145,13 @@ struct physdev_manage_pci {
 	uint8_t devfn;
 };
 
+#define PHYSDEVOP_restore_msi            19
+struct physdev_restore_msi {
+	/* IN */
+	uint8_t bus;
+	uint8_t devfn;
+};
+
 #define PHYSDEVOP_manage_pci_add_ext	20
 struct physdev_manage_pci_ext {
 	/* IN */
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH 4/7] x86: Expand the x86_msi_ops to have a restore MSIs.
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

The MSI restore function will become a function pointer in an
x86_msi_ops struct. It defaults to the implementation in the
io_apic.c and msi.c. We piggyback on the indirection mechanism
introduced by "x86: Introduce x86_msi_ops".

Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 arch/x86/include/asm/pci.h      |    9 +++++++++
 arch/x86/include/asm/x86_init.h |    1 +
 arch/x86/kernel/x86_init.c      |    1 +
 drivers/pci/msi.c               |   29 +++++++++++++++++++++++++++--
 4 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h
index d498943..df75d07 100644
--- a/arch/x86/include/asm/pci.h
+++ b/arch/x86/include/asm/pci.h
@@ -112,19 +112,28 @@ static inline void x86_teardown_msi_irq(unsigned int irq)
 {
 	x86_msi.teardown_msi_irq(irq);
 }
+static inline void x86_restore_msi_irqs(struct pci_dev *dev, int irq)
+{
+	x86_msi.restore_msi_irqs(dev, irq);
+}
 #define arch_setup_msi_irqs x86_setup_msi_irqs
 #define arch_teardown_msi_irqs x86_teardown_msi_irqs
 #define arch_teardown_msi_irq x86_teardown_msi_irq
+#define arch_restore_msi_irqs x86_restore_msi_irqs
 /* implemented in arch/x86/kernel/apic/io_apic. */
 int native_setup_msi_irqs(struct pci_dev *dev, int nvec, int type);
 void native_teardown_msi_irq(unsigned int irq);
+void native_restore_msi_irqs(struct pci_dev *dev, int irq);
 /* default to the implementation in drivers/lib/msi.c */
 #define HAVE_DEFAULT_MSI_TEARDOWN_IRQS
+#define HAVE_DEFAULT_MSI_RESTORE_IRQS
 void default_teardown_msi_irqs(struct pci_dev *dev);
+void default_restore_msi_irqs(struct pci_dev *dev, int irq);
 #else
 #define native_setup_msi_irqs		NULL
 #define native_teardown_msi_irq		NULL
 #define default_teardown_msi_irqs	NULL
+#define default_restore_msi_irqs	NULL
 #endif
 
 #define PCI_DMA_BUS_IS_PHYS (dma_ops->is_phys)
diff --git a/arch/x86/include/asm/x86_init.h b/arch/x86/include/asm/x86_init.h
index d3d8590..7af18be 100644
--- a/arch/x86/include/asm/x86_init.h
+++ b/arch/x86/include/asm/x86_init.h
@@ -174,6 +174,7 @@ struct x86_msi_ops {
 	int (*setup_msi_irqs)(struct pci_dev *dev, int nvec, int type);
 	void (*teardown_msi_irq)(unsigned int irq);
 	void (*teardown_msi_irqs)(struct pci_dev *dev);
+	void (*restore_msi_irqs)(struct pci_dev *dev, int irq);
 };
 
 extern struct x86_init_ops x86_init;
diff --git a/arch/x86/kernel/x86_init.c b/arch/x86/kernel/x86_init.c
index 6f164bd..bd1fe10 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -110,4 +110,5 @@ struct x86_msi_ops x86_msi = {
 	.setup_msi_irqs = native_setup_msi_irqs,
 	.teardown_msi_irq = native_teardown_msi_irq,
 	.teardown_msi_irqs = default_teardown_msi_irqs,
+	.restore_msi_irqs = default_restore_msi_irqs,
 };
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 0e6d04d..ba2ea4e 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -86,6 +86,31 @@ void default_teardown_msi_irqs(struct pci_dev *dev)
 }
 #endif
 
+#ifndef arch_restore_msi_irqs
+# define arch_restore_msi_irqs default_restore_msi_irqs
+# define HAVE_DEFAULT_MSI_RESTORE_IRQS
+#endif
+
+#ifdef HAVE_DEFAULT_MSI_RESTORE_IRQS
+void default_restore_msi_irqs(struct pci_dev *dev, int irq)
+{
+	struct msi_desc *entry;
+
+	entry = NULL;
+	if (dev->msix_enabled) {
+		list_for_each_entry(entry, &dev->msi_list, list) {
+			if (irq == entry->irq)
+				break;
+		}
+	} else if (dev->msi_enabled)  {
+		entry = irq_get_msi_desc(irq);
+	}
+
+	if (entry)
+		write_msi_msg(irq, &entry->msg);
+}
+#endif
+
 static void msi_set_enable(struct pci_dev *dev, int pos, int enable)
 {
 	u16 control;
@@ -360,7 +385,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev)
 
 	pci_intx_for_msi(dev, 0);
 	msi_set_enable(dev, pos, 0);
-	write_msi_msg(dev->irq, &entry->msg);
+	arch_restore_msi_irqs(dev, dev->irq);
 
 	pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control);
 	msi_mask_irq(entry, msi_capable_mask(control), entry->masked);
@@ -388,7 +413,7 @@ static void __pci_restore_msix_state(struct pci_dev *dev)
 	pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control);
 
 	list_for_each_entry(entry, &dev->msi_list, list) {
-		write_msi_msg(entry->irq, &entry->msg);
+		arch_restore_msi_irqs(dev, entry->irq);
 		msix_mask_irq(entry, entry->masked);
 	}
 
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH 3/7] xen/acpi/sleep: Enable ACPI sleep via the acpi_os_prepare_sleep_register
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

Provide the registration callback to call in the Xen's
ACPI sleep functionality. This means that during S3/S5
we make a hypercall XENPF_enter_acpi_sleep with the
proper PM1A/PM1B registers.

Based of Ke Yu's <ke.yu@intel.com> initial idea.
[ From http://xenbits.xensource.com/linux-2.6.18-xen.hg
change c68699484a65 ]

[v1: Added Copyright and license]
[v2: Added check if PM1A/B the 16-bits MSB contain something. The spec
     only uses 16-bits but might have more in future]
Signed-off-by: Liang Tang <liang.tang@oracle.com>
[v1: Improved the git commit description]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 arch/x86/xen/enlighten.c |    3 ++
 drivers/xen/Makefile     |    2 +-
 drivers/xen/acpi.c       |   62 ++++++++++++++++++++++++++++++++++++++++++++++
 include/xen/acpi.h       |   59 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 125 insertions(+), 1 deletions(-)
 create mode 100644 drivers/xen/acpi.c
 create mode 100644 include/xen/acpi.h

diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
index da8afd5..bb0c864 100644
--- a/arch/x86/xen/enlighten.c
+++ b/arch/x86/xen/enlighten.c
@@ -42,6 +42,7 @@
 #include <xen/page.h>
 #include <xen/hvm.h>
 #include <xen/hvc-console.h>
+#include <xen/acpi.h>
 
 #include <asm/paravirt.h>
 #include <asm/apic.h>
@@ -1277,6 +1278,8 @@ asmlinkage void __init xen_start_kernel(void)
 
 		/* Make sure ACS will be enabled */
 		pci_request_acs();
+
+		xen_acpi_sleep_register();
 	}
 		
 
diff --git a/drivers/xen/Makefile b/drivers/xen/Makefile
index 974fffd..0435996 100644
--- a/drivers/xen/Makefile
+++ b/drivers/xen/Makefile
@@ -17,7 +17,7 @@ obj-$(CONFIG_XEN_SYS_HYPERVISOR)	+= sys-hypervisor.o
 obj-$(CONFIG_XEN_PVHVM)			+= platform-pci.o
 obj-$(CONFIG_XEN_TMEM)			+= tmem.o
 obj-$(CONFIG_SWIOTLB_XEN)		+= swiotlb-xen.o
-obj-$(CONFIG_XEN_DOM0)			+= pci.o
+obj-$(CONFIG_XEN_DOM0)			+= pci.o acpi.o
 obj-$(CONFIG_XEN_PCIDEV_BACKEND)	+= xen-pciback/
 
 xen-evtchn-y				:= evtchn.o
diff --git a/drivers/xen/acpi.c b/drivers/xen/acpi.c
new file mode 100644
index 0000000..9f81a0f
--- /dev/null
+++ b/drivers/xen/acpi.c
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * acpi.c
+ * acpi file for domain 0 kernel
+ *
+ * Copyright (c) 2011 Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+ * Copyright (c) 2011 Yu Ke ke.yu@intel.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#include <xen/acpi.h>
+#include <xen/interface/platform.h>
+#include <asm/xen/hypercall.h>
+#include <asm/xen/hypervisor.h>
+
+acpi_status xen_acpi_notify_hypervisor_state(u8 sleep_state,
+				     u32 pm1a_cnt, u32 pm1b_cnt)
+{
+	struct xen_platform_op op = {
+		.cmd = XENPF_enter_acpi_sleep,
+		.interface_version = XENPF_INTERFACE_VERSION,
+		.u = {
+			.enter_acpi_sleep = {
+				.pm1a_cnt_val = (u16)pm1a_cnt,
+				.pm1b_cnt_val = (u16)pm1b_cnt,
+				.sleep_state = sleep_state,
+			},
+		},
+	};
+
+	if ((pm1a_cnt & 0xffff0000) || (pm1b_cnt & 0xffff0000)) {
+		WARN(1, "Using more than 16bits of PM1A/B 0x%x/0x%x!"
+		     "Email xen-devel@lists.xensource.com  Thank you.\n", \
+		     pm1a_cnt, pm1b_cnt);
+		return AE_ERROR;
+	}
+
+	HYPERVISOR_dom0_op(&op);
+	return AE_ERROR;
+}
diff --git a/include/xen/acpi.h b/include/xen/acpi.h
new file mode 100644
index 0000000..69a6890
--- /dev/null
+++ b/include/xen/acpi.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ * acpi.h
+ * acpi file for domain 0 kernel
+ *
+ * Copyright (c) 2011 Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+ * Copyright (c) 2011 Yu Ke <ke.yu@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+#ifndef _XEN_ACPI_H
+#define _XEN_ACPI_H
+
+#include <linux/types.h>
+
+#ifdef CONFIG_XEN_DOM0
+#include <asm/xen/hypervisor.h>
+#include <xen/xen.h>
+#include <linux/acpi.h>
+#include <acpi/acpiosxf.h>
+
+acpi_status xen_acpi_notify_hypervisor_state(u8 sleep_state,
+				     u32 pm1a_cnt, u32 pm1b_cnd);
+
+static inline void xen_acpi_sleep_register(void)
+{
+	if (xen_initial_domain())
+		acpi_os_prepare_sleep_register(
+			&xen_acpi_notify_hypervisor_state);
+}
+#else
+static inline void xen_acpi_sleep_register(void)
+{
+}
+#endif
+
+#endif	/* _XEN_ACPI_H */
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH 2/7] tboot: Add return values for tboot_sleep
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

. as appropiately. As tboot_sleep now returns values, we are free
to remove the tboot_sleep_wrapper function altogether.

Suggested-by: "Rafael J. Wysocki" <rjw@sisk.pl>
CC: Joseph Cihula <joseph.cihula@intel.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 arch/x86/kernel/tboot.c |   11 +++++++----
 1 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 751d673..5ab5362 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -273,7 +273,8 @@ static void tboot_copy_fadt(const struct acpi_table_fadt *fadt)
 		offsetof(struct acpi_table_facs, firmware_waking_vector);
 }
 
-void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
+static acpi_status tboot_sleep(u8 sleep_state, u32 pm1a_control,
+			       u32 pm1b_control)
 {
 	static u32 acpi_shutdown_map[ACPI_S_STATE_COUNT] = {
 		/* S0,1,2: */ -1, -1, -1,
@@ -282,7 +283,7 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
 		/* S5: */ TB_SHUTDOWN_S5 };
 
 	if (!tboot_enabled())
-		return;
+		return AE_OK;
 
 	tboot_copy_fadt(&acpi_gbl_FADT);
 	tboot->acpi_sinfo.pm1a_cnt_val = pm1a_control;
@@ -293,10 +294,12 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
 	if (sleep_state >= ACPI_S_STATE_COUNT ||
 	    acpi_shutdown_map[sleep_state] == -1) {
 		pr_warning("unsupported sleep state 0x%x\n", sleep_state);
-		return;
+		return AE_ERROR;
 	}
 
 	tboot_shutdown(acpi_shutdown_map[sleep_state]);
+
+	return AE_OK;
 }
 static acpi_status tboot_sleep_wrapper(u8 sleep_state, u32 pm1a_control,
 		u32 pm1b_control)
@@ -353,7 +356,7 @@ static __init int tboot_late_init(void)
 	atomic_set(&ap_wfs_count, 0);
 	register_hotcpu_notifier(&tboot_cpu_notifier);
 
-	acpi_os_prepare_sleep_register(&tboot_sleep_wrapper);
+	acpi_os_prepare_sleep_register(&tboot_sleep);
 	return 0;
 }
 
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH 1/7] x86, acpi, tboot: Have a ACPI os prepare sleep instead of calling tboot_sleep.
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel, Konrad Rzeszutek Wilk
In-Reply-To: <1321392671-9128-1-git-send-email-konrad.wilk@oracle.com>

From: Tang Liang <liang.tang@oracle.com>

The ACPI suspend path makes a call to tboot_sleep right before
it writes the PM1A, PM1B values. We replace the direct call to
tboot via an registration callback similar to __acpi_register_gsi.

As part of this, the tboot_sleep need only to register with the
acpi_os_prepare_sleep_register and if it not (on IA64) then it simply
won't be called.

We can also remove the tboot_sleep declerations.

[v1: Added __attribute__ ((unused))]
[v2: Introduced a wrapper instead of changing tboot_sleep return values]
Signed-off-by: Tang Liang <liang.tang@oracle.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
 arch/x86/kernel/tboot.c       |    9 +++++++++
 drivers/acpi/acpica/hwsleep.c |    7 ++++---
 drivers/acpi/osl.c            |   19 +++++++++++++++++++
 include/acpi/acpiosxf.h       |    6 ++++++
 include/linux/tboot.h         |    3 ---
 5 files changed, 38 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index e2410e2..751d673 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -31,6 +31,7 @@
 #include <linux/pfn.h>
 #include <linux/mm.h>
 #include <linux/tboot.h>
+#include <acpi/acpiosxf.h>
 
 #include <asm/trampoline.h>
 #include <asm/processor.h>
@@ -297,6 +298,12 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
 
 	tboot_shutdown(acpi_shutdown_map[sleep_state]);
 }
+static acpi_status tboot_sleep_wrapper(u8 sleep_state, u32 pm1a_control,
+		u32 pm1b_control)
+{
+	tboot_sleep(sleep_state, pm1a_control, pm1b_control);
+	return AE_OK;
+}
 
 static atomic_t ap_wfs_count;
 
@@ -345,6 +352,8 @@ static __init int tboot_late_init(void)
 
 	atomic_set(&ap_wfs_count, 0);
 	register_hotcpu_notifier(&tboot_cpu_notifier);
+
+	acpi_os_prepare_sleep_register(&tboot_sleep_wrapper);
 	return 0;
 }
 
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index d52da30..b10bc90 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -45,7 +45,6 @@
 #include <acpi/acpi.h>
 #include "accommon.h"
 #include "actables.h"
-#include <linux/tboot.h>
 #include <linux/module.h>
 
 #define _COMPONENT          ACPI_HARDWARE
@@ -344,8 +343,10 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
 
 	ACPI_FLUSH_CPU_CACHE();
 
-	tboot_sleep(sleep_state, pm1a_control, pm1b_control);
-
+	status = acpi_os_prepare_sleep(sleep_state, pm1a_control,
+				 pm1b_control);
+	if (ACPI_FAILURE(status))
+		return_ACPI_STATUS(status);
 	/* Write #2: Write both SLP_TYP + SLP_EN */
 
 	status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control);
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index f31c5c5..40daa68 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1659,3 +1659,22 @@ acpi_status acpi_os_terminate(void)
 
 	return AE_OK;
 }
+
+acpi_status (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl,
+				       u32 pm1b_ctrl);
+
+acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control,
+		u32 pm1b_control)
+{
+	if (__acpi_os_prepare_sleep)
+		return __acpi_os_prepare_sleep(sleep_state, pm1a_control,
+					       pm1b_control);
+	else
+		return	AE_OK;
+}
+
+void acpi_os_prepare_sleep_register(acpi_status (*func)(u8 sleep_state,
+		 u32 pm1a_ctrl,	u32 pm1b_ctrl))
+{
+	__acpi_os_prepare_sleep = func;
+}
diff --git a/include/acpi/acpiosxf.h b/include/acpi/acpiosxf.h
index 83062ed..ebde1e1 100644
--- a/include/acpi/acpiosxf.h
+++ b/include/acpi/acpiosxf.h
@@ -108,6 +108,12 @@ void acpi_os_delete_lock(acpi_spinlock handle);
 
 acpi_cpu_flags acpi_os_acquire_lock(acpi_spinlock handle);
 
+void acpi_os_prepare_sleep_register(acpi_status (*func)(u8 sleep_state,
+				    u32 pm1a_ctrl, u32 pm1b_ctrl));
+
+acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control,
+				  u32 pm1b_control);
+
 void acpi_os_release_lock(acpi_spinlock handle, acpi_cpu_flags flags);
 
 /*
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index 1dba6ee..d57732d 100644
--- a/include/linux/tboot.h
+++ b/include/linux/tboot.h
@@ -143,7 +143,6 @@ static inline int tboot_enabled(void)
 
 extern void tboot_probe(void);
 extern void tboot_shutdown(u32 shutdown_type);
-extern void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control);
 extern struct acpi_table_header *tboot_get_dmar_table(
 				      struct acpi_table_header *dmar_tbl);
 extern int tboot_force_iommu(void);
@@ -153,8 +152,6 @@ extern int tboot_force_iommu(void);
 #define tboot_enabled()			0
 #define tboot_probe()			do { } while (0)
 #define tboot_shutdown(shutdown_type)	do { } while (0)
-#define tboot_sleep(sleep_state, pm1a_control, pm1b_control)	\
-					do { } while (0)
 #define tboot_get_dmar_table(dmar_tbl)	(dmar_tbl)
 #define tboot_force_iommu()		0
 
-- 
1.7.7.1

^ permalink raw reply related

* [PATCH] ACPI cleanup's and enablement for Xen ACPI S3 [v3]
From: Konrad Rzeszutek Wilk @ 2011-11-15 21:31 UTC (permalink / raw)
  To: linux-kernel, rjw, tglx, x86, hpa, len.brown, joseph.cihula,
	linux-pm, tboot-
  Cc: xen-devel

Attached is an [v3] set of patches to enable S3 to work with the Xen hypervisor.

Changes since v2: [https://lkml.org/lkml/2011/9/29/408]
 - Moved tboot_sleep out to the osl.c code.
 - Dropped some patches.
since the RFC posting [http://comments.gmane.org/gmane.linux.acpi.devel/50701]:
 - Per review comments added: __unused__ attribute, support for PM1A/B if more than 16-bit,
   copyright/license.
 - Added support for PHYSDEVOP_restore_msi_ext call.

The first two patches can be considered independently as cleanup - they move
the tboot_sleep out of the ACPI code and move it in the OS part. That is
the OSPM code changes required.

The more complex ones are in the ACPI x86 code. I was not sure how to
post the patches so I grouped in the "functionality" parts.

1). Use the acpi_os_prepare_sleep to register a variant of it. The reason
    for the need for this is explained in more details below. The patches are:

 [PATCH 1/7] x86, acpi, tboot: Have a ACPI os prepare sleep instead
 [PATCH 2/7] tboot: Add return values for tboot_sleep
 [PATCH 3/7] xen/acpi/sleep: Enable ACPI sleep via the

2). Expand x86_msi_ops. Every time we resume, we end up calling write_msi_irq
    to resume the MSI vectors. But when using Xen, we would write the MSI
    vectors using the other x86_msi_ops - hence we expand the x86_msi_ops
    indirection mechanism to take resume in account. The paches are:

 [PATCH 4/7] x86: Expand the x86_msi_ops to have a restore MSIs.
 [PATCH 5/7] xen/pci: Utilize the restore_msi_irqs hook.
3). Make acpi_suspend_lowlevel be a function pointer instead of a function.
    Details of why we want to omit the lowlevel values is explained below.
    Originally I was thinking that perhaps doing it via a registration
    function would be better? But not sure what folks leanings are
    in this case. The patches are:

 [PATCH 6/7] x86/acpi/sleep: Provide registration for
 [PATCH 7/7] xen/acpi/sleep: Register to the acpi_suspend_lowlevel a

Details of what I said in the first postings:

The Xen ACPI S3 functionality requires help from the Linux kernel. The Linux
kernel does the ACPI "stuff" and tells the hypervisor to do the low-level
stuff (such as program the IOAPIC, setup vectors, etc). Naturally do it correctly
the Xen hypervisor must be programmed with correct values that are extracted
as part of parsing the ACPI.

The ACPI code used during suspend is mostly all in hwsleep.c and there is one
particular case where 'hwsleep.c' is calling in the tboot.c code. This is replaced
by making the call go through the OS part of the ACPI code. The reason for
doing this is two fold: 1) cleanup, 2) for Xen case, it needs to make a hypercall
so that the hypervisor can write the PM1A/PM1B bits.

The major difficulties we hit was with 'acpi_suspend_lowlevel' - which tweaks
a lot of lowlevel values and some of them are not properly handled by Xen.
Liang Tang has figured which ones of them we trip over (read below) - and he
suggested that perhaps we can provide a registration mechanism to abstract
this away. The reason for all of this is that Linux does not talk to the
BIOS directly - instead it simply walks through the necessary ACPI methods
and then issues hypercall to Xen which then further completes the remaining
suspend steps.

So the attached patches do exactly that - there are two entry points
in the ACPI.

 1). For S3: acpi_suspend_lowlevel -> .. lots of code -> acpi_enter_sleep_state
 2). For S1/S4/S5: acpi_enter_sleep_state

The first naive idea was of abstracting away in the 'acpi_enter_sleep_state'
function the tboot_sleep code so that we can use it too. And low-behold - it
worked splendidly for powering off (S5 I believe)

For S3 that did not work - during suspend the hypervisor tripped over when
saving cr8. During resume it tripped over at restoring the cr3, cr8, idt,
and gdt values.

When I posted the RFC, the feedback I got was to use a higher upper
interface to make the call to the hypervisor. Instead of doing it at the
lower pv-ops case for cr3, cr8, idt, gdt, etc. The code is much nicer
this way, I've to say.

Anyhow, please take a look!

The patches are also located at

git://git.kernel.org/pub/scm/linux/kernel/git/konrad/xen.git devel/acpi-s3.v5

Konrad Rzeszutek Wilk (5):
      tboot: Add return values for tboot_sleep
      xen/acpi/sleep: Enable ACPI sleep via the acpi_os_prepare_sleep_register
      x86: Expand the x86_msi_ops to have a restore MSIs.
      x86/acpi/sleep: Provide registration for acpi_suspend_lowlevel
      xen/acpi/sleep: Register to the acpi_suspend_lowlevel a callback.

Tang Liang (2):
      x86, acpi, tboot: Have a ACPI os prepare sleep instead of calling tboot_sleep.
      xen/pci: Utilize the restore_msi_irqs hook.

 arch/x86/include/asm/acpi.h     |    2 +-
 arch/x86/include/asm/pci.h      |    9 +++++
 arch/x86/include/asm/x86_init.h |    1 +
 arch/x86/kernel/acpi/boot.c     |    2 +
 arch/x86/kernel/acpi/sleep.c    |    4 +-
 arch/x86/kernel/acpi/sleep.h    |    2 +
 arch/x86/kernel/tboot.c         |   18 ++++++++--
 arch/x86/kernel/x86_init.c      |    1 +
 arch/x86/pci/xen.c              |   27 ++++++++++++++
 arch/x86/xen/enlighten.c        |    3 ++
 drivers/acpi/acpica/hwsleep.c   |    7 ++--
 drivers/acpi/osl.c              |   19 ++++++++++
 drivers/acpi/sleep.c            |    2 +
 drivers/pci/msi.c               |   29 ++++++++++++++-
 drivers/xen/Makefile            |    2 +-
 drivers/xen/acpi.c              |   62 ++++++++++++++++++++++++++++++++
 include/acpi/acpiosxf.h         |    6 +++
 include/linux/tboot.h           |    3 --
 include/xen/acpi.h              |   74 +++++++++++++++++++++++++++++++++++++++
 include/xen/interface/physdev.h |    7 ++++
 20 files changed, 265 insertions(+), 15 deletions(-)

^ permalink raw reply

* Re: [PATCH 1/3 v3] ARM: EXYNOS4: Support for generic I/O power domains on EXYNOS4210/4212
From: Kyungmin Park @ 2011-11-15  9:32 UTC (permalink / raw)
  To: Kukjin Kim
  Cc: linux-samsung-soc, Russell King - ARM Linux, Sylwester Nawrocki,
	linux-pm, linux-arm-kernel
In-Reply-To: <005801cca378$0488d0e0$0d9a72a0$%kim@samsung.com>

On 11/15/11, Kukjin Kim <kgene.kim@samsung.com> wrote:
> Chanwoo Choi wrote:
>> Hi Kukjin Kim,
>>
>> Please reply about Sylwester Nawrocki and me.
>> 2011/11/3 Sylwester Nawrocki <s.nawrocki@samsung.com>
>> Hi Kgene,
>>
>> On 11/03/2011 03:09 AM, Kukjin Kim wrote:
>> > As I said, I don't think we should control/gate the clocks with
> regarding power domain.
>> As far as I know there is a plan to let the drivers override
> start/stop_device callbacks,
>> moreover the clock control can be disabled globally by not implementing
> start/stop_device
>> callbacks or per device by not adding clkdev entities to the device clock
> list at runtime
>> PM core. So IMHO, there is/going to be enough flexibility.
>>
>> > It should be controlled by each regarding device driver and in addition,
> as I know,
>> > to handle block of clock is not recommended on EXYNOS4 now.
>> What do you mean by this ? I couldn't find such information in any
> documentation.
>> Shouldn't "clock gate block" registers be touched by boot loader and the
> kernel?
>> Our boot loaders disable all clocks, and if the global clock gate is not
> enabled
>> by the kernel there is no chance any multimedia device will work.
>>
>> Should the global clock block gate be always enabled then ? I'm afraid it
> is not
>> optimal form power management POV.
>>
> Sylwester, let me check again before replying.
>
>> To Kukjin Kim,
>>
> Hi Chanwoo,
>
> Firstly, please use text-typed e-mail when you reply.
>
>> > It should be controlled by each regarding device driver and in addition,
> as I know,
>> > to handle block of clock is not recommended on EXYNOS4 now.
>>
>> As you said, should we separately control power and clock of power domain?
>
> If you ask my opinion, yes, I mean when it is required, it would be
> controlled independent.

Okay. It's meaning-less, Until change his mind, just drop it. I hope
you don't change this mind for long time.

Thank you,
Kyungmin Park
>
>> or Have to turn on the clock of power domain always?
>
> I didn't say like that.
>
>> I think that it is to happen unnecessary power consumption.
>
> I don't think, when the power of block/domain is downed, does it happen
> _really_ meaningful power consumption?
>
>> If we have to maintain on state of power domain clock, please let me know
> guide about this.
>>
>> Also,
>> For example, fimg2d and framebuffer use LCD0 power domain as parent
> device.I
>> If fimg2d and framebuffer is disabled state due to not using their,
>> I think that we should turn off the clock of LCD0 power domain to reduce
> power leakage.
>
> I think, probably it doesn't matter. If any useful data, please let me know.
>
>> Which each regarding device driver control the clock of power domain?
>>
>> I will expect for your reply.
>>
>
> Thanks.
>
> Best regards,
> Kgene.
> --
> Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
> SW Solution Development Team, Samsung Electronics Co., Ltd.
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc"
> in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply

* Re: [PATCH 1/3 v3] ARM: EXYNOS4: Support for generic I/O power domains on EXYNOS4210/4212
From: Kukjin Kim @ 2011-11-15  9:21 UTC (permalink / raw)
  To: cw00.choi
  Cc: 'linux-samsung-soc', 'Russell King - ARM Linux',
	'Sylwester Nawrocki', 'Kyungmin Park', linux-pm,
	'linux-arm-kernel'
In-Reply-To: <CAGTfZH0xOh=EDRhb3gxOZhV-KQZNY_-o=miBOEne_S5-YsQ_Vg@mail.gmail.com>

Chanwoo Choi wrote:
> Hi Kukjin Kim,
> 
> Please reply about Sylwester Nawrocki and me.
> 2011/11/3 Sylwester Nawrocki <s.nawrocki@samsung.com>
> Hi Kgene,
>
> On 11/03/2011 03:09 AM, Kukjin Kim wrote:
> > As I said, I don't think we should control/gate the clocks with
regarding power domain.
> As far as I know there is a plan to let the drivers override
start/stop_device callbacks,
> moreover the clock control can be disabled globally by not implementing
start/stop_device
> callbacks or per device by not adding clkdev entities to the device clock
list at runtime
> PM core. So IMHO, there is/going to be enough flexibility.
>
> > It should be controlled by each regarding device driver and in addition,
as I know,
> > to handle block of clock is not recommended on EXYNOS4 now.
> What do you mean by this ? I couldn't find such information in any
documentation.
> Shouldn't "clock gate block" registers be touched by boot loader and the
kernel?
> Our boot loaders disable all clocks, and if the global clock gate is not
enabled
> by the kernel there is no chance any multimedia device will work.
>
> Should the global clock block gate be always enabled then ? I'm afraid it
is not
> optimal form power management POV.
>
Sylwester, let me check again before replying.

> To Kukjin Kim,
>
Hi Chanwoo,

Firstly, please use text-typed e-mail when you reply.

> > It should be controlled by each regarding device driver and in addition,
as I know,
> > to handle block of clock is not recommended on EXYNOS4 now.
>
> As you said, should we separately control power and clock of power domain?

If you ask my opinion, yes, I mean when it is required, it would be
controlled independent.

> or Have to turn on the clock of power domain always?

I didn't say like that.

> I think that it is to happen unnecessary power consumption.

I don't think, when the power of block/domain is downed, does it happen
_really_ meaningful power consumption?

> If we have to maintain on state of power domain clock, please let me know
guide about this.
>
> Also, 
> For example, fimg2d and framebuffer use LCD0 power domain as parent
device.I
> If fimg2d and framebuffer is disabled state due to not using their,
> I think that we should turn off the clock of LCD0 power domain to reduce
power leakage.

I think, probably it doesn't matter. If any useful data, please let me know.

> Which each regarding device driver control the clock of power domain?
>
> I will expect for your reply.
>

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.

^ 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