1/* 2 * Blackfin power management 3 * 4 * Copyright 2006-2009 Analog Devices Inc. 5 * 6 * Licensed under the GPL-2 7 * based on arm/mach-omap/pm.c 8 * Copyright 2001, Cliff Brake <cbrake@accelent.com> and others 9 */ 10 11#include <linux/suspend.h> 12#include <linux/sched.h> 13#include <linux/proc_fs.h> 14#include <linux/slab.h> 15#include <linux/io.h> 16#include <linux/irq.h> 17#include <linux/delay.h> 18 19#include <asm/cplb.h> 20#include <asm/gpio.h> 21#include <asm/dma.h> 22#include <asm/dpmc.h> 23#include <asm/pm.h> 24 25#ifdef CONFIG_BF60x 26struct bfin_cpu_pm_fns *bfin_cpu_pm; 27#endif 28 29void bfin_pm_suspend_standby_enter(void) 30{ 31#if !BFIN_GPIO_PINT 32 bfin_pm_standby_setup(); 33#endif 34 35#ifdef CONFIG_BF60x 36 bfin_cpu_pm->enter(PM_SUSPEND_STANDBY); 37#else 38# ifdef CONFIG_PM_BFIN_SLEEP_DEEPER 39 sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); 40# else 41 sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); 42# endif 43#endif 44 45#if !BFIN_GPIO_PINT 46 bfin_pm_standby_restore(); 47#endif 48 49#ifndef CONFIG_BF60x 50#ifdef SIC_IWR0 51 bfin_write_SIC_IWR0(IWR_DISABLE_ALL); 52# ifdef SIC_IWR1 53 /* BF52x system reset does not properly reset SIC_IWR1 which 54 * will screw up the bootrom as it relies on MDMA0/1 waking it 55 * up from IDLE instructions. See this report for more info: 56 * http://blackfin.uclinux.org/gf/tracker/4323 57 */ 58 if (ANOMALY_05000435) 59 bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); 60 else 61 bfin_write_SIC_IWR1(IWR_DISABLE_ALL); 62# endif 63# ifdef SIC_IWR2 64 bfin_write_SIC_IWR2(IWR_DISABLE_ALL); 65# endif 66#else 67 bfin_write_SIC_IWR(IWR_DISABLE_ALL); 68#endif 69 70#endif 71} 72 73int bf53x_suspend_l1_mem(unsigned char *memptr) 74{ 75 dma_memcpy_nocache(memptr, (const void *) L1_CODE_START, 76 L1_CODE_LENGTH); 77 dma_memcpy_nocache(memptr + L1_CODE_LENGTH, 78 (const void *) L1_DATA_A_START, L1_DATA_A_LENGTH); 79 dma_memcpy_nocache(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH, 80 (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH); 81 memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH + 82 L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START, 83 L1_SCRATCH_LENGTH); 84 85 return 0; 86} 87 88int bf53x_resume_l1_mem(unsigned char *memptr) 89{ 90 dma_memcpy_nocache((void *) L1_CODE_START, memptr, L1_CODE_LENGTH); 91 dma_memcpy_nocache((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH, 92 L1_DATA_A_LENGTH); 93 dma_memcpy_nocache((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH + 94 L1_DATA_A_LENGTH, L1_DATA_B_LENGTH); 95 memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH + 96 L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH); 97 98 return 0; 99} 100 101#if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK) 102# ifdef CONFIG_BF60x 103__attribute__((l1_text)) 104# endif 105static void flushinv_all_dcache(void) 106{ 107 register u32 way, bank, subbank, set; 108 register u32 status, addr; 109 u32 dmem_ctl = bfin_read_DMEM_CONTROL(); 110 111 for (bank = 0; bank < 2; ++bank) { 112 if (!(dmem_ctl & (1 << (DMC1_P - bank)))) 113 continue; 114 115 for (way = 0; way < 2; ++way) 116 for (subbank = 0; subbank < 4; ++subbank) 117 for (set = 0; set < 64; ++set) { 118 119 bfin_write_DTEST_COMMAND( 120 way << 26 | 121 bank << 23 | 122 subbank << 16 | 123 set << 5 124 ); 125 CSYNC(); 126 status = bfin_read_DTEST_DATA0(); 127 128 /* only worry about valid/dirty entries */ 129 if ((status & 0x3) != 0x3) 130 continue; 131 132 133 /* construct the address using the tag */ 134 addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5); 135 136 /* flush it */ 137 __asm__ __volatile__("FLUSHINV[%0];" : : "a"(addr)); 138 } 139 } 140} 141#endif 142 143int bfin_pm_suspend_mem_enter(void) 144{ 145 int ret; 146#ifndef CONFIG_BF60x 147 int wakeup; 148#endif 149 150 unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH 151 + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH, 152 GFP_ATOMIC); 153 154 if (memptr == NULL) { 155 panic("bf53x_suspend_l1_mem malloc failed"); 156 return -ENOMEM; 157 } 158 159#ifndef CONFIG_BF60x 160 wakeup = bfin_read_VR_CTL() & ~FREQ; 161 wakeup |= SCKELOW; 162 163#ifdef CONFIG_PM_BFIN_WAKE_PH6 164 wakeup |= PHYWE; 165#endif 166#ifdef CONFIG_PM_BFIN_WAKE_GP 167 wakeup |= GPWE; 168#endif 169#endif 170 171 ret = blackfin_dma_suspend(); 172 173 if (ret) { 174 kfree(memptr); 175 return ret; 176 } 177 178#ifdef CONFIG_GPIO_ADI 179 bfin_gpio_pm_hibernate_suspend(); 180#endif 181 182#if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK) 183 flushinv_all_dcache(); 184 udelay(1); 185#endif 186 _disable_dcplb(); 187 _disable_icplb(); 188 bf53x_suspend_l1_mem(memptr); 189 190#ifndef CONFIG_BF60x 191 do_hibernate(wakeup | vr_wakeup); /* See you later! */ 192#else 193 bfin_cpu_pm->enter(PM_SUSPEND_MEM); 194#endif 195 196 bf53x_resume_l1_mem(memptr); 197 198 _enable_icplb(); 199 _enable_dcplb(); 200 201#ifdef CONFIG_GPIO_ADI 202 bfin_gpio_pm_hibernate_restore(); 203#endif 204 blackfin_dma_resume(); 205 206 kfree(memptr); 207 208 return 0; 209} 210 211/* 212 * bfin_pm_valid - Tell the PM core that we only support the standby sleep 213 * state 214 * @state: suspend state we're checking. 215 * 216 */ 217static int bfin_pm_valid(suspend_state_t state) 218{ 219 return (state == PM_SUSPEND_STANDBY 220#if !(defined(BF533_FAMILY) || defined(CONFIG_BF561)) 221 /* 222 * On BF533/2/1: 223 * If we enter Hibernate the SCKE Pin is driven Low, 224 * so that the SDRAM enters Self Refresh Mode. 225 * However when the reset sequence that follows hibernate 226 * state is executed, SCKE is driven High, taking the 227 * SDRAM out of Self Refresh. 228 * 229 * If you reconfigure and access the SDRAM "very quickly", 230 * you are likely to avoid errors, otherwise the SDRAM 231 * start losing its contents. 232 * An external HW workaround is possible using logic gates. 233 */ 234 || state == PM_SUSPEND_MEM 235#endif 236 ); 237} 238 239/* 240 * bfin_pm_enter - Actually enter a sleep state. 241 * @state: State we're entering. 242 * 243 */ 244static int bfin_pm_enter(suspend_state_t state) 245{ 246 switch (state) { 247 case PM_SUSPEND_STANDBY: 248 bfin_pm_suspend_standby_enter(); 249 break; 250 case PM_SUSPEND_MEM: 251 bfin_pm_suspend_mem_enter(); 252 break; 253 default: 254 return -EINVAL; 255 } 256 257 return 0; 258} 259 260#ifdef CONFIG_BFIN_PM_WAKEUP_TIME_BENCH 261void bfin_pm_end(void) 262{ 263 u32 cycle, cycle2; 264 u64 usec64; 265 u32 usec; 266 267 __asm__ __volatile__ ( 268 "1: %0 = CYCLES2\n" 269 "%1 = CYCLES\n" 270 "%2 = CYCLES2\n" 271 "CC = %2 == %0\n" 272 "if ! CC jump 1b\n" 273 : "=d,a" (cycle2), "=d,a" (cycle), "=d,a" (usec) : : "CC" 274 ); 275 276 usec64 = ((u64)cycle2 << 32) + cycle; 277 do_div(usec64, get_cclk() / USEC_PER_SEC); 278 usec = usec64; 279 if (usec == 0) 280 usec = 1; 281 282 pr_info("PM: resume of kernel completes after %ld msec %03ld usec\n", 283 usec / USEC_PER_MSEC, usec % USEC_PER_MSEC); 284} 285#endif 286 287static const struct platform_suspend_ops bfin_pm_ops = { 288 .enter = bfin_pm_enter, 289 .valid = bfin_pm_valid, 290#ifdef CONFIG_BFIN_PM_WAKEUP_TIME_BENCH 291 .end = bfin_pm_end, 292#endif 293}; 294 295static int __init bfin_pm_init(void) 296{ 297 suspend_set_ops(&bfin_pm_ops); 298 return 0; 299} 300 301__initcall(bfin_pm_init); 302