* Re: Runtime PM discussion notes
From: Rafael J. Wysocki @ 2011-07-13 9:04 UTC (permalink / raw)
To: Paul Walmsley; +Cc: Mark Brown, linux-pm, linux-omap
In-Reply-To: <alpine.DEB.2.00.1107130105530.30229@utopia.booyaka.com>
On Wednesday, July 13, 2011, Paul Walmsley wrote:
> (cc'ing Len)
>
> Hi Mark,
>
> On Mon, 11 Jul 2011, Mark Brown wrote:
>
> > The interesting bits are things like being able to kill lots of the SoC
> > core supplies when the RAM is in retention mode - the CPU needs to go
> > through its shutdown procedures.
>
> This is indeed possible on OMAP3+ chips with TWL4030+ PMICs. Probably
> other PMICs also. TI calls it "off-mode." The N900 shipped with this
> feature enabled. Not sure how many other similar products did.
>
> This can be enabled in mainline, but not all of the mainline drivers have
> context save/restore code merged yet, so in mainline it only works with a
> subset of drivers.
>
> > Actually, it just occurred to me that if we're waiting for a system
> > timer and can hand that off to a suitable timer in the PMIC then we can
> > do a suspend to RAM for the deep idle state from the hardware point of
> > view.
>
> Yep. At LinuxCon Cambridge two years ago, we had a discussion about
> whether it would be possible to enter ACPI S-states from CPUIdle (or some
> idle governor) on Intel chips. If I remember correctly, the conclusion
> was that ACPI always disables the screen/backlight, so it would only be
> useful for situations where that was acceptable.
The reason why you can't enter ACPI S-states from CPUidle is because you
need to go out of the idle loop to execute some ACPI-specific stuff. Which
is not even specific to Intel chips, but to ACPI in general.
So entering ACPI S-states from idle is a no-no and I don't think it'll
change in foreseeable future.
> To the best of my (limited) knowledge, that's the only case I know of
> where there's a hardware limitation that prevents dynamic idle from
> reaching the same low power state as system suspend. If someone has hard
> details of a similar example, it would be great to know about it.
Google G1 had this problem IIRC, but I don't have any details.
Thanks,
Rafael
^ permalink raw reply
* Re: Runtime PM discussion notes
From: Paul Walmsley @ 2011-07-13 7:17 UTC (permalink / raw)
To: Mark Brown; +Cc: linux-pm, linux-omap
In-Reply-To: <20110711112619.GH5092@opensource.wolfsonmicro.com>
(cc'ing Len)
Hi Mark,
On Mon, 11 Jul 2011, Mark Brown wrote:
> The interesting bits are things like being able to kill lots of the SoC
> core supplies when the RAM is in retention mode - the CPU needs to go
> through its shutdown procedures.
This is indeed possible on OMAP3+ chips with TWL4030+ PMICs. Probably
other PMICs also. TI calls it "off-mode." The N900 shipped with this
feature enabled. Not sure how many other similar products did.
This can be enabled in mainline, but not all of the mainline drivers have
context save/restore code merged yet, so in mainline it only works with a
subset of drivers.
> Actually, it just occurred to me that if we're waiting for a system
> timer and can hand that off to a suitable timer in the PMIC then we can
> do a suspend to RAM for the deep idle state from the hardware point of
> view.
Yep. At LinuxCon Cambridge two years ago, we had a discussion about
whether it would be possible to enter ACPI S-states from CPUIdle (or some
idle governor) on Intel chips. If I remember correctly, the conclusion
was that ACPI always disables the screen/backlight, so it would only be
useful for situations where that was acceptable.
To the best of my (limited) knowledge, that's the only case I know of
where there's a hardware limitation that prevents dynamic idle from
reaching the same low power state as system suspend. If someone has hard
details of a similar example, it would be great to know about it.
- Paul
^ permalink raw reply
* [PATCH] mrst_pmu: driver for Intel Moorestown Power Management Unit
From: Len Brown @ 2011-07-13 3:30 UTC (permalink / raw)
To: linux-pm; +Cc: linux-kernel
From: Len Brown <len.brown@intel.com>
The Moorestown (MRST) Power Management Unit (PMU) driver
directs the SOC power states in the "Langwell" south complex (SCU).
It hooks pci_platform_pm_ops[] and thus observes all PCI ".set_state"
requests. For devices in the SC, the pmu driver translates those
PCI requests into the appropriate commands for the SCU.
The PMU driver helps implement S0i3, a deep system idle power idle state.
Entry into S0i3 is via cpuidle, just like regular processor c-states.
S0i3 depends on pre-conditions including uni-processor, graphics off,
and certain IO devices in the SC must be off. If those pre-conditions
are met, then the PMU allows cpuidle to enter S0i3, otherwise such requests
are demoted to Atom C6.
This driver is based on prototype work by Bruce Flemming,
Illyas Mansoor, Rajeev D. Muralidhar and Vishwesh M. Rudramuni.
It includes contributions from H. Peter Anvin, Arjan van de Ven,
Kristen Accardi, and Yong Wang.
Signed-off-by: Len Brown <len.brown@intel.com>
---
MAINTAINERS | 6 +
arch/x86/platform/mrst/Makefile | 1 +
arch/x86/platform/mrst/pmu.c | 814 +++++++++++++++++++++++++++++++++++++++
arch/x86/platform/mrst/pmu.h | 234 +++++++++++
4 files changed, 1055 insertions(+), 0 deletions(-)
create mode 100644 arch/x86/platform/mrst/pmu.c
create mode 100644 arch/x86/platform/mrst/pmu.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 187282d..d37b387 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3344,6 +3344,12 @@ F: drivers/net/ixgb/
F: drivers/net/ixgbe/
F: drivers/net/ixgbevf/
+INTEL MRST PMU DRIVER
+M: Len Brown <len.brown@intel.com>
+L: linux-pm@lists.linux-foundation.org
+S: Supported
+F: arch/x86/platform/mrst/pmu.*
+
INTEL PRO/WIRELESS 2100 NETWORK CONNECTION SUPPORT
L: linux-wireless@vger.kernel.org
S: Orphan
diff --git a/arch/x86/platform/mrst/Makefile b/arch/x86/platform/mrst/Makefile
index f61ccdd..1ea3877 100644
--- a/arch/x86/platform/mrst/Makefile
+++ b/arch/x86/platform/mrst/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_X86_MRST) += mrst.o
obj-$(CONFIG_X86_MRST) += vrtc.o
obj-$(CONFIG_EARLY_PRINTK_MRST) += early_printk_mrst.o
+obj-$(CONFIG_X86_MRST) += pmu.o
diff --git a/arch/x86/platform/mrst/pmu.c b/arch/x86/platform/mrst/pmu.c
new file mode 100644
index 0000000..3ca8447
--- /dev/null
+++ b/arch/x86/platform/mrst/pmu.c
@@ -0,0 +1,814 @@
+/*
+ * mrst/pmu.c - driver for MRST Power Management Unit
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/cpuidle.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/seq_file.h>
+#include <linux/sfi.h>
+#include <asm/intel_scu_ipc.h>
+#include "pmu.h"
+
+#define IPCMSG_FW_REVISION 0xF4
+
+struct mrst_device {
+ u16 pci_dev_num; /* DEBUG only */
+ u16 lss;
+ u16 latest_request;
+ unsigned int pci_state_counts[PCI_D3cold + 1]; /* DEBUG only */
+};
+
+/*
+ * comlete list of MRST PCI devices
+ */
+static struct mrst_device mrst_devs[] = {
+/* 0 */ { 0x0800, LSS_SPI0 }, /* Moorestown SPI Ctrl 0 */
+/* 1 */ { 0x0801, LSS_SPI1 }, /* Moorestown SPI Ctrl 1 */
+/* 2 */ { 0x0802, LSS_I2C0 }, /* Moorestown I2C 0 */
+/* 3 */ { 0x0803, LSS_I2C1 }, /* Moorestown I2C 1 */
+/* 4 */ { 0x0804, LSS_I2C2 }, /* Moorestown I2C 2 */
+/* 5 */ { 0x0805, LSS_KBD }, /* Moorestown Keyboard Ctrl */
+/* 6 */ { 0x0806, LSS_USB_HC }, /* Moorestown USB Ctrl */
+/* 7 */ { 0x0807, LSS_SD_HC0 }, /* Moorestown SD Host Ctrl 0 */
+/* 8 */ { 0x0808, LSS_SD_HC1 }, /* Moorestown SD Host Ctrl 1 */
+/* 9 */ { 0x0809, LSS_NAND }, /* Moorestown NAND Ctrl */
+/* 10 */ { 0x080a, LSS_AUDIO }, /* Moorestown Audio Ctrl */
+/* 11 */ { 0x080b, LSS_IMAGING }, /* Moorestown ISP */
+/* 12 */ { 0x080c, LSS_SECURITY }, /* Moorestown Security Controller */
+/* 13 */ { 0x080d, LSS_DISPLAY }, /* Moorestown External Displays */
+/* 14 */ { 0x080e, 0 }, /* Moorestown SCU IPC */
+/* 15 */ { 0x080f, LSS_GPIO }, /* Moorestown GPIO Controller */
+/* 16 */ { 0x0810, 0 }, /* Moorestown Power Management Unit */
+/* 17 */ { 0x0811, LSS_USB_OTG }, /* Moorestown OTG Ctrl */
+/* 18 */ { 0x0812, LSS_SPI2 }, /* Moorestown SPI Ctrl 2 */
+/* 19 */ { 0x0813, 0 }, /* Moorestown SC DMA */
+/* 20 */ { 0x0814, LSS_AUDIO_LPE }, /* Moorestown LPE DMA */
+/* 21 */ { 0x0815, LSS_AUDIO_SSP }, /* Moorestown SSP0 */
+
+/* 22 */ { 0x084F, LSS_SD_HC2 }, /* Moorestown SD Host Ctrl 2 */
+
+/* 23 */ { 0x4102, 0 }, /* Lincroft */
+/* 24 */ { 0x4110, 0 }, /* Lincroft */
+};
+
+/* n.b. We ignore PCI-id 0x815 in LSS9 b/c MeeGo has no driver for it */
+static u16 mrst_lss9_pci_ids[] = {0x080a, 0x0814, 0};
+static u16 mrst_lss10_pci_ids[] = {0x0800, 0x0801, 0x0802, 0x0803,
+ 0x0804, 0x0805, 0x080f, 0};
+
+/* handle concurrent SMP invokations of pmu_pci_set_power_state() */
+static spinlock_t mrst_pmu_power_state_lock;
+
+static unsigned int wake_counters[MRST_NUM_LSS]; /* DEBUG only */
+static unsigned int pmu_irq_stats[INT_INVALID + 1]; /* DEBUG only */
+
+/*
+ * s0i3_pmu_command_pending flag needs no SMP locking because...
+ *
+ * checked in mrst_pmu_max_cstate(), MP mode
+ *
+ * set true in mrst_pmu_s0i3_prepare() in UP mode, IRQs off
+ * set false in mrst_s0i3() upon successful s0i3 exit in UP mode, IRQs off
+ * set false in pmu_irq() after S0i3 exit aborted, MP-mode, IRQ's enabled
+ */
+static bool s0i3_pmu_command_pending;
+
+static int graphics_is_off;
+static int lss_s0i3_enabled;
+static bool mrst_pmu_s0i3_enable;
+
+/* debug counters */
+static u32 pmu_wait_ready_calls;
+static u32 pmu_wait_ready_udelays;
+static u32 pmu_wait_ready_udelays_max;
+static u32 pmu_wait_done_calls;
+static u32 pmu_wait_done_udelays;
+static u32 pmu_wait_done_udelays_max;
+static u32 pmu_set_power_state_entry;
+static u32 pmu_set_power_state_send_cmd;
+
+static struct mrst_device *pci_id_2_mrst_dev(u16 pci_dev_num)
+{
+ int index;
+
+ if ((pci_dev_num >= 0x0800) && (pci_dev_num <= 0x815))
+ index = pci_dev_num - 0x800;
+ else if (pci_dev_num == 0x084F)
+ index = 22;
+ else if (pci_dev_num == 0x4102)
+ index = 23;
+ else if (pci_dev_num == 0x4110)
+ index = 24;
+ else
+ BUG();
+
+ BUG_ON(pci_dev_num != mrst_devs[index].pci_dev_num);
+
+ return &mrst_devs[index];
+}
+
+/**
+ * mrst_pmu_validate_cstates
+ * @dev: cpuidle_device
+ *
+ * Certain states are not appropriate for governor to pick in some cases.
+ * This function will be called as cpuidle_device's prepare callback and
+ * thus tells governor to ignore such states when selecting the next state
+ * to enter.
+ */
+
+#define IDLE_STATE3_IS_C4 3
+#define IDLE_STATE4_IS_C6 4
+#define IDLE_STATE5_IS_S0I3 5
+
+int mrst_pmu_max_cstate(void)
+{
+ int cpu = smp_processor_id();
+
+ /*
+ * If there is a pending PMU command, we cannot enter C6.
+ */
+ if (s0i3_pmu_command_pending)
+ return IDLE_STATE3_IS_C4;
+
+ /*
+ * Disallow S0i3 if: PMU is not initialized, or CPU1 is active,
+ * or if device LSS is insufficient, or the GPU is active,
+ * or if it has been explicitly disabled, or PMU is busy.
+ */
+ if (!pmu_reg || !cpumask_equal(cpu_online_mask, cpumask_of(cpu)) ||
+ !lss_s0i3_enabled || !graphics_is_off || !mrst_pmu_s0i3_enable ||
+ pmu_read_busy_status())
+ return IDLE_STATE4_IS_C6;
+ else
+ return IDLE_STATE5_IS_S0I3;
+}
+
+void mrst_pmu_pending_set(int value)
+{
+ s0i3_pmu_command_pending = value;
+}
+
+void mrst_pmu_s0i3_prepare(void)
+{
+ /* Clear any possible error conditions */
+ pmu_write_ics(0x300);
+
+ /* set wake control to current D-states */
+ pmu_write_wssc(S0I3_SSS_TARGET);
+
+ /* Avoid entering conventional C6 until the PMU command has cleared */
+ mrst_pmu_pending_set(true);
+}
+
+/*
+ * pmu_update_wake_counters(): read PM_WKS, update wake_counters[]
+ * DEBUG only.
+ */
+static void pmu_update_wake_counters(void)
+{
+ int lss;
+ u32 wake_status;
+
+ wake_status = pmu_read_wks();
+
+ for (lss = 0; lss < MRST_NUM_LSS; ++lss) {
+ if (wake_status & (1 << lss))
+ wake_counters[lss]++;
+ }
+}
+
+int mrst_pmu_s0i3_entry(void)
+{
+ int status;
+
+ status = mrst_s0i3_entry(PM_S0I3_COMMAND, &pmu_reg->pm_cmd);
+ pmu_update_wake_counters();
+ return status;
+}
+
+/* poll for maximum of 5ms for busy bit to clear */
+static int pmu_wait_ready(void)
+{
+ int udelays;
+
+ pmu_wait_ready_calls++;
+
+ for (udelays = 0; udelays < 5000; ++udelays) {
+ if (udelays > pmu_wait_ready_udelays_max)
+ pmu_wait_ready_udelays_max = udelays;
+
+ if (pmu_read_busy_status() == 0)
+ return 0;
+
+ udelay(10);
+ pmu_wait_ready_udelays++;
+ }
+
+ /*
+ * if this fires, observe
+ * /sys/kernel/debug/mrst_pmu_wait_ready_calls
+ * /sys/kernel/debug/mrst_pmu_wait_ready_udelays
+ */
+ WARN_ONCE(1, "SCU not ready for 5ms");
+ return -EBUSY;
+}
+/* poll for maximum of 50ms us for busy bit to clear */
+static int pmu_wait_done(void)
+{
+ int udelays;
+
+ pmu_wait_done_calls++;
+
+ for (udelays = 0; udelays < 5000; ++udelays) {
+ if (udelays > pmu_wait_done_udelays_max)
+ pmu_wait_done_udelays_max = udelays;
+
+ if (pmu_read_busy_status() == 0)
+ return 0;
+
+ udelay(100);
+ pmu_wait_done_udelays++;
+ }
+
+ /*
+ * if this fires, observe
+ * /sys/kernel/debug/mrst_pmu_wait_done_calls
+ * /sys/kernel/debug/mrst_pmu_wait_done_udelays
+ */
+ WARN_ONCE(1, "SCU not done for 50ms");
+ return -EBUSY;
+}
+
+u32 mrst_pmu_msi_is_disabled(void)
+{
+ return pmu_msi_is_disabled();
+}
+
+void mrst_pmu_enable_msi(void)
+{
+ pmu_msi_enable();
+}
+
+/**
+ * pmu_irq - pmu driver interrupt handler
+ * Context: interrupt context
+ */
+static irqreturn_t pmu_irq(int irq, void *dummy)
+{
+ union pmu_pm_ics pmu_ics;
+
+ pmu_ics.value = pmu_read_ics();
+
+ if (!pmu_ics.bits.pending)
+ return IRQ_NONE;
+
+ switch (pmu_ics.bits.cause) {
+ case INT_SPURIOUS:
+ case INT_CMD_DONE:
+ case INT_CMD_ERR:
+ case INT_WAKE_RX:
+ case INT_SS_ERROR:
+ case INT_S0IX_MISS:
+ case INT_NO_ACKC6:
+ pmu_irq_stats[pmu_ics.bits.cause]++;
+ break;
+ default:
+ pmu_irq_stats[INT_INVALID]++;
+ }
+
+ mrst_pmu_pending_set(false);
+
+ pmu_write_ics(pmu_ics.value); /* Clear pending interrupt */
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * Translate PCI power management to MRST LSS D-states
+ */
+static int pci_2_mrst_state(int lss, pci_power_t pci_state)
+{
+ switch (pci_state) {
+ case PCI_D0:
+ if (SSMSK(D0i1, lss) & D0I1_ACG_SSS_TARGET)
+ return D0i1;
+ else
+ return D0;
+ case PCI_D1:
+ return D0i1;
+ case PCI_D2:
+ return D0i2;
+ case PCI_D3hot:
+ case PCI_D3cold:
+ return D0i3;
+ default:
+ WARN(1, "pci_state %d\n", pci_state);
+ return 0;
+ }
+}
+
+static int pmu_issue_command(u32 pm_ssc)
+{
+ union pmu_pm_set_cfg_cmd_t command;
+
+ if (pmu_read_busy_status()) {
+ pr_debug("pmu is busy, Operation not permitted\n");
+ return -1;
+ }
+
+ /*
+ * enable interrupts in PMU so that interrupts are
+ * propagated when ioc bit for a particular set
+ * command is set
+ */
+
+ pmu_irq_enable();
+
+ /* Configure the sub systems for pmu2 */
+
+ pmu_write_ssc(pm_ssc);
+
+ /*
+ * Send the set config command for pmu its configured
+ * for mode CM_IMMEDIATE & hence with No Trigger
+ */
+
+ command.pmu2_params.d_param.cfg_mode = CM_IMMEDIATE;
+ command.pmu2_params.d_param.cfg_delay = 0;
+ command.pmu2_params.d_param.rsvd = 0;
+
+ /* construct the command to send SET_CFG to particular PMU */
+ command.pmu2_params.d_param.cmd = SET_CFG_CMD;
+ command.pmu2_params.d_param.ioc = 0;
+ command.pmu2_params.d_param.mode_id = 0;
+ command.pmu2_params.d_param.sys_state = SYS_STATE_S0I0;
+
+ /* write the value of PM_CMD into particular PMU */
+ pr_debug("pmu command being written %x\n",
+ command.pmu_pm_set_cfg_cmd_value);
+
+ pmu_write_cmd(command.pmu_pm_set_cfg_cmd_value);
+
+ return 0;
+}
+
+static u16 pmu_min_lss_pci_req(u16 *ids, u16 pci_state)
+{
+ u16 existing_request;
+ int i;
+
+ for (i = 0; ids[i]; ++i) {
+ existing_request = pci_id_2_mrst_dev(ids[i])->latest_request;
+ if (existing_request < pci_state)
+ pci_state = existing_request;
+ }
+ return pci_state;
+}
+
+/**
+ * pmu_pci_set_power_state - Callback function is used by all the PCI devices
+ * for a platform specific device power on/shutdown.
+ */
+
+int pmu_pci_set_power_state(struct pci_dev *pdev, pci_power_t pci_state)
+{
+ u32 old_sss, new_sss;
+ int status = 0;
+ struct mrst_device *mrst_dev;
+
+ pmu_set_power_state_entry++;
+
+ BUG_ON(pdev->vendor != PCI_VENDOR_ID_INTEL);
+ BUG_ON(pci_state < PCI_D0 || pci_state > PCI_D3cold);
+
+ mrst_dev = pci_id_2_mrst_dev(pdev->device);
+ mrst_dev->pci_state_counts[pci_state]++; /* count invocations */
+
+ /* PMU driver calls self as part of PCI initialization, ignore */
+ if (pdev->device == PCI_DEV_ID_MRST_PMU)
+ return 0;
+
+ BUG_ON(!pmu_reg); /* SW bug if called before initialized */
+
+ spin_lock(&mrst_pmu_power_state_lock);
+
+ if (pdev->d3_delay) {
+ dev_dbg(&pdev->dev, "d3_delay %d, should be 0\n",
+ pdev->d3_delay);
+ pdev->d3_delay = 0;
+ }
+ /*
+ * If Lincroft graphics, simply remember state
+ */
+ if ((pdev->class >> 16) == PCI_BASE_CLASS_DISPLAY
+ && !((pdev->class & PCI_SUB_CLASS_MASK) >> 8)) {
+ if (pci_state == PCI_D0)
+ graphics_is_off = 0;
+ else
+ graphics_is_off = 1;
+ goto ret;
+ }
+
+ if (!mrst_dev->lss)
+ goto ret; /* device with no LSS */
+
+ if (mrst_dev->latest_request == pci_state)
+ goto ret; /* no change */
+
+ mrst_dev->latest_request = pci_state; /* record latest request */
+
+ /*
+ * LSS9 and LSS10 contain multiple PCI devices.
+ * Use the lowest numbered (highest power) state in the LSS
+ */
+ if (mrst_dev->lss == 9)
+ pci_state = pmu_min_lss_pci_req(mrst_lss9_pci_ids, pci_state);
+ else if (mrst_dev->lss == 10)
+ pci_state = pmu_min_lss_pci_req(mrst_lss10_pci_ids, pci_state);
+
+ status = pmu_wait_ready();
+ if (status)
+ goto ret;
+
+ old_sss = pmu_read_sss();
+ new_sss = old_sss & ~SSMSK(3, mrst_dev->lss);
+ new_sss |= SSMSK(pci_2_mrst_state(mrst_dev->lss, pci_state),
+ mrst_dev->lss);
+
+ if (new_sss == old_sss)
+ goto ret; /* nothing to do */
+
+ pmu_set_power_state_send_cmd++;
+
+ status = pmu_issue_command(new_sss);
+
+ if (unlikely(status != 0)) {
+ dev_err(&pdev->dev, "Failed to Issue a PM command\n");
+ goto ret;
+ }
+
+ if (pmu_wait_done())
+ goto ret;
+
+ lss_s0i3_enabled =
+ ((pmu_read_sss() & S0I3_SSS_TARGET) == S0I3_SSS_TARGET);
+ret:
+ spin_unlock(&mrst_pmu_power_state_lock);
+ return status;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static char *d0ix_names[] = {"D0", "D0i1", "D0i2", "D0i3"};
+
+static inline const char *d0ix_name(int state)
+{
+ return d0ix_names[(int) state];
+}
+
+static int debug_mrst_pmu_show(struct seq_file *s, void *unused)
+{
+ struct pci_dev *pdev = NULL;
+ u32 cur_pmsss;
+ int lss;
+
+ seq_printf(s, "0x%08X D0I1_ACG_SSS_TARGET\n", D0I1_ACG_SSS_TARGET);
+
+ cur_pmsss = pmu_read_sss();
+
+ seq_printf(s, "0x%08X S0I3_SSS_TARGET\n", S0I3_SSS_TARGET);
+
+ seq_printf(s, "0x%08X Current SSS ", cur_pmsss);
+ seq_printf(s, lss_s0i3_enabled ? "\n" : "[BLOCKS s0i3]\n");
+
+ if (cpumask_equal(cpu_online_mask, cpumask_of(0)))
+ seq_printf(s, "cpu0 is only cpu online\n");
+ else
+ seq_printf(s, "cpu0 is NOT only cpu online [BLOCKS S0i3]\n");
+
+ seq_printf(s, "GFX: %s\n", graphics_is_off ? "" : "[BLOCKS s0i3]");
+
+
+ for_each_pci_dev(pdev) {
+ int pos;
+ u16 pmcsr;
+ struct mrst_device *mrst_dev;
+ int i;
+
+ mrst_dev = pci_id_2_mrst_dev(pdev->device);
+
+ seq_printf(s, "%s %04x/%04X %-16.16s ",
+ dev_name(&pdev->dev),
+ pdev->vendor, pdev->device,
+ dev_driver_string(&pdev->dev));
+
+
+ if (mrst_dev->lss)
+ seq_printf(s, "LSS %2d %-4s ", mrst_dev->lss,
+ d0ix_name(((cur_pmsss >>
+ (mrst_dev->lss * 2)) & 0x3)));
+ else
+ seq_printf(s, " ");
+
+ /* PCI PM config space setting */
+ pos = pci_find_capability(pdev, PCI_CAP_ID_PM);
+ if (pos != 0) {
+ pci_read_config_word(pdev, pos + PCI_PM_CTRL, &pmcsr);
+ seq_printf(s, "PCI-%-4s",
+ pci_power_name(pmcsr & PCI_PM_CTRL_STATE_MASK));
+ } else {
+ seq_printf(s, " ");
+ }
+
+ seq_printf(s, " %s ", pci_power_name(mrst_dev->latest_request));
+ for (i = 0; i <= PCI_D3cold; ++i)
+ seq_printf(s, "%d ", mrst_dev->pci_state_counts[i]);
+
+ if (mrst_dev->lss) {
+ unsigned int lssmask;
+
+ lssmask = SSMSK(D0i3, mrst_dev->lss);
+
+ if ((lssmask & S0I3_SSS_TARGET) &&
+ ((lssmask & cur_pmsss) !=
+ (lssmask & S0I3_SSS_TARGET)))
+ seq_printf(s , "[BLOCKS s0i3]");
+ }
+
+ seq_printf(s, "\n");
+ }
+ seq_printf(s, "Wake Counters:\n");
+ for (lss = 0; lss < MRST_NUM_LSS; ++lss)
+ seq_printf(s, "LSS%d %d\n", lss, wake_counters[lss]);
+
+ seq_printf(s, "Interrupt Counters:\n");
+ seq_printf(s,
+ "INT_SPURIOUS \t%8u\n" "INT_CMD_DONE \t%8u\n"
+ "INT_CMD_ERR \t%8u\n" "INT_WAKE_RX \t%8u\n"
+ "INT_SS_ERROR \t%8u\n" "INT_S0IX_MISS\t%8u\n"
+ "INT_NO_ACKC6 \t%8u\n" "INT_INVALID \t%8u\n",
+ pmu_irq_stats[INT_SPURIOUS], pmu_irq_stats[INT_CMD_DONE],
+ pmu_irq_stats[INT_CMD_ERR], pmu_irq_stats[INT_WAKE_RX],
+ pmu_irq_stats[INT_SS_ERROR], pmu_irq_stats[INT_S0IX_MISS],
+ pmu_irq_stats[INT_NO_ACKC6], pmu_irq_stats[INT_INVALID]);
+
+ seq_printf(s, "mrst_pmu_wait_ready_calls %8d\n",
+ pmu_wait_ready_calls);
+ seq_printf(s, "mrst_pmu_wait_ready_udelays %8d\n",
+ pmu_wait_ready_udelays);
+ seq_printf(s, "mrst_pmu_wait_ready_udelays_max %8d\n",
+ pmu_wait_ready_udelays_max);
+ seq_printf(s, "mrst_pmu_wait_done_calls %8d\n",
+ pmu_wait_done_calls);
+ seq_printf(s, "mrst_pmu_wait_done_udelays %8d\n",
+ pmu_wait_done_udelays);
+ seq_printf(s, "mrst_pmu_wait_done_udelays_max %8d\n",
+ pmu_wait_done_udelays_max);
+ seq_printf(s, "mrst_pmu_set_power_state_entry %8d\n",
+ pmu_set_power_state_entry);
+ seq_printf(s, "mrst_pmu_set_power_state_send_cmd %8d\n",
+ pmu_set_power_state_send_cmd);
+ seq_printf(s, "SCU busy: %d\n", pmu_read_busy_status());
+
+ return 0;
+}
+
+static int debug_mrst_pmu_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, debug_mrst_pmu_show, NULL);
+}
+
+static const struct file_operations devices_state_operations = {
+ .open = debug_mrst_pmu_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif /* DEBUG_FS */
+
+/*
+ * Validate SCU PCI shim PCI vendor capability byte
+ * against LSS hard-coded in mrst_devs[] above.
+ * DEBUG only.
+ */
+static void pmu_scu_firmware_debug(void)
+{
+ struct pci_dev *pdev = NULL;
+
+ for_each_pci_dev(pdev) {
+ struct mrst_device *mrst_dev;
+ u8 pci_config_lss;
+ int pos;
+
+ mrst_dev = pci_id_2_mrst_dev(pdev->device);
+ if (mrst_dev->lss == 0)
+ continue; /* no LSS in our table */
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR);
+ if (!pos != 0) {
+ printk(KERN_ERR FW_BUG "pmu: 0x%04X "
+ "missing PCI Vendor Capability\n",
+ pdev->device);
+ continue;
+ }
+ pci_read_config_byte(pdev, pos + 4, &pci_config_lss);
+ if (!(pci_config_lss & PCI_VENDOR_CAP_LOG_SS_MASK)) {
+ printk(KERN_ERR FW_BUG "pmu: 0x%04X "
+ "invalid PCI Vendor Capability 0x%x "
+ " expected LSS 0x%X\n",
+ pdev->device, pci_config_lss, mrst_dev->lss);
+ continue;
+ }
+ pci_config_lss &= PCI_VENDOR_CAP_LOG_ID_MASK;
+
+ if (mrst_dev->lss == pci_config_lss)
+ continue;
+
+ printk(KERN_ERR FW_BUG "pmu: 0x%04X LSS = %d, expected %d\n",
+ pdev->device, pci_config_lss, mrst_dev->lss);
+ }
+}
+
+/**
+ * pmu_probe
+ */
+static int __devinit pmu_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+{
+ int ret;
+ struct mrst_pmu_reg *pmu;
+
+ /* Init the device */
+ ret = pci_enable_device(pdev);
+ if (ret) {
+ dev_err(&pdev->dev, "Unable to Enable PCI device\n");
+ return ret;
+ }
+
+ ret = pci_request_regions(pdev, MRST_PMU_DRV_NAME);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Cannot obtain PCI resources, aborting\n");
+ goto out_err1;
+ }
+
+ /* Map the memory of PMU reg base */
+ pmu = pci_iomap(pdev, 0, 0);
+ if (!pmu) {
+ dev_err(&pdev->dev, "Unable to map the PMU address space\n");
+ ret = -ENOMEM;
+ goto out_err2;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ /* /sys/kernel/debug/mrst_pmu */
+ (void) debugfs_create_file("mrst_pmu", S_IFREG | S_IRUGO,
+ NULL, NULL, &devices_state_operations);
+#endif
+ pmu_reg = pmu; /* success */
+
+ if (request_irq(pdev->irq, pmu_irq, 0, MRST_PMU_DRV_NAME, NULL)) {
+ dev_err(&pdev->dev, "Registering isr has failed\n");
+ ret = -1;
+ goto out_err3;
+ }
+
+ pmu_scu_firmware_debug();
+
+ pmu_write_wkc(S0I3_WAKE_SOURCES); /* Enable S0i3 wakeup sources */
+
+ pmu_wait_ready();
+
+ pmu_write_ssc(D0I1_ACG_SSS_TARGET); /* Enable Auto-Clock_Gating */
+ pmu_write_cmd(0x201);
+
+ spin_lock_init(&mrst_pmu_power_state_lock);
+
+ /* Enable the hardware interrupt */
+ pmu_irq_enable();
+ return 0;
+
+out_err3:
+ free_irq(pdev->irq, NULL);
+ pci_iounmap(pdev, pmu_reg);
+ pmu_reg = NULL;
+out_err2:
+ pci_release_region(pdev, 0);
+out_err1:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void __devexit pmu_remove(struct pci_dev *pdev)
+{
+ dev_err(&pdev->dev, "Mid PM pmu_remove called\n");
+
+ /* Freeing up the irq */
+ free_irq(pdev->irq, NULL);
+
+ pci_iounmap(pdev, pmu_reg);
+ pmu_reg = NULL;
+
+ /* disable the current PCI device */
+ pci_release_region(pdev, 0);
+ pci_disable_device(pdev);
+}
+
+static DEFINE_PCI_DEVICE_TABLE(pmu_pci_ids) = {
+ { PCI_VDEVICE(INTEL, PCI_DEV_ID_MRST_PMU), 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(pci, pmu_pci_ids);
+
+static struct pci_driver driver = {
+ .name = MRST_PMU_DRV_NAME,
+ .id_table = pmu_pci_ids,
+ .probe = pmu_probe,
+ .remove = __devexit_p(pmu_remove),
+};
+
+/**
+ * pmu_pci_register - register the PMU driver as PCI device
+ */
+static int __init pmu_pci_register(void)
+{
+ return pci_register_driver(&driver);
+}
+
+/* Register and probe via fs_initcall() to preceed device_initcall() */
+fs_initcall(pmu_pci_register);
+
+static void __exit mid_pci_cleanup(void)
+{
+ pci_unregister_driver(&driver);
+}
+
+static int ia_major;
+static int ia_minor;
+
+static int pmu_sfi_parse_oem(struct sfi_table_header *table)
+{
+ struct sfi_table_simple *sb;
+
+ sb = (struct sfi_table_simple *)table;
+ ia_major = (sb->pentry[1] >> 0) & 0xFFFF;
+ ia_minor = (sb->pentry[1] >> 16) & 0xFFFF;
+ printk(KERN_INFO "mrst_pmu: IA FW version v%x.%x\n",
+ ia_major, ia_minor);
+
+ return 0;
+}
+
+static int __init scu_fw_check(void)
+{
+ int ret;
+ u32 fw_version;
+
+ sfi_table_parse("OEMB", NULL, NULL, pmu_sfi_parse_oem);
+
+ if (ia_major < 0x6005 || ia_minor < 0x1525) {
+ WARN(1, "mrst_pmu: IA FW version too old\n");
+ return -1;
+ }
+
+ ret = intel_scu_ipc_command(IPCMSG_FW_REVISION, 0, NULL, 0,
+ &fw_version, 1);
+
+ if (ret) {
+ WARN(1, "mrst_pmu: IPC FW version? %d\n", ret);
+ } else {
+ int scu_major = (fw_version >> 8) & 0xFF;
+ int scu_minor = (fw_version >> 0) & 0xFF;
+
+ printk(KERN_INFO "mrst_pmu: firmware v%x\n", fw_version);
+
+ if ((scu_major >= 0xC0) && (scu_minor >= 0x49)) {
+ printk(KERN_INFO "mrst_pmu: enabling S0i3\n");
+ mrst_pmu_s0i3_enable = true;
+ } else {
+ WARN(1, "mrst_pmu: S0i3 disabled, old firmware %X.%X",
+ scu_major, scu_minor);
+ }
+ }
+ return 0;
+}
+late_initcall(scu_fw_check);
+module_exit(mid_pci_cleanup);
diff --git a/arch/x86/platform/mrst/pmu.h b/arch/x86/platform/mrst/pmu.h
new file mode 100644
index 0000000..bfbfe64
--- /dev/null
+++ b/arch/x86/platform/mrst/pmu.h
@@ -0,0 +1,234 @@
+/*
+ * mrst/pmu.h - private definitions for MRST Power Management Unit mrst/pmu.c
+ *
+ * Copyright (c) 2011, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _MRST_PMU_H_
+#define _MRST_PMU_H_
+
+#define PCI_DEV_ID_MRST_PMU 0x0810
+#define MRST_PMU_DRV_NAME "mrst_pmu"
+#define PCI_SUB_CLASS_MASK 0xFF00
+
+#define PCI_VENDOR_CAP_LOG_ID_MASK 0x7F
+#define PCI_VENDOR_CAP_LOG_SS_MASK 0x80
+
+#define SUB_SYS_ALL_D0I1 0x01155555
+#define S0I3_WAKE_SOURCES 0x00001FFF
+
+#define PM_S0I3_COMMAND \
+ ((0 << 31) | /* Reserved */ \
+ (0 << 30) | /* Core must be idle */ \
+ (0xc2 << 22) | /* ACK C6 trigger */ \
+ (3 << 19) | /* Trigger on DMI message */ \
+ (3 << 16) | /* Enter S0i3 */ \
+ (0 << 13) | /* Numeric mode ID (sw) */ \
+ (3 << 9) | /* Trigger mode */ \
+ (0 << 8) | /* Do not interrupt */ \
+ (1 << 0)) /* Set configuration */
+
+#define LSS_DMI 0
+#define LSS_SD_HC0 1
+#define LSS_SD_HC1 2
+#define LSS_NAND 3
+#define LSS_IMAGING 4
+#define LSS_SECURITY 5
+#define LSS_DISPLAY 6
+#define LSS_USB_HC 7
+#define LSS_USB_OTG 8
+#define LSS_AUDIO 9
+#define LSS_AUDIO_LPE 9
+#define LSS_AUDIO_SSP 9
+#define LSS_I2C0 10
+#define LSS_I2C1 10
+#define LSS_I2C2 10
+#define LSS_KBD 10
+#define LSS_SPI0 10
+#define LSS_SPI1 10
+#define LSS_SPI2 10
+#define LSS_GPIO 10
+#define LSS_SRAM 11 /* used by SCU, do not touch */
+#define LSS_SD_HC2 12
+/* LSS hardware bits 15,14,13 are hardwired to 0, thus unusable */
+#define MRST_NUM_LSS 13
+
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+
+#define SSMSK(mask, lss) ((mask) << ((lss) * 2))
+#define D0 0
+#define D0i1 1
+#define D0i2 2
+#define D0i3 3
+
+#define S0I3_SSS_TARGET ( \
+ SSMSK(D0i1, LSS_DMI) | \
+ SSMSK(D0i3, LSS_SD_HC0) | \
+ SSMSK(D0i3, LSS_SD_HC1) | \
+ SSMSK(D0i3, LSS_NAND) | \
+ SSMSK(D0i3, LSS_SD_HC2) | \
+ SSMSK(D0i3, LSS_IMAGING) | \
+ SSMSK(D0i3, LSS_SECURITY) | \
+ SSMSK(D0i3, LSS_DISPLAY) | \
+ SSMSK(D0i3, LSS_USB_HC) | \
+ SSMSK(D0i3, LSS_USB_OTG) | \
+ SSMSK(D0i3, LSS_AUDIO) | \
+ SSMSK(D0i1, LSS_I2C0))
+
+/*
+ * D0i1 on Langwell is Autonomous Clock Gating (ACG).
+ * Enable ACG on every LSS except camera and audio
+ */
+#define D0I1_ACG_SSS_TARGET \
+ (SUB_SYS_ALL_D0I1 & ~SSMSK(D0i1, LSS_IMAGING) & ~SSMSK(D0i1, LSS_AUDIO))
+
+enum cm_mode {
+ CM_NOP, /* ignore the config mode value */
+ CM_IMMEDIATE,
+ CM_DELAY,
+ CM_TRIGGER,
+ CM_INVALID
+};
+
+enum sys_state {
+ SYS_STATE_S0I0,
+ SYS_STATE_S0I1,
+ SYS_STATE_S0I2,
+ SYS_STATE_S0I3,
+ SYS_STATE_S3,
+ SYS_STATE_S5
+};
+
+#define SET_CFG_CMD 1
+
+enum int_status {
+ INT_SPURIOUS = 0,
+ INT_CMD_DONE = 1,
+ INT_CMD_ERR = 2,
+ INT_WAKE_RX = 3,
+ INT_SS_ERROR = 4,
+ INT_S0IX_MISS = 5,
+ INT_NO_ACKC6 = 6,
+ INT_INVALID = 7,
+};
+
+/* PMU register interface */
+static struct mrst_pmu_reg {
+ u32 pm_sts; /* 0x00 */
+ u32 pm_cmd; /* 0x04 */
+ u32 pm_ics; /* 0x08 */
+ u32 _resv1; /* 0x0C */
+ u32 pm_wkc[2]; /* 0x10 */
+ u32 pm_wks[2]; /* 0x18 */
+ u32 pm_ssc[4]; /* 0x20 */
+ u32 pm_sss[4]; /* 0x30 */
+ u32 pm_wssc[4]; /* 0x40 */
+ u32 pm_c3c4; /* 0x50 */
+ u32 pm_c5c6; /* 0x54 */
+ u32 pm_msi_disable; /* 0x58 */
+} *pmu_reg;
+
+static inline u32 pmu_read_sts(void) { return readl(&pmu_reg->pm_sts); }
+static inline u32 pmu_read_ics(void) { return readl(&pmu_reg->pm_ics); }
+static inline u32 pmu_read_wks(void) { return readl(&pmu_reg->pm_wks[0]); }
+static inline u32 pmu_read_sss(void) { return readl(&pmu_reg->pm_sss[0]); }
+
+static inline void pmu_write_cmd(u32 arg) { writel(arg, &pmu_reg->pm_cmd); }
+static inline void pmu_write_ics(u32 arg) { writel(arg, &pmu_reg->pm_ics); }
+static inline void pmu_write_wkc(u32 arg) { writel(arg, &pmu_reg->pm_wkc[0]); }
+static inline void pmu_write_ssc(u32 arg) { writel(arg, &pmu_reg->pm_ssc[0]); }
+static inline void pmu_write_wssc(u32 arg)
+ { writel(arg, &pmu_reg->pm_wssc[0]); }
+
+static inline void pmu_msi_enable(void) { writel(0, &pmu_reg->pm_msi_disable); }
+static inline u32 pmu_msi_is_disabled(void)
+ { return readl(&pmu_reg->pm_msi_disable); }
+
+union pmu_pm_ics {
+ struct {
+ u32 cause:8;
+ u32 enable:1;
+ u32 pending:1;
+ u32 reserved:22;
+ } bits;
+ u32 value;
+};
+
+static inline void pmu_irq_enable(void)
+{
+ union pmu_pm_ics pmu_ics;
+
+ pmu_ics.value = pmu_read_ics();
+ pmu_ics.bits.enable = 1;
+ pmu_write_ics(pmu_ics.value);
+}
+
+union pmu_pm_status {
+ struct {
+ u32 pmu_rev:8;
+ u32 pmu_busy:1;
+ u32 mode_id:4;
+ u32 Reserved:19;
+ } pmu_status_parts;
+ u32 pmu_status_value;
+};
+
+static inline int pmu_read_busy_status(void)
+{
+ union pmu_pm_status result;
+
+ result.pmu_status_value = pmu_read_sts();
+
+ return result.pmu_status_parts.pmu_busy;
+}
+
+/* pmu set config parameters */
+struct cfg_delay_param_t {
+ u32 cmd:8;
+ u32 ioc:1;
+ u32 cfg_mode:4;
+ u32 mode_id:3;
+ u32 sys_state:3;
+ u32 cfg_delay:8;
+ u32 rsvd:5;
+};
+
+struct cfg_trig_param_t {
+ u32 cmd:8;
+ u32 ioc:1;
+ u32 cfg_mode:4;
+ u32 mode_id:3;
+ u32 sys_state:3;
+ u32 cfg_trig_type:3;
+ u32 cfg_trig_val:8;
+ u32 cmbi:1;
+ u32 rsvd1:1;
+};
+
+union pmu_pm_set_cfg_cmd_t {
+ union {
+ struct cfg_delay_param_t d_param;
+ struct cfg_trig_param_t t_param;
+ } pmu2_params;
+ u32 pmu_pm_set_cfg_cmd_value;
+};
+
+#ifdef FUTURE_PATCH
+extern int mrst_s0i3_entry(u32 regval, u32 *regaddr);
+#else
+static inline int mrst_s0i3_entry(u32 regval, u32 *regaddr) { return -1; }
+#endif
+#endif
--
1.7.4.4
^ permalink raw reply related
* Re: [PATCH] ARM / shmobile: Make A3RV be a subdomain of A4LC on SH7372
From: Magnus Damm @ 2011-07-13 1:39 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: Linux PM mailing list, linux-sh
In-Reply-To: <201107130040.02752.rjw@sisk.pl>
On Wed, Jul 13, 2011 at 7:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Instead of coding the undocumented dependencies between power domains
> A3RV and A4LC on SH7372 directly into the low-level power up/down
> routines, make A3RV be a subdomain of A4LC, which will cause the
> same dependecies to hold.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---
Thanks for your effort of cleaning up the code!
You are correct that putting A3RV as a subdomain of A4LC will help
with one side of the problem. The not so visible problem at this point
is the connection between A4R and A3RV. A4R is a power domain which
contains some I/O devices and the two power domains A3RV and A3RI.
Because of the hardware dependency between A4R and A3RV we would need
to force A4R to be powered on whenever A3RV is being used. So A4R is
the "real" parent pm domain from the data sheet point of view.
To simplify the software side one option could be to make A4LC the
parent of A3RV and A4R the parent of A4LC. This is not however a very
good fit either - the hardware allows us to have A4LC powered on
without A4R or A3RV. This case is quite common and happens when any
LCD panel is enabled but the rest of the system is idle.
For the case when no A4R support is in place your suggestion is fine.
But to support A4R we need to deal with something similar to what my
patch does anyway. It is worth nothing that A4R contains an interrupt
controller and needs special care that way too. Also the I/O devices
in A4R require QoS software to be in place before we can start
powering down A4R for real.
So the reason behind my somewhat ugly existing A3RV and A4LC power
domain code is to make sure the framework code can do whatever we need
to do for A4R support. Or perhaps it can be done in a better way? Any
ideas?
Thanks,
/ magnus
^ permalink raw reply
* [PATCH] ARM / shmobile: Make A3RV be a subdomain of A4LC on SH7372
From: Rafael J. Wysocki @ 2011-07-12 22:40 UTC (permalink / raw)
To: Magnus Damm; +Cc: Linux PM mailing list, linux-sh
From: Rafael J. Wysocki <rjw@sisk.pl>
Instead of coding the undocumented dependencies between power domains
A3RV and A4LC on SH7372 directly into the low-level power up/down
routines, make A3RV be a subdomain of A4LC, which will cause the
same dependecies to hold.
Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
This patch hasn't been tested yet, so caveat emptor!
Thanks,
Rafael
---
arch/arm/mach-shmobile/pm-sh7372.c | 43 +---------------------------------
arch/arm/mach-shmobile/setup-sh7372.c | 3 ++
2 files changed, 5 insertions(+), 41 deletions(-)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -91,36 +91,6 @@ static int pd_power_up(struct generic_pm
return ret;
}
-static int pd_power_up_a3rv(struct generic_pm_domain *genpd)
-{
- int ret = pd_power_up(genpd);
-
- /* force A4LC on after A3RV has been requested on */
- pm_genpd_poweron(&sh7372_a4lc.genpd);
-
- return ret;
-}
-
-static int pd_power_down_a3rv(struct generic_pm_domain *genpd)
-{
- int ret = pd_power_down(genpd);
-
- /* try to power down A4LC after A3RV is requested off */
- pm_genpd_poweron(&sh7372_a4lc.genpd);
- queue_work(pm_wq, &sh7372_a4lc.genpd.power_off_work);
-
- return ret;
-}
-
-static int pd_power_down_a4lc(struct generic_pm_domain *genpd)
-{
- /* only power down A4LC if A3RV is off */
- if (!(__raw_readl(PSTR) & (1 << sh7372_a3rv.bit_shift)))
- return pd_power_down(genpd);
-
- return 0;
-}
-
static bool pd_active_wakeup(struct device *dev)
{
return true;
@@ -134,17 +104,8 @@ void sh7372_init_pm_domain(struct sh7372
genpd->stop_device = pm_clk_suspend;
genpd->start_device = pm_clk_resume;
genpd->active_wakeup = pd_active_wakeup;
-
- if (sh7372_pd == &sh7372_a4lc) {
- genpd->power_off = pd_power_down_a4lc;
- genpd->power_on = pd_power_up;
- } else if (sh7372_pd == &sh7372_a3rv) {
- genpd->power_off = pd_power_down_a3rv;
- genpd->power_on = pd_power_up_a3rv;
- } else {
- genpd->power_off = pd_power_down;
- genpd->power_on = pd_power_up;
- }
+ genpd->power_off = pd_power_down;
+ genpd->power_on = pd_power_up;
genpd->power_on(&sh7372_pd->genpd);
}
Index: linux-2.6/arch/arm/mach-shmobile/setup-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/setup-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/setup-sh7372.c
@@ -30,6 +30,7 @@
#include <linux/sh_dma.h>
#include <linux/sh_intc.h>
#include <linux/sh_timer.h>
+#include <linux/pm_domain.h>
#include <mach/hardware.h>
#include <mach/sh7372.h>
#include <asm/mach-types.h>
@@ -848,6 +849,8 @@ void __init sh7372_add_standard_devices(
sh7372_init_pm_domain(&sh7372_a3ri);
sh7372_init_pm_domain(&sh7372_a3sg);
+ pm_genpd_add_subdomain(&sh7372_a4lc.genpd, &sh7372_a3sg.genpd);
+
platform_add_devices(sh7372_early_devices,
ARRAY_SIZE(sh7372_early_devices));
^ permalink raw reply
* Re: [PATCH 0/4] OMAP: PM: omap_device: update PM domain support for v3.1
From: Rafael J. Wysocki @ 2011-07-12 20:42 UTC (permalink / raw)
To: Kevin Hilman; +Cc: linux-omap, linux-pm, linux-arm-kernel
In-Reply-To: <1310426969-30306-1-git-send-email-khilman@ti.com>
Hi,
On Tuesday, July 12, 2011, Kevin Hilman wrote:
> For v3.1, the PM core has some changes that impact various assumptions
> made (by me) during the design and implementation of the PM domain
> support in the omap_device layer.
>
> This series is needed to update our PM domain layer to behave properly
> under the new rules of the PM core code which are planned to be merged
> for v3.1 (see recent linux-pm discussions for the gory details.)
>
> This series is based on the pm-domains branch of Rafael Wysocki's
> suspend-2.6 tree, and also depends on the patch which adds
> pm_runtime_status_suspended() helper function[1].
>
> Rafael, because of the dependencies on your pm-domains branch (due to
> the pwr_domain -> pm_domain rename), it might be easiest to merge
> these via your tree. Let me know if that's OK with you.
Yes, it is. I'm going to apply them to my pm-runtime branch that is based
on pm-domains.
Thanks,
Rafael
^ permalink raw reply
* Re: [RFC] Unprepare callback for cpuidle_device
From: Vaidyanathan Srinivasan @ 2011-07-12 18:32 UTC (permalink / raw)
To: asinghal; +Cc: linux-pm, johlstei
In-Reply-To: <57f0d61f9e15b2af262607e872746317.squirrel@www.codeaurora.org>
* asinghal@codeaurora.org <asinghal@codeaurora.org> [2011-07-11 14:45:58]:
> hello Vaidy,
> doesn't tick_nohz_get_sleep_length just return ts->sleep_length
>
> variable value from the tick_sched structure for that cpu ?
Yes, I assumed tick_nohz_get_sleep_length() was going to look for the
next timer, but it seems to return the sleep_length that was computed
during tick_nohz_stop_sched_tick() as you have mentioned.
> The calculation of this sleep_length variable happens in function
>
> tick_nohz_stop_Sched_tick after call to function get_next_timer_interrupt ??
This makes the implementation more complex. Even if we keep the
prepare() and add an unprepare(), all of these will be called within
pm_idle() only which is much later than tick_nohz_stop_sched_tick() in
cpu_idle(). So it is not easy to have a cpuidle callback before the
ts->sleep_length is updated.
This leaves us with the option of exploring deferrable hrtimers that
will be skipped during idle entry.
--Vaidy
PS: Please reply inline to keep the discussion well structured.
> thanks,
> amar
>
>
>
> > * asinghal@codeaurora.org <asinghal@codeaurora.org> [2011-07-11 10:26:56]:
> >
> >> hello Deepthi,
> >> please see my replies inline:
> >>
> >>
> >> > Hi Amar,
> >> >
> >> > On Friday 08 July 2011 01:22 AM, asinghal@codeaurora.org wrote:
> >> >> Hello Trinabh,
> >> >> i cannot use the enter callback due to the following
> >> >> reason:
> >> >>
> >> >> the residency calculation(tick)nohz_get_sleep_length) and the idle
> >> state
> >> >> selection happens in the menu governor. The enter callback is called
> >> >> with
> >> >> the selected state.
> >> >
> >> > One would first execute prepare then call select and enter .
> >> > So in the newer proposed code, there is no prepare routine. One
> >> would
> >> > just execute select() and then enter() (Prepare functionality is
> >> part of
> >> > the
> >> > enter routine itself) Is it not possible to cancel the timer,
> >> > calculate the execute nohz_get_sleep in enter routine again,
> >> > then select the idle state depending on the time . Automatically
> >> promote
> >> > or demote
> >> > idle state based on the latest value returned in the driver ?
> >> >
> >> Cancelling the timer and calculating the sleep length again in the enter
> >> callback is possible; but what does not make sense is calling the select
> >> routine again ; definitely the select routine of the governor needs to
> >> be
> >> called only once. And actually, the sleep length is calculated in the
> >
> > Can you promote the idle state within the driver based on the new
> > information provided by tick_nohz_get_sleep_length() after canceling
> > the timer? This will be a hack... but better than calling select()
> > again. You are right, we need to design in such a way that select()
> > makes the right choice.
> >
> >> function tick_nohz_stop_sched_tick.( so shld the prepare callback be
> >> moved
> >> before that in cpu_idle ??)
> >
> > The prepare callback is in the right place now... if you cancel the
> > timer in prepare, then subsequent select() which calls
> > tick_nohz_get_sleep_length() will get the correct idle time. Your
> > problem is in restarting the timer in case we abort the idle. The
> > current place of prepare is fine and more over we are heading in the
> > direction to deprecate the prepare itself.
> >
> >> I am looking for a nice way to cancel and restart the high resolution
> >> timers in the idle code; even if that means rearranging the cpu_idle
> >> code.
> >
> > Slight difference, you can cancel and restart within the driver, the
> > problem is that you want the ->select() to be executed _after_
> > canceling your hrtimer because it will predict idle much shorter
> > otherwise. This is where the cpuidle framework should help.
> >
> >> Another way i was looking to solve this issue was add deferrable feature
> >> to HR timers; if someone has thoughts on that please chime in.
> >
> > This seems to be a much better idea, where the
> > tick_nohz_get_sleep_length() can automatically skip your deferred
> > hrtimer with a hope that it will be canceled. We don't have
> > a mechanism to actually defer it.
> >
> > You can try to add deferred flag in hrtimer_mode { } similar to how
> > pinned was added. Enhance tick_nohz_get_sleep_length() to skip over
> > deferred hrtimer as well.
> >
> > But the number of users for this feature will be very little to
> > justify the complexity.
> >
> > --Vaidy
> >
> >
> >
>
>
^ permalink raw reply
* Re: Issues using runtime API's with console uart
From: Vishwanath Sripathy @ 2011-07-12 11:51 UTC (permalink / raw)
To: Govindraj, linux-omap, Linux PM mailing list
Cc: Partha Basak, linux-arm-kernel
In-Reply-To: <CAAL8m4yeT9Qm0WFP-7rVH6dMuLd0YHB9zGS4NiHL8thOY4QWFg@mail.gmail.com>
+linux_pm.
Vishwa
> -----Original Message-----
> From: Govindraj [mailto:govindraj.ti@gmail.com]
> Sent: Tuesday, July 12, 2011 5:14 PM
> To: linux-omap@vger.kernel.org
> Cc: Kevin Hilman; Paul Walmsley; Basak, Partha; Tony Lindgren;
> Sripathy, Vishwanath; linux-arm-kernel@lists.infradead.org; Rajendra
> Nayak; Shilimkar, Santosh
> Subject: RFC: Issues using runtime API's with console uart
>
> Hi All,
>
> With recent uart runtime conversion I am facing some issues with
> runtime
> API usage with console.
>
> With runtime adaptation the clock cutting is done aggressively with
> get and put.
>
> as below with console_write API in uart code.
>
> serial_omap_console_write(.....)
> {
> get_sync();
> .
> .
> .
> put();
> }
>
> serial_omap_set_mctrl(..)
> {
> get_sync();
> .
> .
> .
> put()
> }
>
> Now if debug messages(pr_debug) from hwmod and omap_device are
> enabled
> then get_sync and put will internally have prints which get
> redirected to
> console_write and will call get_sync.
>
> so from a runtime API context I call another runtime API which leads
> to power lock contention, since every runtime API entrant tries to
> get
> a power lock.
>
> To elaborate further from above code snippets consider set_mctrl
> calling get_sync and printk from omap_hwmod calling console_write
> which calls get_sync.
>
> -> leads to get_sync calling get_sync.
>
> similarly with put_sync having prints calling console driver calling
> get_sync
> leading to power_lock contention.
>
> As of today I don't see any clean approaches to be available.
>
> Some of the approaches I am considering is
>
> 1.) let the clock gating for uart be handled with idle path as done
> today with
> prepare and resume calls.
>
> 2.) Aggressively binding all get and put with console lock to
> suppress
> prints from get/put and to printed during console_unlock.
>
> uart_port_enb(..)
> {
> if (console_trylock()) {
> get_sync();
> console_unlock();
> }
> }
>
> Even this approach doesn't seem fool proof, Similarly if I have
> port_disable
> console unlock(has not yet released the console lock)
> will have a print stating uart clock is disabled will call
> console_write
> which will call port_enb since above trylock fails and
> eventually
> might oops out trying
> to print the message as put has already disabled the uart
> clocks.
>
>
> I don't see any clean method rather using approach[1] as of today.
>
> So any suggestions on any other approach will be helpful.
>
> ---
> Thanks,
> Govindraj.R
^ permalink raw reply
* Re: [PATCH] PM / Runtime: add new helper function: pm_runtime_status_suspended()
From: Rafael J. Wysocki @ 2011-07-12 9:33 UTC (permalink / raw)
To: Kevin Hilman; +Cc: linux-pm, linux-omap
In-Reply-To: <1310424682-21255-1-git-send-email-khilman@ti.com>
On Tuesday, July 12, 2011, Kevin Hilman wrote:
> This boolean function simply returns whether or not the runtime status
> of the device is 'suspended'. Unlike pm_runtime_suspended(), this
> function returns the runtime status whether or not runtime PM for the
> device has been disabled or not.
>
> Also add entry to Documentation/power/runtime.txt
>
> Suggested-by: Rafael J. Wysocki <rjw@sisk.pl>
> Signed-off-by: Kevin Hilman <khilman@ti.com>
Applied to suspend-2.6/pm-runtime, thanks!
Rafael
> ---
> Documentation/power/runtime_pm.txt | 3 +++
> include/linux/pm_runtime.h | 6 ++++++
> 2 files changed, 9 insertions(+), 0 deletions(-)
>
> diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt
> index 4b011b1..feca92f 100644
> --- a/Documentation/power/runtime_pm.txt
> +++ b/Documentation/power/runtime_pm.txt
> @@ -400,6 +400,9 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
> - return true if the device's runtime PM status is 'suspended' and its
> 'power.disable_depth' field is equal to zero, or false otherwise
>
> + bool pm_runtime_status_suspended(struct device *dev);
> + - return true if the device's runtime PM status is 'suspended'
> +
> void pm_runtime_allow(struct device *dev);
> - set the power.runtime_auto flag for the device and decrease its usage
> counter (used by the /sys/devices/.../power/control interface to
> diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
> index dfb8539..daac05d 100644
> --- a/include/linux/pm_runtime.h
> +++ b/include/linux/pm_runtime.h
> @@ -82,6 +82,11 @@ static inline bool pm_runtime_suspended(struct device *dev)
> && !dev->power.disable_depth;
> }
>
> +static inline bool pm_runtime_status_suspended(struct device *dev)
> +{
> + return dev->power.runtime_status == RPM_SUSPENDED;
> +}
> +
> static inline bool pm_runtime_enabled(struct device *dev)
> {
> return !dev->power.disable_depth;
> @@ -130,6 +135,7 @@ static inline void pm_runtime_put_noidle(struct device *dev) {}
> static inline bool device_run_wake(struct device *dev) { return false; }
> static inline void device_set_run_wake(struct device *dev, bool enable) {}
> static inline bool pm_runtime_suspended(struct device *dev) { return false; }
> +static inline bool pm_runtime_status_suspended(struct device *dev) { return false; }
> static inline bool pm_runtime_enabled(struct device *dev) { return false; }
>
> static inline int pm_generic_runtime_idle(struct device *dev) { return 0; }
>
^ permalink raw reply
* Re: [PATCH 00/10] mm: Linux VM Infrastructure to support Memory Power Management
From: amit kachhap @ 2011-07-12 5:31 UTC (permalink / raw)
To: Andrew Morton
Cc: linux-kernel, linaro-mm-sig, linux-mm, thomas.abraham,
Ankita Garg, linux-pm, linux-arm-kernel
In-Reply-To: <20110528005640.9076c0b1.akpm@linux-foundation.org>
Hi All,
In response to the discussion about power savings with memory regions
features following measurements are done.
Title:
On a system with 2GB memory , 1GB is static and the other 1GB in
various power states.
Brief environment description:
Samsung smdk-exynos board is used for this work and full board level
power consumption is measured that comprises of cpu and other
components. It has 2 DMC's(Dynamic memory controller) with each
supporting 1 GB DDR3 memory. Power characteristics of DMC0 controlled
memory remain same but memory controlled by DMC1 is changed to 4
different power states. The following numbers describe the maximum
power savings measured after executing the software from DMC0
controlled memory which changes the power states of DMC1 controlled
memory. Here the actual numbers are not mentioned but the percentage
power savings is shown in reference to the change in overall power
consumption. The memory region patches are expected to facilitate
transition of memory into into one of the following low power states.
1) Percentage power savings when DMC1(1GB) moved to self refresh mode
from idle/unaccess mode= 2.69%
2) Percentage power savings when DMC1(1GB) moved to precharge mode
from idle/unaccess mode= 3.23%
3) Percentage power savings when DMC1(1GB) clock is gated = 6.32%
The above power savings is indicative of the benefits that memory
regions could provide in this platform.
Thanks & Regards,
Amit Daniel Kachhap
Samsung India s/w operations, Bangalore
On Sat, May 28, 2011 at 1:26 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Fri, 27 May 2011 18:01:28 +0530 Ankita Garg <ankita@in.ibm.com> wrote:
>
>> This patchset proposes a generic memory regions infrastructure that can be
>> used to tag boundaries of memory blocks which belongs to a specific memory
>> power management domain and further enable exploitation of platform memory
>> power management capabilities.
>
> A couple of quick thoughts...
>
> I'm seeing no estimate of how much energy we might save when this work
> is completed. But saving energy is the entire point of the entire
> patchset! So please spend some time thinking about that and update and
> maintain the [patch 0/n] description so others can get some idea of the
> benefit we might get from all of this. That estimate should include an
> estimate of what proportion of machines are likely to have hardware
> which can use this feature and in what timeframe.
>
> IOW, if it saves one microwatt on 0.001% of machines, not interested ;)
>
>
> Also, all this code appears to be enabled on all machines? So machines
> which don't have the requisite hardware still carry any additional
> overhead which is added here. I can see that ifdeffing a feature like
> this would be ghastly but please also have a think about the
> implications of this and add that discussion also.
>
> If possible, it would be good to think up some microbenchmarks which
> probe the worst-case performance impact and describe those and present
> the results. So others can gain an understanding of the runtime costs.
>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply
* [PATCH 4/4] OMAP: PM: disable idle on suspend for GPIO and UART
From: Kevin Hilman @ 2011-07-11 23:29 UTC (permalink / raw)
To: Rafael J. Wysocki, Paul Walmsley, linux-omap; +Cc: linux-pm, linux-arm-kernel
In-Reply-To: <1310426969-30306-1-git-send-email-khilman@ti.com>
Until these drivers are runtime PM converted, their device power
states are managed by calling custom driver hooks late in the
idle/suspend path. Therefore, do not let the suspend/resume core code
automatically idle these devices since they will be managed manually
by the OMAP PM core very late in the idle/suspend path.
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/mach-omap2/gpio.c | 2 ++
arch/arm/mach-omap2/serial.c | 1 +
2 files changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-omap2/gpio.c b/arch/arm/mach-omap2/gpio.c
index 9529842..48e5ece 100644
--- a/arch/arm/mach-omap2/gpio.c
+++ b/arch/arm/mach-omap2/gpio.c
@@ -87,6 +87,8 @@ static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
return PTR_ERR(od);
}
+ omap_device_disable_idle_on_suspend(od);
+
gpio_bank_count++;
return 0;
}
diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
index 1ac361b..466fc722 100644
--- a/arch/arm/mach-omap2/serial.c
+++ b/arch/arm/mach-omap2/serial.c
@@ -805,6 +805,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata)
WARN(IS_ERR(od), "Could not build omap_device for %s: %s.\n",
name, oh->name);
+ omap_device_disable_idle_on_suspend(od);
oh->mux = omap_hwmod_mux_init(bdata->pads, bdata->pads_cnt);
uart->irq = oh->mpu_irqs[0].irq;
--
1.7.6
^ permalink raw reply related
* [PATCH 3/4] OMAP: PM: omap_device: add API to disable idle on suspend
From: Kevin Hilman @ 2011-07-11 23:29 UTC (permalink / raw)
To: Rafael J. Wysocki, Paul Walmsley, linux-omap; +Cc: linux-pm, linux-arm-kernel
In-Reply-To: <1310426969-30306-1-git-send-email-khilman@ti.com>
By default, omap_devices will be automatically idled on suspend
(and re-enabled on resume.) Using this new API, device init code
can disable this feature if desired.
NOTE: any driver/device that has been runtime PM converted should
not be using this API.
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/plat-omap/include/plat/omap_device.h | 5 +++++
arch/arm/plat-omap/omap_device.c | 6 ++++++
2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
index bc36d05..ee405b36 100644
--- a/arch/arm/plat-omap/include/plat/omap_device.h
+++ b/arch/arm/plat-omap/include/plat/omap_device.h
@@ -46,6 +46,7 @@ extern struct device omap_device_parent;
/* omap_device.flags values */
#define OMAP_DEVICE_SUSPENDED BIT(0)
+#define OMAP_DEVICE_NO_IDLE_ON_SUSPEND BIT(1)
/**
* struct omap_device - omap_device wrapper for platform_devices
@@ -121,6 +122,10 @@ int omap_device_enable_hwmods(struct omap_device *od);
int omap_device_disable_clocks(struct omap_device *od);
int omap_device_enable_clocks(struct omap_device *od);
+static inline void omap_device_disable_idle_on_suspend(struct omap_device *od)
+{
+ od->flags |= OMAP_DEVICE_NO_IDLE_ON_SUSPEND;
+}
/*
* Entries should be kept in latency order ascending
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index b93cfdc..2526fa3 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -573,6 +573,9 @@ static int _od_suspend_noirq(struct device *dev)
struct omap_device *od = to_omap_device(pdev);
int ret;
+ if (od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)
+ return pm_generic_suspend_noirq(dev);
+
ret = pm_generic_suspend_noirq(dev);
if (!ret && !pm_runtime_status_suspended(dev)) {
@@ -590,6 +593,9 @@ static int _od_resume_noirq(struct device *dev)
struct platform_device *pdev = to_platform_device(dev);
struct omap_device *od = to_omap_device(pdev);
+ if (od->flags & OMAP_DEVICE_NO_IDLE_ON_SUSPEND)
+ return pm_generic_resume_noirq(dev);
+
if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
!pm_runtime_status_suspended(dev)) {
od->flags &= ~OMAP_DEVICE_SUSPENDED;
--
1.7.6
^ permalink raw reply related
* [PATCH 2/4] OMAP: PM: omap_device: add system PM methods for PM domain handling
From: Kevin Hilman @ 2011-07-11 23:29 UTC (permalink / raw)
To: Rafael J. Wysocki, Paul Walmsley, linux-omap; +Cc: linux-pm, linux-arm-kernel
In-Reply-To: <1310426969-30306-1-git-send-email-khilman@ti.com>
In the omap_device PM domain callbacks, use omap_device idle/enable to
automatically manage device idle states during system suspend/resume.
If an omap_device has not already been runtime suspended, the
->suspend_noirq() method of the PM domain will use omap_device_idle()
to idle the HW after calling the driver's ->runtime_suspend()
callback. Similarily, upon resume, if the device was suspended during
->suspend_noirq(), the ->resume_noirq() method of the PM domain will
use omap_device_enable() to enable the HW and then call the driver's
->runtime_resume() callback.
If a device has already been runtime suspended, the noirq methods of
the PM domain leave the device runtime suspended by default.
However, if a driver needs to runtime resume a device during suspend
(for example, to change its wakeup settings), it may do so using
pm_runtime_get* in it's ->suspend() callback.
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/plat-omap/include/plat/omap_device.h | 4 +++
arch/arm/plat-omap/omap_device.c | 36 +++++++++++++++++++++++++
2 files changed, 40 insertions(+), 0 deletions(-)
diff --git a/arch/arm/plat-omap/include/plat/omap_device.h b/arch/arm/plat-omap/include/plat/omap_device.h
index e4c349f..bc36d05 100644
--- a/arch/arm/plat-omap/include/plat/omap_device.h
+++ b/arch/arm/plat-omap/include/plat/omap_device.h
@@ -44,6 +44,9 @@ extern struct device omap_device_parent;
#define OMAP_DEVICE_STATE_IDLE 2
#define OMAP_DEVICE_STATE_SHUTDOWN 3
+/* omap_device.flags values */
+#define OMAP_DEVICE_SUSPENDED BIT(0)
+
/**
* struct omap_device - omap_device wrapper for platform_devices
* @pdev: platform_device
@@ -73,6 +76,7 @@ struct omap_device {
s8 pm_lat_level;
u8 hwmods_cnt;
u8 _state;
+ u8 flags;
};
/* Device driver interface (call via platform_data fn ptrs) */
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index f7d2ff7..b93cfdc 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -566,11 +566,47 @@ static int _od_runtime_resume(struct device *dev)
}
#endif
+#ifdef CONFIG_SUSPEND
+static int _od_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_device *od = to_omap_device(pdev);
+ int ret;
+
+ ret = pm_generic_suspend_noirq(dev);
+
+ if (!ret && !pm_runtime_status_suspended(dev)) {
+ if (pm_generic_runtime_suspend(dev) == 0) {
+ omap_device_idle(pdev);
+ od->flags |= OMAP_DEVICE_SUSPENDED;
+ }
+ }
+
+ return ret;
+}
+
+static int _od_resume_noirq(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_device *od = to_omap_device(pdev);
+
+ if ((od->flags & OMAP_DEVICE_SUSPENDED) &&
+ !pm_runtime_status_suspended(dev)) {
+ od->flags &= ~OMAP_DEVICE_SUSPENDED;
+ omap_device_enable(pdev);
+ pm_generic_runtime_resume(dev);
+ }
+
+ return pm_generic_resume_noirq(dev);
+}
+#endif
+
static struct dev_pm_domain omap_device_pm_domain = {
.ops = {
SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
_od_runtime_idle)
USE_PLATFORM_PM_SLEEP_OPS
+ SET_SYSTEM_SLEEP_PM_OPS(_od_suspend_noirq, _od_resume_noirq)
}
};
--
1.7.6
^ permalink raw reply related
* [PATCH 1/4] OMAP: PM: omap_device: conditionally use PM domain runtime helpers
From: Kevin Hilman @ 2011-07-11 23:29 UTC (permalink / raw)
To: Rafael J. Wysocki, Paul Walmsley, linux-omap; +Cc: linux-pm, linux-arm-kernel
In-Reply-To: <1310426969-30306-1-git-send-email-khilman@ti.com>
Only build and use the runtime PM helper functions only when runtime
PM is actually enabled.
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
arch/arm/plat-omap/omap_device.c | 7 ++++---
1 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/arch/arm/plat-omap/omap_device.c b/arch/arm/plat-omap/omap_device.c
index d21579b..f7d2ff7 100644
--- a/arch/arm/plat-omap/omap_device.c
+++ b/arch/arm/plat-omap/omap_device.c
@@ -537,6 +537,7 @@ int omap_early_device_register(struct omap_device *od)
return 0;
}
+#ifdef CONFIG_PM_RUNTIME
static int _od_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
@@ -563,12 +564,12 @@ static int _od_runtime_resume(struct device *dev)
return pm_generic_runtime_resume(dev);
}
+#endif
static struct dev_pm_domain omap_device_pm_domain = {
.ops = {
- .runtime_suspend = _od_runtime_suspend,
- .runtime_idle = _od_runtime_idle,
- .runtime_resume = _od_runtime_resume,
+ SET_RUNTIME_PM_OPS(_od_runtime_suspend, _od_runtime_resume,
+ _od_runtime_idle)
USE_PLATFORM_PM_SLEEP_OPS
}
};
--
1.7.6
^ permalink raw reply related
* [PATCH 0/4] OMAP: PM: omap_device: update PM domain support for v3.1
From: Kevin Hilman @ 2011-07-11 23:29 UTC (permalink / raw)
To: Rafael J. Wysocki, Paul Walmsley, linux-omap; +Cc: linux-pm, linux-arm-kernel
For v3.1, the PM core has some changes that impact various assumptions
made (by me) during the design and implementation of the PM domain
support in the omap_device layer.
This series is needed to update our PM domain layer to behave properly
under the new rules of the PM core code which are planned to be merged
for v3.1 (see recent linux-pm discussions for the gory details.)
This series is based on the pm-domains branch of Rafael Wysocki's
suspend-2.6 tree, and also depends on the patch which adds
pm_runtime_status_suspended() helper function[1].
Rafael, because of the dependencies on your pm-domains branch (due to
the pwr_domain -> pm_domain rename), it might be easiest to merge
these via your tree. Let me know if that's OK with you.
I've tested this series with all of the PM core changes planned for
v3.1 (Rafael's linux-next branch[2]) and have done various
suspend/resume testing (including off-mode) with individual devices
disabled via /sys/devices/.../power/control.
Kevin
[1] https://patchwork.kernel.org/patch/966732/
[2] git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6.git
Kevin Hilman (4):
OMAP: PM: omap_device: conditionally use PM domain runtime helpers
OMAP: PM: omap_device: add system PM methods for PM domain handling
OMAP: PM: omap_device: add API to disable idle on suspend
OMAP: PM: disable idle on suspend for GPIO and UART
arch/arm/mach-omap2/Kconfig | 4 +-
arch/arm/mach-omap2/gpio.c | 2 +
arch/arm/mach-omap2/serial.c | 1 +
arch/arm/plat-omap/include/plat/omap_device.h | 9 +++++
arch/arm/plat-omap/omap_device.c | 49 +++++++++++++++++++++++--
5 files changed, 60 insertions(+), 5 deletions(-)
--
1.7.6
^ permalink raw reply
* [PATCH] PM / Runtime: add new helper function: pm_runtime_status_suspended()
From: Kevin Hilman @ 2011-07-11 22:51 UTC (permalink / raw)
To: Rafael J. Wysocki, linux-pm; +Cc: linux-omap
This boolean function simply returns whether or not the runtime status
of the device is 'suspended'. Unlike pm_runtime_suspended(), this
function returns the runtime status whether or not runtime PM for the
device has been disabled or not.
Also add entry to Documentation/power/runtime.txt
Suggested-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Kevin Hilman <khilman@ti.com>
---
Documentation/power/runtime_pm.txt | 3 +++
include/linux/pm_runtime.h | 6 ++++++
2 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/Documentation/power/runtime_pm.txt b/Documentation/power/runtime_pm.txt
index 4b011b1..feca92f 100644
--- a/Documentation/power/runtime_pm.txt
+++ b/Documentation/power/runtime_pm.txt
@@ -400,6 +400,9 @@ drivers/base/power/runtime.c and include/linux/pm_runtime.h:
- return true if the device's runtime PM status is 'suspended' and its
'power.disable_depth' field is equal to zero, or false otherwise
+ bool pm_runtime_status_suspended(struct device *dev);
+ - return true if the device's runtime PM status is 'suspended'
+
void pm_runtime_allow(struct device *dev);
- set the power.runtime_auto flag for the device and decrease its usage
counter (used by the /sys/devices/.../power/control interface to
diff --git a/include/linux/pm_runtime.h b/include/linux/pm_runtime.h
index dfb8539..daac05d 100644
--- a/include/linux/pm_runtime.h
+++ b/include/linux/pm_runtime.h
@@ -82,6 +82,11 @@ static inline bool pm_runtime_suspended(struct device *dev)
&& !dev->power.disable_depth;
}
+static inline bool pm_runtime_status_suspended(struct device *dev)
+{
+ return dev->power.runtime_status == RPM_SUSPENDED;
+}
+
static inline bool pm_runtime_enabled(struct device *dev)
{
return !dev->power.disable_depth;
@@ -130,6 +135,7 @@ static inline void pm_runtime_put_noidle(struct device *dev) {}
static inline bool device_run_wake(struct device *dev) { return false; }
static inline void device_set_run_wake(struct device *dev, bool enable) {}
static inline bool pm_runtime_suspended(struct device *dev) { return false; }
+static inline bool pm_runtime_status_suspended(struct device *dev) { return false; }
static inline bool pm_runtime_enabled(struct device *dev) { return false; }
static inline int pm_generic_runtime_idle(struct device *dev) { return 0; }
--
1.7.6
^ permalink raw reply related
* Re: pm_runtime_suspended() can be false if RPM_SUSPENDED
From: Hilman, Kevin @ 2011-07-11 22:50 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-pm, Linux OMAP
In-Reply-To: <201107120036.49823.rjw@sisk.pl>
On Mon, Jul 11, 2011 at 3:36 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Monday, July 11, 2011, Hilman, Kevin wrote:
>> On Mon, Jul 11, 2011 at 1:22 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > On Monday, July 11, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >>
>> >> [...]
>> >>
>> >> >>
>> >> >> However, based on the pm_runtime_set_active() problem you mentioned
>> >> >> above, I'm not sure this will help either, since what the PM domain's
>> >> >> noirq callback will want to do will be based on the actual device
>> >> >> hardware state, not on the PM core's view of the device state.
>> >> >
>> >> > Yes. For devices whose runtime PM is never enabled, this is quite clear
>> >> > (we must assume they are operational). For devices whose runtime PM is
>> >> > temporarily disabled and the reenabled, it's not that clear, but at
>> >> > least for the system suspend case we may require drivers not to use
>> >> > pm_runtime_set_active/suspended() in their callbacks, so that we may
>> >> > assume that the status hasn't changed between .suspend() and .resume().
>> >> >
>> >> > So, I think your approach (to check power.runtime_status) is correct in this
>> >> > respect.
>> >>
>> >> OK, I'll just directly check power.runtime_status in the noirq methods,
>> >> since at that point I always know that disable_depth > 0.
>> >
>> > That's what I wanted to say. :-) The only problem with that is if
>> > CONFIG_PM_RUNTIME is unset, power.runtime_status is not present, so I think
>> > we'll need a static inline to work around that.
>> >
>>
>> OK, I can prepare a patch for a static inline for
>> pm_runtime_status_suspended(), unless you have one already in
>> progress.
>
> No, I don't, please send me one if you can. :-)
>
OK, coming right up...
^ permalink raw reply
* Re: pm_runtime_suspended() can be false if RPM_SUSPENDED
From: Rafael J. Wysocki @ 2011-07-11 22:36 UTC (permalink / raw)
To: Hilman, Kevin; +Cc: linux-pm, Linux OMAP
In-Reply-To: <CALyTGOANs9ZUKmWH43s7H1Aod-y=jAueo045yzao66ze7Wko5g@mail.gmail.com>
On Monday, July 11, 2011, Hilman, Kevin wrote:
> On Mon, Jul 11, 2011 at 1:22 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Monday, July 11, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >>
> >> [...]
> >>
> >> >>
> >> >> However, based on the pm_runtime_set_active() problem you mentioned
> >> >> above, I'm not sure this will help either, since what the PM domain's
> >> >> noirq callback will want to do will be based on the actual device
> >> >> hardware state, not on the PM core's view of the device state.
> >> >
> >> > Yes. For devices whose runtime PM is never enabled, this is quite clear
> >> > (we must assume they are operational). For devices whose runtime PM is
> >> > temporarily disabled and the reenabled, it's not that clear, but at
> >> > least for the system suspend case we may require drivers not to use
> >> > pm_runtime_set_active/suspended() in their callbacks, so that we may
> >> > assume that the status hasn't changed between .suspend() and .resume().
> >> >
> >> > So, I think your approach (to check power.runtime_status) is correct in this
> >> > respect.
> >>
> >> OK, I'll just directly check power.runtime_status in the noirq methods,
> >> since at that point I always know that disable_depth > 0.
> >
> > That's what I wanted to say. :-) The only problem with that is if
> > CONFIG_PM_RUNTIME is unset, power.runtime_status is not present, so I think
> > we'll need a static inline to work around that.
> >
>
> OK, I can prepare a patch for a static inline for
> pm_runtime_status_suspended(), unless you have one already in
> progress.
No, I don't, please send me one if you can. :-)
Thanks,
Rafael
^ permalink raw reply
* Re: [RFC] Unprepare callback for cpuidle_device
From: asinghal @ 2011-07-11 21:45 UTC (permalink / raw)
To: svaidy; +Cc: linux-pm, asinghal, johlstei
In-Reply-To: <20110711190856.GC25533@dirshya.in.ibm.com>
hello Vaidy,
doesn't tick_nohz_get_sleep_length just return ts->sleep_length
variable value from the tick_sched structure for that cpu ?
The calculation of this sleep_length variable happens in function
tick_nohz_stop_Sched_tick after call to function get_next_timer_interrupt ??
thanks,
amar
> * asinghal@codeaurora.org <asinghal@codeaurora.org> [2011-07-11 10:26:56]:
>
>> hello Deepthi,
>> please see my replies inline:
>>
>>
>> > Hi Amar,
>> >
>> > On Friday 08 July 2011 01:22 AM, asinghal@codeaurora.org wrote:
>> >> Hello Trinabh,
>> >> i cannot use the enter callback due to the following
>> >> reason:
>> >>
>> >> the residency calculation(tick)nohz_get_sleep_length) and the idle
>> state
>> >> selection happens in the menu governor. The enter callback is called
>> >> with
>> >> the selected state.
>> >
>> > One would first execute prepare then call select and enter .
>> > So in the newer proposed code, there is no prepare routine. One
>> would
>> > just execute select() and then enter() (Prepare functionality is
>> part of
>> > the
>> > enter routine itself) Is it not possible to cancel the timer,
>> > calculate the execute nohz_get_sleep in enter routine again,
>> > then select the idle state depending on the time . Automatically
>> promote
>> > or demote
>> > idle state based on the latest value returned in the driver ?
>> >
>> Cancelling the timer and calculating the sleep length again in the enter
>> callback is possible; but what does not make sense is calling the select
>> routine again ; definitely the select routine of the governor needs to
>> be
>> called only once. And actually, the sleep length is calculated in the
>
> Can you promote the idle state within the driver based on the new
> information provided by tick_nohz_get_sleep_length() after canceling
> the timer? This will be a hack... but better than calling select()
> again. You are right, we need to design in such a way that select()
> makes the right choice.
>
>> function tick_nohz_stop_sched_tick.( so shld the prepare callback be
>> moved
>> before that in cpu_idle ??)
>
> The prepare callback is in the right place now... if you cancel the
> timer in prepare, then subsequent select() which calls
> tick_nohz_get_sleep_length() will get the correct idle time. Your
> problem is in restarting the timer in case we abort the idle. The
> current place of prepare is fine and more over we are heading in the
> direction to deprecate the prepare itself.
>
>> I am looking for a nice way to cancel and restart the high resolution
>> timers in the idle code; even if that means rearranging the cpu_idle
>> code.
>
> Slight difference, you can cancel and restart within the driver, the
> problem is that you want the ->select() to be executed _after_
> canceling your hrtimer because it will predict idle much shorter
> otherwise. This is where the cpuidle framework should help.
>
>> Another way i was looking to solve this issue was add deferrable feature
>> to HR timers; if someone has thoughts on that please chime in.
>
> This seems to be a much better idea, where the
> tick_nohz_get_sleep_length() can automatically skip your deferred
> hrtimer with a hope that it will be canceled. We don't have
> a mechanism to actually defer it.
>
> You can try to add deferred flag in hrtimer_mode { } similar to how
> pinned was added. Enhance tick_nohz_get_sleep_length() to skip over
> deferred hrtimer as well.
>
> But the number of users for this feature will be very little to
> justify the complexity.
>
> --Vaidy
>
>
>
^ permalink raw reply
* Re: pm_runtime_suspended() can be false if RPM_SUSPENDED
From: Hilman, Kevin @ 2011-07-11 20:34 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-pm, Linux OMAP
In-Reply-To: <201107112222.18917.rjw@sisk.pl>
On Mon, Jul 11, 2011 at 1:22 PM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Monday, July 11, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>>
>> [...]
>>
>> >>
>> >> However, based on the pm_runtime_set_active() problem you mentioned
>> >> above, I'm not sure this will help either, since what the PM domain's
>> >> noirq callback will want to do will be based on the actual device
>> >> hardware state, not on the PM core's view of the device state.
>> >
>> > Yes. For devices whose runtime PM is never enabled, this is quite clear
>> > (we must assume they are operational). For devices whose runtime PM is
>> > temporarily disabled and the reenabled, it's not that clear, but at
>> > least for the system suspend case we may require drivers not to use
>> > pm_runtime_set_active/suspended() in their callbacks, so that we may
>> > assume that the status hasn't changed between .suspend() and .resume().
>> >
>> > So, I think your approach (to check power.runtime_status) is correct in this
>> > respect.
>>
>> OK, I'll just directly check power.runtime_status in the noirq methods,
>> since at that point I always know that disable_depth > 0.
>
> That's what I wanted to say. :-) The only problem with that is if
> CONFIG_PM_RUNTIME is unset, power.runtime_status is not present, so I think
> we'll need a static inline to work around that.
>
OK, I can prepare a patch for a static inline for
pm_runtime_status_suspended(), unless you have one already in
progress.
Kevin
^ permalink raw reply
* Re: pm_runtime_suspended() can be false if RPM_SUSPENDED
From: Rafael J. Wysocki @ 2011-07-11 20:22 UTC (permalink / raw)
To: Kevin Hilman; +Cc: linux-pm, Linux OMAP
In-Reply-To: <874o2sy3p9.fsf@ti.com>
On Monday, July 11, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>
> [...]
>
> >>
> >> However, based on the pm_runtime_set_active() problem you mentioned
> >> above, I'm not sure this will help either, since what the PM domain's
> >> noirq callback will want to do will be based on the actual device
> >> hardware state, not on the PM core's view of the device state.
> >
> > Yes. For devices whose runtime PM is never enabled, this is quite clear
> > (we must assume they are operational). For devices whose runtime PM is
> > temporarily disabled and the reenabled, it's not that clear, but at
> > least for the system suspend case we may require drivers not to use
> > pm_runtime_set_active/suspended() in their callbacks, so that we may
> > assume that the status hasn't changed between .suspend() and .resume().
> >
> > So, I think your approach (to check power.runtime_status) is correct in this
> > respect.
>
> OK, I'll just directly check power.runtime_status in the noirq methods,
> since at that point I always know that disable_depth > 0.
That's what I wanted to say. :-) The only problem with that is if
CONFIG_PM_RUNTIME is unset, power.runtime_status is not present, so I think
we'll need a static inline to work around that.
Thanks,
Rafael
^ permalink raw reply
* Re: pm_runtime_suspended() can be false if RPM_SUSPENDED
From: Kevin Hilman @ 2011-07-11 20:14 UTC (permalink / raw)
To: Rafael J. Wysocki; +Cc: linux-pm, Linux OMAP
In-Reply-To: <201107112202.48280.rjw@sisk.pl>
"Rafael J. Wysocki" <rjw@sisk.pl> writes:
[...]
>>
>> However, based on the pm_runtime_set_active() problem you mentioned
>> above, I'm not sure this will help either, since what the PM domain's
>> noirq callback will want to do will be based on the actual device
>> hardware state, not on the PM core's view of the device state.
>
> Yes. For devices whose runtime PM is never enabled, this is quite clear
> (we must assume they are operational). For devices whose runtime PM is
> temporarily disabled and the reenabled, it's not that clear, but at
> least for the system suspend case we may require drivers not to use
> pm_runtime_set_active/suspended() in their callbacks, so that we may
> assume that the status hasn't changed between .suspend() and .resume().
>
> So, I think your approach (to check power.runtime_status) is correct in this
> respect.
OK, I'll just directly check power.runtime_status in the noirq methods,
since at that point I always know that disable_depth > 0.
Kevin
^ permalink raw reply
* Re: pm_runtime_suspended() can be false if RPM_SUSPENDED
From: Rafael J. Wysocki @ 2011-07-11 20:02 UTC (permalink / raw)
To: Kevin Hilman; +Cc: linux-pm, Linux OMAP
In-Reply-To: <87fwmcy624.fsf@ti.com>
On Monday, July 11, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>
> > Hi,
> >
> > On Saturday, July 09, 2011, Kevin Hilman wrote:
> >> Hi Rafael,
> >>
> >> Just curious why pm_runtime_suspended() requires the device to be
> >> enabled for it to return true:
> >>
> >> static inline bool pm_runtime_suspended(struct device *dev)
> >> {
> >> return dev->power.runtime_status == RPM_SUSPENDED
> >> && !dev->power.disable_depth;
> >> }
> >>
> >> I must be misunderstanding something, but I would consider a device that
> >> has been runtime suspended before runtime PM was disabled to still be
> >> runtime suspended.
> >
> > The problem is that while the runtime PM of the device is disabled
> > (ie. dev->power.disable_depth > 0), its status may be switched from
> > RPM_SUSPENDED to RPM_ACTIVE on the fly, using pm_runtime_set_active()
> > (and the other way around) and it doesn't reflect the real status in
> > principle.
>
> OK, I guess this is the root of the confusion. Namely, that
> dev->power.runtime_status doesn't necessarily reflect the actual device
> state.
>
> > So it was a tough choice what to do about that and I decided
> > to go with returning false (in many cases runtime PM disabled means the
> > device is always operational).
>
> And with your recent changes (pm_runtime_disable() in suspend path), it
> also be very common that
>
> >> I just noticed this when testing with your pm-domains branch. when I
> >> noticed that an 'if (pm_runtime_suspended(dev))' check in my PM domain's
> >> ->suspend_noirq() was always failing since it's after the PM core calls
> >> pm_runtime_disable(). I had to change my PM domain code to only check
> >> dev->power.runtime_status for it to work.
> >
> > Well, at this point I'm kind of cautious about changing pm_runtime_suspended(),
> > so perhaps we need a separate callback returnig true in the status is
> > RPM_SUSPENDED regardless of the value of power.disable_depth, like
> > pm_runtime_status_suspended() or something similar.
>
> OK, but documentation would probably be needed to describe why you might
> use one vs. the other.
>
> However, based on the pm_runtime_set_active() problem you mentioned
> above, I'm not sure this will help either, since what the PM domain's
> noirq callback will want to do will be based on the actual device
> hardware state, not on the PM core's view of the device state.
Yes. For devices whose runtime PM is never enabled, this is quite clear
(we must assume they are operational). For devices whose runtime PM is
temporarily disabled and the reenabled, it's not that clear, but at
least for the system suspend case we may require drivers not to use
pm_runtime_set_active/suspended() in their callbacks, so that we may
assume that the status hasn't changed between .suspend() and .resume().
So, I think your approach (to check power.runtime_status) is correct in this
respect.
Thanks,
Rafael
^ permalink raw reply
* [GIT PULL] Power management regression fix for 3.0
From: Rafael J. Wysocki @ 2011-07-11 19:41 UTC (permalink / raw)
To: Linus Torvalds; +Cc: Linux PM mailing list, LKML, Colin Cross
Hi Linus,
Please pull a power management regression fix for 3.0 from:
git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6.git pm-fixes
It fixes a regression introduced by the recent removal of sysdev_suspend().
drivers/base/syscore.c | 8 ++++++++
1 files changed, 8 insertions(+), 0 deletions(-)
---------------
Colin Cross (1):
PM: Reintroduce dropped call to check_wakeup_irqs
^ permalink raw reply
* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
From: Rafael J. Wysocki @ 2011-07-11 19:39 UTC (permalink / raw)
To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list
In-Reply-To: <87oc10zv4g.fsf@ti.com>
On Monday, July 11, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>
> [...]
>
> >
> > There's one more case to consider, namely devices that are runtime
> > suspended, set up to wake up the system from sleep states
> > (ie. device_may_wakeup(dev) returns "true") and such that
> > genpd->active_wakeup(dev) returns "true" for them, because they need
> > to be resumed at this point too (arguably, it makes a little sense to
> > runtime suspend such devices, but that's possible in principle).
> >
> > So, IMO, the patch should look like this:
> >
> > ---
> > drivers/base/power/domain.c | 19 +++++++++++++++++++
> > 1 file changed, 19 insertions(+)
> >
> >> Index: linux-2.6/drivers/base/power/domain.c
> > ===================================================================
> > --- linux-2.6.orig/drivers/base/power/domain.c
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
> > }
> >
> > /**
> > + * resume_needed - Check whether to resume a device before system suspend.
> > + * @dev: Device to handle.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> > +{
> > + bool active_wakeup;
> > +
> > + if (!device_can_wakeup(dev))
> > + return false;
> > +
> > + active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> > + return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
>
> This also returns true and causes a resume if active_wakeup = false and
> device_may_wakeup() = false. That doesn't seem right.
This is on purpose. :-) If active_wakeup is false, the device may signal
remote wakeup while suspended. So, if active_wakeup is false and the device
is suspended, we have to assume that the device has been set up to signal
remote wakeup for runtime PM (if it is not suspended, attempting to resume it
will not have any effect). Now, if device_may_wakeup() returns false in
addition to that, we may need to change the device's wakeup settings, so the
driver's callbacks should be invoked during suspend, so we're resuming the
device (we can't just leave it suspended and then invoke the driver's callbacks
in the hope they'll do the right thing).
I don't really think we can do anything else without using new device flags.
> > +}
> > +
> > +/**
> > * pm_genpd_prepare - Start power transition of a device in a PM domain.
> > * @dev: Device to start the transition of.
> > *
> > @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
> > return -EBUSY;
> > }
> >
> > + if (resume_needed(dev, genpd))
> > + pm_runtime_resume(dev);
> > +
> > genpd_acquire_lock(genpd);
> >
> > if (genpd->prepared_count++ == 0)
>
> IIUC, if a device is runtime suspended when a system suspend happens,
> the device will be runtime resumed, but never re-suspended.
It will be resuspended by the pm_runtime_idle() in pm_genpd_complete()
(added by one of the new patches I've been posting for the last few days).
> Should resumes by the PM core be done with a get (and a corresponding
> put in .complete())?
Not necessarily. :-)
Thanks,
Rafael
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox