diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/kernel/Makefile linux-2.4.0-rmk2-np2-fb1/arch/arm/kernel/Makefile --- linux-2.4.0-rmk2-np2-org/arch/arm/kernel/Makefile Thu Feb 8 13:26:57 2001 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/kernel/Makefile Wed Feb 7 18:07:26 2001 @@ -17,7 +17,7 @@ O_OBJS_rpc = dma-rpc.o O_OBJS_footbridge = dma-footbridge.o isa.o O_OBJS_l7200 = fiq.o -O_OBJS_sa1100 = dma-sa1100.o +O_OBJS_sa1100 = dma-sa1100.o powermgr.o pci-nexuspci = plx90x0.o pci-footbridge = dec21285.o @@ -37,7 +37,7 @@ obj-n := obj- := -export-objs := armksyms.o dma.o ecard.o fiq.o oldlatches.o time.o +export-objs := armksyms.o dma.o ecard.o fiq.o oldlatches.o time.o powermgr.o no-irq-arch := $(CONFIG_ARCH_INTEGRATOR) $(CONFIG_ARCH_CLPS711X) \ $(CONFIG_ARCH_FOOTBRIDGE) $(CONFIG_ARCH_EBSA110) diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/kernel/head-armv.S linux-2.4.0-rmk2-np2-fb1/arch/arm/kernel/head-armv.S --- linux-2.4.0-rmk2-np2-org/arch/arm/kernel/head-armv.S Thu Feb 8 13:26:57 2001 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/kernel/head-armv.S Wed Feb 7 16:31:01 2001 @@ -188,6 +188,150 @@ stmia r8, {r0, r2} @ Save control register values b SYMBOL_NAME(start_kernel) +/* For Power manager */ +#ifdef CONFIG_SA1100_SPM +#define RCSR_P 0x90030004 /* Reset controller status register */ +#define PSSR_P 0x90020004 /* Power management sleep status register */ + + +/* + * This code is the code first called when the processor wakes up from sleep + * (ie gets a reset and the sleep scratch pad is non-zero). It has been + * hardcoded for armv4, sorry. + */ + + .globl coproc_data + .globl entry_from_sleep + +entry_from_sleep: + + /* test if came out of sleep-mode reset (only!) */ + mov r0, #0x90000000 + add r0, r0,#0x00030000 + add r0, r0,#0x00000004 +/* ldr r0, #0x90030004 */ + ldr r0, [r0] + teq r0, #8 + bne Lerror + + /* + * test if saved state before slept: + * software sleep must be set + * battery fault may NOT? be set + * vdd fault must not be set + * YYY at some point need to deal with faulted sleep modes here + */ + mov r0, #0x9000000 + add r0,r0,#0x00020000 + add r0,r0,#0x00000004 +/* ldr r0, #0x90020004 */ + ldr r0, [r0] + and r0, r0, #7 + teq r0, #1 + bne Lwerror + +/* + * First thing to do is to get the page tables set up so that we can call the + * kernel + * in the correct place. This is relocatable code... + */ + + adr r5, coproc_data + ldmia r5, {r6, r8} + + mov r4, r8 @ page table gets set by flush function + mov lr, pc + b Larmv4_flush_early @ Call processor flush (returns ctrl reg) + + ldr lr, Lbranch_sleep + mcr p15, 0, r6, c1, c0 @ Enable MMU & caches. In 3 instructions + @ we lose this page! + mov pc, lr + + nop + nop + nop + + +Lbranch_sleep: .long Lalready_done_mmap_sleep @ Real address of routine + +/* + * don't change these without changing the matching wakeup code +(sa1100-idle.S) + */ +coproc_data: .long @ coproc15 register 1 + .long @ coproc15 register 2 + .long @ sp_before_sleep + .align + +/* + * these three NOPs are placeholders for the data in coproc_data; + * for some reason the assembler isn't leaving space for them + */ + nop + nop + nop +Lalready_done_mmap_sleep: + +/* + * change supervisory bits to be supervisor mode (should this be here?) + * restore after to original status in virtual mode + */ + mrs r3, cpsr + bic r3, r3, #0x1f + orr r3, r3, #0x13 + msr cpsr, r3 + + adr r5, coproc_data + ldr sp, [r5, #8] @ Setup stack + + mov lr, pc + b Lsa_fastclock + + mov fp, #0 + b wakeup_from_sleep_mode + + + +/* diagnotic error messages (not anymore) */ +Lwerror: + b . +Larmv4_flush_early: + mov r0, #0 + mcr p15, 0, r0, c7, c7, 0 @ flush I,D caches on v4 + mcr p15, 0, r0, c7, c10, 4 @ drain write buffer on v4 + mcr p15, 0, r0, c9, c0 @ flush read buffer on v4 + mcr p15, 0, r0, c8, c7 @ flush I,D TLBs on v4 + mcr p15, 0, r4, c2, c0 @ load page table pointer + mov r0, #5 @ Domains 0, 1 = client + mcr p15, 0, r0, c3, c0 @ load domain access register + mrc p15, 0, r0, c1, c0 @ get control register v4 + @ + @ Setting with no alignment handler.. + orr r0, r0, #0x3d @ ....DPWC.M + @ Setting with alignment handler + @ orr r0, r0, #0x3f @ ....DPWCAM + orr r0, r0, #1 << 12 @ v4 supports separate I cache + mov pc, lr + + +Lsa_fastclock: mcr p15, 0, r4, c15, c1, 2 @ Enable clock switching + mov pc, lr + +Lerror: mov r0, #0x02000000 + mov r1, #0x11 + orr r1, r1, r1, lsl #8 + orr r1, r1, r1, lsl #16 + str r1, [r0], #4 + str r1, [r0], #4 + str r1, [r0], #4 + str r1, [r0], #4 + b Lerror + +#endif /* End of Simple PowerManager */ + + + /* diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/kernel/powermgr.c linux-2.4.0-rmk2-np2-fb1/arch/arm/kernel/powermgr.c --- linux-2.4.0-rmk2-np2-org/arch/arm/kernel/powermgr.c Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/kernel/powermgr.c Wed Feb 7 16:33:00 2001 @@ -0,0 +1,550 @@ +/* + * Simple Power Manager + * Copyright (c) Compaq Computer Corporation, 1998, 1999 + * + * Author: Carl Waldspurger + * + * $Log: powermgr.c,v $ + * Revision 2.4 2000/01/19 18:25:23 kerr + * More copyright conformance. + * + * Revision 2.3 1999/12/23 01:28:09 kerr + * Updated Copyright information for entire source tree. + * + * Revision 2.2 1999/05/12 18:01:06 caw + * Included changes from itsy_sessions branch. + * + * Revision 2.1.2.1 1999/04/21 01:26:16 caw + * Changed powerbutton initialization to powerbutton_powermgr_register(). + * + * Revision 2.1 1998/11/25 01:28:02 kerr + * Added clock speed change capability (with loadable module ioctl interface). + * Does not depend on sleep mode for implementation. + * + * Revision 2.0 1998/06/24 21:45:22 kramer + * Major Bump: Move all version numbers up to 2.0 for external release. + * -drew + * + * Revision 1.8 1998/06/16 19:19:47 kramer + * Added copyright information + * + * Revision 1.7 1998/04/14 02:25:43 caw + * Implemented updated suspend_check() interfaces. Added support for + * automatic timeout-based suspends based on kernel timers. + * + * Revision 1.6 1998/03/28 00:14:48 caw + * Export symbols to allow calls from modules. Minor cleanup. + * + * Revision 1.5 1998/03/27 02:01:11 caw + * Added missing include of "config.h", fixing missing procfs behavior. + * + * Revision 1.4 1998/03/26 01:01:55 kerr + * Power management using the official interface. It works! + * + * Revision 1.3 1998/03/26 00:34:26 caw + * Updated interface. Changed suspend/resume ordering. + * + * Revision 1.2 1998/03/21 02:04:38 caw + * Fixed procfs problem. Most functionality still untested. + * + * Revision 1.1 1998/03/21 02:02:18 caw + * Initial revision. + * + */ + +/* + * TODO + * + * - should kmalloc/copy client name fields + * - unregisters can cause impl not to meet resume/suspend ordering spec + * fix by using list of clients instead of array + * + */ + +/* From 2.4 log of Itsy project + * Porting from 2.0.x linux kernel to 2.4.x kernel + * -2001/2/7 Chester Kuo + * + */ + +/* + * debugging + * + */ + +#define POWERMGR_DEBUG (1) +#define POWERMGR_DEBUG_VERBOSE (1) + +/* + * includes + * + */ + + +#include +#include + +#include +#include +#include + +#ifdef CONFIG_PROC_FS +#include +#include +#endif + +#include + +/* + * constants + * + */ + +#define POWERMGR_NAME "powermgr" +#define POWERMGR_NAME_VERBOSE "Power Manager" +#define POWERMGR_VERSION_STRING "$Revision: 2.4 $" + +#define POWERMGR_NCLIENTS (32) + +/* + * types + * + */ + +typedef struct { + /* clients */ + powermgr_client clients[POWERMGR_NCLIENTS]; + + /* periodic timer */ + struct timer_list timer; + int check_jiffies; + int idle_jiffies; + + /* statistics */ + ulong suspend_check_count; + ulong suspend_count; + ulong resume_count; +} powermgr_state; + +/* + * globals + * + */ + +static powermgr_state powermgr_global; + +#ifdef CONFIG_PROC_FS +static int powermgr_get_info(char *, char **, off_t, int, int); +#endif /* CONFIG_PROC_FS */ + + +/* + * exported symbols + * + */ + +#if 0 +static struct symbol_table powermgr_syms = { +#include + X(powermgr_suspend_check), + X(powermgr_suspend), + X(powermgr_resume), + X(powermgr_register), + X(powermgr_unregister), +#include +}; +#endif + +/* + * powermgr_client operations + * + */ + +static inline int powermgr_client_valid(const powermgr_client *c) +{ + /* + * modifies: nothing + * effects: Returns TRUE iff c is a valid client. + * + */ + + return(c->id != POWERMGR_ID_INVALID); +} + +static inline void powermgr_client_copy(powermgr_client *to, + const powermgr_client *from) +{ + /* + * modifies: to + * effects: Copies all fields of from into to. + * + */ + + memcpy((void *) to, (void *) from, sizeof(powermgr_client)); +} + +/* + * procfs operations + * + */ + +#ifdef CONFIG_PROC_FS +static int powermgr_get_info(char *buf, + char **start, + off_t offset, + int length, + int unused) +{ + powermgr_state *m = &powermgr_global; + int len, i; + + /* format stats */ + len = sprintf(buf, + "suspend-checks: %8lu\n" + "suspends: %8lu\n" + "restores: %8lu\n", + m->suspend_check_count, + m->suspend_count, + m->resume_count); + + for (i = 0; i < POWERMGR_NCLIENTS; i++) + { + powermgr_client *c = &m->clients[i]; + if (powermgr_client_valid(c)) + len += sprintf(buf + len, + "client %2d: %s\n", + c->id, + (c->name == NULL) ? "" : c->name); + } + + return(len); +} +#endif /* CONFIG_PROC_FS */ + +/* + * powermgr_state operations + * + */ + +static void powermgr_state_init(powermgr_state *m) +{ + int i; + + /* initialize */ + memset((void *) m, 0, sizeof(powermgr_state)); + + /* invalidate clients */ + for (i = 0; i < POWERMGR_NCLIENTS; i++) + m->clients[i].id = POWERMGR_ID_INVALID; + + /* initialize stats */ + m->suspend_check_count = 0; + m->suspend_count = 0; + m->resume_count = 0; +} + +/* + * exported client operations + * + */ + +powermgr_id powermgr_register(const powermgr_client *client) +{ + /* + * modifies: powermgr_global + * effects: Register as a power manager client. The power manager + * will communicate via the client-specified callbacks. + * Returns a unique identifier for use with future calls, + * or POWERMGR_ID_INVALID if unable to register. + * storage: Data is copied from client, so it is safe to deallocate + * client after registering. + * caveats: The "id" field specified by client is ignored. + * + */ + + powermgr_state *m = &powermgr_global; + int i; + + /* search for free slot */ + for (i = 0; i < POWERMGR_NCLIENTS; i++) + { + powermgr_client *c = &m->clients[i]; + if (!powermgr_client_valid(c)) + { + /* copy client into available slot, update id */ + powermgr_client_copy(c, client); + c->id = i; + return(c->id); + } + } + + /* no free slots */ + return(POWERMGR_ID_INVALID); +} + +extern int powermgr_unregister(powermgr_id id) +{ + /* + * modifies: powermgr_global + * effects: Deregisters id as a power manager client. + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + + powermgr_state *m = &powermgr_global; + + /* fail if invalid id */ + if ((id < 0) || (id > POWERMGR_ID_MAX)) + return(POWERMGR_FAILURE); + + /* fail if no currently registered client with id */ + if (!powermgr_client_valid(&m->clients[id])) + return(POWERMGR_FAILURE); + + /* invalidate client */ + m->clients[id].id = POWERMGR_ID_INVALID; + return(POWERMGR_SUCCESS); +} + +/* + * exported power management operations + * + */ + +int powermgr_suspend_check(int idle_jiffies, int *retry_jiffies) +{ + /* + * modifies: retry_jiffies, powermgr_global + * effects: Returns TRUE iff all registered clients are willing + * to be suspended. Clients should be willing to + * suspend after an inactivity period of idle_jiffies. + * Otherwise returns FALSE and sets retry_jiffies to a + * reasonable time delay in jiffies, after which another + * check may return TRUE. + * + */ + + powermgr_state *m = &powermgr_global; + int i, suspendible, delay; + + /* initialize */ + suspendible = 1; + delay = 0; + + /* query each valid client */ + for (i = 0; i < POWERMGR_NCLIENTS; i++) + { + powermgr_client *c = &m->clients[i]; + if (powermgr_client_valid(c)) + if (c->suspend_check != NULL) + { + int check = (*(c->suspend_check))(c->cookie, idle_jiffies); + if (check != 0) + { + /* client doesn't want to suspend */ + suspendible = 0; + if (check > delay) + delay = check; + } + } + } + + /* update stats */ + m->suspend_check_count++; + + /* set retry time delay (measured in jiffies) */ + *retry_jiffies = delay; + + return(suspendible); +} + +int powermgr_suspend(void) +{ + /* + * modifies: powermgr_global + * effects: Forcibly suspends all registered clients, in the reverse order + * of registration (i.e. first to register suspends last). + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + + powermgr_state *m = &powermgr_global; + int i; + + /* suspend each valid client, in reverse registration order */ + for (i = POWERMGR_NCLIENTS - 1; i >= 0; i--) + { + powermgr_client *c = &m->clients[i]; + if (powermgr_client_valid(c)) + if (c->suspend != NULL) + (void) (*(c->suspend))(c->cookie); + } + + /* update stats */ + m->suspend_count++; + + /* always successful */ + return(POWERMGR_SUCCESS); +} + +int powermgr_resume(int resume_flags) +{ + /* + * modifies: powermgr_global + * effects: Resumes all registered clients, in the order of registration + * (i.e. first to register resumes first). + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + + powermgr_state *m = &powermgr_global; + int i; + + /* resume each valid client, in registration order */ + for (i = 0; i < POWERMGR_NCLIENTS; i++) + { + powermgr_client *c = &m->clients[i]; + if (powermgr_client_valid(c)) + if (c->resume != NULL) + (void) (*(c->resume))(c->cookie, resume_flags); + } + + /* update stats */ + m->resume_count++; + + /* always successful */ + return(POWERMGR_SUCCESS); +} + +int powermgr_init(void) +{ + /* + * modifies: powermgr_global + * effects: Initializes power manager. + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + + static int initialized = 0; + + /* initialize only once */ + if (!initialized) + { + extern void powerbutton_powermgr_register(void); + extern void general_powermgr_register(void); + + /* initialize global state */ + initialized = 1; + powermgr_state_init(&powermgr_global); + + /* register low-level clients */ + general_powermgr_register(); + powerbutton_powermgr_register(); + + /* everything OK */ + return(POWERMGR_SUCCESS); + } + + /* already initialized */ + return(POWERMGR_FAILURE); +} + +#if 0 +void powermgr_init_ksyms(void) +{ + /* + * effects: Exports powermgr kernel symbols for modules. + * caveats: Separate function due to boot order restrictions. + * + */ + +#ifdef CONFIG_MODULES + /* export symbols */ + (void) register_symtab(&powermgr_syms); +#endif /* CONFIG_MODULES */ +} +#endif + +void powermgr_init_procfs(void) +{ +#ifdef CONFIG_PROC_FS + struct proc_dir_entry *entry; + /* + * effects: Registers /proc/powermgr device. + * caveats: Separate function due to boot order restrictions. + * + */ + + /* register procfs entry */ + entry = create_proc_entry("powermgr", + S_IRUSR | S_IRGRP | S_IROTH, + &proc_root); + if(entry) { + entry->read_proc = powermgr_get_info; + } else { + printk(KERN_ERR + ": can't create /proc/powermgr \n"); + return(-ENOMEM); + } + +#endif /* CONFIG_PROC_FS */ +} + +static void powermgr_suspend_timeout(unsigned long ignore) +{ + /* + * requires: powermgr_global.timer initialized + * modifies: powermgr_global + * effects: Invoked by kernel timer mechanism. + * + */ + +// extern void sa1100_sleep_mode_enter(void); + + powermgr_state *m = &powermgr_global; + int retry_jiffies = 0; + + /* suspend if possible, otherwise wait retry_jiffies */ +#if 0 + if (powermgr_suspend_check(m->idle_jiffies, &retry_jiffies)) + sa1100_sleep_mode_enter(); +#endif + + /* restart timer */ + if (retry_jiffies < m->check_jiffies) + retry_jiffies = m->check_jiffies; + m->timer.expires = jiffies + retry_jiffies; + add_timer(&m->timer); +} + +void powermgr_start_timer(int idle_jiffies, int check_jiffies) +{ + /* + * modifies: powermgr_global + * effects: Starts periodic timer to initiate suspends. Clients are + * recommended to suspend after idle_jiffies of inactivity. + * Suspend checks will be made at most once every check_jiffies. + * + */ + + powermgr_state *m = &powermgr_global; + + /* save timeouts */ + m->check_jiffies = check_jiffies; + m->idle_jiffies = idle_jiffies; + + /* initialize timer */ + init_timer(&m->timer); + m->timer.function = powermgr_suspend_timeout; + m->timer.data = 0; + m->timer.expires = jiffies + check_jiffies; + + /* start timer */ + add_timer(&m->timer); +} + +EXPORT_SYMBOL(powermgr_suspend_check); +EXPORT_SYMBOL(powermgr_suspend); +EXPORT_SYMBOL(powermgr_resume); +EXPORT_SYMBOL(powermgr_register); +EXPORT_SYMBOL(powermgr_unregister); diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/Makefile linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/Makefile --- linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/Makefile Thu Feb 8 13:26:57 2001 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/Makefile Wed Feb 7 16:33:57 2001 @@ -12,7 +12,7 @@ # Object file lists. obj-y := arch.o hw.o dma-sa1100.o # mm.o -obj-m := gpio.o +obj-m := gpio.o regmon.o obj-n := obj- := @@ -24,6 +24,8 @@ obj-$(CONFIG_APM) += apm.o obj-$(CONFIG_SA1100_USB) += sa1100_usbd.o obj-$(CONFIG_PROFILER) += hwtimer.o +#obj-$(CONFIG_SA1100_PB) += powerbutton.o +obj-$(CONFIG_SA1100_SLEEP) += sa1100-sleep-mode.o sa1100-sleep.o powerbutton.o SA1100_USBD_OBJS = usb_ctl.o usb_ep0.o usb_recv.o usb_send.o usb-eth.o diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/hw.c linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/hw.c --- linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/hw.c Thu Feb 8 13:26:57 2001 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/hw.c Wed Feb 7 16:36:28 2001 @@ -270,6 +270,9 @@ yopy_gpio_init(); #endif } +#ifdef CONFIG_SA1100_SPM + powermgr_init_procfs(); +#endif return 0; } diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/itsy_util.h linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/itsy_util.h --- linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/itsy_util.h Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/itsy_util.h Wed Feb 7 16:36:59 2001 @@ -0,0 +1,82 @@ +/* + * Itsy Driver Utility Header + * Copyright (c) Compaq Computer Corporation, 1999 + * + * Author: Carl Waldspurger + * + * $Log: itsy_util.h,v $ + * Revision 2.3 1999/12/23 01:27:58 kerr + * Updated Copyright information for entire source tree. + * + * Revision 2.2 1999/02/19 19:38:23 caw + * Defined KOBJ_DEALLOC() macro. + * + * Revision 2.1 1999/02/12 22:41:28 caw + * Initial revision. + * + */ + +#ifndef ITSY_UTIL_H +#define ITSY_UTIL_H + +/* + * useful macros + * + */ + +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) +#endif + +#define KOBJ_ALLOC(obj) ((obj *) kmalloc(sizeof(obj), GFP_KERNEL)) +#define KOBJ_INIT(x, obj) (memset((void *) (x), 0, sizeof(obj))) +#define KOBJ_DEALLOC(x, obj) (kfree_s((void *) x, sizeof(obj))) + +#define SAFE_STRING(s) (((s) != NULL) ? (s) : "") + +/* + * generic list operation macros + * + */ + +#define INSTANTIATE_LIST_LOOKUP(ELT) \ +static ELT * ELT ## _lookup(ELT *head, int id) \ +{ \ + ELT *e; \ + \ + /* search element list for matching id */ \ + for (e = head; e != NULL; e = e->next) \ + if (e->id == id) \ + return(e); \ + \ + /* not found */ \ + return(NULL); \ +} + +#define INSTANTIATE_LIST_INSERT(ELT) \ +static void ELT ## _insert(ELT **head, ELT *e) \ +{ \ + ELT *h = *head; \ + \ + /* add element to head of list */ \ + e->next = h; \ + if (h != NULL) \ + h->prev = e; \ + *head = e; \ + e->prev = NULL; \ +} + +#define INSTANTIATE_LIST_REMOVE(ELT) \ +static void ELT ## _remove(ELT **head, ELT *e) \ +{ \ + /* splice element out of list */ \ + if (e->prev != NULL) \ + e->prev->next = e->next; \ + else \ + *head = e->next; \ + if (e->next != NULL) \ + e->next->prev = e->prev; \ +} + +#endif /* ITSY_UTIL_H */ diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/powerbutton.c linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/powerbutton.c --- linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/powerbutton.c Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/powerbutton.c Wed Feb 7 16:37:19 2001 @@ -0,0 +1,1001 @@ +/* + * Itsy Powerbutton Device + * Copyright (c) Compaq Computer Corporation, 1998, 1999 + * + * Authors: Deborah A. Wallach and Carl Waldspurger + * + * $Log: powerbutton.c,v $ + * Revision 2.7 2000/01/11 22:35:32 kerr + * Support for deep sleep mode. + * + * Revision 2.6 1999/12/23 01:28:01 kerr + * Updated Copyright information for entire source tree. + * + * Revision 2.5 1999/12/11 04:45:36 mann + * Power manager support for flash. + * Powerbutton bugfix: don't try to go to sleep from interrupt level, as + * drivers may be in an unclean state. + * + * Revision 2.4 1999/10/08 21:58:57 kerr + * Include linux/version.h so dependencies are correct. + * + * Revision 2.3 1999/09/10 21:41:18 caw + * Updated to reflect changes to itsy_event_header field names. + * + * Revision 2.2 1999/05/21 19:20:26 brakmo + * Modified so releasing the button doesn't wake it up (used in gmanager) + * + * Revision 2.1 1999/05/12 18:01:05 caw + * Included changes from itsy_sessions branch. + * + * Revision 2.0.2.3 1999/04/23 22:06:02 caw + * Added support for POWERBUTTON_ENTER_SLEEP_MODE operation. + * Expanded overview documentation. + * + * Revision 2.0.2.2 1999/04/23 20:04:21 caw + * Updated to support both up and down events. Other minor improvements. + * + * Revision 2.0.2.1 1999/04/21 01:24:30 caw + * Substantial rewrite, with support for file I/O and button events. + * Backwards-compatibility OK, but new features still experimental. + * + */ + +/* + * Overview + * + * This driver supports interrupt-driven button input for the special + * power button on the Itsy platform. The original driver (written + * completely by Debby Wallach) invoked sleep mode whenever this button + * was pressed and released. The current driver also supports standard + * file I/O operations, plus an ioctl() to control whether or not sleep + * mode should be entered. + * + * Note that the routine powerbutton_powermgr_register() is invoked + * early, prior to the main powerbutton_init() initialization routine. + * This is done in the powermgr initialization code to ensure that the + * powerbutton device is registered in the appropriate order. The global + * variable pb_pm_ready is used as a guard to prevent any early callbacks + * from the powermgr from accessing uninitialized data. + * + */ + +/* Log from 2.7 itsy project + * + * -2001/2/2 Chester Kuo + * Porting from linux kernel 2.0.x versiion of itsy project to kernel 2.4.x + * + */ + +/* + * debugging + * + */ + +#define PB_DEBUG (1) +#define PB_DEBUG_VERBOSE (1) + +#define CONFIG_POWERMGR 1 +/* + * includes + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include + +#include +#include +#include + +#ifdef CONFIG_PROC_FS +#include +#include +#endif + +#ifdef CONFIG_POWERMGR +#include +#endif + +//#include +//#include +#include +//#include + + +/* + * externals + * + */ + +extern void sa1100_sleep_mode_enter(void); +extern void sa1100_deep_sleep_mode_enter(void); +/* + * constants + * + */ + +/* names */ +#define PB_NAME "SA11x0-PowerButton" +#define PB_NAME_VERBOSE "SA11x0 PowerButton driver" +#define PB_VERSION_STRING "$Revision: 2.8 $" + +/* device number */ +#define PB_MAJOR (120) + +/* interrupt number */ +#define PB_IRQ (IRQ_GPIO0) + +/* buffer sizes */ +#define PB_BUFFER_LG_NEVENTS (6) +#define PB_BUFFER_NEVENTS (1 << PB_BUFFER_LG_NEVENTS) +#define PB_BUFFER_INDEX_MASK (PB_BUFFER_NEVENTS - 1) + +#define MAX(a,b) ((a>b)?a:b) + + +static char *dev_id = "sa1100-powerbutton"; +static wait_queue_head_t waitq; +static int my_counter; +static DECLARE_TASK_QUEUE(tq_scheduler); + +/* + * types + * + */ + +typedef struct { + struct timeval timestamp; + unsigned char event_class; + unsigned char event_type; + unsigned char size; + unsigned char flags; +} event_header; + +typedef struct { + event_header common; + int sid; + unsigned short state; +} buttons_event; + +/* I know this is dirty work,let me know if you have good idea */ +/* memory-mapped GPIO registers */ +typedef struct { + volatile unsigned int pin_level; + volatile unsigned int pin_direction; + volatile unsigned int pin_out_set; + volatile unsigned int pin_out_clear; + volatile unsigned int rising_edge; + volatile unsigned int falling_edge; + volatile unsigned int edge_detect; + volatile unsigned int alternate_fun; +} gpio_reg; + +#define GPIO_BASE_V ((gpio_reg *) GPLR) + +typedef struct pb_client { + /* linked list */ + struct pb_client *prev, *next; + + /* identity */ + int id; + + /* allow sleep mode? */ + int enable_sleep; + + /* buffer management */ + int buf_next; + + /* dropped events */ + int drop_count; +} pb_client; + +typedef struct pb_dev { + /* memory-mapped regs */ + gpio_reg *gpio; + + /* buffer management */ +// struct wait_queue *waitq; + buttons_event *buf; + int buf_events; + int buf_bytes; + + /* buffer indexing */ + int buf_mask; + int buf_next; + + /* clients */ + pb_client *clients; + int client_id_next; + int nclients; + + /* statistics */ + unsigned long interrupt_count; + unsigned long event_count; + +#ifdef CONFIG_POWERMGR + /* power management */ + powermgr_id pm_id; + int resuming; +#endif +} pb_dev; + +/* + * globals + * + */ + +/* early powermgr initialization (ugh!) */ +static powermgr_id pb_pm_id = POWERMGR_ID_INVALID; +static int pb_pm_ready = 0; + +static pb_dev pb_global; + + +#ifdef CONFIG_PROC_FS +static int pb_procfs_info(char *, char **, off_t, int, int); +#endif /* CONFIG_PROC_FS */ + +/* + * instantiate list operations + * + */ + +#if 0 +INSTANTIATE_LIST_INSERT(pb_client); +INSTANTIATE_LIST_REMOVE(pb_client); +#endif + +/* + * procfs operations + * + */ +#ifdef CONFIG_PROC_FS +static int pb_procfs_info(char *buf, + char **start, + off_t offset, + int length, + int unused) +{ + pb_dev *dev = &pb_global; + int len = 0; + + /* format debugging info */ + len += sprintf(buf + len, + "debugging:\n" + " %5d pb_pm_ready\n", + pb_pm_ready); + + /* format current state */ + len += sprintf(buf + len, + "statistics:\n" + " %5ld interrupts\n" + " %5ld events\n" + " %5d clients\n", + dev->interrupt_count, + dev->event_count, + dev->nclients); + + /* format client info */ + if (dev->clients != NULL) + { + pb_client *c; + + len += sprintf(buf + len, "clients:\n"); + for (c = dev->clients; c != NULL; c = c->next) + len += sprintf(buf + len, + " %5d %s\n", + c->id, + (c->enable_sleep ? "yes" : "no")); + } + + return(len); +} +#endif /* CONFIG_PROC_FS */ + +/* + * memory-mapped register operations + * + */ + +static inline void pb_gpio_clear(gpio_reg *gpio) +{ + /* clear pending interrupts, if any */ + GEDR |= (1 << PB_IRQ); +} + +static inline void pb_gpio_setup(gpio_reg *gpio) +{ + if (PB_DEBUG_VERBOSE) + printk(PB_NAME "into pb_gpio_setup function\n"); + /* clear interrupts */ + pb_gpio_clear(gpio); + + /* setup interrupts on both button up and down */ + GPDR &= ~( 0x00000001); + set_GPIO_IRQ_edge(GPIO_GPIO0,GPIO_BOTH_EDGES); +} + +static inline void pb_gpio_suspend_setup(gpio_reg *gpio) +{ + /* clear interrupts */ + pb_gpio_clear(gpio); + + /* setup interrupts only on button down */ + /* gpio->rising_edge |= (1 << PB_IRQ); */ + gpio->rising_edge &= ~(1 << PB_IRQ); + + gpio->falling_edge |= (1 << PB_IRQ); + /* gpio->falling_edge &= ~(1 << PB_IRQ); */ +} + +static inline void pb_enable_wakeup(void) +{ + /* enable power button wakeup */ + if (PB_DEBUG_VERBOSE) + printk(PB_NAME "into pb_enable_wakeup function\n"); +#if 0 + *((uint *) PWER) |= (1 << PB_IRQ); +#endif +} + +/* + * pb_client operations + * + */ + +static pb_client *pb_client_create(int id) +{ + /* + * modifies: nothing + * effects: Returns a new, initialized pb_client, or + * NULL if unable to allocate storage. + */ + + pb_client *client; + + /* allocate storage, fail if unable */ + client = (pb_client *)kmalloc(sizeof(pb_client),GFP_KERNEL); + if (client == NULL) + return(NULL); + memset(client,0,sizeof(pb_client)); + + /* set identity */ + client->id = id; + + /* enable sleep mode by default */ + client->enable_sleep = 1; + + /* everything OK */ + return(client); +} + +static void pb_client_destroy(pb_client *client) +{ + /* + * modifies: client + * effects: Reclaims all storage associated with client. + * + */ + + kfree(client); +} + +#ifdef CONFIG_POWERMGR +/* + * power management operations + * + */ + +static int powerbutton_suspend_check(void *ignore, int idle_jiffies) +{ + /* + * modifies: nothing + * effects: Always returns zero; i.e. willing to sleep immediately. + * + */ + + return(0); +} + +/* n.b. also called externally from wakeup_from_sleep_mode() */ +int powerbutton_suspend(void *ignore) +{ + pb_dev *dev = &pb_global; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_suspend\n"); + + if (!pb_pm_ready) + return(0); + + pb_gpio_suspend_setup(dev->gpio); + pb_enable_wakeup(); + disable_irq(PB_IRQ); + + /* return value ignored */ + return(0); +} + +static int powerbutton_resume(void *ignore, int resume_flags) +{ + pb_dev *dev = &pb_global; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_resume\n"); + + if (!pb_pm_ready) + return(0); + + /* set flag */ + dev->resuming = 1; + + pb_gpio_setup(dev->gpio); + enable_irq(PB_IRQ); + + /* return value ignored */ + return(0); +} + +static const powermgr_client pb_powermgr = { + /* callback functions */ + powerbutton_suspend_check, + powerbutton_suspend, + powerbutton_resume, + + /* uninterpreted client data */ + NULL, + + /* identity */ + POWERMGR_ID_INVALID, + PB_NAME_VERBOSE, + + /* power-consumption info */ + 0, + 0 +}; +#endif /* CONFIG_POWERMGR */ + +/* + * pb_dev operations + * + */ + +static int pb_dev_init(pb_dev *dev) +{ + /* + * modifies: dev + * effects: Initializes powerbutton device dev. + * Returns 0 iff successful, otherwise error code. + */ + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME "into pb_dev_init function\n"); + memset(dev,0,sizeof(pb_dev)); + + /* allocate buffer, fail if unable */ + dev->buf_events = PB_BUFFER_NEVENTS; + dev->buf_bytes = dev->buf_events * sizeof(buttons_event); + dev->buf = (buttons_event *)kmalloc(dev->buf_bytes, GFP_KERNEL); + if (dev->buf == NULL) + return(-ENOMEM); + + /* buffer indexing */ + dev->buf_mask = PB_BUFFER_INDEX_MASK; + dev->buf_next = 0; + + /* initialize gpio */ + dev->gpio = GPIO_BASE_V; + +#ifdef CONFIG_POWERMGR + /* initialize powermgr state */ + dev->pm_id = POWERMGR_ID_INVALID; + dev->resuming = 0; +#endif /* CONFIG_POWERMGR */ + + init_waitqueue_head(&waitq); + + /* everything OK */ + return(0); +} + +static void pb_dev_event(pb_dev *dev, unsigned short state) +{ + /* + * modifies: buf + * effects: Creates event with specified state. Event is added to the + * buffer associated with dev. Wakes up any clients that may + * be blocked waiting for the event. + * + */ + + buttons_event *next; + + /* use next slot in circular buffer */ + next = &dev->buf[dev->buf_next & dev->buf_mask]; + + /* generic session event */ + //Chester + //do_gettimeofday(&next->common.timestamp); + + next->common.event_class = EVENT_CLASS_BUTTONS; + next->common.event_type = EVENT_BUTTONS_STATE; + next->common.size = sizeof(buttons_event); + next->common.flags = EVENT_FLAG_NONE; + + /* specific buttons event */ + next->sid = SESSION_ID_NONE; + next->state = state; + + /* advance buffer index */ + dev->buf_next++; + + /* wakeup waiters, if any */ + wake_up_interruptible(&waitq); + + /* update statistics */ + dev->event_count++; +} + +/* + * interrupt processing + * + */ + +static int pb_sleep_enabled(pb_dev *dev) +{ + pb_client *c; + + /* disallowed if disabled by any client */ + for (c = dev->clients; c != NULL; c = c->next) + if (!c->enable_sleep) + return(0); + + /* enabled */ + return(1); +} + +static void pb_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + pb_dev *dev = &pb_global; + gpio_reg *gpio = dev->gpio; + unsigned int button_up; + + /* determine button state */ + button_up = GPLR & (1 << PB_IRQ); + + /* clear edge detect register */ + pb_gpio_clear(gpio); + + /* buffer event */ + pb_dev_event(dev, button_up ? 0 : BUTTON_POWER); + + /* update statistics */ + dev->interrupt_count++; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_interrupt %ld, up=%u\n", + dev->interrupt_count, + button_up); + +#ifdef CONFIG_POWERMGR + printk(PB_NAME ": into power manager function\n"); + /* on button up, enter sleep mode if enabled */ + if (button_up && pb_sleep_enabled(dev)) + { + /* enter sleep mode, unless resuming */ + if (dev->resuming) { + dev->resuming = 0; + } else { + /* Wait until we are no longer at interrupt level --mann */ + static struct tq_struct tq; + tq.routine = (void (*)(void*)) sa1100_sleep_mode_enter; + tq.data = NULL; + printk(PB_NAME ": into sleep mode function\n"); + queue_task(&tq, &tq_scheduler); + } + run_task_queue(&tq_scheduler); + } +#endif /* CONFIG_POWERMGR */ + //Chester +#if 0 + if (button_up) { + my_counter++; + if(my_counter % 2) { + BCR_clear(BCR_FREEBIRD_LCD_DISP|BCR_FREEBIRD_LCD_PWR|BCR_FREEBIRD_LCD_BACKLIGHT); + } else { + BCR_set(BCR_FREEBIRD_LCD_DISP| BCR_FREEBIRD_LCD_PWR |BCR_FREEBIRD_LCD_BACKLIGHT); + } + } +#endif +} + +static void pb_hardware_init(pb_dev *dev) +{ + if (PB_DEBUG_VERBOSE) + printk(PB_NAME "into pb_hardware_init function\n"); + pb_gpio_setup(dev->gpio); + pb_enable_wakeup(); +} + +/* + * file operations + * + */ + +#ifdef CONFIG_POWERMGR +static int pb_enter_sleep_mode(void) +{ + /* must be root to force sleep */ + if (!suser()) + return(-EPERM); + + /* enter sleep mode */ + sa1100_sleep_mode_enter(); + return(0); +} + +static int pb_enter_deep_sleep_mode(void) +{ + /* must be root to force sleep */ + if (!suser()) + return(-EPERM); + + /* enter sleep mode */ + sa1100_deep_sleep_mode_enter(); + return(0); +} +#endif /* CONFIG_POWERMGR */ + +static int pb_dev_get_sleep(pb_dev *dev, int *enabled) +{ + /* copyout reply value */ + //put_user(pb_sleep_enabled(dev), enabled); + return(0); +} + +static int pb_client_get_sleep(pb_client *client, int *enabled) +{ + /* copyout reply value */ + //put_user(client->enable_sleep, enabled); + return(0); +} +static int pb_client_set_sleep(pb_client *client, int enable) +{ + /* update client flag */ + client->enable_sleep = enable; + return(0); +} + +static int pb_ioctl(struct inode *inode, + struct file *file, + uint command, + ulong arg) +{ + pb_client *client = (pb_client *) file->private_data; + pb_dev *dev = &pb_global; + int size, err; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_ioctl: cmd=%d, arg=%lu\n", command, arg); + + /* sanity checks */ + if (_IOC_TYPE(command) != POWERBUTTON_IOCTL_TYPE) + return(-EINVAL); + if (_IOC_NR(command) > POWERBUTTON_IOCTL_MAXNR) + return(-EINVAL); + + /* verify transfer memory */ + size = _IOC_SIZE(command); + if (_IOC_DIR(command) & _IOC_READ) + //dirty +#if 0 + if ((err = verify_area(VERIFY_WRITE, (void *) arg, size)) != 0) + return(err); + if (_IOC_DIR(command) & _IOC_WRITE) + if ((err = verify_area(VERIFY_READ, (void *) arg, size)) != 0) + return(err); + #endif + + /* dispatch based on command */ + switch (command) { + case POWERBUTTON_GET_SLEEP_ALL: + return(pb_dev_get_sleep(dev, (int *) arg)); + case POWERBUTTON_GET_SLEEP: + return(pb_client_get_sleep(client, (int *) arg)); + case POWERBUTTON_SET_SLEEP: + return(pb_client_set_sleep(client, (int) arg)); +#ifdef CONFIG_POWERMGR + case POWERBUTTON_ENTER_SLEEP_MODE: + return(pb_enter_sleep_mode()); + case POWERBUTTON_ENTER_DEEP_SLEEP_MODE: + return(pb_enter_deep_sleep_mode()); +#endif /* CONFIG_POWERMGR */ + } + + /* invalid command */ + return(-EINVAL); +} + +static int pb_read(struct inode *inode, + struct file *file, + char *buffer, + int count) +{ + /* + * modifies: buffer + * effects: Copies available powerbutton events, if any, to buffer. + * Returns the number of bytes copied. + * + */ + + const int event_bytes = sizeof(buttons_event); + + pb_client *client = (pb_client *) file->private_data; + pb_dev *dev = &pb_global; + int total, buf_first, err; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_read\n"); + + /* verify user-space buffer validity */ + //dirty +#if 0 + if ((err = verify_area(VERIFY_WRITE, (void *) buffer, count)) != 0) + return(err); +#endif + + /* initialize */ + total = 0; + + /* determine first available buffered event */ + //Chester + buf_first = MAX(0, dev->buf_next - dev->buf_events); + + /* skip over dropped events */ + if (client->buf_next < buf_first) + { + client->buf_next = buf_first; + client->drop_count++; + } + + /* transfer integral number of events */ + for (; + (client->buf_next < dev->buf_next) && (count >= event_bytes); + client->buf_next++) + { + buttons_event *event; + + /* examine next buffered event */ + event = &dev->buf[client->buf_next & dev->buf_mask]; + + /* copyout event */ + //memcpy_tofs((void *) buffer, (const void *) event, event_bytes); + + /* update counts */ + total += event_bytes; + buffer += event_bytes; + count -= event_bytes; + } + + + return(total); +} + +#if 0 +static int pb_select(struct inode *inode, + struct file *file, + int mode, + /*select_table *table*/) +{ + pb_client *client = (pb_client *) file->private_data; + pb_dev *dev = &pb_global; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_select\n"); + + /* handle select on input */ + if (mode == SEL_IN) + { + /* any events buffered? */ + if (client->buf_next < dev->buf_next) + return(1); + + /* wait for event */ + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_select: wait...\n"); + //select_wait(&dev->waitq, table); + return(0); + } + + /* SEL_OUT, SEL_EX not supported */ + return(0); +} +#endif + +static int pb_open(struct inode *inode, struct file *file) +{ + pb_dev *dev = &pb_global; + pb_client *client; + int id; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_open\n"); + + /* add new client, fail if unable */ + id = dev->client_id_next++; + if ((client = pb_client_create(id)) == NULL) + return(-ENOMEM); + + /* skip over old events */ + client->buf_next = dev->buf_next; + +#if 0 + /* add to client list */ + pb_client_insert(&dev->clients, client); + dev->nclients++; +#endif + + /* attach client state to file */ + file->private_data = client; + + MOD_INC_USE_COUNT; + /* everything OK */ + return(0); +} + +static void pb_release(struct inode *inode, struct file *file) +{ + pb_client *client = (pb_client *) file->private_data; + pb_dev *dev = &pb_global; + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": pb_release\n"); + +#if 0 + /* remove from client list */ + pb_client_remove(&dev->clients, client); + dev->nclients--; +#endif + + /* reclaim storage */ + pb_client_destroy(client); + file->private_data = NULL; + + MOD_DEC_USE_COUNT; +} + +static struct file_operations pb_fops = +{ + read: pb_read, + //pb_select, + ioctl: pb_ioctl, + open: pb_open, + release:pb_release, +}; + +/* + * initialization + * + */ + +static char *pb_version(void) +{ + /* + * modifies: nothing + * effects: Returns the current cvs revision number. + * storage: Return value is statically-allocated. + * + */ + + static char version[8]; + char *revision; + int i = 0; + + /* extract version number from cvs string */ + for (revision = PB_VERSION_STRING; *revision != '\0'; revision++) + if (isdigit((int) *revision) || (*revision == '.')) + version[i++] = *revision; + version[i] = '\0'; + + return(version); +} + +void powerbutton_powermgr_register(void) +{ +#ifdef CONFIG_POWERMGR + /* register with power manager */ + pb_pm_id = powermgr_register(&pb_powermgr); +#endif /* CONFIG_POWERMGR */ +} + +static int __init powerbutton_init(void) +{ + static int initialized = 0; + struct proc_dir_entry *entry; + + pb_dev *dev = &pb_global; + + /* initialize only once */ + if (initialized++) + return(-EPERM); + + if (PB_DEBUG_VERBOSE) + printk(PB_NAME ": Power-button_init\n"); + + /* initialize global state */ + pb_dev_init(dev); + + /* initialize hardware */ + pb_hardware_init(dev); + if (PB_DEBUG_VERBOSE) + printk(PB_NAME "after pb_hardware_init function\n"); + + /* register device */ + if (register_chrdev(PB_MAJOR, "sa1100-pb", &pb_fops) != 0) + { + /* log error and fail */ + printk(PB_NAME ": unable to register major device %d\n", PB_MAJOR); + return -EBUSY; + } + +#ifdef CONFIG_PROC_FS + /* register procfs device */ + entry = create_proc_entry("sa1100-pb",S_IRUSR | S_IRGRP | S_IROTH,&proc_root); + + if (entry) { + entry->read_proc = pb_procfs_info; + } else { + printk(KERN_ERR ":can't create powerbutton proc directory\n"); + return(-ENOMEM); + } +#endif /* CONFIG_PROC_FS */ + +#ifdef CONFIG_POWERMGR + /* use result of earlier power manager registration */ + dev->pm_id = pb_pm_id; + if (dev->pm_id == POWERMGR_ID_INVALID) + if (PB_DEBUG) + printk(PB_NAME ": unable to register with power manager\n"); +#endif /* CONFIG_POWERMGR */ + + /* initialize shared IRQ */ + if (request_irq(PB_IRQ,pb_interrupt,SA_INTERRUPT| SA_SAMPLE_RANDOM,PB_NAME,dev_id)) + { + /* log error and fail */ + printk(PB_NAME ": request_irq failed\n"); + return -EBUSY; + } + + /* allow powermgr operations */ + pb_pm_ready = 1; + + /* log device registration */ + printk(PB_NAME_VERBOSE " version %s initialized\n", pb_version()); + + /* everything OK */ + return(0); +} + +module_init(powerbutton_init); + +static void __exit powerbutton_exit(void) +{ + unregister_chrdev(PB_MAJOR,PB_NAME); + freeirq(PB_IRQ,dev_id); + +} +module_exit(powerbutton_exit); diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/regmon.c linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/regmon.c --- linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/regmon.c Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/regmon.c Wed Feb 7 16:37:45 2001 @@ -0,0 +1,344 @@ + +/* + * regmon.c: Register monitor of SA-1110 + */ + +#include +#include +#include /* because we are a module */ +#include /* for the __init macros */ +#include /* all the /proc functions */ +#include +#include /* to copy to/from userspace */ +#include + +#define MODULE_NAME "regmon" +#define CPU_DIRNAME "cpu" +#define REG_DIRNAME "registers" + +static ssize_t proc_read_reg(struct file * file, char * buf, + size_t nbytes, loff_t *ppos); +static ssize_t proc_write_reg(struct file * file, const char * buffer, + size_t count, loff_t *ppos); + +static struct file_operations proc_reg_operations = { + read: proc_read_reg, + write: proc_write_reg +}; + +typedef struct sa1110_reg_entry { + u32 phyaddr; + char* name; + char* description; + unsigned short low_ino; +} sa1110_reg_entry_t; + +static sa1110_reg_entry_t sa1110_regs[] = +{ +/* {phyaddr, name, description} */ +{ 0x80000000, "UDCCR", "UDC control register" }, +{ 0x80000004, "UDCAR", "UDC address register" }, +{ 0x80000008, "UDCOMP", "UDC OUT max packet register" }, +{ 0x8000000C, "UDCIMP", "UDC IN max packet register" }, +{ 0x80000010, "UDCCS0", "UDC endpoint 0 control/status register" }, +{ 0x80000014, "UDCCS1", "UDC endpoint 1 (out) control/status register" }, +{ 0x80000018, "UDCCS2", "UDC endpoint 2 (in) control/status register" }, +{ 0x8000001C, "UDCD0", "UDC endpoint 0 data register" }, +{ 0x80000020, "UDCWC", "UDC endpoint 0 write count register" }, +{ 0x80000028, "UDCDR", "UDC transmit/receive data register (FIFOs)" }, +{ 0x80000030, "UDCSR", "UDC status/interrupt register" }, +{ 0x80010000, "UTCR0", "UART control register 0" }, +{ 0x80010004, "UTCR1", "UART control register 1" }, +{ 0x80030008, "UTCR2", "UART control register 2" }, +{ 0x8001000C, "UTCR3", "UART control register 3" }, +{ 0x80010014, "UTDR", "UART data register" }, +{ 0x8001001C, "UTSR0", "UART status register 0" }, +{ 0x80010020, "UTSR1", "UART status register 1" }, +{ 0x80020060, "GPCLKR0", "GPCLK Control Register 0" }, +{ 0x80020064, "GPCLKR1", "GPCLK Control Register 1" }, +{ 0x8002006C, "GPCLKR2", "GPCLK Control Register 2" }, +{ 0x80020070, "GPCLKR3", "GPCLK Control Register 3" }, +{ 0x80030000, "UTCR0", "UART control register 0" }, +{ 0x80030004, "UTCR1", "UART control register 1" }, +{ 0x80030008, "UTCR2", "UART control register 2" }, +{ 0x8003000C, "UTCR3", "UART control register 3" }, +{ 0x80030010, "UTCR4", "UART control register 4" }, +{ 0x80030014, "UTDR", "UART data register" }, +{ 0x8003001C, "UTSR0", "UART status register 0" }, +{ 0x80030020, "UTSR1", "UART status register 1" }, +{ 0x80040060, "HSCR0", "HSSP control register 0" }, +{ 0x80040064, "HSCR1", "HSSP control register 1" }, +{ 0x8004006C, "HSDR", "HSSP data register" }, +{ 0x80040074, "HSSR0", "HSSP status register 0" }, +{ 0x80040078, "HSSR1", "HSSP status register 1" }, +{ 0x80050000, "UTCR0", "UART control register 0" }, +{ 0x80050004, "UTCR1", "UART control register 1" }, +{ 0x80050008, "UTCR2", "UART control register 2" }, +{ 0x8005000C, "UTCR3", "UART control register 3" }, +{ 0x80050014, "UTDR", "UART data register" }, +{ 0x8005001C, "UTSR0", "UART status register 0" }, +{ 0x80050020, "UTSR1", "UART status register 1" }, +{ 0x80060000, "MCCR0", "MCP control register 0" }, +{ 0x80060008, "MCDR0", "MCP data register 0" }, +{ 0x8006000C, "MCDR1", "MCP data register 1" }, +{ 0x80060010, "MCDR2", "MCP data register 2" }, +{ 0x80060018, "MCSR", "MCP status register" }, +{ 0x80070060, "SSCR0", "SSP control register 0" }, +{ 0x80070064, "SSCR1", "SSP control register 1" }, +{ 0x8007006C, "SSDR", "SSP data register" }, +{ 0x80070074, "SSSR", "SSP status register" }, +{ 0x90000000, "OSMR0", "OS timer match registers 0" }, +{ 0x90000004, "OSMR1", "OS timer match registers 1" }, +{ 0x90000008, "OSMR2", "OS timer match registers 2" }, +{ 0x9000000C, "OSMR3", "OS timer match registers 3" }, +{ 0x90000010, "OSCR", "OS timer counter register" }, +{ 0x90000014, "OSSR", "OS timer status register" }, +{ 0x90000018, "OWER", "OS timer watchdog enable register" }, +{ 0x9000001C, "OIER", "OS timer interrupt enable register" }, +{ 0x90010000, "RTAR", "Real-time clock alarm register" }, +{ 0x90010004, "RCNR", "Real-time clock count register" }, +{ 0x90010008, "RTTR", "Real-time clock trim register" }, +{ 0x90010010, "RTSR", "Real-time clock status register" }, +{ 0x90020000, "PMCR", "Power manager control register" }, +{ 0x90020004, "PSSR", "Power manager sleep status register" }, +{ 0x90020008, "PSPR", "Power manager scratchpad register" }, +{ 0x9002000C, "PWER", "Power manager wakeup enable register" }, +{ 0x90020010, "PCFR", "Power manager configuration register" }, +{ 0x90020014, "PPCR", "Power manager PLL configuration register" }, +{ 0x90020018, "PGSR", "Power manager GPIO sleep state register" }, +{ 0x9002001C, "POSR", "Power manager oscillator status register" }, +{ 0x90030000, "RSRR", "Reset controller software reset register" }, +{ 0x90030004, "RCSR", "Reset controller status register" }, +{ 0x90030008, "TUCR", "Reserved for test" }, +{ 0x90040000, "GPLR", "GPIO pin level register" }, +{ 0x90040004, "GPDR", "GPIO pin direction register" }, +{ 0x90040008, "GPSR", "GPIO pin output set register" }, +{ 0x9004000C, "GPCR", "GPIO pin output clear register" }, +{ 0x90040010, "GRER", "GPIO rising-edge register" }, +{ 0x90040014, "GFER", "GPIO falling-edge register" }, +{ 0x90040018, "GEDR", "GPIO edge detect status register" }, +{ 0x9004001C, "GAFR", "GPIO alternate function register" }, +{ 0x90050000, "ICIP", "Interrupt controller irq pending register" }, +{ 0x90050004, "ICMR", "Interrupt controller mask register" }, +{ 0x90050008, "ICLR", "Interrupt controller FIQ level register" }, +{ 0x9005000C, "ICCR", "Interrupt controller control register" }, +{ 0x90050010, "ICFP", "Interrupt controller FIQ pending register" }, +{ 0x90050020, "ICPR", "Interrupt controller pending register" }, +{ 0x90060000, "PPDR", "PPC pin direction register" }, +{ 0x90060004, "PPSR", "PPC pin state register" }, +{ 0x90060008, "PPAR", "PPC pin assignment register" }, +{ 0x9006000C, "PSDR", "PPC sleep mode direction register" }, +{ 0x90060010, "PPFR", "PPC pin flag register" }, +{ 0x90060028, "HSCR2", "HSSP control register 2" }, +{ 0x90060030, "MCCR1", "MCP control register 1" }, +{ 0xA0000000, "MDCNFG", "DRAM configuration register" }, +{ 0xA0000004, "MDCAS00", "DRAM CAS waveform rotate register 0 for DRAM bank +pair 0/1" }, +{ 0xA0000008, "MDCAS01", "DRAM CAS waveform rotate register 1 for DRAM bank +pair 0/1" }, +{ 0xA000000C, "MDCAS02", "DRAM CAS waveform rotate register 2 for DRAM bank +pair 0/1" }, +{ 0xA0000010, "MSC0", "Static memory control register 0" }, +{ 0xA0000014, "MSC1", "Static memory control register 1" }, +{ 0xA0000018, "MECR", "Expansion bus configuration register" }, +{ 0xA000001C, "MDREFR", "DRAM refresh control register" }, +{ 0xA0000020, "MDCAS20", "DRAM CAS waveform rotate register 0 for DRAM bank +pair 2/3" }, +{ 0xA0000024, "MDCAS21", "DRAM CAS waveform rotate register 1 for DRAM bank +pair 2/3" }, +{ 0xA0000028, "MDCAS22", "DRAM CAS waveform rotate register 2 for DRAM bank +pair 2/3" }, +{ 0xA000002C, "MSC2", "Static memory control register 2" }, +{ 0xA0000030, "SMCNFG", "SMROM configuration register" }, +{ 0xB0000000, "DDAR0", "DMA device address register" }, +/* +{ 0xB0000004, "DCSR0", "DMA control/status register 0 - write ones to +set" }, +{ 0xB0000008, "DCSR0", "DMA control/status register 0 - write ones to +clear" }, +*/ +{ 0xB000000C, "DCSR0", "DMA control/status register 0 - read only" }, +{ 0xB0000010, "DBSA0", "DMA buffer A start address 0" }, +{ 0xB0000014, "DBTA0", "DMA buffer A transfer count 0" }, +{ 0xB0000018, "DBSB0", "DMA buffer B start address 0" }, +{ 0xB000001C, "DBTB0", "DMA buffer B transfer count 0" }, +{ 0xB0000020, "DDAR1", "DMA device address register 1" }, +/* +{ 0xB0000024, "DCSR1", "DMA control/status register 1 - write ones to +set" }, +{ 0xB0000028, "DCSR1", "DMA control/status register 1 - write ones to +clear" }, +*/ +{ 0xB000002C, "DCSR1", "DMA control/status register 1 - read only" }, +{ 0xB0000030, "DBSA1", "DMA buffer A start address 1" }, +{ 0xB0000034, "DBTA1", "DMA buffer A transfer count 1" }, +{ 0xB0000038, "DBSB1", "DMA buffer B start address 1" }, +{ 0xB000003C, "DBTB1", "DMA buffer B transfer count 1" }, +{ 0xB0000040, "DDAR2", "DMA device address register 2" }, +/* +{ 0xB0000044, "DCSR2", "DMA control/status register 2 - write ones to +set" }, +{ 0xB0000048, "DCSR2", "DMA control/status register 2 - write ones to +clear" }, +*/ +{ 0xB000004C, "DCSR2", "DMA control/status register 2 - read only" }, +{ 0xB0000050, "DBSA2", "DMA buffer A start address 2" }, +{ 0xB0000054, "DBTA2", "DMA buffer A transfer count 2" }, +{ 0xB0000058, "DBSB2", "DMA buffer B start address 2" }, +{ 0xB000005C, "DBTB2", "DMA buffer B transfer count 2" }, +{ 0xB0000060, "DDAR3", "DMA device address register 3" }, +/* +{ 0xB0000064, "DCSR3", "DMA control/status register 3 - write ones to +set" }, +{ 0xB0000068, "DCSR3", "DMA control/status register 3 - Write ones to +clear" }, +*/ +{ 0xB000006C, "DCSR3", "DMA control/status register 3 - Read only" }, +{ 0xB0000070, "DBSA3", "DMA buffer A start address 3" }, +{ 0xB0000074, "DBTA3", "DMA buffer A transfer count 3" }, +{ 0xB0000078, "DBSB3", "DMA buffer B start address 3" }, +{ 0xB000007C, "DBTB3", "DMA buffer B transfer count 3" }, +{ 0xB0000080, "DDAR4", "DMA device address register 4" }, +/* +{ 0xB0000084, "DCSR4", "DMA control/status register 4 - write ones to +set" }, +{ 0xB0000088, "DCSR4", "DMA control/status register 4 - write ones to +clear" }, +*/ +{ 0xB000008C, "DCSR4", "DMA control/status register 4 - read only" }, +{ 0xB0000090, "DBSA4", "DMA buffer A start address 4" }, +{ 0xB0000094, "DBTA4", "DMA buffer A transfer count 4" }, +{ 0xB0000098, "DBSB4", "DMA buffer B start address 4" }, +{ 0xB000009C, "DBTB4", "DMA buffer B transfer count 4" }, +{ 0xB00000A0, "DDAR5", "DMA device address register 5" }, +/* +{ 0xB00000A4, "DCSR5", "DMA control/status register 5 - write ones to +set" }, +{ 0xB00000A8, "DCSR5", "DMA control/status register 5 - write ones to +clear" }, +*/ +{ 0xB00000AC, "DCSR5", "DMA control/status register 5 - read only" }, +{ 0xB00000B0, "DBSA5", "DMA buffer A start address 5" }, +{ 0xB00000B4, "DBTA5", "DMA buffer A transfer count 5" }, +{ 0xB00000B8, "DBSB5", "DMA buffer B start address 5" }, +{ 0xB00000BC, "DBTB5", "DMA buffer B transfer count 5" }, +{ 0xB0100000, "LCCR0", "LCD controller control register 0" }, +{ 0xB0100004, "LCSR", "LCD controller status register" }, +{ 0xB0100010, "DBAR1", "DMA channel 1 base address register" }, +{ 0xB0100014, "DCAR1", "DMA channel 1 current address register" }, +{ 0xB0100018, "DBAR2", "DMA channel 2 base address register" }, +{ 0xB010001C, "DCAR2", "DMA channel 2 current address register" }, +{ 0xB0100020, "LCCR1", "LCD controller control register 1" }, +{ 0xB0100024, "LCCR2", "LCD controller control register 2" }, +{ 0xB0100028, "LCCR3", "LCD controller control register 3" } +}; + +#define NUM_OF_SA1110_REG_ENTRY (sizeof(sa1110_regs)/sizeof(sa1110_reg_entry_t)) + +static int proc_read_reg(struct file * file, char * buf, + size_t nbytes, loff_t *ppos) +{ + int i_ino = (file->f_dentry->d_inode)->i_ino; + char outputbuf[15]; + int count; + int i; + sa1110_reg_entry_t* current_reg=NULL; + if (*ppos>0) /* Assume reading completed in previous read*/ + return 0; + for (i=0;iphyaddr))); + *ppos+=count; + if (count>nbytes) /* Assume output can be read at one time */ + return -EINVAL; + if (copy_to_user(buf, outputbuf, count)) + return -EFAULT; + return count; +} +static ssize_t proc_write_reg(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + int i_ino = (file->f_dentry->d_inode)->i_ino; + sa1110_reg_entry_t* current_reg=NULL; + int i; + unsigned long newRegValue; + char *endp; + + for (i=0;iphyaddr))=newRegValue; + return (count+endp-buffer); +} + +static struct proc_dir_entry *regdir; +static struct proc_dir_entry *cpudir; + +static int __init init_reg_monitor(void) +{ + struct proc_dir_entry *entry; + int i; + + cpudir = proc_mkdir(CPU_DIRNAME, &proc_root); + if (cpudir == NULL) { + printk(KERN_ERR MODULE_NAME + ": can't create /proc/" CPU_DIRNAME "\n"); + return(-ENOMEM); + } + + regdir = proc_mkdir(REG_DIRNAME, cpudir); + if (regdir == NULL) { + printk(KERN_ERR MODULE_NAME + ": can't create /proc/" CPU_DIRNAME "/" REG_DIRNAME +"\n"); + return(-ENOMEM); + } + + for(i=0;ilow_ino; + entry->proc_fops = &proc_reg_operations; + } else { + printk(KERN_ERR MODULE_NAME + ": can't create /proc/" REG_DIRNAME + "/%s\n", sa1110_regs[i].name); + return(-ENOMEM); + } + } + return (0); +} + +static void __exit cleanup_reg_monitor(void) +{ + int i; + for(i=0;i + * Update to linux-2.4.x from linux-2.2.x tree + * + */ + +#include +#include +#include +#include +#include +#include +#include + +unsigned long sleep_mode_save_area[1024]; + +extern void rtc_set_alarm(int seconds); +extern void rtc_disable_alarm(void); +extern int rtc_alarm_happened(void); + +void sa1100_sleep_mode_enter(void) +{ +// extern void sa1100_sleep_mode(void *, unsigned long, unsigned long); + extern void entry_from_sleep(void); + extern unsigned long coproc_data; + unsigned long the_sp; + +#if 0 + if (is_itsy_v2()) { + /* do not turn off DRAM once in sleep mode */ +#if 0 + outl((inl(PGSR_V) & (~GPIO_2nDRAMEN)), PGSR_V); +#else + PGSR &=~(GPIO_GPIO (18)); +#endif + + } +#endif + + rtc_set_alarm(45); + + { + int delay; + if (powermgr_suspend_check(0, &delay)) { + powermgr_suspend(); + } + else { + printk("suspend_check failed with delay %d\n", delay); + return; + } + } + + PCFR = ( PCFR_ClkStp + PCFR_PCMCIANeg + PCFR_StMemNeg); + + /* get the stack pointer */ + __asm__ __volatile__("mov %0, sp" \ + : "=r" (the_sp) \ + :); + the_sp = virt_to_phys(the_sp); + + sa1100_sleep_mode(&coproc_data, /* where to write some info */ + virt_to_phys((unsigned long)entry_from_sleep), + the_sp + ); + + return; /* should never reach here */ +} + +void sa1100_deep_sleep_mode_enter(void) +{ + extern void sa1100_sleep_mode(void *, unsigned long, unsigned long); + extern void entry_from_sleep(void); +// extern void lcdOff(void); + +#if 0 + if (is_itsy_v1()) sa1100_sleep_mode_enter(); /* YYY interface for V1 not implemented yet in monitor */ + /* remove this check once implemented in monitor and it should be fine */ +#endif + + { + int delay; + if (powermgr_suspend_check(0, &delay)) { + powermgr_suspend(); + } + else { + printk("suspend_check failed with delay %d\n", delay); + return; + } + } + + /* Turn off lcd screen LSB */ +#ifdef CONFIG_SA1100_FREEBIRD + BCR_clear(BCR_FREEBIRD_LCD_DISP|BCR_FREEBIRD_LCD_PWR|BCR_FREEBIRD_LCD_BACKLIGHT); +#endif + +#if 0 + if (is_itsy_v2()) { + /* turn off DRAM once in sleep mode */ +#if 0 + outl((inl(PGSR_V) | GPIO_2nDRAMEN), PGSR_V); +#else + PGSR |=(GPIO_GPIO (18)); +#endif + } +#endif + + PCFR = (PCFR_ClkStp + PCFR_PCMCIANeg + PCFR_StMemNeg); + + PSPR = (0x01); + PMCR = (0x01); + while (1); + + return; /* should never reach here */ +} + +/* called after make the phys-to-virt transition */ +asmlinkage void wakeup_from_sleep_mode(void) +{ +// extern void wakeup_done(void); +// extern void sa1100_sleep_mode_final(void); + + if (rtc_alarm_happened()) { + extern void entry_from_sleep(void); + extern int powerbutton_suspend(void *); + // extern void lcdOff(void); + + /* turn off lcd screen */ +#ifdef CONFIG_SA1100_FREEBIRD + BCR_clear(BCR_FREEBIRD_LCD_DISP|BCR_FREEBIRD_LCD_PWR|BCR_FREEBIRD_LCD_BACKLIGHT); +#endif + + /* rewrite address to jump to on stack for monitor */ + /* get stack address from PSPR (scratch pad register) + * it's physical, so convert to virtual so we can write it + */ + + //Chester + //phys_to_virt(PSPR) = virt_to_phys((unsigned long)entry_from_sleep); + + /* hmm, can't test if we woke up due to button press? */ + + /* reinitialize button to wake us up */ + powerbutton_suspend(0); + + printk("woke up due to rtc alarm... going back to sleep\n"); + rtc_disable_alarm(); + sa1100_sleep_mode_final(); + return; /* should never reach here */ + } + + powermgr_resume(PSSR); + + /* wakeup is done and clean; clear the reset and power status bits */ + PSSR = 0x1f; + RCSR = 0x0f; + + wakeup_done(); + + return; /* won't actually reach here */ +} + + +/* This is here so flush_cache_all can be called from assembly */ +asmlinkage void call_flush_cache_all(void) +{ + flush_cache_all(); +} + + +/* power management functions */ +static unsigned long general_regs[4]; /* storage of general registers during sleep */ + +static int general_suspend_check(void *unused, int idle_jiffies) +{ + return(0); +} + +static int general_suspend(void *cookie) +{ + unsigned long *general_regs = (unsigned long *)cookie; + general_regs[1] = ICLR; + general_regs[2] = (ICCR & 1); /* only real bit */ + general_regs[3] = OSCR; + + return(1); /* YYY - values ignored for now */ +} + +static int general_resume(void *cookie, int resume_flags) +{ + unsigned long *general_regs = (unsigned long *)cookie; + + ICMR = 0x0; /* reset ICMR */ + /* outl(general_regs[0], IRQ_ICMR_V); *****XXX**** not until everyone masks properly */ + ICLR = general_regs[1]; + ICCR = general_regs[2]; + OSCR = general_regs[3]; + RCSR = 0x0f; + + return(1); /* YYY - values ignored for now */ +} + +void general_powermgr_register(void) +{ + powermgr_client client; + powermgr_id general_powermgr_id; + + client.suspend_check = general_suspend_check; + client.suspend = general_suspend; + client.resume = general_resume; + client.cookie = general_regs; + client.name = "General registers"; + client.uamps_idle = 0; /* YYY ? */ + client.uamps_sleep = 0; /* YYY ? */ + general_powermgr_id = powermgr_register(&client); + if (general_powermgr_id == POWERMGR_ID_INVALID) { + printk("Failed to register %s module for power management!!!\n", client.name); + return; + } + + /* clear the reset and power status bits */ + PSSR = 0x1f; + RCSR = 0xf; +} diff -urN linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/sa1100-sleep.S linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/sa1100-sleep.S --- linux-2.4.0-rmk2-np2-org/arch/arm/mach-sa1100/sa1100-sleep.S Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/arch/arm/mach-sa1100/sa1100-sleep.S Wed Feb 7 16:41:02 2001 @@ -0,0 +1,88 @@ +/* + * SA1100 Sleep Mode + * Copyright (c) Compaq Computer Corporation, 1998, 1999 + * + * Author: Deborah A. Wallach + * 03-25-98 + * -Redefine for the 2.4.x kernel + * -Chester Kuo + * + * Enter SA1100 sleep mode + * + */ + +#define PMCR_V 0xfa020000 /* Power manager control register */ +#define PSSR_V 0xfa020004 /* Power manager sleep status register */ +#define PSPR_V 0xfa020008 /* Power manager scratch pad register */ +#define PWER_V 0xfa02000c /* Power manager wakeup enable register */ +#define PCFR_V 0xfa020010 /* Power manager general configuration register */ +#define PPCR_V 0xfa020014 /* Power manager PLL configuration register */ +#define PGSR_V 0xfa020018 /* Power manager GPIO sleep state register */ +#define POSR_V 0xfa02001c /* Power manager oscillator status register */ +#define GFER_V 0xfa040014 /* GPIO Falling-Edge detect Register */ +#define GEDR_V 0xfa040018 /* GPIO Edge Detect status Register */ +#define GPDR_V 0xfa040004 /* GPIO pin direction register */ + +@ r0 is &coproc_data /* where to write some info */ +@ r1 is the physical address of entry_from_sleep (where to jump when wake up) +@ r2 is the physical address of the stack pointer in sleep_mode_enter + + .text + .align + .globl sa1100_sleep_mode + .globl sa1100_sleep_mode_final +sa1100_sleep_mode: + @ save coprocessor 15 register 1 (control) + mrc p15, 0, r3, c1, c0 + bic r3, r3, #0x0c00 @ these bits are not valid + bic r3, r3, #0xc000 + bic r3, r3, #0x00ff0000 + bic r3, r3, #0xff000000 + str r3, [r0, #0] + + @ save coprocessor 15 register 2 (tlb base) + mrc p15, 0, r3, c2, c0 + bic r3, r3, #0x00ff @ these bits are not valid + bic r3, r3, #0x3f00 + str r3, [r0, #4] + @ save stack pointer + str sp, [r0, #8] + + @ interface between monitor and linux: + @ store wakeup routine on stack for monitor + @ put stack address monitor can use in scratchpad register + stmfd sp!, {r1} + @ + sub r2, r2, #4 @ make r2 (phys addr of sp) match... + ldr r0, =PSPR_V + str r2, [r0] + + ldr r0, =sleep_mode_save_area + stmea r0!, {r4 - r9, fp, sp, lr} @ Store most regs + mrs ip, cpsr + stmea r0!, {ip} @ Save cpsr_SVC +sa1100_sleep_mode_final: + bl call_flush_cache_all + + @ PMCR = PMCR_SF (go to sleep!) + mov r0, #1 + ldr r1, =PMCR_V + str r0, [r1] + +@ mov r0, #'x' +@ bl serial_echo_print_wait_V + b . @ better not get here + + + +@ this function should be called at the end of wakeup; +@ it will restore the previous registers and stack and jump to the place +@ the code was at when it went to sleep + .globl wakeup_done +wakeup_done: + ldr sp, =sleep_mode_save_area + add sp, sp, #40 @ number of registers saved + ldmea sp!, {ip} + msr spsr, ip @ Save tasks CPSR into SPSR for this return + ldmea sp!, {r4 - r9, fp, sp, pc}^ @ Load all regs saved previously + diff -urN linux-2.4.0-rmk2-np2-org/drivers/char/sa1100-rtc.c linux-2.4.0-rmk2-np2-fb1/drivers/char/sa1100-rtc.c --- linux-2.4.0-rmk2-np2-org/drivers/char/sa1100-rtc.c Thu Feb 8 13:26:57 2001 +++ linux-2.4.0-rmk2-np2-fb1/drivers/char/sa1100-rtc.c Wed Feb 7 16:45:19 2001 @@ -16,6 +16,10 @@ * * 0.01 Nils Faerber * 10012000 initial release + * 0.02 Chester Kuo + * 2-2-2001 add function when into sleep mode,RTC clock still alive when + * into the sleep mode function,so we need it + * */ #include @@ -26,15 +30,27 @@ #include #include #include +#ifdef CONFIG_PROC_FS #include +#endif #include #include #include -#define DRIVER_VERSION "0.01" +#define DRIVER_VERSION "0.02" #define DEFAULT_DIVIDER 0x8000 #define DEFAULT_TRIM 0x0000 +/* For register */ + +#define RTSR_STATUS_MASK (3) +#define RTSR_ENABLE_MASK (0xc) +#define RTSR_CURRENT_ENABLED (RTSR & RTSR_ENABLE_MASK) +#define RTSR_ALARM_ENABLE (4) +#define RTSR_1HZ_ENABLE (0x8) +#define RTSR_ALARM_STATUS (1) +#define RTSR_1HZ_STATUS (2) + static int rtc_usage = 0; static int rtc_irq_data = 0; @@ -131,6 +147,56 @@ tval->tm_mon = y; tval->tm_mday = days + 1; } +/* For Sleep mode suport ,Chester */ +void rtc_set_alarm(int seconds) +{ + int rtsr_value = (RTSR_CURRENT_ENABLED | RTSR_ALARM_ENABLE); + RTAR = (RCNR + seconds); + if (seconds != 0) { + /* clear RTC alarm status bit */ + rtsr_value |= RTSR_ALARM_STATUS; + } + RTSR = rtsr_value; + PWER |= (1<< IRQ_RTCAlrm); +} +void rtc_clear_alarm(void) +{ + RTSR = ((RTSR_CURRENT_ENABLED & RTSR_1HZ_ENABLE) | RTSR_ALARM_STATUS); + PWER &= ~(1<< IRQ_RTCAlrm); +} +void rtc_disable_alarm(void) +{ + RTSR = (RTSR_CURRENT_ENABLED & RTSR_1HZ_ENABLE); + PWER &= ~(1<< IRQ_RTCAlrm); +} + +int rtc_alarm_happened(void) +{ + return ((RTSR & 0x01) && (PWER & (1<< IRQ_RTCAlrm))); +} +void rtc_set_1hz_detect(void) +{ + RTSR = (RTSR_CURRENT_ENABLED | RTSR_1HZ_ENABLE | RTSR_1HZ_STATUS); + PWER |= (1<"); @@ -474,7 +542,9 @@ spin_unlock_irq (&rtc_lock); free_irq(IRQ_RTC1Hz,NULL); free_irq(IRQ_RTCAlrm,NULL); +#ifdef CONFIG_PROC_FS remove_proc_entry ("driver/rtc", NULL); +#endif misc_deregister(&sa1100rtc_miscdev); } @@ -483,7 +553,9 @@ unsigned long tmpctr; misc_register(&sa1100rtc_miscdev); +#ifdef CONFIG_PROC_FS create_proc_read_entry ("driver/rtc", 0, 0, rtc_read_proc, NULL); +#endif /* Reset the interrupt flags and disable interrupts */ tmpctr=RTSR; RTSR = (tmpctr | 0x0003) & 0xfff3; diff -urN linux-2.4.0-rmk2-np2-org/include/asm-arm/arch-sa1100/power_button.h linux-2.4.0-rmk2-np2-fb1/include/asm-arm/arch-sa1100/power_button.h --- linux-2.4.0-rmk2-np2-org/include/asm-arm/arch-sa1100/power_button.h Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/include/asm-arm/arch-sa1100/power_button.h Wed Feb 7 17:06:16 2001 @@ -0,0 +1,78 @@ + /* + * Drived from Itsy project + */ + +#ifndef POWER_BUTTON_H +#define POWER_BUTTON_H + +/* + * constants + * + */ + +#define BUTTON_POWER (0x200) +#define EVENT_BUTTONS_STATE (1) + + + +/* event classes */ +#define EVENT_CLASS_SESSION (1) +#define EVENT_CLASS_DISPLAY (2) +#define EVENT_CLASS_TOUCHSCREEN (3) +#define EVENT_CLASS_BUTTONS (4) +#define EVENT_CLASS_ADC (5) + +/* event flags (bitmask) */ +#define EVENT_FLAG_NONE (0x00) +#define EVENT_FLAG_SYNTHETIC (0x01) +#define EVENT_FLAG_GRABBED (0x02) +#define EVENT_FLAG_SUSER (0x04) + + +/* session event types */ +#define EVENT_SESSION_FOREGROUND (1) +#define EVENT_SESSION_BACKGROUND (2) +#define EVENT_SESSION_CREATED (3) +#define EVENT_SESSION_DESTROYED (4) +#define EVENT_SESSION_NAME (5) +#define EVENT_SESSION_DATA (6) +#define EVENT_SESSION_MANAGER_REQUEST (7) + +/* special sids */ +#define SESSION_ID_INVALID (-1) +#define SESSION_ID_FOREGROUND (0) +#define SESSION_ID_NONE (0) + +/* sizes */ +#define SESSION_NAME_LEN (16) + + +/* + * ioctl interface + * + */ + +/* driver ioctl type */ +#define POWERBUTTON_IOCTL_TYPE 'p' +#define POWERBUTTON_IOCTL_MAXNR (4) + +/* ioctl commands */ +#define POWERBUTTON_GET_SLEEP_ALL _IOR(POWERBUTTON_IOCTL_TYPE, 0, int) +#define POWERBUTTON_GET_SLEEP _IOR(POWERBUTTON_IOCTL_TYPE, 1, int) +#define POWERBUTTON_SET_SLEEP _IO(POWERBUTTON_IOCTL_TYPE, 2) +#define POWERBUTTON_ENTER_SLEEP_MODE _IO(POWERBUTTON_IOCTL_TYPE, 3) +#define POWERBUTTON_ENTER_DEEP_SLEEP_MODE _IO(POWERBUTTON_IOCTL_TYPE, 4) + + + +/* driver ioctl type */ +#define BUTTONS_IOCTL_TYPE 'b' +#define BUTTONS_IOCTL_MAXNR (2) + +/* ioctl commands */ +#define BUTTONS_GET_GRAB_ALL _IOR(BUTTONS_IOCTL_TYPE, 0, ushort) +#define BUTTONS_GET_GRAB _IOR(BUTTONS_IOCTL_TYPE, 1, ushort) +#define BUTTONS_SET_GRAB _IO(BUTTONS_IOCTL_TYPE, 2) + + +#endif /* POWER_BUTTON_H */ diff -urN linux-2.4.0-rmk2-np2-org/include/asm-arm/arch-sa1100/powermgr.h linux-2.4.0-rmk2-np2-fb1/include/asm-arm/arch-sa1100/powermgr.h --- linux-2.4.0-rmk2-np2-org/include/asm-arm/arch-sa1100/powermgr.h Thu Jan 1 08:00:00 1970 +++ linux-2.4.0-rmk2-np2-fb1/include/asm-arm/arch-sa1100/powermgr.h Wed Feb 7 17:06:30 2001 @@ -0,0 +1,199 @@ +/* + * Power Management Interface + * Copyright (c) Compaq Computer Corporation, 1998 + * + * Author: Carl Waldspurger + * + * $Log: powermgr.h,v $ + * Revision 2.1 1999/12/23 01:28:30 kerr + * Updated Copyright information for entire source tree. + * + * Revision 2.0 1998/06/25 18:41:45 kramer + * Major Bump: Update all files to version 2.0 for external release + * -drew + * + * Revision 1.5 1998/06/16 19:20:09 kramer + * Added copyright information + * + * Revision 1.4 1998/04/14 02:23:32 caw + * Updated suspend_check() interfaces. + * + * Revision 1.3 1998/03/28 00:15:41 caw + * Defined powermgr_init_ksyms(). + * + * Revision 1.2 1998/03/26 00:35:25 caw + * Updated interface. + * + * Revision 1.1 1998/03/20 22:59:48 caw + * Initial revision. + * + */ + +#ifndef powermgr_h +#define powermgr_h + +/* + * constants + * + */ + +/* ids */ +#define POWERMGR_ID_INVALID (-1) +#define POWERMGR_ID_MAX (31) + +/* status */ +#define POWERMGR_SUCCESS (0) +#define POWERMGR_FAILURE (-1) + +/* retry delay */ +#define POWERMGR_RETRY_UNKNOWN (-1) + +/* resume flags */ +#define POWERMGR_RESUME_FROM_SLEEP (1 << 0) +#define POWERMGR_RESUME_FROM_BATTERY_FAULT (1 << 1) +#define POWERMGR_RESUME_FROM_VDD_FAULT (1 << 2) + +/* + * types + * + */ + +/* client identifier */ +typedef int powermgr_id; + +/* + * client callback functions + * + */ + +typedef int (*powermgr_suspend_check_callback_fun)(void *cookie, int idle); +typedef int (*powermgr_suspend_callback_fun)(void *cookie); +typedef int (*powermgr_resume_callback_fun)(void *cookie, int resume_flags); + +/* + * int client_suspend_check(void *cookie, int idle_jiffies); + * modifies: cookie + * effects: Checks if client is willing to be suspended. Suspending + * after an inactive period of idle_jiffies is recommended. + * Returns 0 iff suspendible, otherwise returns a retry delay + * measured in jiffies (or POWERMGR_RETRY_UNKNOWN). + * + * int client_suspend(void *cookie); + * modifies: cookie + * effects: Saves state in preparation for imminent suspension. + * Return value is currently ignored. + * + * int client_resume(void *cookie, int resume_flags); + * modifies: cookie + * effects: Restores state after wakeup from suspension. The cause of + * the suspension is encoded by the bitmask resume_flags + * (e.g. POWERMGR_RESUME_FROM_SLEEP, _FROM_BATTERY_FAULT, etc). + * Return value is currently ignored. + * + */ + +typedef struct { + /* callback functions */ + powermgr_suspend_check_callback_fun suspend_check; + powermgr_suspend_callback_fun suspend; + powermgr_resume_callback_fun resume; + + /* uninterpreted client data */ + void *cookie; + + /* identity */ + powermgr_id id; + char *name; + + /* power-consumption info */ + int uamps_idle; + int uamps_sleep; +} powermgr_client; + +/* + * client operations + * + */ + +extern powermgr_id powermgr_register(const powermgr_client *client); + /* + * effects: Register as a power manager client. The power manager + * will communicate via the client-specified callbacks. + * Returns a unique identifier for use with future calls, + * or POWERMGR_ID_INVALID if unable to register. + * storage: Data is copied from client, so it is safe to deallocate + * client after registering. + * caveats: The "id" field specified by client is ignored. + * + */ + +extern int powermgr_unregister(powermgr_id id); + /* + * effects: Deregisters id as a power manager client. + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + +/* + * power management operations + * + */ + +extern int powermgr_init(void); + /* + * effects: Initializes power manager. + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + +extern void powermgr_init_ksyms(void); + /* + * effects: Exports powermgr kernel symbols for modules. + * caveats: Separate function due to boot order restrictions. + * + */ + +extern void powermgr_init_procfs(void); + /* + * effects: Registers /proc/powermgr device. + * caveats: Separate function due to boot order restrictions. + * + */ + +extern int powermgr_suspend_check(int idle_jiffies, int *retry_jiffies); + /* + * modifies: retry_jiffies + * effects: Returns TRUE iff all registered clients are willing + * to be suspended. Clients should be willing to + * suspend after an inactivity period of idle_jiffies. + * Otherwise returns FALSE and sets retry_jiffies to a + * reasonable time delay in jiffies, after which another + * check may return TRUE. + * + */ + +extern int powermgr_suspend(void); + /* + * effects: Forcibly suspends all registered clients, in the reverse order + * of registration (i.e. first to register suspends last). + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + +extern int powermgr_resume(int resume_flags); + /* + * effects: Resumes all registered clients, in the order of registration + * (i.e. first to register resumes first). + * Returns POWERMGR_SUCCESS or POWERMGR_FAILURE. + * + */ + +extern void powermgr_start_timer(int idle_jiffies, int check_jiffies); + /* + * effects: Starts periodic timer to initiate suspends. Clients are + * recommended to suspend after idle_jiffies of inactivity. + * Suspend checks will be made at most once every check_jiffies. + * + */ + +#endif /* powermgr_h */