1 /* 2 * hp6x0 Power Management Routines 3 * 4 * Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License. 8 */ 9 #include <linux/init.h> 10 #include <linux/suspend.h> 11 #include <linux/errno.h> 12 #include <linux/time.h> 13 #include <linux/delay.h> 14 #include <linux/gfp.h> 15 #include <asm/io.h> 16 #include <asm/hd64461.h> 17 #include <mach/hp6xx.h> 18 #include <cpu/dac.h> 19 #include <asm/freq.h> 20 #include <asm/watchdog.h> 21 22 #define INTR_OFFSET 0x600 23 24 #define STBCR 0xffffff82 25 #define STBCR2 0xffffff88 26 27 #define STBCR_STBY 0x80 28 #define STBCR_MSTP2 0x04 29 30 #define MCR 0xffffff68 31 #define RTCNT 0xffffff70 32 33 #define MCR_RMODE 2 34 #define MCR_RFSH 4 35 36 extern u8 wakeup_start; 37 extern u8 wakeup_end; 38 39 static void pm_enter(void) 40 { 41 u8 stbcr, csr; 42 u16 frqcr, mcr; 43 u32 vbr_new, vbr_old; 44 45 set_bl_bit(); 46 47 /* set wdt */ 48 csr = sh_wdt_read_csr(); 49 csr &= ~WTCSR_TME; 50 csr |= WTCSR_CKS_4096; 51 sh_wdt_write_csr(csr); 52 csr = sh_wdt_read_csr(); 53 sh_wdt_write_cnt(0); 54 55 /* disable PLL1 */ 56 frqcr = ctrl_inw(FRQCR); 57 frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY); 58 ctrl_outw(frqcr, FRQCR); 59 60 /* enable standby */ 61 stbcr = ctrl_inb(STBCR); 62 ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR); 63 64 /* set self-refresh */ 65 mcr = ctrl_inw(MCR); 66 ctrl_outw(mcr & ~MCR_RFSH, MCR); 67 68 /* set interrupt handler */ 69 asm volatile("stc vbr, %0" : "=r" (vbr_old)); 70 vbr_new = get_zeroed_page(GFP_ATOMIC); 71 udelay(50); 72 memcpy((void*)(vbr_new + INTR_OFFSET), 73 &wakeup_start, &wakeup_end - &wakeup_start); 74 asm volatile("ldc %0, vbr" : : "r" (vbr_new)); 75 76 ctrl_outw(0, RTCNT); 77 ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR); 78 79 cpu_sleep(); 80 81 asm volatile("ldc %0, vbr" : : "r" (vbr_old)); 82 83 free_page(vbr_new); 84 85 /* enable PLL1 */ 86 frqcr = ctrl_inw(FRQCR); 87 frqcr |= FRQCR_PSTBY; 88 ctrl_outw(frqcr, FRQCR); 89 udelay(50); 90 frqcr |= FRQCR_PLLEN; 91 ctrl_outw(frqcr, FRQCR); 92 93 ctrl_outb(stbcr, STBCR); 94 95 clear_bl_bit(); 96 } 97 98 static int hp6x0_pm_enter(suspend_state_t state) 99 { 100 u8 stbcr, stbcr2; 101 #ifdef CONFIG_HD64461_ENABLER 102 u8 scr; 103 u16 hd64461_stbcr; 104 #endif 105 106 #ifdef CONFIG_HD64461_ENABLER 107 outb(0, HD64461_PCC1CSCIER); 108 109 scr = inb(HD64461_PCC1SCR); 110 scr |= HD64461_PCCSCR_VCC1; 111 outb(scr, HD64461_PCC1SCR); 112 113 hd64461_stbcr = inw(HD64461_STBCR); 114 hd64461_stbcr |= HD64461_STBCR_SPC1ST; 115 outw(hd64461_stbcr, HD64461_STBCR); 116 #endif 117 118 ctrl_outb(0x1f, DACR); 119 120 stbcr = ctrl_inb(STBCR); 121 ctrl_outb(0x01, STBCR); 122 123 stbcr2 = ctrl_inb(STBCR2); 124 ctrl_outb(0x7f , STBCR2); 125 126 outw(0xf07f, HD64461_SCPUCR); 127 128 pm_enter(); 129 130 outw(0, HD64461_SCPUCR); 131 ctrl_outb(stbcr, STBCR); 132 ctrl_outb(stbcr2, STBCR2); 133 134 #ifdef CONFIG_HD64461_ENABLER 135 hd64461_stbcr = inw(HD64461_STBCR); 136 hd64461_stbcr &= ~HD64461_STBCR_SPC1ST; 137 outw(hd64461_stbcr, HD64461_STBCR); 138 139 outb(0x4c, HD64461_PCC1CSCIER); 140 outb(0x00, HD64461_PCC1CSCR); 141 #endif 142 143 return 0; 144 } 145 146 static struct platform_suspend_ops hp6x0_pm_ops = { 147 .enter = hp6x0_pm_enter, 148 .valid = suspend_valid_only_mem, 149 }; 150 151 static int __init hp6x0_pm_init(void) 152 { 153 suspend_set_ops(&hp6x0_pm_ops); 154 return 0; 155 } 156 157 late_initcall(hp6x0_pm_init); 158