1
0
Fork 0
alistair23-linux/arch/arm/mach-omap1/pm.c

695 lines
19 KiB
C
Raw Normal View History

/*
* linux/arch/arm/mach-omap1/pm.c
*
* OMAP Power Management Routines
*
* Original code for the SA11x0:
* Copyright (c) 2001 Cliff Brake <cbrake@accelent.com>
*
* Modified for the PXA250 by Nicolas Pitre:
* Copyright (c) 2002 Monta Vista Software, Inc.
*
* Modified for the OMAP1510 by David Singleton:
* Copyright (c) 2002 Monta Vista Software, Inc.
*
* Cleanup 2004 for OMAP1510/1610 by Dirk Behme <dirk.behme@de.bosch.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* 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.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/suspend.h>
#include <linux/sched.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/atomic.h>
#include <linux/cpu.h>
#include <asm/fncpy.h>
#include <asm/system_misc.h>
#include <asm/irq.h>
#include <asm/mach/time.h>
#include <asm/mach/irq.h>
#include <mach/tc.h>
#include <mach/mux.h>
ARM: OMAP: Move plat-omap/dma-omap.h to include/linux/omap-dma.h Based on earlier discussions[1] we attempted to find a suitable location for the omap DMA header in commit 2b6c4e73 (ARM: OMAP: DMA: Move plat/dma.h to plat-omap/dma-omap.h) until the conversion to dmaengine is complete. Unfortunately that was before I was able to try to test compile of the ARM multiplatform builds for omap2+, and the end result was not very good. So I'm creating yet another all over the place patch to cut the last dependency for building omap2+ for ARM multiplatform. After this, we have finally removed the driver dependencies to the arch/arm code, except for few drivers that are being worked on. The other option was to make the <plat-omap/dma-omap.h> path to work, but we'd have to add some new header directory to for multiplatform builds. Or we would have to manually include arch/arm/plat-omap/include again from arch/arm/Makefile for omap2+. Neither of these alternatives sound appealing as they will likely lead addition of various other headers exposed to the drivers, which we want to avoid for the multiplatform kernels. Since we already have a minimal include/linux/omap-dma.h, let's just use that instead and add a note to it to not use the custom omap DMA functions any longer where possible. Note that converting omap DMA to dmaengine depends on dmaengine supporting automatically incrementing the FIFO address at the device end, and converting all the remaining legacy drivers. So it's going to be few more merge windows. [1] https://patchwork.kernel.org/patch/1519591/# cc: Russell King <linux@arm.linux.org.uk> cc: Kevin Hilman <khilman@ti.com> cc: "Benoît Cousson" <b-cousson@ti.com> cc: Herbert Xu <herbert@gondor.apana.org.au> cc: "David S. Miller" <davem@davemloft.net> cc: Vinod Koul <vinod.koul@intel.com> cc: Dan Williams <djbw@fb.com> cc: Mauro Carvalho Chehab <mchehab@infradead.org> cc: Laurent Pinchart <laurent.pinchart@ideasonboard.com> cc: Guennadi Liakhovetski <g.liakhovetski@gmx.de> cc: David Woodhouse <dwmw2@infradead.org> cc: Kyungmin Park <kyungmin.park@samsung.com> cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org> cc: Tomi Valkeinen <tomi.valkeinen@ti.com> cc: Florian Tobias Schandinat <FlorianSchandinat@gmx.de> cc: Hans Verkuil <hans.verkuil@cisco.com> cc: Vaibhav Hiremath <hvaibhav@ti.com> cc: Lokesh Vutla <lokeshvutla@ti.com> cc: Rusty Russell <rusty@rustcorp.com.au> cc: Artem Bityutskiy <artem.bityutskiy@linux.intel.com> cc: Afzal Mohammed <afzal@ti.com> cc: linux-crypto@vger.kernel.org cc: linux-media@vger.kernel.org cc: linux-mtd@lists.infradead.org cc: linux-usb@vger.kernel.org cc: linux-fbdev@vger.kernel.org Acked-by: Felipe Balbi <balbi@ti.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2012-11-30 09:41:50 -07:00
#include <linux/omap-dma.h>
#include <clocksource/timer-ti-dm.h>
#include <mach/irqs.h>
#include "iomap.h"
#include "clock.h"
#include "pm.h"
#include "soc.h"
#include "sram.h"
static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE];
static unsigned short dsp_sleep_save[DSP_SLEEP_SAVE_SIZE];
static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE];
static unsigned int mpui7xx_sleep_save[MPUI7XX_SLEEP_SAVE_SIZE];
static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE];
static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE];
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
static unsigned short enable_dyn_sleep;
static ssize_t idle_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%hu\n", enable_dyn_sleep);
}
static ssize_t idle_store(struct kobject *kobj, struct kobj_attribute *attr,
const char * buf, size_t n)
{
unsigned short value;
if (sscanf(buf, "%hu", &value) != 1 ||
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
(value != 0 && value != 1) ||
(value != 0 && !IS_ENABLED(CONFIG_OMAP_32K_TIMER))) {
pr_err("idle_sleep_store: Invalid value\n");
return -EINVAL;
}
enable_dyn_sleep = value;
return n;
}
static struct kobj_attribute sleep_while_idle_attr =
__ATTR(sleep_while_idle, 0644, idle_show, idle_store);
static void (*omap_sram_suspend)(unsigned long r0, unsigned long r1) = NULL;
/*
* Let's power down on idle, but only if we are really
* idle, because once we start down the path of
* going idle we continue to do idle even if we get
* a clock tick interrupt . .
*/
void omap1_pm_idle(void)
{
extern __u32 arm_idlect1_mask;
__u32 use_idlect1 = arm_idlect1_mask;
local_fiq_disable();
#if defined(CONFIG_OMAP_MPU_TIMER) && !defined(CONFIG_OMAP_DM_TIMER)
use_idlect1 = use_idlect1 & ~(1 << 9);
#endif
#ifdef CONFIG_OMAP_DM_TIMER
use_idlect1 = omap_dm_timer_modify_idlect_mask(use_idlect1);
#endif
if (omap_dma_running())
use_idlect1 &= ~(1 << 6);
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
/*
* We should be able to remove the do_sleep variable and multiple
* tests above as soon as drivers, timer and DMA code have been fixed.
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
* Even the sleep block count should become obsolete.
*/
if ((use_idlect1 != ~0) || !enable_dyn_sleep) {
__u32 saved_idlect1 = omap_readl(ARM_IDLECT1);
if (cpu_is_omap15xx())
use_idlect1 &= OMAP1510_BIG_SLEEP_REQUEST;
else
use_idlect1 &= OMAP1610_IDLECT1_SLEEP_VAL;
omap_writel(use_idlect1, ARM_IDLECT1);
__asm__ volatile ("mcr p15, 0, r0, c7, c0, 4");
omap_writel(saved_idlect1, ARM_IDLECT1);
local_fiq_enable();
return;
}
omap_sram_suspend(omap_readl(ARM_IDLECT1),
omap_readl(ARM_IDLECT2));
local_fiq_enable();
}
/*
* Configuration of the wakeup event is board specific. For the
* moment we put it into this helper function. Later it may move
* to board specific files.
*/
static void omap_pm_wakeup_setup(void)
{
u32 level1_wake = 0;
u32 level2_wake = OMAP_IRQ_BIT(INT_UART2);
/*
* Turn off all interrupts except GPIO bank 1, L1-2nd level cascade,
* and the L2 wakeup interrupts: keypad and UART2. Note that the
* drivers must still separately call omap_set_gpio_wakeup() to
* wake up to a GPIO interrupt.
*/
if (cpu_is_omap7xx())
level1_wake = OMAP_IRQ_BIT(INT_7XX_GPIO_BANK1) |
OMAP_IRQ_BIT(INT_7XX_IH2_IRQ);
else if (cpu_is_omap15xx())
level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) |
OMAP_IRQ_BIT(INT_1510_IH2_IRQ);
else if (cpu_is_omap16xx())
level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) |
OMAP_IRQ_BIT(INT_1610_IH2_IRQ);
omap_writel(~level1_wake, OMAP_IH1_MIR);
if (cpu_is_omap7xx()) {
omap_writel(~level2_wake, OMAP_IH2_0_MIR);
omap_writel(~(OMAP_IRQ_BIT(INT_7XX_WAKE_UP_REQ) |
OMAP_IRQ_BIT(INT_7XX_MPUIO_KEYPAD)),
OMAP_IH2_1_MIR);
} else if (cpu_is_omap15xx()) {
level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD);
omap_writel(~level2_wake, OMAP_IH2_MIR);
} else if (cpu_is_omap16xx()) {
level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD);
omap_writel(~level2_wake, OMAP_IH2_0_MIR);
/* INT_1610_WAKE_UP_REQ is needed for GPIO wakeup... */
omap_writel(~OMAP_IRQ_BIT(INT_1610_WAKE_UP_REQ),
OMAP_IH2_1_MIR);
omap_writel(~0x0, OMAP_IH2_2_MIR);
omap_writel(~0x0, OMAP_IH2_3_MIR);
}
/* New IRQ agreement, recalculate in cascade order */
omap_writel(1, OMAP_IH2_CONTROL);
omap_writel(1, OMAP_IH1_CONTROL);
}
#define EN_DSPCK 13 /* ARM_CKCTL */
#define EN_APICK 6 /* ARM_IDLECT2 */
#define DSP_EN 1 /* ARM_RSTCT1 */
void omap1_pm_suspend(void)
{
unsigned long arg0 = 0, arg1 = 0;
printk(KERN_INFO "PM: OMAP%x is trying to enter deep sleep...\n",
omap_rev());
omap_serial_wake_trigger(1);
if (!cpu_is_omap15xx())
omap_writew(0xffff, ULPD_SOFT_DISABLE_REQ_REG);
/*
* Step 1: turn off interrupts (FIXME: NOTE: already disabled)
*/
local_irq_disable();
local_fiq_disable();
/*
* Step 2: save registers
*
* The omap is a strange/beautiful device. The caches, memory
* and register state are preserved across power saves.
* We have to save and restore very little register state to
* idle the omap.
*
* Save interrupt, MPUI, ARM and UPLD control registers.
*/
if (cpu_is_omap7xx()) {
MPUI7XX_SAVE(OMAP_IH1_MIR);
MPUI7XX_SAVE(OMAP_IH2_0_MIR);
MPUI7XX_SAVE(OMAP_IH2_1_MIR);
MPUI7XX_SAVE(MPUI_CTRL);
MPUI7XX_SAVE(MPUI_DSP_BOOT_CONFIG);
MPUI7XX_SAVE(MPUI_DSP_API_CONFIG);
MPUI7XX_SAVE(EMIFS_CONFIG);
MPUI7XX_SAVE(EMIFF_SDRAM_CONFIG);
} else if (cpu_is_omap15xx()) {
MPUI1510_SAVE(OMAP_IH1_MIR);
MPUI1510_SAVE(OMAP_IH2_MIR);
MPUI1510_SAVE(MPUI_CTRL);
MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG);
MPUI1510_SAVE(MPUI_DSP_API_CONFIG);
MPUI1510_SAVE(EMIFS_CONFIG);
MPUI1510_SAVE(EMIFF_SDRAM_CONFIG);
} else if (cpu_is_omap16xx()) {
MPUI1610_SAVE(OMAP_IH1_MIR);
MPUI1610_SAVE(OMAP_IH2_0_MIR);
MPUI1610_SAVE(OMAP_IH2_1_MIR);
MPUI1610_SAVE(OMAP_IH2_2_MIR);
MPUI1610_SAVE(OMAP_IH2_3_MIR);
MPUI1610_SAVE(MPUI_CTRL);
MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG);
MPUI1610_SAVE(MPUI_DSP_API_CONFIG);
MPUI1610_SAVE(EMIFS_CONFIG);
MPUI1610_SAVE(EMIFF_SDRAM_CONFIG);
}
ARM_SAVE(ARM_CKCTL);
ARM_SAVE(ARM_IDLECT1);
ARM_SAVE(ARM_IDLECT2);
if (!(cpu_is_omap15xx()))
ARM_SAVE(ARM_IDLECT3);
ARM_SAVE(ARM_EWUPCT);
ARM_SAVE(ARM_RSTCT1);
ARM_SAVE(ARM_RSTCT2);
ARM_SAVE(ARM_SYSST);
ULPD_SAVE(ULPD_CLOCK_CTRL);
ULPD_SAVE(ULPD_STATUS_REQ);
/* (Step 3 removed - we now allow deep sleep by default) */
/*
* Step 4: OMAP DSP Shutdown
*/
/* stop DSP */
omap_writew(omap_readw(ARM_RSTCT1) & ~(1 << DSP_EN), ARM_RSTCT1);
/* shut down dsp_ck */
if (!cpu_is_omap7xx())
omap_writew(omap_readw(ARM_CKCTL) & ~(1 << EN_DSPCK), ARM_CKCTL);
/* temporarily enabling api_ck to access DSP registers */
omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2);
/* save DSP registers */
DSP_SAVE(DSP_IDLECT2);
/* Stop all DSP domain clocks */
__raw_writew(0, DSP_IDLECT2);
/*
* Step 5: Wakeup Event Setup
*/
omap_pm_wakeup_setup();
/*
* Step 6: ARM and Traffic controller shutdown
*/
/* disable ARM watchdog */
omap_writel(0x00F5, OMAP_WDT_TIMER_MODE);
omap_writel(0x00A0, OMAP_WDT_TIMER_MODE);
/*
* Step 6b: ARM and Traffic controller shutdown
*
* Step 6 continues here. Prepare jump to power management
* assembly code in internal SRAM.
*
* Since the omap_cpu_suspend routine has been copied to
* SRAM, we'll do an indirect procedure call to it and pass the
* contents of arm_idlect1 and arm_idlect2 so it can restore
* them when it wakes up and it will return.
*/
arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1];
arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2];
/*
* Step 6c: ARM and Traffic controller shutdown
*
* Jump to assembly code. The processor will stay there
* until wake up.
*/
omap_sram_suspend(arg0, arg1);
/*
* If we are here, processor is woken up!
*/
/*
* Restore DSP clocks
*/
/* again temporarily enabling api_ck to access DSP registers */
omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2);
/* Restore DSP domain clocks */
DSP_RESTORE(DSP_IDLECT2);
/*
* Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did
*/
if (!(cpu_is_omap15xx()))
ARM_RESTORE(ARM_IDLECT3);
ARM_RESTORE(ARM_CKCTL);
ARM_RESTORE(ARM_EWUPCT);
ARM_RESTORE(ARM_RSTCT1);
ARM_RESTORE(ARM_RSTCT2);
ARM_RESTORE(ARM_SYSST);
ULPD_RESTORE(ULPD_CLOCK_CTRL);
ULPD_RESTORE(ULPD_STATUS_REQ);
if (cpu_is_omap7xx()) {
MPUI7XX_RESTORE(EMIFS_CONFIG);
MPUI7XX_RESTORE(EMIFF_SDRAM_CONFIG);
MPUI7XX_RESTORE(OMAP_IH1_MIR);
MPUI7XX_RESTORE(OMAP_IH2_0_MIR);
MPUI7XX_RESTORE(OMAP_IH2_1_MIR);
} else if (cpu_is_omap15xx()) {
MPUI1510_RESTORE(MPUI_CTRL);
MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG);
MPUI1510_RESTORE(MPUI_DSP_API_CONFIG);
MPUI1510_RESTORE(EMIFS_CONFIG);
MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG);
MPUI1510_RESTORE(OMAP_IH1_MIR);
MPUI1510_RESTORE(OMAP_IH2_MIR);
} else if (cpu_is_omap16xx()) {
MPUI1610_RESTORE(MPUI_CTRL);
MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG);
MPUI1610_RESTORE(MPUI_DSP_API_CONFIG);
MPUI1610_RESTORE(EMIFS_CONFIG);
MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG);
MPUI1610_RESTORE(OMAP_IH1_MIR);
MPUI1610_RESTORE(OMAP_IH2_0_MIR);
MPUI1610_RESTORE(OMAP_IH2_1_MIR);
MPUI1610_RESTORE(OMAP_IH2_2_MIR);
MPUI1610_RESTORE(OMAP_IH2_3_MIR);
}
if (!cpu_is_omap15xx())
omap_writew(0, ULPD_SOFT_DISABLE_REQ_REG);
/*
* Re-enable interrupts
*/
local_irq_enable();
local_fiq_enable();
omap_serial_wake_trigger(0);
printk(KERN_INFO "PM: OMAP%x is re-starting from deep sleep...\n",
omap_rev());
}
#ifdef CONFIG_DEBUG_FS
/*
* Read system PM registers for debugging
*/
static int omap_pm_debug_show(struct seq_file *m, void *v)
{
ARM_SAVE(ARM_CKCTL);
ARM_SAVE(ARM_IDLECT1);
ARM_SAVE(ARM_IDLECT2);
if (!(cpu_is_omap15xx()))
ARM_SAVE(ARM_IDLECT3);
ARM_SAVE(ARM_EWUPCT);
ARM_SAVE(ARM_RSTCT1);
ARM_SAVE(ARM_RSTCT2);
ARM_SAVE(ARM_SYSST);
ULPD_SAVE(ULPD_IT_STATUS);
ULPD_SAVE(ULPD_CLOCK_CTRL);
ULPD_SAVE(ULPD_SOFT_REQ);
ULPD_SAVE(ULPD_STATUS_REQ);
ULPD_SAVE(ULPD_DPLL_CTRL);
ULPD_SAVE(ULPD_POWER_CTRL);
if (cpu_is_omap7xx()) {
MPUI7XX_SAVE(MPUI_CTRL);
MPUI7XX_SAVE(MPUI_DSP_STATUS);
MPUI7XX_SAVE(MPUI_DSP_BOOT_CONFIG);
MPUI7XX_SAVE(MPUI_DSP_API_CONFIG);
MPUI7XX_SAVE(EMIFF_SDRAM_CONFIG);
MPUI7XX_SAVE(EMIFS_CONFIG);
} else if (cpu_is_omap15xx()) {
MPUI1510_SAVE(MPUI_CTRL);
MPUI1510_SAVE(MPUI_DSP_STATUS);
MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG);
MPUI1510_SAVE(MPUI_DSP_API_CONFIG);
MPUI1510_SAVE(EMIFF_SDRAM_CONFIG);
MPUI1510_SAVE(EMIFS_CONFIG);
} else if (cpu_is_omap16xx()) {
MPUI1610_SAVE(MPUI_CTRL);
MPUI1610_SAVE(MPUI_DSP_STATUS);
MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG);
MPUI1610_SAVE(MPUI_DSP_API_CONFIG);
MPUI1610_SAVE(EMIFF_SDRAM_CONFIG);
MPUI1610_SAVE(EMIFS_CONFIG);
}
seq_printf(m,
"ARM_CKCTL_REG: 0x%-8x \n"
"ARM_IDLECT1_REG: 0x%-8x \n"
"ARM_IDLECT2_REG: 0x%-8x \n"
"ARM_IDLECT3_REG: 0x%-8x \n"
"ARM_EWUPCT_REG: 0x%-8x \n"
"ARM_RSTCT1_REG: 0x%-8x \n"
"ARM_RSTCT2_REG: 0x%-8x \n"
"ARM_SYSST_REG: 0x%-8x \n"
"ULPD_IT_STATUS_REG: 0x%-4x \n"
"ULPD_CLOCK_CTRL_REG: 0x%-4x \n"
"ULPD_SOFT_REQ_REG: 0x%-4x \n"
"ULPD_DPLL_CTRL_REG: 0x%-4x \n"
"ULPD_STATUS_REQ_REG: 0x%-4x \n"
"ULPD_POWER_CTRL_REG: 0x%-4x \n",
ARM_SHOW(ARM_CKCTL),
ARM_SHOW(ARM_IDLECT1),
ARM_SHOW(ARM_IDLECT2),
ARM_SHOW(ARM_IDLECT3),
ARM_SHOW(ARM_EWUPCT),
ARM_SHOW(ARM_RSTCT1),
ARM_SHOW(ARM_RSTCT2),
ARM_SHOW(ARM_SYSST),
ULPD_SHOW(ULPD_IT_STATUS),
ULPD_SHOW(ULPD_CLOCK_CTRL),
ULPD_SHOW(ULPD_SOFT_REQ),
ULPD_SHOW(ULPD_DPLL_CTRL),
ULPD_SHOW(ULPD_STATUS_REQ),
ULPD_SHOW(ULPD_POWER_CTRL));
if (cpu_is_omap7xx()) {
seq_printf(m,
"MPUI7XX_CTRL_REG 0x%-8x \n"
"MPUI7XX_DSP_STATUS_REG: 0x%-8x \n"
"MPUI7XX_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
"MPUI7XX_DSP_API_CONFIG_REG: 0x%-8x \n"
"MPUI7XX_SDRAM_CONFIG_REG: 0x%-8x \n"
"MPUI7XX_EMIFS_CONFIG_REG: 0x%-8x \n",
MPUI7XX_SHOW(MPUI_CTRL),
MPUI7XX_SHOW(MPUI_DSP_STATUS),
MPUI7XX_SHOW(MPUI_DSP_BOOT_CONFIG),
MPUI7XX_SHOW(MPUI_DSP_API_CONFIG),
MPUI7XX_SHOW(EMIFF_SDRAM_CONFIG),
MPUI7XX_SHOW(EMIFS_CONFIG));
} else if (cpu_is_omap15xx()) {
seq_printf(m,
"MPUI1510_CTRL_REG 0x%-8x \n"
"MPUI1510_DSP_STATUS_REG: 0x%-8x \n"
"MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
"MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n"
"MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n"
"MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n",
MPUI1510_SHOW(MPUI_CTRL),
MPUI1510_SHOW(MPUI_DSP_STATUS),
MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG),
MPUI1510_SHOW(MPUI_DSP_API_CONFIG),
MPUI1510_SHOW(EMIFF_SDRAM_CONFIG),
MPUI1510_SHOW(EMIFS_CONFIG));
} else if (cpu_is_omap16xx()) {
seq_printf(m,
"MPUI1610_CTRL_REG 0x%-8x \n"
"MPUI1610_DSP_STATUS_REG: 0x%-8x \n"
"MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n"
"MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n"
"MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n"
"MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n",
MPUI1610_SHOW(MPUI_CTRL),
MPUI1610_SHOW(MPUI_DSP_STATUS),
MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG),
MPUI1610_SHOW(MPUI_DSP_API_CONFIG),
MPUI1610_SHOW(EMIFF_SDRAM_CONFIG),
MPUI1610_SHOW(EMIFS_CONFIG));
}
return 0;
}
DEFINE_SHOW_ATTRIBUTE(omap_pm_debug);
static void omap_pm_init_debugfs(void)
{
struct dentry *d;
d = debugfs_create_dir("pm_debug", NULL);
debugfs_create_file("omap_pm", S_IWUSR | S_IRUGO, d, NULL,
&omap_pm_debug_fops);
}
#endif /* CONFIG_DEBUG_FS */
/*
* omap_pm_prepare - Do preliminary suspend work.
*
*/
static int omap_pm_prepare(void)
{
/* We cannot sleep in idle until we have resumed */
cpu_idle_poll_ctrl(true);
return 0;
}
/*
* omap_pm_enter - Actually enter a sleep state.
* @state: State we're entering.
*
*/
static int omap_pm_enter(suspend_state_t state)
{
switch (state)
{
case PM_SUSPEND_MEM:
omap1_pm_suspend();
break;
default:
return -EINVAL;
}
return 0;
}
/**
* omap_pm_finish - Finish up suspend sequence.
*
* This is called after we wake back up (or if entering the sleep state
* failed).
*/
static void omap_pm_finish(void)
{
cpu_idle_poll_ctrl(false);
}
static irqreturn_t omap_wakeup_interrupt(int irq, void *dev)
{
return IRQ_HANDLED;
}
static struct irqaction omap_wakeup_irq = {
.name = "peripheral wakeup",
.handler = omap_wakeup_interrupt
};
static const struct platform_suspend_ops omap_pm_ops = {
.prepare = omap_pm_prepare,
.enter = omap_pm_enter,
.finish = omap_pm_finish,
.valid = suspend_valid_only_mem,
};
static int __init omap_pm_init(void)
{
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
int error = 0;
if (!cpu_class_is_omap1())
return -ENODEV;
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
pr_info("Power Management for TI OMAP.\n");
if (!IS_ENABLED(CONFIG_OMAP_32K_TIMER))
pr_info("OMAP1 PM: sleep states in idle disabled due to no 32KiHz timer\n");
if (!IS_ENABLED(CONFIG_OMAP_DM_TIMER))
pr_info("OMAP1 PM: sleep states in idle disabled due to no DMTIMER support\n");
if (IS_ENABLED(CONFIG_OMAP_32K_TIMER) &&
IS_ENABLED(CONFIG_OMAP_DM_TIMER)) {
/* OMAP16xx only */
pr_info("OMAP1 PM: sleep states in idle enabled\n");
enable_dyn_sleep = 1;
}
/*
* We copy the assembler sleep/wakeup routines to SRAM.
* These routines need to be in SRAM as that's the only
* memory the MPU can see when it wakes up.
*/
if (cpu_is_omap7xx()) {
omap_sram_suspend = omap_sram_push(omap7xx_cpu_suspend,
omap7xx_cpu_suspend_sz);
} else if (cpu_is_omap15xx()) {
omap_sram_suspend = omap_sram_push(omap1510_cpu_suspend,
omap1510_cpu_suspend_sz);
} else if (cpu_is_omap16xx()) {
omap_sram_suspend = omap_sram_push(omap1610_cpu_suspend,
omap1610_cpu_suspend_sz);
}
if (omap_sram_suspend == NULL) {
printk(KERN_ERR "PM not initialized: Missing SRAM support\n");
return -ENODEV;
}
arm_pm_idle = omap1_pm_idle;
if (cpu_is_omap7xx())
setup_irq(INT_7XX_WAKE_UP_REQ, &omap_wakeup_irq);
else if (cpu_is_omap16xx())
setup_irq(INT_1610_WAKE_UP_REQ, &omap_wakeup_irq);
/* Program new power ramp-up time
* (0 for most boards since we don't lower voltage when in deep sleep)
*/
omap_writew(ULPD_SETUP_ANALOG_CELL_3_VAL, ULPD_SETUP_ANALOG_CELL_3);
/* Setup ULPD POWER_CTRL_REG - enter deep sleep whenever possible */
omap_writew(ULPD_POWER_CTRL_REG_VAL, ULPD_POWER_CTRL);
/* Configure IDLECT3 */
if (cpu_is_omap7xx())
omap_writel(OMAP7XX_IDLECT3_VAL, OMAP7XX_IDLECT3);
else if (cpu_is_omap16xx())
omap_writel(OMAP1610_IDLECT3_VAL, OMAP1610_IDLECT3);
suspend_set_ops(&omap_pm_ops);
#ifdef CONFIG_DEBUG_FS
omap_pm_init_debugfs();
#endif
error = sysfs_create_file(power_kobj, &sleep_while_idle_attr.attr);
if (error)
printk(KERN_ERR "sysfs_create_file failed: %d\n", error);
if (cpu_is_omap16xx()) {
/* configure LOW_PWR pin */
omap_cfg_reg(T20_1610_LOW_PWR);
}
ARM: OMAP1: PM: fix some build warnings on 1510-only Kconfigs Building an OMAP1510-only Kconfig generates the following warnings: arch/arm/mach-omap1/pm.c: In function ¡omap1_pm_idle¢: arch/arm/mach-omap1/pm.c:123:2: warning: #warning Enable 32kHz OS timer in order to allow sleep states in idle [-Wcpp] #warning Enable 32kHz OS timer in order to allow sleep states in idle ^ arch/arm/mach-omap1/pm.c: At top level: arch/arm/mach-omap1/pm.c:76:23: warning: ¡enable_dyn_sleep¢ defined but not used [-Wunused-variable] static unsigned short enable_dyn_sleep = 0; ^ These are not so easy to fix in an obviously correct fashion, since I don't have these devices up and running in my testbed. So, use arch/arm/plat-omap/Kconfig and the existing pm.c source as a guide, and posit that deep power saving states are only supported on OMAP16xx chips with kernels built with both CONFIG_OMAP_DM_TIMER=y and CONFIG_OMAP_32K_TIMER=y. While here, clean up a few printk()s and unnecessary #ifdefs. This second version of the patch incorporates several suggestions from Jon Hunter <jgchunter@gmail.com>. Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jon Hunter <jonathanh@nvidia.com> Cc: Aaro Koskinen <aaro.koskinen@iki.fi> Cc: Tuukka Tikkanen <tuukka.tikkanen@linaro.org> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: linux-omap@vger.kernel.org Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Acked-by: Jon Hunter <jgchunter@gmail.com> Signed-off-by: Tony Lindgren <tony@atomide.com>
2015-02-06 15:56:07 -07:00
return error;
}
__initcall(omap_pm_init);