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 + -
显示快捷键?