mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 20:22:09 +00:00
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>
This commit is contained in:
parent
ef48e8e349
commit
3aa770e797
@ -638,6 +638,16 @@ source "fs/Kconfig.binfmt"
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Power management options (EXPERIMENTAL)"
|
||||
depends on EXPERIMENTAL
|
||||
|
||||
source kernel/power/Kconfig
|
||||
|
||||
config APM
|
||||
bool "Advanced Power Management Emulation"
|
||||
depends on PM
|
||||
endmenu
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
source "drivers/Kconfig"
|
||||
|
@ -2,5 +2,8 @@
|
||||
# Makefile for the HP6xx specific parts of the kernel
|
||||
#
|
||||
|
||||
obj-y := mach.o setup.o
|
||||
obj-y := mach.o setup.o
|
||||
obj-$(CONFIG_PM) += pm.o pm_wakeup.o
|
||||
obj-$(CONFIG_APM) += hp6xx_apm.o
|
||||
|
||||
|
||||
|
123
arch/sh/boards/hp6xx/hp6xx_apm.c
Normal file
123
arch/sh/boards/hp6xx/hp6xx_apm.c
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
* bios-less APM driver for hp680
|
||||
*
|
||||
* Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/apm_bios.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/apm.h>
|
||||
#include <asm/adc.h>
|
||||
#include <asm/hp6xx/hp6xx.h>
|
||||
|
||||
#define SH7709_PGDR 0xa400012c
|
||||
|
||||
#define APM_CRITICAL 10
|
||||
#define APM_LOW 30
|
||||
|
||||
#define HP680_BATTERY_MAX 875
|
||||
#define HP680_BATTERY_MIN 600
|
||||
#define HP680_BATTERY_AC_ON 900
|
||||
|
||||
#define MODNAME "hp6x0_apm"
|
||||
|
||||
static int hp6x0_apm_get_info(char *buf, char **start, off_t fpos, int length)
|
||||
{
|
||||
u8 pgdr;
|
||||
char *p;
|
||||
int battery_status;
|
||||
int battery_flag;
|
||||
int ac_line_status;
|
||||
int time_units = APM_BATTERY_LIFE_UNKNOWN;
|
||||
|
||||
int battery = adc_single(ADC_CHANNEL_BATTERY);
|
||||
int backup = adc_single(ADC_CHANNEL_BACKUP);
|
||||
int charging = adc_single(ADC_CHANNEL_CHARGE);
|
||||
int percentage;
|
||||
|
||||
percentage = 100 * (battery - HP680_BATTERY_MIN) /
|
||||
(HP680_BATTERY_MAX - HP680_BATTERY_MIN);
|
||||
|
||||
ac_line_status = (battery > HP680_BATTERY_AC_ON) ?
|
||||
APM_AC_ONLINE : APM_AC_OFFLINE;
|
||||
|
||||
p = buf;
|
||||
|
||||
pgdr = ctrl_inb(SH7709_PGDR);
|
||||
if (pgdr & PGDR_MAIN_BATTERY_OUT) {
|
||||
battery_status = APM_BATTERY_STATUS_NOT_PRESENT;
|
||||
battery_flag = 0x80;
|
||||
percentage = -1;
|
||||
} else if (charging < 8 ) {
|
||||
battery_status = APM_BATTERY_STATUS_CHARGING;
|
||||
battery_flag = 0x08;
|
||||
ac_line_status = 0xff;
|
||||
} else if (percentage <= APM_CRITICAL) {
|
||||
battery_status = APM_BATTERY_STATUS_CRITICAL;
|
||||
battery_flag = 0x04;
|
||||
} else if (percentage <= APM_LOW) {
|
||||
battery_status = APM_BATTERY_STATUS_LOW;
|
||||
battery_flag = 0x02;
|
||||
} else {
|
||||
battery_status = APM_BATTERY_STATUS_HIGH;
|
||||
battery_flag = 0x01;
|
||||
}
|
||||
|
||||
p += sprintf(p, "1.0 1.2 0x%02x 0x%02x 0x%02x 0x%02x %d%% %d %s\n",
|
||||
APM_32_BIT_SUPPORT,
|
||||
ac_line_status,
|
||||
battery_status,
|
||||
battery_flag,
|
||||
percentage,
|
||||
time_units,
|
||||
"min");
|
||||
p += sprintf(p, "bat=%d backup=%d charge=%d\n",
|
||||
battery, backup, charging);
|
||||
|
||||
return p - buf;
|
||||
}
|
||||
|
||||
static irqreturn_t hp6x0_apm_interrupt(int irq, void *dev, struct pt_regs *regs)
|
||||
{
|
||||
if (!apm_suspended)
|
||||
apm_queue_event(APM_USER_SUSPEND);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int __init hp6x0_apm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = request_irq(HP680_BTN_IRQ, hp6x0_apm_interrupt,
|
||||
SA_INTERRUPT, MODNAME, 0);
|
||||
if (unlikely(ret < 0)) {
|
||||
printk(KERN_ERR MODNAME ": IRQ %d request failed\n",
|
||||
HP680_BTN_IRQ);
|
||||
return ret;
|
||||
}
|
||||
|
||||
apm_get_info = hp6x0_apm_get_info;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit hp6x0_apm_exit(void)
|
||||
{
|
||||
free_irq(HP680_BTN_IRQ, 0);
|
||||
apm_get_info = 0;
|
||||
}
|
||||
|
||||
module_init(hp6x0_apm_init);
|
||||
module_exit(hp6x0_apm_exit);
|
||||
|
||||
MODULE_AUTHOR("Adriy Skulysh");
|
||||
MODULE_DESCRIPTION("hp6xx Advanced Power Management");
|
||||
MODULE_LICENSE("GPL");
|
88
arch/sh/boards/hp6xx/pm.c
Normal file
88
arch/sh/boards/hp6xx/pm.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* hp6x0 Power Management Routines
|
||||
*
|
||||
* Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License.
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/time.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/hd64461.h>
|
||||
#include <asm/hp6xx/hp6xx.h>
|
||||
#include <asm/cpu/dac.h>
|
||||
#include <asm/pm.h>
|
||||
|
||||
#define STBCR 0xffffff82
|
||||
#define STBCR2 0xffffff88
|
||||
|
||||
static int hp6x0_pm_enter(suspend_state_t state)
|
||||
{
|
||||
u8 stbcr, stbcr2;
|
||||
#ifdef CONFIG_HD64461_ENABLER
|
||||
u8 scr;
|
||||
u16 hd64461_stbcr;
|
||||
#endif
|
||||
|
||||
if (state != PM_SUSPEND_MEM)
|
||||
return -EINVAL;
|
||||
|
||||
#ifdef CONFIG_HD64461_ENABLER
|
||||
outb(0, HD64461_PCC1CSCIER);
|
||||
|
||||
scr = inb(HD64461_PCC1SCR);
|
||||
scr |= HD64461_PCCSCR_VCC1;
|
||||
outb(scr, HD64461_PCC1SCR);
|
||||
|
||||
hd64461_stbcr = inw(HD64461_STBCR);
|
||||
hd64461_stbcr |= HD64461_STBCR_SPC1ST;
|
||||
outw(hd64461_stbcr, HD64461_STBCR);
|
||||
#endif
|
||||
|
||||
ctrl_outb(0x1f, DACR);
|
||||
|
||||
stbcr = ctrl_inb(STBCR);
|
||||
ctrl_outb(0x01, STBCR);
|
||||
|
||||
stbcr2 = ctrl_inb(STBCR2);
|
||||
ctrl_outb(0x7f , STBCR2);
|
||||
|
||||
outw(0xf07f, HD64461_SCPUCR);
|
||||
|
||||
pm_enter();
|
||||
|
||||
outw(0, HD64461_SCPUCR);
|
||||
ctrl_outb(stbcr, STBCR);
|
||||
ctrl_outb(stbcr2, STBCR2);
|
||||
|
||||
#ifdef CONFIG_HD64461_ENABLER
|
||||
hd64461_stbcr = inw(HD64461_STBCR);
|
||||
hd64461_stbcr &= ~HD64461_STBCR_SPC1ST;
|
||||
outw(hd64461_stbcr, HD64461_STBCR);
|
||||
|
||||
outb(0x4c, HD64461_PCC1CSCIER);
|
||||
outb(0x00, HD64461_PCC1CSCR);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set to PM_DISK_FIRMWARE so we can quickly veto suspend-to-disk.
|
||||
*/
|
||||
static struct pm_ops hp6x0_pm_ops = {
|
||||
.pm_disk_mode = PM_DISK_FIRMWARE,
|
||||
.enter = hp6x0_pm_enter,
|
||||
};
|
||||
|
||||
static int __init hp6x0_pm_init(void)
|
||||
{
|
||||
pm_set_ops(&hp6x0_pm_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
late_initcall(hp6x0_pm_init);
|
58
arch/sh/boards/hp6xx/pm_wakeup.S
Normal file
58
arch/sh/boards/hp6xx/pm_wakeup.S
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/cpu/mmu_context.h>
|
||||
|
||||
#define k0 r0
|
||||
#define k1 r1
|
||||
#define k2 r2
|
||||
#define k3 r3
|
||||
#define k4 r4
|
||||
|
||||
/*
|
||||
* Kernel mode register usage:
|
||||
* k0 scratch
|
||||
* k1 scratch
|
||||
* k2 scratch (Exception code)
|
||||
* k3 scratch (Return address)
|
||||
* k4 scratch
|
||||
* k5 reserved
|
||||
* k6 Global Interrupt Mask (0--15 << 4)
|
||||
* k7 CURRENT_THREAD_INFO (pointer to current thread info)
|
||||
*/
|
||||
|
||||
ENTRY(wakeup_start)
|
||||
! clear STBY bit
|
||||
mov #-126, k2
|
||||
and #127, k0
|
||||
mov.b k0, @k2
|
||||
! enable refresh
|
||||
mov.l 5f, k1
|
||||
mov.w 6f, k0
|
||||
mov.w k0, @k1
|
||||
! jump to handler
|
||||
mov.l 2f, k2
|
||||
mov.l 3f, k3
|
||||
mov.l @k2, k2
|
||||
|
||||
mov.l 4f, k1
|
||||
jmp @k1
|
||||
nop
|
||||
|
||||
.align 2
|
||||
1: .long EXPEVT
|
||||
2: .long INTEVT
|
||||
3: .long ret_from_irq
|
||||
4: .long handle_exception
|
||||
5: .long 0xffffff68
|
||||
6: .word 0x0524
|
||||
|
||||
ENTRY(wakeup_end)
|
||||
nop
|
@ -15,6 +15,9 @@
|
||||
#include <asm/hp6xx/hp6xx.h>
|
||||
#include <asm/cpu/dac.h>
|
||||
|
||||
#define SCPCR 0xa4000116
|
||||
#define SCPDR 0xa4000136
|
||||
|
||||
const char *get_system_type(void)
|
||||
{
|
||||
return "HP6xx";
|
||||
@ -24,6 +27,7 @@ int __init platform_setup(void)
|
||||
{
|
||||
u8 v8;
|
||||
u16 v;
|
||||
|
||||
v = inw(HD64461_STBCR);
|
||||
v |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST |
|
||||
HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST |
|
||||
@ -50,5 +54,15 @@ int __init platform_setup(void)
|
||||
v8 &= ~DACR_DAE;
|
||||
ctrl_outb(v8,DACR);
|
||||
|
||||
v8 = ctrl_inb(SCPDR);
|
||||
v8 |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
|
||||
v8 &= ~SCPDR_TS_SCAN_ENABLE;
|
||||
ctrl_outb(v8, SCPDR);
|
||||
|
||||
v = ctrl_inw(SCPCR);
|
||||
v &= ~SCPCR_TS_MASK;
|
||||
v |= SCPCR_TS_ENABLE;
|
||||
ctrl_outw(v, SCPCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -18,3 +18,5 @@ obj-$(CONFIG_SH_CPU_FREQ) += cpufreq.o
|
||||
obj-$(CONFIG_MODULES) += module.o
|
||||
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
|
||||
obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o
|
||||
obj-$(CONFIG_APM) += apm.o
|
||||
obj-$(CONFIG_PM) += pm.o
|
||||
|
539
arch/sh/kernel/apm.c
Normal file
539
arch/sh/kernel/apm.c
Normal file
@ -0,0 +1,539 @@
|
||||
/*
|
||||
* bios-less APM driver for hp680
|
||||
*
|
||||
* Copyright 2005 (c) Andriy Skulysh <askulysh@gmail.com>
|
||||
*
|
||||
* based on ARM APM driver by
|
||||
* Jamey Hicks <jamey@crl.dec.com>
|
||||
*
|
||||
* adapted from the APM BIOS driver for Linux by
|
||||
* Stephen Rothwell (sfr@linuxcare.com)
|
||||
*
|
||||
* APM 1.2 Reference:
|
||||
* Intel Corporation, Microsoft Corporation. Advanced Power Management
|
||||
* (APM) BIOS Interface Specification, Revision 1.2, February 1996.
|
||||
*
|
||||
* [This document is available from Microsoft at:
|
||||
* http://www.microsoft.com/hwdev/busbios/amp_12.htm]
|
||||
*/
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/poll.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/apm_bios.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_legacy.h>
|
||||
#include <asm/apm.h>
|
||||
|
||||
#define MODNAME "apm"
|
||||
|
||||
/*
|
||||
* The apm_bios device is one of the misc char devices.
|
||||
* This is its minor number.
|
||||
*/
|
||||
#define APM_MINOR_DEV 134
|
||||
|
||||
/*
|
||||
* Maximum number of events stored
|
||||
*/
|
||||
#define APM_MAX_EVENTS 16
|
||||
|
||||
struct apm_queue {
|
||||
unsigned int event_head;
|
||||
unsigned int event_tail;
|
||||
apm_event_t events[APM_MAX_EVENTS];
|
||||
};
|
||||
|
||||
/*
|
||||
* The per-file APM data
|
||||
*/
|
||||
struct apm_user {
|
||||
struct list_head list;
|
||||
|
||||
unsigned int suser: 1;
|
||||
unsigned int writer: 1;
|
||||
unsigned int reader: 1;
|
||||
|
||||
int suspend_result;
|
||||
unsigned int suspend_state;
|
||||
#define SUSPEND_NONE 0 /* no suspend pending */
|
||||
#define SUSPEND_PENDING 1 /* suspend pending read */
|
||||
#define SUSPEND_READ 2 /* suspend read, pending ack */
|
||||
#define SUSPEND_ACKED 3 /* suspend acked */
|
||||
#define SUSPEND_DONE 4 /* suspend completed */
|
||||
|
||||
struct apm_queue queue;
|
||||
};
|
||||
|
||||
/*
|
||||
* Local variables
|
||||
*/
|
||||
static int suspends_pending;
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue);
|
||||
|
||||
/*
|
||||
* This is a list of everyone who has opened /dev/apm_bios
|
||||
*/
|
||||
static DECLARE_RWSEM(user_list_lock);
|
||||
static LIST_HEAD(apm_user_list);
|
||||
|
||||
/*
|
||||
* kapmd info. kapmd provides us a process context to handle
|
||||
* "APM" events within - specifically necessary if we're going
|
||||
* to be suspending the system.
|
||||
*/
|
||||
static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait);
|
||||
static DECLARE_COMPLETION(kapmd_exit);
|
||||
static DEFINE_SPINLOCK(kapmd_queue_lock);
|
||||
static struct apm_queue kapmd_queue;
|
||||
|
||||
int apm_suspended;
|
||||
EXPORT_SYMBOL(apm_suspended);
|
||||
|
||||
/* Platform-specific apm_read_proc(). */
|
||||
int (*apm_get_info)(char *buf, char **start, off_t fpos, int length);
|
||||
EXPORT_SYMBOL(apm_get_info);
|
||||
|
||||
/*
|
||||
* APM event queue management.
|
||||
*/
|
||||
static inline int queue_empty(struct apm_queue *q)
|
||||
{
|
||||
return q->event_head == q->event_tail;
|
||||
}
|
||||
|
||||
static inline apm_event_t queue_get_event(struct apm_queue *q)
|
||||
{
|
||||
q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
|
||||
return q->events[q->event_tail];
|
||||
}
|
||||
|
||||
static void queue_add_event(struct apm_queue *q, apm_event_t event)
|
||||
{
|
||||
q->event_head = (q->event_head + 1) % APM_MAX_EVENTS;
|
||||
if (q->event_head == q->event_tail) {
|
||||
static int notified;
|
||||
|
||||
if (notified++ == 0)
|
||||
printk(KERN_ERR "apm: an event queue overflowed\n");
|
||||
|
||||
q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS;
|
||||
}
|
||||
q->events[q->event_head] = event;
|
||||
}
|
||||
|
||||
static void queue_event_one_user(struct apm_user *as, apm_event_t event)
|
||||
{
|
||||
if (as->suser && as->writer) {
|
||||
switch (event) {
|
||||
case APM_SYS_SUSPEND:
|
||||
case APM_USER_SUSPEND:
|
||||
/*
|
||||
* If this user already has a suspend pending,
|
||||
* don't queue another one.
|
||||
*/
|
||||
if (as->suspend_state != SUSPEND_NONE)
|
||||
return;
|
||||
|
||||
as->suspend_state = SUSPEND_PENDING;
|
||||
suspends_pending++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
queue_add_event(&as->queue, event);
|
||||
}
|
||||
|
||||
static void queue_event(apm_event_t event, struct apm_user *sender)
|
||||
{
|
||||
struct apm_user *as;
|
||||
|
||||
down_read(&user_list_lock);
|
||||
|
||||
list_for_each_entry(as, &apm_user_list, list)
|
||||
if (as != sender && as->reader)
|
||||
queue_event_one_user(as, event);
|
||||
|
||||
up_read(&user_list_lock);
|
||||
wake_up_interruptible(&apm_waitqueue);
|
||||
}
|
||||
|
||||
/**
|
||||
* apm_queue_event - queue an APM event for kapmd
|
||||
* @event: APM event
|
||||
*
|
||||
* Queue an APM event for kapmd to process and ultimately take the
|
||||
* appropriate action. Only a subset of events are handled:
|
||||
* %APM_LOW_BATTERY
|
||||
* %APM_POWER_STATUS_CHANGE
|
||||
* %APM_USER_SUSPEND
|
||||
* %APM_SYS_SUSPEND
|
||||
* %APM_CRITICAL_SUSPEND
|
||||
*/
|
||||
void apm_queue_event(apm_event_t event)
|
||||
{
|
||||
spin_lock_irq(&kapmd_queue_lock);
|
||||
queue_add_event(&kapmd_queue, event);
|
||||
spin_unlock_irq(&kapmd_queue_lock);
|
||||
|
||||
wake_up_interruptible(&kapmd_wait);
|
||||
}
|
||||
EXPORT_SYMBOL(apm_queue_event);
|
||||
|
||||
static void apm_suspend(void)
|
||||
{
|
||||
struct apm_user *as;
|
||||
int err;
|
||||
|
||||
apm_suspended = 1;
|
||||
err = pm_suspend(PM_SUSPEND_MEM);
|
||||
|
||||
/*
|
||||
* Anyone on the APM queues will think we're still suspended.
|
||||
* Send a message so everyone knows we're now awake again.
|
||||
*/
|
||||
queue_event(APM_NORMAL_RESUME, NULL);
|
||||
|
||||
/*
|
||||
* Finally, wake up anyone who is sleeping on the suspend.
|
||||
*/
|
||||
down_read(&user_list_lock);
|
||||
list_for_each_entry(as, &apm_user_list, list) {
|
||||
as->suspend_result = err;
|
||||
as->suspend_state = SUSPEND_DONE;
|
||||
}
|
||||
up_read(&user_list_lock);
|
||||
|
||||
wake_up(&apm_suspend_waitqueue);
|
||||
apm_suspended = 0;
|
||||
}
|
||||
|
||||
static ssize_t apm_read(struct file *fp, char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct apm_user *as = fp->private_data;
|
||||
apm_event_t event;
|
||||
int i = count, ret = 0;
|
||||
|
||||
if (count < sizeof(apm_event_t))
|
||||
return -EINVAL;
|
||||
|
||||
if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
|
||||
wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue));
|
||||
|
||||
while ((i >= sizeof(event)) && !queue_empty(&as->queue)) {
|
||||
event = queue_get_event(&as->queue);
|
||||
|
||||
ret = -EFAULT;
|
||||
if (copy_to_user(buf, &event, sizeof(event)))
|
||||
break;
|
||||
|
||||
if (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)
|
||||
as->suspend_state = SUSPEND_READ;
|
||||
|
||||
buf += sizeof(event);
|
||||
i -= sizeof(event);
|
||||
}
|
||||
|
||||
if (i < count)
|
||||
ret = count - i;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static unsigned int apm_poll(struct file *fp, poll_table * wait)
|
||||
{
|
||||
struct apm_user *as = fp->private_data;
|
||||
|
||||
poll_wait(fp, &apm_waitqueue, wait);
|
||||
return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM;
|
||||
}
|
||||
|
||||
/*
|
||||
* apm_ioctl - handle APM ioctl
|
||||
*
|
||||
* APM_IOC_SUSPEND
|
||||
* This IOCTL is overloaded, and performs two functions. It is used to:
|
||||
* - initiate a suspend
|
||||
* - acknowledge a suspend read from /dev/apm_bios.
|
||||
* Only when everyone who has opened /dev/apm_bios with write permission
|
||||
* has acknowledge does the actual suspend happen.
|
||||
*/
|
||||
static int
|
||||
apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg)
|
||||
{
|
||||
struct apm_user *as = filp->private_data;
|
||||
unsigned long flags;
|
||||
int err = -EINVAL;
|
||||
|
||||
if (!as->suser || !as->writer)
|
||||
return -EPERM;
|
||||
|
||||
switch (cmd) {
|
||||
case APM_IOC_SUSPEND:
|
||||
as->suspend_result = -EINTR;
|
||||
|
||||
if (as->suspend_state == SUSPEND_READ) {
|
||||
/*
|
||||
* If we read a suspend command from /dev/apm_bios,
|
||||
* then the corresponding APM_IOC_SUSPEND ioctl is
|
||||
* interpreted as an acknowledge.
|
||||
*/
|
||||
as->suspend_state = SUSPEND_ACKED;
|
||||
suspends_pending--;
|
||||
} else {
|
||||
/*
|
||||
* Otherwise it is a request to suspend the system.
|
||||
* Queue an event for all readers, and expect an
|
||||
* acknowledge from all writers who haven't already
|
||||
* acknowledged.
|
||||
*/
|
||||
queue_event(APM_USER_SUSPEND, as);
|
||||
}
|
||||
|
||||
/*
|
||||
* If there are no further acknowledges required, suspend
|
||||
* the system.
|
||||
*/
|
||||
if (suspends_pending == 0)
|
||||
apm_suspend();
|
||||
|
||||
/*
|
||||
* Wait for the suspend/resume to complete. If there are
|
||||
* pending acknowledges, we wait here for them.
|
||||
*
|
||||
* Note that we need to ensure that the PM subsystem does
|
||||
* not kick us out of the wait when it suspends the threads.
|
||||
*/
|
||||
flags = current->flags;
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
/*
|
||||
* Note: do not allow a thread which is acking the suspend
|
||||
* to escape until the resume is complete.
|
||||
*/
|
||||
if (as->suspend_state == SUSPEND_ACKED)
|
||||
wait_event(apm_suspend_waitqueue,
|
||||
as->suspend_state == SUSPEND_DONE);
|
||||
else
|
||||
wait_event_interruptible(apm_suspend_waitqueue,
|
||||
as->suspend_state == SUSPEND_DONE);
|
||||
|
||||
current->flags = flags;
|
||||
err = as->suspend_result;
|
||||
as->suspend_state = SUSPEND_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int apm_release(struct inode * inode, struct file * filp)
|
||||
{
|
||||
struct apm_user *as = filp->private_data;
|
||||
filp->private_data = NULL;
|
||||
|
||||
down_write(&user_list_lock);
|
||||
list_del(&as->list);
|
||||
up_write(&user_list_lock);
|
||||
|
||||
/*
|
||||
* We are now unhooked from the chain. As far as new
|
||||
* events are concerned, we no longer exist. However, we
|
||||
* need to balance suspends_pending, which means the
|
||||
* possibility of sleeping.
|
||||
*/
|
||||
if (as->suspend_state != SUSPEND_NONE) {
|
||||
suspends_pending -= 1;
|
||||
if (suspends_pending == 0)
|
||||
apm_suspend();
|
||||
}
|
||||
|
||||
kfree(as);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apm_open(struct inode * inode, struct file * filp)
|
||||
{
|
||||
struct apm_user *as;
|
||||
|
||||
as = kzalloc(sizeof(*as), GFP_KERNEL);
|
||||
if (as) {
|
||||
/*
|
||||
* XXX - this is a tiny bit broken, when we consider BSD
|
||||
* process accounting. If the device is opened by root, we
|
||||
* instantly flag that we used superuser privs. Who knows,
|
||||
* we might close the device immediately without doing a
|
||||
* privileged operation -- cevans
|
||||
*/
|
||||
as->suser = capable(CAP_SYS_ADMIN);
|
||||
as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE;
|
||||
as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ;
|
||||
|
||||
down_write(&user_list_lock);
|
||||
list_add(&as->list, &apm_user_list);
|
||||
up_write(&user_list_lock);
|
||||
|
||||
filp->private_data = as;
|
||||
}
|
||||
|
||||
return as ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static struct file_operations apm_bios_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = apm_read,
|
||||
.poll = apm_poll,
|
||||
.ioctl = apm_ioctl,
|
||||
.open = apm_open,
|
||||
.release = apm_release,
|
||||
};
|
||||
|
||||
static struct miscdevice apm_device = {
|
||||
.minor = APM_MINOR_DEV,
|
||||
.name = "apm_bios",
|
||||
.fops = &apm_bios_fops
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
/*
|
||||
* Arguments, with symbols from linux/apm_bios.h.
|
||||
*
|
||||
* 0) Linux driver version (this will change if format changes)
|
||||
* 1) APM BIOS Version. Usually 1.0, 1.1 or 1.2.
|
||||
* 2) APM flags from APM Installation Check (0x00):
|
||||
* bit 0: APM_16_BIT_SUPPORT
|
||||
* bit 1: APM_32_BIT_SUPPORT
|
||||
* bit 2: APM_IDLE_SLOWS_CLOCK
|
||||
* bit 3: APM_BIOS_DISABLED
|
||||
* bit 4: APM_BIOS_DISENGAGED
|
||||
* 3) AC line status
|
||||
* 0x00: Off-line
|
||||
* 0x01: On-line
|
||||
* 0x02: On backup power (BIOS >= 1.1 only)
|
||||
* 0xff: Unknown
|
||||
* 4) Battery status
|
||||
* 0x00: High
|
||||
* 0x01: Low
|
||||
* 0x02: Critical
|
||||
* 0x03: Charging
|
||||
* 0x04: Selected battery not present (BIOS >= 1.2 only)
|
||||
* 0xff: Unknown
|
||||
* 5) Battery flag
|
||||
* bit 0: High
|
||||
* bit 1: Low
|
||||
* bit 2: Critical
|
||||
* bit 3: Charging
|
||||
* bit 7: No system battery
|
||||
* 0xff: Unknown
|
||||
* 6) Remaining battery life (percentage of charge):
|
||||
* 0-100: valid
|
||||
* -1: Unknown
|
||||
* 7) Remaining battery life (time units):
|
||||
* Number of remaining minutes or seconds
|
||||
* -1: Unknown
|
||||
* 8) min = minutes; sec = seconds
|
||||
*/
|
||||
static int apm_read_proc(char *buf, char **start, off_t fpos, int length)
|
||||
{
|
||||
if (likely(apm_get_info))
|
||||
return apm_get_info(buf, start, fpos, length);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int kapmd(void *arg)
|
||||
{
|
||||
daemonize("kapmd");
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
do {
|
||||
apm_event_t event;
|
||||
|
||||
wait_event_interruptible(kapmd_wait,
|
||||
!queue_empty(&kapmd_queue) || !pm_active);
|
||||
|
||||
if (!pm_active)
|
||||
break;
|
||||
|
||||
spin_lock_irq(&kapmd_queue_lock);
|
||||
event = 0;
|
||||
if (!queue_empty(&kapmd_queue))
|
||||
event = queue_get_event(&kapmd_queue);
|
||||
spin_unlock_irq(&kapmd_queue_lock);
|
||||
|
||||
switch (event) {
|
||||
case 0:
|
||||
break;
|
||||
|
||||
case APM_LOW_BATTERY:
|
||||
case APM_POWER_STATUS_CHANGE:
|
||||
queue_event(event, NULL);
|
||||
break;
|
||||
|
||||
case APM_USER_SUSPEND:
|
||||
case APM_SYS_SUSPEND:
|
||||
queue_event(event, NULL);
|
||||
if (suspends_pending == 0)
|
||||
apm_suspend();
|
||||
break;
|
||||
|
||||
case APM_CRITICAL_SUSPEND:
|
||||
apm_suspend();
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
complete_and_exit(&kapmd_exit, 0);
|
||||
}
|
||||
|
||||
static int __init apm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
pm_active = 1;
|
||||
|
||||
ret = kernel_thread(kapmd, NULL, CLONE_KERNEL);
|
||||
if (unlikely(ret < 0)) {
|
||||
pm_active = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
create_proc_info_entry("apm", 0, NULL, apm_read_proc);
|
||||
|
||||
ret = misc_register(&apm_device);
|
||||
if (unlikely(ret != 0)) {
|
||||
remove_proc_entry("apm", NULL);
|
||||
|
||||
pm_active = 0;
|
||||
wake_up(&kapmd_wait);
|
||||
wait_for_completion(&kapmd_exit);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit apm_exit(void)
|
||||
{
|
||||
misc_deregister(&apm_device);
|
||||
remove_proc_entry("apm", NULL);
|
||||
|
||||
pm_active = 0;
|
||||
wake_up(&kapmd_wait);
|
||||
wait_for_completion(&kapmd_exit);
|
||||
}
|
||||
|
||||
module_init(apm_init);
|
||||
module_exit(apm_exit);
|
||||
|
||||
MODULE_AUTHOR("Stephen Rothwell, Andriy Skulysh");
|
||||
MODULE_DESCRIPTION("Advanced Power Management");
|
||||
MODULE_LICENSE("GPL");
|
@ -308,7 +308,7 @@ ENTRY(exception_error)
|
||||
.align 2
|
||||
ret_from_exception:
|
||||
preempt_stop()
|
||||
ret_from_irq:
|
||||
ENTRY(ret_from_irq)
|
||||
!
|
||||
mov #OFF_SR, r0
|
||||
mov.l @(r0,r15), r0 ! get status register
|
||||
@ -704,7 +704,7 @@ interrupt:
|
||||
!
|
||||
!
|
||||
.align 2
|
||||
handle_exception:
|
||||
ENTRY(handle_exception)
|
||||
! Using k0, k1 for scratch registers (r0_bank1, r1_bank),
|
||||
! save all registers onto stack.
|
||||
!
|
||||
|
88
arch/sh/kernel/pm.c
Normal file
88
arch/sh/kernel/pm.c
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Generic Power Management Routine
|
||||
*
|
||||
* Copyright (c) 2006 Andriy Skulysh <askulsyh@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License.
|
||||
*/
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <asm/freq.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/watchdog.h>
|
||||
#include <asm/pm.h>
|
||||
|
||||
#define INTR_OFFSET 0x600
|
||||
|
||||
#define STBCR 0xffffff82
|
||||
#define STBCR2 0xffffff88
|
||||
|
||||
#define STBCR_STBY 0x80
|
||||
#define STBCR_MSTP2 0x04
|
||||
|
||||
#define MCR 0xffffff68
|
||||
#define RTCNT 0xffffff70
|
||||
|
||||
#define MCR_RMODE 2
|
||||
#define MCR_RFSH 4
|
||||
|
||||
void pm_enter(void)
|
||||
{
|
||||
u8 stbcr, csr;
|
||||
u16 frqcr, mcr;
|
||||
u32 vbr_new, vbr_old;
|
||||
|
||||
set_bl_bit();
|
||||
|
||||
/* set wdt */
|
||||
csr = sh_wdt_read_csr();
|
||||
csr &= ~WTCSR_TME;
|
||||
csr |= WTCSR_CKS_4096;
|
||||
sh_wdt_write_csr(csr);
|
||||
csr = sh_wdt_read_csr();
|
||||
sh_wdt_write_cnt(0);
|
||||
|
||||
/* disable PLL1 */
|
||||
frqcr = ctrl_inw(FRQCR);
|
||||
frqcr &= ~(FRQCR_PLLEN | FRQCR_PSTBY);
|
||||
ctrl_outw(frqcr, FRQCR);
|
||||
|
||||
/* enable standby */
|
||||
stbcr = ctrl_inb(STBCR);
|
||||
ctrl_outb(stbcr | STBCR_STBY | STBCR_MSTP2, STBCR);
|
||||
|
||||
/* set self-refresh */
|
||||
mcr = ctrl_inw(MCR);
|
||||
ctrl_outw(mcr & ~MCR_RFSH, MCR);
|
||||
|
||||
/* set interrupt handler */
|
||||
asm volatile("stc vbr, %0" : "=r" (vbr_old));
|
||||
vbr_new = get_zeroed_page(GFP_ATOMIC);
|
||||
udelay(50);
|
||||
memcpy((void*)(vbr_new + INTR_OFFSET),
|
||||
&wakeup_start, &wakeup_end - &wakeup_start);
|
||||
asm volatile("ldc %0, vbr" : : "r" (vbr_new));
|
||||
|
||||
ctrl_outw(0, RTCNT);
|
||||
ctrl_outw(mcr | MCR_RFSH | MCR_RMODE, MCR);
|
||||
|
||||
cpu_sleep();
|
||||
|
||||
asm volatile("ldc %0, vbr" : : "r" (vbr_old));
|
||||
|
||||
free_page(vbr_new);
|
||||
|
||||
/* enable PLL1 */
|
||||
frqcr = ctrl_inw(FRQCR);
|
||||
frqcr |= FRQCR_PSTBY;
|
||||
ctrl_outw(frqcr, FRQCR);
|
||||
udelay(50);
|
||||
frqcr |= FRQCR_PLLEN;
|
||||
ctrl_outw(frqcr, FRQCR);
|
||||
|
||||
ctrl_outb(stbcr, STBCR);
|
||||
|
||||
clear_bl_bit();
|
||||
}
|
@ -116,6 +116,10 @@ EXPORT_SYMBOL(__down_trylock);
|
||||
EXPORT_SYMBOL(synchronize_irq);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
EXPORT_SYMBOL(pm_suspend);
|
||||
#endif
|
||||
|
||||
EXPORT_SYMBOL(csum_partial);
|
||||
#ifdef CONFIG_IPV6
|
||||
EXPORT_SYMBOL(csum_ipv6_magic);
|
||||
|
@ -143,8 +143,33 @@ void handle_timer_tick(struct pt_regs *regs)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
int timer_suspend(struct sys_device *dev, pm_message_t state)
|
||||
{
|
||||
struct sys_timer *sys_timer = container_of(dev, struct sys_timer, dev);
|
||||
|
||||
sys_timer->ops->stop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int timer_resume(struct sys_device *dev)
|
||||
{
|
||||
struct sys_timer *sys_timer = container_of(dev, struct sys_timer, dev);
|
||||
|
||||
sys_timer->ops->start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define timer_suspend NULL
|
||||
#define timer_resume NULL
|
||||
#endif
|
||||
|
||||
static struct sysdev_class timer_sysclass = {
|
||||
set_kset_name("timer"),
|
||||
.suspend = timer_suspend,
|
||||
.resume = timer_resume,
|
||||
};
|
||||
|
||||
static int __init timer_init_sysfs(void)
|
||||
|
@ -188,6 +188,18 @@ static struct clk tmu0_clk = {
|
||||
.ops = &tmu_clk_ops,
|
||||
};
|
||||
|
||||
static int tmu_timer_start(void)
|
||||
{
|
||||
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmu_timer_stop(void)
|
||||
{
|
||||
ctrl_outb(0, TMU_TSTR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tmu_timer_init(void)
|
||||
{
|
||||
unsigned long interval;
|
||||
@ -197,7 +209,7 @@ static int tmu_timer_init(void)
|
||||
tmu0_clk.parent = clk_get("module_clk");
|
||||
|
||||
/* Start TMU0 */
|
||||
ctrl_outb(0, TMU_TSTR);
|
||||
tmu_timer_stop();
|
||||
#if !defined(CONFIG_CPU_SUBTYPE_SH7300) && !defined(CONFIG_CPU_SUBTYPE_SH7760)
|
||||
ctrl_outb(TMU_TOCR_INIT, TMU_TOCR);
|
||||
#endif
|
||||
@ -211,13 +223,15 @@ static int tmu_timer_init(void)
|
||||
ctrl_outl(interval, TMU0_TCOR);
|
||||
ctrl_outl(interval, TMU0_TCNT);
|
||||
|
||||
ctrl_outb(TMU_TSTR_INIT, TMU_TSTR);
|
||||
tmu_timer_start();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sys_timer_ops tmu_timer_ops = {
|
||||
.init = tmu_timer_init,
|
||||
.start = tmu_timer_start,
|
||||
.stop = tmu_timer_stop,
|
||||
.get_frequency = tmu_timer_get_frequency,
|
||||
.get_offset = tmu_timer_get_offset,
|
||||
};
|
||||
|
@ -15,7 +15,6 @@
|
||||
#define HP680_TS_ABS_Y_MIN 80
|
||||
#define HP680_TS_ABS_Y_MAX 910
|
||||
|
||||
#define SCPCR 0xa4000116
|
||||
#define PHDR 0xa400012e
|
||||
#define SCPDR 0xa4000136
|
||||
|
||||
@ -77,19 +76,6 @@ static irqreturn_t hp680_ts_interrupt(int irq, void *dev, struct pt_regs *regs)
|
||||
|
||||
static int __init hp680_ts_init(void)
|
||||
{
|
||||
u8 scpdr;
|
||||
u16 scpcr;
|
||||
|
||||
scpdr = ctrl_inb(SCPDR);
|
||||
scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
|
||||
scpdr &= ~SCPDR_TS_SCAN_ENABLE;
|
||||
ctrl_outb(scpdr, SCPDR);
|
||||
|
||||
scpcr = ctrl_inw(SCPCR);
|
||||
scpcr &= ~SCPCR_TS_MASK;
|
||||
scpcr |= SCPCR_TS_ENABLE;
|
||||
ctrl_outw(scpcr, SCPCR);
|
||||
|
||||
hp680_ts_dev = input_allocate_device();
|
||||
if (!hp680_ts_dev)
|
||||
return -ENOMEM;
|
||||
|
46
include/asm-sh/apm.h
Normal file
46
include/asm-sh/apm.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com>
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __ASM_SH_APM_H
|
||||
#define __ASM_SH_APM_H
|
||||
|
||||
#define APM_AC_OFFLINE 0
|
||||
#define APM_AC_ONLINE 1
|
||||
#define APM_AC_BACKUP 2
|
||||
#define APM_AC_UNKNOWN 0xff
|
||||
|
||||
#define APM_BATTERY_STATUS_HIGH 0
|
||||
#define APM_BATTERY_STATUS_LOW 1
|
||||
#define APM_BATTERY_STATUS_CRITICAL 2
|
||||
#define APM_BATTERY_STATUS_CHARGING 3
|
||||
#define APM_BATTERY_STATUS_NOT_PRESENT 4
|
||||
#define APM_BATTERY_STATUS_UNKNOWN 0xff
|
||||
|
||||
#define APM_BATTERY_LIFE_UNKNOWN 0xFFFF
|
||||
#define APM_BATTERY_LIFE_MINUTES 0x8000
|
||||
#define APM_BATTERY_LIFE_VALUE_MASK 0x7FFF
|
||||
|
||||
#define APM_BATTERY_FLAG_HIGH (1 << 0)
|
||||
#define APM_BATTERY_FLAG_LOW (1 << 1)
|
||||
#define APM_BATTERY_FLAG_CRITICAL (1 << 2)
|
||||
#define APM_BATTERY_FLAG_CHARGING (1 << 3)
|
||||
#define APM_BATTERY_FLAG_NOT_PRESENT (1 << 7)
|
||||
#define APM_BATTERY_FLAG_UNKNOWN 0xff
|
||||
|
||||
#define APM_UNITS_MINS 0
|
||||
#define APM_UNITS_SECS 1
|
||||
#define APM_UNITS_UNKNOWN -1
|
||||
|
||||
|
||||
extern int (*apm_get_info)(char *buf, char **start, off_t fpos, int length);
|
||||
extern int apm_suspended;
|
||||
|
||||
void apm_queue_event(apm_event_t event);
|
||||
|
||||
#endif
|
@ -18,5 +18,9 @@
|
||||
#define MIN_DIVISOR_NR 0
|
||||
#define MAX_DIVISOR_NR 4
|
||||
|
||||
#define FRQCR_CKOEN 0x0100
|
||||
#define FRQCR_PLLEN 0x0080
|
||||
#define FRQCR_PSTBY 0x0040
|
||||
|
||||
#endif /* __ASM_CPU_SH3_FREQ_H */
|
||||
|
||||
|
@ -40,7 +40,12 @@
|
||||
#define HD64461_LCDCBAR 0x11000
|
||||
#define HD64461_LCDCLOR 0x11002
|
||||
#define HD64461_LCDCCR 0x11004
|
||||
#define HD64461_LCDCCR_MOFF 0x80
|
||||
#define HD64461_LCDCCR_STBACK 0x0400
|
||||
#define HD64461_LCDCCR_STREQ 0x0100
|
||||
#define HD64461_LCDCCR_MOFF 0x0080
|
||||
#define HD64461_LCDCCR_REFSEL 0x0040
|
||||
#define HD64461_LCDCCR_EPON 0x0020
|
||||
#define HD64461_LCDCCR_SPON 0x0010
|
||||
|
||||
#define HD64461_LDR1 0x11010
|
||||
#define HD64461_LDR1_DON 0x01
|
||||
|
@ -2,16 +2,33 @@
|
||||
#define __ASM_SH_HP6XX_H
|
||||
|
||||
/*
|
||||
* Copyright (C) 2003 Andriy Skulysh
|
||||
* Copyright (C) 2003, 2004, 2005 Andriy Skulysh
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define HP680_TS_IRQ IRQ3_IRQ
|
||||
#define HP680_BTN_IRQ IRQ0_IRQ
|
||||
#define HP680_TS_IRQ IRQ3_IRQ
|
||||
#define HP680_HD64461_IRQ IRQ4_IRQ
|
||||
|
||||
#define DAC_LCD_BRIGHTNESS 0
|
||||
#define DAC_SPEAKER_VOLUME 1
|
||||
|
||||
#define PGDR_OPENED 0x01
|
||||
#define PGDR_MAIN_BATTERY_OUT 0x04
|
||||
#define PGDR_PLAY_BUTTON 0x08
|
||||
#define PGDR_REWIND_BUTTON 0x10
|
||||
#define PGDR_RECORD_BUTTON 0x20
|
||||
|
||||
#define PHDR_TS_PEN_DOWN 0x08
|
||||
|
||||
#define PJDR_LED_BLINK 0x02
|
||||
|
||||
#define PKDR_LED_GREEN 0x10
|
||||
|
||||
#define SCPDR_TS_SCAN_ENABLE 0x20
|
||||
#define SCPDR_TS_SCAN_Y 0x02
|
||||
#define SCPDR_TS_SCAN_X 0x01
|
||||
@ -21,11 +38,43 @@
|
||||
|
||||
#define ADC_CHANNEL_TS_Y 1
|
||||
#define ADC_CHANNEL_TS_X 2
|
||||
#define ADC_CHANNEL_BATTERY 3
|
||||
#define ADC_CHANNEL_BACKUP 4
|
||||
#define ADC_CHANNEL_CHARGE 5
|
||||
|
||||
#define HD64461_GPADR_SPEAKER 0x01
|
||||
#define HD64461_GPADR_PCMCIA0 (0x02|0x08)
|
||||
|
||||
#define HD64461_GPBDR_LCDOFF 0x01
|
||||
#define HD64461_GPBDR_LCD_CONTRAST_MASK 0x78
|
||||
#define HD64461_GPBDR_LED_RED 0x80
|
||||
|
||||
#include <asm/hd64461.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define PJDR 0xa4000130
|
||||
#define PKDR 0xa4000132
|
||||
|
||||
static inline void hp6xx_led_red(int on)
|
||||
{
|
||||
u16 v16;
|
||||
v16 = ctrl_inw(CONFIG_HD64461_IOBASE + HD64461_GPBDR - 0x10000);
|
||||
if (on)
|
||||
ctrl_outw(v16 & (~HD64461_GPBDR_LED_RED), CONFIG_HD64461_IOBASE + HD64461_GPBDR - 0x10000);
|
||||
else
|
||||
ctrl_outw(v16 | HD64461_GPBDR_LED_RED, CONFIG_HD64461_IOBASE + HD64461_GPBDR - 0x10000);
|
||||
}
|
||||
|
||||
static inline void hp6xx_led_green(int on)
|
||||
{
|
||||
u8 v8;
|
||||
|
||||
v8 = ctrl_inb(PKDR);
|
||||
if (on)
|
||||
ctrl_outb(v8 & (~PKDR_LED_GREEN), PKDR);
|
||||
else
|
||||
ctrl_outb(v8 | PKDR_LED_GREEN, PKDR);
|
||||
}
|
||||
|
||||
|
||||
#endif /* __ASM_SH_HP6XX_H */
|
||||
|
17
include/asm-sh/pm.h
Normal file
17
include/asm-sh/pm.h
Normal file
@ -0,0 +1,17 @@
|
||||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
*
|
||||
* Copyright 2006 (c) Andriy Skulysh <askulysh@gmail.com>
|
||||
*
|
||||
*/
|
||||
#ifndef __ASM_SH_PM_H
|
||||
#define __ASM_SH_PM_H
|
||||
|
||||
extern u8 wakeup_start;
|
||||
extern u8 wakeup_end;
|
||||
|
||||
void pm_enter(void);
|
||||
|
||||
#endif
|
@ -172,6 +172,31 @@ static __inline__ void local_irq_disable(void)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
static __inline__ void set_bl_bit(void)
|
||||
{
|
||||
unsigned long __dummy0, __dummy1;
|
||||
|
||||
__asm__ __volatile__ ("stc sr, %0\n\t"
|
||||
"or %2, %0\n\t"
|
||||
"and %3, %0\n\t"
|
||||
"ldc %0, sr"
|
||||
: "=&r" (__dummy0), "=r" (__dummy1)
|
||||
: "r" (0x10000000), "r" (0xffffff0f)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
static __inline__ void clear_bl_bit(void)
|
||||
{
|
||||
unsigned long __dummy0, __dummy1;
|
||||
|
||||
__asm__ __volatile__ ("stc sr, %0\n\t"
|
||||
"and %2, %0\n\t"
|
||||
"ldc %0, sr"
|
||||
: "=&r" (__dummy0), "=r" (__dummy1)
|
||||
: "1" (~0x10000000)
|
||||
: "memory");
|
||||
}
|
||||
|
||||
#define local_save_flags(x) \
|
||||
__asm__("stc sr, %0; and #0xf0, %0" : "=&z" (x) :/**/: "memory" )
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
struct sys_timer_ops {
|
||||
int (*init)(void);
|
||||
int (*start)(void);
|
||||
int (*stop)(void);
|
||||
unsigned long (*get_offset)(void);
|
||||
unsigned long (*get_frequency)(void);
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user