pm.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 697 行 · 第 1/2 页
C
697 行
/* * linux/arch/arm/mach-omap/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/config.h>#include <linux/init.h>#include <linux/pm.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/sysctl.h>#include <linux/errno.h>#include <linux/serial_reg.h>#include <linux/delay.h>#include <linux/pm.h>#include <asm/memory.h>#include <asm/system.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/serial.h>#include <asm/mach-types.h>#include <asm/arch/hardware.h>#include <asm/arch/omap16xx.h>#include <asm/arch/pm.h>#include <asm/arch/mux.h>#include <asm/arch/tps65010.h>#include "clock.h"static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE];static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE];static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE];static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE];/* * 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 omap_pm_idle(void){ int (*func_ptr)(void) = 0; unsigned int mask32 = 0; /* * If the DSP is being used let's just idle the CPU, the overhead * to wake up from Big Sleep is big, milliseconds versus micro * seconds for wait for interrupt. */ cli(); clf(); if (need_resched()) { stf(); sti(); return; } mask32 = omap_readl(ARM_SYSST); stf(); sti(); if ((mask32 & DSP_IDLE) == 0) { __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); } else { if (cpu_is_omap1510()) { func_ptr = (void *)(OMAP1510_SRAM_IDLE_SUSPEND); } else if (cpu_is_omap1610() || cpu_is_omap1710()) { func_ptr = (void *)(OMAP1610_SRAM_IDLE_SUSPEND); } else if (cpu_is_omap5912()) { func_ptr = (void *)(OMAP5912_SRAM_IDLE_SUSPEND); } func_ptr(); }}/* * 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){ /* * Enable ARM XOR clock and release peripheral from reset by * writing 1 to PER_EN bit in ARM_RSTCT2, this is required * for UART configuration to use UART2 to wake up. */ omap_writel(omap_readl(ARM_IDLECT2) | ENABLE_XORCLK, ARM_IDLECT2); omap_writel(omap_readl(ARM_RSTCT2) | PER_EN, ARM_RSTCT2); omap_writew(MODEM_32K_EN, ULPD_CLOCK_CTRL); /* * Turn off all interrupts except L1-2nd level cascade, * and the L2 wakeup interrupts: keypad and UART2. */ omap_writel(~IRQ_LEVEL2, OMAP_IH1_MIR); if (cpu_is_omap1510()) { omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_MIR); } if (cpu_is_omap16xx()) { omap_writel(~(IRQ_UART2 | IRQ_KEYBOARD), OMAP_IH2_0_MIR); omap_writel(~0x0, OMAP_IH2_1_MIR); omap_writel(~0x0, OMAP_IH2_2_MIR); omap_writel(~0x0, OMAP_IH2_3_MIR); } /* New IRQ agreement */ omap_writel(1, OMAP_IH1_CONTROL); /* external PULL to down, bit 22 = 0 */ omap_writel(omap_readl(PULL_DWN_CTRL_2) & ~(1<<22), PULL_DWN_CTRL_2);}void omap_pm_suspend(void){ unsigned int mask32 = 0; unsigned long arg0 = 0, arg1 = 0; int (*func_ptr)(unsigned short, unsigned short) = 0; unsigned short save_dsp_idlect2; printk("PM: OMAP%x is entering deep sleep now ...\n", system_rev); if (machine_is_omap_osk()) { /* Stop LED1 (D9) blink */ tps65010_set_led(LED1, OFF); } /* * Step 1: turn off interrupts */ cli(); clf(); /* * 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_omap1510()) { 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); 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: LOW_PWR signal enabling * * Allow the LOW_PWR signal to be visible on MPUIO5 ball. */ if (cpu_is_omap1510()) { /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ omap_writew(omap_readw(ULPD_POWER_CTRL) | OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); } else if (cpu_is_omap16xx()) { /* POWER_CTRL_REG = 0x1 (LOW_POWER is available) */ omap_writew(omap_readw(ULPD_POWER_CTRL) | OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); } /* configure LOW_PWR pin */ omap_cfg_reg(T20_1610_LOW_PWR); /* * Step 4: OMAP DSP Shutdown */ /* Set DSP_RST = 1 and DSP_EN = 0, put DSP block into reset */ omap_writel((omap_readl(ARM_RSTCT1) | DSP_RST) & ~DSP_ENABLE, ARM_RSTCT1); /* Set DSP boot mode to DSP-IDLE, DSP_BOOT_MODE = 0x2 */ omap_writel(DSP_IDLE_MODE, MPUI_DSP_BOOT_CONFIG); /* Set EN_DSPCK = 0, stop DSP block clock */ omap_writel(omap_readl(ARM_CKCTL) & ~DSP_CLOCK_ENABLE, ARM_CKCTL); /* Stop any DSP domain clocks */ omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); save_dsp_idlect2 = __raw_readw(DSP_IDLECT2); __raw_writew(0, DSP_IDLECT2); /* * Step 5: Wakeup Event Setup */ omap_pm_wakeup_setup(); /* * Step 6a: ARM and Traffic controller shutdown * * Step 6 starts here with clock and watchdog disable */ /* stop clocks */ mask32 = omap_readl(ARM_IDLECT2); mask32 &= ~(1<<EN_WDTCK); /* bit 0 -> 0 (WDT clock) */ mask32 |= (1<<EN_XORPCK); /* bit 1 -> 1 (XORPCK clock) */ mask32 &= ~(1<<EN_PERCK); /* bit 2 -> 0 (MPUPER_CK clock) */ mask32 &= ~(1<<EN_LCDCK); /* bit 3 -> 0 (LCDC clock) */ mask32 &= ~(1<<EN_LBCK); /* bit 4 -> 0 (local bus clock) */ mask32 |= (1<<EN_APICK); /* bit 6 -> 1 (MPUI clock) */ mask32 &= ~(1<<EN_TIMCK); /* bit 7 -> 0 (MPU timer clock) */ mask32 &= ~(1<<DMACK_REQ); /* bit 8 -> 0 (DMAC clock) */ mask32 &= ~(1<<EN_GPIOCK); /* bit 9 -> 0 (GPIO clock) */ omap_writel(mask32, ARM_IDLECT2); /* 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]; if (cpu_is_omap1510()) { func_ptr = (void *)(OMAP1510_SRAM_API_SUSPEND); } else if (cpu_is_omap1610() || cpu_is_omap1710()) { func_ptr = (void *)(OMAP1610_SRAM_API_SUSPEND); } else if (cpu_is_omap5912()) { func_ptr = (void *)(OMAP5912_SRAM_API_SUSPEND); } /* * Step 6c: ARM and Traffic controller shutdown * * Jump to assembly code. The processor will stay there * until wake up. */ func_ptr(arg0, arg1); /* * If we are here, processor is woken up! */ if (cpu_is_omap1510()) { /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ omap_writew(omap_readw(ULPD_POWER_CTRL) & ~OMAP1510_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); } else if (cpu_is_omap16xx()) { /* POWER_CTRL_REG = 0x0 (LOW_POWER is disabled) */ omap_writew(omap_readw(ULPD_POWER_CTRL) & ~OMAP1610_ULPD_LOW_POWER_REQ, ULPD_POWER_CTRL); } /* Restore DSP clocks */ omap_writel(omap_readl(ARM_IDLECT2) | (1<<EN_APICK), ARM_IDLECT2); __raw_writew(save_dsp_idlect2, DSP_IDLECT2); ARM_RESTORE(ARM_IDLECT2); /* * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did */ 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_omap1510()) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?