435 lines · 8.3 KB · C
RawDownload
/* * FR-V Power Management Routines * * Copyright (c) 2004 Red Hat, Inc. * * Based on SA1100 version: * Copyright (c) 2001 Cliff Brake <cbrake@accelent.com> * * 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/module.h>#include <linux/pm.h>#include <linux/pm_legacy.h>#include <linux/sched.h>#include <linux/interrupt.h>#include <linux/sysctl.h>#include <linux/errno.h>#include <linux/delay.h>#include <asm/uaccess.h> #include <asm/mb86943a.h> #include "local.h" void (*pm_power_off)(void);EXPORT_SYMBOL(pm_power_off); extern void frv_change_cmode(int); /* * Debug macros */#define DEBUG int pm_do_suspend(void){	local_irq_disable(); 	__set_LEDS(0xb1); 	/* go zzz */	frv_cpu_suspend(pdm_suspend_mode); 	__set_LEDS(0xb2); 	local_irq_enable(); 	return 0;} static unsigned long __irq_mask; /* * Setup interrupt masks, etc to enable wakeup by power switch */static void __default_power_switch_setup(void){	/* default is to mask all interrupt sources. */	__irq_mask = *(unsigned long *)0xfeff9820;	*(unsigned long *)0xfeff9820 = 0xfffe0000;} /* * Cleanup interrupt masks, etc after wakeup by power switch */static void __default_power_switch_cleanup(void){	*(unsigned long *)0xfeff9820 = __irq_mask;} /* * Return non-zero if wakeup irq was caused by power switch */static int __default_power_switch_check(void){	return 1;} void (*__power_switch_wake_setup)(void) = __default_power_switch_setup;int  (*__power_switch_wake_check)(void) = __default_power_switch_check;void (*__power_switch_wake_cleanup)(void) = __default_power_switch_cleanup; int pm_do_bus_sleep(void){	local_irq_disable(); 	/*         * Here is where we need some platform-dependent setup	 * of the interrupt state so that appropriate wakeup	 * sources are allowed and all others are masked.	 */	__power_switch_wake_setup(); 	__set_LEDS(0xa1); 	/* go zzz	 *	 * This is in a loop in case power switch shares an irq with other	 * devices. The wake_check() tells us if we need to finish waking	 * or go back to sleep.	 */	do {		frv_cpu_suspend(HSR0_PDM_BUS_SLEEP);	} while (__power_switch_wake_check && !__power_switch_wake_check()); 	__set_LEDS(0xa2); 	/*         * Here is where we need some platform-dependent restore	 * of the interrupt state prior to being called.	 */	__power_switch_wake_cleanup(); 	local_irq_enable(); 	return 0;} unsigned long sleep_phys_sp(void *sp){	return virt_to_phys(sp);} #ifdef CONFIG_SYSCTL/* * Use a temporary sysctl number. Horrid, but will be cleaned up in 2.6 * when all the PM interfaces exist nicely. */#define CTL_PM 9899#define CTL_PM_SUSPEND 1#define CTL_PM_CMODE 2#define CTL_PM_P0 4#define CTL_PM_CM 5 static int user_atoi(char *ubuf, size_t len){	char buf[16];	unsigned long ret; 	if (len > 15)		return -EINVAL; 	if (copy_from_user(buf, ubuf, len))		return -EFAULT; 	buf[len] = 0;	ret = simple_strtoul(buf, NULL, 0);	if (ret > INT_MAX)		return -ERANGE;	return ret;} /* * Send us to sleep. */static int sysctl_pm_do_suspend(ctl_table *ctl, int write, struct file *filp,				void *buffer, size_t *lenp, loff_t *fpos){	int retval, mode; 	if (*lenp <= 0)		return -EIO; 	mode = user_atoi(buffer, *lenp);	if ((mode != 1) && (mode != 5))		return -EINVAL; 	retval = pm_send_all(PM_SUSPEND, (void *)3); 	if (retval == 0) {		if (mode == 5)		    retval = pm_do_bus_sleep();		else		    retval = pm_do_suspend();		pm_send_all(PM_RESUME, (void *)0);	} 	return retval;} static int try_set_cmode(int new_cmode){	if (new_cmode > 15)		return -EINVAL;	if (!(clock_cmodes_permitted & (1<<new_cmode)))		return -EINVAL; 	/* tell all the drivers we're suspending */	pm_send_all(PM_SUSPEND, (void *)3); 	/* now change cmode */	local_irq_disable();	frv_dma_pause_all(); 	frv_change_cmode(new_cmode); 	determine_clocks(0);	time_divisor_init(); #ifdef DEBUG	determine_clocks(1);#endif	frv_dma_resume_all();	local_irq_enable(); 	/* tell all the drivers we're resuming */	pm_send_all(PM_RESUME, (void *)0);	return 0;}  static int cmode_procctl(ctl_table *ctl, int write, struct file *filp,			 void *buffer, size_t *lenp, loff_t *fpos){	int new_cmode; 	if (!write)		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos); 	new_cmode = user_atoi(buffer, *lenp); 	return try_set_cmode(new_cmode)?:*lenp;} static int cmode_sysctl(ctl_table *table, int *name, int nlen,			void *oldval, size_t *oldlenp,			void *newval, size_t newlen, void **context){	if (oldval && oldlenp) {		size_t oldlen; 		if (get_user(oldlen, oldlenp))			return -EFAULT; 		if (oldlen != sizeof(int))			return -EINVAL; 		if (put_user(clock_cmode_current, (unsigned int *)oldval) ||		    put_user(sizeof(int), oldlenp))			return -EFAULT;	}	if (newval && newlen) {		int new_cmode; 		if (newlen != sizeof(int))			return -EINVAL; 		if (get_user(new_cmode, (int *)newval))			return -EFAULT; 		return try_set_cmode(new_cmode)?:1;	}	return 1;} static int try_set_p0(int new_p0){	unsigned long flags, clkc; 	if (new_p0 < 0 || new_p0 > 1)		return -EINVAL; 	local_irq_save(flags);	__set_PSR(flags & ~PSR_ET); 	frv_dma_pause_all(); 	clkc = __get_CLKC();	if (new_p0)		clkc |= CLKC_P0;	else		clkc &= ~CLKC_P0;	__set_CLKC(clkc); 	determine_clocks(0);	time_divisor_init(); #ifdef DEBUG	determine_clocks(1);#endif	frv_dma_resume_all();	local_irq_restore(flags);	return 0;} static int try_set_cm(int new_cm){	unsigned long flags, clkc; 	if (new_cm < 0 || new_cm > 1)		return -EINVAL; 	local_irq_save(flags);	__set_PSR(flags & ~PSR_ET); 	frv_dma_pause_all(); 	clkc = __get_CLKC();	clkc &= ~CLKC_CM;	clkc |= new_cm;	__set_CLKC(clkc); 	determine_clocks(0);	time_divisor_init(); #if 1 //def DEBUG	determine_clocks(1);#endif 	frv_dma_resume_all();	local_irq_restore(flags);	return 0;} static int p0_procctl(ctl_table *ctl, int write, struct file *filp,		      void *buffer, size_t *lenp, loff_t *fpos){	int new_p0; 	if (!write)		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos); 	new_p0 = user_atoi(buffer, *lenp); 	return try_set_p0(new_p0)?:*lenp;} static int p0_sysctl(ctl_table *table, int *name, int nlen,		     void *oldval, size_t *oldlenp,		     void *newval, size_t newlen, void **context){	if (oldval && oldlenp) {		size_t oldlen; 		if (get_user(oldlen, oldlenp))			return -EFAULT; 		if (oldlen != sizeof(int))			return -EINVAL; 		if (put_user(clock_p0_current, (unsigned int *)oldval) ||		    put_user(sizeof(int), oldlenp))			return -EFAULT;	}	if (newval && newlen) {		int new_p0; 		if (newlen != sizeof(int))			return -EINVAL; 		if (get_user(new_p0, (int *)newval))			return -EFAULT; 		return try_set_p0(new_p0)?:1;	}	return 1;} static int cm_procctl(ctl_table *ctl, int write, struct file *filp,		      void *buffer, size_t *lenp, loff_t *fpos){	int new_cm; 	if (!write)		return proc_dointvec(ctl, write, filp, buffer, lenp, fpos); 	new_cm = user_atoi(buffer, *lenp); 	return try_set_cm(new_cm)?:*lenp;} static int cm_sysctl(ctl_table *table, int *name, int nlen,		     void *oldval, size_t *oldlenp,		     void *newval, size_t newlen, void **context){	if (oldval && oldlenp) {		size_t oldlen; 		if (get_user(oldlen, oldlenp))			return -EFAULT; 		if (oldlen != sizeof(int))			return -EINVAL; 		if (put_user(clock_cm_current, (unsigned int *)oldval) ||		    put_user(sizeof(int), oldlenp))			return -EFAULT;	}	if (newval && newlen) {		int new_cm; 		if (newlen != sizeof(int))			return -EINVAL; 		if (get_user(new_cm, (int *)newval))			return -EFAULT; 		return try_set_cm(new_cm)?:1;	}	return 1;}  static struct ctl_table pm_table[] ={	{CTL_PM_SUSPEND, "suspend", NULL, 0, 0200, NULL, &sysctl_pm_do_suspend},	{CTL_PM_CMODE, "cmode", &clock_cmode_current, sizeof(int), 0644, NULL, &cmode_procctl, &cmode_sysctl, NULL},	{CTL_PM_P0, "p0", &clock_p0_current, sizeof(int), 0644, NULL, &p0_procctl, &p0_sysctl, NULL},	{CTL_PM_CM, "cm", &clock_cm_current, sizeof(int), 0644, NULL, &cm_procctl, &cm_sysctl, NULL},	{0}}; static struct ctl_table pm_dir_table[] ={	{CTL_PM, "pm", NULL, 0, 0555, pm_table},	{0}}; /* * Initialize power interface */static int __init pm_init(void){	register_sysctl_table(pm_dir_table, 1);	return 0;} __initcall(pm_init); #endif

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t