Following patch series consolidates backend cpuidle driver
for pseries and powernv platforms.
Current existing backend driver for pseries has been moved to
arch/powerpc/sysdev and has been extended to accommodate
powernv idle power mgmt states.
As seen in V1 of this patch series, too much code duplication persists
having separate back-end driver for individual platforms.
Moving the idle states over to cpuidle framework can take advantage
of advanced heuristics, tunables and features provided by cpuidle
framework. Additional idle states can be exploited using the cpuidle
framework. The statistics and tracing infrastructure provided by
the cpuidle framework also helps in enabling power management
related tools and help tune the system and applications.
This series aims to maintain compatibility and functionality to
existing pseries and powernv idle cpu management code.
There are no new functions or idle states added as part of this series.
This can be extended by adding more states to this existing framework.
With this patch series the powernv cpuidle functionalities
are on-par with pSeries idle management. Other POWERPC
platforms can use this to exploit CPUIDLE framework.
This patch mainly focus on an integrated CPUIDLE backend
driver for POWERPC. Minor cpuidle clean-ups will be
taken up going further.
One need to enable POWERPC_IDLE config option to exploit these
backend drivers.
V1 -> http://lkml.org/lkml/2013/7/23/143
Deepthi Dharwar (6):
cpuidle/pseries: Fix kernel command line parameter smt-snooze-delay
cpuidle/pseries: Remove dependency of pseries.h file
pseries: Move plpar_wrapper.h to powerpc common include/asm location.
cpuidle/pseries: Move the pseries_idle backend driver to sysdev.
cpuidle/powerpc: Backend-powerpc idle driver for powernv and pseries.
cpuidle/powernv: Enable idle powernv cpu to call into the cpuidle framework.
arch/powerpc/include/asm/plpar_wrappers.h | 324 ++++++++++++++++++
arch/powerpc/include/asm/processor.h | 2
arch/powerpc/platforms/powernv/setup.c | 12 +
arch/powerpc/platforms/pseries/Kconfig | 9
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/cmm.c | 3
arch/powerpc/platforms/pseries/dtl.c | 3
arch/powerpc/platforms/pseries/hotplug-cpu.c | 3
arch/powerpc/platforms/pseries/hvconsole.c | 2
arch/powerpc/platforms/pseries/iommu.c | 3
arch/powerpc/platforms/pseries/kexec.c | 2
arch/powerpc/platforms/pseries/lpar.c | 2
arch/powerpc/platforms/pseries/plpar_wrappers.h | 324 ------------------
arch/powerpc/platforms/pseries/processor_idle.c | 362 --------------------
arch/powerpc/platforms/pseries/pseries.h | 3
arch/powerpc/platforms/pseries/setup.c | 2
arch/powerpc/platforms/pseries/smp.c | 2
arch/powerpc/sysdev/Kconfig | 9
arch/powerpc/sysdev/Makefile | 1
arch/powerpc/sysdev/processor_idle.c | 424 +++++++++++++++++++++++
20 files changed, 780 insertions(+), 713 deletions(-)
create mode 100644 arch/powerpc/include/asm/plpar_wrappers.h
delete mode 100644 arch/powerpc/platforms/pseries/plpar_wrappers.h
delete mode 100644 arch/powerpc/platforms/pseries/processor_idle.c
create mode 100644 arch/powerpc/sysdev/processor_idle.c
-- Deepthi
smt-snooze-delay is tunable provided currently on powerpc to delay the
entry of an idle cpu to NAP state. By default, the value is 100us,
which is entry criteria for NAP state i.e only if the idle period is
above 100us it would enter NAP. Value of -1 disables entry into NAP.
This value can be set either through sysfs, ppc64_cpu util or by
passing it via kernel command line. Currently this feature is broken
when the value is passed via the kernel command line.
This patch aims to fix this, by taking the appropritate action
based on the value after the pseries driver is registered.
This check is carried on in the backend driver rather in
setup_smt_snooze_delay() as one is not sure if the cpuidle driver
is even registered when setup routine is executed.
Also, this fixes re-enabling of NAP states by setting appropriate
value without having to reboot.
Also, to note is, smt-snooze-delay is per-cpu variable.
This can be used to enable/disable NAP on per-cpu
basis using sysfs but when this variable is passed
via kernel command line or using the smt-snooze-delay
it applies to all the cpus. Per-cpu tuning can
only be done via sysfs.
Signed-off-by: Deepthi Dharwar <[email protected]>
---
arch/powerpc/platforms/pseries/processor_idle.c | 35 ++++++++++++++++++-----
1 file changed, 28 insertions(+), 7 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 4644efa0..d6a1caa 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -170,18 +170,37 @@ static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
void update_smt_snooze_delay(int cpu, int residency)
{
struct cpuidle_driver *drv = cpuidle_get_driver();
- struct cpuidle_device *dev = per_cpu(cpuidle_devices, cpu);
+ struct cpuidle_device *dev;
if (cpuidle_state_table != dedicated_states)
return;
- if (residency < 0) {
- /* Disable the Nap state on that cpu */
- if (dev)
- dev->states_usage[1].disable = 1;
- } else
- if (drv)
+ if (!drv)
+ return;
+
+ if (cpu == -1) {
+ if (residency < 0) {
+ /* Disable NAP on all cpus */
+ drv->states[1].disabled = true;
+ return;
+ } else {
drv->states[1].target_residency = residency;
+ drv->states[1].disabled = false;
+ return;
+ }
+ }
+
+ dev = per_cpu(cpuidle_devices, cpu);
+ if (!dev)
+ return;
+
+ if (residency < 0)
+ dev->states_usage[1].disable = 1;
+ else {
+ drv->states[1].target_residency = residency;
+ drv->states[1].disabled = false;
+ dev->states_usage[1].disable = 0;
+ }
}
static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
@@ -331,6 +350,8 @@ static int __init pseries_processor_idle_init(void)
return retval;
}
+ update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
+
retval = pseries_idle_devices_init();
if (retval) {
pseries_idle_devices_uninit();
As a part of pseries_idle cleanup to make the backend driver
code common to both pseries and powernv, this patch
is essential to remove the dependency of pseries.h
header file.
Move the declaration of smt_snooze_delay from pseries.h
to processor_idle.c. smt_snooze_delay variable is
needed for both pseries and powernv.
/* Snooze Delay, pseries_idle */
DECLARE_PER_CPU(long, smt_snooze_delay);
Signed-off-by: Deepthi Dharwar <[email protected]>
---
arch/powerpc/platforms/pseries/processor_idle.c | 4 +++-
arch/powerpc/platforms/pseries/pseries.h | 3 ---
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index d6a1caa..64da5cc 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -20,7 +20,9 @@
#include <asm/runlatch.h>
#include "plpar_wrappers.h"
-#include "pseries.h"
+
+/* Snooze Delay, pseries_idle */
+DECLARE_PER_CPU(long, smt_snooze_delay);
struct cpuidle_driver pseries_idle_driver = {
.name = "pseries_idle",
diff --git a/arch/powerpc/platforms/pseries/pseries.h b/arch/powerpc/platforms/pseries/pseries.h
index c2a3a25..d1b07e6 100644
--- a/arch/powerpc/platforms/pseries/pseries.h
+++ b/arch/powerpc/platforms/pseries/pseries.h
@@ -60,9 +60,6 @@ extern struct device_node *dlpar_configure_connector(u32);
extern int dlpar_attach_node(struct device_node *);
extern int dlpar_detach_node(struct device_node *);
-/* Snooze Delay, pseries_idle */
-DECLARE_PER_CPU(long, smt_snooze_delay);
-
/* PCI root bridge prepare function override for pseries */
struct pci_host_bridge;
int pseries_root_bridge_prepare(struct pci_host_bridge *bridge);
As a part of pseries_idle backend driver cleanup to make
the code common to both pseries and powernv archs, it
is necessary to move the backend-driver code to powerpc/sysdev.
As a pre-requisite to that, it is essential to move plpar_wrapper.h
to include/asm.
Signed-off-by: Deepthi Dharwar <[email protected]>
---
arch/powerpc/include/asm/plpar_wrappers.h | 324 +++++++++++++++++++++++
arch/powerpc/platforms/pseries/cmm.c | 3
arch/powerpc/platforms/pseries/dtl.c | 3
arch/powerpc/platforms/pseries/hotplug-cpu.c | 3
arch/powerpc/platforms/pseries/hvconsole.c | 2
arch/powerpc/platforms/pseries/iommu.c | 3
arch/powerpc/platforms/pseries/kexec.c | 2
arch/powerpc/platforms/pseries/lpar.c | 2
arch/powerpc/platforms/pseries/plpar_wrappers.h | 324 -----------------------
arch/powerpc/platforms/pseries/processor_idle.c | 3
arch/powerpc/platforms/pseries/setup.c | 2
arch/powerpc/platforms/pseries/smp.c | 2
12 files changed, 335 insertions(+), 338 deletions(-)
create mode 100644 arch/powerpc/include/asm/plpar_wrappers.h
delete mode 100644 arch/powerpc/platforms/pseries/plpar_wrappers.h
diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h
new file mode 100644
index 0000000..f35787b
--- /dev/null
+++ b/arch/powerpc/include/asm/plpar_wrappers.h
@@ -0,0 +1,324 @@
+#ifndef _PSERIES_PLPAR_WRAPPERS_H
+#define _PSERIES_PLPAR_WRAPPERS_H
+
+#include <linux/string.h>
+#include <linux/irqflags.h>
+
+#include <asm/hvcall.h>
+#include <asm/paca.h>
+#include <asm/page.h>
+
+/* Get state of physical CPU from query_cpu_stopped */
+int smp_query_cpu_stopped(unsigned int pcpu);
+#define QCSS_STOPPED 0
+#define QCSS_STOPPING 1
+#define QCSS_NOT_STOPPED 2
+#define QCSS_HARDWARE_ERROR -1
+#define QCSS_HARDWARE_BUSY -2
+
+static inline long poll_pending(void)
+{
+ return plpar_hcall_norets(H_POLL_PENDING);
+}
+
+static inline u8 get_cede_latency_hint(void)
+{
+ return get_lppaca()->cede_latency_hint;
+}
+
+static inline void set_cede_latency_hint(u8 latency_hint)
+{
+ get_lppaca()->cede_latency_hint = latency_hint;
+}
+
+static inline long cede_processor(void)
+{
+ return plpar_hcall_norets(H_CEDE);
+}
+
+static inline long extended_cede_processor(unsigned long latency_hint)
+{
+ long rc;
+ u8 old_latency_hint = get_cede_latency_hint();
+
+ set_cede_latency_hint(latency_hint);
+
+ rc = cede_processor();
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* Ensure that H_CEDE returns with IRQs on */
+ if (WARN_ON(!(mfmsr() & MSR_EE)))
+ __hard_irq_enable();
+#endif
+
+ set_cede_latency_hint(old_latency_hint);
+
+ return rc;
+}
+
+static inline long vpa_call(unsigned long flags, unsigned long cpu,
+ unsigned long vpa)
+{
+ flags = flags << H_VPA_FUNC_SHIFT;
+
+ return plpar_hcall_norets(H_REGISTER_VPA, flags, cpu, vpa);
+}
+
+static inline long unregister_vpa(unsigned long cpu)
+{
+ return vpa_call(H_VPA_DEREG_VPA, cpu, 0);
+}
+
+static inline long register_vpa(unsigned long cpu, unsigned long vpa)
+{
+ return vpa_call(H_VPA_REG_VPA, cpu, vpa);
+}
+
+static inline long unregister_slb_shadow(unsigned long cpu)
+{
+ return vpa_call(H_VPA_DEREG_SLB, cpu, 0);
+}
+
+static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
+{
+ return vpa_call(H_VPA_REG_SLB, cpu, vpa);
+}
+
+static inline long unregister_dtl(unsigned long cpu)
+{
+ return vpa_call(H_VPA_DEREG_DTL, cpu, 0);
+}
+
+static inline long register_dtl(unsigned long cpu, unsigned long vpa)
+{
+ return vpa_call(H_VPA_REG_DTL, cpu, vpa);
+}
+
+static inline long plpar_page_set_loaned(unsigned long vpa)
+{
+ unsigned long cmo_page_sz = cmo_get_page_size();
+ long rc = 0;
+ int i;
+
+ for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
+ rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED, vpa + i, 0);
+
+ for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
+ plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE,
+ vpa + i - cmo_page_sz, 0);
+
+ return rc;
+}
+
+static inline long plpar_page_set_active(unsigned long vpa)
+{
+ unsigned long cmo_page_sz = cmo_get_page_size();
+ long rc = 0;
+ int i;
+
+ for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
+ rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE, vpa + i, 0);
+
+ for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
+ plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED,
+ vpa + i - cmo_page_sz, 0);
+
+ return rc;
+}
+
+extern void vpa_init(int cpu);
+
+static inline long plpar_pte_enter(unsigned long flags,
+ unsigned long hpte_group, unsigned long hpte_v,
+ unsigned long hpte_r, unsigned long *slot)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_ENTER, retbuf, flags, hpte_group, hpte_v, hpte_r);
+
+ *slot = retbuf[0];
+
+ return rc;
+}
+
+static inline long plpar_pte_remove(unsigned long flags, unsigned long ptex,
+ unsigned long avpn, unsigned long *old_pteh_ret,
+ unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_REMOVE, retbuf, flags, ptex, avpn);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+/* plpar_pte_remove_raw can be called in real mode. It calls plpar_hcall_raw */
+static inline long plpar_pte_remove_raw(unsigned long flags, unsigned long ptex,
+ unsigned long avpn, unsigned long *old_pteh_ret,
+ unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall_raw(H_REMOVE, retbuf, flags, ptex, avpn);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+static inline long plpar_pte_read(unsigned long flags, unsigned long ptex,
+ unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_READ, retbuf, flags, ptex);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+/* plpar_pte_read_raw can be called in real mode. It calls plpar_hcall_raw */
+static inline long plpar_pte_read_raw(unsigned long flags, unsigned long ptex,
+ unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall_raw(H_READ, retbuf, flags, ptex);
+
+ *old_pteh_ret = retbuf[0];
+ *old_ptel_ret = retbuf[1];
+
+ return rc;
+}
+
+/*
+ * plpar_pte_read_4_raw can be called in real mode.
+ * ptes must be 8*sizeof(unsigned long)
+ */
+static inline long plpar_pte_read_4_raw(unsigned long flags, unsigned long ptex,
+ unsigned long *ptes)
+
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
+
+ rc = plpar_hcall9_raw(H_READ, retbuf, flags | H_READ_4, ptex);
+
+ memcpy(ptes, retbuf, 8*sizeof(unsigned long));
+
+ return rc;
+}
+
+static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex,
+ unsigned long avpn)
+{
+ return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
+}
+
+static inline long plpar_tce_get(unsigned long liobn, unsigned long ioba,
+ unsigned long *tce_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+
+ rc = plpar_hcall(H_GET_TCE, retbuf, liobn, ioba);
+
+ *tce_ret = retbuf[0];
+
+ return rc;
+}
+
+static inline long plpar_tce_put(unsigned long liobn, unsigned long ioba,
+ unsigned long tceval)
+{
+ return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);
+}
+
+static inline long plpar_tce_put_indirect(unsigned long liobn,
+ unsigned long ioba, unsigned long page, unsigned long count)
+{
+ return plpar_hcall_norets(H_PUT_TCE_INDIRECT, liobn, ioba, page, count);
+}
+
+static inline long plpar_tce_stuff(unsigned long liobn, unsigned long ioba,
+ unsigned long tceval, unsigned long count)
+{
+ return plpar_hcall_norets(H_STUFF_TCE, liobn, ioba, tceval, count);
+}
+
+static inline long plpar_get_term_char(unsigned long termno,
+ unsigned long *len_ret, char *buf_ret)
+{
+ long rc;
+ unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
+ unsigned long *lbuf = (unsigned long *)buf_ret; /* TODO: alignment? */
+
+ rc = plpar_hcall(H_GET_TERM_CHAR, retbuf, termno);
+
+ *len_ret = retbuf[0];
+ lbuf[0] = retbuf[1];
+ lbuf[1] = retbuf[2];
+
+ return rc;
+}
+
+static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
+ const char *buffer)
+{
+ unsigned long *lbuf = (unsigned long *)buffer; /* TODO: alignment? */
+ return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0],
+ lbuf[1]);
+}
+
+/* Set various resource mode parameters */
+static inline long plpar_set_mode(unsigned long mflags, unsigned long resource,
+ unsigned long value1, unsigned long value2)
+{
+ return plpar_hcall_norets(H_SET_MODE, mflags, resource, value1, value2);
+}
+
+/*
+ * Enable relocation on exceptions on this partition
+ *
+ * Note: this call has a partition wide scope and can take a while to complete.
+ * If it returns H_LONG_BUSY_* it should be retried periodically until it
+ * returns H_SUCCESS.
+ */
+static inline long enable_reloc_on_exceptions(void)
+{
+ /* mflags = 3: Exceptions at 0xC000000000004000 */
+ return plpar_set_mode(3, 3, 0, 0);
+}
+
+/*
+ * Disable relocation on exceptions on this partition
+ *
+ * Note: this call has a partition wide scope and can take a while to complete.
+ * If it returns H_LONG_BUSY_* it should be retried periodically until it
+ * returns H_SUCCESS.
+ */
+static inline long disable_reloc_on_exceptions(void) {
+ return plpar_set_mode(0, 3, 0, 0);
+}
+
+static inline long plapr_set_ciabr(unsigned long ciabr)
+{
+ return plpar_set_mode(0, 1, ciabr, 0);
+}
+
+static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0)
+{
+ return plpar_set_mode(0, 2, dawr0, dawrx0);
+}
+
+#endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/cmm.c b/arch/powerpc/platforms/pseries/cmm.c
index c638535..1e561be 100644
--- a/arch/powerpc/platforms/pseries/cmm.c
+++ b/arch/powerpc/platforms/pseries/cmm.c
@@ -40,8 +40,7 @@
#include <asm/pgalloc.h>
#include <asm/uaccess.h>
#include <linux/memory.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
#define CMM_DRIVER_VERSION "1.0.0"
#define CMM_DEFAULT_DELAY 1
diff --git a/arch/powerpc/platforms/pseries/dtl.c b/arch/powerpc/platforms/pseries/dtl.c
index 0cc0ac0..f6cb051 100644
--- a/arch/powerpc/platforms/pseries/dtl.c
+++ b/arch/powerpc/platforms/pseries/dtl.c
@@ -29,8 +29,7 @@
#include <asm/firmware.h>
#include <asm/lppaca.h>
#include <asm/debug.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
struct dtl {
struct dtl_entry *buf;
diff --git a/arch/powerpc/platforms/pseries/hotplug-cpu.c b/arch/powerpc/platforms/pseries/hotplug-cpu.c
index 217ca5c..a8ef932 100644
--- a/arch/powerpc/platforms/pseries/hotplug-cpu.c
+++ b/arch/powerpc/platforms/pseries/hotplug-cpu.c
@@ -30,7 +30,8 @@
#include <asm/machdep.h>
#include <asm/vdso_datapage.h>
#include <asm/xics.h>
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
+
#include "offline_states.h"
/* This version can't take the spinlock, because it never returns */
diff --git a/arch/powerpc/platforms/pseries/hvconsole.c b/arch/powerpc/platforms/pseries/hvconsole.c
index b344f94..f3f108b 100644
--- a/arch/powerpc/platforms/pseries/hvconsole.c
+++ b/arch/powerpc/platforms/pseries/hvconsole.c
@@ -28,7 +28,7 @@
#include <linux/errno.h>
#include <asm/hvcall.h>
#include <asm/hvconsole.h>
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
/**
* hvc_get_chars - retrieve characters from firmware for denoted vterm adatper
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index 23fc1dc..4821933 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -48,8 +48,7 @@
#include <asm/ppc-pci.h>
#include <asm/udbg.h>
#include <asm/mmzone.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
static void tce_invalidate_pSeries_sw(struct iommu_table *tbl,
diff --git a/arch/powerpc/platforms/pseries/kexec.c b/arch/powerpc/platforms/pseries/kexec.c
index 7d94bdc..13fa95b3 100644
--- a/arch/powerpc/platforms/pseries/kexec.c
+++ b/arch/powerpc/platforms/pseries/kexec.c
@@ -17,9 +17,9 @@
#include <asm/mpic.h>
#include <asm/xics.h>
#include <asm/smp.h>
+#include <asm/plpar_wrappers.h>
#include "pseries.h"
-#include "plpar_wrappers.h"
static void pseries_kexec_cpu_down(int crash_shutdown, int secondary)
{
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 8bad880..e1873bc 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -41,8 +41,8 @@
#include <asm/smp.h>
#include <asm/trace.h>
#include <asm/firmware.h>
+#include <asm/plpar_wrappers.h>
-#include "plpar_wrappers.h"
#include "pseries.h"
/* Flag bits for H_BULK_REMOVE */
diff --git a/arch/powerpc/platforms/pseries/plpar_wrappers.h b/arch/powerpc/platforms/pseries/plpar_wrappers.h
deleted file mode 100644
index f35787b..0000000
--- a/arch/powerpc/platforms/pseries/plpar_wrappers.h
+++ /dev/null
@@ -1,324 +0,0 @@
-#ifndef _PSERIES_PLPAR_WRAPPERS_H
-#define _PSERIES_PLPAR_WRAPPERS_H
-
-#include <linux/string.h>
-#include <linux/irqflags.h>
-
-#include <asm/hvcall.h>
-#include <asm/paca.h>
-#include <asm/page.h>
-
-/* Get state of physical CPU from query_cpu_stopped */
-int smp_query_cpu_stopped(unsigned int pcpu);
-#define QCSS_STOPPED 0
-#define QCSS_STOPPING 1
-#define QCSS_NOT_STOPPED 2
-#define QCSS_HARDWARE_ERROR -1
-#define QCSS_HARDWARE_BUSY -2
-
-static inline long poll_pending(void)
-{
- return plpar_hcall_norets(H_POLL_PENDING);
-}
-
-static inline u8 get_cede_latency_hint(void)
-{
- return get_lppaca()->cede_latency_hint;
-}
-
-static inline void set_cede_latency_hint(u8 latency_hint)
-{
- get_lppaca()->cede_latency_hint = latency_hint;
-}
-
-static inline long cede_processor(void)
-{
- return plpar_hcall_norets(H_CEDE);
-}
-
-static inline long extended_cede_processor(unsigned long latency_hint)
-{
- long rc;
- u8 old_latency_hint = get_cede_latency_hint();
-
- set_cede_latency_hint(latency_hint);
-
- rc = cede_processor();
-#ifdef CONFIG_TRACE_IRQFLAGS
- /* Ensure that H_CEDE returns with IRQs on */
- if (WARN_ON(!(mfmsr() & MSR_EE)))
- __hard_irq_enable();
-#endif
-
- set_cede_latency_hint(old_latency_hint);
-
- return rc;
-}
-
-static inline long vpa_call(unsigned long flags, unsigned long cpu,
- unsigned long vpa)
-{
- flags = flags << H_VPA_FUNC_SHIFT;
-
- return plpar_hcall_norets(H_REGISTER_VPA, flags, cpu, vpa);
-}
-
-static inline long unregister_vpa(unsigned long cpu)
-{
- return vpa_call(H_VPA_DEREG_VPA, cpu, 0);
-}
-
-static inline long register_vpa(unsigned long cpu, unsigned long vpa)
-{
- return vpa_call(H_VPA_REG_VPA, cpu, vpa);
-}
-
-static inline long unregister_slb_shadow(unsigned long cpu)
-{
- return vpa_call(H_VPA_DEREG_SLB, cpu, 0);
-}
-
-static inline long register_slb_shadow(unsigned long cpu, unsigned long vpa)
-{
- return vpa_call(H_VPA_REG_SLB, cpu, vpa);
-}
-
-static inline long unregister_dtl(unsigned long cpu)
-{
- return vpa_call(H_VPA_DEREG_DTL, cpu, 0);
-}
-
-static inline long register_dtl(unsigned long cpu, unsigned long vpa)
-{
- return vpa_call(H_VPA_REG_DTL, cpu, vpa);
-}
-
-static inline long plpar_page_set_loaned(unsigned long vpa)
-{
- unsigned long cmo_page_sz = cmo_get_page_size();
- long rc = 0;
- int i;
-
- for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
- rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED, vpa + i, 0);
-
- for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
- plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE,
- vpa + i - cmo_page_sz, 0);
-
- return rc;
-}
-
-static inline long plpar_page_set_active(unsigned long vpa)
-{
- unsigned long cmo_page_sz = cmo_get_page_size();
- long rc = 0;
- int i;
-
- for (i = 0; !rc && i < PAGE_SIZE; i += cmo_page_sz)
- rc = plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_ACTIVE, vpa + i, 0);
-
- for (i -= cmo_page_sz; rc && i != 0; i -= cmo_page_sz)
- plpar_hcall_norets(H_PAGE_INIT, H_PAGE_SET_LOANED,
- vpa + i - cmo_page_sz, 0);
-
- return rc;
-}
-
-extern void vpa_init(int cpu);
-
-static inline long plpar_pte_enter(unsigned long flags,
- unsigned long hpte_group, unsigned long hpte_v,
- unsigned long hpte_r, unsigned long *slot)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_ENTER, retbuf, flags, hpte_group, hpte_v, hpte_r);
-
- *slot = retbuf[0];
-
- return rc;
-}
-
-static inline long plpar_pte_remove(unsigned long flags, unsigned long ptex,
- unsigned long avpn, unsigned long *old_pteh_ret,
- unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_REMOVE, retbuf, flags, ptex, avpn);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-/* plpar_pte_remove_raw can be called in real mode. It calls plpar_hcall_raw */
-static inline long plpar_pte_remove_raw(unsigned long flags, unsigned long ptex,
- unsigned long avpn, unsigned long *old_pteh_ret,
- unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall_raw(H_REMOVE, retbuf, flags, ptex, avpn);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-static inline long plpar_pte_read(unsigned long flags, unsigned long ptex,
- unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_READ, retbuf, flags, ptex);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-/* plpar_pte_read_raw can be called in real mode. It calls plpar_hcall_raw */
-static inline long plpar_pte_read_raw(unsigned long flags, unsigned long ptex,
- unsigned long *old_pteh_ret, unsigned long *old_ptel_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall_raw(H_READ, retbuf, flags, ptex);
-
- *old_pteh_ret = retbuf[0];
- *old_ptel_ret = retbuf[1];
-
- return rc;
-}
-
-/*
- * plpar_pte_read_4_raw can be called in real mode.
- * ptes must be 8*sizeof(unsigned long)
- */
-static inline long plpar_pte_read_4_raw(unsigned long flags, unsigned long ptex,
- unsigned long *ptes)
-
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
-
- rc = plpar_hcall9_raw(H_READ, retbuf, flags | H_READ_4, ptex);
-
- memcpy(ptes, retbuf, 8*sizeof(unsigned long));
-
- return rc;
-}
-
-static inline long plpar_pte_protect(unsigned long flags, unsigned long ptex,
- unsigned long avpn)
-{
- return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn);
-}
-
-static inline long plpar_tce_get(unsigned long liobn, unsigned long ioba,
- unsigned long *tce_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
-
- rc = plpar_hcall(H_GET_TCE, retbuf, liobn, ioba);
-
- *tce_ret = retbuf[0];
-
- return rc;
-}
-
-static inline long plpar_tce_put(unsigned long liobn, unsigned long ioba,
- unsigned long tceval)
-{
- return plpar_hcall_norets(H_PUT_TCE, liobn, ioba, tceval);
-}
-
-static inline long plpar_tce_put_indirect(unsigned long liobn,
- unsigned long ioba, unsigned long page, unsigned long count)
-{
- return plpar_hcall_norets(H_PUT_TCE_INDIRECT, liobn, ioba, page, count);
-}
-
-static inline long plpar_tce_stuff(unsigned long liobn, unsigned long ioba,
- unsigned long tceval, unsigned long count)
-{
- return plpar_hcall_norets(H_STUFF_TCE, liobn, ioba, tceval, count);
-}
-
-static inline long plpar_get_term_char(unsigned long termno,
- unsigned long *len_ret, char *buf_ret)
-{
- long rc;
- unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
- unsigned long *lbuf = (unsigned long *)buf_ret; /* TODO: alignment? */
-
- rc = plpar_hcall(H_GET_TERM_CHAR, retbuf, termno);
-
- *len_ret = retbuf[0];
- lbuf[0] = retbuf[1];
- lbuf[1] = retbuf[2];
-
- return rc;
-}
-
-static inline long plpar_put_term_char(unsigned long termno, unsigned long len,
- const char *buffer)
-{
- unsigned long *lbuf = (unsigned long *)buffer; /* TODO: alignment? */
- return plpar_hcall_norets(H_PUT_TERM_CHAR, termno, len, lbuf[0],
- lbuf[1]);
-}
-
-/* Set various resource mode parameters */
-static inline long plpar_set_mode(unsigned long mflags, unsigned long resource,
- unsigned long value1, unsigned long value2)
-{
- return plpar_hcall_norets(H_SET_MODE, mflags, resource, value1, value2);
-}
-
-/*
- * Enable relocation on exceptions on this partition
- *
- * Note: this call has a partition wide scope and can take a while to complete.
- * If it returns H_LONG_BUSY_* it should be retried periodically until it
- * returns H_SUCCESS.
- */
-static inline long enable_reloc_on_exceptions(void)
-{
- /* mflags = 3: Exceptions at 0xC000000000004000 */
- return plpar_set_mode(3, 3, 0, 0);
-}
-
-/*
- * Disable relocation on exceptions on this partition
- *
- * Note: this call has a partition wide scope and can take a while to complete.
- * If it returns H_LONG_BUSY_* it should be retried periodically until it
- * returns H_SUCCESS.
- */
-static inline long disable_reloc_on_exceptions(void) {
- return plpar_set_mode(0, 3, 0, 0);
-}
-
-static inline long plapr_set_ciabr(unsigned long ciabr)
-{
- return plpar_set_mode(0, 1, ciabr, 0);
-}
-
-static inline long plapr_set_watchpoint0(unsigned long dawr0, unsigned long dawrx0)
-{
- return plpar_set_mode(0, 2, dawr0, dawrx0);
-}
-
-#endif /* _PSERIES_PLPAR_WRAPPERS_H */
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
index 64da5cc..0d75a54 100644
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ b/arch/powerpc/platforms/pseries/processor_idle.c
@@ -18,8 +18,7 @@
#include <asm/machdep.h>
#include <asm/firmware.h>
#include <asm/runlatch.h>
-
-#include "plpar_wrappers.h"
+#include <asm/plpar_wrappers.h>
/* Snooze Delay, pseries_idle */
DECLARE_PER_CPU(long, smt_snooze_delay);
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c11c823..4291589 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -66,8 +66,8 @@
#include <asm/firmware.h>
#include <asm/eeh.h>
#include <asm/reg.h>
+#include <asm/plpar_wrappers.h>
-#include "plpar_wrappers.h"
#include "pseries.h"
int CMO_PrPSP = -1;
diff --git a/arch/powerpc/platforms/pseries/smp.c b/arch/powerpc/platforms/pseries/smp.c
index 306643c..1c79af7 100644
--- a/arch/powerpc/platforms/pseries/smp.c
+++ b/arch/powerpc/platforms/pseries/smp.c
@@ -43,8 +43,8 @@
#include <asm/cputhreads.h>
#include <asm/xics.h>
#include <asm/dbell.h>
+#include <asm/plpar_wrappers.h>
-#include "plpar_wrappers.h"
#include "pseries.h"
#include "offline_states.h"
This patch enables idle powernv cpu to hook on to the cpuidle
framework, if available, else call on to default idle platform
code.
Signed-off-by: Deepthi Dharwar <[email protected]>
---
arch/powerpc/platforms/powernv/setup.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/platforms/powernv/setup.c b/arch/powerpc/platforms/powernv/setup.c
index 84438af..d4283dd 100644
--- a/arch/powerpc/platforms/powernv/setup.c
+++ b/arch/powerpc/platforms/powernv/setup.c
@@ -25,6 +25,7 @@
#include <linux/of.h>
#include <linux/interrupt.h>
#include <linux/bug.h>
+#include <linux/cpuidle.h>
#include <asm/machdep.h>
#include <asm/firmware.h>
@@ -196,6 +197,15 @@ static int __init pnv_probe(void)
return 1;
}
+void powernv_idle(void)
+{
+ /* Hook to cpuidle framework if available, else
+ * call on default platform idle code
+ */
+ if (cpuidle_idle_call())
+ power7_idle();
+}
+
define_machine(powernv) {
.name = "PowerNV",
.probe = pnv_probe,
@@ -205,7 +215,7 @@ define_machine(powernv) {
.show_cpuinfo = pnv_show_cpuinfo,
.progress = pnv_progress,
.machine_shutdown = pnv_shutdown,
- .power_save = power7_idle,
+ .power_save = powernv_idle,
.calibrate_decr = generic_calibrate_decr,
#ifdef CONFIG_KEXEC
.kexec_cpu_down = pnv_kexec_cpu_down,
The following patch extends the current pseries backend
idle driver to powernv platform.
Signed-off-by: Deepthi Dharwar <[email protected]>
---
arch/powerpc/include/asm/processor.h | 2 -
arch/powerpc/sysdev/Kconfig | 8 +-
arch/powerpc/sysdev/Makefile | 2 -
arch/powerpc/sysdev/processor_idle.c | 132 ++++++++++++++++++++++------------
4 files changed, 92 insertions(+), 52 deletions(-)
diff --git a/arch/powerpc/include/asm/processor.h b/arch/powerpc/include/asm/processor.h
index 47a35b0..e64b817 100644
--- a/arch/powerpc/include/asm/processor.h
+++ b/arch/powerpc/include/asm/processor.h
@@ -426,7 +426,7 @@ enum idle_boot_override {IDLE_NO_OVERRIDE = 0, IDLE_POWERSAVE_OFF};
extern int powersave_nap; /* set if nap mode can be used in idle loop */
extern void power7_nap(void);
-#ifdef CONFIG_PSERIES_IDLE
+#ifdef CONFIG_POWERPC_IDLE
extern void update_smt_snooze_delay(int cpu, int residency);
#else
static inline void update_smt_snooze_delay(int cpu, int residency) {}
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index 8564a3f..f61d794 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -35,11 +35,11 @@ config GE_FPGA
bool
default n
-config PSERIES_IDLE
- bool "Cpuidle driver for pSeries platforms"
+config POWERPC_IDLE
+ bool "Cpuidle driver for POWERPC platforms"
depends on CPU_IDLE
- depends on PPC_PSERIES
+ depends on PPC_PSERIES || PPC_POWERNV
default y
help
Select this option to enable processor idle state management
- for pSeries through cpuidle subsystem.
+ for POWER and POWERNV through cpuidle subsystem.
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 93d2cdd..ec290e2 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -49,7 +49,7 @@ endif
obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
-obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
+obj-$(CONFIG_POWERPC_IDLE) += processor_idle.o
obj-$(CONFIG_CPM) += cpm_common.o
obj-$(CONFIG_CPM2) += cpm2.o cpm2_pic.o
diff --git a/arch/powerpc/sysdev/processor_idle.c b/arch/powerpc/sysdev/processor_idle.c
index 0d75a54..d152f540d 100644
--- a/arch/powerpc/sysdev/processor_idle.c
+++ b/arch/powerpc/sysdev/processor_idle.c
@@ -20,18 +20,18 @@
#include <asm/runlatch.h>
#include <asm/plpar_wrappers.h>
-/* Snooze Delay, pseries_idle */
+/* Snooze Delay, powerpc_idle */
DECLARE_PER_CPU(long, smt_snooze_delay);
-struct cpuidle_driver pseries_idle_driver = {
- .name = "pseries_idle",
+struct cpuidle_driver powerpc_idle_driver = {
+ .name = "powerpc_idle",
.owner = THIS_MODULE,
};
#define MAX_IDLE_STATE_COUNT 2
static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
-static struct cpuidle_device __percpu *pseries_cpuidle_devices;
+static struct cpuidle_device __percpu *powerpc_cpuidle_devices;
static struct cpuidle_state *cpuidle_state_table;
static inline void idle_loop_prolog(unsigned long *in_purr)
@@ -55,13 +55,14 @@ static int snooze_loop(struct cpuidle_device *dev,
int index)
{
unsigned long in_purr;
- int cpu = dev->cpu;
+#ifndef PPC_POWERNV
idle_loop_prolog(&in_purr);
+#endif
local_irq_enable();
set_thread_flag(TIF_POLLING_NRFLAG);
- while ((!need_resched()) && cpu_online(cpu)) {
+ while (!need_resched()) {
ppc64_runlatch_off();
HMT_low();
HMT_very_low();
@@ -71,7 +72,9 @@ static int snooze_loop(struct cpuidle_device *dev,
clear_thread_flag(TIF_POLLING_NRFLAG);
smp_mb();
+#ifndef PPC_POWERNV
idle_loop_epilog(in_purr);
+#endif
return index;
}
@@ -135,10 +138,21 @@ static int shared_cede_loop(struct cpuidle_device *dev,
return index;
}
+#ifdef PPC_POWERNV
+static int nap_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ ppc64_runlatch_off();
+ power7_idle();
+ return index;
+}
+#endif
+
/*
* States for dedicated partition case.
*/
-static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
+static struct cpuidle_state pseries_dedicated_states[MAX_IDLE_STATE_COUNT] = {
{ /* Snooze */
.name = "snooze",
.desc = "snooze",
@@ -158,7 +172,7 @@ static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
/*
* States for shared partition case.
*/
-static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
+static struct cpuidle_state pseries_shared_states[MAX_IDLE_STATE_COUNT] = {
{ /* Shared Cede */
.name = "Shared Cede",
.desc = "Shared Cede",
@@ -168,13 +182,34 @@ static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
.enter = &shared_cede_loop },
};
+#ifdef PPC_POWERNV
+static struct cpuidle_state powernv_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Snooze */
+ .name = "snooze",
+ .desc = "snooze",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &snooze_loop },
+ { /* CEDE */
+ .name = "CEDE",
+ .desc = "CEDE",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 100,
+ .enter = &nap_loop },
+};
+#endif
+
void update_smt_snooze_delay(int cpu, int residency)
{
struct cpuidle_driver *drv = cpuidle_get_driver();
struct cpuidle_device *dev;
- if (cpuidle_state_table != dedicated_states)
+#ifndef PPC_POWERNV
+ if (cpuidle_state_table != pseries_dedicated_states)
return;
+#endif
if (!drv)
return;
@@ -204,12 +239,12 @@ void update_smt_snooze_delay(int cpu, int residency)
}
}
-static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
+static int powerpc_cpuidle_add_cpu_notifier(struct notifier_block *n,
unsigned long action, void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
struct cpuidle_device *dev =
- per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
+ per_cpu_ptr(powerpc_cpuidle_devices, hotcpu);
if (dev && cpuidle_get_driver()) {
switch (action) {
@@ -235,16 +270,16 @@ static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
}
static struct notifier_block setup_hotplug_notifier = {
- .notifier_call = pseries_cpuidle_add_cpu_notifier,
+ .notifier_call = powerpc_cpuidle_add_cpu_notifier,
};
/*
- * pseries_cpuidle_driver_init()
+ * powerpc_cpuidle_driver_init()
*/
-static int pseries_cpuidle_driver_init(void)
+static int powerpc_cpuidle_driver_init(void)
{
int idle_state;
- struct cpuidle_driver *drv = &pseries_idle_driver;
+ struct cpuidle_driver *drv = &powerpc_idle_driver;
drv->state_count = 0;
@@ -266,38 +301,38 @@ static int pseries_cpuidle_driver_init(void)
return 0;
}
-/* pseries_idle_devices_uninit(void)
+/* powerpc_idle_devices_uninit(void)
* unregister cpuidle devices and de-allocate memory
*/
-static void pseries_idle_devices_uninit(void)
+static void powerpc_idle_devices_uninit(void)
{
int i;
struct cpuidle_device *dev;
for_each_possible_cpu(i) {
- dev = per_cpu_ptr(pseries_cpuidle_devices, i);
+ dev = per_cpu_ptr(powerpc_cpuidle_devices, i);
cpuidle_unregister_device(dev);
}
- free_percpu(pseries_cpuidle_devices);
+ free_percpu(powerpc_cpuidle_devices);
return;
}
-/* pseries_idle_devices_init()
+/* powerpc_idle_devices_init()
* allocate, initialize and register cpuidle device
*/
-static int pseries_idle_devices_init(void)
+static int powerpc_idle_devices_init(void)
{
int i;
- struct cpuidle_driver *drv = &pseries_idle_driver;
+ struct cpuidle_driver *drv = &powerpc_idle_driver;
struct cpuidle_device *dev;
- pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
- if (pseries_cpuidle_devices == NULL)
+ powerpc_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+ if (powerpc_cpuidle_devices == NULL)
return -ENOMEM;
for_each_possible_cpu(i) {
- dev = per_cpu_ptr(pseries_cpuidle_devices, i);
+ dev = per_cpu_ptr(powerpc_cpuidle_devices, i);
dev->state_count = drv->state_count;
dev->cpu = i;
if (cpuidle_register_device(dev)) {
@@ -311,74 +346,79 @@ static int pseries_idle_devices_init(void)
}
/*
- * pseries_idle_probe()
+ * powerpc_idle_probe()
* Choose state table for shared versus dedicated partition
*/
-static int pseries_idle_probe(void)
+static int powerpc_idle_probe(void)
{
+#ifndef PPC_POWERNV
if (!firmware_has_feature(FW_FEATURE_SPLPAR))
return -ENODEV;
+#endif
if (cpuidle_disable != IDLE_NO_OVERRIDE)
return -ENODEV;
if (max_idle_state == 0) {
- printk(KERN_DEBUG "pseries processor idle disabled.\n");
+ printk(KERN_DEBUG "powerpc processor idle disabled.\n");
return -EPERM;
}
+#ifdef PPC_POWERNV
+ cpuidle_state_table = powernv_states;
+#else
if (get_lppaca()->shared_proc)
- cpuidle_state_table = shared_states;
+ cpuidle_state_table = pseries_shared_states;
else
- cpuidle_state_table = dedicated_states;
-
+ cpuidle_state_table = pseries_dedicated_states;
+#endif
return 0;
}
-static int __init pseries_processor_idle_init(void)
+static int __init powerpc_processor_idle_init(void)
{
int retval;
- retval = pseries_idle_probe();
+ retval = powerpc_idle_probe();
if (retval)
return retval;
- pseries_cpuidle_driver_init();
- retval = cpuidle_register_driver(&pseries_idle_driver);
+ powerpc_cpuidle_driver_init();
+ retval = cpuidle_register_driver(&powerpc_idle_driver);
if (retval) {
- printk(KERN_DEBUG "Registration of pseries driver failed.\n");
+ printk(KERN_DEBUG "Registration of powerpc driver failed.\n");
return retval;
}
update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
- retval = pseries_idle_devices_init();
+ retval = powerpc_idle_devices_init();
if (retval) {
- pseries_idle_devices_uninit();
- cpuidle_unregister_driver(&pseries_idle_driver);
+ powerpc_idle_devices_uninit();
+ cpuidle_unregister_driver(&powerpc_idle_driver);
return retval;
}
register_cpu_notifier(&setup_hotplug_notifier);
- printk(KERN_DEBUG "pseries_idle_driver registered\n");
+ printk(KERN_DEBUG "powerpc_idle_driver registered\n");
return 0;
}
-static void __exit pseries_processor_idle_exit(void)
+static void __exit powerpc_processor_idle_exit(void)
{
unregister_cpu_notifier(&setup_hotplug_notifier);
- pseries_idle_devices_uninit();
- cpuidle_unregister_driver(&pseries_idle_driver);
+ powerpc_idle_devices_uninit();
+ cpuidle_unregister_driver(&powerpc_idle_driver);
return;
}
-module_init(pseries_processor_idle_init);
-module_exit(pseries_processor_idle_exit);
+module_init(powerpc_processor_idle_init);
+module_exit(powerpc_processor_idle_exit);
MODULE_AUTHOR("Deepthi Dharwar <[email protected]>");
-MODULE_DESCRIPTION("Cpuidle driver for POWER");
+MODULE_DESCRIPTION("Cpuidle driver for POWERPC");
MODULE_LICENSE("GPL");
Move pseries_idle backend driver code to arch/powerpc/sysdev
so that the code can be used for a common driver for powernv
and pseries. This removes a lot of code duplicacy.
Signed-off-by: Deepthi Dharwar <[email protected]>
---
arch/powerpc/platforms/pseries/Kconfig | 9 -
arch/powerpc/platforms/pseries/Makefile | 1
arch/powerpc/platforms/pseries/processor_idle.c | 384 -----------------------
arch/powerpc/sysdev/Kconfig | 9 +
arch/powerpc/sysdev/Makefile | 1
arch/powerpc/sysdev/processor_idle.c | 384 +++++++++++++++++++++++
6 files changed, 394 insertions(+), 394 deletions(-)
delete mode 100644 arch/powerpc/platforms/pseries/processor_idle.c
create mode 100644 arch/powerpc/sysdev/processor_idle.c
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 62b4f80..bb59bb0 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -119,12 +119,3 @@ config DTL
which are accessible through a debugfs file.
Say N if you are unsure.
-
-config PSERIES_IDLE
- bool "Cpuidle driver for pSeries platforms"
- depends on CPU_IDLE
- depends on PPC_PSERIES
- default y
- help
- Select this option to enable processor idle state management
- through cpuidle subsystem.
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 8ae0103..4b22379 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -21,7 +21,6 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
obj-$(CONFIG_CMM) += cmm.o
obj-$(CONFIG_DTL) += dtl.o
obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o
-obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
ifeq ($(CONFIG_PPC_PSERIES),y)
obj-$(CONFIG_SUSPEND) += suspend.o
diff --git a/arch/powerpc/platforms/pseries/processor_idle.c b/arch/powerpc/platforms/pseries/processor_idle.c
deleted file mode 100644
index 0d75a54..0000000
--- a/arch/powerpc/platforms/pseries/processor_idle.c
+++ /dev/null
@@ -1,384 +0,0 @@
-/*
- * processor_idle - idle state cpuidle driver.
- * Adapted from drivers/idle/intel_idle.c and
- * drivers/acpi/processor_idle.c
- *
- */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/moduleparam.h>
-#include <linux/cpuidle.h>
-#include <linux/cpu.h>
-#include <linux/notifier.h>
-
-#include <asm/paca.h>
-#include <asm/reg.h>
-#include <asm/machdep.h>
-#include <asm/firmware.h>
-#include <asm/runlatch.h>
-#include <asm/plpar_wrappers.h>
-
-/* Snooze Delay, pseries_idle */
-DECLARE_PER_CPU(long, smt_snooze_delay);
-
-struct cpuidle_driver pseries_idle_driver = {
- .name = "pseries_idle",
- .owner = THIS_MODULE,
-};
-
-#define MAX_IDLE_STATE_COUNT 2
-
-static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
-static struct cpuidle_device __percpu *pseries_cpuidle_devices;
-static struct cpuidle_state *cpuidle_state_table;
-
-static inline void idle_loop_prolog(unsigned long *in_purr)
-{
- *in_purr = mfspr(SPRN_PURR);
- /*
- * Indicate to the HV that we are idle. Now would be
- * a good time to find other work to dispatch.
- */
- get_lppaca()->idle = 1;
-}
-
-static inline void idle_loop_epilog(unsigned long in_purr)
-{
- get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
- get_lppaca()->idle = 0;
-}
-
-static int snooze_loop(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
-{
- unsigned long in_purr;
- int cpu = dev->cpu;
-
- idle_loop_prolog(&in_purr);
- local_irq_enable();
- set_thread_flag(TIF_POLLING_NRFLAG);
-
- while ((!need_resched()) && cpu_online(cpu)) {
- ppc64_runlatch_off();
- HMT_low();
- HMT_very_low();
- }
-
- HMT_medium();
- clear_thread_flag(TIF_POLLING_NRFLAG);
- smp_mb();
-
- idle_loop_epilog(in_purr);
-
- return index;
-}
-
-static void check_and_cede_processor(void)
-{
- /*
- * Ensure our interrupt state is properly tracked,
- * also checks if no interrupt has occurred while we
- * were soft-disabled
- */
- if (prep_irq_for_idle()) {
- cede_processor();
-#ifdef CONFIG_TRACE_IRQFLAGS
- /* Ensure that H_CEDE returns with IRQs on */
- if (WARN_ON(!(mfmsr() & MSR_EE)))
- __hard_irq_enable();
-#endif
- }
-}
-
-static int dedicated_cede_loop(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
-{
- unsigned long in_purr;
-
- idle_loop_prolog(&in_purr);
- get_lppaca()->donate_dedicated_cpu = 1;
-
- ppc64_runlatch_off();
- HMT_medium();
- check_and_cede_processor();
-
- get_lppaca()->donate_dedicated_cpu = 0;
-
- idle_loop_epilog(in_purr);
-
- return index;
-}
-
-static int shared_cede_loop(struct cpuidle_device *dev,
- struct cpuidle_driver *drv,
- int index)
-{
- unsigned long in_purr;
-
- idle_loop_prolog(&in_purr);
-
- /*
- * Yield the processor to the hypervisor. We return if
- * an external interrupt occurs (which are driven prior
- * to returning here) or if a prod occurs from another
- * processor. When returning here, external interrupts
- * are enabled.
- */
- check_and_cede_processor();
-
- idle_loop_epilog(in_purr);
-
- return index;
-}
-
-/*
- * States for dedicated partition case.
- */
-static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
- { /* Snooze */
- .name = "snooze",
- .desc = "snooze",
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 0,
- .target_residency = 0,
- .enter = &snooze_loop },
- { /* CEDE */
- .name = "CEDE",
- .desc = "CEDE",
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 10,
- .target_residency = 100,
- .enter = &dedicated_cede_loop },
-};
-
-/*
- * States for shared partition case.
- */
-static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
- { /* Shared Cede */
- .name = "Shared Cede",
- .desc = "Shared Cede",
- .flags = CPUIDLE_FLAG_TIME_VALID,
- .exit_latency = 0,
- .target_residency = 0,
- .enter = &shared_cede_loop },
-};
-
-void update_smt_snooze_delay(int cpu, int residency)
-{
- struct cpuidle_driver *drv = cpuidle_get_driver();
- struct cpuidle_device *dev;
-
- if (cpuidle_state_table != dedicated_states)
- return;
-
- if (!drv)
- return;
-
- if (cpu == -1) {
- if (residency < 0) {
- /* Disable NAP on all cpus */
- drv->states[1].disabled = true;
- return;
- } else {
- drv->states[1].target_residency = residency;
- drv->states[1].disabled = false;
- return;
- }
- }
-
- dev = per_cpu(cpuidle_devices, cpu);
- if (!dev)
- return;
-
- if (residency < 0)
- dev->states_usage[1].disable = 1;
- else {
- drv->states[1].target_residency = residency;
- drv->states[1].disabled = false;
- dev->states_usage[1].disable = 0;
- }
-}
-
-static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
- unsigned long action, void *hcpu)
-{
- int hotcpu = (unsigned long)hcpu;
- struct cpuidle_device *dev =
- per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
-
- if (dev && cpuidle_get_driver()) {
- switch (action) {
- case CPU_ONLINE:
- case CPU_ONLINE_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_enable_device(dev);
- cpuidle_resume_and_unlock();
- break;
-
- case CPU_DEAD:
- case CPU_DEAD_FROZEN:
- cpuidle_pause_and_lock();
- cpuidle_disable_device(dev);
- cpuidle_resume_and_unlock();
- break;
-
- default:
- return NOTIFY_DONE;
- }
- }
- return NOTIFY_OK;
-}
-
-static struct notifier_block setup_hotplug_notifier = {
- .notifier_call = pseries_cpuidle_add_cpu_notifier,
-};
-
-/*
- * pseries_cpuidle_driver_init()
- */
-static int pseries_cpuidle_driver_init(void)
-{
- int idle_state;
- struct cpuidle_driver *drv = &pseries_idle_driver;
-
- drv->state_count = 0;
-
- for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
-
- if (idle_state > max_idle_state)
- break;
-
- /* is the state not enabled? */
- if (cpuidle_state_table[idle_state].enter == NULL)
- continue;
-
- drv->states[drv->state_count] = /* structure copy */
- cpuidle_state_table[idle_state];
-
- drv->state_count += 1;
- }
-
- return 0;
-}
-
-/* pseries_idle_devices_uninit(void)
- * unregister cpuidle devices and de-allocate memory
- */
-static void pseries_idle_devices_uninit(void)
-{
- int i;
- struct cpuidle_device *dev;
-
- for_each_possible_cpu(i) {
- dev = per_cpu_ptr(pseries_cpuidle_devices, i);
- cpuidle_unregister_device(dev);
- }
-
- free_percpu(pseries_cpuidle_devices);
- return;
-}
-
-/* pseries_idle_devices_init()
- * allocate, initialize and register cpuidle device
- */
-static int pseries_idle_devices_init(void)
-{
- int i;
- struct cpuidle_driver *drv = &pseries_idle_driver;
- struct cpuidle_device *dev;
-
- pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
- if (pseries_cpuidle_devices == NULL)
- return -ENOMEM;
-
- for_each_possible_cpu(i) {
- dev = per_cpu_ptr(pseries_cpuidle_devices, i);
- dev->state_count = drv->state_count;
- dev->cpu = i;
- if (cpuidle_register_device(dev)) {
- printk(KERN_DEBUG \
- "cpuidle_register_device %d failed!\n", i);
- return -EIO;
- }
- }
-
- return 0;
-}
-
-/*
- * pseries_idle_probe()
- * Choose state table for shared versus dedicated partition
- */
-static int pseries_idle_probe(void)
-{
-
- if (!firmware_has_feature(FW_FEATURE_SPLPAR))
- return -ENODEV;
-
- if (cpuidle_disable != IDLE_NO_OVERRIDE)
- return -ENODEV;
-
- if (max_idle_state == 0) {
- printk(KERN_DEBUG "pseries processor idle disabled.\n");
- return -EPERM;
- }
-
- if (get_lppaca()->shared_proc)
- cpuidle_state_table = shared_states;
- else
- cpuidle_state_table = dedicated_states;
-
- return 0;
-}
-
-static int __init pseries_processor_idle_init(void)
-{
- int retval;
-
- retval = pseries_idle_probe();
- if (retval)
- return retval;
-
- pseries_cpuidle_driver_init();
- retval = cpuidle_register_driver(&pseries_idle_driver);
- if (retval) {
- printk(KERN_DEBUG "Registration of pseries driver failed.\n");
- return retval;
- }
-
- update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
-
- retval = pseries_idle_devices_init();
- if (retval) {
- pseries_idle_devices_uninit();
- cpuidle_unregister_driver(&pseries_idle_driver);
- return retval;
- }
-
- register_cpu_notifier(&setup_hotplug_notifier);
- printk(KERN_DEBUG "pseries_idle_driver registered\n");
-
- return 0;
-}
-
-static void __exit pseries_processor_idle_exit(void)
-{
-
- unregister_cpu_notifier(&setup_hotplug_notifier);
- pseries_idle_devices_uninit();
- cpuidle_unregister_driver(&pseries_idle_driver);
-
- return;
-}
-
-module_init(pseries_processor_idle_init);
-module_exit(pseries_processor_idle_exit);
-
-MODULE_AUTHOR("Deepthi Dharwar <[email protected]>");
-MODULE_DESCRIPTION("Cpuidle driver for POWER");
-MODULE_LICENSE("GPL");
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
index ab4cb54..8564a3f 100644
--- a/arch/powerpc/sysdev/Kconfig
+++ b/arch/powerpc/sysdev/Kconfig
@@ -34,3 +34,12 @@ config SCOM_DEBUGFS
config GE_FPGA
bool
default n
+
+config PSERIES_IDLE
+ bool "Cpuidle driver for pSeries platforms"
+ depends on CPU_IDLE
+ depends on PPC_PSERIES
+ default y
+ help
+ Select this option to enable processor idle state management
+ for pSeries through cpuidle subsystem.
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index f67ac90..93d2cdd 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -49,6 +49,7 @@ endif
obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
+obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
obj-$(CONFIG_CPM) += cpm_common.o
obj-$(CONFIG_CPM2) += cpm2.o cpm2_pic.o
diff --git a/arch/powerpc/sysdev/processor_idle.c b/arch/powerpc/sysdev/processor_idle.c
new file mode 100644
index 0000000..0d75a54
--- /dev/null
+++ b/arch/powerpc/sysdev/processor_idle.c
@@ -0,0 +1,384 @@
+/*
+ * processor_idle - idle state cpuidle driver.
+ * Adapted from drivers/idle/intel_idle.c and
+ * drivers/acpi/processor_idle.c
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/cpuidle.h>
+#include <linux/cpu.h>
+#include <linux/notifier.h>
+
+#include <asm/paca.h>
+#include <asm/reg.h>
+#include <asm/machdep.h>
+#include <asm/firmware.h>
+#include <asm/runlatch.h>
+#include <asm/plpar_wrappers.h>
+
+/* Snooze Delay, pseries_idle */
+DECLARE_PER_CPU(long, smt_snooze_delay);
+
+struct cpuidle_driver pseries_idle_driver = {
+ .name = "pseries_idle",
+ .owner = THIS_MODULE,
+};
+
+#define MAX_IDLE_STATE_COUNT 2
+
+static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
+static struct cpuidle_device __percpu *pseries_cpuidle_devices;
+static struct cpuidle_state *cpuidle_state_table;
+
+static inline void idle_loop_prolog(unsigned long *in_purr)
+{
+ *in_purr = mfspr(SPRN_PURR);
+ /*
+ * Indicate to the HV that we are idle. Now would be
+ * a good time to find other work to dispatch.
+ */
+ get_lppaca()->idle = 1;
+}
+
+static inline void idle_loop_epilog(unsigned long in_purr)
+{
+ get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
+ get_lppaca()->idle = 0;
+}
+
+static int snooze_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+ int cpu = dev->cpu;
+
+ idle_loop_prolog(&in_purr);
+ local_irq_enable();
+ set_thread_flag(TIF_POLLING_NRFLAG);
+
+ while ((!need_resched()) && cpu_online(cpu)) {
+ ppc64_runlatch_off();
+ HMT_low();
+ HMT_very_low();
+ }
+
+ HMT_medium();
+ clear_thread_flag(TIF_POLLING_NRFLAG);
+ smp_mb();
+
+ idle_loop_epilog(in_purr);
+
+ return index;
+}
+
+static void check_and_cede_processor(void)
+{
+ /*
+ * Ensure our interrupt state is properly tracked,
+ * also checks if no interrupt has occurred while we
+ * were soft-disabled
+ */
+ if (prep_irq_for_idle()) {
+ cede_processor();
+#ifdef CONFIG_TRACE_IRQFLAGS
+ /* Ensure that H_CEDE returns with IRQs on */
+ if (WARN_ON(!(mfmsr() & MSR_EE)))
+ __hard_irq_enable();
+#endif
+ }
+}
+
+static int dedicated_cede_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+
+ idle_loop_prolog(&in_purr);
+ get_lppaca()->donate_dedicated_cpu = 1;
+
+ ppc64_runlatch_off();
+ HMT_medium();
+ check_and_cede_processor();
+
+ get_lppaca()->donate_dedicated_cpu = 0;
+
+ idle_loop_epilog(in_purr);
+
+ return index;
+}
+
+static int shared_cede_loop(struct cpuidle_device *dev,
+ struct cpuidle_driver *drv,
+ int index)
+{
+ unsigned long in_purr;
+
+ idle_loop_prolog(&in_purr);
+
+ /*
+ * Yield the processor to the hypervisor. We return if
+ * an external interrupt occurs (which are driven prior
+ * to returning here) or if a prod occurs from another
+ * processor. When returning here, external interrupts
+ * are enabled.
+ */
+ check_and_cede_processor();
+
+ idle_loop_epilog(in_purr);
+
+ return index;
+}
+
+/*
+ * States for dedicated partition case.
+ */
+static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Snooze */
+ .name = "snooze",
+ .desc = "snooze",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &snooze_loop },
+ { /* CEDE */
+ .name = "CEDE",
+ .desc = "CEDE",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 10,
+ .target_residency = 100,
+ .enter = &dedicated_cede_loop },
+};
+
+/*
+ * States for shared partition case.
+ */
+static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
+ { /* Shared Cede */
+ .name = "Shared Cede",
+ .desc = "Shared Cede",
+ .flags = CPUIDLE_FLAG_TIME_VALID,
+ .exit_latency = 0,
+ .target_residency = 0,
+ .enter = &shared_cede_loop },
+};
+
+void update_smt_snooze_delay(int cpu, int residency)
+{
+ struct cpuidle_driver *drv = cpuidle_get_driver();
+ struct cpuidle_device *dev;
+
+ if (cpuidle_state_table != dedicated_states)
+ return;
+
+ if (!drv)
+ return;
+
+ if (cpu == -1) {
+ if (residency < 0) {
+ /* Disable NAP on all cpus */
+ drv->states[1].disabled = true;
+ return;
+ } else {
+ drv->states[1].target_residency = residency;
+ drv->states[1].disabled = false;
+ return;
+ }
+ }
+
+ dev = per_cpu(cpuidle_devices, cpu);
+ if (!dev)
+ return;
+
+ if (residency < 0)
+ dev->states_usage[1].disable = 1;
+ else {
+ drv->states[1].target_residency = residency;
+ drv->states[1].disabled = false;
+ dev->states_usage[1].disable = 0;
+ }
+}
+
+static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
+ unsigned long action, void *hcpu)
+{
+ int hotcpu = (unsigned long)hcpu;
+ struct cpuidle_device *dev =
+ per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
+
+ if (dev && cpuidle_get_driver()) {
+ switch (action) {
+ case CPU_ONLINE:
+ case CPU_ONLINE_FROZEN:
+ cpuidle_pause_and_lock();
+ cpuidle_enable_device(dev);
+ cpuidle_resume_and_unlock();
+ break;
+
+ case CPU_DEAD:
+ case CPU_DEAD_FROZEN:
+ cpuidle_pause_and_lock();
+ cpuidle_disable_device(dev);
+ cpuidle_resume_and_unlock();
+ break;
+
+ default:
+ return NOTIFY_DONE;
+ }
+ }
+ return NOTIFY_OK;
+}
+
+static struct notifier_block setup_hotplug_notifier = {
+ .notifier_call = pseries_cpuidle_add_cpu_notifier,
+};
+
+/*
+ * pseries_cpuidle_driver_init()
+ */
+static int pseries_cpuidle_driver_init(void)
+{
+ int idle_state;
+ struct cpuidle_driver *drv = &pseries_idle_driver;
+
+ drv->state_count = 0;
+
+ for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT; ++idle_state) {
+
+ if (idle_state > max_idle_state)
+ break;
+
+ /* is the state not enabled? */
+ if (cpuidle_state_table[idle_state].enter == NULL)
+ continue;
+
+ drv->states[drv->state_count] = /* structure copy */
+ cpuidle_state_table[idle_state];
+
+ drv->state_count += 1;
+ }
+
+ return 0;
+}
+
+/* pseries_idle_devices_uninit(void)
+ * unregister cpuidle devices and de-allocate memory
+ */
+static void pseries_idle_devices_uninit(void)
+{
+ int i;
+ struct cpuidle_device *dev;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(pseries_cpuidle_devices, i);
+ cpuidle_unregister_device(dev);
+ }
+
+ free_percpu(pseries_cpuidle_devices);
+ return;
+}
+
+/* pseries_idle_devices_init()
+ * allocate, initialize and register cpuidle device
+ */
+static int pseries_idle_devices_init(void)
+{
+ int i;
+ struct cpuidle_driver *drv = &pseries_idle_driver;
+ struct cpuidle_device *dev;
+
+ pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
+ if (pseries_cpuidle_devices == NULL)
+ return -ENOMEM;
+
+ for_each_possible_cpu(i) {
+ dev = per_cpu_ptr(pseries_cpuidle_devices, i);
+ dev->state_count = drv->state_count;
+ dev->cpu = i;
+ if (cpuidle_register_device(dev)) {
+ printk(KERN_DEBUG \
+ "cpuidle_register_device %d failed!\n", i);
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * pseries_idle_probe()
+ * Choose state table for shared versus dedicated partition
+ */
+static int pseries_idle_probe(void)
+{
+
+ if (!firmware_has_feature(FW_FEATURE_SPLPAR))
+ return -ENODEV;
+
+ if (cpuidle_disable != IDLE_NO_OVERRIDE)
+ return -ENODEV;
+
+ if (max_idle_state == 0) {
+ printk(KERN_DEBUG "pseries processor idle disabled.\n");
+ return -EPERM;
+ }
+
+ if (get_lppaca()->shared_proc)
+ cpuidle_state_table = shared_states;
+ else
+ cpuidle_state_table = dedicated_states;
+
+ return 0;
+}
+
+static int __init pseries_processor_idle_init(void)
+{
+ int retval;
+
+ retval = pseries_idle_probe();
+ if (retval)
+ return retval;
+
+ pseries_cpuidle_driver_init();
+ retval = cpuidle_register_driver(&pseries_idle_driver);
+ if (retval) {
+ printk(KERN_DEBUG "Registration of pseries driver failed.\n");
+ return retval;
+ }
+
+ update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
+
+ retval = pseries_idle_devices_init();
+ if (retval) {
+ pseries_idle_devices_uninit();
+ cpuidle_unregister_driver(&pseries_idle_driver);
+ return retval;
+ }
+
+ register_cpu_notifier(&setup_hotplug_notifier);
+ printk(KERN_DEBUG "pseries_idle_driver registered\n");
+
+ return 0;
+}
+
+static void __exit pseries_processor_idle_exit(void)
+{
+
+ unregister_cpu_notifier(&setup_hotplug_notifier);
+ pseries_idle_devices_uninit();
+ cpuidle_unregister_driver(&pseries_idle_driver);
+
+ return;
+}
+
+module_init(pseries_processor_idle_init);
+module_exit(pseries_processor_idle_exit);
+
+MODULE_AUTHOR("Deepthi Dharwar <[email protected]>");
+MODULE_DESCRIPTION("Cpuidle driver for POWER");
+MODULE_LICENSE("GPL");
> -----Original Message-----
> From: Deepthi Dharwar [mailto:[email protected]]
> Sent: Wednesday, July 31, 2013 10:59 AM
> To: [email protected]; [email protected]; linux-
> [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]
> Cc: [email protected]; Wang Dongsheng-B40534; [email protected]
> Subject: [PATCH V2 4/6] cpuidle/pseries: Move the pseries_idle backend
> driver to sysdev.
>
> Move pseries_idle backend driver code to arch/powerpc/sysdev
> so that the code can be used for a common driver for powernv
> and pseries. This removes a lot of code duplicacy.
>
Why not drivers/cpuidle/?
I think it should be move to drivers/cpuidle.
-dongsheng
> Signed-off-by: Deepthi Dharwar <[email protected]>
> ---
> arch/powerpc/platforms/pseries/Kconfig | 9 -
> arch/powerpc/platforms/pseries/Makefile | 1
> arch/powerpc/platforms/pseries/processor_idle.c | 384 -----------------
> ------
> arch/powerpc/sysdev/Kconfig | 9 +
> arch/powerpc/sysdev/Makefile | 1
> arch/powerpc/sysdev/processor_idle.c | 384
> +++++++++++++++++++++++
> 6 files changed, 394 insertions(+), 394 deletions(-)
> delete mode 100644 arch/powerpc/platforms/pseries/processor_idle.c
> create mode 100644 arch/powerpc/sysdev/processor_idle.c
>
> diff --git a/arch/powerpc/platforms/pseries/Kconfig
> b/arch/powerpc/platforms/pseries/Kconfig
> index 62b4f80..bb59bb0 100644
> --- a/arch/powerpc/platforms/pseries/Kconfig
> +++ b/arch/powerpc/platforms/pseries/Kconfig
> @@ -119,12 +119,3 @@ config DTL
> which are accessible through a debugfs file.
>
> Say N if you are unsure.
> -
> -config PSERIES_IDLE
> - bool "Cpuidle driver for pSeries platforms"
> - depends on CPU_IDLE
> - depends on PPC_PSERIES
> - default y
> - help
> - Select this option to enable processor idle state management
> - through cpuidle subsystem.
> diff --git a/arch/powerpc/platforms/pseries/Makefile
> b/arch/powerpc/platforms/pseries/Makefile
> index 8ae0103..4b22379 100644
> --- a/arch/powerpc/platforms/pseries/Makefile
> +++ b/arch/powerpc/platforms/pseries/Makefile
> @@ -21,7 +21,6 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
> obj-$(CONFIG_CMM) += cmm.o
> obj-$(CONFIG_DTL) += dtl.o
> obj-$(CONFIG_IO_EVENT_IRQ) += io_event_irq.o
> -obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
>
> ifeq ($(CONFIG_PPC_PSERIES),y)
> obj-$(CONFIG_SUSPEND) += suspend.o
> diff --git a/arch/powerpc/platforms/pseries/processor_idle.c
> b/arch/powerpc/platforms/pseries/processor_idle.c
> deleted file mode 100644
> index 0d75a54..0000000
> --- a/arch/powerpc/platforms/pseries/processor_idle.c
> +++ /dev/null
> @@ -1,384 +0,0 @@
> -/*
> - * processor_idle - idle state cpuidle driver.
> - * Adapted from drivers/idle/intel_idle.c and
> - * drivers/acpi/processor_idle.c
> - *
> - */
> -
> -#include <linux/kernel.h>
> -#include <linux/module.h>
> -#include <linux/init.h>
> -#include <linux/moduleparam.h>
> -#include <linux/cpuidle.h>
> -#include <linux/cpu.h>
> -#include <linux/notifier.h>
> -
> -#include <asm/paca.h>
> -#include <asm/reg.h>
> -#include <asm/machdep.h>
> -#include <asm/firmware.h>
> -#include <asm/runlatch.h>
> -#include <asm/plpar_wrappers.h>
> -
> -/* Snooze Delay, pseries_idle */
> -DECLARE_PER_CPU(long, smt_snooze_delay);
> -
> -struct cpuidle_driver pseries_idle_driver = {
> - .name = "pseries_idle",
> - .owner = THIS_MODULE,
> -};
> -
> -#define MAX_IDLE_STATE_COUNT 2
> -
> -static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
> -static struct cpuidle_device __percpu *pseries_cpuidle_devices;
> -static struct cpuidle_state *cpuidle_state_table;
> -
> -static inline void idle_loop_prolog(unsigned long *in_purr)
> -{
> - *in_purr = mfspr(SPRN_PURR);
> - /*
> - * Indicate to the HV that we are idle. Now would be
> - * a good time to find other work to dispatch.
> - */
> - get_lppaca()->idle = 1;
> -}
> -
> -static inline void idle_loop_epilog(unsigned long in_purr)
> -{
> - get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
> - get_lppaca()->idle = 0;
> -}
> -
> -static int snooze_loop(struct cpuidle_device *dev,
> - struct cpuidle_driver *drv,
> - int index)
> -{
> - unsigned long in_purr;
> - int cpu = dev->cpu;
> -
> - idle_loop_prolog(&in_purr);
> - local_irq_enable();
> - set_thread_flag(TIF_POLLING_NRFLAG);
> -
> - while ((!need_resched()) && cpu_online(cpu)) {
> - ppc64_runlatch_off();
> - HMT_low();
> - HMT_very_low();
> - }
> -
> - HMT_medium();
> - clear_thread_flag(TIF_POLLING_NRFLAG);
> - smp_mb();
> -
> - idle_loop_epilog(in_purr);
> -
> - return index;
> -}
> -
> -static void check_and_cede_processor(void)
> -{
> - /*
> - * Ensure our interrupt state is properly tracked,
> - * also checks if no interrupt has occurred while we
> - * were soft-disabled
> - */
> - if (prep_irq_for_idle()) {
> - cede_processor();
> -#ifdef CONFIG_TRACE_IRQFLAGS
> - /* Ensure that H_CEDE returns with IRQs on */
> - if (WARN_ON(!(mfmsr() & MSR_EE)))
> - __hard_irq_enable();
> -#endif
> - }
> -}
> -
> -static int dedicated_cede_loop(struct cpuidle_device *dev,
> - struct cpuidle_driver *drv,
> - int index)
> -{
> - unsigned long in_purr;
> -
> - idle_loop_prolog(&in_purr);
> - get_lppaca()->donate_dedicated_cpu = 1;
> -
> - ppc64_runlatch_off();
> - HMT_medium();
> - check_and_cede_processor();
> -
> - get_lppaca()->donate_dedicated_cpu = 0;
> -
> - idle_loop_epilog(in_purr);
> -
> - return index;
> -}
> -
> -static int shared_cede_loop(struct cpuidle_device *dev,
> - struct cpuidle_driver *drv,
> - int index)
> -{
> - unsigned long in_purr;
> -
> - idle_loop_prolog(&in_purr);
> -
> - /*
> - * Yield the processor to the hypervisor. We return if
> - * an external interrupt occurs (which are driven prior
> - * to returning here) or if a prod occurs from another
> - * processor. When returning here, external interrupts
> - * are enabled.
> - */
> - check_and_cede_processor();
> -
> - idle_loop_epilog(in_purr);
> -
> - return index;
> -}
> -
> -/*
> - * States for dedicated partition case.
> - */
> -static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
> - { /* Snooze */
> - .name = "snooze",
> - .desc = "snooze",
> - .flags = CPUIDLE_FLAG_TIME_VALID,
> - .exit_latency = 0,
> - .target_residency = 0,
> - .enter = &snooze_loop },
> - { /* CEDE */
> - .name = "CEDE",
> - .desc = "CEDE",
> - .flags = CPUIDLE_FLAG_TIME_VALID,
> - .exit_latency = 10,
> - .target_residency = 100,
> - .enter = &dedicated_cede_loop },
> -};
> -
> -/*
> - * States for shared partition case.
> - */
> -static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
> - { /* Shared Cede */
> - .name = "Shared Cede",
> - .desc = "Shared Cede",
> - .flags = CPUIDLE_FLAG_TIME_VALID,
> - .exit_latency = 0,
> - .target_residency = 0,
> - .enter = &shared_cede_loop },
> -};
> -
> -void update_smt_snooze_delay(int cpu, int residency)
> -{
> - struct cpuidle_driver *drv = cpuidle_get_driver();
> - struct cpuidle_device *dev;
> -
> - if (cpuidle_state_table != dedicated_states)
> - return;
> -
> - if (!drv)
> - return;
> -
> - if (cpu == -1) {
> - if (residency < 0) {
> - /* Disable NAP on all cpus */
> - drv->states[1].disabled = true;
> - return;
> - } else {
> - drv->states[1].target_residency = residency;
> - drv->states[1].disabled = false;
> - return;
> - }
> - }
> -
> - dev = per_cpu(cpuidle_devices, cpu);
> - if (!dev)
> - return;
> -
> - if (residency < 0)
> - dev->states_usage[1].disable = 1;
> - else {
> - drv->states[1].target_residency = residency;
> - drv->states[1].disabled = false;
> - dev->states_usage[1].disable = 0;
> - }
> -}
> -
> -static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
> - unsigned long action, void *hcpu)
> -{
> - int hotcpu = (unsigned long)hcpu;
> - struct cpuidle_device *dev =
> - per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
> -
> - if (dev && cpuidle_get_driver()) {
> - switch (action) {
> - case CPU_ONLINE:
> - case CPU_ONLINE_FROZEN:
> - cpuidle_pause_and_lock();
> - cpuidle_enable_device(dev);
> - cpuidle_resume_and_unlock();
> - break;
> -
> - case CPU_DEAD:
> - case CPU_DEAD_FROZEN:
> - cpuidle_pause_and_lock();
> - cpuidle_disable_device(dev);
> - cpuidle_resume_and_unlock();
> - break;
> -
> - default:
> - return NOTIFY_DONE;
> - }
> - }
> - return NOTIFY_OK;
> -}
> -
> -static struct notifier_block setup_hotplug_notifier = {
> - .notifier_call = pseries_cpuidle_add_cpu_notifier,
> -};
> -
> -/*
> - * pseries_cpuidle_driver_init()
> - */
> -static int pseries_cpuidle_driver_init(void)
> -{
> - int idle_state;
> - struct cpuidle_driver *drv = &pseries_idle_driver;
> -
> - drv->state_count = 0;
> -
> - for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT;
> ++idle_state) {
> -
> - if (idle_state > max_idle_state)
> - break;
> -
> - /* is the state not enabled? */
> - if (cpuidle_state_table[idle_state].enter == NULL)
> - continue;
> -
> - drv->states[drv->state_count] = /* structure copy */
> - cpuidle_state_table[idle_state];
> -
> - drv->state_count += 1;
> - }
> -
> - return 0;
> -}
> -
> -/* pseries_idle_devices_uninit(void)
> - * unregister cpuidle devices and de-allocate memory
> - */
> -static void pseries_idle_devices_uninit(void)
> -{
> - int i;
> - struct cpuidle_device *dev;
> -
> - for_each_possible_cpu(i) {
> - dev = per_cpu_ptr(pseries_cpuidle_devices, i);
> - cpuidle_unregister_device(dev);
> - }
> -
> - free_percpu(pseries_cpuidle_devices);
> - return;
> -}
> -
> -/* pseries_idle_devices_init()
> - * allocate, initialize and register cpuidle device
> - */
> -static int pseries_idle_devices_init(void)
> -{
> - int i;
> - struct cpuidle_driver *drv = &pseries_idle_driver;
> - struct cpuidle_device *dev;
> -
> - pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
> - if (pseries_cpuidle_devices == NULL)
> - return -ENOMEM;
> -
> - for_each_possible_cpu(i) {
> - dev = per_cpu_ptr(pseries_cpuidle_devices, i);
> - dev->state_count = drv->state_count;
> - dev->cpu = i;
> - if (cpuidle_register_device(dev)) {
> - printk(KERN_DEBUG \
> - "cpuidle_register_device %d failed!\n", i);
> - return -EIO;
> - }
> - }
> -
> - return 0;
> -}
> -
> -/*
> - * pseries_idle_probe()
> - * Choose state table for shared versus dedicated partition
> - */
> -static int pseries_idle_probe(void)
> -{
> -
> - if (!firmware_has_feature(FW_FEATURE_SPLPAR))
> - return -ENODEV;
> -
> - if (cpuidle_disable != IDLE_NO_OVERRIDE)
> - return -ENODEV;
> -
> - if (max_idle_state == 0) {
> - printk(KERN_DEBUG "pseries processor idle disabled.\n");
> - return -EPERM;
> - }
> -
> - if (get_lppaca()->shared_proc)
> - cpuidle_state_table = shared_states;
> - else
> - cpuidle_state_table = dedicated_states;
> -
> - return 0;
> -}
> -
> -static int __init pseries_processor_idle_init(void)
> -{
> - int retval;
> -
> - retval = pseries_idle_probe();
> - if (retval)
> - return retval;
> -
> - pseries_cpuidle_driver_init();
> - retval = cpuidle_register_driver(&pseries_idle_driver);
> - if (retval) {
> - printk(KERN_DEBUG "Registration of pseries driver failed.\n");
> - return retval;
> - }
> -
> - update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
> -
> - retval = pseries_idle_devices_init();
> - if (retval) {
> - pseries_idle_devices_uninit();
> - cpuidle_unregister_driver(&pseries_idle_driver);
> - return retval;
> - }
> -
> - register_cpu_notifier(&setup_hotplug_notifier);
> - printk(KERN_DEBUG "pseries_idle_driver registered\n");
> -
> - return 0;
> -}
> -
> -static void __exit pseries_processor_idle_exit(void)
> -{
> -
> - unregister_cpu_notifier(&setup_hotplug_notifier);
> - pseries_idle_devices_uninit();
> - cpuidle_unregister_driver(&pseries_idle_driver);
> -
> - return;
> -}
> -
> -module_init(pseries_processor_idle_init);
> -module_exit(pseries_processor_idle_exit);
> -
> -MODULE_AUTHOR("Deepthi Dharwar <[email protected]>");
> -MODULE_DESCRIPTION("Cpuidle driver for POWER");
> -MODULE_LICENSE("GPL");
> diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig
> index ab4cb54..8564a3f 100644
> --- a/arch/powerpc/sysdev/Kconfig
> +++ b/arch/powerpc/sysdev/Kconfig
> @@ -34,3 +34,12 @@ config SCOM_DEBUGFS
> config GE_FPGA
> bool
> default n
> +
> +config PSERIES_IDLE
> + bool "Cpuidle driver for pSeries platforms"
> + depends on CPU_IDLE
> + depends on PPC_PSERIES
> + default y
> + help
> + Select this option to enable processor idle state management
> + for pSeries through cpuidle subsystem.
> diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
> index f67ac90..93d2cdd 100644
> --- a/arch/powerpc/sysdev/Makefile
> +++ b/arch/powerpc/sysdev/Makefile
> @@ -49,6 +49,7 @@ endif
> obj-$(CONFIG_PPC4xx_MSI) += ppc4xx_msi.o
> obj-$(CONFIG_PPC4xx_CPM) += ppc4xx_cpm.o
> obj-$(CONFIG_PPC4xx_GPIO) += ppc4xx_gpio.o
> +obj-$(CONFIG_PSERIES_IDLE) += processor_idle.o
>
> obj-$(CONFIG_CPM) += cpm_common.o
> obj-$(CONFIG_CPM2) += cpm2.o cpm2_pic.o
> diff --git a/arch/powerpc/sysdev/processor_idle.c
> b/arch/powerpc/sysdev/processor_idle.c
> new file mode 100644
> index 0000000..0d75a54
> --- /dev/null
> +++ b/arch/powerpc/sysdev/processor_idle.c
> @@ -0,0 +1,384 @@
> +/*
> + * processor_idle - idle state cpuidle driver.
> + * Adapted from drivers/idle/intel_idle.c and
> + * drivers/acpi/processor_idle.c
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/moduleparam.h>
> +#include <linux/cpuidle.h>
> +#include <linux/cpu.h>
> +#include <linux/notifier.h>
> +
> +#include <asm/paca.h>
> +#include <asm/reg.h>
> +#include <asm/machdep.h>
> +#include <asm/firmware.h>
> +#include <asm/runlatch.h>
> +#include <asm/plpar_wrappers.h>
> +
> +/* Snooze Delay, pseries_idle */
> +DECLARE_PER_CPU(long, smt_snooze_delay);
> +
> +struct cpuidle_driver pseries_idle_driver = {
> + .name = "pseries_idle",
> + .owner = THIS_MODULE,
> +};
> +
> +#define MAX_IDLE_STATE_COUNT 2
> +
> +static int max_idle_state = MAX_IDLE_STATE_COUNT - 1;
> +static struct cpuidle_device __percpu *pseries_cpuidle_devices;
> +static struct cpuidle_state *cpuidle_state_table;
> +
> +static inline void idle_loop_prolog(unsigned long *in_purr)
> +{
> + *in_purr = mfspr(SPRN_PURR);
> + /*
> + * Indicate to the HV that we are idle. Now would be
> + * a good time to find other work to dispatch.
> + */
> + get_lppaca()->idle = 1;
> +}
> +
> +static inline void idle_loop_epilog(unsigned long in_purr)
> +{
> + get_lppaca()->wait_state_cycles += mfspr(SPRN_PURR) - in_purr;
> + get_lppaca()->idle = 0;
> +}
> +
> +static int snooze_loop(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv,
> + int index)
> +{
> + unsigned long in_purr;
> + int cpu = dev->cpu;
> +
> + idle_loop_prolog(&in_purr);
> + local_irq_enable();
> + set_thread_flag(TIF_POLLING_NRFLAG);
> +
> + while ((!need_resched()) && cpu_online(cpu)) {
> + ppc64_runlatch_off();
> + HMT_low();
> + HMT_very_low();
> + }
> +
> + HMT_medium();
> + clear_thread_flag(TIF_POLLING_NRFLAG);
> + smp_mb();
> +
> + idle_loop_epilog(in_purr);
> +
> + return index;
> +}
> +
> +static void check_and_cede_processor(void)
> +{
> + /*
> + * Ensure our interrupt state is properly tracked,
> + * also checks if no interrupt has occurred while we
> + * were soft-disabled
> + */
> + if (prep_irq_for_idle()) {
> + cede_processor();
> +#ifdef CONFIG_TRACE_IRQFLAGS
> + /* Ensure that H_CEDE returns with IRQs on */
> + if (WARN_ON(!(mfmsr() & MSR_EE)))
> + __hard_irq_enable();
> +#endif
> + }
> +}
> +
> +static int dedicated_cede_loop(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv,
> + int index)
> +{
> + unsigned long in_purr;
> +
> + idle_loop_prolog(&in_purr);
> + get_lppaca()->donate_dedicated_cpu = 1;
> +
> + ppc64_runlatch_off();
> + HMT_medium();
> + check_and_cede_processor();
> +
> + get_lppaca()->donate_dedicated_cpu = 0;
> +
> + idle_loop_epilog(in_purr);
> +
> + return index;
> +}
> +
> +static int shared_cede_loop(struct cpuidle_device *dev,
> + struct cpuidle_driver *drv,
> + int index)
> +{
> + unsigned long in_purr;
> +
> + idle_loop_prolog(&in_purr);
> +
> + /*
> + * Yield the processor to the hypervisor. We return if
> + * an external interrupt occurs (which are driven prior
> + * to returning here) or if a prod occurs from another
> + * processor. When returning here, external interrupts
> + * are enabled.
> + */
> + check_and_cede_processor();
> +
> + idle_loop_epilog(in_purr);
> +
> + return index;
> +}
> +
> +/*
> + * States for dedicated partition case.
> + */
> +static struct cpuidle_state dedicated_states[MAX_IDLE_STATE_COUNT] = {
> + { /* Snooze */
> + .name = "snooze",
> + .desc = "snooze",
> + .flags = CPUIDLE_FLAG_TIME_VALID,
> + .exit_latency = 0,
> + .target_residency = 0,
> + .enter = &snooze_loop },
> + { /* CEDE */
> + .name = "CEDE",
> + .desc = "CEDE",
> + .flags = CPUIDLE_FLAG_TIME_VALID,
> + .exit_latency = 10,
> + .target_residency = 100,
> + .enter = &dedicated_cede_loop },
> +};
> +
> +/*
> + * States for shared partition case.
> + */
> +static struct cpuidle_state shared_states[MAX_IDLE_STATE_COUNT] = {
> + { /* Shared Cede */
> + .name = "Shared Cede",
> + .desc = "Shared Cede",
> + .flags = CPUIDLE_FLAG_TIME_VALID,
> + .exit_latency = 0,
> + .target_residency = 0,
> + .enter = &shared_cede_loop },
> +};
> +
> +void update_smt_snooze_delay(int cpu, int residency)
> +{
> + struct cpuidle_driver *drv = cpuidle_get_driver();
> + struct cpuidle_device *dev;
> +
> + if (cpuidle_state_table != dedicated_states)
> + return;
> +
> + if (!drv)
> + return;
> +
> + if (cpu == -1) {
> + if (residency < 0) {
> + /* Disable NAP on all cpus */
> + drv->states[1].disabled = true;
> + return;
> + } else {
> + drv->states[1].target_residency = residency;
> + drv->states[1].disabled = false;
> + return;
> + }
> + }
> +
> + dev = per_cpu(cpuidle_devices, cpu);
> + if (!dev)
> + return;
> +
> + if (residency < 0)
> + dev->states_usage[1].disable = 1;
> + else {
> + drv->states[1].target_residency = residency;
> + drv->states[1].disabled = false;
> + dev->states_usage[1].disable = 0;
> + }
> +}
> +
> +static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
> + unsigned long action, void *hcpu)
> +{
> + int hotcpu = (unsigned long)hcpu;
> + struct cpuidle_device *dev =
> + per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
> +
> + if (dev && cpuidle_get_driver()) {
> + switch (action) {
> + case CPU_ONLINE:
> + case CPU_ONLINE_FROZEN:
> + cpuidle_pause_and_lock();
> + cpuidle_enable_device(dev);
> + cpuidle_resume_and_unlock();
> + break;
> +
> + case CPU_DEAD:
> + case CPU_DEAD_FROZEN:
> + cpuidle_pause_and_lock();
> + cpuidle_disable_device(dev);
> + cpuidle_resume_and_unlock();
> + break;
> +
> + default:
> + return NOTIFY_DONE;
> + }
> + }
> + return NOTIFY_OK;
> +}
> +
> +static struct notifier_block setup_hotplug_notifier = {
> + .notifier_call = pseries_cpuidle_add_cpu_notifier,
> +};
> +
> +/*
> + * pseries_cpuidle_driver_init()
> + */
> +static int pseries_cpuidle_driver_init(void)
> +{
> + int idle_state;
> + struct cpuidle_driver *drv = &pseries_idle_driver;
> +
> + drv->state_count = 0;
> +
> + for (idle_state = 0; idle_state < MAX_IDLE_STATE_COUNT;
> ++idle_state) {
> +
> + if (idle_state > max_idle_state)
> + break;
> +
> + /* is the state not enabled? */
> + if (cpuidle_state_table[idle_state].enter == NULL)
> + continue;
> +
> + drv->states[drv->state_count] = /* structure copy */
> + cpuidle_state_table[idle_state];
> +
> + drv->state_count += 1;
> + }
> +
> + return 0;
> +}
> +
> +/* pseries_idle_devices_uninit(void)
> + * unregister cpuidle devices and de-allocate memory
> + */
> +static void pseries_idle_devices_uninit(void)
> +{
> + int i;
> + struct cpuidle_device *dev;
> +
> + for_each_possible_cpu(i) {
> + dev = per_cpu_ptr(pseries_cpuidle_devices, i);
> + cpuidle_unregister_device(dev);
> + }
> +
> + free_percpu(pseries_cpuidle_devices);
> + return;
> +}
> +
> +/* pseries_idle_devices_init()
> + * allocate, initialize and register cpuidle device
> + */
> +static int pseries_idle_devices_init(void)
> +{
> + int i;
> + struct cpuidle_driver *drv = &pseries_idle_driver;
> + struct cpuidle_device *dev;
> +
> + pseries_cpuidle_devices = alloc_percpu(struct cpuidle_device);
> + if (pseries_cpuidle_devices == NULL)
> + return -ENOMEM;
> +
> + for_each_possible_cpu(i) {
> + dev = per_cpu_ptr(pseries_cpuidle_devices, i);
> + dev->state_count = drv->state_count;
> + dev->cpu = i;
> + if (cpuidle_register_device(dev)) {
> + printk(KERN_DEBUG \
> + "cpuidle_register_device %d failed!\n", i);
> + return -EIO;
> + }
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * pseries_idle_probe()
> + * Choose state table for shared versus dedicated partition
> + */
> +static int pseries_idle_probe(void)
> +{
> +
> + if (!firmware_has_feature(FW_FEATURE_SPLPAR))
> + return -ENODEV;
> +
> + if (cpuidle_disable != IDLE_NO_OVERRIDE)
> + return -ENODEV;
> +
> + if (max_idle_state == 0) {
> + printk(KERN_DEBUG "pseries processor idle disabled.\n");
> + return -EPERM;
> + }
> +
> + if (get_lppaca()->shared_proc)
> + cpuidle_state_table = shared_states;
> + else
> + cpuidle_state_table = dedicated_states;
> +
> + return 0;
> +}
> +
> +static int __init pseries_processor_idle_init(void)
> +{
> + int retval;
> +
> + retval = pseries_idle_probe();
> + if (retval)
> + return retval;
> +
> + pseries_cpuidle_driver_init();
> + retval = cpuidle_register_driver(&pseries_idle_driver);
> + if (retval) {
> + printk(KERN_DEBUG "Registration of pseries driver failed.\n");
> + return retval;
> + }
> +
> + update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
> +
> + retval = pseries_idle_devices_init();
> + if (retval) {
> + pseries_idle_devices_uninit();
> + cpuidle_unregister_driver(&pseries_idle_driver);
> + return retval;
> + }
> +
> + register_cpu_notifier(&setup_hotplug_notifier);
> + printk(KERN_DEBUG "pseries_idle_driver registered\n");
> +
> + return 0;
> +}
> +
> +static void __exit pseries_processor_idle_exit(void)
> +{
> +
> + unregister_cpu_notifier(&setup_hotplug_notifier);
> + pseries_idle_devices_uninit();
> + cpuidle_unregister_driver(&pseries_idle_driver);
> +
> + return;
> +}
> +
> +module_init(pseries_processor_idle_init);
> +module_exit(pseries_processor_idle_exit);
> +
> +MODULE_AUTHOR("Deepthi Dharwar <[email protected]>");
> +MODULE_DESCRIPTION("Cpuidle driver for POWER");
> +MODULE_LICENSE("GPL");
>
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
>
> -static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
> +static int powerpc_cpuidle_add_cpu_notifier(struct notifier_block *n,
> unsigned long action, void *hcpu)
> {
> int hotcpu = (unsigned long)hcpu;
> struct cpuidle_device *dev =
> - per_cpu_ptr(pseries_cpuidle_devices, hotcpu);
> + per_cpu_ptr(powerpc_cpuidle_devices, hotcpu);
>
> if (dev && cpuidle_get_driver()) {
> switch (action) {
> @@ -235,16 +270,16 @@ static int pseries_cpuidle_add_cpu_notifier(struct
> notifier_block *n,
> }
>
> static struct notifier_block setup_hotplug_notifier = {
> - .notifier_call = pseries_cpuidle_add_cpu_notifier,
> + .notifier_call = powerpc_cpuidle_add_cpu_notifier,
> };
>
I think Daniel means move the notifier to cpuidle framework, not just powerpc.
And should be remove all about *device*. If the notifier handle using device,
you can use "cpuidle_devices".
- dongsheng
> -static int __init pseries_processor_idle_init(void)
> +static int __init powerpc_processor_idle_init(void)
> {
> int retval;
>
> - retval = pseries_idle_probe();
> + retval = powerpc_idle_probe();
> if (retval)
> return retval;
>
> - pseries_cpuidle_driver_init();
> - retval = cpuidle_register_driver(&pseries_idle_driver);
> + powerpc_cpuidle_driver_init();
> + retval = cpuidle_register_driver(&powerpc_idle_driver);
> if (retval) {
> - printk(KERN_DEBUG "Registration of pseries driver failed.\n");
> + printk(KERN_DEBUG "Registration of powerpc driver failed.\n");
> return retval;
> }
>
> update_smt_snooze_delay(-1, per_cpu(smt_snooze_delay, 0));
>
> - retval = pseries_idle_devices_init();
> + retval = powerpc_idle_devices_init();
Should be remove all *device*, using cpuidle_register.
- dongsheng
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
Hi Dongsheng,
On 07/31/2013 08:52 AM, Wang Dongsheng-B40534 wrote:
>
>
>> -----Original Message-----
>> From: Deepthi Dharwar [mailto:[email protected]]
>> Sent: Wednesday, July 31, 2013 10:59 AM
>> To: [email protected]; [email protected]; linux-
>> [email protected]; [email protected];
>> [email protected]; [email protected];
>> [email protected]; [email protected]
>> Cc: [email protected]; Wang Dongsheng-B40534; [email protected]
>> Subject: [PATCH V2 4/6] cpuidle/pseries: Move the pseries_idle backend
>> driver to sysdev.
>>
>> Move pseries_idle backend driver code to arch/powerpc/sysdev
>> so that the code can be used for a common driver for powernv
>> and pseries. This removes a lot of code duplicacy.
>>
> Why not drivers/cpuidle/?
>
> I think it should be move to drivers/cpuidle.
Please take a look at what the cpuidle under drivers has to provide.
cpuidle has two parts to it. The front end and the back end. The front
end constitutes the cpuidle governors, registering of arch specific
cpuidle drivers, disabling and enabling of cpuidle feature. It is this
front end code which is present under drivers/cpuidle.
The arch specific cpuidle drivers which decide what needs to be done to
enter a specific idle state chosen by the cpuidle governor is what
constitutes the back end of cpuidle. This will not be in drivers/cpuidle
but in an arch/ specific code.
The cpuidle under drivers/cpuidle drives the idle power management, but
the low level handling of the entry into idle states should be taken
care of by the architecture.
Your recent patch :
cpuidle: add freescale e500 family porcessors idle support IMO should
hook onto the backend cpuidle driver that this patchset provides.
Regards
Preeti U Murthy
Hi Preeti,
> -----Original Message-----
> From: Preeti U Murthy [mailto:[email protected]]
> Sent: Wednesday, July 31, 2013 12:00 PM
> To: Wang Dongsheng-B40534
> Cc: Deepthi Dharwar; [email protected]; [email protected];
> [email protected]; [email protected];
> [email protected]; [email protected]; linuxppc-
> [email protected]; [email protected]; [email protected]
> Subject: Re: [PATCH V2 4/6] cpuidle/pseries: Move the pseries_idle
> backend driver to sysdev.
>
> Hi Dongsheng,
>
> On 07/31/2013 08:52 AM, Wang Dongsheng-B40534 wrote:
> >
> >
> >> -----Original Message-----
> >> From: Deepthi Dharwar [mailto:[email protected]]
> >> Sent: Wednesday, July 31, 2013 10:59 AM
> >> To: [email protected]; [email protected]; linux-
> >> [email protected]; [email protected];
> >> [email protected]; [email protected];
> >> [email protected]; [email protected]
> >> Cc: [email protected]; Wang Dongsheng-B40534; [email protected]
> >> Subject: [PATCH V2 4/6] cpuidle/pseries: Move the pseries_idle
> >> backend driver to sysdev.
> >>
> >> Move pseries_idle backend driver code to arch/powerpc/sysdev so that
> >> the code can be used for a common driver for powernv and pseries.
> >> This removes a lot of code duplicacy.
> >>
> > Why not drivers/cpuidle/?
> >
> > I think it should be move to drivers/cpuidle.
>
> Please take a look at what the cpuidle under drivers has to provide.
> cpuidle has two parts to it. The front end and the back end. The front
> end constitutes the cpuidle governors, registering of arch specific
> cpuidle drivers, disabling and enabling of cpuidle feature. It is this
> front end code which is present under drivers/cpuidle.
>
> The arch specific cpuidle drivers which decide what needs to be done to
> enter a specific idle state chosen by the cpuidle governor is what
> constitutes the back end of cpuidle. This will not be in drivers/cpuidle
> but in an arch/ specific code.
>
> The cpuidle under drivers/cpuidle drives the idle power management, but
> the low level handling of the entry into idle states should be taken care
> of by the architecture.
>
> Your recent patch :
> cpuidle: add freescale e500 family porcessors idle support IMO should
> hook onto the backend cpuidle driver that this patchset provides.
>
Sorry, I don't think so, cpuidle framework has been already very common.
Here we just need to do state definition and handling. I wonder whether
we need this layer.
If your handle is platform dependent, it should be in arch/platform.
If it is only for some platforms and the operation of these platforms can be
multiplexed, Why cannot as a driver to put into driver/cpuidle?
If it a general driver, I think we can put some common operating to driver/cpuidle
and make the platform specific code to arch/powerpc/platform.
This patch include front end and back end, not just back end.
This patch include too many state of different platforms and handle function. This state
and handle that should belong to itself platforms. Not a general way. If Deepthi will do
a general powerpc cpuidle, I think, it's cannot just using the macro to distinguish
platform. the front end code maybe move to driver/cpuidle(drvier register) should be better,
make the Low Power State and what should be handle to arch/powerpc/platform/**, because different
platforms have different state of low power consumption, and the processing method.
The front end can provide some general methods to register into general powerpc cpuidle driver.
-dongsheng
????{.n?+???????+%?????ݶ??w??{.n?+????{??G?????{ay?ʇڙ?,j??f???h?????????z_??(?階?ݢj"???m??????G????????????&???~???iO???z??v?^?m????????????I?
Hi Dongsheng,
On 07/31/2013 11:16 AM, Wang Dongsheng-B40534 wrote:
> Hi Preeti,
>
>> -----Original Message-----
>> From: Preeti U Murthy [mailto:[email protected]]
>> Sent: Wednesday, July 31, 2013 12:00 PM
>> To: Wang Dongsheng-B40534
>> Cc: Deepthi Dharwar; [email protected]; [email protected];
>> [email protected]; [email protected];
>> [email protected]; [email protected]; linuxppc-
>> [email protected]; [email protected]; [email protected]
>> Subject: Re: [PATCH V2 4/6] cpuidle/pseries: Move the pseries_idle
>> backend driver to sysdev.
>>
>> Hi Dongsheng,
>>
>> On 07/31/2013 08:52 AM, Wang Dongsheng-B40534 wrote:
>>>
>>>
>>>> -----Original Message-----
>>>> From: Deepthi Dharwar [mailto:[email protected]]
>>>> Sent: Wednesday, July 31, 2013 10:59 AM
>>>> To: [email protected]; [email protected]; linux-
>>>> [email protected]; [email protected];
>>>> [email protected]; [email protected];
>>>> [email protected]; [email protected]
>>>> Cc: [email protected]; Wang Dongsheng-B40534; [email protected]
>>>> Subject: [PATCH V2 4/6] cpuidle/pseries: Move the pseries_idle
>>>> backend driver to sysdev.
>>>>
>>>> Move pseries_idle backend driver code to arch/powerpc/sysdev so that
>>>> the code can be used for a common driver for powernv and pseries.
>>>> This removes a lot of code duplicacy.
>>>>
>>> Why not drivers/cpuidle/?
>>>
>>> I think it should be move to drivers/cpuidle.
>>
>> Please take a look at what the cpuidle under drivers has to provide.
>> cpuidle has two parts to it. The front end and the back end. The front
>> end constitutes the cpuidle governors, registering of arch specific
>> cpuidle drivers, disabling and enabling of cpuidle feature. It is this
>> front end code which is present under drivers/cpuidle.
>>
>> The arch specific cpuidle drivers which decide what needs to be done to
>> enter a specific idle state chosen by the cpuidle governor is what
>> constitutes the back end of cpuidle. This will not be in drivers/cpuidle
>> but in an arch/ specific code.
>>
>> The cpuidle under drivers/cpuidle drives the idle power management, but
>> the low level handling of the entry into idle states should be taken care
>> of by the architecture.
>>
>> Your recent patch :
>> cpuidle: add freescale e500 family porcessors idle support IMO should
>> hook onto the backend cpuidle driver that this patchset provides.
>>
> Sorry, I don't think so, cpuidle framework has been already very common.
> Here we just need to do state definition and handling. I wonder whether
> we need this layer.
>
> If your handle is platform dependent, it should be in arch/platform.
>
> If it is only for some platforms and the operation of these platforms can be
> multiplexed, Why cannot as a driver to put into driver/cpuidle?
>
> If it a general driver, I think we can put some common operating to driver/cpuidle
> and make the platform specific code to arch/powerpc/platform.
>
> This patch include front end and back end, not just back end.
>
> This patch include too many state of different platforms and handle function. This state
> and handle that should belong to itself platforms. Not a general way. If Deepthi will do
> a general powerpc cpuidle, I think, it's cannot just using the macro to distinguish
> platform. the front end code maybe move to driver/cpuidle(drvier register) should be better,
> make the Low Power State and what should be handle to arch/powerpc/platform/**, because different
> platforms have different state of low power consumption, and the processing method.
> The front end can provide some general methods to register into general powerpc cpuidle driver.
As Daniel pointed out, with a call to cpuidle_register(), we can get the
cpuidle_driver and cpuidle_device registered through the generic cpuidle
framework. Hence we can get rid of the powerpc_idle_devices_init() routine.
We can have the hotplug notifier in the generic cpuidle framework as
well. The rest of the patchset however should be arch specific IMO.
Regards
Preeti U Murthy
>
> -dongsheng
>
> _______________________________________________
> Linuxppc-dev mailing list
> [email protected]
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
On Wed, 2013-07-31 at 08:29 +0530, Deepthi Dharwar wrote:
> /*
> - * pseries_idle_probe()
> + * powerpc_idle_probe()
> * Choose state table for shared versus dedicated partition
> */
> -static int pseries_idle_probe(void)
> +static int powerpc_idle_probe(void)
> {
>
> +#ifndef PPC_POWERNV
> if (!firmware_has_feature(FW_FEATURE_SPLPAR))
> return -ENODEV;
> +#endif
A bunch of ifdefs is not a good start for a file you're claiming is now
generic for all powerpc.
Certainly you shouldn't be calling pseries stuff based only on the
absence of powernv.
And do you not support building one kernel that supports both pseries
and powernv?
> if (cpuidle_disable != IDLE_NO_OVERRIDE)
> return -ENODEV;
>
> if (max_idle_state == 0) {
> - printk(KERN_DEBUG "pseries processor idle disabled.\n");
> + printk(KERN_DEBUG "powerpc processor idle disabled.\n");
> return -EPERM;
> }
>
> +#ifdef PPC_POWERNV
> + cpuidle_state_table = powernv_states;
> +#else
> if (get_lppaca()->shared_proc)
Here's another example. get_lppaca() will only build on book3s -- and
yet we get requests for e500 code to use this file.
-Scott
On Tue, 2013-08-06 at 18:08 -0500, Scott Wood wrote:
> Here's another example. get_lppaca() will only build on book3s -- and
> yet we get requests for e500 code to use this file.
Indeed, Besides there is already accessors afaik for lppaca that compile
to nothing on E (and if not they would be trivial to add).
Cheers,
Ben.
On Wed, 2013-08-07 at 09:30 +1000, Benjamin Herrenschmidt wrote:
> On Tue, 2013-08-06 at 18:08 -0500, Scott Wood wrote:
> > Here's another example. get_lppaca() will only build on book3s -- and
> > yet we get requests for e500 code to use this file.
>
> Indeed, Besides there is already accessors afaik for lppaca that compile
> to nothing on E (and if not they would be trivial to add).
I don't see such an accessor, but if there were, what would happen when
the caller goes on to dereference that nothing?
There is an accessor for shared_proc specifically (in the spinlock code)
-- not that it would be much help on booke to just compile away that
check and always select one of the pseries state tables over the other.
-Scott
On 08/07/2013 05:11 AM, Scott Wood wrote:
> On Wed, 2013-08-07 at 09:30 +1000, Benjamin Herrenschmidt wrote:
>> On Tue, 2013-08-06 at 18:08 -0500, Scott Wood wrote:
>>> Here's another example. get_lppaca() will only build on book3s -- and
>>> yet we get requests for e500 code to use this file.
>>
>> Indeed, Besides there is already accessors afaik for lppaca that compile
>> to nothing on E (and if not they would be trivial to add).
>
> I don't see such an accessor, but if there were, what would happen when
> the caller goes on to dereference that nothing?
>
> There is an accessor for shared_proc specifically (in the spinlock code)
> -- not that it would be much help on booke to just compile away that
> check and always select one of the pseries state tables over the other.
>
> -Scott
Thanks a lot Scott and Ben for the review.
I have addressed the issues in V3 of this patch series which I have just
posted out.
Regards,
Deepthi
>
>
> _______________________________________________
> Linuxppc-dev mailing list
> [email protected]
> https://lists.ozlabs.org/listinfo/linuxppc-dev
>
>