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