Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

sh: APM/PM support.

This adds some simple PM stubs and the basic APM interfaces,
primarily for use by hp6xx, where the existing userland
expects it.

Signed-off-by: Andriy Skulysh <askulysh@gmail.com>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>

authored by

Andriy Skulysh and committed by
Paul Mundt
3aa770e7 ef48e8e3

+1124 -22
+10
arch/sh/Kconfig
··· 638 638 639 639 endmenu 640 640 641 + menu "Power management options (EXPERIMENTAL)" 642 + depends on EXPERIMENTAL 643 + 644 + source kernel/power/Kconfig 645 + 646 + config APM 647 + bool "Advanced Power Management Emulation" 648 + depends on PM 649 + endmenu 650 + 641 651 source "net/Kconfig" 642 652 643 653 source "drivers/Kconfig"
+4 -1
arch/sh/boards/hp6xx/Makefile
··· 2 2 # Makefile for the HP6xx specific parts of the kernel 3 3 # 4 4 5 - obj-y := mach.o setup.o 5 + obj-y := mach.o setup.o 6 + obj-$(CONFIG_PM) += pm.o pm_wakeup.o 7 + obj-$(CONFIG_APM) += hp6xx_apm.o 8 + 6 9
+123
arch/sh/boards/hp6xx/hp6xx_apm.c
··· 1 + /* 2 + * bios-less APM driver for hp680 3 + * 4 + * Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License. 8 + */ 9 + #include <linux/config.h> 10 + #include <linux/module.h> 11 + #include <linux/apm_bios.h> 12 + #include <linux/kernel.h> 13 + #include <linux/init.h> 14 + #include <linux/interrupt.h> 15 + #include <asm/io.h> 16 + #include <asm/apm.h> 17 + #include <asm/adc.h> 18 + #include <asm/hp6xx/hp6xx.h> 19 + 20 + #define SH7709_PGDR 0xa400012c 21 + 22 + #define APM_CRITICAL 10 23 + #define APM_LOW 30 24 + 25 + #define HP680_BATTERY_MAX 875 26 + #define HP680_BATTERY_MIN 600 27 + #define HP680_BATTERY_AC_ON 900 28 + 29 + #define MODNAME "hp6x0_apm" 30 + 31 + static int hp6x0_apm_get_info(char *buf, char **start, off_t fpos, int length) 32 + { 33 + u8 pgdr; 34 + char *p; 35 + int battery_status; 36 + int battery_flag; 37 + int ac_line_status; 38 + int time_units = APM_BATTERY_LIFE_UNKNOWN; 39 + 40 + int battery = adc_single(ADC_CHANNEL_BATTERY); 41 + int backup = adc_single(ADC_CHANNEL_BACKUP); 42 + int charging = adc_single(ADC_CHANNEL_CHARGE); 43 + int percentage; 44 + 45 + percentage = 100 * (battery - HP680_BATTERY_MIN) / 46 + (HP680_BATTERY_MAX - HP680_BATTERY_MIN); 47 + 48 + ac_line_status = (battery > HP680_BATTERY_AC_ON) ? 49 + APM_AC_ONLINE : APM_AC_OFFLINE; 50 + 51 + p = buf; 52 + 53 + pgdr = ctrl_inb(SH7709_PGDR); 54 + if (pgdr & PGDR_MAIN_BATTERY_OUT) { 55 + battery_status = APM_BATTERY_STATUS_NOT_PRESENT; 56 + battery_flag = 0x80; 57 + percentage = -1; 58 + } else if (charging < 8 ) { 59 + battery_status = APM_BATTERY_STATUS_CHARGING; 60 + battery_flag = 0x08; 61 + ac_line_status = 0xff; 62 + } else if (percentage <= APM_CRITICAL) { 63 + battery_status = APM_BATTERY_STATUS_CRITICAL; 64 + battery_flag = 0x04; 65 + } else if (percentage <= APM_LOW) { 66 + battery_status = APM_BATTERY_STATUS_LOW; 67 + battery_flag = 0x02; 68 + } else { 69 + battery_status = APM_BATTERY_STATUS_HIGH; 70 + battery_flag = 0x01; 71 + } 72 + 73 + p += sprintf(p, "1.0 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n", 74 + APM_32_BIT_SUPPORT, 75 + ac_line_status, 76 + battery_status, 77 + battery_flag, 78 + percentage, 79 + time_units, 80 + "min"); 81 + p += sprintf(p, "bat=%d backup=%d charge=%d\n", 82 + battery, backup, charging); 83 + 84 + return p - buf; 85 + } 86 + 87 + static irqreturn_t hp6x0_apm_interrupt(int irq, void *dev, struct pt_regs *regs) 88 + { 89 + if (!apm_suspended) 90 + apm_queue_event(APM_USER_SUSPEND); 91 + 92 + return IRQ_HANDLED; 93 + } 94 + 95 + static int __init hp6x0_apm_init(void) 96 + { 97 + int ret; 98 + 99 + ret = request_irq(HP680_BTN_IRQ, hp6x0_apm_interrupt, 100 + SA_INTERRUPT, MODNAME, 0); 101 + if (unlikely(ret < 0)) { 102 + printk(KERN_ERR MODNAME ": IRQ %d request failed\n", 103 + HP680_BTN_IRQ); 104 + return ret; 105 + } 106 + 107 + apm_get_info = hp6x0_apm_get_info; 108 + 109 + return ret; 110 + } 111 + 112 + static void __exit hp6x0_apm_exit(void) 113 + { 114 + free_irq(HP680_BTN_IRQ, 0); 115 + apm_get_info = 0; 116 + } 117 + 118 + module_init(hp6x0_apm_init); 119 + module_exit(hp6x0_apm_exit); 120 + 121 + MODULE_AUTHOR("Adriy Skulysh"); 122 + MODULE_DESCRIPTION("hp6xx Advanced Power Management"); 123 + MODULE_LICENSE("GPL");
+88
arch/sh/boards/hp6xx/pm.c
··· 1 + /* 2 + * hp6x0 Power Management Routines 3 + * 4 + * Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License. 8 + */ 9 + #include <linux/config.h> 10 + #include <linux/init.h> 11 + #include <linux/suspend.h> 12 + #include <linux/errno.h> 13 + #include <linux/time.h> 14 + #include <asm/io.h> 15 + #include <asm/hd64461.h> 16 + #include <asm/hp6xx/hp6xx.h> 17 + #include <asm/cpu/dac.h> 18 + #include <asm/pm.h> 19 + 20 + #define STBCR 0xffffff82 21 + #define STBCR2 0xffffff88 22 + 23 + static int hp6x0_pm_enter(suspend_state_t state) 24 + { 25 + u8 stbcr, stbcr2; 26 + #ifdef CONFIG_HD64461_ENABLER 27 + u8 scr; 28 + u16 hd64461_stbcr; 29 + #endif 30 + 31 + if (state != PM_SUSPEND_MEM) 32 + return -EINVAL; 33 + 34 + #ifdef CONFIG_HD64461_ENABLER 35 + outb(0, HD64461_PCC1CSCIER); 36 + 37 + scr = inb(HD64461_PCC1SCR); 38 + scr |= HD64461_PCCSCR_VCC1; 39 + outb(scr, HD64461_PCC1SCR); 40 + 41 + hd64461_stbcr = inw(HD64461_STBCR); 42 + hd64461_stbcr |= HD64461_STBCR_SPC1ST; 43 + outw(hd64461_stbcr, HD64461_STBCR); 44 + #endif 45 + 46 + ctrl_outb(0x1f, DACR); 47 + 48 + stbcr = ctrl_inb(STBCR); 49 + ctrl_outb(0x01, STBCR); 50 + 51 + stbcr2 = ctrl_inb(STBCR2); 52 + ctrl_outb(0x7f , STBCR2); 53 + 54 + outw(0xf07f, HD64461_SCPUCR); 55 + 56 + pm_enter(); 57 + 58 + outw(0, HD64461_SCPUCR); 59 + ctrl_outb(stbcr, STBCR); 60 + ctrl_outb(stbcr2, STBCR2); 61 + 62 + #ifdef CONFIG_HD64461_ENABLER 63 + hd64461_stbcr = inw(HD64461_STBCR); 64 + hd64461_stbcr &= ~HD64461_STBCR_SPC1ST; 65 + outw(hd64461_stbcr, HD64461_STBCR); 66 + 67 + outb(0x4c, HD64461_PCC1CSCIER); 68 + outb(0x00, HD64461_PCC1CSCR); 69 + #endif 70 + 71 + return 0; 72 + } 73 + 74 + /* 75 + * Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk. 76 + */ 77 + static struct pm_ops hp6x0_pm_ops = { 78 + .pm_disk_mode = PM_DISK_FIRMWARE, 79 + .enter = hp6x0_pm_enter, 80 + }; 81 + 82 + static int __init hp6x0_pm_init(void) 83 + { 84 + pm_set_ops(&hp6x0_pm_ops); 85 + return 0; 86 + } 87 + 88 + late_initcall(hp6x0_pm_init);
+58
arch/sh/boards/hp6xx/pm_wakeup.S
··· 1 + /* 2 + * Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com> 3 + * 4 + * This file is subject to the terms and conditions of the GNU General Public 5 + * License. See the file "COPYING" in the main directory of this archive 6 + * for more details. 7 + * 8 + */ 9 + 10 + #include <linux/linkage.h> 11 + #include <asm/cpu/mmu_context.h> 12 + 13 + #define k0 r0 14 + #define k1 r1 15 + #define k2 r2 16 + #define k3 r3 17 + #define k4 r4 18 + 19 + /* 20 + * Kernel mode register usage: 21 + * k0 scratch 22 + * k1 scratch 23 + * k2 scratch (Exception code) 24 + * k3 scratch (Return address) 25 + * k4 scratch 26 + * k5 reserved 27 + * k6 Global Interrupt Mask (0--15 << 4) 28 + * k7 CURRENT_THREAD_INFO (pointer to current thread info) 29 + */ 30 + 31 + ENTRY(wakeup_start) 32 + ! clear STBY bit 33 + mov #-126, k2 34 + and #127, k0 35 + mov.b k0, @k2 36 + ! enable refresh 37 + mov.l 5f, k1 38 + mov.w 6f, k0 39 + mov.w k0, @k1 40 + ! jump to handler 41 + mov.l 2f, k2 42 + mov.l 3f, k3 43 + mov.l @k2, k2 44 + 45 + mov.l 4f, k1 46 + jmp @k1 47 + nop 48 + 49 + .align 2 50 + 1: .long EXPEVT 51 + 2: .long INTEVT 52 + 3: .long ret_from_irq 53 + 4: .long handle_exception 54 + 5: .long 0xffffff68 55 + 6: .word 0x0524 56 + 57 + ENTRY(wakeup_end) 58 + nop
+14
arch/sh/boards/hp6xx/setup.c
··· 15 15 #include <asm/hp6xx/hp6xx.h> 16 16 #include <asm/cpu/dac.h> 17 17 18 + #define SCPCR 0xa4000116 19 + #define SCPDR 0xa4000136 20 + 18 21 const char *get_system_type(void) 19 22 { 20 23 return "HP6xx"; ··· 27 24 { 28 25 u8 v8; 29 26 u16 v; 27 + 30 28 v = inw(HD64461_STBCR); 31 29 v |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST | 32 30 HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST | ··· 53 49 v8 = ctrl_inb(DACR); 54 50 v8 &= ~DACR_DAE; 55 51 ctrl_outb(v8,DACR); 52 + 53 + v8 = ctrl_inb(SCPDR); 54 + v8 |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y; 55 + v8 &= ~SCPDR_TS_SCAN_ENABLE; 56 + ctrl_outb(v8, SCPDR); 57 + 58 + v = ctrl_inw(SCPCR); 59 + v &= ~SCPCR_TS_MASK; 60 + v |= SCPCR_TS_ENABLE; 61 + ctrl_outw(v, SCPCR); 56 62 57 63 return 0; 58 64 }
+2
arch/sh/kernel/Makefile
··· 18 18 obj-$(CONFIG_MODULES) += module.o 19 19 obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 20 20 obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o 21 + obj-$(CONFIG_APM) += apm.o 22 + obj-$(CONFIG_PM) += pm.o
+539
arch/sh/kernel/apm.c
··· 1 + /* 2 + * bios-less APM driver for hp680 3 + * 4 + * Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com> 5 + * 6 + * based on ARM APM driver by 7 + * Jamey Hicks <jamey@crl.dec.com> 8 + * 9 + * adapted from the APM BIOS driver for Linux by 10 + * Stephen Rothwell (sfr@linuxcare.com) 11 + * 12 + * APM 1.2 Reference: 13 + * Intel Corporation, Microsoft Corporation. Advanced Power Management 14 + * (APM) BIOS Interface Specification, Revision 1.2, February 1996. 15 + * 16 + * [This document is available from Microsoft at: 17 + * http://www.microsoft.com/hwdev/busbios/amp_12.htm] 18 + */ 19 + #include <linux/config.h> 20 + #include <linux/module.h> 21 + #include <linux/poll.h> 22 + #include <linux/timer.h> 23 + #include <linux/slab.h> 24 + #include <linux/proc_fs.h> 25 + #include <linux/miscdevice.h> 26 + #include <linux/apm_bios.h> 27 + #include <linux/pm.h> 28 + #include <linux/pm_legacy.h> 29 + #include <asm/apm.h> 30 + 31 + #define MODNAME "apm" 32 + 33 + /* 34 + * The apm_bios device is one of the misc char devices. 35 + * This is its minor number. 36 + */ 37 + #define APM_MINOR_DEV 134 38 + 39 + /* 40 + * Maximum number of events stored 41 + */ 42 + #define APM_MAX_EVENTS 16 43 + 44 + struct apm_queue { 45 + unsigned int event_head; 46 + unsigned int event_tail; 47 + apm_event_t events[APM_MAX_EVENTS]; 48 + }; 49 + 50 + /* 51 + * The per-file APM data 52 + */ 53 + struct apm_user { 54 + struct list_head list; 55 + 56 + unsigned int suser: 1; 57 + unsigned int writer: 1; 58 + unsigned int reader: 1; 59 + 60 + int suspend_result; 61 + unsigned int suspend_state; 62 + #define SUSPEND_NONE 0 /* no suspend pending */ 63 + #define SUSPEND_PENDING 1 /* suspend pending read */ 64 + #define SUSPEND_READ 2 /* suspend read, pending ack */ 65 + #define SUSPEND_ACKED 3 /* suspend acked */ 66 + #define SUSPEND_DONE 4 /* suspend completed */ 67 + 68 + struct apm_queue queue; 69 + }; 70 + 71 + /* 72 + * Local variables 73 + */ 74 + static int suspends_pending; 75 + 76 + static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); 77 + static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); 78 + 79 + /* 80 + * This is a list of everyone who has opened /dev/apm_bios 81 + */ 82 + static DECLARE_RWSEM(user_list_lock); 83 + static LIST_HEAD(apm_user_list); 84 + 85 + /* 86 + * kapmd info. kapmd provides us a process context to handle 87 + * "APM" events within - specifically necessary if we're going 88 + * to be suspending the system. 89 + */ 90 + static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait); 91 + static DECLARE_COMPLETION(kapmd_exit); 92 + static DEFINE_SPINLOCK(kapmd_queue_lock); 93 + static struct apm_queue kapmd_queue; 94 + 95 + int apm_suspended; 96 + EXPORT_SYMBOL(apm_suspended); 97 + 98 + /* Platform-specific apm_read_proc(). */ 99 + int (*apm_get_info)(char *buf, char **start, off_t fpos, int length); 100 + EXPORT_SYMBOL(apm_get_info); 101 + 102 + /* 103 + * APM event queue management. 104 + */ 105 + static inline int queue_empty(struct apm_queue *q) 106 + { 107 + return q->event_head == q->event_tail; 108 + } 109 + 110 + static inline apm_event_t queue_get_event(struct apm_queue *q) 111 + { 112 + q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS; 113 + return q->events[q->event_tail]; 114 + } 115 + 116 + static void queue_add_event(struct apm_queue *q, apm_event_t event) 117 + { 118 + q->event_head = (q->event_head + 1) % APM_MAX_EVENTS; 119 + if (q->event_head == q->event_tail) { 120 + static int notified; 121 + 122 + if (notified++ == 0) 123 + printk(KERN_ERR "apm: an event queue overflowed\n"); 124 + 125 + q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS; 126 + } 127 + q->events[q->event_head] = event; 128 + } 129 + 130 + static void queue_event_one_user(struct apm_user *as, apm_event_t event) 131 + { 132 + if (as->suser && as->writer) { 133 + switch (event) { 134 + case APM_SYS_SUSPEND: 135 + case APM_USER_SUSPEND: 136 + /* 137 + * If this user already has a suspend pending, 138 + * don't queue another one. 139 + */ 140 + if (as->suspend_state != SUSPEND_NONE) 141 + return; 142 + 143 + as->suspend_state = SUSPEND_PENDING; 144 + suspends_pending++; 145 + break; 146 + } 147 + } 148 + queue_add_event(&as->queue, event); 149 + } 150 + 151 + static void queue_event(apm_event_t event, struct apm_user *sender) 152 + { 153 + struct apm_user *as; 154 + 155 + down_read(&user_list_lock); 156 + 157 + list_for_each_entry(as, &apm_user_list, list) 158 + if (as != sender && as->reader) 159 + queue_event_one_user(as, event); 160 + 161 + up_read(&user_list_lock); 162 + wake_up_interruptible(&apm_waitqueue); 163 + } 164 + 165 + /** 166 + * apm_queue_event - queue an APM event for kapmd 167 + * @event: APM event 168 + * 169 + * Queue an APM event for kapmd to process and ultimately take the 170 + * appropriate action. Only a subset of events are handled: 171 + * %APM_LOW_BATTERY 172 + * %APM_POWER_STATUS_CHANGE 173 + * %APM_USER_SUSPEND 174 + * %APM_SYS_SUSPEND 175 + * %APM_CRITICAL_SUSPEND 176 + */ 177 + void apm_queue_event(apm_event_t event) 178 + { 179 + spin_lock_irq(&kapmd_queue_lock); 180 + queue_add_event(&kapmd_queue, event); 181 + spin_unlock_irq(&kapmd_queue_lock); 182 + 183 + wake_up_interruptible(&kapmd_wait); 184 + } 185 + EXPORT_SYMBOL(apm_queue_event); 186 + 187 + static void apm_suspend(void) 188 + { 189 + struct apm_user *as; 190 + int err; 191 + 192 + apm_suspended = 1; 193 + err = pm_suspend(PM_SUSPEND_MEM); 194 + 195 + /* 196 + * Anyone on the APM queues will think we're still suspended. 197 + * Send a message so everyone knows we're now awake again. 198 + */ 199 + queue_event(APM_NORMAL_RESUME, NULL); 200 + 201 + /* 202 + * Finally, wake up anyone who is sleeping on the suspend. 203 + */ 204 + down_read(&user_list_lock); 205 + list_for_each_entry(as, &apm_user_list, list) { 206 + as->suspend_result = err; 207 + as->suspend_state = SUSPEND_DONE; 208 + } 209 + up_read(&user_list_lock); 210 + 211 + wake_up(&apm_suspend_waitqueue); 212 + apm_suspended = 0; 213 + } 214 + 215 + static ssize_t apm_read(struct file *fp, char __user *buf, 216 + size_t count, loff_t *ppos) 217 + { 218 + struct apm_user *as = fp->private_data; 219 + apm_event_t event; 220 + int i = count, ret = 0; 221 + 222 + if (count < sizeof(apm_event_t)) 223 + return -EINVAL; 224 + 225 + if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK) 226 + return -EAGAIN; 227 + 228 + wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue)); 229 + 230 + while ((i >= sizeof(event)) && !queue_empty(&as->queue)) { 231 + event = queue_get_event(&as->queue); 232 + 233 + ret = -EFAULT; 234 + if (copy_to_user(buf, &event, sizeof(event))) 235 + break; 236 + 237 + if (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND) 238 + as->suspend_state = SUSPEND_READ; 239 + 240 + buf += sizeof(event); 241 + i -= sizeof(event); 242 + } 243 + 244 + if (i < count) 245 + ret = count - i; 246 + 247 + return ret; 248 + } 249 + 250 + static unsigned int apm_poll(struct file *fp, poll_table * wait) 251 + { 252 + struct apm_user *as = fp->private_data; 253 + 254 + poll_wait(fp, &apm_waitqueue, wait); 255 + return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM; 256 + } 257 + 258 + /* 259 + * apm_ioctl - handle APM ioctl 260 + * 261 + * APM_IOC_SUSPEND 262 + * This IOCTL is overloaded, and performs two functions. It is used to: 263 + * - initiate a suspend 264 + * - acknowledge a suspend read from /dev/apm_bios. 265 + * Only when everyone who has opened /dev/apm_bios with write permission 266 + * has acknowledge does the actual suspend happen. 267 + */ 268 + static int 269 + apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) 270 + { 271 + struct apm_user *as = filp->private_data; 272 + unsigned long flags; 273 + int err = -EINVAL; 274 + 275 + if (!as->suser || !as->writer) 276 + return -EPERM; 277 + 278 + switch (cmd) { 279 + case APM_IOC_SUSPEND: 280 + as->suspend_result = -EINTR; 281 + 282 + if (as->suspend_state == SUSPEND_READ) { 283 + /* 284 + * If we read a suspend command from /dev/apm_bios, 285 + * then the corresponding APM_IOC_SUSPEND ioctl is 286 + * interpreted as an acknowledge. 287 + */ 288 + as->suspend_state = SUSPEND_ACKED; 289 + suspends_pending--; 290 + } else { 291 + /* 292 + * Otherwise it is a request to suspend the system. 293 + * Queue an event for all readers, and expect an 294 + * acknowledge from all writers who haven't already 295 + * acknowledged. 296 + */ 297 + queue_event(APM_USER_SUSPEND, as); 298 + } 299 + 300 + /* 301 + * If there are no further acknowledges required, suspend 302 + * the system. 303 + */ 304 + if (suspends_pending == 0) 305 + apm_suspend(); 306 + 307 + /* 308 + * Wait for the suspend/resume to complete. If there are 309 + * pending acknowledges, we wait here for them. 310 + * 311 + * Note that we need to ensure that the PM subsystem does 312 + * not kick us out of the wait when it suspends the threads. 313 + */ 314 + flags = current->flags; 315 + current->flags |= PF_NOFREEZE; 316 + 317 + /* 318 + * Note: do not allow a thread which is acking the suspend 319 + * to escape until the resume is complete. 320 + */ 321 + if (as->suspend_state == SUSPEND_ACKED) 322 + wait_event(apm_suspend_waitqueue, 323 + as->suspend_state == SUSPEND_DONE); 324 + else 325 + wait_event_interruptible(apm_suspend_waitqueue, 326 + as->suspend_state == SUSPEND_DONE); 327 + 328 + current->flags = flags; 329 + err = as->suspend_result; 330 + as->suspend_state = SUSPEND_NONE; 331 + break; 332 + } 333 + 334 + return err; 335 + } 336 + 337 + static int apm_release(struct inode * inode, struct file * filp) 338 + { 339 + struct apm_user *as = filp->private_data; 340 + filp->private_data = NULL; 341 + 342 + down_write(&user_list_lock); 343 + list_del(&as->list); 344 + up_write(&user_list_lock); 345 + 346 + /* 347 + * We are now unhooked from the chain. As far as new 348 + * events are concerned, we no longer exist. However, we 349 + * need to balance suspends_pending, which means the 350 + * possibility of sleeping. 351 + */ 352 + if (as->suspend_state != SUSPEND_NONE) { 353 + suspends_pending -= 1; 354 + if (suspends_pending == 0) 355 + apm_suspend(); 356 + } 357 + 358 + kfree(as); 359 + return 0; 360 + } 361 + 362 + static int apm_open(struct inode * inode, struct file * filp) 363 + { 364 + struct apm_user *as; 365 + 366 + as = kzalloc(sizeof(*as), GFP_KERNEL); 367 + if (as) { 368 + /* 369 + * XXX - this is a tiny bit broken, when we consider BSD 370 + * process accounting. If the device is opened by root, we 371 + * instantly flag that we used superuser privs. Who knows, 372 + * we might close the device immediately without doing a 373 + * privileged operation -- cevans 374 + */ 375 + as->suser = capable(CAP_SYS_ADMIN); 376 + as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE; 377 + as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ; 378 + 379 + down_write(&user_list_lock); 380 + list_add(&as->list, &apm_user_list); 381 + up_write(&user_list_lock); 382 + 383 + filp->private_data = as; 384 + } 385 + 386 + return as ? 0 : -ENOMEM; 387 + } 388 + 389 + static struct file_operations apm_bios_fops = { 390 + .owner = THIS_MODULE, 391 + .read = apm_read, 392 + .poll = apm_poll, 393 + .ioctl = apm_ioctl, 394 + .open = apm_open, 395 + .release = apm_release, 396 + }; 397 + 398 + static struct miscdevice apm_device = { 399 + .minor = APM_MINOR_DEV, 400 + .name = "apm_bios", 401 + .fops = &apm_bios_fops 402 + }; 403 + 404 + 405 + #ifdef CONFIG_PROC_FS 406 + /* 407 + * Arguments, with symbols from linux/apm_bios.h. 408 + * 409 + * 0) Linux driver version (this will change if format changes) 410 + * 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2. 411 + * 2) APM flags from APM Installation Check (0x00): 412 + * bit 0: APM_16_BIT_SUPPORT 413 + * bit 1: APM_32_BIT_SUPPORT 414 + * bit 2: APM_IDLE_SLOWS_CLOCK 415 + * bit 3: APM_BIOS_DISABLED 416 + * bit 4: APM_BIOS_DISENGAGED 417 + * 3) AC line status 418 + * 0x00: Off-line 419 + * 0x01: On-line 420 + * 0x02: On backup power (BIOS >= 1.1 only) 421 + * 0xff: Unknown 422 + * 4) Battery status 423 + * 0x00: High 424 + * 0x01: Low 425 + * 0x02: Critical 426 + * 0x03: Charging 427 + * 0x04: Selected battery not present (BIOS >= 1.2 only) 428 + * 0xff: Unknown 429 + * 5) Battery flag 430 + * bit 0: High 431 + * bit 1: Low 432 + * bit 2: Critical 433 + * bit 3: Charging 434 + * bit 7: No system battery 435 + * 0xff: Unknown 436 + * 6) Remaining battery life (percentage of charge): 437 + * 0-100: valid 438 + * -1: Unknown 439 + * 7) Remaining battery life (time units): 440 + * Number of remaining minutes or seconds 441 + * -1: Unknown 442 + * 8) min = minutes; sec = seconds 443 + */ 444 + static int apm_read_proc(char *buf, char **start, off_t fpos, int length) 445 + { 446 + if (likely(apm_get_info)) 447 + return apm_get_info(buf, start, fpos, length); 448 + 449 + return -EINVAL; 450 + } 451 + #endif 452 + 453 + static int kapmd(void *arg) 454 + { 455 + daemonize("kapmd"); 456 + current->flags |= PF_NOFREEZE; 457 + 458 + do { 459 + apm_event_t event; 460 + 461 + wait_event_interruptible(kapmd_wait, 462 + !queue_empty(&kapmd_queue) || !pm_active); 463 + 464 + if (!pm_active) 465 + break; 466 + 467 + spin_lock_irq(&kapmd_queue_lock); 468 + event = 0; 469 + if (!queue_empty(&kapmd_queue)) 470 + event = queue_get_event(&kapmd_queue); 471 + spin_unlock_irq(&kapmd_queue_lock); 472 + 473 + switch (event) { 474 + case 0: 475 + break; 476 + 477 + case APM_LOW_BATTERY: 478 + case APM_POWER_STATUS_CHANGE: 479 + queue_event(event, NULL); 480 + break; 481 + 482 + case APM_USER_SUSPEND: 483 + case APM_SYS_SUSPEND: 484 + queue_event(event, NULL); 485 + if (suspends_pending == 0) 486 + apm_suspend(); 487 + break; 488 + 489 + case APM_CRITICAL_SUSPEND: 490 + apm_suspend(); 491 + break; 492 + } 493 + } while (1); 494 + 495 + complete_and_exit(&kapmd_exit, 0); 496 + } 497 + 498 + static int __init apm_init(void) 499 + { 500 + int ret; 501 + 502 + pm_active = 1; 503 + 504 + ret = kernel_thread(kapmd, NULL, CLONE_KERNEL); 505 + if (unlikely(ret < 0)) { 506 + pm_active = 0; 507 + return ret; 508 + } 509 + 510 + create_proc_info_entry("apm", 0, NULL, apm_read_proc); 511 + 512 + ret = misc_register(&apm_device); 513 + if (unlikely(ret != 0)) { 514 + remove_proc_entry("apm", NULL); 515 + 516 + pm_active = 0; 517 + wake_up(&kapmd_wait); 518 + wait_for_completion(&kapmd_exit); 519 + } 520 + 521 + return ret; 522 + } 523 + 524 + static void __exit apm_exit(void) 525 + { 526 + misc_deregister(&apm_device); 527 + remove_proc_entry("apm", NULL); 528 + 529 + pm_active = 0; 530 + wake_up(&kapmd_wait); 531 + wait_for_completion(&kapmd_exit); 532 + } 533 + 534 + module_init(apm_init); 535 + module_exit(apm_exit); 536 + 537 + MODULE_AUTHOR("Stephen Rothwell, Andriy Skulysh"); 538 + MODULE_DESCRIPTION("Advanced Power Management"); 539 + MODULE_LICENSE("GPL");
+2 -2
arch/sh/kernel/entry.S
··· 308 308 .align 2 309 309 ret_from_exception: 310 310 preempt_stop() 311 - ret_from_irq: 311 + ENTRY(ret_from_irq) 312 312 ! 313 313 mov #OFF_SR, r0 314 314 mov.l @(r0,r15), r0 ! get status register ··· 704 704 ! 705 705 ! 706 706 .align 2 707 - handle_exception: 707 + ENTRY(handle_exception) 708 708 ! Using k0, k1 for scratch registers (r0_bank1, r1_bank), 709 709 ! save all registers onto stack. 710 710 !
+88
arch/sh/kernel/pm.c
··· 1 + /* 2 + * Generic Power Management Routine 3 + * 4 + * Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com> 5 + * 6 + * This program is free software; you can redistribute it and/or 7 + * modify it under the terms of the GNU General Public License. 8 + */ 9 + #include <linux/suspend.h> 10 + #include <linux/delay.h> 11 + #include <linux/gfp.h> 12 + #include <asm/freq.h> 13 + #include <asm/io.h> 14 + #include <asm/watchdog.h> 15 + #include <asm/pm.h> 16 + 17 + #define INTR_OFFSET 0x600 18 + 19 + #define STBCR 0xffffff82 20 + #define STBCR2 0xffffff88 21 + 22 + #define STBCR_STBY 0x80 23 + #define STBCR_MSTP2 0x04 24 + 25 + #define MCR 0xffffff68 26 + #define RTCNT 0xffffff70 27 + 28 + #define MCR_RMODE 2 29 + #define MCR_RFSH 4 30 + 31 + void pm_enter(void) 32 + { 33 + u8 stbcr, csr; 34 + u16 frqcr, mcr; 35 + u32 vbr_new, vbr_old; 36 + 37 + set_bl_bit(); 38 + 39 + /* set wdt */ 40 + csr = sh_wdt_read_csr(); 41 + csr &= ~WTCSR_TME; 42 + csr |= WTCSR_CKS_4096; 43 + sh_wdt_write_csr(csr); 44 + csr = sh_wdt_read_csr(); 45 + sh_wdt_write_cnt(0); 46 + 47 + /* disable PLL1 */ 48 + frqcr = ctrl_inw(FRQCR); 49 + frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY); 50 + ctrl_outw(frqcr, FRQCR); 51 + 52 + /* enable standby */ 53 + stbcr = ctrl_inb(STBCR); 54 + ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR); 55 + 56 + /* set self-refresh */ 57 + mcr = ctrl_inw(MCR); 58 + ctrl_outw(mcr & ~MCR_RFSH, MCR); 59 + 60 + /* set interrupt handler */ 61 + asm volatile("stc vbr, %0" : "=r" (vbr_old)); 62 + vbr_new = get_zeroed_page(GFP_ATOMIC); 63 + udelay(50); 64 + memcpy((void*)(vbr_new + INTR_OFFSET), 65 + &wakeup_start, &wakeup_end - &wakeup_start); 66 + asm volatile("ldc %0, vbr" : : "r" (vbr_new)); 67 + 68 + ctrl_outw(0, RTCNT); 69 + ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR); 70 + 71 + cpu_sleep(); 72 + 73 + asm volatile("ldc %0, vbr" : : "r" (vbr_old)); 74 + 75 + free_page(vbr_new); 76 + 77 + /* enable PLL1 */ 78 + frqcr = ctrl_inw(FRQCR); 79 + frqcr |= FRQCR_PSTBY; 80 + ctrl_outw(frqcr, FRQCR); 81 + udelay(50); 82 + frqcr |= FRQCR_PLLEN; 83 + ctrl_outw(frqcr, FRQCR); 84 + 85 + ctrl_outb(stbcr, STBCR); 86 + 87 + clear_bl_bit(); 88 + }
+4
arch/sh/kernel/sh_ksyms.c
··· 116 116 EXPORT_SYMBOL(synchronize_irq); 117 117 #endif 118 118 119 + #ifdef CONFIG_PM 120 + EXPORT_SYMBOL(pm_suspend); 121 + #endif 122 + 119 123 EXPORT_SYMBOL(csum_partial); 120 124 #ifdef CONFIG_IPV6 121 125 EXPORT_SYMBOL(csum_ipv6_magic);
+25
arch/sh/kernel/time.c
··· 143 143 } 144 144 } 145 145 146 + #ifdef CONFIG_PM 147 + int timer_suspend(struct sys_device *dev, pm_message_t state) 148 + { 149 + struct sys_timer *sys_timer = container_of(dev, struct sys_timer, dev); 150 + 151 + sys_timer->ops->stop(); 152 + 153 + return 0; 154 + } 155 + 156 + int timer_resume(struct sys_device *dev) 157 + { 158 + struct sys_timer *sys_timer = container_of(dev, struct sys_timer, dev); 159 + 160 + sys_timer->ops->start(); 161 + 162 + return 0; 163 + } 164 + #else 165 + #define timer_suspend NULL 166 + #define timer_resume NULL 167 + #endif 168 + 146 169 static struct sysdev_class timer_sysclass = { 147 170 set_kset_name("timer"), 171 + .suspend = timer_suspend, 172 + .resume = timer_resume, 148 173 }; 149 174 150 175 static int __init timer_init_sysfs(void)
+16 -2
arch/sh/kernel/timers/timer-tmu.c
··· 188 188 .ops = &tmu_clk_ops, 189 189 }; 190 190 191 + static int tmu_timer_start(void) 192 + { 193 + ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); 194 + return 0; 195 + } 196 + 197 + static int tmu_timer_stop(void) 198 + { 199 + ctrl_outb(0, TMU_TSTR); 200 + return 0; 201 + } 202 + 191 203 static int tmu_timer_init(void) 192 204 { 193 205 unsigned long interval; ··· 209 197 tmu0_clk.parent = clk_get("module_clk"); 210 198 211 199 /* Start TMU0 */ 212 - ctrl_outb(0, TMU_TSTR); 200 + tmu_timer_stop(); 213 201 #if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760) 214 202 ctrl_outb(TMU_TOCR_INIT, TMU_TOCR); 215 203 #endif ··· 223 211 ctrl_outl(interval, TMU0_TCOR); 224 212 ctrl_outl(interval, TMU0_TCNT); 225 213 226 - ctrl_outb(TMU_TSTR_INIT, TMU_TSTR); 214 + tmu_timer_start(); 227 215 228 216 return 0; 229 217 } 230 218 231 219 struct sys_timer_ops tmu_timer_ops = { 232 220 .init = tmu_timer_init, 221 + .start = tmu_timer_start, 222 + .stop = tmu_timer_stop, 233 223 .get_frequency = tmu_timer_get_frequency, 234 224 .get_offset = tmu_timer_get_offset, 235 225 };
-14
drivers/input/touchscreen/hp680_ts_input.c
··· 15 15 #define HP680_TS_ABS_Y_MIN 80 16 16 #define HP680_TS_ABS_Y_MAX 910 17 17 18 - #define SCPCR 0xa4000116 19 18 #define PHDR 0xa400012e 20 19 #define SCPDR 0xa4000136 21 20 ··· 76 77 77 78 static int __init hp680_ts_init(void) 78 79 { 79 - u8 scpdr; 80 - u16 scpcr; 81 - 82 - scpdr = ctrl_inb(SCPDR); 83 - scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y; 84 - scpdr &= ~SCPDR_TS_SCAN_ENABLE; 85 - ctrl_outb(scpdr, SCPDR); 86 - 87 - scpcr = ctrl_inw(SCPCR); 88 - scpcr &= ~SCPCR_TS_MASK; 89 - scpcr |= SCPCR_TS_ENABLE; 90 - ctrl_outw(scpcr, SCPCR); 91 - 92 80 hp680_ts_dev = input_allocate_device(); 93 81 if (!hp680_ts_dev) 94 82 return -ENOMEM;
+46
include/asm-sh/apm.h
··· 1 + /* 2 + * Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com> 3 + * 4 + * This file is subject to the terms and conditions of the GNU General Public 5 + * License. See the file "COPYING" in the main directory of this archive 6 + * for more details. 7 + * 8 + */ 9 + 10 + #ifndef __ASM_SH_APM_H 11 + #define __ASM_SH_APM_H 12 + 13 + #define APM_AC_OFFLINE 0 14 + #define APM_AC_ONLINE 1 15 + #define APM_AC_BACKUP 2 16 + #define APM_AC_UNKNOWN 0xff 17 + 18 + #define APM_BATTERY_STATUS_HIGH 0 19 + #define APM_BATTERY_STATUS_LOW 1 20 + #define APM_BATTERY_STATUS_CRITICAL 2 21 + #define APM_BATTERY_STATUS_CHARGING 3 22 + #define APM_BATTERY_STATUS_NOT_PRESENT 4 23 + #define APM_BATTERY_STATUS_UNKNOWN 0xff 24 + 25 + #define APM_BATTERY_LIFE_UNKNOWN 0xFFFF 26 + #define APM_BATTERY_LIFE_MINUTES 0x8000 27 + #define APM_BATTERY_LIFE_VALUE_MASK 0x7FFF 28 + 29 + #define APM_BATTERY_FLAG_HIGH (1 << 0) 30 + #define APM_BATTERY_FLAG_LOW (1 << 1) 31 + #define APM_BATTERY_FLAG_CRITICAL (1 << 2) 32 + #define APM_BATTERY_FLAG_CHARGING (1 << 3) 33 + #define APM_BATTERY_FLAG_NOT_PRESENT (1 << 7) 34 + #define APM_BATTERY_FLAG_UNKNOWN 0xff 35 + 36 + #define APM_UNITS_MINS 0 37 + #define APM_UNITS_SECS 1 38 + #define APM_UNITS_UNKNOWN -1 39 + 40 + 41 + extern int (*apm_get_info)(char *buf, char **start, off_t fpos, int length); 42 + extern int apm_suspended; 43 + 44 + void apm_queue_event(apm_event_t event); 45 + 46 + #endif
+4
include/asm-sh/cpu-sh3/freq.h
··· 18 18 #define MIN_DIVISOR_NR 0 19 19 #define MAX_DIVISOR_NR 4 20 20 21 + #define FRQCR_CKOEN 0x0100 22 + #define FRQCR_PLLEN 0x0080 23 + #define FRQCR_PSTBY 0x0040 24 + 21 25 #endif /* __ASM_CPU_SH3_FREQ_H */ 22 26
+6 -1
include/asm-sh/hd64461.h
··· 40 40 #define HD64461_LCDCBAR 0x11000 41 41 #define HD64461_LCDCLOR 0x11002 42 42 #define HD64461_LCDCCR 0x11004 43 - #define HD64461_LCDCCR_MOFF 0x80 43 + #define HD64461_LCDCCR_STBACK 0x0400 44 + #define HD64461_LCDCCR_STREQ 0x0100 45 + #define HD64461_LCDCCR_MOFF 0x0080 46 + #define HD64461_LCDCCR_REFSEL 0x0040 47 + #define HD64461_LCDCCR_EPON 0x0020 48 + #define HD64461_LCDCCR_SPON 0x0010 44 49 45 50 #define HD64461_LDR1 0x11010 46 51 #define HD64461_LDR1_DON 0x01
+51 -2
include/asm-sh/hp6xx/hp6xx.h
··· 2 2 #define __ASM_SH_HP6XX_H 3 3 4 4 /* 5 - * Copyright (C) 2003 Andriy Skulysh 5 + * Copyright (C) 2003, 2004, 2005 Andriy Skulysh 6 + * 7 + * This file is subject to the terms and conditions of the GNU General Public 8 + * License. See the file "COPYING" in the main directory of this archive 9 + * for more details. 10 + * 6 11 */ 7 12 8 - #define HP680_TS_IRQ IRQ3_IRQ 13 + #define HP680_BTN_IRQ IRQ0_IRQ 14 + #define HP680_TS_IRQ IRQ3_IRQ 15 + #define HP680_HD64461_IRQ IRQ4_IRQ 9 16 10 17 #define DAC_LCD_BRIGHTNESS 0 11 18 #define DAC_SPEAKER_VOLUME 1 12 19 20 + #define PGDR_OPENED 0x01 21 + #define PGDR_MAIN_BATTERY_OUT 0x04 22 + #define PGDR_PLAY_BUTTON 0x08 23 + #define PGDR_REWIND_BUTTON 0x10 24 + #define PGDR_RECORD_BUTTON 0x20 25 + 13 26 #define PHDR_TS_PEN_DOWN 0x08 27 + 28 + #define PJDR_LED_BLINK 0x02 29 + 30 + #define PKDR_LED_GREEN 0x10 14 31 15 32 #define SCPDR_TS_SCAN_ENABLE 0x20 16 33 #define SCPDR_TS_SCAN_Y 0x02 ··· 38 21 39 22 #define ADC_CHANNEL_TS_Y 1 40 23 #define ADC_CHANNEL_TS_X 2 24 + #define ADC_CHANNEL_BATTERY 3 25 + #define ADC_CHANNEL_BACKUP 4 26 + #define ADC_CHANNEL_CHARGE 5 41 27 42 28 #define HD64461_GPADR_SPEAKER 0x01 43 29 #define HD64461_GPADR_PCMCIA0 (0x02|0x08) 30 + 44 31 #define HD64461_GPBDR_LCDOFF 0x01 32 + #define HD64461_GPBDR_LCD_CONTRAST_MASK 0x78 45 33 #define HD64461_GPBDR_LED_RED 0x80 34 + 35 + #include <asm/hd64461.h> 36 + #include <asm/io.h> 37 + 38 + #define PJDR 0xa4000130 39 + #define PKDR 0xa4000132 40 + 41 + static inline void hp6xx_led_red(int on) 42 + { 43 + u16 v16; 44 + v16 = ctrl_inw(CONFIG_HD64461_IOBASE + HD64461_GPBDR - 0x10000); 45 + if (on) 46 + ctrl_outw(v16 & (~HD64461_GPBDR_LED_RED), CONFIG_HD64461_IOBASE + HD64461_GPBDR - 0x10000); 47 + else 48 + ctrl_outw(v16 | HD64461_GPBDR_LED_RED, CONFIG_HD64461_IOBASE + HD64461_GPBDR - 0x10000); 49 + } 50 + 51 + static inline void hp6xx_led_green(int on) 52 + { 53 + u8 v8; 54 + 55 + v8 = ctrl_inb(PKDR); 56 + if (on) 57 + ctrl_outb(v8 & (~PKDR_LED_GREEN), PKDR); 58 + else 59 + ctrl_outb(v8 | PKDR_LED_GREEN, PKDR); 60 + } 46 61 47 62 48 63 #endif /* __ASM_SH_HP6XX_H */
+17
include/asm-sh/pm.h
··· 1 + /* 2 + * This file is subject to the terms and conditions of the GNU General Public 3 + * License. See the file "COPYING" in the main directory of this archive 4 + * for more details. 5 + * 6 + * Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com> 7 + * 8 + */ 9 + #ifndef __ASM_SH_PM_H 10 + #define __ASM_SH_PM_H 11 + 12 + extern u8 wakeup_start; 13 + extern u8 wakeup_end; 14 + 15 + void pm_enter(void); 16 + 17 + #endif
+25
include/asm-sh/system.h
··· 172 172 : "memory"); 173 173 } 174 174 175 + static __inline__ void set_bl_bit(void) 176 + { 177 + unsigned long __dummy0, __dummy1; 178 + 179 + __asm__ __volatile__ ("stc sr, %0\n\t" 180 + "or %2, %0\n\t" 181 + "and %3, %0\n\t" 182 + "ldc %0, sr" 183 + : "=&r" (__dummy0), "=r" (__dummy1) 184 + : "r" (0x10000000), "r" (0xffffff0f) 185 + : "memory"); 186 + } 187 + 188 + static __inline__ void clear_bl_bit(void) 189 + { 190 + unsigned long __dummy0, __dummy1; 191 + 192 + __asm__ __volatile__ ("stc sr, %0\n\t" 193 + "and %2, %0\n\t" 194 + "ldc %0, sr" 195 + : "=&r" (__dummy0), "=r" (__dummy1) 196 + : "1" (~0x10000000) 197 + : "memory"); 198 + } 199 + 175 200 #define local_save_flags(x) \ 176 201 __asm__("stc sr, %0; and #0xf0, %0" : "=&z" (x) :/**/: "memory" ) 177 202
+2
include/asm-sh/timer.h
··· 6 6 7 7 struct sys_timer_ops { 8 8 int (*init)(void); 9 + int (*start)(void); 10 + int (*stop)(void); 9 11 unsigned long (*get_offset)(void); 10 12 unsigned long (*get_frequency)(void); 11 13 };