linux/arch/blackfin/mach-common/pm.c
<<
>>
Prefs
   1/*
   2 * File:         arch/blackfin/mach-common/pm.c
   3 * Based on:     arm/mach-omap/pm.c
   4 * Author:       Cliff Brake <cbrake@accelent.com> Copyright (c) 2001
   5 *
   6 * Created:      2001
   7 * Description:  Blackfin power management
   8 *
   9 * Modified:     Nicolas Pitre - PXA250 support
  10 *                Copyright (c) 2002 Monta Vista Software, Inc.
  11 *               David Singleton - OMAP1510
  12 *                Copyright (c) 2002 Monta Vista Software, Inc.
  13 *               Dirk Behme <dirk.behme@de.bosch.com> - OMAP1510/1610
  14 *                Copyright 2004
  15 *               Copyright 2004-2008 Analog Devices Inc.
  16 *
  17 * Bugs:         Enter bugs at http://blackfin.uclinux.org/
  18 *
  19 * This program is free software; you can redistribute it and/or modify
  20 * it under the terms of the GNU General Public License as published by
  21 * the Free Software Foundation; either version 2 of the License, or
  22 * (at your option) any later version.
  23 *
  24 * This program is distributed in the hope that it will be useful,
  25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27 * GNU General Public License for more details.
  28 *
  29 * You should have received a copy of the GNU General Public License
  30 * along with this program; if not, see the file COPYING, or write
  31 * to the Free Software Foundation, Inc.,
  32 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  33 */
  34
  35#include <linux/suspend.h>
  36#include <linux/sched.h>
  37#include <linux/proc_fs.h>
  38#include <linux/io.h>
  39#include <linux/irq.h>
  40
  41#include <asm/gpio.h>
  42#include <asm/dma.h>
  43#include <asm/dpmc.h>
  44
  45#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_H
  46#define WAKEUP_TYPE     PM_WAKE_HIGH
  47#endif
  48
  49#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_L
  50#define WAKEUP_TYPE     PM_WAKE_LOW
  51#endif
  52
  53#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_F
  54#define WAKEUP_TYPE     PM_WAKE_FALLING
  55#endif
  56
  57#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_R
  58#define WAKEUP_TYPE     PM_WAKE_RISING
  59#endif
  60
  61#ifdef CONFIG_PM_WAKEUP_GPIO_POLAR_EDGE_B
  62#define WAKEUP_TYPE     PM_WAKE_BOTH_EDGES
  63#endif
  64
  65
  66void bfin_pm_suspend_standby_enter(void)
  67{
  68        unsigned long flags;
  69
  70#ifdef CONFIG_PM_WAKEUP_BY_GPIO
  71        gpio_pm_wakeup_request(CONFIG_PM_WAKEUP_GPIO_NUMBER, WAKEUP_TYPE);
  72#endif
  73
  74        local_irq_save(flags);
  75        bfin_pm_standby_setup();
  76
  77#ifdef CONFIG_PM_BFIN_SLEEP_DEEPER
  78        sleep_deeper(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]);
  79#else
  80        sleep_mode(bfin_sic_iwr[0], bfin_sic_iwr[1], bfin_sic_iwr[2]);
  81#endif
  82
  83        bfin_pm_standby_restore();
  84
  85#if defined(CONFIG_BF54x) || defined(CONFIG_BF52x)  || defined(CONFIG_BF561)
  86        bfin_write_SIC_IWR0(IWR_DISABLE_ALL);
  87#if defined(CONFIG_BF52x)
  88        /* BF52x system reset does not properly reset SIC_IWR1 which
  89         * will screw up the bootrom as it relies on MDMA0/1 waking it
  90         * up from IDLE instructions.  See this report for more info:
  91         * http://blackfin.uclinux.org/gf/tracker/4323
  92         */
  93        bfin_write_SIC_IWR1(IWR_ENABLE(10) | IWR_ENABLE(11));
  94#else
  95        bfin_write_SIC_IWR1(IWR_DISABLE_ALL);
  96#endif
  97# ifdef CONFIG_BF54x
  98        bfin_write_SIC_IWR2(IWR_DISABLE_ALL);
  99# endif
 100#else
 101        bfin_write_SIC_IWR(IWR_DISABLE_ALL);
 102#endif
 103
 104        local_irq_restore(flags);
 105}
 106
 107int bf53x_suspend_l1_mem(unsigned char *memptr)
 108{
 109        dma_memcpy(memptr, (const void *) L1_CODE_START, L1_CODE_LENGTH);
 110        dma_memcpy(memptr + L1_CODE_LENGTH, (const void *) L1_DATA_A_START,
 111                        L1_DATA_A_LENGTH);
 112        dma_memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH,
 113                        (const void *) L1_DATA_B_START, L1_DATA_B_LENGTH);
 114        memcpy(memptr + L1_CODE_LENGTH + L1_DATA_A_LENGTH +
 115                        L1_DATA_B_LENGTH, (const void *) L1_SCRATCH_START,
 116                        L1_SCRATCH_LENGTH);
 117
 118        return 0;
 119}
 120
 121int bf53x_resume_l1_mem(unsigned char *memptr)
 122{
 123        dma_memcpy((void *) L1_CODE_START, memptr, L1_CODE_LENGTH);
 124        dma_memcpy((void *) L1_DATA_A_START, memptr + L1_CODE_LENGTH,
 125                        L1_DATA_A_LENGTH);
 126        dma_memcpy((void *) L1_DATA_B_START, memptr + L1_CODE_LENGTH +
 127                        L1_DATA_A_LENGTH, L1_DATA_B_LENGTH);
 128        memcpy((void *) L1_SCRATCH_START, memptr + L1_CODE_LENGTH +
 129                        L1_DATA_A_LENGTH + L1_DATA_B_LENGTH, L1_SCRATCH_LENGTH);
 130
 131        return 0;
 132}
 133
 134#ifdef CONFIG_BFIN_WB
 135static void flushinv_all_dcache(void)
 136{
 137        u32 way, bank, subbank, set;
 138        u32 status, addr;
 139        u32 dmem_ctl = bfin_read_DMEM_CONTROL();
 140
 141        for (bank = 0; bank < 2; ++bank) {
 142                if (!(dmem_ctl & (1 << (DMC1_P - bank))))
 143                        continue;
 144
 145                for (way = 0; way < 2; ++way)
 146                        for (subbank = 0; subbank < 4; ++subbank)
 147                                for (set = 0; set < 64; ++set) {
 148
 149                                        bfin_write_DTEST_COMMAND(
 150                                                way << 26 |
 151                                                bank << 23 |
 152                                                subbank << 16 |
 153                                                set << 5
 154                                        );
 155                                        CSYNC();
 156                                        status = bfin_read_DTEST_DATA0();
 157
 158                                        /* only worry about valid/dirty entries */
 159                                        if ((status & 0x3) != 0x3)
 160                                                continue;
 161
 162                                        /* construct the address using the tag */
 163                                        addr = (status & 0xFFFFC800) | (subbank << 12) | (set << 5);
 164
 165                                        /* flush it */
 166                                        __asm__ __volatile__("FLUSHINV[%0];" : : "a"(addr));
 167                                }
 168        }
 169}
 170#endif
 171
 172static inline void dcache_disable(void)
 173{
 174#ifdef CONFIG_BFIN_DCACHE
 175        unsigned long ctrl;
 176
 177#ifdef CONFIG_BFIN_WB
 178        flushinv_all_dcache();
 179#endif
 180        SSYNC();
 181        ctrl = bfin_read_DMEM_CONTROL();
 182        ctrl &= ~ENDCPLB;
 183        bfin_write_DMEM_CONTROL(ctrl);
 184        SSYNC();
 185#endif
 186}
 187
 188static inline void dcache_enable(void)
 189{
 190#ifdef CONFIG_BFIN_DCACHE
 191        unsigned long ctrl;
 192        SSYNC();
 193        ctrl = bfin_read_DMEM_CONTROL();
 194        ctrl |= ENDCPLB;
 195        bfin_write_DMEM_CONTROL(ctrl);
 196        SSYNC();
 197#endif
 198}
 199
 200static inline void icache_disable(void)
 201{
 202#ifdef CONFIG_BFIN_ICACHE
 203        unsigned long ctrl;
 204        SSYNC();
 205        ctrl = bfin_read_IMEM_CONTROL();
 206        ctrl &= ~ENICPLB;
 207        bfin_write_IMEM_CONTROL(ctrl);
 208        SSYNC();
 209#endif
 210}
 211
 212static inline void icache_enable(void)
 213{
 214#ifdef CONFIG_BFIN_ICACHE
 215        unsigned long ctrl;
 216        SSYNC();
 217        ctrl = bfin_read_IMEM_CONTROL();
 218        ctrl |= ENICPLB;
 219        bfin_write_IMEM_CONTROL(ctrl);
 220        SSYNC();
 221#endif
 222}
 223
 224int bfin_pm_suspend_mem_enter(void)
 225{
 226        unsigned long flags;
 227        int wakeup, ret;
 228
 229        unsigned char *memptr = kmalloc(L1_CODE_LENGTH + L1_DATA_A_LENGTH
 230                                         + L1_DATA_B_LENGTH + L1_SCRATCH_LENGTH,
 231                                          GFP_KERNEL);
 232
 233        if (memptr == NULL) {
 234                panic("bf53x_suspend_l1_mem malloc failed");
 235                return -ENOMEM;
 236        }
 237
 238        wakeup = bfin_read_VR_CTL() & ~FREQ;
 239        wakeup |= SCKELOW;
 240
 241#ifdef CONFIG_PM_BFIN_WAKE_PH6
 242        wakeup |= PHYWE;
 243#endif
 244#ifdef CONFIG_PM_BFIN_WAKE_GP
 245        wakeup |= GPWE;
 246#endif
 247
 248        local_irq_save(flags);
 249
 250        ret = blackfin_dma_suspend();
 251
 252        if (ret) {
 253                local_irq_restore(flags);
 254                kfree(memptr);
 255                return ret;
 256        }
 257
 258        bfin_gpio_pm_hibernate_suspend();
 259
 260        dcache_disable();
 261        icache_disable();
 262        bf53x_suspend_l1_mem(memptr);
 263
 264        do_hibernate(wakeup | vr_wakeup);       /* Goodbye */
 265
 266        bf53x_resume_l1_mem(memptr);
 267
 268        icache_enable();
 269        dcache_enable();
 270
 271        bfin_gpio_pm_hibernate_restore();
 272        blackfin_dma_resume();
 273
 274        local_irq_restore(flags);
 275        kfree(memptr);
 276
 277        return 0;
 278}
 279
 280/*
 281 *      bfin_pm_valid - Tell the PM core that we only support the standby sleep
 282 *                      state
 283 *      @state:         suspend state we're checking.
 284 *
 285 */
 286static int bfin_pm_valid(suspend_state_t state)
 287{
 288        return (state == PM_SUSPEND_STANDBY
 289#ifndef BF533_FAMILY
 290        /*
 291         * On BF533/2/1:
 292         * If we enter Hibernate the SCKE Pin is driven Low,
 293         * so that the SDRAM enters Self Refresh Mode.
 294         * However when the reset sequence that follows hibernate
 295         * state is executed, SCKE is driven High, taking the
 296         * SDRAM out of Self Refresh.
 297         *
 298         * If you reconfigure and access the SDRAM "very quickly",
 299         * you are likely to avoid errors, otherwise the SDRAM
 300         * start losing its contents.
 301         * An external HW workaround is possible using logic gates.
 302         */
 303        || state == PM_SUSPEND_MEM
 304#endif
 305        );
 306}
 307
 308/*
 309 *      bfin_pm_enter - Actually enter a sleep state.
 310 *      @state:         State we're entering.
 311 *
 312 */
 313static int bfin_pm_enter(suspend_state_t state)
 314{
 315        switch (state) {
 316        case PM_SUSPEND_STANDBY:
 317                bfin_pm_suspend_standby_enter();
 318                break;
 319        case PM_SUSPEND_MEM:
 320                bfin_pm_suspend_mem_enter();
 321                break;
 322        default:
 323                return -EINVAL;
 324        }
 325
 326        return 0;
 327}
 328
 329struct platform_suspend_ops bfin_pm_ops = {
 330        .enter = bfin_pm_enter,
 331        .valid  = bfin_pm_valid,
 332};
 333
 334static int __init bfin_pm_init(void)
 335{
 336        suspend_set_ops(&bfin_pm_ops);
 337        return 0;
 338}
 339
 340__initcall(bfin_pm_init);
 341