linux/arch/arm/plat-s3c24xx/pm.c
<<
>>
Prefs
   1/* linux/arch/arm/plat-s3c24xx/pm.c
   2 *
   3 * Copyright (c) 2004,2006 Simtec Electronics
   4 *      Ben Dooks <ben@simtec.co.uk>
   5 *
   6 * S3C24XX Power Manager (Suspend-To-RAM) support
   7 *
   8 * See Documentation/arm/Samsung-S3C24XX/Suspend.txt for more information
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  23 *
  24 * Parts based on arch/arm/mach-pxa/pm.c
  25 *
  26 * Thanks to Dimitry Andric for debugging
  27*/
  28
  29#include <linux/init.h>
  30#include <linux/suspend.h>
  31#include <linux/errno.h>
  32#include <linux/time.h>
  33#include <linux/interrupt.h>
  34#include <linux/crc32.h>
  35#include <linux/ioport.h>
  36#include <linux/delay.h>
  37#include <linux/serial_core.h>
  38#include <linux/io.h>
  39
  40#include <asm/cacheflush.h>
  41#include <mach/hardware.h>
  42
  43#include <plat/regs-serial.h>
  44#include <mach/regs-clock.h>
  45#include <mach/regs-gpio.h>
  46#include <mach/regs-mem.h>
  47#include <mach/regs-irq.h>
  48
  49#include <asm/mach/time.h>
  50
  51#include <plat/pm.h>
  52
  53/* for external use */
  54
  55unsigned long s3c_pm_flags;
  56
  57#define PFX "s3c24xx-pm: "
  58
  59static struct sleep_save core_save[] = {
  60        SAVE_ITEM(S3C2410_LOCKTIME),
  61        SAVE_ITEM(S3C2410_CLKCON),
  62
  63        /* we restore the timings here, with the proviso that the board
  64         * brings the system up in an slower, or equal frequency setting
  65         * to the original system.
  66         *
  67         * if we cannot guarantee this, then things are going to go very
  68         * wrong here, as we modify the refresh and both pll settings.
  69         */
  70
  71        SAVE_ITEM(S3C2410_BWSCON),
  72        SAVE_ITEM(S3C2410_BANKCON0),
  73        SAVE_ITEM(S3C2410_BANKCON1),
  74        SAVE_ITEM(S3C2410_BANKCON2),
  75        SAVE_ITEM(S3C2410_BANKCON3),
  76        SAVE_ITEM(S3C2410_BANKCON4),
  77        SAVE_ITEM(S3C2410_BANKCON5),
  78
  79        SAVE_ITEM(S3C2410_CLKDIVN),
  80        SAVE_ITEM(S3C2410_MPLLCON),
  81        SAVE_ITEM(S3C2410_UPLLCON),
  82        SAVE_ITEM(S3C2410_CLKSLOW),
  83        SAVE_ITEM(S3C2410_REFRESH),
  84};
  85
  86static struct gpio_sleep {
  87        void __iomem    *base;
  88        unsigned int     gpcon;
  89        unsigned int     gpdat;
  90        unsigned int     gpup;
  91} gpio_save[] = {
  92        [0] = {
  93                .base   = S3C2410_GPACON,
  94        },
  95        [1] = {
  96                .base   = S3C2410_GPBCON,
  97        },
  98        [2] = {
  99                .base   = S3C2410_GPCCON,
 100        },
 101        [3] = {
 102                .base   = S3C2410_GPDCON,
 103        },
 104        [4] = {
 105                .base   = S3C2410_GPECON,
 106        },
 107        [5] = {
 108                .base   = S3C2410_GPFCON,
 109        },
 110        [6] = {
 111                .base   = S3C2410_GPGCON,
 112        },
 113        [7] = {
 114                .base   = S3C2410_GPHCON,
 115        },
 116};
 117
 118static struct sleep_save misc_save[] = {
 119        SAVE_ITEM(S3C2410_DCLKCON),
 120};
 121
 122#ifdef CONFIG_S3C2410_PM_DEBUG
 123
 124#define SAVE_UART(va) \
 125        SAVE_ITEM((va) + S3C2410_ULCON), \
 126        SAVE_ITEM((va) + S3C2410_UCON), \
 127        SAVE_ITEM((va) + S3C2410_UFCON), \
 128        SAVE_ITEM((va) + S3C2410_UMCON), \
 129        SAVE_ITEM((va) + S3C2410_UBRDIV)
 130
 131static struct sleep_save uart_save[] = {
 132        SAVE_UART(S3C24XX_VA_UART0),
 133        SAVE_UART(S3C24XX_VA_UART1),
 134#ifndef CONFIG_CPU_S3C2400
 135        SAVE_UART(S3C24XX_VA_UART2),
 136#endif
 137};
 138
 139/* debug
 140 *
 141 * we send the debug to printascii() to allow it to be seen if the
 142 * system never wakes up from the sleep
 143*/
 144
 145extern void printascii(const char *);
 146
 147void pm_dbg(const char *fmt, ...)
 148{
 149        va_list va;
 150        char buff[256];
 151
 152        va_start(va, fmt);
 153        vsprintf(buff, fmt, va);
 154        va_end(va);
 155
 156        printascii(buff);
 157}
 158
 159static void s3c2410_pm_debug_init(void)
 160{
 161        unsigned long tmp = __raw_readl(S3C2410_CLKCON);
 162
 163        /* re-start uart clocks */
 164        tmp |= S3C2410_CLKCON_UART0;
 165        tmp |= S3C2410_CLKCON_UART1;
 166        tmp |= S3C2410_CLKCON_UART2;
 167
 168        __raw_writel(tmp, S3C2410_CLKCON);
 169        udelay(10);
 170}
 171
 172#define DBG(fmt...) pm_dbg(fmt)
 173#else
 174#define DBG(fmt...) printk(KERN_DEBUG fmt)
 175
 176#define s3c2410_pm_debug_init() do { } while(0)
 177
 178static struct sleep_save uart_save[] = {};
 179#endif
 180
 181#if defined(CONFIG_S3C2410_PM_CHECK) && CONFIG_S3C2410_PM_CHECK_CHUNKSIZE != 0
 182
 183/* suspend checking code...
 184 *
 185 * this next area does a set of crc checks over all the installed
 186 * memory, so the system can verify if the resume was ok.
 187 *
 188 * CONFIG_S3C2410_PM_CHECK_CHUNKSIZE defines the block-size for the CRC,
 189 * increasing it will mean that the area corrupted will be less easy to spot,
 190 * and reducing the size will cause the CRC save area to grow
 191*/
 192
 193#define CHECK_CHUNKSIZE (CONFIG_S3C2410_PM_CHECK_CHUNKSIZE * 1024)
 194
 195static u32 crc_size;    /* size needed for the crc block */
 196static u32 *crcs;       /* allocated over suspend/resume */
 197
 198typedef u32 *(run_fn_t)(struct resource *ptr, u32 *arg);
 199
 200/* s3c2410_pm_run_res
 201 *
 202 * go thorugh the given resource list, and look for system ram
 203*/
 204
 205static void s3c2410_pm_run_res(struct resource *ptr, run_fn_t fn, u32 *arg)
 206{
 207        while (ptr != NULL) {
 208                if (ptr->child != NULL)
 209                        s3c2410_pm_run_res(ptr->child, fn, arg);
 210
 211                if ((ptr->flags & IORESOURCE_MEM) &&
 212                    strcmp(ptr->name, "System RAM") == 0) {
 213                        DBG("Found system RAM at %08lx..%08lx\n",
 214                            ptr->start, ptr->end);
 215                        arg = (fn)(ptr, arg);
 216                }
 217
 218                ptr = ptr->sibling;
 219        }
 220}
 221
 222static void s3c2410_pm_run_sysram(run_fn_t fn, u32 *arg)
 223{
 224        s3c2410_pm_run_res(&iomem_resource, fn, arg);
 225}
 226
 227static u32 *s3c2410_pm_countram(struct resource *res, u32 *val)
 228{
 229        u32 size = (u32)(res->end - res->start)+1;
 230
 231        size += CHECK_CHUNKSIZE-1;
 232        size /= CHECK_CHUNKSIZE;
 233
 234        DBG("Area %08lx..%08lx, %d blocks\n", res->start, res->end, size);
 235
 236        *val += size * sizeof(u32);
 237        return val;
 238}
 239
 240/* s3c2410_pm_prepare_check
 241 *
 242 * prepare the necessary information for creating the CRCs. This
 243 * must be done before the final save, as it will require memory
 244 * allocating, and thus touching bits of the kernel we do not
 245 * know about.
 246*/
 247
 248static void s3c2410_pm_check_prepare(void)
 249{
 250        crc_size = 0;
 251
 252        s3c2410_pm_run_sysram(s3c2410_pm_countram, &crc_size);
 253
 254        DBG("s3c2410_pm_prepare_check: %u checks needed\n", crc_size);
 255
 256        crcs = kmalloc(crc_size+4, GFP_KERNEL);
 257        if (crcs == NULL)
 258                printk(KERN_ERR "Cannot allocated CRC save area\n");
 259}
 260
 261static u32 *s3c2410_pm_makecheck(struct resource *res, u32 *val)
 262{
 263        unsigned long addr, left;
 264
 265        for (addr = res->start; addr < res->end;
 266             addr += CHECK_CHUNKSIZE) {
 267                left = res->end - addr;
 268
 269                if (left > CHECK_CHUNKSIZE)
 270                        left = CHECK_CHUNKSIZE;
 271
 272                *val = crc32_le(~0, phys_to_virt(addr), left);
 273                val++;
 274        }
 275
 276        return val;
 277}
 278
 279/* s3c2410_pm_check_store
 280 *
 281 * compute the CRC values for the memory blocks before the final
 282 * sleep.
 283*/
 284
 285static void s3c2410_pm_check_store(void)
 286{
 287        if (crcs != NULL)
 288                s3c2410_pm_run_sysram(s3c2410_pm_makecheck, crcs);
 289}
 290
 291/* in_region
 292 *
 293 * return TRUE if the area defined by ptr..ptr+size contatins the
 294 * what..what+whatsz
 295*/
 296
 297static inline int in_region(void *ptr, int size, void *what, size_t whatsz)
 298{
 299        if ((what+whatsz) < ptr)
 300                return 0;
 301
 302        if (what > (ptr+size))
 303                return 0;
 304
 305        return 1;
 306}
 307
 308static u32 *s3c2410_pm_runcheck(struct resource *res, u32 *val)
 309{
 310        void *save_at = phys_to_virt(s3c2410_sleep_save_phys);
 311        unsigned long addr;
 312        unsigned long left;
 313        void *ptr;
 314        u32 calc;
 315
 316        for (addr = res->start; addr < res->end;
 317             addr += CHECK_CHUNKSIZE) {
 318                left = res->end - addr;
 319
 320                if (left > CHECK_CHUNKSIZE)
 321                        left = CHECK_CHUNKSIZE;
 322
 323                ptr = phys_to_virt(addr);
 324
 325                if (in_region(ptr, left, crcs, crc_size)) {
 326                        DBG("skipping %08lx, has crc block in\n", addr);
 327                        goto skip_check;
 328                }
 329
 330                if (in_region(ptr, left, save_at, 32*4 )) {
 331                        DBG("skipping %08lx, has save block in\n", addr);
 332                        goto skip_check;
 333                }
 334
 335                /* calculate and check the checksum */
 336
 337                calc = crc32_le(~0, ptr, left);
 338                if (calc != *val) {
 339                        printk(KERN_ERR PFX "Restore CRC error at "
 340                               "%08lx (%08x vs %08x)\n", addr, calc, *val);
 341
 342                        DBG("Restore CRC error at %08lx (%08x vs %08x)\n",
 343                            addr, calc, *val);
 344                }
 345
 346        skip_check:
 347                val++;
 348        }
 349
 350        return val;
 351}
 352
 353/* s3c2410_pm_check_restore
 354 *
 355 * check the CRCs after the restore event and free the memory used
 356 * to hold them
 357*/
 358
 359static void s3c2410_pm_check_restore(void)
 360{
 361        if (crcs != NULL) {
 362                s3c2410_pm_run_sysram(s3c2410_pm_runcheck, crcs);
 363                kfree(crcs);
 364                crcs = NULL;
 365        }
 366}
 367
 368#else
 369
 370#define s3c2410_pm_check_prepare() do { } while(0)
 371#define s3c2410_pm_check_restore() do { } while(0)
 372#define s3c2410_pm_check_store()   do { } while(0)
 373#endif
 374
 375/* helper functions to save and restore register state */
 376
 377void s3c2410_pm_do_save(struct sleep_save *ptr, int count)
 378{
 379        for (; count > 0; count--, ptr++) {
 380                ptr->val = __raw_readl(ptr->reg);
 381                DBG("saved %p value %08lx\n", ptr->reg, ptr->val);
 382        }
 383}
 384
 385/* s3c2410_pm_do_restore
 386 *
 387 * restore the system from the given list of saved registers
 388 *
 389 * Note, we do not use DBG() in here, as the system may not have
 390 * restore the UARTs state yet
 391*/
 392
 393void s3c2410_pm_do_restore(struct sleep_save *ptr, int count)
 394{
 395        for (; count > 0; count--, ptr++) {
 396                printk(KERN_DEBUG "restore %p (restore %08lx, was %08x)\n",
 397                       ptr->reg, ptr->val, __raw_readl(ptr->reg));
 398
 399                __raw_writel(ptr->val, ptr->reg);
 400        }
 401}
 402
 403/* s3c2410_pm_do_restore_core
 404 *
 405 * similar to s3c2410_pm_do_restore_core
 406 *
 407 * WARNING: Do not put any debug in here that may effect memory or use
 408 * peripherals, as things may be changing!
 409*/
 410
 411static void s3c2410_pm_do_restore_core(struct sleep_save *ptr, int count)
 412{
 413        for (; count > 0; count--, ptr++) {
 414                __raw_writel(ptr->val, ptr->reg);
 415        }
 416}
 417
 418/* s3c2410_pm_show_resume_irqs
 419 *
 420 * print any IRQs asserted at resume time (ie, we woke from)
 421*/
 422
 423static void s3c2410_pm_show_resume_irqs(int start, unsigned long which,
 424                                        unsigned long mask)
 425{
 426        int i;
 427
 428        which &= ~mask;
 429
 430        for (i = 0; i <= 31; i++) {
 431                if ((which) & (1L<<i)) {
 432                        DBG("IRQ %d asserted at resume\n", start+i);
 433                }
 434        }
 435}
 436
 437/* s3c2410_pm_check_resume_pin
 438 *
 439 * check to see if the pin is configured correctly for sleep mode, and
 440 * make any necessary adjustments if it is not
 441*/
 442
 443static void s3c2410_pm_check_resume_pin(unsigned int pin, unsigned int irqoffs)
 444{
 445        unsigned long irqstate;
 446        unsigned long pinstate;
 447        int irq = s3c2410_gpio_getirq(pin);
 448
 449        if (irqoffs < 4)
 450                irqstate = s3c_irqwake_intmask & (1L<<irqoffs);
 451        else
 452                irqstate = s3c_irqwake_eintmask & (1L<<irqoffs);
 453
 454        pinstate = s3c2410_gpio_getcfg(pin);
 455
 456        if (!irqstate) {
 457                if (pinstate == S3C2410_GPIO_IRQ)
 458                        DBG("Leaving IRQ %d (pin %d) enabled\n", irq, pin);
 459        } else {
 460                if (pinstate == S3C2410_GPIO_IRQ) {
 461                        DBG("Disabling IRQ %d (pin %d)\n", irq, pin);
 462                        s3c2410_gpio_cfgpin(pin, S3C2410_GPIO_INPUT);
 463                }
 464        }
 465}
 466
 467/* s3c2410_pm_configure_extint
 468 *
 469 * configure all external interrupt pins
 470*/
 471
 472static void s3c2410_pm_configure_extint(void)
 473{
 474        int pin;
 475
 476        /* for each of the external interrupts (EINT0..EINT15) we
 477         * need to check wether it is an external interrupt source,
 478         * and then configure it as an input if it is not
 479        */
 480
 481        for (pin = S3C2410_GPF0; pin <= S3C2410_GPF7; pin++) {
 482                s3c2410_pm_check_resume_pin(pin, pin - S3C2410_GPF0);
 483        }
 484
 485        for (pin = S3C2410_GPG0; pin <= S3C2410_GPG7; pin++) {
 486                s3c2410_pm_check_resume_pin(pin, (pin - S3C2410_GPG0)+8);
 487        }
 488}
 489
 490/* offsets for CON/DAT/UP registers */
 491
 492#define OFFS_CON        (S3C2410_GPACON - S3C2410_GPACON)
 493#define OFFS_DAT        (S3C2410_GPADAT - S3C2410_GPACON)
 494#define OFFS_UP         (S3C2410_GPBUP  - S3C2410_GPBCON)
 495
 496/* s3c2410_pm_save_gpios()
 497 *
 498 * Save the state of the GPIOs
 499 */
 500
 501static void s3c2410_pm_save_gpios(void)
 502{
 503        struct gpio_sleep *gps = gpio_save;
 504        unsigned int gpio;
 505
 506        for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
 507                void __iomem *base = gps->base;
 508
 509                gps->gpcon = __raw_readl(base + OFFS_CON);
 510                gps->gpdat = __raw_readl(base + OFFS_DAT);
 511
 512                if (gpio > 0)
 513                        gps->gpup = __raw_readl(base + OFFS_UP);
 514
 515        }
 516}
 517
 518/* Test whether the given masked+shifted bits of an GPIO configuration
 519 * are one of the SFN (special function) modes. */
 520
 521static inline int is_sfn(unsigned long con)
 522{
 523        return (con == 2 || con == 3);
 524}
 525
 526/* Test if the given masked+shifted GPIO configuration is an input */
 527
 528static inline int is_in(unsigned long con)
 529{
 530        return con == 0;
 531}
 532
 533/* Test if the given masked+shifted GPIO configuration is an output */
 534
 535static inline int is_out(unsigned long con)
 536{
 537        return con == 1;
 538}
 539
 540/* s3c2410_pm_restore_gpio()
 541 *
 542 * Restore one of the GPIO banks that was saved during suspend. This is
 543 * not as simple as once thought, due to the possibility of glitches
 544 * from the order that the CON and DAT registers are set in.
 545 *
 546 * The three states the pin can be are {IN,OUT,SFN} which gives us 9
 547 * combinations of changes to check. Three of these, if the pin stays
 548 * in the same configuration can be discounted. This leaves us with
 549 * the following:
 550 *
 551 * { IN => OUT }  Change DAT first
 552 * { IN => SFN }  Change CON first
 553 * { OUT => SFN } Change CON first, so new data will not glitch
 554 * { OUT => IN }  Change CON first, so new data will not glitch
 555 * { SFN => IN }  Change CON first
 556 * { SFN => OUT } Change DAT first, so new data will not glitch [1]
 557 *
 558 * We do not currently deal with the UP registers as these control
 559 * weak resistors, so a small delay in change should not need to bring
 560 * these into the calculations.
 561 *
 562 * [1] this assumes that writing to a pin DAT whilst in SFN will set the
 563 *     state for when it is next output.
 564 */
 565
 566static void s3c2410_pm_restore_gpio(int index, struct gpio_sleep *gps)
 567{
 568        void __iomem *base = gps->base;
 569        unsigned long gps_gpcon = gps->gpcon;
 570        unsigned long gps_gpdat = gps->gpdat;
 571        unsigned long old_gpcon;
 572        unsigned long old_gpdat;
 573        unsigned long old_gpup = 0x0;
 574        unsigned long gpcon;
 575        int nr;
 576
 577        old_gpcon = __raw_readl(base + OFFS_CON);
 578        old_gpdat = __raw_readl(base + OFFS_DAT);
 579
 580        if (base == S3C2410_GPACON) {
 581                /* GPACON only has one bit per control / data and no PULLUPs.
 582                 * GPACON[x] = 0 => Output, 1 => SFN */
 583
 584                /* first set all SFN bits to SFN */
 585
 586                gpcon = old_gpcon | gps->gpcon;
 587                __raw_writel(gpcon, base + OFFS_CON);
 588
 589                /* now set all the other bits */
 590
 591                __raw_writel(gps_gpdat, base + OFFS_DAT);
 592                __raw_writel(gps_gpcon, base + OFFS_CON);
 593        } else {
 594                unsigned long old, new, mask;
 595                unsigned long change_mask = 0x0;
 596
 597                old_gpup = __raw_readl(base + OFFS_UP);
 598
 599                /* Create a change_mask of all the items that need to have
 600                 * their CON value changed before their DAT value, so that
 601                 * we minimise the work between the two settings.
 602                 */
 603
 604                for (nr = 0, mask = 0x03; nr < 32; nr += 2, mask <<= 2) {
 605                        old = (old_gpcon & mask) >> nr;
 606                        new = (gps_gpcon & mask) >> nr;
 607
 608                        /* If there is no change, then skip */
 609
 610                        if (old == new)
 611                                continue;
 612
 613                        /* If both are special function, then skip */
 614
 615                        if (is_sfn(old) && is_sfn(new))
 616                                continue;
 617
 618                        /* Change is IN => OUT, do not change now */
 619
 620                        if (is_in(old) && is_out(new))
 621                                continue;
 622
 623                        /* Change is SFN => OUT, do not change now */
 624
 625                        if (is_sfn(old) && is_out(new))
 626                                continue;
 627
 628                        /* We should now be at the case of IN=>SFN,
 629                         * OUT=>SFN, OUT=>IN, SFN=>IN. */
 630
 631                        change_mask |= mask;
 632                }
 633
 634                /* Write the new CON settings */
 635
 636                gpcon = old_gpcon & ~change_mask;
 637                gpcon |= gps_gpcon & change_mask;
 638
 639                __raw_writel(gpcon, base + OFFS_CON);
 640
 641                /* Now change any items that require DAT,CON */
 642
 643                __raw_writel(gps_gpdat, base + OFFS_DAT);
 644                __raw_writel(gps_gpcon, base + OFFS_CON);
 645                __raw_writel(gps->gpup, base + OFFS_UP);
 646        }
 647
 648        DBG("GPIO[%d] CON %08lx => %08lx, DAT %08lx => %08lx\n",
 649            index, old_gpcon, gps_gpcon, old_gpdat, gps_gpdat);
 650}
 651
 652
 653/** s3c2410_pm_restore_gpios()
 654 *
 655 * Restore the state of the GPIOs
 656 */
 657
 658static void s3c2410_pm_restore_gpios(void)
 659{
 660        struct gpio_sleep *gps = gpio_save;
 661        int gpio;
 662
 663        for (gpio = 0; gpio < ARRAY_SIZE(gpio_save); gpio++, gps++) {
 664                s3c2410_pm_restore_gpio(gpio, gps);
 665        }
 666}
 667
 668void (*pm_cpu_prep)(void);
 669void (*pm_cpu_sleep)(void);
 670
 671#define any_allowed(mask, allow) (((mask) & (allow)) != (allow))
 672
 673/* s3c2410_pm_enter
 674 *
 675 * central control for sleep/resume process
 676*/
 677
 678static int s3c2410_pm_enter(suspend_state_t state)
 679{
 680        unsigned long regs_save[16];
 681
 682        /* ensure the debug is initialised (if enabled) */
 683
 684        s3c2410_pm_debug_init();
 685
 686        DBG("s3c2410_pm_enter(%d)\n", state);
 687
 688        if (pm_cpu_prep == NULL || pm_cpu_sleep == NULL) {
 689                printk(KERN_ERR PFX "error: no cpu sleep functions set\n");
 690                return -EINVAL;
 691        }
 692
 693        /* check if we have anything to wake-up with... bad things seem
 694         * to happen if you suspend with no wakeup (system will often
 695         * require a full power-cycle)
 696        */
 697
 698        if (!any_allowed(s3c_irqwake_intmask, s3c_irqwake_intallow) &&
 699            !any_allowed(s3c_irqwake_eintmask, s3c_irqwake_eintallow)) {
 700                printk(KERN_ERR PFX "No sources enabled for wake-up!\n");
 701                printk(KERN_ERR PFX "Aborting sleep\n");
 702                return -EINVAL;
 703        }
 704
 705        /* prepare check area if configured */
 706
 707        s3c2410_pm_check_prepare();
 708
 709        /* store the physical address of the register recovery block */
 710
 711        s3c2410_sleep_save_phys = virt_to_phys(regs_save);
 712
 713        DBG("s3c2410_sleep_save_phys=0x%08lx\n", s3c2410_sleep_save_phys);
 714
 715        /* save all necessary core registers not covered by the drivers */
 716
 717        s3c2410_pm_save_gpios();
 718        s3c2410_pm_do_save(misc_save, ARRAY_SIZE(misc_save));
 719        s3c2410_pm_do_save(core_save, ARRAY_SIZE(core_save));
 720        s3c2410_pm_do_save(uart_save, ARRAY_SIZE(uart_save));
 721
 722        /* set the irq configuration for wake */
 723
 724        s3c2410_pm_configure_extint();
 725
 726        DBG("sleep: irq wakeup masks: %08lx,%08lx\n",
 727            s3c_irqwake_intmask, s3c_irqwake_eintmask);
 728
 729        __raw_writel(s3c_irqwake_intmask, S3C2410_INTMSK);
 730        __raw_writel(s3c_irqwake_eintmask, S3C2410_EINTMASK);
 731
 732        /* ack any outstanding external interrupts before we go to sleep */
 733
 734        __raw_writel(__raw_readl(S3C2410_EINTPEND), S3C2410_EINTPEND);
 735        __raw_writel(__raw_readl(S3C2410_INTPND), S3C2410_INTPND);
 736        __raw_writel(__raw_readl(S3C2410_SRCPND), S3C2410_SRCPND);
 737
 738        /* call cpu specific preparation */
 739
 740        pm_cpu_prep();
 741
 742        /* flush cache back to ram */
 743
 744        flush_cache_all();
 745
 746        s3c2410_pm_check_store();
 747
 748        /* send the cpu to sleep... */
 749
 750        __raw_writel(0x00, S3C2410_CLKCON);  /* turn off clocks over sleep */
 751
 752        /* s3c2410_cpu_save will also act as our return point from when
 753         * we resume as it saves its own register state, so use the return
 754         * code to differentiate return from save and return from sleep */
 755
 756        if (s3c2410_cpu_save(regs_save) == 0) {
 757                flush_cache_all();
 758                pm_cpu_sleep();
 759        }
 760
 761        /* restore the cpu state */
 762
 763        cpu_init();
 764
 765        /* restore the system state */
 766
 767        s3c2410_pm_do_restore_core(core_save, ARRAY_SIZE(core_save));
 768        s3c2410_pm_do_restore(misc_save, ARRAY_SIZE(misc_save));
 769        s3c2410_pm_do_restore(uart_save, ARRAY_SIZE(uart_save));
 770        s3c2410_pm_restore_gpios();
 771
 772        s3c2410_pm_debug_init();
 773
 774        /* check what irq (if any) restored the system */
 775
 776        DBG("post sleep: IRQs 0x%08x, 0x%08x\n",
 777            __raw_readl(S3C2410_SRCPND),
 778            __raw_readl(S3C2410_EINTPEND));
 779
 780        s3c2410_pm_show_resume_irqs(IRQ_EINT0, __raw_readl(S3C2410_SRCPND),
 781                                    s3c_irqwake_intmask);
 782
 783        s3c2410_pm_show_resume_irqs(IRQ_EINT4-4, __raw_readl(S3C2410_EINTPEND),
 784                                    s3c_irqwake_eintmask);
 785
 786        DBG("post sleep, preparing to return\n");
 787
 788        s3c2410_pm_check_restore();
 789
 790        /* ok, let's return from sleep */
 791
 792        DBG("S3C2410 PM Resume (post-restore)\n");
 793        return 0;
 794}
 795
 796static struct platform_suspend_ops s3c2410_pm_ops = {
 797        .enter          = s3c2410_pm_enter,
 798        .valid          = suspend_valid_only_mem,
 799};
 800
 801/* s3c2410_pm_init
 802 *
 803 * Attach the power management functions. This should be called
 804 * from the board specific initialisation if the board supports
 805 * it.
 806*/
 807
 808int __init s3c2410_pm_init(void)
 809{
 810        printk("S3C2410 Power Management, (c) 2004 Simtec Electronics\n");
 811
 812        suspend_set_ops(&s3c2410_pm_ops);
 813        return 0;
 814}
 815
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.