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