ARM: tegra: power: implement LP1 suspend/resume for Tegra3
Yudong Tan [Thu, 18 Aug 2011 22:26:52 +0000 (15:26 -0700)]
Bug 862502

Change-Id: If70e54fb32ce14d5f13dde1d7fb4c1f1499a6722
Reviewed-on: http://git-master/r/47398
Reviewed-by: Daniel Willemsen <dwillemsen@nvidia.com>
Tested-by: Daniel Willemsen <dwillemsen@nvidia.com>

Rebase-Id: Ra77a54e6930692bca628a97bf1de10a30408cdef

arch/arm/mach-tegra/pm-t3.c
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h
arch/arm/mach-tegra/reset.h
arch/arm/mach-tegra/sleep-t30.S
arch/arm/mach-tegra/sleep.h

index c4f9552..487a11f 100644 (file)
@@ -329,7 +329,7 @@ int tegra_cluster_control(unsigned int us, unsigned int flags)
                if (us)
                        tegra_lp2_set_trigger(us);
 
-               tegra_suspend_dram(TEGRA_SUSPEND_LP1);
+               tegra_suspend_dram(TEGRA_SUSPEND_LP1, flags);
 
                if (us)
                        tegra_lp2_set_trigger(0);
index f42d4fe..e81dae1 100644 (file)
@@ -68,6 +68,7 @@
 #include "reset.h"
 #include "sleep.h"
 #include "timer.h"
+#include "reset.h"
 
 struct suspend_context {
        /*
@@ -101,6 +102,7 @@ static pgd_t *tegra_pgd;
 static DEFINE_SPINLOCK(tegra_lp2_lock);
 static cpumask_t tegra_in_lp2;
 static cpumask_t *iram_cpu_lp2_mask;
+static unsigned long *iram_cpu_lp1_mask;
 static u8 *iram_save;
 static unsigned long iram_save_size;
 static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA);
@@ -666,8 +668,6 @@ static void tegra_pm_set(enum tegra_suspend_mode mode)
                 * scratch 41 to tegra_resume
                 */
                writel(0x0, pmc + PMC_SCRATCH39);
-               __raw_writel(virt_to_phys(tegra_resume), pmc + PMC_SCRATCH41);
-               wmb();
 
                /* Enable DPD sample to trigger sampling pads data and direction
                 * in which pad will be driven during lp0 mode*/
@@ -680,8 +680,10 @@ static void tegra_pm_set(enum tegra_suspend_mode mode)
                pmc_32kwritel(tegra_lp0_vec_start, PMC_SCRATCH1);
 
                reg |= TEGRA_POWER_EFFECT_LP0;
-               break;
+               /* No break here. LP0 code falls through to write SCRATCH41 */
        case TEGRA_SUSPEND_LP1:
+               __raw_writel(virt_to_phys(tegra_resume), pmc + PMC_SCRATCH41);
+               wmb();
                break;
        case TEGRA_SUSPEND_LP2:
                rate = clk_get_rate(tegra_pclk);
@@ -711,7 +713,7 @@ static int tegra_suspend_enter(suspend_state_t state)
        if (pdata && pdata->board_suspend)
                pdata->board_suspend(current_suspend_mode, TEGRA_SUSPEND_BEFORE_PERIPHERAL);
 
-       ret = tegra_suspend_dram(current_suspend_mode);
+       ret = tegra_suspend_dram(current_suspend_mode, 0);
 
        if (pdata && pdata->board_resume)
                pdata->board_resume(current_suspend_mode, TEGRA_RESUME_AFTER_PERIPHERAL);
@@ -744,7 +746,7 @@ static void tegra_suspend_check_pwr_stats(void)
        return;
 }
 
-int tegra_suspend_dram(enum tegra_suspend_mode mode)
+int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags)
 {
        BUG_ON(mode < 0 || mode >= TEGRA_MAX_SUSPEND_MODE);
 
@@ -775,7 +777,11 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode)
                tegra_lp0_suspend_mc();
        }
 
-       suspend_cpu_complex(0);
+       suspend_cpu_complex(flags);
+
+       if (mode == TEGRA_SUSPEND_LP1)
+               *iram_cpu_lp1_mask = 1;
+
        flush_cache_all();
        outer_flush_all();
        outer_disable();
@@ -790,9 +796,10 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode)
        if (mode == TEGRA_SUSPEND_LP0) {
                tegra_lp0_resume_mc();
                tegra_lp0_cpu_mode(false);
-       }
+       } else if (mode == TEGRA_SUSPEND_LP1)
+               *iram_cpu_lp1_mask = 0;
 
-       restore_cpu_complex(0);
+       restore_cpu_complex(flags);
 
        cpu_cluster_pm_exit();
        cpu_pm_exit();
@@ -804,6 +811,8 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode)
 
        tegra_common_resume();
 
+       pr_info("Exited suspend state %s\n", lp_state[mode]);
+
        return 0;
 }
 
@@ -1022,6 +1031,7 @@ out:
        }
 
        iram_cpu_lp2_mask = tegra_cpu_lp2_mask;
+       iram_cpu_lp1_mask = tegra_cpu_lp1_mask;
 fail:
 #endif
        if (plat->suspend_mode == TEGRA_SUSPEND_NONE)
index 0b62dcf..2ffbf24 100644 (file)
@@ -67,7 +67,7 @@ unsigned long tegra_cpu_lp2_min_residency(void);
 void tegra_clear_cpu_in_lp2(int cpu);
 bool tegra_set_cpu_in_lp2(int cpu);
 
-int tegra_suspend_dram(enum tegra_suspend_mode mode);
+int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags);
 
 #define FLOW_CTRL_CLUSTER_CONTROL \
        (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x2c)
index 6d51149..58ddea4 100644 (file)
@@ -47,7 +47,7 @@ void tegra_secondary_startup(void);
                 __tegra_cpu_reset_handler_start)
 
 #ifdef CONFIG_PM_SLEEP
-#define tegra_cpu_lp1_map (*(unsigned long *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
+#define tegra_cpu_lp1_mask ((unsigned long *)(IO_ADDRESS(TEGRA_RESET_HANDLER_BASE + \
                ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP1] - \
                 (u32)__tegra_cpu_reset_handler_start))))
 
index 1ca1f00..b0310ae 100644 (file)
 #include "sleep.h"
 #include "flowctrl.h"
 
+#define EMC_CFG                                0xc
+#define EMC_ADR_CFG                    0x10
+#define EMC_TIMING_CONTROL             0x28
+#define EMC_REFRESH                    0x70
+#define EMC_NOP                                0xdc
+#define EMC_SELF_REF                   0xe0
+#define EMC_MRW                                0xe8
+#define EMC_REQ_CTRL                   0x2b0
+#define EMC_EMC_STATUS                 0x2b4
+#define EMC_FBIO_CFG5                  0x104
+#define EMC_AUTO_CAL_CONFIG            0x2a4
+#define EMC_AUTO_CAL_INTERVAL          0x2a8
+#define EMC_AUTO_CAL_STATUS            0x2ac
+#define EMC_CFG_DIG_DLL                        0x2bc
+#define EMC_ZCAL_INTERVAL              0x2e0
+#define EMC_ZQ_CAL                     0x2ec
+#define EMC_XM2VTTGENPADCTRL           0x310
+#define EMC_XM2VTTGENPADCTRL2          0x314
+
+#define PMC_PWRGATE_TOGGLE             0x30
+#define PMC_REMOVE_CLAMPING_CMD                0x34
+#define PMC_PWRGATE_STATUS             0x38
+
+#define PMC_PWRGATE_PARTID_L2C         (0x5)
+
+#define PMC_IO_DPD_REQ                 0x1b8
+#define PMC_IO_DPD_STATUS              0x1bc
+
+#define CLK_RESET_CCLK_BURST           0x20
+#define CLK_RESET_CCLK_DIVIDER         0x24
+#define CLK_RESET_SCLK_BURST           0x28
+#define CLK_RESET_SCLK_DIVIDER         0x2c
+
+#define CLK_RESET_PLLC_BASE            0x80
+#define CLK_RESET_PLLM_BASE            0x90
+#define CLK_RESET_PLLP_BASE            0xa0
+#define CLK_RESET_PLLA_BASE            0xb0
+#define CLK_RESET_PLLX_BASE            0xe0
+
+#define CLK_RESET_PLLC_MISC            0x8c
+#define CLK_RESET_PLLM_MISC            0x9c
+#define CLK_RESET_PLLP_MISC            0xac
+#define CLK_RESET_PLLA_MISC            0xbc
+#define CLK_RESET_PLLX_MISC            0xe4
+
+#define CLK_RESET_PLLP_OUTA            0xa4
+#define CLK_RESET_PLLP_OUTB            0xa8
+
+#define PMC_PLLP_WB0_OVERRIDE          0xf8
+
+#define CLK_RESET_CLK_SOURCE_MSELECT   0x3b4
+
+#define MSELECT_CLKM                   (0x3 << 30)
+
+#define USE_PLL_LOCK_BITS 0
+
 #define TEGRA30_POWER_HOTPLUG_SHUTDOWN (1 << 27) /* Hotplug shutdown */
 
+.macro emc_device_mask, rd, base
+       ldr     \rd, [\base, #EMC_ADR_CFG]
+       tst     \rd, #(0x3<<24)
+       moveq   \rd, #(0x1<<8)          @ just 1 device
+       movne   \rd, #(0x3<<8)          @ 2 devices
+.endm
+
+.macro emc_timing_update, rd, base
+       mov     \rd, #1
+       str     \rd, [\base, #EMC_TIMING_CONTROL]
+1001:
+       ldr     \rd, [\base, #EMC_EMC_STATUS]
+       tst     \rd, #(0x1<<23)         @ wait until EMC_STATUS_TIMING_UPDATE_STALLED is clear
+       bne     1001b
+.endm
+
 #if defined(CONFIG_HOTPLUG_CPU) || defined(CONFIG_PM_SLEEP)
 /*
  * tegra30_hotplug_shutdown(void)
@@ -118,6 +190,35 @@ ENDPROC(tegra30_cpu_shutdown)
 
 #ifdef CONFIG_PM_SLEEP
 /*
+ * tegra3_sleep_core(unsigned long v2p)
+ *
+ * enters suspend in LP0 or LP1 by turning off the mmu and jumping to
+ * tegra3_tear_down_core in IRAM
+ */
+ENTRY(tegra3_sleep_core)
+       mov     r12, pc                 @ return here is via r12
+       b       tegra_cpu_save
+
+       /* preload all the address literals that are needed for the
+        * CPU power-gating process, to avoid loads from SDRAM (which are
+        * not supported once SDRAM is put into self-refresh.
+        * LP0 / LP1 use physical address, since the MMU needs to be
+        * disabled before putting SDRAM into self-refresh to avoid
+        * memory access due to page table walks */
+       mov32   r4, TEGRA_PMC_BASE
+       mov32   r5, TEGRA_CLK_RESET_BASE
+       mov32   r6, TEGRA_FLOW_CTRL_BASE
+       mov32   r7, TEGRA_TMRUS_BASE
+
+       mov32   r1, tegra3_tear_down_core
+       mov32   r2, tegra3_iram_start
+       sub     r1, r1, r2
+       mov32   r2, TEGRA_IRAM_CODE_AREA
+       add     r1, r1, r2
+       b       tegra_turn_off_mmu
+ENDPROC(tegra3_sleep_core)
+
+/*
  * tegra3_sleep_cpu_secondary(unsigned long v2p)
  *
  * Enters LP2 on secondary CPU by exiting coherency and powergating the CPU.
@@ -138,6 +239,11 @@ ENDPROC(tegra3_sleep_cpu_secondary)
  * Switches the CPU cluster to PLL-P and enters sleep.
  */
 ENTRY(tegra3_tear_down_cpu)
+       mov32   r4, TEGRA_PMC_BASE
+       mov32   r5, TEGRA_CLK_RESET_BASE
+       mov32   r6, TEGRA_FLOW_CTRL_BASE
+       mov32   r7, TEGRA_TMRUS_BASE
+
        bl      tegra_cpu_pllp
        b       tegra3_enter_sleep
 ENDPROC(tegra3_tear_down_cpu)
@@ -160,8 +266,274 @@ tegra3_iram_start:
  *
  * NOTE: THIS *MUST* BE RELOCATED TO TEGRA_IRAM_CODE_AREA AND MUST BE FIRST.
  */
+.macro pll_enable, rd, car, base, misc
+       ldr     \rd, [\car, #\base]
+       tst     \rd, #(1<<30)
+       orreq   \rd, \rd, #(1<<30)
+       streq   \rd, [\car, #\base]
+#if USE_PLL_LOCK_BITS
+       ldr     \rd, [\car, #\misc]
+       orr     \rd, \rd, #(1<<18)
+       str     \rd, [\car, #\misc]
+#endif
+.endm
+
+ENTRY(tegra3_lp1_reset)
+       /* the CPU and system bus are running at 32KHz and executing from
+        * IRAM when this code is executed; immediately switch to CLKM and
+        * enable PLLP, PLLM, PLLC, PLLA and PLLX. */
+       mov32   r0, TEGRA_CLK_RESET_BASE
+
+       mov     r1, #(1<<28)
+       str     r1, [r0, #CLK_RESET_SCLK_BURST]
+       str     r1, [r0, #CLK_RESET_CCLK_BURST]
+       mov     r1, #0
+       str     r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+       str     r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+
+       /* enable PLLM via PMC */
+       mov32   r2, TEGRA_PMC_BASE
+       ldr     r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+       orr     r1, r1, #(1<<12)
+       str     r1, [r2, #PMC_PLLP_WB0_OVERRIDE]
+
+       pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+       pll_enable r1, r0, CLK_RESET_PLLA_BASE, CLK_RESET_PLLA_MISC
+       pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
+       pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
+       mov32   r7, TEGRA_TMRUS_BASE
+       ldr     r1, [r7]
+
+#if USE_PLL_LOCK_BITS
+       pll_locked r1, r0, CLK_RESET_PLLM_BASE
+       pll_locked r1, r0, CLK_RESET_PLLP_BASE
+       pll_locked r1, r0, CLK_RESET_PLLA_BASE
+       pll_locked r1, r0, CLK_RESET_PLLC_BASE
+       pll_locked r1, r0, CLK_RESET_PLLX_BASE
+#else
+       add     r1, r1, #0xff   @ 255uS delay for PLL stabilization
+       wait_until r1, r7, r3
+#endif
+       add     r5, pc, #tegra3_sdram_pad_save-(.+8)    @ r5 reserved for pad base
+
+       ldr     r4, [r5, #0x18]
+       str     r4, [r0, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+       ldr     r4, [r5, #0x1C]
+       str     r4, [r0, #CLK_RESET_SCLK_BURST]
+
+       mov32   r4, ((1<<28) | (8))     @ burst policy is PLLX
+       str     r4, [r0, #CLK_RESET_CCLK_BURST]
+
+#if defined (CONFIG_CACHE_L2X0)
+       /* power up L2 */
+       ldr     r0, [r2, #PMC_PWRGATE_STATUS]
+       tst     r0, #(1<<PMC_PWRGATE_PARTID_L2C)
+       bne     powerup_l2_done
+       movw    r0, #(1<<8) | PMC_PWRGATE_PARTID_L2C
+       str     r0, [r2, #PMC_PWRGATE_TOGGLE]
+powerup_l2_wait:
+       ldr     r0, [r2, #PMC_PWRGATE_STATUS]
+       tst     r0, #(1<<PMC_PWRGATE_PARTID_L2C)
+       beq     powerup_l2_wait
+powerup_l2_done:
+       mov     r0, #PMC_PWRGATE_PARTID_L2C
+       str     r0, [r2, #PMC_REMOVE_CLAMPING_CMD]
+#endif
+
+       mov32   r0, TEGRA_EMC_BASE                      @ r0 reserved for emc base
+
+       ldr     r1, [r5, #0x14] @ PMC_IO_DPD_STATUS
+       mvn     r1, r1
+       bic     r1, r1, #(0x1<<31)
+       orr     r1, r1, #(0x1<<30)
+       str     r1, [r2, #PMC_IO_DPD_REQ]
+       ldr     r1, [r5, #0xC]
+       str     r1, [r0, #EMC_XM2VTTGENPADCTRL]
+       ldr     r1, [r5, #0x10]
+       str     r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+       ldr     r1, [r5, #0x8]
+       str     r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+
+       ldr     r1, [r0, #EMC_CFG_DIG_DLL]
+       orr     r1, r1, #(0x1<<30)              @ set DLL_RESET
+       str     r1, [r0, #EMC_CFG_DIG_DLL]
+
+       emc_timing_update r1, r0
+
+       ldr     r1, [r0, #EMC_AUTO_CAL_CONFIG]
+       orr     r1, r1, #(0x1<<31)              @ set AUTO_CAL_ACTIVE
+       str     r1, [r0, #EMC_AUTO_CAL_CONFIG]
+
+emc_wait_audo_cal_onetime:
+       ldr     r1, [r0, #EMC_AUTO_CAL_STATUS]
+       tst     r1, #(0x1<<31)          @ wait until AUTO_CAL_ACTIVE is clear
+       bne     emc_wait_audo_cal_onetime
+
+       ldr     r1, [r0, #EMC_CFG]
+       bic     r1, r1, #(1<<31)        @ disable DRAM_CLK_STOP
+       str     r1, [r0, #EMC_CFG]
+
+       mov     r1, #0
+       str     r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh
+       mov     r1, #1
+       str     r1, [r0, #EMC_NOP]
+       str     r1, [r0, #EMC_NOP]
+       str     r1, [r0, #EMC_REFRESH]
+
+       emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+       ldr     r2, [r0, #EMC_EMC_STATUS]
+       ands    r2, r2, r1
+       bne     exit_selfrefresh_loop
+
+       lsr     r1, r1, #8              @ devSel, bit0:dev0 bit1:dev1
+
+       mov32   r7, TEGRA_TMRUS_BASE
+       ldr     r2, [r0, #EMC_FBIO_CFG5]
+
+       and     r2, r2, #3
+       cmp     r2, #2
+       beq     emc_lpddr2
+
+       mov32   r2, 0x80000011
+       str     r2, [r0, #EMC_ZQ_CAL]
+       ldr     r2, [r7]
+       add     r2, r2, #10
+       wait_until r2, r7, r3
 
-/* !!!FIXME!!! Add LP1/LP1 code */
+       tst     r1, #2
+       beq zcal_done
+
+       mov32   r2, 0x40000011
+       str     r2, [r0, #EMC_ZQ_CAL]
+       ldr     r2, [r7]
+       add     r2, r2, #10
+       wait_until r2, r7, r3
+       b zcal_done
+
+emc_lpddr2:
+
+       mov32   r2, 0x800A00AB
+       str     r2, [r0, #EMC_MRW]
+       ldr     r2, [r7]
+       add     r2, r2, #1
+       wait_until r2, r7, r3
+
+       tst     r1, #2
+       beq zcal_done
+
+       mov32   r2, 0x400A00AB
+       str     r2, [r0, #EMC_MRW]
+       ldr     r2, [r7]
+       add     r2, r2, #1
+       wait_until r2, r7, r3
+
+zcal_done:
+
+       mov     r1, #0
+       str     r1, [r0, #EMC_REQ_CTRL]
+       ldr     r1, [r5, #0x4]
+       str     r1, [r0, #EMC_ZCAL_INTERVAL]
+       ldr     r1, [r5, #0x0]
+       str     r1, [r0, #EMC_CFG]
+
+       mov32   r0, TEGRA_PMC_BASE
+       ldr     r0, [r0, #PMC_SCRATCH41]
+       mov     pc, r0
+ENDPROC(tegra3_lp1_reset)
+
+       .align  L1_CACHE_SHIFT
+       .type   tegra3_sdram_pad_save, %object
+tegra3_sdram_pad_save:
+       .word   0
+       .word   0
+       .word   0
+       .word   0
+       .word   0
+       .word   0
+       .word   0
+       .word   0
+
+tegra3_sdram_pad_address:
+       .word   TEGRA_EMC_BASE + EMC_CFG                                @0x0
+       .word   TEGRA_EMC_BASE + EMC_ZCAL_INTERVAL                      @0x4
+       .word   TEGRA_EMC_BASE + EMC_AUTO_CAL_INTERVAL                  @0x8
+       .word   TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL                   @0xc
+       .word   TEGRA_EMC_BASE + EMC_XM2VTTGENPADCTRL2                  @0x10
+       .word   TEGRA_PMC_BASE + PMC_IO_DPD_STATUS                      @0x14
+       .word   TEGRA_CLK_RESET_BASE + CLK_RESET_CLK_SOURCE_MSELECT     @0x18
+       .word   TEGRA_CLK_RESET_BASE + CLK_RESET_SCLK_BURST             @0x1c
+
+tegra3_sdram_pad_size:
+       .word   tegra3_sdram_pad_address - tegra3_sdram_pad_save
+
+/*
+ * tegra3_tear_down_core
+ *
+ * copied into and executed from IRAM
+ * puts memory in self-refresh for LP0 and LP1
+ */
+tegra3_tear_down_core:
+       bl      tegra3_sdram_self_refresh
+       bl      tegra3_cpu_clk32k
+       b       tegra3_enter_sleep
+
+/*
+ * tegra3_cpu_clk32k
+ *
+ * In LP0 and LP1 all plls will be turned off.  Switch the CPU and system clock
+ * to the 32khz clock (clks)
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+tegra3_cpu_clk32k:
+       /* start by jumping to clkm to safely disable PLLs, then jump
+        * to clks */
+       mov     r0, #(1 << 28)
+       str     r0, [r5, #CLK_RESET_SCLK_BURST]
+       str     r0, [r5, #CLK_RESET_CCLK_BURST]
+       mov     r0, #0
+       str     r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+       str     r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+       /* switch the clock source for mselect to be CLK_M */
+       ldr     r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+       orr     r0, r0, #MSELECT_CLKM
+       str     r0, [r5, #CLK_RESET_CLK_SOURCE_MSELECT]
+
+       /* 2 us delay between changing sclk and disabling PLLs */
+       wait_for_us r1, r7, r9
+       add     r1, r1, #2
+       wait_until r1, r7, r9
+
+#if 1
+       /* switch to CLKS */
+       mov     r0, #0  /* burst policy = 32KHz */
+       str     r0, [r5, #CLK_RESET_SCLK_BURST]
+#endif
+       /* disable PLLM via PMC in LP0 and LP1 states */
+       ldr     r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+       bic     r0, r0, #(1<<12)
+       str     r0, [r4, #PMC_PLLP_WB0_OVERRIDE]
+
+       /* disable PLLP, PLLA, PLLC, and PLLX in LP0 and LP1 states */
+       ldr     r0, [r5, #CLK_RESET_PLLP_BASE]
+       bic     r0, r0, #(1<<30)
+       str     r0, [r5, #CLK_RESET_PLLP_BASE]
+       ldr     r0, [r5, #CLK_RESET_PLLA_BASE]
+       bic     r0, r0, #(1<<30)
+       str     r0, [r5, #CLK_RESET_PLLA_BASE]
+       ldr     r0, [r5, #CLK_RESET_PLLC_BASE]
+       bic     r0, r0, #(1<<30)
+       str     r0, [r5, #CLK_RESET_PLLC_BASE]
+       ldr     r0, [r5, #CLK_RESET_PLLX_BASE]
+       bic     r0, r0, #(1<<30)
+       str     r0, [r5, #CLK_RESET_PLLX_BASE]
+       mov     pc, lr
 
 /*
  * tegra3_enter_sleep
@@ -169,14 +541,15 @@ tegra3_iram_start:
  * uses flow controller to enter sleep state
  * executes from IRAM with SDRAM in selfrefresh when target state is LP0 or LP1
  * executes from SDRAM with target state is LP2
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
  */
 tegra3_enter_sleep:
-       mov32   r7, TEGRA_TMRUS_BASE
        ldr     r1, [r7]
-       mov32   r4, TEGRA_PMC_BASE
        str     r1, [r4, #PMC_SCRATCH38]
        dsb
-       mov32   r6, TEGRA_FLOW_CTRL_BASE
        cpu_id  r1
 
        cpu_to_csr_reg  r2, r1
@@ -199,6 +572,94 @@ halted:
        /* !!!FIXME!!! Implement halt failure handler */
        b       halted
 
+/*
+ * tegra3_sdram_self_refresh
+ *
+ * called with MMU off and caches disabled
+ * puts sdram in self refresh
+ * must execute from IRAM
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ */
+
+tegra3_sdram_self_refresh:
+
+       adr     r2, tegra3_sdram_pad_address
+       adr     r8, tegra3_sdram_pad_save
+       mov     r9, #0
+
+padsave:
+       ldr     r0, [r2, r9]                    @ r0 is emc register address
+
+       ldr     r1, [r0]
+       str     r1, [r8, r9]                    @ save emc register
+
+       add     r9, r9, #4
+       ldr     r0, tegra3_sdram_pad_size
+       cmp     r0, r9
+       bne     padsave
+padsave_done:
+
+       dsb
+
+       mov32   r0, TEGRA_EMC_BASE                      @ r0 reserved for emc base
+
+       mov     r1, #0
+       str     r1, [r0, #EMC_ZCAL_INTERVAL]
+       str     r1, [r0, #EMC_AUTO_CAL_INTERVAL]
+       ldr     r1, [r0, #EMC_CFG]
+       bic     r1, r1, #(1<<28)
+       str     r1, [r0, #EMC_CFG]              @ disable DYN_SELF_REF
+
+       emc_timing_update r1, r0
+
+       ldr     r1, [r7]
+       add     r1, r1, #5
+       wait_until r1, r7, r2
+
+emc_wait_audo_cal:
+       ldr     r1, [r0, #EMC_AUTO_CAL_STATUS]
+       tst     r1, #(0x1<<31)          @ wait until AUTO_CAL_ACTIVE is clear
+       bne     emc_wait_audo_cal
+
+       mov     r1, #3
+       str     r1, [r0, #EMC_REQ_CTRL]         @ stall incoming DRAM requests
+
+emcidle:
+       ldr     r1, [r0, #EMC_EMC_STATUS]
+       tst     r1, #4
+       beq     emcidle
+
+       mov     r1, #1
+       str     r1, [r0, #EMC_SELF_REF]
+
+       emc_device_mask r1, r0
+
+emcself:
+       ldr     r2, [r0, #EMC_EMC_STATUS]
+       and     r2, r2, r1
+       cmp     r2, r1
+       bne     emcself                         @ loop until DDR in self-refresh
+
+       ldr     r1, [r0, #EMC_XM2VTTGENPADCTRL]
+       mov32   r2, 0xF8F8FFFF          @ clear XM2VTTGEN_DRVUP and XM2VTTGEN_DRVDN
+       and     r1, r1, r2
+       str     r1, [r0, #EMC_XM2VTTGENPADCTRL]
+       ldr     r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+       orr     r1, r1, #7                      @ set E_NO_VTTGEN
+       str     r1, [r0, #EMC_XM2VTTGENPADCTRL2]
+
+       emc_timing_update r1, r0
+
+       mov32   r1, 0x8EC00000
+       str     r1, [r4, #PMC_IO_DPD_REQ]
+
+       dsb
+
+       mov     pc, lr
+
        .ltorg
 /* dummy symbol for end of IRAM */
        .align L1_CACHE_SHIFT
index 3dce6f9..3f103c5 100644 (file)
                                        + IO_PPSB_VIRT)
 
 #ifdef __ASSEMBLY__
+/* waits until the microsecond counter (base) ticks, for exact timing loops */
+.macro  wait_for_us, rd, base, tmp
+       ldr    \rd, [\base]
+1001:   ldr    \tmp, [\base]
+       cmp    \rd, \tmp
+       beq    1001b
+       mov    \tmp, \rd
+.endm
+
 /* waits until the microsecond counter (base) is > rn */
 .macro wait_until, rn, base, tmp
        add     \rn, \rn, #1
@@ -207,8 +216,7 @@ static inline void tegra_sleep_core(unsigned long v2p)
 #ifdef CONFIG_ARCH_TEGRA_2x_SOC
        tegra2_sleep_core(v2p);
 #else
-       /* tegra3_sleep_core(v2p);    !!!FIXME!!! not supported yet */
-       BUG();
+       tegra3_sleep_core(v2p);
 #endif
 }