* Re: [PATCH 1/4] cpupower: Add cpupower-idle-info manpage
From: Thomas Renninger @ 2011-12-17 9:21 UTC (permalink / raw)
To: Jean Pihet
Cc: cpufreq, linux-acpi, linux, linux-pm, linux-omap,
linux-arm-kernel
In-Reply-To: <CAORVsuVrm+dWGgs-taatnUMZsYvYg_tdx1=7ekWp0qM+25DorQ@mail.gmail.com>
On Friday 16 December 2011 17:18:37 Jean Pihet wrote:
> Hi Thomas,
>
> On Fri, Dec 16, 2011 at 3:35 PM, Thomas Renninger <trenn@suse.de> wrote:
> > The last missing manpage for cpupower tools.
> Great!
>
> > More info about other architecture's sleep state specialities would be great.
> I wonder if it is the right place for some arch specific information
> about the sleep states.
>
> I propose to document this in a more generic doc file (e.g.
> Documentation/arm/OMAP/omap_pm) and link it from the manpage.
Duplicating info at different places increases needed maintenance.
Mabye above should be more about the kernel implementation details
and the manpage should more include the bits a typical user
wants to know?
> What do you think?
The POLL state is a bit confusing on X86 cpupower idle-info output.
Also on X86 the CPU may decide to use other states than the kernel
requested and the cpupower monitor tool should get used to see them.
This directly affects the output/usage of the tool and to understand it,
it should be documented in the manpage.
IMO it's not bad to have a somewhat bigger manpage here.
Especially on ARM, also the "ordinary" user wants to know
about how/whether the cpu power functionalities work as expected
without the need of looking at the kernel sources.
For now I could only try out cpupower on an ARM platform
with cpufreq support.
Would be great if someone could give cpupower idle-info
a try on such a machine.
Think of a typical user, not a kernel hacker and what he needs to
know to interpret and understand the output.
Maybe it's self-explanatory already, maybe the one or other arch
specific bit should get explained?
Thomas
^ permalink raw reply
* [PATCH 7/7] xen: Utilize the restore_msi_irqs hook.
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: Konrad Rzeszutek Wilk, hpa
In-Reply-To: <1324075099-11397-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: Tang Liang <liang.tang@oracle.com>
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.4
^ permalink raw reply related
* [PATCH 6/7] x86: Expand the x86_msi_ops to have a restore MSIs.
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: linux-pci, Thomas Gleixner, Konrad Rzeszutek Wilk, Jesse Barnes,
hpa
In-Reply-To: <1324075099-11397-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".
Cc: x86@kernel.org
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Jesse Barnes <jbarnes@virtuousgeek.org>
Cc: linux-pci@vger.kernel.org
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 1971e65..cd52084 100644
--- a/arch/x86/include/asm/x86_init.h
+++ b/arch/x86/include/asm/x86_init.h
@@ -177,6 +177,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 c1d6cd5..83b05ad 100644
--- a/arch/x86/kernel/x86_init.c
+++ b/arch/x86/kernel/x86_init.c
@@ -114,4 +114,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.4
^ permalink raw reply related
* [PATCH 5/7] xen/acpi/sleep: Register to the acpi_suspend_lowlevel a callback.
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: Konrad Rzeszutek Wilk, hpa
In-Reply-To: <1324075099-11397-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 | 16 +++++++++++++++-
1 files changed, 15 insertions(+), 1 deletions(-)
diff --git a/include/xen/acpi.h b/include/xen/acpi.h
index 48a9c01..ebaabbb 100644
--- a/include/xen/acpi.h
+++ b/include/xen/acpi.h
@@ -43,11 +43,25 @@
int 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_set_prepare_sleep(
&xen_acpi_notify_hypervisor_state);
+
+ acpi_suspend_lowlevel = xen_acpi_suspend_lowlevel;
+ }
}
#else
static inline void xen_acpi_sleep_register(void)
--
1.7.7.4
^ permalink raw reply related
* [PATCH 4/7] xen/acpi/sleep: Enable ACPI sleep via the __acpi_os_prepare_sleep
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: Konrad Rzeszutek Wilk, hpa
In-Reply-To: <1324075099-11397-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>
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 | 58 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 124 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 1f92865..d7a44cc 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..119d42a
--- /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>
+
+int 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 -1;
+ }
+
+ HYPERVISOR_dom0_op(&op);
+ return 1;
+}
diff --git a/include/xen/acpi.h b/include/xen/acpi.h
new file mode 100644
index 0000000..48a9c01
--- /dev/null
+++ b/include/xen/acpi.h
@@ -0,0 +1,58 @@
+/******************************************************************************
+ * 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>
+
+int 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_set_prepare_sleep(
+ &xen_acpi_notify_hypervisor_state);
+}
+#else
+static inline void xen_acpi_sleep_register(void)
+{
+}
+#endif
+
+#endif /* _XEN_ACPI_H */
--
1.7.7.4
^ permalink raw reply related
* [PATCH 3/7] x86/acpi/sleep: Provide registration for acpi_suspend_lowlevel.
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: Shane Wang, Thomas Gleixner, Konrad Rzeszutek Wilk, hpa
In-Reply-To: <1324075099-11397-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.
CC: Thomas Gleixner <tglx@linutronix.de>
CC: "H. Peter Anvin" <hpa@zytor.com>
CC: x86@kernel.org
CC: Len Brown <len.brown@intel.com>
CC: Joseph Cihula <joseph.cihula@intel.com>
CC: Shane Wang <shane.wang@intel.com>
CC: linux-pm@lists.linux-foundation.org
CC: linux-acpi@vger.kernel.org
CC: Len Brown <len.brown@intel.com>
Signed-off-by: Liang Tang <liang.tang@oracle.com>
[v1: Fix when CONFIG_ACPI_SLEEP is not set]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
arch/x86/include/asm/acpi.h | 2 +-
arch/x86/kernel/acpi/boot.c | 7 +++++++
arch/x86/kernel/acpi/sleep.c | 4 ++--
arch/x86/kernel/acpi/sleep.h | 2 ++
drivers/acpi/sleep.c | 2 ++
5 files changed, 14 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..90e06ef 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,12 @@ 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;
+#ifdef CONFIG_ACPI_SLEEP
+int (*acpi_suspend_lowlevel)(void) = x86_acpi_suspend_lowlevel;
+#else
+int (*acpi_suspend_lowlevel)(void);
+#endif
+
/*
* 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..355fa4b 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 -ENOSYS;
error = acpi_suspend_lowlevel();
if (error)
return error;
--
1.7.7.4
^ permalink raw reply related
* [PATCH 2/7] tboot: Add return values for tboot_sleep
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: Konrad Rzeszutek Wilk, hpa
In-Reply-To: <1324075099-11397-1-git-send-email-konrad.wilk@oracle.com>
. as appropiately. As tboot_sleep now returns values.
remove tboot_sleep_wrapper.
Suggested-by: "Rafael J. Wysocki" <rjw@sisk.pl>
CC: Joseph Cihula <joseph.cihula@intel.com>
[v1: Return -1/0/+1 instead of ACPI_xx values]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
arch/x86/kernel/tboot.c | 13 ++++---------
1 files changed, 4 insertions(+), 9 deletions(-)
diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index 1a4ab7d..6410744 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -272,7 +272,7 @@ 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 int 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,
@@ -281,7 +281,7 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
/* S5: */ TB_SHUTDOWN_S5 };
if (!tboot_enabled())
- return;
+ return 0;
tboot_copy_fadt(&acpi_gbl_FADT);
tboot->acpi_sinfo.pm1a_cnt_val = pm1a_control;
@@ -292,15 +292,10 @@ 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 -1;
}
tboot_shutdown(acpi_shutdown_map[sleep_state]);
-}
-static int tboot_sleep_wrapper(u8 sleep_state, u32 pm1a_control,
- u32 pm1b_control)
-{
- tboot_sleep(sleep_state, pm1a_control, pm1b_control);
return 0;
}
@@ -352,7 +347,7 @@ static __init int tboot_late_init(void)
atomic_set(&ap_wfs_count, 0);
register_hotcpu_notifier(&tboot_cpu_notifier);
- acpi_os_set_prepare_sleep(&tboot_sleep_wrapper);
+ acpi_os_set_prepare_sleep(&tboot_sleep);
return 0;
}
--
1.7.7.4
^ 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-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: Shane Wang, Thomas Gleixner, xen-devel, Konrad Rzeszutek Wilk,
hpa
In-Reply-To: <1324075099-11397-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.
CC: Thomas Gleixner <tglx@linutronix.de>
CC: "H. Peter Anvin" <hpa@zytor.com>
CC: x86@kernel.org
CC: Len Brown <len.brown@intel.com>
CC: Joseph Cihula <joseph.cihula@intel.com>
CC: Shane Wang <shane.wang@intel.com>
CC: xen-devel@lists.xensource.com
CC: linux-pm@lists.linux-foundation.org
CC: tboot-devel@lists.sourceforge.net
CC: linux-acpi@vger.kernel.org
[v1: Added __attribute__ ((unused))]
[v2: Introduced a wrapper instead of changing tboot_sleep return values]
[v3: Added return value AE_CTRL_SKIP for acpi_os_sleep_prepare]
Signed-off-by: Tang Liang <liang.tang@oracle.com>
[v1: Fix compile issues on IA64 and PPC64]
[v2: Fix where __acpi_os_prepare_sleep==NULL and did not go in sleep properly]
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
---
arch/x86/kernel/tboot.c | 8 ++++++++
drivers/acpi/acpica/hwsleep.c | 10 +++++++---
drivers/acpi/osl.c | 24 ++++++++++++++++++++++++
include/acpi/acexcep.h | 1 +
include/linux/acpi.h | 10 ++++++++++
include/linux/tboot.h | 1 -
6 files changed, 50 insertions(+), 4 deletions(-)
diff --git a/arch/x86/kernel/tboot.c b/arch/x86/kernel/tboot.c
index e2410e2..1a4ab7d 100644
--- a/arch/x86/kernel/tboot.c
+++ b/arch/x86/kernel/tboot.c
@@ -297,6 +297,12 @@ void tboot_sleep(u8 sleep_state, u32 pm1a_control, u32 pm1b_control)
tboot_shutdown(acpi_shutdown_map[sleep_state]);
}
+static int tboot_sleep_wrapper(u8 sleep_state, u32 pm1a_control,
+ u32 pm1b_control)
+{
+ tboot_sleep(sleep_state, pm1a_control, pm1b_control);
+ return 0;
+}
static atomic_t ap_wfs_count;
@@ -345,6 +351,8 @@ static __init int tboot_late_init(void)
atomic_set(&ap_wfs_count, 0);
register_hotcpu_notifier(&tboot_cpu_notifier);
+
+ acpi_os_set_prepare_sleep(&tboot_sleep_wrapper);
return 0;
}
diff --git a/drivers/acpi/acpica/hwsleep.c b/drivers/acpi/acpica/hwsleep.c
index d52da30..992359a 100644
--- a/drivers/acpi/acpica/hwsleep.c
+++ b/drivers/acpi/acpica/hwsleep.c
@@ -43,9 +43,9 @@
*/
#include <acpi/acpi.h>
+#include <linux/acpi.h>
#include "accommon.h"
#include "actables.h"
-#include <linux/tboot.h>
#include <linux/module.h>
#define _COMPONENT ACPI_HARDWARE
@@ -344,8 +344,12 @@ 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_SKIP(status))
+ return_ACPI_STATUS(AE_OK);
+ 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..f3aae4b 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -76,6 +76,9 @@ EXPORT_SYMBOL(acpi_in_debugger);
extern char line_buf[80];
#endif /*ENABLE_DEBUGGER */
+static int (*__acpi_os_prepare_sleep)(u8 sleep_state, u32 pm1a_ctrl,
+ u32 pm1b_ctrl);
+
static acpi_osd_handler acpi_irq_handler;
static void *acpi_irq_context;
static struct workqueue_struct *kacpid_wq;
@@ -1659,3 +1662,24 @@ acpi_status acpi_os_terminate(void)
return AE_OK;
}
+
+acpi_status acpi_os_prepare_sleep(u8 sleep_state, u32 pm1a_control,
+ u32 pm1b_control)
+{
+ int rc = 0;
+ if (__acpi_os_prepare_sleep)
+ rc = __acpi_os_prepare_sleep(sleep_state,
+ pm1a_control, pm1b_control);
+ if (rc < 0)
+ return AE_ERROR;
+ else if (rc > 0)
+ return AE_CTRL_SKIP;
+
+ return AE_OK;
+}
+
+void acpi_os_set_prepare_sleep(int (*func)(u8 sleep_state,
+ u32 pm1a_ctrl, u32 pm1b_ctrl))
+{
+ __acpi_os_prepare_sleep = func;
+}
diff --git a/include/acpi/acexcep.h b/include/acpi/acexcep.h
index 5b6c391..fa0d22c 100644
--- a/include/acpi/acexcep.h
+++ b/include/acpi/acexcep.h
@@ -57,6 +57,7 @@
#define ACPI_SUCCESS(a) (!(a))
#define ACPI_FAILURE(a) (a)
+#define ACPI_SKIP(a) (a == AE_CTRL_SKIP)
#define AE_OK (acpi_status) 0x0000
/*
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 6001b4da..fccd017 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -359,4 +359,14 @@ static inline int suspend_nvs_register(unsigned long a, unsigned long b)
}
#endif
+#ifdef CONFIG_ACPI
+void acpi_os_set_prepare_sleep(int (*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);
+#else
+#define acpi_os_set_prepare_sleep(func, pm1a_ctrl, pm1b_ctrl) do { } while (0)
+#endif
+
#endif /*_LINUX_ACPI_H*/
diff --git a/include/linux/tboot.h b/include/linux/tboot.h
index 1dba6ee..c75128b 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);
--
1.7.7.4
^ permalink raw reply related
* [PATCH] ACPI cleanup's and enablement for Xen ACPI S3 [v4].
From: Konrad Rzeszutek Wilk @ 2011-12-16 22:38 UTC (permalink / raw)
To: linux-kernel, rjw, x86, len.brown, joseph.cihula, linux-pm,
tboot-devel, linux-acpi, liang.tang
Cc: hpa
Attached is an [v4] set of patches to enable S3 to work with the Xen hypervisor
and also do some cleanups. Posting just before holidays so that when folks are
tired of unwrapping presents, drinking eggnog, etc, they can read some
exciting patches!
Changes since v3: [http://lkml.org/lkml/2011/11/15/340]:
- Redid the acpi_os_prepare_sleep fnc per Rafael's suggestions.
- Fixed compile issues on other platforms, various config options.
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 only OSPM code changes done.
The more complex ones are in the ACPI x86 code. I was not sure how to
post the patches so I grouped them in "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.v7
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 | 7 ++++
arch/x86/kernel/acpi/sleep.c | 4 +-
arch/x86/kernel/acpi/sleep.h | 2 +
arch/x86/kernel/tboot.c | 9 +++--
arch/x86/kernel/x86_init.c | 1 +
arch/x86/pci/xen.c | 27 ++++++++++++++
arch/x86/xen/enlighten.c | 3 ++
drivers/acpi/acpica/hwsleep.c | 10 ++++--
drivers/acpi/osl.c | 24 +++++++++++++
drivers/acpi/sleep.c | 2 +
drivers/pci/msi.c | 29 ++++++++++++++-
drivers/xen/Makefile | 2 +-
drivers/xen/acpi.c | 62 +++++++++++++++++++++++++++++++++
include/acpi/acexcep.h | 1 +
include/linux/acpi.h | 10 +++++
include/linux/tboot.h | 1 -
include/xen/acpi.h | 72 +++++++++++++++++++++++++++++++++++++++
include/xen/interface/physdev.h | 7 ++++
21 files changed, 272 insertions(+), 13 deletions(-)
Konrad Rzeszutek Wilk (5):
tboot: Add return values for tboot_sleep
x86/acpi/sleep: Provide registration for acpi_suspend_lowlevel.
xen/acpi/sleep: Enable ACPI sleep via the __acpi_os_prepare_sleep
xen/acpi/sleep: Register to the acpi_suspend_lowlevel a callback.
x86: Expand the x86_msi_ops to have a restore MSIs.
Tang Liang (2):
x86, acpi, tboot: Have a ACPI os prepare sleep instead of calling tboot_sleep.
xen: Utilize the restore_msi_irqs hook.
^ permalink raw reply
* Re: [PATCH 1/4] cpupower: Add cpupower-idle-info manpage
From: Jean Pihet @ 2011-12-16 16:18 UTC (permalink / raw)
To: Thomas Renninger
Cc: cpufreq, linux-acpi, linux, linux-pm, linux-omap,
linux-arm-kernel
In-Reply-To: <1324046154-64824-2-git-send-email-trenn@suse.de>
Hi Thomas,
On Fri, Dec 16, 2011 at 3:35 PM, Thomas Renninger <trenn@suse.de> wrote:
> The last missing manpage for cpupower tools.
Great!
> More info about other architecture's sleep state specialities would be great.
I wonder if it is the right place for some arch specific information
about the sleep states.
I propose to document this in a more generic doc file (e.g.
Documentation/arm/OMAP/omap_pm) and link it from the manpage.
What do you think?
>
> Signed-off-by: Thomas Renninger <trenn@suse.de>
> CC: linux-arm-kernel@lists.infradead.org
> CC: jean.pihet@newoldbits.com
> CC: linux-omap@vger.kernel.org
> CC: linux-pm@lists.linux-foundation.org
> CC: linux-acpi@vger.kernel.org
> CC: cpufreq@vger.kernel.org
> CC: linux@dominikbrodowski.net
Regards,
Jean
^ permalink raw reply
* [PATCH 1/4] cpupower: Add cpupower-idle-info manpage
From: Thomas Renninger @ 2011-12-16 14:35 UTC (permalink / raw)
To: linux; +Cc: cpufreq, linux-acpi, linux-pm, linux-omap, linux-arm-kernel
In-Reply-To: <1324046154-64824-1-git-send-email-trenn@suse.de>
The last missing manpage for cpupower tools.
More info about other architecture's sleep state specialities would be great.
Signed-off-by: Thomas Renninger <trenn@suse.de>
CC: linux-arm-kernel@lists.infradead.org
CC: jean.pihet@newoldbits.com
CC: linux-omap@vger.kernel.org
CC: linux-pm@lists.linux-foundation.org
CC: linux-acpi@vger.kernel.org
CC: cpufreq@vger.kernel.org
CC: linux@dominikbrodowski.net
---
tools/power/cpupower/man/cpupower-idle-info.1 | 90 +++++++++++++++++++++++++
1 files changed, 90 insertions(+), 0 deletions(-)
create mode 100644 tools/power/cpupower/man/cpupower-idle-info.1
diff --git a/tools/power/cpupower/man/cpupower-idle-info.1 b/tools/power/cpupower/man/cpupower-idle-info.1
new file mode 100644
index 0000000..4178eff
--- /dev/null
+++ b/tools/power/cpupower/man/cpupower-idle-info.1
@@ -0,0 +1,90 @@
+.TH "CPUPOWER-IDLE-INFO" "1" "0.1" "" "cpupower Manual"
+.SH "NAME"
+.LP
+cpupower idle\-info \- Utility to retrieve cpu idle kernel information
+.SH "SYNTAX"
+.LP
+cpupower [ \-c cpulist ] idle\-info [\fIoptions\fP]
+.SH "DESCRIPTION"
+.LP
+A tool which prints out per cpu idle information helpful to developers and interested users.
+.SH "OPTIONS"
+.LP
+.TP
+\fB\-f\fR \fB\-\-silent\fR
+Only print a summary of all available C-states in the system.
+.TP
+\fB\-e\fR \fB\-\-proc\fR
+deprecated.
+Prints out idle information in old /proc/acpi/processor/*/power format. This
+interface has been removed from the kernel for quite some time, do not let
+further code depend on this option, best do not use it.
+
+.SH IDLE\-INFO DESCRIPTIONS
+CPU sleep state statistics and descriptions are retrieved from sysfs files,
+exported by the cpuidle kernel subsystem. The kernel only updates these
+statistics when it enters or leaves an idle state, therefore on a very idle or
+a very busy system, these statistics may not be accurate. They still provide a
+good overview about the usage and availability of processor sleep states on
+the platform.
+
+Be aware that the sleep states as exported by the hardware or BIOS and used by
+the Linux kernel may not exactly reflect the capabilities of the
+processor. This often is the case on the X86 architecture when the acpi_idle
+driver is used. It is also possible that the hardware overrules the kernel
+requests, due to internal activity monitors or other reasons.
+On recent X86 platforms it is often possible to read out hardware registers
+which monitor the duration of sleep states the processor resided in. The
+cpupower monitor tool (cpupower\-monitor(1)) can be used to show real sleep
+state residencies. Please refer to the architecture specific description
+section below.
+
+.SH IDLE\-INFO ARCHITECTURE SPECIFIC DESCRIPTIONS
+.SS "X86"
+POLL idle state
+
+If cpuidle is active, X86 platforms have one special idle state.
+The POLL idle state is not a real idle state, it does not save any
+power. Instead, a busy\-loop is executed doing nothing for a short period of
+time. This state is used if the kernel knows that work has to be processed
+very soon and entering any real hardware idle state may result in a slight
+performance penalty.
+
+There exist two different cpuidle drivers on the X86 architecture platform:
+
+"acpi_idle" cpuidle driver
+
+The acpi_idle cpuidle driver retrieves available sleep states (C\-states) from
+the ACPI BIOS tables (from the _CST ACPI function on recent platforms or from
+the FADT BIOS table on older ones).
+The C1 state is not retrieved from ACPI tables. If the C1 state is entered,
+the kernel will call the hlt instruction (or mwait on Intel).
+
+"intel_idle" cpuidle driver
+
+In kernel 2.6.36 the intel_idle driver was introduced.
+It only serves recent Intel CPUs (Nehalem, Westmere, Sandybridge, Atoms or
+newer). On older Intel CPUs the acpi_idle driver is still used (if the BIOS
+provides C\-state ACPI tables).
+The intel_idle driver knows the sleep state capabilities of the processor and
+ignores ACPI BIOS exported processor sleep states tables.
+
+.SH "REMARKS"
+.LP
+By default only values of core zero are displayed. How to display settings of
+other cores is described in the cpupower(1) manpage in the \-\-cpu option
+section.
+.SH REFERENCES
+http://www.acpi.info/spec.htm
+.SH "FILES"
+.nf
+\fI/sys/devices/system/cpu/cpu*/cpuidle/state*\fP
+\fI/sys/devices/system/cpu/cpuidle/*\fP
+.fi
+.SH "AUTHORS"
+.nf
+Thomas Renninger <trenn@suse.de>
+.fi
+.SH "SEE ALSO"
+.LP
+cpupower(1), cpupower\-monitor(1), cpupower\-info(1), cpupower\-set(1)
--
1.7.6.1
^ permalink raw reply related
* Re: [PATCH v2] ahci: platform support for suspend/resume
From: Jeff Garzik @ 2011-12-15 19:18 UTC (permalink / raw)
To: Brian Norris
Cc: Lin Ming, Linux Kernel, balbi, linux-ide, Tejun Heo, linux-pm,
Anton Vorontsov
In-Reply-To: <CAN8TOE-yZyHC3n+dSu9wZ=UBmenD67OHbAyrF2K_sgf0O7oUJA@mail.gmail.com>
On 12/15/2011 01:13 PM, Brian Norris wrote:
> On Fri, Nov 18, 2011 at 11:28 AM, Felipe Balbi<balbi@ti.com> wrote:
>> On Fri, Nov 18, 2011 at 11:10:10AM -0800, Brian Norris wrote:
>>> Add platform hooks for custom suspend() and resume() functions. The
>>> generic suspend/resume code in drivers/ata/ahci_platform.c is adapted
>>> from the PCI version in drivers/ata/ahci.c.
>>>
>>> Signed-off-by: Brian Norris<computersforpeace@gmail.com>
>>
>> Reviewed-by: Felipe Balbi<balbi@ti.com>
>
> Thanks for the review, Felipe.
>
> What's the status on this? There have been no comments for almost a
> month. Can it be merged?
The previous version was queued, but did not apply to upstream (I
emailed a comment at the time). Hopefully this one works...
^ permalink raw reply
* Re: [PATCH v2] ahci: platform support for suspend/resume
From: Anton Vorontsov @ 2011-12-15 18:43 UTC (permalink / raw)
To: Brian Norris
Cc: Lin Ming, Linux Kernel, balbi, linux-ide, Tejun Heo, linux-pm,
Jeff Garzik
In-Reply-To: <CAN8TOE-yZyHC3n+dSu9wZ=UBmenD67OHbAyrF2K_sgf0O7oUJA@mail.gmail.com>
On Thu, Dec 15, 2011 at 10:13:19AM -0800, Brian Norris wrote:
> On Fri, Nov 18, 2011 at 11:28 AM, Felipe Balbi <balbi@ti.com> wrote:
> > On Fri, Nov 18, 2011 at 11:10:10AM -0800, Brian Norris wrote:
> >> Add platform hooks for custom suspend() and resume() functions. The
> >> generic suspend/resume code in drivers/ata/ahci_platform.c is adapted
> >> from the PCI version in drivers/ata/ahci.c.
> >>
> >> Signed-off-by: Brian Norris <computersforpeace@gmail.com>
> >
> > Reviewed-by: Felipe Balbi <balbi@ti.com>
>
> Thanks for the review, Felipe.
>
> What's the status on this? There have been no comments for almost a
> month. Can it be merged?
FWIW, the patch looks quite useful. Hope this will help:
Acked-by: Anton Vorontsov <cbouatmailru@gmail.com>
Thanks!
--
Anton Vorontsov
Email: cbouatmailru@gmail.com
^ permalink raw reply
* Re: [PATCH v2] PM: HIBERNATION: skip the swap size check if the snapshot image size is anticipative
From: Barry Song @ 2011-12-15 8:50 UTC (permalink / raw)
To: Barry Song
Cc: Xiangzhen Ye, linux-kernel, workgroup.linux, Barry Song, linux-pm,
linux-arm-kernel
In-Reply-To: <1322208422-12346-1-git-send-email-Barry.Song@csr.com>
Pavel ,Rafael, any feedback?
-barry
2011/11/25 Barry Song <Barry.Song@csr.com>:
> From: Barry Song <baohua.song@csr.com>
>
> Current swsusp requires swap partitions even larger than real saved pages
> based on the worst compression ratio:
> but for an embedded system, which has limited storage space, then it might
> can't give the large partition to save snapshot.
> In the another way, some embedded systems can definitely know the most size
> needed for snapshot since they run some specific application lists.
> So this patch provides the possibility for users to tell kernel even
> the system has a little snapshot partition, but it is still enough.
> For example, if the system need to save 120MB memory, origin swsusp will require
> a 130MB partition to save snapshot. but if users know 30MB is enough for them(
> compressed image will be less than 30MB), they just make a 30MB partition by
> echo 0 > /sys/power/check_swap_size
>
> Signed-off-by: Barry Song <Baohua.Song@csr.com>
> Cc: Xiangzhen Ye <Xiangzhen.Ye@csr.com>
> ---
> -v2:drop swap_enough bootargs and use /sys/power/check_swap_size node
>
> Documentation/power/interface.txt | 5 +++++
> kernel/power/hibernate.c | 22 ++++++++++++++++++++++
> kernel/power/power.h | 2 ++
> kernel/power/swap.c | 9 +++++++++
> 4 files changed, 38 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/power/interface.txt b/Documentation/power/interface.txt
> index c537834..5e205f0 100644
> --- a/Documentation/power/interface.txt
> +++ b/Documentation/power/interface.txt
> @@ -47,6 +47,11 @@ Writing to this file will accept one of
> 'testproc'
> 'test'
>
> +/sys/power/check_swap_size controls whether we can skip checking the swap
> +partition size by worst compression ratio. If users know the swap partition
> +is enough for compressed snapshot, write 0 to /sys/power/check_swap_size.
> +It is useful for an embedded system with known running softwares.
> +
> /sys/power/image_size controls the size of the image created by
> the suspend-to-disk mechanism. It can be written a string
> representing a non-negative integer that will be used as an upper
> diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c
> index 1c53f7f..5552473 100644
> --- a/kernel/power/hibernate.c
> +++ b/kernel/power/hibernate.c
> @@ -1024,11 +1024,33 @@ static ssize_t reserved_size_store(struct kobject *kobj,
>
> power_attr(reserved_size);
>
> +static ssize_t check_swap_size_show(struct kobject *kobj, struct kobj_attribute *attr,
> + char *buf)
> +{
> + return sprintf(buf, "%d\n", check_swap_size);
> +}
> +
> +static ssize_t check_swap_size_store(struct kobject *kobj, struct kobj_attribute *attr,
> + const char *buf, size_t n)
> +{
> + int check_size;
> +
> + if (sscanf(buf, "%d", &check_size) == 1) {
> + check_swap_size = check_size;
> + return n;
> + }
> +
> + return -EINVAL;
> +}
> +
> +power_attr(check_swap_size);
> +
> static struct attribute * g[] = {
> &disk_attr.attr,
> &resume_attr.attr,
> &image_size_attr.attr,
> &reserved_size_attr.attr,
> + &check_swap_size_attr.attr,
> NULL,
> };
>
> diff --git a/kernel/power/power.h b/kernel/power/power.h
> index 23a2db1..4f0fa78 100644
> --- a/kernel/power/power.h
> +++ b/kernel/power/power.h
> @@ -74,6 +74,8 @@ static struct kobj_attribute _name##_attr = { \
>
> /* Preferred image size in bytes (default 500 MB) */
> extern unsigned long image_size;
> +/* If 0, skip checking whether the swap size is enough for compressed snapshot */
> +extern int check_swap_size;
> /* Size of memory reserved for drivers (default SPARE_PAGES x PAGE_SIZE) */
> extern unsigned long reserved_size;
> extern int in_suspend;
> diff --git a/kernel/power/swap.c b/kernel/power/swap.c
> index 11a594c..db90195 100644
> --- a/kernel/power/swap.c
> +++ b/kernel/power/swap.c
> @@ -37,6 +37,12 @@
> #define HIBERNATE_SIG "S1SUSPEND"
>
> /*
> + * if users know swap partitions are enough for compressed snapshots,
> + * echo 0 > /sys/power/check_swap_size
> + */
> +int check_swap_size = 1;
> +
> +/*
> * The swap map is a data structure used for keeping track of each page
> * written to a swap partition. It consists of many swap_map_page
> * structures that contain each an array of MAP_PAGE_ENTRIES swap entries.
> @@ -772,6 +778,9 @@ static int enough_swap(unsigned int nr_pages, unsigned int flags)
> unsigned int free_swap = count_swap_pages(root_swap, 1);
> unsigned int required;
>
> + if (!check_swap_size)
> + return 1;
> +
> pr_debug("PM: Free swap pages: %u\n", free_swap);
>
> required = PAGES_FOR_IO + ((flags & SF_NOCOMPRESS_MODE) ?
> --
> 1.7.1
_______________________________________________
linux-pm mailing list
linux-pm@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/linux-pm
^ permalink raw reply
* Re: [PATCH 0/2] OMAP: PM: switch from omap_pm_ functions to PM QoS
From: Jean Pihet @ 2011-12-14 14:57 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet
In-Reply-To: <CAORVsuU3_+653sUMZUWrJ+QHgaWEDqZAy0kEv_o=5m8zfeS4dw@mail.gmail.com>
Hi Paul, Kevin,
This patch series is depending on the latest constraints stuff for
OMAP [1], which has been reviewed by Kevin.
[1] http://marc.info/?l=linux-omap&m=132387443205028&w=2
Regards,
Jean
On Mon, Dec 12, 2011 at 5:27 PM, Jean Pihet <jean.pihet@newoldbits.com> wrote:
> Hi Kevin, Paul,
>
> ping on this series
>
> Thanks,
> Jean
>
> On Wed, Oct 19, 2011 at 4:28 PM, <jean.pihet@newoldbits.com> wrote:
>> From: Jean Pihet <j-pihet@ti.com>
>>
>> . Convert the OMAP I2C driver to the PM QoS API for MPU latency constraints
>> . Remove the latency related functions from OMAP PM in favor of
>> the generic per-device PM QoS API
>>
>>
>> Apply on top of the OMAP PM QoS patch set [1].
>> Based on the pm-qos branch of the linux-pm git tree (3.1.0-rc3), cf. [2].
>>
>> Tested on OMAP3 Beagleboard (ES2.x) with constraints on MPU, CORE, PER in
>> RETention and OFF modes.
>>
>> [1] http://thread.gmane.org/gmane.linux.ports.arm.omap/65971
>> [2] git://github.com/rjwysocki/linux-pm.git
>>
>>
>> Jean Pihet (2):
>> OMAP: convert I2C driver to PM QoS for latency constraints
>> OMAP: PM: remove the latency related functions from the API
>>
>> Documentation/arm/OMAP/omap_pm | 55 +++-------------
>> arch/arm/plat-omap/i2c.c | 20 ------
>> arch/arm/plat-omap/include/plat/omap-pm.h | 99 -----------------------------
>> arch/arm/plat-omap/omap-pm-noop.c | 88 -------------------------
>> drivers/i2c/busses/i2c-omap.c | 30 +++++-----
>> include/linux/i2c-omap.h | 1 -
>> 6 files changed, 24 insertions(+), 269 deletions(-)
>>
>> --
>> 1.7.4.1
>>
^ permalink raw reply
* Re: [PATCH v5 0/6] PM QoS: implement the OMAP low level constraints management code
From: Jean Pihet @ 2011-12-14 14:55 UTC (permalink / raw)
To: Kevin Hilman; +Cc: linux-omap, Linux PM mailing list, Jean Pihet, linux-arm
In-Reply-To: <87zkewrpu7.fsf@ti.com>
Hi Kevin,
On Wed, Dec 14, 2011 at 12:53 AM, Kevin Hilman <khilman@ti.com> wrote:
> Hi Jean,
>
> 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
>> . Implement the low level code which controls the power domains next power
>> states, through the hwmod and pwrdm layers
>> . Add cpuidle and power domains wake-up latency figures for OMAP3, cf.
>> comments in the code and [1] for the details on where the numbers
>> are magically coming from
>> . Implement the relation between the cpuidle and per-device PM QoS frameworks
>> in the OMAP3 specific idle callbacks.
>> The chosen C-state shall satisfy the following conditions:
>> . the 'valid' field is enabled,
>> . it satisfies the enable_off_mode flag,
>> . the next state for MPU and CORE power domains is not lower than the
>> state programmed by the per-device PM QoS.
>
> I had a couple minor comments on this version, but after that, feel free
> to add:
>
> Reviewed-by: Kevin Hilman <khilman@ti.com>
Thanks for reviewing!
I fixed (most of) the minor remarks and re-submitted.
> after that, this series will go upstream through Paul.
>
> Kevin
Regards,
Jean
^ permalink raw reply
* [PATCH 6/6] OMAP3: powerdomain data: add wake-up latency figures
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323874304-5001-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Figures are added to the power domains structs for RET and OFF modes.
Note: the latency figures for MPU, PER, CORE, NEON have been obtained
from actual measurements.
The latency figures for the other power domains are preliminary and
shall be added.
Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.
Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/powerdomains3xxx_data.c | 83 +++++++++++++++++++++++++++
1 files changed, 83 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomains3xxx_data.c b/arch/arm/mach-omap2/powerdomains3xxx_data.c
index 8ef26da..63a3afd 100644
--- a/arch/arm/mach-omap2/powerdomains3xxx_data.c
+++ b/arch/arm/mach-omap2/powerdomains3xxx_data.c
@@ -31,6 +31,19 @@
/*
* Powerdomains
+ *
+ * The wakeup_lat values are derived from HW and SW measurements on
+ * the actual target. For more details cf.
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#Results_for_individual_power_domains
+ *
+ * Note: the latency figures for MPU, PER, CORE, NEON have been obtained
+ * from actual measurements.
+ * The latency figures for the other power domains are preliminary and
+ * shall be added.
+ *
+ * Note: only the SW restore timing values are taken into account.
+ * The HW impact of the sys_clkreq and sys_offmode signals is not taken
+ * into account - TDB
*/
static struct powerdomain iva2_pwrdm = {
@@ -51,6 +64,13 @@ static struct powerdomain iva2_pwrdm = {
[2] = PWRSTS_OFF_ON,
[3] = PWRSTS_ON,
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1100,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 350,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "mpu_iva" },
};
@@ -67,6 +87,13 @@ static struct powerdomain mpu_3xxx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_OFF_ON,
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1830,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 121,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "mpu_iva" },
};
@@ -94,6 +121,13 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 3082,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 153,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -116,6 +150,13 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 3082,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 153,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -131,6 +172,13 @@ static struct powerdomain dss_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 70,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 20,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -152,6 +200,13 @@ static struct powerdomain sgx_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 1000,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -167,6 +222,13 @@ static struct powerdomain cam_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 850,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 35,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -182,6 +244,13 @@ static struct powerdomain per_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 671,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 31,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
@@ -196,6 +265,13 @@ static struct powerdomain neon_pwrdm = {
.prcm_offs = OMAP3430_NEON_MOD,
.pwrsts = PWRSTS_OFF_RET_ON,
.pwrsts_logic_ret = PWRSTS_RET,
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 0,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 0,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "mpu_iva" },
};
@@ -218,6 +294,13 @@ static struct powerdomain usbhost_pwrdm = {
.pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */
},
+ .wakeup_lat = {
+ [PWRDM_FUNC_PWRST_OFF] = 800,
+ [PWRDM_FUNC_PWRST_OSWR] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_CSWR] = 150,
+ [PWRDM_FUNC_PWRST_INACTIVE] = UNSUP_STATE,
+ [PWRDM_FUNC_PWRST_ON] = 0,
+ },
.voltdm = { .name = "core" },
};
--
1.7.5.4
^ permalink raw reply related
* [PATCH 5/6] OMAP3: update cpuidle latency and threshold figures
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323874304-5001-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
Update the data from the measurements performed at HW and SW levels.
Cf. http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
for a detailed explanation on where are the numbers coming from.
ToDo:
- Measure the wake-up latencies for all power domains for OMAP3
- Correct some numbers when sys_clkreq and sys_offmode are supported
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/cpuidle34xx.c | 52 +++++++++++++++++++++++-------------
1 files changed, 33 insertions(+), 19 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index c67835f..3caa932 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -40,27 +40,41 @@
#ifdef CONFIG_CPU_IDLE
/*
- * The MPU latencies/thresholds for various C states have
- * to be configured from the respective board files.
- * These are some default values (which might not provide
- * the best power savings) used on boards which do not
- * pass these details from the board file.
+ * The MPU latency and threshold values for the C-states are the worst case
+ * values from the HW and SW, as described in details at
+ * http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement#cpuidle_results
+ *
+ * Measurements conditions and remarks:
+ * . the measurements have been performed at OPP50
+ * . the sys_offmode signal is not supported and so not used for the
+ * measurements. Instead the latency and threshold values for C9 are
+ * corrected with the value for Triton 2, which is 11.5ms
+ * . the sys_clkreq signal is not used and so a correction is needed - TBD
+ * . the sys_clkoff signal is supported, this value need to be corrected with
+ * the correct value of SYSCLK on/off timings (1ms for sysclk on, 2.5ms
+ * for sysclk off)
+ * . in order to force the cpuidle algorithm to chose the power efficient
+ * C-states (C1, C3, C5, C7) in preference, the other C-states have a
+ * threshold value equal to the next power efficient C-state
+ *
+ * The latency and threshold values can be overriden by data from the board
+ * files, using omap3_pm_init_cpuidle.
*/
static struct cpuidle_params cpuidle_params_table[] = {
- /* C1 */
- {2 + 2, 5, 1},
- /* C2 */
- {10 + 10, 30, 1},
- /* C3 */
- {50 + 50, 300, 1},
- /* C4 */
- {1500 + 1800, 4000, 1},
- /* C5 */
- {2500 + 7500, 12000, 1},
- /* C6 */
- {3000 + 8500, 15000, 1},
- /* C7 */
- {10000 + 30000, 300000, 1},
+ /* C1 . MPU WFI + Core active */
+ {73 + 78, 152, 1},
+ /* C2 . MPU WFI + Core inactive */
+ {165 + 88, 345, 1},
+ /* C3 . MPU CSWR + Core inactive */
+ {163 + 182, 345, 1},
+ /* C4 . MPU OFF + Core inactive */
+ {2852 + 605, 150000, 1},
+ /* C5 . MPU RET + Core RET */
+ {800 + 366, 2120, 1},
+ /* C6 . MPU OFF + Core RET */
+ {4080 + 801, 215000, 1},
+ /* C7 . MPU OFF + Core OFF */
+ {4300 + 13000, 215000, 1},
};
#define OMAP3_NUM_STATES ARRAY_SIZE(cpuidle_params_table)
--
1.7.5.4
^ permalink raw reply related
* [PATCH 4/6] OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and CORE constraints
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323874304-5001-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
The MPU latency figures for cpuidle include the MPU itself and also
the peripherals needed for the MPU to execute instructions (e.g.
main memory, caches, IRQ controller, MMU etc). On OMAP3 those
peripherals belong to the MPU and CORE power domains and so the
cpuidle C-states are a combination of MPU and CORE states.
This patch implements the relation between the cpuidle and per-
device PM QoS frameworks in the OMAP3 specific idle callbacks.
The chosen C-state shall satisfy the following conditions:
. the 'valid' field is enabled,
. it satisfies the enable_off_mode flag,
. the next state for MPU and CORE power domains is not lower than the
next state calculated by the per-device PM QoS.
Tested on OMAP3 Beagleboard in RET/OFF using wake-up latency constraints
on MPU, CORE and PER.
Signed-off-by: Jean Pihet <j-pihet@ti.com>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/cpuidle34xx.c | 57 ++++++++++++++++++++----------------
arch/arm/mach-omap2/pm.h | 17 ++++++++++-
2 files changed, 47 insertions(+), 27 deletions(-)
diff --git a/arch/arm/mach-omap2/cpuidle34xx.c b/arch/arm/mach-omap2/cpuidle34xx.c
index 1f71ebb..c67835f 100644
--- a/arch/arm/mach-omap2/cpuidle34xx.c
+++ b/arch/arm/mach-omap2/cpuidle34xx.c
@@ -40,7 +40,7 @@
#ifdef CONFIG_CPU_IDLE
/*
- * The latencies/thresholds for various C states have
+ * The MPU latencies/thresholds for various C states have
* to be configured from the respective board files.
* These are some default values (which might not provide
* the best power savings) used on boards which do not
@@ -75,14 +75,14 @@ struct omap3_idle_statedata omap3_idle_data[OMAP3_NUM_STATES];
struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
static int _cpuidle_allow_idle(struct powerdomain *pwrdm,
- struct clockdomain *clkdm)
+ struct clockdomain *clkdm)
{
clkdm_allow_idle(clkdm);
return 0;
}
static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
- struct clockdomain *clkdm)
+ struct clockdomain *clkdm)
{
clkdm_deny_idle(clkdm);
return 0;
@@ -96,10 +96,13 @@ static int _cpuidle_deny_idle(struct powerdomain *pwrdm,
*
* Called from the CPUidle framework to program the device to the
* specified target state selected by the governor.
+ *
+ * Note: this function does not check for any pending activity or dependency
+ * between power domains states, so the caller shall check the parameters
+ * correctness.
*/
static int omap3_enter_idle(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
+ struct cpuidle_driver *drv, int index)
{
struct omap3_idle_statedata *cx =
cpuidle_get_statedata(&dev->states_usage[index]);
@@ -174,18 +177,23 @@ return_sleep_time:
* 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.
+ * A state is valid if:
+ * . the 'valid' field is enabled,
+ * . it satisfies the enable_off_mode flag,
+ * . the next state for MPU and CORE power domains is not lower than the
+ * state programmed by the per-device PM QoS.
*/
static int next_valid_state(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
+ struct cpuidle_driver *drv, int index)
{
struct cpuidle_state_usage *curr_usage = &dev->states_usage[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;
+ u32 mpu_pm_qos_next_state = mpu_pd->wkup_lat_next_state;
+ u32 core_pm_qos_next_state = core_pd->wkup_lat_next_state;
+
int next_index = -1;
if (enable_off_mode) {
@@ -202,7 +210,9 @@ static int next_valid_state(struct cpuidle_device *dev,
/* Check if current state is valid */
if ((cx->valid) &&
(cx->mpu_state >= mpu_deepest_state) &&
- (cx->core_state >= core_deepest_state)) {
+ (cx->core_state >= core_deepest_state) &&
+ (cx->mpu_state >= mpu_pm_qos_next_state) &&
+ (cx->core_state >= core_pm_qos_next_state)) {
return index;
} else {
int idx = OMAP3_NUM_STATES - 1;
@@ -227,7 +237,9 @@ static int next_valid_state(struct cpuidle_device *dev,
cx = cpuidle_get_statedata(&dev->states_usage[idx]);
if ((cx->valid) &&
(cx->mpu_state >= mpu_deepest_state) &&
- (cx->core_state >= core_deepest_state)) {
+ (cx->core_state >= core_deepest_state) &&
+ (cx->mpu_state >= mpu_pm_qos_next_state) &&
+ (cx->core_state >= core_pm_qos_next_state)) {
next_index = idx;
break;
}
@@ -248,12 +260,15 @@ static int next_valid_state(struct cpuidle_device *dev,
* @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.
+ * Called from the CPUidle framework to program the device to the
+ * specified target state selected by the governor.
+ *
+ * This function checks for any pending activity or dependency between
+ * power domains states 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)
+ struct cpuidle_driver *drv, int index)
{
int new_state_idx;
u32 core_next_state, per_next_state = 0, per_saved_state = 0, cam_state;
@@ -275,19 +290,13 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
goto select_state;
}
- /*
- * FIXME: we currently manage device-specific idle states
- * for PER and CORE in combination with CPU-specific
- * idle states. This is wrong, and device-specific
- * idle management needs to be separated out into
- * its own code.
- */
+ new_state_idx = next_valid_state(dev, drv, index);
/*
* Prevent PER off if CORE is not in retention or off as this
* would disable PER wakeups completely.
*/
- cx = cpuidle_get_statedata(&dev->states_usage[index]);
+ cx = cpuidle_get_statedata(&dev->states_usage[new_state_idx]);
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) &&
@@ -298,8 +307,6 @@ 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, drv, index);
-
select_state:
ret = omap3_enter_idle(dev, drv, new_state_idx);
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index b737b11..ab32465 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -44,9 +44,22 @@ static inline int omap4_opp_init(void)
* omap3_pm_init_cpuidle
*/
struct cpuidle_params {
- u32 exit_latency; /* exit_latency = sleep + wake-up latencies */
+ /*
+ * exit_latency = sleep + wake-up latencies of the MPU,
+ * which include the MPU itself and the peripherals needed
+ * for the MPU to execute instructions (e.g. main memory,
+ * caches, IRQ controller, MMU etc). Some of those peripherals
+ * can belong to other power domains than the MPU subsystem and so
+ * the corresponding latencies must be included in this figure.
+ */
+ u32 exit_latency;
+ /*
+ * target_residency: required amount of time in the C state
+ * to break even on energy cost
+ */
u32 target_residency;
- u8 valid; /* validates the C-state */
+ /* validates the C-state on the given board */
+ u8 valid;
};
#if defined(CONFIG_PM) && defined(CONFIG_CPU_IDLE)
--
1.7.5.4
^ permalink raw reply related
* [PATCH 3/6] OMAP: PM: register to the per-device PM QoS framework
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323874304-5001-1-git-send-email-j-pihet@ti.com>
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>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/pm.c | 71 +++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 70 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 1881fe9..a40d4f3 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -11,15 +11,18 @@
#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 <linux/export.h>
#include <plat/omap-pm.h>
#include <plat/omap_device.h>
-#include "common.h"
+#include <plat/omap_hwmod.h>
+#include "common.h"
#include "voltage.h"
#include "powerdomain.h"
#include "clockdomain.h"
@@ -215,12 +218,78 @@ static void __init omap4_init_voltages(void)
omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva");
}
+/* 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",
+ 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 */
+ 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 = to_omap_device(pdev);
+ if (od == NULL) {
+ pr_err("%s: Error: no omap_device for device %s\n",
+ __func__, dev_name(dev_pm_qos_req->dev));
+ return -EINVAL;
+ }
+
+ /* Check that the omap_device has an unique hwmod entry */
+ 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);
+ WARN(ret, KERN_ERR "Cannot add global notifier for dev PM QoS\n");
+
+ return ret;
+}
+
static int __init omap2_common_pm_init(void)
{
if (!of_have_populated_dt())
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);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 2/6] OMAP2+: omap_hwmod: manage the wake-up latency constraints
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323874304-5001-1-git-send-email-j-pihet@ti.com>
From: Jean Pihet <j-pihet@ti.com>
The OMAP PM code implements a handler for the per-device PM QoS framework.
The handler queries the omap_hwmod layer in order to manage the power domains
wake-up latency constraints. Hwmod retrieves the correct power domain
and if it exists it calls the corresponding power domain function.
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>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/omap_hwmod.c | 27 +++++++++++++++++++++++++-
arch/arm/plat-omap/include/plat/omap_hwmod.h | 2 +
2 files changed, 28 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 529142a..c3a4cc4 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -143,6 +143,7 @@
#include "powerdomain.h"
#include <plat/clock.h>
#include <plat/omap_hwmod.h>
+#include <plat/omap_device.h>
#include <plat/prcm.h>
#include "cm2xxx_3xxx.h"
@@ -2616,10 +2617,34 @@ ohsps_unlock:
}
/**
+ * omap_hwmod_set_wkup_constraint- set/release a wake-up latency constraint
+ *
+ * @oh: struct omap_hwmod* to which the target device belongs to.
+ * @cookie: identifier of the constraints list for @oh.
+ * @min_latency: the minimum allowed wake-up latency for @oh.
+ *
+ * Returns 0 upon success, -EINVAL in case of invalid parameters, or
+ * the return value from pwrdm_set_wkup_lat_constraint.
+ */
+int omap_hwmod_set_wkup_lat_constraint(struct omap_hwmod *oh,
+ void *cookie, long min_latency)
+{
+ struct powerdomain *pwrdm = omap_hwmod_get_pwrdm(oh);
+
+ if (!pwrdm) {
+ pr_err("%s: Error: could not find powerdomain "
+ "for %s\n", __func__, oh->name);
+ return -EINVAL;
+ }
+
+ return pwrdm_set_wkup_lat_constraint(pwrdm, cookie, min_latency);
+}
+
+/**
* omap_hwmod_get_context_loss_count - get lost context count
* @oh: struct omap_hwmod *
*
- * Query the powerdomain of of @oh to get the context loss
+ * Query the powerdomain of @oh to get the context loss
* count for this device.
*
* Returns the context loss count of the powerdomain assocated with @oh
diff --git a/arch/arm/plat-omap/include/plat/omap_hwmod.h b/arch/arm/plat-omap/include/plat/omap_hwmod.h
index 8b372ed..222f792 100644
--- a/arch/arm/plat-omap/include/plat/omap_hwmod.h
+++ b/arch/arm/plat-omap/include/plat/omap_hwmod.h
@@ -600,6 +600,8 @@ int omap_hwmod_for_each_by_class(const char *classname,
void *user);
int omap_hwmod_set_postsetup_state(struct omap_hwmod *oh, u8 state);
+int omap_hwmod_set_wkup_lat_constraint(struct omap_hwmod *oh, void *cookie,
+ long min_latency);
int omap_hwmod_get_context_loss_count(struct omap_hwmod *oh);
int omap_hwmod_no_setup_reset(struct omap_hwmod *oh);
--
1.7.5.4
^ permalink raw reply related
* [PATCH 1/6] OMAP2+: powerdomain: control power domains next state
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
In-Reply-To: <1323874304-5001-1-git-send-email-j-pihet@ti.com>
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>
Reviewed-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/powerdomain.c | 245 +++++++++++++++++++++++++++++++++++++
arch/arm/mach-omap2/powerdomain.h | 36 +++++-
2 files changed, 279 insertions(+), 2 deletions(-)
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 8a18d1b..677a182 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"
@@ -112,6 +114,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-device wake-up constraints framework data */
+ 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;
@@ -199,6 +207,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_wakeuplat_update_pwrst - Update power domain power state if needed
+ * @pwrdm: struct powerdomain * to which requesting device belongs to.
+ * @min_latency: the allowed wake-up latency for the given power domain. A
+ * value of PM_QOS_DEV_LAT_DEFAULT_VALUE means 'no constraint' on the pwrdm.
+ *
+ * Finds the power domain next power state that fulfills the constraint.
+ * Programs a new target state if it is different from current power state.
+ * The power domains get the next power state programmed directly in the
+ * registers.
+ *
+ * Returns 0 in case of success, -EINVAL in case of invalid parameters,
+ * or the return value from omap_set_pwrdm_state.
+ */
+static int _pwrdm_wakeuplat_update_pwrst(struct powerdomain *pwrdm,
+ long min_latency)
+{
+ int ret = 0, new_state;
+
+ if (!pwrdm) {
+ WARN(1, "powerdomain: %s: invalid parameter(s)", __func__);
+ return -EINVAL;
+ }
+
+ /*
+ * Find the next supported power state with
+ * wakeup latency < minimum constraint
+ */
+ for (new_state = 0x0; new_state < PWRDM_MAX_PWRSTS; new_state++) {
+ if (min_latency == PM_QOS_DEV_LAT_DEFAULT_VALUE)
+ break;
+ if ((pwrdm->wakeup_lat[new_state] != UNSUP_STATE) &&
+ (pwrdm->wakeup_lat[new_state] <= min_latency))
+ break;
+ }
+
+ switch (new_state) {
+ case PWRDM_FUNC_PWRST_OFF:
+ new_state = PWRDM_POWER_OFF;
+ break;
+ case PWRDM_FUNC_PWRST_OSWR:
+ pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_OFF);
+ new_state = PWRDM_POWER_RET;
+ break;
+ case PWRDM_FUNC_PWRST_CSWR:
+ pwrdm_set_logic_retst(pwrdm, PWRDM_POWER_RET);
+ new_state = PWRDM_POWER_RET;
+ break;
+ case PWRDM_FUNC_PWRST_INACTIVE:
+ new_state = PWRDM_POWER_INACTIVE;
+ break;
+ case PWRDM_FUNC_PWRST_ON:
+ new_state = PWRDM_POWER_ON;
+ break;
+ default:
+ pr_warn("powerdomain: requested latency constraint not "
+ "supported %s set to ON state\n", pwrdm->name);
+ new_state = PWRDM_POWER_ON;
+ break;
+ }
+
+ pwrdm->wkup_lat_next_state = new_state;
+ if (pwrdm_read_next_pwrst(pwrdm) != new_state)
+ ret = omap_set_pwrdm_state(pwrdm, new_state);
+
+ pr_debug("powerdomain: %s pwrst: curr=%d, prev=%d next=%d "
+ "min_latency=%ld, set_state=%d\n", pwrdm->name,
+ pwrdm_read_pwrst(pwrdm), pwrdm_read_prev_pwrst(pwrdm),
+ pwrdm_read_next_pwrst(pwrdm), min_latency, new_state);
+
+ return ret;
+}
+
/* Public functions */
/**
@@ -998,6 +1158,91 @@ int pwrdm_post_transition(void)
}
/**
+ * 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.
+ *
+ * Note about the resources allocation and release:
+ * The free_new_user and free_node variables are used to indicate
+ * if the allocated resources can be freed, respectively for the
+ * newly allocated list entry and the current list entry.
+ * Cf. kerneldoc for the _pwrdm_update_wakeuplat_list function for
+ * detailed information about those variables and the
+ * allocation and release of the constraints list entries.
+ */
+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);
+
+ /* 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;
+}
+
+/**
* pwrdm_get_context_loss_count - get powerdomain's context loss count
* @pwrdm: struct powerdomain * to wait for
*
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index 0d72a8a..b4a60cf 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -19,7 +19,9 @@
#include <linux/types.h>
#include <linux/list.h>
-
+#include <linux/plist.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
#include <linux/atomic.h>
#include <plat/cpu.h>
@@ -45,6 +47,16 @@
#define PWRSTS_RET_ON (PWRSTS_RET | PWRSTS_ON)
#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON)
+/* Powerdomain functional power states */
+#define PWRDM_FUNC_PWRST_OFF 0x0
+#define PWRDM_FUNC_PWRST_OSWR 0x1
+#define PWRDM_FUNC_PWRST_CSWR 0x2
+#define PWRDM_FUNC_PWRST_INACTIVE 0x3
+#define PWRDM_FUNC_PWRST_ON 0x4
+
+#define PWRDM_MAX_FUNC_PWRSTS 5
+
+#define UNSUP_STATE -1
/* Powerdomain flags */
#define PWRDM_HAS_HDWR_SAR (1 << 0) /* hardware save-and-restore support */
@@ -96,7 +108,13 @@ struct powerdomain;
* @state_counter:
* @timer:
* @state_timer:
- *
+ * @wakeup_lat: wakeup latencies (in us) for possible powerdomain power states
+ * Note about the wakeup latencies ordering: the values must be sorted
+ * in decremental order
+ * @wkup_lat_plist_head: pwrdm wake-up latency constraints list
+ * @wkup_lat_plist_lock: spinlock that protects the constraints lists
+ * domains states
+ * @wkup_lat_next_state: next pwrdm state, calculated from the constraints list
* @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
*/
struct powerdomain {
@@ -125,6 +143,16 @@ struct powerdomain {
s64 timer;
s64 state_timer[PWRDM_MAX_PWRSTS];
#endif
+ const s32 wakeup_lat[PWRDM_MAX_FUNC_PWRSTS];
+ struct plist_head wkup_lat_plist_head;
+ spinlock_t wkup_lat_plist_lock;
+ int wkup_lat_next_state;
+};
+
+/* Linked list for the wake-up latency constraints */
+struct pwrdm_wkup_constraints_entry {
+ void *cookie;
+ struct plist_node node;
};
/**
@@ -217,6 +245,10 @@ int pwrdm_clkdm_state_switch(struct clockdomain *clkdm);
int pwrdm_pre_transition(void);
int pwrdm_post_transition(void);
int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
+
+int pwrdm_set_wkup_lat_constraint(struct powerdomain *pwrdm, void *cookie,
+ long min_latency);
+
int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
--
1.7.5.4
^ permalink raw reply related
* [PATCH v6 0/6] PM QoS: implement the OMAP low level constraints management code
From: jean.pihet @ 2011-12-14 14:51 UTC (permalink / raw)
To: Kevin Hilman, Linux PM mailing list, linux-omap,
Rafael J. Wysocki, Paul Walmsley, magnus.damm, Todd Poynor
Cc: Jean Pihet, linux-arm
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
. Implement the low level code which controls the power domains next power
states, through the hwmod and pwrdm layers
. Add cpuidle and power domains wake-up latency figures for OMAP3, cf.
comments in the code and [1] for the details on where the numbers
are magically coming from
. Implement the relation between the cpuidle and per-device PM QoS frameworks
in the OMAP3 specific idle callbacks.
The chosen C-state shall satisfy the following conditions:
. the 'valid' field is enabled,
. it satisfies the enable_off_mode flag,
. the next state for MPU and CORE power domains is not lower than the
state programmed by the per-device PM QoS.
ToDo:
1. support OMAP4 chipset when the low power modes will be supported
2. validate the constraints framework on OMAP4 HW (done on OMAP3)
3. Re-visit the OMAP power domains states initialization procedure. Currently
the power states that have been changed from the constraints API which were
applied before the initialization of the power domains are lost
4. Further clean-up the OMAP PM layer, use the generic frameworks instead (OPP,
PM QoS...)
Based on the pm-qos branch of the linux-omap git tree (3.2.0-rc4), cf. [2].
Tested on OMAP3 Beagleboard (ES2.x) with constraints on MPU, CORE, PER in
RETention and OFF modes.
[1] http://www.omappedia.org/wiki/Power_Management_Device_Latencies_Measurement
[2] git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git
v6:
. minor change in the commits description after Kevin's review
. added Kevin's Reviewed-by
v5:
. rebased on latest linux-omap [2]
. rework after Kevin's comments on the MLs
v4:
. split up the patches which remove the omap_pm_ code from the patch set.
Those patches are to be submitted later, on top of this patch set.
. latency numbers: provide the measurements setup and conditions in the code
comments, added the link to the details on wiki [1].
. improved kerneldoc
. split big functions into smaller ones, in order to improve the readability
v3: reworked the error return path and improved the kerneldoc
v2: reworked the OMAP specific cpuidle code to demote the initial C-state to
a valid C-state which fulfills the per-device constraints
v1: initial version
Jean Pihet (6):
OMAP2+: powerdomain: control power domains next state
OMAP2+: omap_hwmod: manage the wake-up latency constraints
OMAP: PM: register to the per-device PM QoS framework
OMAP3: cpuidle: next C-state decision depends on the PM QoS MPU and
CORE constraints
OMAP3: update cpuidle latency and threshold figures
OMAP3: powerdomain data: add wake-up latency figures
arch/arm/mach-omap2/cpuidle34xx.c | 107 +++++++-----
arch/arm/mach-omap2/omap_hwmod.c | 27 +++-
arch/arm/mach-omap2/pm.c | 71 ++++++++-
arch/arm/mach-omap2/pm.h | 17 ++-
arch/arm/mach-omap2/powerdomain.c | 245 ++++++++++++++++++++++++++
arch/arm/mach-omap2/powerdomain.h | 36 ++++-
arch/arm/mach-omap2/powerdomains3xxx_data.c | 83 +++++++++
arch/arm/plat-omap/include/plat/omap_hwmod.h | 2 +
8 files changed, 539 insertions(+), 49 deletions(-)
--
1.7.5.4
^ permalink raw reply
* [RFC PATCH 2/2] thermal: Add generic cpu cooling implementation
From: Amit Daniel Kachhap @ 2011-12-13 15:13 UTC (permalink / raw)
To: linux-pm; +Cc: linaro-dev, patches, linux-kernel, linux-acpi
In-Reply-To: <1323789196-4942-1-git-send-email-amit.kachhap@linaro.org>
This patch adds support for generic cpu thermal cooling low level
implementations using frequency scaling and cpuhotplugg currently.
Different cpu related cooling devices can be registered by the
user and the binding of these cooling devices to the corresponding
trip points can be easily done as the registration API's return the
cooling device pointer.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
Documentation/thermal/cpu-cooling-api.txt | 52 +++++
drivers/thermal/Kconfig | 11 +
drivers/thermal/Makefile | 1 +
drivers/thermal/cpu_cooling.c | 302 +++++++++++++++++++++++++++++
include/linux/cpu_cooling.h | 45 +++++
5 files changed, 411 insertions(+), 0 deletions(-)
create mode 100644 Documentation/thermal/cpu-cooling-api.txt
create mode 100644 drivers/thermal/cpu_cooling.c
create mode 100644 include/linux/cpu_cooling.h
diff --git a/Documentation/thermal/cpu-cooling-api.txt b/Documentation/thermal/cpu-cooling-api.txt
new file mode 100644
index 0000000..d30b4f2
--- /dev/null
+++ b/Documentation/thermal/cpu-cooling-api.txt
@@ -0,0 +1,52 @@
+CPU cooling api's How To
+===================================
+
+Written by Amit Daniel Kachhap <amit.kachhap@linaro.org>
+
+Updated: 13 Dec 2011
+
+Copyright (c) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
+
+0. Introduction
+
+The generic cpu cooling(freq clipping, cpuhotplug) provides
+registration/unregistration api's to the user. The binding of the cooling
+devices to the trip types is left for the user. The registration api's returns
+the cooling device pointer.
+
+1. cpufreq cooling api's
+
+1.1 cpufreq registration api's
+1.1.1 struct thermal_cooling_device *cpufreq_cooling_register(
+ struct freq_pctg_table *tab_ptr, unsigned int tab_size,
+ const struct cpumask *mask_val)
+
+ This interface function registers the cpufreq cooling device with the name
+ "thermal-cpufreq".
+
+ tab_ptr: The table containing the percentage of frequency to be clipped for
+ each cooling state.
+ .freq_clip_pctg[NR_CPUS]:Percentage of frequency to be clipped for each
+ cpu.
+ .polling_interval: polling interval for this cooling state.
+ tab_size: the total number of cooling state.
+ mask_val: all the allowed cpu's where frequency clipping can happen.
+
+1.1.2 void cpufreq_cooling_unregister(void)
+
+ This interface function unregisters the "thermal-cpufreq" cooling device.
+
+
+1.2 cpuhotplug registration api's
+
+1.2.1 struct thermal_cooling_device *cpuhotplug_cooling_register(
+ const struct cpumask *mask_val)
+
+ This interface function registers the cpuhotplug cooling device with the name
+ "thermal-cpuhotplug".
+
+ mask_val: all the allowed cpu's which can be hotplugged out.
+
+1.1.2 void cpuhotplug_cooling_unregister(void)
+
+ This interface function unregisters the "thermal-cpuhotplug" cooling device.
diff --git a/drivers/thermal/Kconfig b/drivers/thermal/Kconfig
index f7f71b2..298c1cd 100644
--- a/drivers/thermal/Kconfig
+++ b/drivers/thermal/Kconfig
@@ -18,3 +18,14 @@ config THERMAL_HWMON
depends on THERMAL
depends on HWMON=y || HWMON=THERMAL
default y
+
+config CPU_THERMAL
+ bool "generic cpu cooling support"
+ depends on THERMAL
+ help
+ This implements the generic cpu cooling mechanism through frequency
+ reduction, cpu hotplug and any other ways of reducing temperature. An
+ ACPI version of this already exists(drivers/acpi/processor_thermal.c).
+ This will be useful for platforms using the generic thermal interface
+ and not the ACPI interface.
+ If you want this support, you should say Y or M here.
diff --git a/drivers/thermal/Makefile b/drivers/thermal/Makefile
index 31108a0..655cbc4 100644
--- a/drivers/thermal/Makefile
+++ b/drivers/thermal/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_THERMAL) += thermal_sys.o
+obj-$(CONFIG_CPU_THERMAL) += cpu_cooling.o
diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c
new file mode 100644
index 0000000..cdd148c
--- /dev/null
+++ b/drivers/thermal/cpu_cooling.c
@@ -0,0 +1,302 @@
+/*
+ * linux/drivers/thermal/cpu_cooling.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
+ * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/thermal.h>
+#include <linux/platform_device.h>
+#include <linux/cpufreq.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/cpu_cooling.h>
+
+#ifdef CONFIG_CPU_FREQ
+struct cpufreq_cooling_device {
+ struct thermal_cooling_device *cool_dev;
+ struct freq_pctg_table *tab_ptr;
+ unsigned int tab_size;
+ unsigned int cpufreq_state;
+ const struct cpumask *allowed_cpus;
+};
+
+static struct cpufreq_cooling_device *cpufreq_device;
+
+/*Below codes defines functions to be used for cpufreq as cooling device*/
+static bool is_cpufreq_valid(int cpu)
+{
+ struct cpufreq_policy policy;
+ if (!cpufreq_get_policy(&policy, cpu))
+ return true;
+ return false;
+}
+
+static int cpufreq_apply_cooling(int cooling_state)
+{
+ int cpuid, this_cpu = smp_processor_id();
+
+ if (!is_cpufreq_valid(this_cpu))
+ return 0;
+
+ if (cooling_state > cpufreq_device->tab_size)
+ return -EINVAL;
+
+ /*Check if last cooling level is same as current cooling level*/
+ if (cpufreq_device->cpufreq_state == cooling_state)
+ return 0;
+
+ cpufreq_device->cpufreq_state = cooling_state;
+
+ for_each_cpu(cpuid, cpufreq_device->allowed_cpus) {
+ if (is_cpufreq_valid(cpuid))
+ cpufreq_update_policy(cpuid);
+ }
+
+ return 0;
+}
+
+static int thermal_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct cpufreq_policy *policy = data;
+ struct freq_pctg_table *th_table;
+ unsigned long max_freq = 0;
+ unsigned int cpu = policy->cpu, th_pctg = 0, level;
+
+ if (event != CPUFREQ_ADJUST)
+ return 0;
+
+ level = cpufreq_device->cpufreq_state;
+
+ if (level > 0) {
+ th_table =
+ &(cpufreq_device->tab_ptr[level - 1]);
+ th_pctg = th_table->freq_clip_pctg[cpu];
+ }
+
+ max_freq =
+ (policy->cpuinfo.max_freq * (100 - th_pctg)) / 100;
+
+ cpufreq_verify_within_limits(policy, 0, max_freq);
+
+ return 0;
+}
+
+/*
+ * cpufreq cooling device callback functions
+ */
+static int cpufreq_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = cpufreq_device->tab_size;
+ return 0;
+}
+
+static int cpufreq_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = cpufreq_device->cpufreq_state;
+ return 0;
+}
+
+/*This cooling may be as PASSIVE/STATE_ACTIVE type*/
+static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ cpufreq_apply_cooling(state);
+ return 0;
+}
+
+/* bind cpufreq callbacks to cpufreq cooling device */
+static struct thermal_cooling_device_ops cpufreq_cooling_ops = {
+ .get_max_state = cpufreq_get_max_state,
+ .get_cur_state = cpufreq_get_cur_state,
+ .set_cur_state = cpufreq_set_cur_state,
+};
+
+static struct notifier_block thermal_cpufreq_notifier_block = {
+ .notifier_call = thermal_cpufreq_notifier,
+};
+
+struct thermal_cooling_device *cpufreq_cooling_register(
+ struct freq_pctg_table *tab_ptr, unsigned int tab_size,
+ const struct cpumask *mask_val)
+{
+ struct thermal_cooling_device *cool_dev;
+
+ if (tab_ptr == NULL || tab_size == 0)
+ return ERR_PTR(-EINVAL);
+
+ cpufreq_device =
+ kzalloc(sizeof(struct cpufreq_cooling_device), GFP_KERNEL);
+
+ if (!cpufreq_device)
+ return ERR_PTR(-ENOMEM);
+
+ cool_dev = thermal_cooling_device_register("thermal-cpufreq", NULL,
+ &cpufreq_cooling_ops);
+ if (!cool_dev) {
+ kfree(cpufreq_device);
+ return ERR_PTR(-EINVAL);
+ }
+
+ cpufreq_device->tab_ptr = tab_ptr;
+ cpufreq_device->tab_size = tab_size;
+ cpufreq_device->cool_dev = cool_dev;
+ cpufreq_device->allowed_cpus = mask_val;
+
+ cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ return cool_dev;
+}
+EXPORT_SYMBOL(cpufreq_cooling_register);
+
+void cpufreq_cooling_unregister(void)
+{
+ cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block,
+ CPUFREQ_POLICY_NOTIFIER);
+ thermal_cooling_device_unregister(cpufreq_device->cool_dev);
+ kfree(cpufreq_device);
+}
+EXPORT_SYMBOL(cpufreq_cooling_unregister);
+#else /*!CONFIG_CPU_FREQ*/
+struct thermal_cooling_device *cpufreq_cooling_register(
+ struct freq_pctg_table *tab_ptr, unsigned int tab_size)
+{
+ return NULL;
+}
+EXPORT_SYMBOL(cpufreq_cooling_register);
+void cpufreq_cooling_unregister(void)
+{
+ return;
+}
+EXPORT_SYMBOL(cpufreq_cooling_unregister);
+#endif /*CONFIG_CPU_FREQ*/
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+struct hotplug_cooling_device {
+ struct thermal_cooling_device *cool_dev;
+ unsigned int hotplug_state;
+ const struct cpumask *allowed_cpus;
+};
+static struct hotplug_cooling_device *hotplug_device;
+
+/*
+ * cpu hotplug cooling device callback functions
+ */
+static int cpuhotplug_get_max_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ *state = 1;
+ return 0;
+}
+
+static int cpuhotplug_get_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long *state)
+{
+ /*This cooling device may be of type ACTIVE, so state field
+ can be 0 or 1*/
+ *state = hotplug_device->hotplug_state;
+ return 0;
+}
+
+/*This cooling may be as PASSIVE/STATE_ACTIVE type*/
+static int cpuhotplug_set_cur_state(struct thermal_cooling_device *cdev,
+ unsigned long state)
+{
+ int cpuid, this_cpu = smp_processor_id();
+
+ if (hotplug_device->hotplug_state == state)
+ return 0;
+
+ /*This cooling device may be of type ACTIVE, so state field
+ can be 0 or 1*/
+ if (state == 1) {
+ for_each_cpu(cpuid, hotplug_device->allowed_cpus) {
+ if (cpu_online(cpuid) && (cpuid != this_cpu))
+ cpu_down(cpuid);
+ }
+ } else if (state == 0) {
+ for_each_cpu(cpuid, hotplug_device->allowed_cpus) {
+ if (!cpu_online(cpuid) && (cpuid != this_cpu))
+ cpu_up(cpuid);
+ }
+ } else
+ return -EINVAL;
+
+ hotplug_device->hotplug_state = state;
+
+ return 0;
+}
+/* bind hotplug callbacks to cpu hotplug cooling device */
+static struct thermal_cooling_device_ops cpuhotplug_cooling_ops = {
+ .get_max_state = cpuhotplug_get_max_state,
+ .get_cur_state = cpuhotplug_get_cur_state,
+ .set_cur_state = cpuhotplug_set_cur_state,
+};
+
+struct thermal_cooling_device *cpuhotplug_cooling_register(
+ const struct cpumask *mask_val)
+{
+ struct thermal_cooling_device *cool_dev;
+
+ hotplug_device =
+ kzalloc(sizeof(struct hotplug_cooling_device), GFP_KERNEL);
+
+ if (!hotplug_device)
+ return ERR_PTR(-ENOMEM);
+
+ cool_dev = thermal_cooling_device_register("thermal-cpuhotplug", NULL,
+ &cpuhotplug_cooling_ops);
+ if (!cool_dev) {
+ kfree(hotplug_device);
+ return ERR_PTR(-EINVAL);
+ }
+
+ hotplug_device->cool_dev = cool_dev;
+ hotplug_device->hotplug_state = 0;
+ hotplug_device->allowed_cpus = mask_val;
+
+ return cool_dev;
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_register);
+
+void cpuhotplug_cooling_unregister(void)
+{
+ thermal_cooling_device_unregister(hotplug_device->cool_dev);
+ kfree(hotplug_device);
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_unregister);
+#else /*!CONFIG_HOTPLUG_CPU*/
+struct thermal_cooling_device *cpuhotplug_cooling_register(
+ const struct cpumask *mask_val)
+{
+ return NULL;
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_register);
+void cpuhotplug_cooling_unregister(void)
+{
+ return;
+}
+EXPORT_SYMBOL(cpuhotplug_cooling_unregister);
+#endif /*CONFIG_HOTPLUG_CPU*/
diff --git a/include/linux/cpu_cooling.h b/include/linux/cpu_cooling.h
new file mode 100644
index 0000000..0c57375
--- /dev/null
+++ b/include/linux/cpu_cooling.h
@@ -0,0 +1,45 @@
+/*
+ * linux/include/linux/cpu_cooling.h
+ *
+ * Copyright (C) 2011 Samsung Electronics Co., Ltd(http://www.samsung.com)
+ * Copyright (C) 2011 Amit Daniel <amit.kachhap@linaro.org>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef __CPU_COOLING_H__
+#define __CPU_COOLING_H__
+
+#include <linux/thermal.h>
+
+struct freq_pctg_table {
+ unsigned int freq_clip_pctg[NR_CPUS];
+ unsigned int polling_interval;
+};
+
+extern struct thermal_cooling_device *cpufreq_cooling_register(
+ struct freq_pctg_table *tab_ptr, unsigned int tab_size,
+ const struct cpumask *mask_val);
+
+extern void cpufreq_cooling_unregister(void);
+
+extern struct thermal_cooling_device *cpuhotplug_cooling_register(
+ const struct cpumask *mask_val);
+
+extern void cpuhotplug_cooling_unregister(void);
+
+#endif /* __CPU_COOLING_H__ */
--
1.7.1
^ permalink raw reply related
* [RFC PATCH 1/2] thermal: Add a new trip type to use cooling device instance number
From: Amit Daniel Kachhap @ 2011-12-13 15:13 UTC (permalink / raw)
To: linux-pm; +Cc: linaro-dev, patches, linux-kernel, linux-acpi
In-Reply-To: <1323789196-4942-1-git-send-email-amit.kachhap@linaro.org>
This patch adds a new trip type THERMAL_TRIP_STATE_ACTIVE. This
trip behaves same as THERMAL_TRIP_ACTIVE but also passes the cooling
device instance number. This helps the cooling device registered as
different instances to perform appropriate cooling action decision in
the set_cur_state call back function.
Also since the trip temperature's are in ascending order so some logic
is put in place to skip the un-necessary checks.
Signed-off-by: Amit Daniel Kachhap <amit.kachhap@linaro.org>
---
Documentation/thermal/sysfs-api.txt | 4 ++--
drivers/thermal/thermal_sys.c | 27 ++++++++++++++++++++++++++-
include/linux/thermal.h | 1 +
3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/Documentation/thermal/sysfs-api.txt b/Documentation/thermal/sysfs-api.txt
index b61e46f..5c1d44e 100644
--- a/Documentation/thermal/sysfs-api.txt
+++ b/Documentation/thermal/sysfs-api.txt
@@ -184,8 +184,8 @@ trip_point_[0-*]_temp
trip_point_[0-*]_type
Strings which indicate the type of the trip point.
- E.g. it can be one of critical, hot, passive, active[0-*] for ACPI
- thermal zone.
+ E.g. it can be one of critical, hot, passive, active[0-1],
+ state-active[0-*] for ACPI thermal zone.
RO, Optional
cdev[0-*]
diff --git a/drivers/thermal/thermal_sys.c b/drivers/thermal/thermal_sys.c
index dd9a574..72b1ab3 100644
--- a/drivers/thermal/thermal_sys.c
+++ b/drivers/thermal/thermal_sys.c
@@ -192,6 +192,8 @@ trip_point_type_show(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "passive\n");
case THERMAL_TRIP_ACTIVE:
return sprintf(buf, "active\n");
+ case THERMAL_TRIP_STATE_ACTIVE:
+ return sprintf(buf, "state-active\n");
default:
return sprintf(buf, "unknown\n");
}
@@ -1035,7 +1037,7 @@ EXPORT_SYMBOL(thermal_cooling_device_unregister);
void thermal_zone_device_update(struct thermal_zone_device *tz)
{
int count, ret = 0;
- long temp, trip_temp;
+ long temp, trip_temp, max_state, last_trip_change = 0;
enum thermal_trip_type trip_type;
struct thermal_cooling_device_instance *instance;
struct thermal_cooling_device *cdev;
@@ -1086,6 +1088,29 @@ void thermal_zone_device_update(struct thermal_zone_device *tz)
cdev->ops->set_cur_state(cdev, 0);
}
break;
+ case THERMAL_TRIP_STATE_ACTIVE:
+ list_for_each_entry(instance, &tz->cooling_devices,
+ node) {
+ if (instance->trip != count)
+ continue;
+
+ if (temp <= last_trip_change)
+ continue;
+
+ cdev = instance->cdev;
+ cdev->ops->get_max_state(cdev, &max_state);
+
+ if ((temp >= trip_temp) &&
+ ((count + 1) <= max_state))
+ cdev->ops->set_cur_state(cdev,
+ count + 1);
+ else if ((temp < trip_temp) &&
+ (count <= max_state))
+ cdev->ops->set_cur_state(cdev, count);
+
+ last_trip_change = trip_temp;
+ }
+ break;
case THERMAL_TRIP_PASSIVE:
if (temp >= trip_temp || tz->passive)
thermal_zone_device_passive(tz, temp,
diff --git a/include/linux/thermal.h b/include/linux/thermal.h
index 47b4a27..d7d0a27 100644
--- a/include/linux/thermal.h
+++ b/include/linux/thermal.h
@@ -42,6 +42,7 @@ enum thermal_trip_type {
THERMAL_TRIP_PASSIVE,
THERMAL_TRIP_HOT,
THERMAL_TRIP_CRITICAL,
+ THERMAL_TRIP_STATE_ACTIVE,
};
struct thermal_zone_device_ops {
--
1.7.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox