diff --git a/arch/mips/au1000/common/power.c b/arch/mips/au1000/common/power.c index f492631..4175396 100644 --- a/arch/mips/au1000/common/power.c +++ b/arch/mips/au1000/common/power.c @@ -1,6 +1,11 @@ /* * BRIEF MODULE DESCRIPTION - * Au1000 Power Management routines. + * Au1xxx Power Management routines (sysfs and procfs interfaces). + * + * Copyright 2006 Rodolfo Giometti + * + * Based on some previous version's functions, so I report below the previous + * copyright message: * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. @@ -32,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -43,18 +47,16 @@ #include #include #include +#include -#ifdef CONFIG_PM +static unsigned long cpu_freq; -#define DEBUG 1 -#ifdef DEBUG +#ifdef CONFIG_DEBUG_KERNEL # define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) #else # define DPRINTK(fmt, args...) #endif -static void au1000_calibrate_delay(void); - extern void set_au1x00_speed(unsigned int new_freq); extern unsigned int get_au1x00_speed(void); extern unsigned long get_au1x00_uart_baud_base(void); @@ -63,11 +65,14 @@ extern unsigned long save_local_and_disa extern void restore_local_and_enable(int controller, unsigned long mask); extern void local_enable_irq(unsigned int irq_nr); -/* Quick acpi hack. This will have to change! */ -#define CTL_ACPI 9999 -#define ACPI_S1_SLP_TYP 19 -#define ACPI_SLEEP 21 +/* Define the board specific sleep functions */ +int (*board_before_sleep)(void); +void (*board_after_sleep)(int reason); +/* Quick acpi hack. This will have to change! */ +#define CTL_ACPI 9999 +#define ACPI_S1_SLP_TYP 19 +#define ACPI_SLEEP 21 static DEFINE_SPINLOCK(pm_lock); @@ -83,6 +88,7 @@ static DEFINE_SPINLOCK(pm_lock); static uint sleep_aux_pll_cntrl; static uint sleep_cpu_pll_cntrl; static uint sleep_pin_function; +static uint sleep_gpio2_enable; static uint sleep_uart0_inten; static uint sleep_uart0_fifoctl; static uint sleep_uart0_linectl; @@ -92,16 +98,45 @@ static uint sleep_usbhost_enable; static uint sleep_usbdev_enable; static uint sleep_static_memctlr[4][3]; -/* Define this to cause the value you write to /proc/sys/pm/sleep to - * set the TOY timer for the amount of time you want to sleep. - * This is done mainly for testing, but may be useful in other cases. - * The value is number of 32KHz ticks to sleep. - */ -#define SLEEP_TEST_TIMEOUT 1 -#ifdef SLEEP_TEST_TIMEOUT -static int sleep_ticks; -void wakeup_counter0_set(int ticks); -#endif +/* This is the number of bits of precision for the loops_per_jiffy. Each + bit takes on average 1.5/HZ seconds. This (like the original) is a little + better than 1% */ +#define LPS_PREC 8 + +static void au1000_calibrate_delay(void) +{ + unsigned long ticks, loopbit; + int lps_precision = LPS_PREC; + + loops_per_jiffy = (1 << 12); + + while (loops_per_jiffy <<= 1) { + /* wait for "start of" clock tick */ + ticks = jiffies; + while (ticks == jiffies) + /* nothing */ ; + /* Go .. */ + ticks = jiffies; + __delay(loops_per_jiffy); + ticks = jiffies - ticks; + if (ticks) + break; + } + +/* Do a binary approximation to get loops_per_jiffy set to equal one clock + (up to lps_precision bits) */ + loops_per_jiffy >>= 1; + loopbit = loops_per_jiffy; + while (lps_precision-- && (loopbit >>= 1)) { + loops_per_jiffy |= loopbit; + ticks = jiffies; + while (ticks == jiffies); + ticks = jiffies; + __delay(loops_per_jiffy); + if (jiffies != ticks) /* longer than 1 tick */ + loops_per_jiffy &= ~loopbit; + } +} static void save_core_regs(void) @@ -147,6 +182,7 @@ save_core_regs(void) sleep_cpu_pll_cntrl = au_readl(SYS_CPUPLL); sleep_pin_function = au_readl(SYS_PINFUNC); + sleep_gpio2_enable = au_readl(GPIO2_ENABLE); /* Save the static memory controller configuration. */ @@ -173,6 +209,7 @@ restore_core_regs(void) au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync(); au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync(); au_writel(sleep_pin_function, SYS_PINFUNC); au_sync(); + au_writel(sleep_gpio2_enable, GPIO2_ENABLE); au_sync(); /* Restore the static memory controller configuration. */ @@ -206,150 +243,98 @@ restore_core_regs(void) wakeup_counter0_adjust(); } -unsigned long suspend_mode; - -void wakeup_from_suspend(void) -{ - suspend_mode = 0; -} - -int au_sleep(void) +int au_sleep(int reason, int force) { + int ticks = 0; unsigned long wakeup, flags; - extern void save_and_sleep(void); + int board_reason = 0; + + /* We need to configure the GPIO or timer interrupts that will bring + * us out of sleep, so we use the specific board wake up source + * (if present) */ + if (board_before_sleep) + board_reason = board_before_sleep(); + if (!force && board_before_sleep) + reason = board_reason; - spin_lock_irqsave(&pm_lock,flags); + spin_lock_irqsave(&pm_lock, flags); save_core_regs(); flush_cache_all(); - /** The code below is all system dependent and we should probably - ** have a function call out of here to set this up. You need - ** to configure the GPIO or timer interrupts that will bring - ** you out of sleep. - ** For testing, the TOY counter wakeup is useful. - **/ - -#if 0 - au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD); - - /* gpio 6 can cause a wake up event */ - wakeup = au_readl(SYS_WAKEMSK); - wakeup &= ~(1 << 8); /* turn off match20 wakeup */ - wakeup |= 1 << 6; /* turn on gpio 6 wakeup */ -#else - /* For testing, allow match20 to wake us up. - */ -#ifdef SLEEP_TEST_TIMEOUT - wakeup_counter0_set(sleep_ticks); -#endif - wakeup = 1 << 8; /* turn on match20 wakeup */ - wakeup = 0; -#endif - au_writel(1, SYS_WAKESRC); /* clear cause */ - au_sync(); + if (reason == 0) /* machine_halt() */ + wakeup = 0; + else if (reason < 0) { /* TOY wake up */ + ticks = -reason*HZ; + wakeup_counter0_set(ticks); + wakeup = 1<<8; /* turn on match20 as wake up */ + } + else { /* GPIO[0-7] wake up */ + /* FIXME: this setting for GPIO[6] should be into specific + * boards setup... */ + /* We force pin GPIO[6]/SMROMCKE as gpio6 */ + au_writel(au_readl(SYS_PINSTATERD)&~(1<<11), SYS_PINSTATERD); + + wakeup = reason&0x0ff; /* turn on selected gpio as wake up */ + } + DPRINTK("reason %d force %d wakeup %lX ticks %d\n", + reason, force, wakeup, ticks); + au_writel(wakeup, SYS_WAKEMSK); + au_writel(~0, SYS_WAKESRC); /* clear cause */ au_sync(); + DPRINTK("Zzz...\n"); save_and_sleep(); - /* after a wakeup, the cpu vectors back to 0x1fc00000 so + /* after a wake up, the cpu vectors back to 0x1fc00000 so * it's up to the boot code to get us back here. */ restore_core_regs(); - spin_unlock_irqrestore(&pm_lock, flags); - return 0; -} + DPRINTK("Yep!\n"); -static int pm_do_sleep(ctl_table * ctl, int write, struct file *file, - void __user *buffer, size_t * len, loff_t *ppos) -{ - int retval = 0; -#ifdef SLEEP_TEST_TIMEOUT -#define TMPBUFLEN2 16 - char buf[TMPBUFLEN2], *p; -#endif - - if (!write) { - *len = 0; - } else { -#ifdef SLEEP_TEST_TIMEOUT - if (*len > TMPBUFLEN2 - 1) { - return -EFAULT; - } - if (copy_from_user(buf, buffer, *len)) { - return -EFAULT; - } - buf[*len] = 0; - p = buf; - sleep_ticks = simple_strtoul(p, &p, 0); -#endif - retval = pm_send_all(PM_SUSPEND, (void *) 2); - - if (retval) - return retval; - - au_sleep(); - retval = pm_send_all(PM_RESUME, (void *) 0); - } - return retval; -} + spin_unlock_irqrestore(&pm_lock, flags); -static int pm_do_suspend(ctl_table * ctl, int write, struct file *file, - void __user *buffer, size_t * len, loff_t *ppos) -{ - int retval = 0; + /* Get wake up source */ + reason = au_readl(SYS_WAKESRC)>>18; + if (reason&(1<<8)) /* Wake up thanks to TOY */ + reason = -ticks*HZ; + + /* Call specific board routine */ + if (board_after_sleep) + board_after_sleep(reason); - if (!write) { - *len = 0; - } else { - retval = pm_send_all(PM_SUSPEND, (void *) 2); - if (retval) - return retval; - suspend_mode = 1; - - retval = pm_send_all(PM_RESUME, (void *) 0); - } - return retval; + return 0; } - -static int pm_do_freq(ctl_table * ctl, int write, struct file *file, +static int au1xxx_pm_do_freq(ctl_table * ctl, int write, struct file *file, void __user *buffer, size_t * len, loff_t *ppos) { int retval = 0, i; - unsigned long val, pll; -#define TMPBUFLEN 64 -#define MAX_CPU_FREQ 396 - char buf[TMPBUFLEN], *p; + unsigned long *valp = (unsigned long *) ctl->data; + unsigned long pll; unsigned long flags, intc0_mask, intc1_mask; unsigned long old_baud_base, old_cpu_freq, baud_rate, old_clk, old_refresh; unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh; spin_lock_irqsave(&pm_lock, flags); - if (!write) { + + if (!write) *len = 0; - } else { - /* Parse the new frequency */ - if (*len > TMPBUFLEN - 1) { - spin_unlock_irqrestore(&pm_lock, flags); - return -EFAULT; - } - if (copy_from_user(buf, buffer, *len)) { - spin_unlock_irqrestore(&pm_lock, flags); - return -EFAULT; - } - buf[*len] = 0; - p = buf; - val = simple_strtoul(p, &p, 0); - if (val > MAX_CPU_FREQ) { + + retval = proc_dointvec(ctl, write, file, buffer, len, ppos); + +#define MAX_CPU_FREQ 396 + if (write) { + /* Check the new frequency */ + if (*valp > MAX_CPU_FREQ) { spin_unlock_irqrestore(&pm_lock, flags); return -EFAULT; } - pll = val / 12; + pll = *valp / 12; if ((pll > 33) || (pll < 7)) { /* 396 MHz max, 84 MHz min */ /* revisit this for higher speed cpus */ spin_unlock_irqrestore(&pm_lock, flags); @@ -424,72 +409,66 @@ static int pm_do_freq(ctl_table * ctl, i return retval; } +/* + * Called after processes are frozen, but before we shut down devices. + */ +int au1xxx_pm_prepare(suspend_state_t state) +{ + DPRINTK("state = %d\n", state); + return 0; +} -static struct ctl_table pm_table[] = { - {ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, &pm_do_suspend}, - {ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &pm_do_sleep}, - {CTL_ACPI, "freq", NULL, 0, 0600, NULL, &pm_do_freq}, - {0} -}; - -static struct ctl_table pm_dir_table[] = { - {CTL_ACPI, "pm", NULL, 0, 0555, pm_table}, - {0} -}; +/* + * Called after down devices. + */ +int au1xxx_pm_enter(suspend_state_t state) +{ + DPRINTK("state = %d\n", state); + //return au_sleep(1<<6, 1); /* wake up due GPIO 6 */ + return au_sleep(-3, 0); /* wake up after 3 seconds */ +} /* - * Initialize power interface + * Called after devices are re-setup, but before processes are thawed. */ -static int __init pm_init(void) +int au1xxx_pm_finish(suspend_state_t state) { - register_sysctl_table(pm_dir_table, 1); + DPRINTK("state = %d\n", state); return 0; } -__initcall(pm_init); - +/* + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. + */ +static struct pm_ops au1xxx_pm_ops = { + .pm_disk_mode = PM_DISK_FIRMWARE, + .prepare = au1xxx_pm_prepare, + .enter = au1xxx_pm_enter, + .finish = au1xxx_pm_finish, +}; /* - * This is right out of init/main.c + * Set up the old "/proc/sys/pm" interface. */ +static struct ctl_table au1xxx_pm_table[] = { + { CTL_ACPI, "freq", &cpu_freq, sizeof(int), 0600, NULL, &au1xxx_pm_do_freq }, + { 0 }, +}; -/* This is the number of bits of precision for the loops_per_jiffy. Each - bit takes on average 1.5/HZ seconds. This (like the original) is a little - better than 1% */ -#define LPS_PREC 8 +static struct ctl_table pm_dir_table[] = { + { CTL_ACPI, "pm", NULL, 0, 0555, au1xxx_pm_table }, + { 0 }, +}; -static void au1000_calibrate_delay(void) +static int __init au1xxx_pm_init(void) { - unsigned long ticks, loopbit; - int lps_precision = LPS_PREC; - - loops_per_jiffy = (1 << 12); + /* The new interface */ + pm_set_ops(&au1xxx_pm_ops); - while (loops_per_jiffy <<= 1) { - /* wait for "start of" clock tick */ - ticks = jiffies; - while (ticks == jiffies) - /* nothing */ ; - /* Go .. */ - ticks = jiffies; - __delay(loops_per_jiffy); - ticks = jiffies - ticks; - if (ticks) - break; - } + /* The old interface */ + register_sysctl_table(pm_dir_table, 1); -/* Do a binary approximation to get loops_per_jiffy set to equal one clock - (up to lps_precision bits) */ - loops_per_jiffy >>= 1; - loopbit = loops_per_jiffy; - while (lps_precision-- && (loopbit >>= 1)) { - loops_per_jiffy |= loopbit; - ticks = jiffies; - while (ticks == jiffies); - ticks = jiffies; - __delay(loops_per_jiffy); - if (jiffies != ticks) /* longer than 1 tick */ - loops_per_jiffy &= ~loopbit; - } + return 0; } -#endif /* CONFIG_PM */ + +device_initcall(au1xxx_pm_init); diff --git a/arch/mips/au1000/common/reset.c b/arch/mips/au1000/common/reset.c index c93af22..49b949f 100644 --- a/arch/mips/au1000/common/reset.c +++ b/arch/mips/au1000/common/reset.c @@ -36,9 +36,7 @@ #include #include #include - -extern int au_sleep(void); -extern void (*flush_cache_all)(void); +#include void au1000_restart(char *command) { @@ -179,7 +177,7 @@ void au1000_halt(void) au_writew(au_readw(0xB980001C) | (1<<14), 0xB980001C); #endif #ifdef CONFIG_PM - au_sleep(); + au_sleep(0, 1); /* should not get here */ printk(KERN_ERR "Unable to put cpu in sleep mode\n"); diff --git a/arch/mips/au1000/common/sleeper.S b/arch/mips/au1000/common/sleeper.S index 44dac3b..34dad65 100644 --- a/arch/mips/au1000/common/sleeper.S +++ b/arch/mips/au1000/common/sleeper.S @@ -9,22 +9,54 @@ * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. */ +#include #include #include #include #include #include +#include + +/* + * Note: This file is *not* conditional on CONFIG_PM since Alchemy sleep + * need not be tied to any particular power management scheme. + */ + + .extern __flush_cache_all .text - .set macro - .set noat .align 5 -/* Save all of the processor general registers and go to sleep. - * A wakeup condition will get us back here to restore the registers. +/* + * Save the processor general registers and go to sleep. A wakeup + * condition will get us back here to restore the registers. */ -LEAF(save_and_sleep) +/* still need to fix alignment issues here */ +save_and_sleep_frmsz = 48 +NESTED(save_and_sleep, save_and_sleep_frmsz, ra) + .set noreorder + .set nomacro + .set noat + subu sp, save_and_sleep_frmsz + sw ra, save_and_sleep_frmsz-4(sp) + sw s0, save_and_sleep_frmsz-8(sp) + sw s1, save_and_sleep_frmsz-12(sp) + sw s2, save_and_sleep_frmsz-16(sp) + sw s3, save_and_sleep_frmsz-20(sp) + sw s4, save_and_sleep_frmsz-24(sp) + sw s5, save_and_sleep_frmsz-28(sp) + sw s6, save_and_sleep_frmsz-32(sp) + sw s7, save_and_sleep_frmsz-36(sp) + sw s8, save_and_sleep_frmsz-40(sp) + sw gp, save_and_sleep_frmsz-44(sp) + + /* We only need to save the registers that the calling function + * hasn't saved for us. 0 is always zero. 8 - 15, 24 and 25 are + * temporaries and can be used without saving. 26 and 27 are reserved + * for interrupt/trap handling and expected to change. 29 is the + * stack pointer which is handled as a special case here. + */ subu sp, PT_SIZE sw $1, PT_R1(sp) sw $2, PT_R2(sp) @@ -33,14 +65,6 @@ LEAF(save_and_sleep) sw $5, PT_R5(sp) sw $6, PT_R6(sp) sw $7, PT_R7(sp) - sw $8, PT_R8(sp) - sw $9, PT_R9(sp) - sw $10, PT_R10(sp) - sw $11, PT_R11(sp) - sw $12, PT_R12(sp) - sw $13, PT_R13(sp) - sw $14, PT_R14(sp) - sw $15, PT_R15(sp) sw $16, PT_R16(sp) sw $17, PT_R17(sp) sw $18, PT_R18(sp) @@ -49,32 +73,54 @@ LEAF(save_and_sleep) sw $21, PT_R21(sp) sw $22, PT_R22(sp) sw $23, PT_R23(sp) - sw $24, PT_R24(sp) - sw $25, PT_R25(sp) - sw $26, PT_R26(sp) - sw $27, PT_R27(sp) sw $28, PT_R28(sp) - sw $29, PT_R29(sp) sw $30, PT_R30(sp) sw $31, PT_R31(sp) +#define PT_C0STATUS PT_LO +#define PT_CONTEXT PT_HI +#define PT_PAGEMASK PT_EPC +#define PT_CONFIG PT_BVADDR mfc0 k0, CP0_STATUS - sw k0, 0x20(sp) + sw k0, PT_C0STATUS(sp) // 0x20 mfc0 k0, CP0_CONTEXT - sw k0, 0x1c(sp) + sw k0, PT_CONTEXT(sp) // 0x1c mfc0 k0, CP0_PAGEMASK - sw k0, 0x18(sp) + sw k0, PT_PAGEMASK(sp) // 0x18 mfc0 k0, CP0_CONFIG - sw k0, 0x14(sp) + sw k0, PT_CONFIG(sp) // 0x14 + +#if 0 + /* Infinite loop to allow JTAG attach before sleep mode (debug only) + */ +2: li t1, 0 + beq t1, zero, 2b + nop +#endif + + .set macro + .set at + + li t0, SYS_SLPPWR + sw zero, 0(t0) /* Get the processor ready to sleep */ + sync /* Now set up the scratch registers so the boot rom will * return to this point upon wakeup. + * sys_scratch0 : SP + * sys_scratch1 : RA */ - la k0, 1f - lui k1, 0xb190 - ori k1, 0x18 - sw sp, 0(k1) - ori k1, 0x1c - sw k0, 0(k1) + li t0, SYS_SCRATCH0 + li t1, SYS_SCRATCH1 + sw sp, 0(t0) + la k0, resume_from_sleep + sw k0, 0(t1) + +/* Flush DCACHE to make sure context is in memory +*/ + la t1, __flush_cache_all /* _flush_cache_all is a function ptr */ + lw t0,0(t1) + jal t0 + nop /* Put SDRAM into self refresh. Preload instructions into cache, * issue a precharge, then auto refresh, then sleep commands to it. @@ -87,30 +133,74 @@ LEAF(save_and_sleep) cache 0x14, 96(t0) .set mips0 + /* Put SDRAM to sleep */ sdsleep: - lui k0, 0xb400 - sw zero, 0x001c(k0) /* Precharge */ - sw zero, 0x0020(k0) /* Auto refresh */ - sw zero, 0x0030(k0) /* SDRAM sleep */ + li a0, MEM_PHYS_ADDR + or a0, a0, 0xA0000000 +#if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1100) || defined(CONFIG_SOC_AU1500) + lw k0, MEM_SDMODE0(a0) + sw zero, MEM_SDPRECMD(a0) /* Precharge */ + sw zero, MEM_SDAUTOREF(a0) /* Auto Refresh */ + sw zero, MEM_SDSLEEP(a0) /* Sleep */ sync - - lui k1, 0xb190 - sw zero, 0x0078(k1) /* get ready to sleep */ +#endif +#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) + sw zero, MEM_SDPRECMD(a0) /* Precharge */ + sw zero, MEM_SDSREF(a0) + + #lw t0, MEM_SDSTAT(a0) + #and t0, t0, 0x01000000 + li t0, 0x01000000 +refresh_not_set: + lw t1, MEM_SDSTAT(a0) + and t2, t1, t0 + beq zero, t2, refresh_not_set + nop + + li t0, ~0x30000000 + lw t1, MEM_SDCONFIGA(a0) + and t1, t0, t1 + sw t1, MEM_SDCONFIGA(a0) sync - sw zero, 0x007c(k1) /* Put processor to sleep */ +#endif + + li t0, SYS_SLEEP + sw zero, 0(t0) /* Put processor to sleep */ sync + nop + nop + nop + nop + nop + nop + nop + nop /* This is where we return upon wakeup. * Reload all of the registers and return. */ -1: nop - lw k0, 0x20(sp) +resume_from_sleep: + nop + .set nomacro + .set noat + +#if 0 + /* Infinite loop to allow JTAG attach before sleep mode (debug only) + */ +2: li t1, 0 + beq t1, zero, 2b + nop +#endif + + /* Restore CPU registers + */ + lw k0, PT_C0STATUS(sp) // 0x20 mtc0 k0, CP0_STATUS - lw k0, 0x1c(sp) + lw k0, PT_CONTEXT(sp) // 0x1c mtc0 k0, CP0_CONTEXT - lw k0, 0x18(sp) + lw k0, PT_PAGEMASK(sp) // 0x18 mtc0 k0, CP0_PAGEMASK - lw k0, 0x14(sp) + lw k0, PT_CONFIG(sp) // 0x14 mtc0 k0, CP0_CONFIG lw $1, PT_R1(sp) lw $2, PT_R2(sp) @@ -119,14 +209,6 @@ sdsleep: lw $5, PT_R5(sp) lw $6, PT_R6(sp) lw $7, PT_R7(sp) - lw $8, PT_R8(sp) - lw $9, PT_R9(sp) - lw $10, PT_R10(sp) - lw $11, PT_R11(sp) - lw $12, PT_R12(sp) - lw $13, PT_R13(sp) - lw $14, PT_R14(sp) - lw $15, PT_R15(sp) lw $16, PT_R16(sp) lw $17, PT_R17(sp) lw $18, PT_R18(sp) @@ -135,15 +217,37 @@ sdsleep: lw $21, PT_R21(sp) lw $22, PT_R22(sp) lw $23, PT_R23(sp) - lw $24, PT_R24(sp) - lw $25, PT_R25(sp) - lw $26, PT_R26(sp) - lw $27, PT_R27(sp) lw $28, PT_R28(sp) - lw $29, PT_R29(sp) lw $30, PT_R30(sp) lw $31, PT_R31(sp) + + .set macro + .set at + + /* Clear the wake source, but save it as the return value of the + function */ + li t0, SYS_WAKESRC + lw v0, 0(t0) + sw v0, PT_R2(sp) + sw zero, 0(t0) + addiu sp, PT_SIZE + lw gp, save_and_sleep_frmsz-44(sp) + lw s8, save_and_sleep_frmsz-40(sp) + lw s7, save_and_sleep_frmsz-36(sp) + lw s6, save_and_sleep_frmsz-32(sp) + lw s5, save_and_sleep_frmsz-28(sp) + lw s4, save_and_sleep_frmsz-24(sp) + lw s3, save_and_sleep_frmsz-20(sp) + lw s2, save_and_sleep_frmsz-16(sp) + lw s1, save_and_sleep_frmsz-12(sp) + lw s0, save_and_sleep_frmsz-8(sp) + lw ra, save_and_sleep_frmsz-4(sp) + + addu sp, save_and_sleep_frmsz jr ra + nop + .set reorder END(save_and_sleep) + diff --git a/arch/mips/au1000/common/time.c b/arch/mips/au1000/common/time.c index f74d66a..177606b 100644 --- a/arch/mips/au1000/common/time.c +++ b/arch/mips/au1000/common/time.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -179,6 +180,8 @@ wakeup_counter0_adjust(void) unsigned long pc0; int time_elapsed; + /* Before reading TOY we have to wait the following bit... */ + while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); pc0 = au_readl(SYS_TOYREAD); if (pc0 < last_match20) { /* counter overflowed */ @@ -188,26 +191,26 @@ wakeup_counter0_adjust(void) time_elapsed = pc0 - last_match20; } - while (time_elapsed > 0) { - time_elapsed -= MATCH20_INC; - last_match20 += MATCH20_INC; - } + last_match20 += time_elapsed - time_elapsed%MATCH20_INC; last_pc0 = pc0; + au_sync(); au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2); au_sync(); - } -/* This is just for debugging to set the timer for a sleep delay. +/* Set the timer for a sleep delay. */ void wakeup_counter0_set(int ticks) { unsigned long pc0; + /* Before reading TOY we have to wait the following bit... */ + while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20); pc0 = au_readl(SYS_TOYREAD); last_pc0 = pc0; + au_sync(); au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2); au_sync(); } diff --git a/include/asm-mips/mach-au1x00/power.h b/include/asm-mips/mach-au1x00/power.h new file mode 100644 index 0000000..8621716 --- /dev/null +++ b/include/asm-mips/mach-au1x00/power.h @@ -0,0 +1,66 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * Include file for power management of Alchemy Semiconductor's + * Au1k CPU. + * + * Copyright 2006 Rodolfo Giometti + * Author: Rodolfo Giometti + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _AU1X00_POWER_H_ +#define _AU1X00_POWER_H_ + +extern void (*flush_cache_all)(void); + +/* Save CPU state and go to sleep */ +extern void save_and_sleep(void); + +/* Set the timer for a sleep delay */ +extern void wakeup_counter0_set(int ticks); + +/* Do system settings before sleeping. + * + * If "reason" + * > 0 then we use GPIO[0-7] as wakeup source according to + * bits set to "1" (ex. reason==5 set up as wake up sorces + * GPIOs 0 and 2). + * < 0 then we use "-reason" as sleeping seconds before wakeup. + * == 0 then we consider machine_halt(). + * + * If "force" != 0 then the "reason" value cannot be changed by + * function board_before_sleep() */ +extern int au_sleep(int reason, int force); + +/* These functions are called by au_sleep() before, and after, + * sleeping mode. + * + * These functions are defaulted to NULL and can remain so. + * + * Function "board_before_sleep()" returns an interger (called "reason") + * and function "board_after_sleep" takes "reason" as argument. Such + * parameter point the cause of sleeping mode as defined for au_sleep(). */ +extern int (*board_before_sleep)(void); +extern void (*board_after_sleep)(int reason); + +#endif /* _AU1X00_POWER_H_ */