Skip to content

Commit

Permalink
sh: APM/PM support.
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andriy Skulysh authored and Paul Mundt committed Sep 27, 2006
1 parent ef48e8e commit 3aa770e
Show file tree
Hide file tree
Showing 21 changed files with 1,124 additions and 22 deletions.
10 changes: 10 additions & 0 deletions arch/sh/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 4 additions & 1 deletion arch/sh/boards/hp6xx/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 123 additions & 0 deletions arch/sh/boards/hp6xx/hp6xx_apm.c
Original file line number Diff line number Diff line change
@@ -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 changes: 88 additions & 0 deletions arch/sh/boards/hp6xx/pm.c
Original file line number Diff line number Diff line change
@@ -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 changes: 58 additions & 0 deletions arch/sh/boards/hp6xx/pm_wakeup.S
Original file line number Diff line number Diff line change
@@ -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
14 changes: 14 additions & 0 deletions arch/sh/boards/hp6xx/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 |
Expand All @@ -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;
}
2 changes: 2 additions & 0 deletions arch/sh/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading

0 comments on commit 3aa770e

Please sign in to comment.