linux/arch/blackfin/mach-common/pm.c
<<
>>
Prefs
   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#include <linux/gpio.h>
  19
  20#include <asm/cplb.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