diff options
Diffstat (limited to 'arch/blackfin/mach-common/pm.c')
| -rw-r--r-- | arch/blackfin/mach-common/pm.c | 225 |
1 files changed, 216 insertions, 9 deletions
diff --git a/arch/blackfin/mach-common/pm.c b/arch/blackfin/mach-common/pm.c index 0be805ca423f..4fe6a2366b13 100644 --- a/arch/blackfin/mach-common/pm.c +++ b/arch/blackfin/mach-common/pm.c | |||
| @@ -38,8 +38,9 @@ | |||
| 38 | #include <linux/io.h> | 38 | #include <linux/io.h> |
| 39 | #include <linux/irq.h> | 39 | #include <linux/irq.h> |
| 40 | 40 | ||
| 41 | #include <asm/dpmc.h> | ||
| 42 | #include <asm/gpio.h> | 41 | #include <asm/gpio.h> |
| 42 | #include <asm/dma.h> | ||
| 43 | #include <asm/dpmc.h> | ||
| 43 | 44 | ||
| 44 | #ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H | 45 | #ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H |
| 45 | #define WAKEUP_TYPE PM_WAKE_HIGH | 46 | #define WAKEUP_TYPE PM_WAKE_HIGH |
| @@ -61,16 +62,17 @@ | |||
| 61 | #define WAKEUP_TYPE PM_WAKE_BOTH_EDGES | 62 | #define WAKEUP_TYPE PM_WAKE_BOTH_EDGES |
| 62 | #endif | 63 | #endif |
| 63 | 64 | ||
| 65 | |||
| 64 | void bfin_pm_suspend_standby_enter(void) | 66 | void bfin_pm_suspend_standby_enter(void) |
| 65 | { | 67 | { |
| 68 | unsigned long flags; | ||
| 69 | |||
| 66 | #ifdef CONFIG_PM_WAKEUP_BY_GPIO | 70 | #ifdef CONFIG_PM_WAKEUP_BY_GPIO |
| 67 | gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); | 71 | gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE); |
| 68 | #endif | 72 | #endif |
| 69 | 73 | ||
| 70 | u32 flags; | ||
| 71 | |||
| 72 | local_irq_save(flags); | 74 | local_irq_save(flags); |
| 73 | bfin_pm_setup(); | 75 | bfin_pm_standby_setup(); |
| 74 | 76 | ||
| 75 | #ifdef CONFIG_PM_BFIN_SLEEP_DEEPER | 77 | #ifdef CONFIG_PM_BFIN_SLEEP_DEEPER |
| 76 | sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); | 78 | sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); |
| @@ -78,7 +80,7 @@ void bfin_pm_suspend_standby_enter(void) | |||
| 78 | sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); | 80 | sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]); |
| 79 | #endif | 81 | #endif |
| 80 | 82 | ||
| 81 | bfin_pm_restore(); | 83 | bfin_pm_standby_restore(); |
| 82 | 84 | ||
| 83 | #if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) | 85 | #if defined(CONFIG_BF54x) || defined(CONFIG_BF52x) || defined(CONFIG_BF561) |
| 84 | bfin_write_SIC_IWR0(IWR_ENABLE_ALL); | 86 | bfin_write_SIC_IWR0(IWR_ENABLE_ALL); |
| @@ -93,6 +95,195 @@ void bfin_pm_suspend_standby_enter(void) | |||
| 93 | local_irq_restore(flags); | 95 | local_irq_restore(flags); |
| 94 | } | 96 | } |
| 95 | 97 | ||
| 98 | int bf53x_suspend_l1_mem(unsigned char *memptr) | ||
| 99 | { | ||
| 100 | dma_memcpy(memptr, (const void *) L1_CODE_START, L1_CODE_LENGTH); | ||
| 101 | dma_memcpy(memptr + L1_CODE_LENGTH, (const void *) L1_DATA_A_START, | ||
| 102 | L1_DATA_A_LENGTH); | ||
| 103 | dma_memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH, | ||
| 104 | (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH); | ||
| 105 | memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH + | ||
| 106 | L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START, | ||
| 107 | L1_SCRATCH_LENGTH); | ||
| 108 | |||
| 109 | return 0; | ||
| 110 | } | ||
| 111 | |||
| 112 | int bf53x_resume_l1_mem(unsigned char *memptr) | ||
| 113 | { | ||
| 114 | dma_memcpy((void *) L1_CODE_START, memptr, L1_CODE_LENGTH); | ||
| 115 | dma_memcpy((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH, | ||
| 116 | L1_DATA_A_LENGTH); | ||
| 117 | dma_memcpy((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH + | ||
| 118 | L1_DATA_A_LENGTH, L1_DATA_B_LENGTH); | ||
| 119 | memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH + | ||
| 120 | L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH); | ||
| 121 | |||
| 122 | return 0; | ||
| 123 | } | ||
| 124 | |||
| 125 | #ifdef CONFIG_BFIN_WB | ||
| 126 | static void flushinv_all_dcache(void) | ||
| 127 | { | ||
| 128 | u32 way, bank, subbank, set; | ||
| 129 | u32 status, addr; | ||
| 130 | u32 dmem_ctl = bfin_read_DMEM_CONTROL(); | ||
| 131 | |||
| 132 | for (bank = 0; bank < 2; ++bank) { | ||
| 133 | if (!(dmem_ctl & (1 << (DMC1_P - bank)))) | ||
| 134 | continue; | ||
| 135 | |||
| 136 | for (way = 0; way < 2; ++way) | ||
| 137 | for (subbank = 0; subbank < 4; ++subbank) | ||
| 138 | for (set = 0; set < 64; ++set) { | ||
| 139 | |||
| 140 | bfin_write_DTEST_COMMAND( | ||
| 141 | way << 26 | | ||
| 142 | bank << 23 | | ||
| 143 | subbank << 16 | | ||
| 144 | set << 5 | ||
| 145 | ); | ||
| 146 | CSYNC(); | ||
| 147 | status = bfin_read_DTEST_DATA0(); | ||
| 148 | |||
| 149 | /* only worry about valid/dirty entries */ | ||
| 150 | if ((status & 0x3) != 0x3) | ||
| 151 | continue; | ||
| 152 | |||
| 153 | /* construct the address using the tag */ | ||
| 154 | addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5); | ||
| 155 | |||
| 156 | /* flush it */ | ||
| 157 | __asm__ __volatile__("FLUSHINV[%0];" : : "a"(addr)); | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | ||
| 161 | #endif | ||
| 162 | |||
| 163 | static inline void dcache_disable(void) | ||
| 164 | { | ||
| 165 | #ifdef CONFIG_BFIN_DCACHE | ||
| 166 | unsigned long ctrl; | ||
| 167 | |||
| 168 | #ifdef CONFIG_BFIN_WB | ||
| 169 | flushinv_all_dcache(); | ||
| 170 | #endif | ||
| 171 | SSYNC(); | ||
| 172 | ctrl = bfin_read_DMEM_CONTROL(); | ||
| 173 | ctrl &= ~ENDCPLB; | ||
| 174 | bfin_write_DMEM_CONTROL(ctrl); | ||
| 175 | SSYNC(); | ||
| 176 | #endif | ||
| 177 | } | ||
| 178 | |||
| 179 | static inline void dcache_enable(void) | ||
| 180 | { | ||
| 181 | #ifdef CONFIG_BFIN_DCACHE | ||
| 182 | unsigned long ctrl; | ||
| 183 | SSYNC(); | ||
| 184 | ctrl = bfin_read_DMEM_CONTROL(); | ||
| 185 | ctrl |= ENDCPLB; | ||
| 186 | bfin_write_DMEM_CONTROL(ctrl); | ||
| 187 | SSYNC(); | ||
| 188 | #endif | ||
| 189 | } | ||
| 190 | |||
| 191 | static inline void icache_disable(void) | ||
| 192 | { | ||
| 193 | #ifdef CONFIG_BFIN_ICACHE | ||
| 194 | unsigned long ctrl; | ||
| 195 | SSYNC(); | ||
| 196 | ctrl = bfin_read_IMEM_CONTROL(); | ||
| 197 | ctrl &= ~ENICPLB; | ||
| 198 | bfin_write_IMEM_CONTROL(ctrl); | ||
| 199 | SSYNC(); | ||
| 200 | #endif | ||
| 201 | } | ||
| 202 | |||
| 203 | static inline void icache_enable(void) | ||
| 204 | { | ||
| 205 | #ifdef CONFIG_BFIN_ICACHE | ||
| 206 | unsigned long ctrl; | ||
| 207 | SSYNC(); | ||
| 208 | ctrl = bfin_read_IMEM_CONTROL(); | ||
| 209 | ctrl |= ENICPLB; | ||
| 210 | bfin_write_IMEM_CONTROL(ctrl); | ||
| 211 | SSYNC(); | ||
| 212 | #endif | ||
| 213 | } | ||
| 214 | |||
| 215 | int bfin_pm_suspend_mem_enter(void) | ||
| 216 | { | ||
| 217 | unsigned long flags; | ||
| 218 | int wakeup, ret; | ||
| 219 | |||
| 220 | unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH | ||
| 221 | + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH, | ||
| 222 | GFP_KERNEL); | ||
| 223 | |||
| 224 | if (memptr == NULL) { | ||
| 225 | panic("bf53x_suspend_l1_mem malloc failed"); | ||
| 226 | return -ENOMEM; | ||
| 227 | } | ||
| 228 | |||
| 229 | wakeup = bfin_read_VR_CTL() & ~FREQ; | ||
| 230 | wakeup |= SCKELOW; | ||
| 231 | |||
| 232 | /* FIXME: merge this somehow with set_irq_wake */ | ||
| 233 | #ifdef CONFIG_PM_BFIN_WAKE_RTC | ||
| 234 | wakeup |= WAKE; | ||
| 235 | #endif | ||
| 236 | #ifdef CONFIG_PM_BFIN_WAKE_PH6 | ||
| 237 | wakeup |= PHYWE; | ||
| 238 | #endif | ||
| 239 | #ifdef CONFIG_PM_BFIN_WAKE_CAN | ||
| 240 | wakeup |= CANWE; | ||
| 241 | #endif | ||
| 242 | #ifdef CONFIG_PM_BFIN_WAKE_GP | ||
| 243 | wakeup |= GPWE; | ||
| 244 | #endif | ||
| 245 | #ifdef CONFIG_PM_BFIN_WAKE_USB | ||
| 246 | wakeup |= USBWE; | ||
| 247 | #endif | ||
| 248 | #ifdef CONFIG_PM_BFIN_WAKE_KEYPAD | ||
| 249 | wakeup |= KPADWE; | ||
| 250 | #endif | ||
| 251 | #ifdef CONFIG_PM_BFIN_WAKE_ROTARY | ||
| 252 | wakeup |= ROTWE; | ||
| 253 | #endif | ||
| 254 | |||
| 255 | local_irq_save(flags); | ||
| 256 | |||
| 257 | ret = blackfin_dma_suspend(); | ||
| 258 | |||
| 259 | if (ret) { | ||
| 260 | local_irq_restore(flags); | ||
| 261 | kfree(memptr); | ||
| 262 | return ret; | ||
| 263 | } | ||
| 264 | |||
| 265 | bfin_gpio_pm_hibernate_suspend(); | ||
| 266 | |||
| 267 | dcache_disable(); | ||
| 268 | icache_disable(); | ||
| 269 | bf53x_suspend_l1_mem(memptr); | ||
| 270 | |||
| 271 | do_hibernate(wakeup); /* Goodbye */ | ||
| 272 | |||
| 273 | bf53x_resume_l1_mem(memptr); | ||
| 274 | |||
| 275 | icache_enable(); | ||
| 276 | dcache_enable(); | ||
| 277 | |||
| 278 | bfin_gpio_pm_hibernate_restore(); | ||
| 279 | blackfin_dma_resume(); | ||
| 280 | |||
| 281 | local_irq_restore(flags); | ||
| 282 | kfree(memptr); | ||
| 283 | |||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 96 | /* | 287 | /* |
| 97 | * bfin_pm_valid - Tell the PM core that we only support the standby sleep | 288 | * bfin_pm_valid - Tell the PM core that we only support the standby sleep |
| 98 | * state | 289 | * state |
| @@ -101,7 +292,24 @@ void bfin_pm_suspend_standby_enter(void) | |||
| 101 | */ | 292 | */ |
| 102 | static int bfin_pm_valid(suspend_state_t state) | 293 | static int bfin_pm_valid(suspend_state_t state) |
| 103 | { | 294 | { |
| 104 | return (state == PM_SUSPEND_STANDBY); | 295 | return (state == PM_SUSPEND_STANDBY |
| 296 | #ifndef BF533_FAMILY | ||
| 297 | /* | ||
| 298 | * On BF533/2/1: | ||
| 299 | * If we enter Hibernate the SCKE Pin is driven Low, | ||
| 300 | * so that the SDRAM enters Self Refresh Mode. | ||
| 301 | * However when the reset sequence that follows hibernate | ||
| 302 | * state is executed, SCKE is driven High, taking the | ||
| 303 | * SDRAM out of Self Refresh. | ||
| 304 | * | ||
| 305 | * If you reconfigure and access the SDRAM "very quickly", | ||
| 306 | * you are likely to avoid errors, otherwise the SDRAM | ||
| 307 | * start losing its contents. | ||
| 308 | * An external HW workaround is possible using logic gates. | ||
| 309 | */ | ||
| 310 | || state == PM_SUSPEND_MEM | ||
| 311 | #endif | ||
| 312 | ); | ||
| 105 | } | 313 | } |
| 106 | 314 | ||
| 107 | /* | 315 | /* |
| @@ -115,10 +323,9 @@ static int bfin_pm_enter(suspend_state_t state) | |||
| 115 | case PM_SUSPEND_STANDBY: | 323 | case PM_SUSPEND_STANDBY: |
| 116 | bfin_pm_suspend_standby_enter(); | 324 | bfin_pm_suspend_standby_enter(); |
| 117 | break; | 325 | break; |
| 118 | |||
| 119 | case PM_SUSPEND_MEM: | 326 | case PM_SUSPEND_MEM: |
| 120 | return -ENOTSUPP; | 327 | bfin_pm_suspend_mem_enter(); |
| 121 | 328 | break; | |
| 122 | default: | 329 | default: |
| 123 | return -EINVAL; | 330 | return -EINVAL; |
| 124 | } | 331 | } |
