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 18#include <asm/cplb.h> 19#include <asm/gpio.h> 20#include <asm/dma.h> 21#include <asm/dpmc.h> 22 23 24void bfin_pm_suspend_standby_enter(void) 25{ 26 unsigned long flags; 27 28 local_irq_save_hw(flags); 29 bfin_pm_standby_setup(); 30 31#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER 32 sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); 33#else 34 sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); 35#endif 36 37 bfin_pm_standby_restore(); 38 39#ifdef SIC_IWR0 40 bfin_write_SIC_IWR0(IWR_DISABLE_ALL); 41# ifdef SIC_IWR1 42 /* BF52x system reset does not properly reset SIC_IWR1 which 43 * will screw up the bootrom as it relies on MDMA0/1 waking it 44 * up from IDLE instructions. See this report for more info: 45 * http://blackfin.uclinux.org/gf/tracker/4323 46 */ 47 if (ANOMALY_05000435) 48 bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11)); 49 else 50 bfin_write_SIC_IWR1(IWR_DISABLE_ALL); 51# endif 52# ifdef SIC_IWR2 53 bfin_write_SIC_IWR2(IWR_DISABLE_ALL); 54# endif 55#else 56 bfin_write_SIC_IWR(IWR_DISABLE_ALL); 57#endif 58 59 local_irq_restore_hw(flags); 60} 61 62int bf53x_suspend_l1_mem(unsigned char *memptr) 63{ 64 dma_memcpy_nocache(memptr, (const void *) L1_CODE_START, 65 L1_CODE_LENGTH); 66 dma_memcpy_nocache(memptr + L1_CODE_LENGTH, 67 (const void *) L1_DATA_A_START, L1_DATA_A_LENGTH); 68 dma_memcpy_nocache(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH, 69 (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH); 70 memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH + 71 L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START, 72 L1_SCRATCH_LENGTH); 73 74 return 0; 75} 76 77int bf53x_resume_l1_mem(unsigned char *memptr) 78{ 79 dma_memcpy_nocache((void *) L1_CODE_START, memptr, L1_CODE_LENGTH); 80 dma_memcpy_nocache((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH, 81 L1_DATA_A_LENGTH); 82 dma_memcpy_nocache((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH + 83 L1_DATA_A_LENGTH, L1_DATA_B_LENGTH); 84 memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH + 85 L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH); 86 87 return 0; 88} 89 90#if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK) 91static void flushinv_all_dcache(void) 92{ 93 u32 way, bank, subbank, set; 94 u32 status, addr; 95 u32 dmem_ctl = bfin_read_DMEM_CONTROL(); 96 97 for (bank = 0; bank < 2; ++bank) { 98 if (!(dmem_ctl & (1 << (DMC1_P - bank)))) 99 continue; 100 101 for (way = 0; way < 2; ++way) 102 for (subbank = 0; subbank < 4; ++subbank) 103 for (set = 0; set < 64; ++set) { 104 105 bfin_write_DTEST_COMMAND( 106 way << 26 | 107 bank << 23 | 108 subbank << 16 | 109 set << 5 110 ); 111 CSYNC(); 112 status = bfin_read_DTEST_DATA0(); 113 114 /* only worry about valid/dirty entries */ 115 if ((status & 0x3) != 0x3) 116 continue; 117 118 /* construct the address using the tag */ 119 addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5); 120 121 /* flush it */ 122 __asm__ __volatile__("FLUSHINV[%0];" : : "a"(addr)); 123 } 124 } 125} 126#endif 127 128int bfin_pm_suspend_mem_enter(void) 129{ 130 unsigned long flags; 131 int wakeup, ret; 132 133 unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH 134 + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH, 135 GFP_KERNEL); 136 137 if (memptr == NULL) { 138 panic("bf53x_suspend_l1_mem malloc failed"); 139 return -ENOMEM; 140 } 141 142 wakeup = bfin_read_VR_CTL() & ~FREQ; 143 wakeup |= SCKELOW; 144 145#ifdef CONFIG_PM_BFIN_WAKE_PH6 146 wakeup |= PHYWE; 147#endif 148#ifdef CONFIG_PM_BFIN_WAKE_GP 149 wakeup |= GPWE; 150#endif 151 152 local_irq_save_hw(flags); 153 154 ret = blackfin_dma_suspend(); 155 156 if (ret) { 157 local_irq_restore_hw(flags); 158 kfree(memptr); 159 return ret; 160 } 161 162 bfin_gpio_pm_hibernate_suspend(); 163 164#if defined(CONFIG_BFIN_EXTMEM_WRITEBACK) || defined(CONFIG_BFIN_L2_WRITEBACK) 165 flushinv_all_dcache(); 166#endif 167 _disable_dcplb(); 168 _disable_icplb(); 169 bf53x_suspend_l1_mem(memptr); 170 171 do_hibernate(wakeup | vr_wakeup); /* See you later! */ 172 173 bf53x_resume_l1_mem(memptr); 174 175 _enable_icplb(); 176 _enable_dcplb(); 177 178 bfin_gpio_pm_hibernate_restore(); 179 blackfin_dma_resume(); 180 181 local_irq_restore_hw(flags); 182 kfree(memptr); 183 184 return 0; 185} 186 187/* 188 * bfin_pm_valid - Tell the PM core that we only support the standby sleep 189 * state 190 * @state: suspend state we're checking. 191 * 192 */ 193static int bfin_pm_valid(suspend_state_t state) 194{ 195 return (state == PM_SUSPEND_STANDBY 196#if !(defined(BF533_FAMILY) || defined(CONFIG_BF561)) 197 || state == PM_SUSPEND_MEM 198#endif 199 ); 200} 201 202/* 203 * bfin_pm_enter - Actually enter a sleep state. 204 * @state: State we're entering. 205 * 206 */ 207static int bfin_pm_enter(suspend_state_t state) 208{ 209 switch (state) { 210 case PM_SUSPEND_STANDBY: 211 bfin_pm_suspend_standby_enter(); 212 break; 213 case PM_SUSPEND_MEM: 214 bfin_pm_suspend_mem_enter(); 215 break; 216 default: 217 return -EINVAL; 218 } 219 220 return 0; 221} 222 223struct platform_suspend_ops bfin_pm_ops = { 224 .enter = bfin_pm_enter, 225 .valid = bfin_pm_valid, 226}; 227 228static int __init bfin_pm_init(void) 229{ 230 suspend_set_ops(&bfin_pm_ops); 231 return 0; 232} 233 234__initcall(bfin_pm_init); 235