pm.c
来自「优龙2410linux2.6.8内核源代码」· C语言 代码 · 共 589 行
C
589 行
/* * PXA250/210 Power Management Routines * * Original code for the SA11x0: * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> * * Modified for the PXA250 by Nicolas Pitre: * Copyright (c) 2002 Monta Vista Software, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License. */#include <linux/config.h>#include <linux/init.h>#include <linux/suspend.h>#include <linux/errno.h>#include <linux/time.h>#include <asm/hardware.h>#include <asm/memory.h>#include <asm/system.h>#include <asm/leds.h>#include <asm/arch/lubbock.h>/* * Debug macros */#undef DEBUGextern void pxa_cpu_suspend(void);extern void pxa_cpu_resume(void);#define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x#define RESTORE(x) x = sleep_save[SLEEP_SAVE_##x]#define RESTORE_GPLEVEL(n) do { \ GPSR##n = sleep_save[SLEEP_SAVE_GPLR##n]; \ GPCR##n = ~sleep_save[SLEEP_SAVE_GPLR##n]; \} while (0)/* * List of global PXA peripheral registers to preserve. * More ones like CP and general purpose register values are preserved * with the stack pointer in sleep.S. */enum { SLEEP_SAVE_START = 0, SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER, SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3, SLEEP_SAVE_GPLR0, SLEEP_SAVE_GPLR1, SLEEP_SAVE_GPLR2, SLEEP_SAVE_GPDR0, SLEEP_SAVE_GPDR1, SLEEP_SAVE_GPDR2, SLEEP_SAVE_GRER0, SLEEP_SAVE_GRER1, SLEEP_SAVE_GRER2, SLEEP_SAVE_GFER0, SLEEP_SAVE_GFER1, SLEEP_SAVE_GFER2, SLEEP_SAVE_GAFR0_L, SLEEP_SAVE_GAFR1_L, SLEEP_SAVE_GAFR2_L, SLEEP_SAVE_GAFR0_U, SLEEP_SAVE_GAFR1_U, SLEEP_SAVE_GAFR2_U, SLEEP_SAVE_FFIER, SLEEP_SAVE_FFLCR, SLEEP_SAVE_FFMCR, SLEEP_SAVE_FFSPR, SLEEP_SAVE_FFISR, SLEEP_SAVE_FFDLL, SLEEP_SAVE_FFDLH, SLEEP_SAVE_BTIER, SLEEP_SAVE_BTLCR, SLEEP_SAVE_BTMCR, SLEEP_SAVE_BTSPR, SLEEP_SAVE_BTISR, SLEEP_SAVE_BTDLL, SLEEP_SAVE_BTDLH, SLEEP_SAVE_STIER, SLEEP_SAVE_STLCR, SLEEP_SAVE_STMCR, SLEEP_SAVE_STSPR, SLEEP_SAVE_STISR, SLEEP_SAVE_STDLL, SLEEP_SAVE_STDLH, SLEEP_SAVE_PWM_PWDUTY0, SLEEP_SAVE_PWM_PERVAL0, SLEEP_SAVE_PWM_CTRL0, SLEEP_SAVE_PWM_PWDUTY1, SLEEP_SAVE_PWM_PERVAL1, SLEEP_SAVE_PWM_CTRL1, SLEEP_SAVE_MECR, SLEEP_SAVE_MCMEM0, SLEEP_SAVE_MCMEM1, SLEEP_SAVE_MCATT0, SLEEP_SAVE_MCATT1, SLEEP_SAVE_MCIO0, SLEEP_SAVE_MCIO1, SLEEP_SAVE_ICMR, SLEEP_SAVE_CKEN, SLEEP_SAVE_CKSUM, SLEEP_SAVE_SIZE};int pm_do_suspend(u32 state, unsigned int resume_time){ unsigned long sleep_save[SLEEP_SAVE_SIZE]; unsigned long checksum = 0; unsigned long delta; int i; if (state != PM_SUSPEND_MEM) return -EINVAL; /* preserve current time */ delta = xtime.tv_sec - RCNR; /* * Temporary solution. This won't be necessary once * we move pxa support into the serial driver */ // Save the FF UART SAVE(FFIER); SAVE(FFLCR); SAVE(FFMCR); SAVE(FFSPR); SAVE(FFISR); FFLCR |= 0x80; SAVE(FFDLL); SAVE(FFDLH); FFLCR &= 0xef; // Save the BT UART SAVE(BTIER); SAVE(BTLCR); SAVE(BTMCR); SAVE(BTSPR); SAVE(BTISR); BTLCR |= 0x80; SAVE(BTDLL); SAVE(BTDLH); BTLCR &= 0xef; // Save the ST UART SAVE(STIER); SAVE(STLCR); SAVE(STMCR); SAVE(STSPR); SAVE(STISR); STLCR |= 0x80; SAVE(STDLL); SAVE(STDLH); STLCR &= 0xef; /* save vital registers */ SAVE(OSCR); SAVE(OSMR0); SAVE(OSMR1); SAVE(OSMR2); SAVE(OSMR3); SAVE(OIER); SAVE(GPLR0); SAVE(GPLR1); SAVE(GPLR2); SAVE(GPDR0); SAVE(GPDR1); SAVE(GPDR2); SAVE(GRER0); SAVE(GRER1); SAVE(GRER2); SAVE(GFER0); SAVE(GFER1); SAVE(GFER2); SAVE(GAFR0_L); SAVE(GAFR0_U); SAVE(GAFR1_L); SAVE(GAFR1_U); SAVE(GAFR2_L); SAVE(GAFR2_U); // Save PWM register SAVE(PWM_CTRL0); SAVE(PWM_PWDUTY0); SAVE(PWM_PERVAL0); SAVE(PWM_CTRL1); SAVE(PWM_PWDUTY1); SAVE(PWM_PERVAL1); // Save PCMCIA register SAVE(MECR); SAVE(MCMEM0); SAVE(MCMEM1); SAVE(MCATT0); SAVE(MCATT1); SAVE(MCIO0); SAVE(MCIO1); SAVE(ICMR); ICMR = 0; SAVE(CKEN); CKEN = 0; /* Note: wake up source are set up in each machine specific files */ /* clear GPIO transition detect bits */ GEDR0 = GEDR0; GEDR1 = GEDR1; GEDR2 = GEDR2; /* Clear sleep reset status */ RCSR = RCSR_SMR; if (resume_time != 0) { unsigned long rcnr;#ifndef PWER_RTC#define PWER_RTC 0x80000000#endif // Set RTC Alarm ICMR &= ~PWER_RTC; RTSR = RTSR_AL + RTSR_HZ; ICLR &= ~PWER_RTC; rcnr = xtime.tv_sec; RCNR = rcnr; rcnr += resume_time; RTAR = rcnr; PWER |= PWER_RTC; RCSR = 0xF; RTSR = RTSR_AL + RTSR_HZ + RTSR_ALE; ICIP &= ~PWER_RTC; ICMR |= PWER_RTC;#undef PWER_RTC } /* set resume return address */ PSPR = virt_to_phys(pxa_cpu_resume); /* before sleeping, calculate and save a checksum */ for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) checksum += sleep_save[i]; sleep_save[SLEEP_SAVE_CKSUM] = checksum; /* *** go zzz *** */ pxa_cpu_suspend(); /* after sleeping, validate the checksum */ checksum = 0; for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) checksum += sleep_save[i]; /* if invalid, display message and wait for a hardware reset */ if (checksum != sleep_save[SLEEP_SAVE_CKSUM]) {#ifdef CONFIG_ARCH_LUBBOCK LUB_HEXLED = 0xbadbadc5;#endif while (1); } /* ensure not to come back here if it wasn't intended */ PSPR = 0; /* restore registers */ RESTORE(GAFR0_L); RESTORE(GAFR0_U); RESTORE(GAFR1_L); RESTORE(GAFR1_U); RESTORE(GAFR2_L); RESTORE(GAFR2_U); RESTORE_GPLEVEL(0); RESTORE_GPLEVEL(1); RESTORE_GPLEVEL(2); RESTORE(GPDR0); RESTORE(GPDR1); RESTORE(GPDR2); RESTORE(GRER0); RESTORE(GRER1); RESTORE(GRER2); RESTORE(GFER0); RESTORE(GFER1); RESTORE(GFER2); PSSR = PSSR_RDH | PSSR_PH; RESTORE(OSMR0); RESTORE(OSMR1); RESTORE(OSMR2); RESTORE(OSMR3); RESTORE(OSCR); RESTORE(OIER); RESTORE(CKEN); ICLR = 0; ICCR = 1; RESTORE(ICMR); // Restore PCMCIA register RESTORE(MECR); RESTORE(MCMEM0); RESTORE(MCMEM1); RESTORE(MCATT0); RESTORE(MCATT1); RESTORE(MCIO0); RESTORE(MCIO1); // Restore PWM register RESTORE(PWM_CTRL0); RESTORE(PWM_PWDUTY0); RESTORE(PWM_PERVAL0); RESTORE(PWM_CTRL1); RESTORE(PWM_PWDUTY1); RESTORE(PWM_PERVAL1); // Restore the ST UART RESTORE(STMCR); RESTORE(STSPR); RESTORE(STLCR); STLCR |= 0x80; RESTORE(STDLH); RESTORE(STDLL); RESTORE(STLCR); RESTORE(STISR); STFCR = FCR_RESETTF | FCR_RESETRF | FCR_TRFIFOE; RESTORE(STIER); // Restore the BT UART RESTORE(BTMCR); RESTORE(BTSPR); RESTORE(BTLCR); BTLCR |= 0x80; RESTORE(BTDLH); RESTORE(BTDLL); RESTORE(BTLCR); RESTORE(BTISR); BTFCR = FCR_RESETTF | FCR_RESETRF | FCR_TRFIFOE; RESTORE(BTIER); // Restore the FF UART RESTORE(FFMCR); RESTORE(FFSPR); RESTORE(FFLCR); FFLCR |= 0x80; RESTORE(FFDLH); RESTORE(FFDLL); RESTORE(FFLCR); RESTORE(FFISR); FFFCR = 0x07; RESTORE(FFIER); /* restore current time */ xtime.tv_sec = RCNR + delta;#ifdef DEBUG printk(KERN_DEBUG "*** made it back from resume\n");#endif return 0;}static int pxa_pm_enter(u32 state){ return pm_do_suspend(state, 0);}unsigned long sleep_phys_sp(void *sp){ return virt_to_phys(sp);}/* * Called after processes are frozen, but before we shut down devices. */static int pxa_pm_prepare(u32 state){ return 0;}/* * Called after devices are re-setup, but before processes are thawed. */static int pxa_pm_finish(u32 state){ return 0;}/* * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. */static struct pm_ops pxa_pm_ops = { .pm_disk_mode = PM_DISK_FIRMWARE, .prepare = pxa_pm_prepare, .enter = pxa_pm_enter, .finish = pxa_pm_finish,};static int __init pxa_pm_init(void){ pm_set_ops(&pxa_pm_ops); return 0;}late_initcall(pxa_pm_init);#if defined(CONFIG_USB_OHCI_HCD) && defined(CONFIG_SA1111)//extern int usb_post_resume(void); commented by hzh#endif#ifdef CONFIG_MTDextern int mtd_suspend(void);extern int mtd_resume(void);#endifstatic int suspend(unsigned int resume_time){ int ret = 0; if (!pm_do_suspend) return -ENOSYS; // printk("suspend devices\n");#ifdef CONFIG_MTD// ret = mtd_suspend(); // FIXME// if (ret)// return ret;#endif ret = device_suspend(4); // printk("sleep...\n"); local_irq_disable(); leds_event(led_stop); ret = pm_do_suspend(PM_SUSPEND_MEM, resume_time); leds_event(led_start); local_irq_enable(); // printk("resume devices\n"); device_resume();#ifdef CONFIG_MTD// mtd_resume(); // FIXME#endif#if defined(CONFIG_USB_OHCI_HCD) && defined(CONFIG_SA1111)// usb_post_resume(); //commented by hzh#endif return ret;}#ifdef CONFIG_SYSCTL/* * We really want this to die. It's a disgusting hack using unallocated * sysctl numbers. We should be using a real interface. */#include <linux/init.h>#include <linux/sysctl.h>static int irq_active = 0;// convert string into 0 to 999999 secondsunsigned int strtosec (char *buf){ int i, rtn = 0; for (i = 0; i < 6; i++) { if (*buf < '0' || *buf > '9') break; rtn *= 10; rtn += *buf++ - '0'; } return rtn;}static intpm_sysctl_proc_handler(ctl_table *ctl, int write, struct file *filp, void *buffer, size_t *lenp, loff_t *ppos) //add ppos{ int resume_time = 0; int save_irq_active = irq_active; int ret = -EIO; printk(KERN_NOTICE "warning: using deprecated sysctl PM interface\n"); if (write && buffer && lenp && *lenp) { resume_time = strtosec((char *)buffer); if (resume_time) printk(KERN_INFO "suspend for %d seconds\n", resume_time); } if (write) { irq_active = 0; // don't re-trigger suspend_catch process ret = suspend(resume_time); irq_active = save_irq_active; } return ret;}static int wake(ctl_table *ctl, int write, struct file *filp, void *buffer, size_t *lenp, loff_t *ppos) //add ppos{ int wake = 1; if (write && buffer && lenp && *lenp) { wake = strtosec((char *)buffer); } if (write) {#ifdef CONFIG_ARCH_ADSBITSYX if (wake) { sio_backlite_on(); } else { sio_backlite_off(); }#endif } return 0;}/* * This came from arch/arm/mach-sa1100/pm.c: * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> * with modifications by Nicolas Pitre and Russell King. * * ARGH! ACPI people defined CTL_ACPI in linux/acpi.h rather than * linux/sysctl.h. [ACPI broke the kernel.] * * Our interface here is obsolete and needs to be replaced. * Quick hack to get this working - use sysctl id 9999. */#warning:sysctl PM interface deprecated#define CTL_ACPI 9999#define ACPI_S1_SLP_TYP 19#define ACPI_S1_SLP_WAKE 20#ifdef SLEEP_ON_IRQstatic wait_queue_head_t sleep_queue;static int irq_present = 0;static irqreturn_t sleep_irq(int irq, void *dev_id, struct pt_regs *regs){ if (irq_active) { irq_active = 0; wake_up(&sleep_queue); } return IRQ_HANDLED;}static int sysctl_pm_suspend_catch(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp, loff_t *ppos) //add ppos{ while (1) { init_waitqueue_head(&sleep_queue); // Set GPIO as non Alternative Function and Input // Setup IRQ (set it up each time since sleeping // fools around with the GPIO quite a bit.) pxa_gpio_mode(IRQ_TO_GPIO(WAKE_UP_IRQ) | GPIO_IN); if (irq_present) { free_irq(WAKE_UP_IRQ, NULL); irq_present=0; } // register IRQ if (request_irq(WAKE_UP_IRQ, sleep_irq, 0, "sleep", NULL)) { return -EINVAL; } set_irq_type(WAKE_UP_IRQ, IRQT_RISING); irq_active = 1; irq_present = 1; interruptible_sleep_on(&sleep_queue); // debounce a little bit so we don't wake right up! current->state = TASK_INTERRUPTIBLE; schedule_timeout(HZ / 2); if (!irq_active) return 0; }}#endifstatic struct ctl_table pm_table[] ={ { .ctl_name = ACPI_S1_SLP_TYP, .procname = "suspend", .mode = 0600, .proc_handler = pm_sysctl_proc_handler, }, { .ctl_name = ACPI_S1_SLP_WAKE, .procname = "wake", .mode = 0600, .proc_handler = wake, },#ifdef SLEEP_ON_IRQ { .ctl_name = ACPI_S1_SLP_TYP, .procname = "suspend_catch", .mode = 0600, .proc_handler = sysctl_pm_suspend_catch, },#endif {0}};static struct ctl_table pm_dir_table[] ={ { .ctl_name = CTL_ACPI, .procname = "pm", .mode = 0555, .child = pm_table, }, {0}};/* * Initialize power interface */static int __init pm_init(void){ register_sysctl_table(pm_dir_table, 1); return 0;}fs_initcall(pm_init);#endif
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?