123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352 |
- /*
- * 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/init.h>
- #include <linux/module.h>
- #include <linux/pm.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"
- /*
- * 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_SUSPEND 1
- #define CTL_PM_CMODE 2
- #define CTL_PM_P0 4
- #define CTL_PM_CM 5
- static int user_atoi(char __user *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(struct ctl_table *ctl, int write,
- void __user *buffer, size_t *lenp, loff_t *fpos)
- {
- int mode;
- if (*lenp <= 0)
- return -EIO;
- mode = user_atoi(buffer, *lenp);
- switch (mode) {
- case 1:
- return pm_do_suspend();
- case 5:
- return pm_do_bus_sleep();
- default:
- return -EINVAL;
- }
- }
- static int try_set_cmode(int new_cmode)
- {
- if (new_cmode > 15)
- return -EINVAL;
- if (!(clock_cmodes_permitted & (1<<new_cmode)))
- return -EINVAL;
- /* 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();
- return 0;
- }
- static int cmode_procctl(struct ctl_table *ctl, int write,
- void __user *buffer, size_t *lenp, loff_t *fpos)
- {
- int new_cmode;
- if (!write)
- return proc_dointvec(ctl, write, buffer, lenp, fpos);
- new_cmode = user_atoi(buffer, *lenp);
- return try_set_cmode(new_cmode)?:*lenp;
- }
- 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(struct ctl_table *ctl, int write,
- void __user *buffer, size_t *lenp, loff_t *fpos)
- {
- int new_p0;
- if (!write)
- return proc_dointvec(ctl, write, buffer, lenp, fpos);
- new_p0 = user_atoi(buffer, *lenp);
- return try_set_p0(new_p0)?:*lenp;
- }
- static int cm_procctl(struct ctl_table *ctl, int write,
- void __user *buffer, size_t *lenp, loff_t *fpos)
- {
- int new_cm;
- if (!write)
- return proc_dointvec(ctl, write, buffer, lenp, fpos);
- new_cm = user_atoi(buffer, *lenp);
- return try_set_cm(new_cm)?:*lenp;
- }
- static struct ctl_table pm_table[] =
- {
- {
- .procname = "suspend",
- .data = NULL,
- .maxlen = 0,
- .mode = 0200,
- .proc_handler = sysctl_pm_do_suspend,
- },
- {
- .procname = "cmode",
- .data = &clock_cmode_current,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = cmode_procctl,
- },
- {
- .procname = "p0",
- .data = &clock_p0_current,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = p0_procctl,
- },
- {
- .procname = "cm",
- .data = &clock_cm_current,
- .maxlen = sizeof(int),
- .mode = 0644,
- .proc_handler = cm_procctl,
- },
- { }
- };
- static struct ctl_table pm_dir_table[] =
- {
- {
- .procname = "pm",
- .mode = 0555,
- .child = pm_table,
- },
- { }
- };
- /*
- * Initialize power interface
- */
- static int __init pm_init(void)
- {
- register_sysctl_table(pm_dir_table);
- return 0;
- }
- __initcall(pm_init);
- #endif
|