aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/boards
diff options
context:
space:
mode:
authorAndriy Skulysh <askulysh@gmail.com>2006-09-27 03:20:22 -0400
committerPaul Mundt <lethal@linux-sh.org>2006-09-27 03:20:22 -0400
commit3aa770e7972723f479122cf66b529017d2175289 (patch)
treef70d870381cec8034693704e346c53b311db688f /arch/sh/boards
parentef48e8e3498605351f91f195cc9af0ef981b0dde (diff)
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>
Diffstat (limited to 'arch/sh/boards')
-rw-r--r--arch/sh/boards/hp6xx/Makefile5
-rw-r--r--arch/sh/boards/hp6xx/hp6xx_apm.c123
-rw-r--r--arch/sh/boards/hp6xx/pm.c88
-rw-r--r--arch/sh/boards/hp6xx/pm_wakeup.S58
-rw-r--r--arch/sh/boards/hp6xx/setup.c14
5 files changed, 287 insertions, 1 deletions
diff --git a/arch/sh/boards/hp6xx/Makefile b/arch/sh/boards/hp6xx/Makefile
index 927fe0aa5dfa..fdf98ffcf437 100644
--- a/arch/sh/boards/hp6xx/Makefile
+++ b/arch/sh/boards/hp6xx/Makefile
@@ -2,5 +2,8 @@
2# Makefile for the HP6xx specific parts of the kernel 2# Makefile for the HP6xx specific parts of the kernel
3# 3#
4 4
5obj-y := mach.o setup.o 5obj-y := mach.o setup.o
6obj-$(CONFIG_PM) += pm.o pm_wakeup.o
7obj-$(CONFIG_APM) += hp6xx_apm.o
8
6 9
diff --git a/arch/sh/boards/hp6xx/hp6xx_apm.c b/arch/sh/boards/hp6xx/hp6xx_apm.c
new file mode 100644
index 000000000000..ad0e712c29f6
--- /dev/null
+++ b/arch/sh/boards/hp6xx/hp6xx_apm.c
@@ -0,0 +1,123 @@
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
31static 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
87static 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
95static 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
112static void __exit hp6x0_apm_exit(void)
113{
114 free_irq(HP680_BTN_IRQ, 0);
115 apm_get_info = 0;
116}
117
118module_init(hp6x0_apm_init);
119module_exit(hp6x0_apm_exit);
120
121MODULE_AUTHOR("Adriy Skulysh");
122MODULE_DESCRIPTION("hp6xx Advanced Power Management");
123MODULE_LICENSE("GPL");
diff --git a/arch/sh/boards/hp6xx/pm.c b/arch/sh/boards/hp6xx/pm.c
new file mode 100644
index 000000000000..0e501bcbd7a9
--- /dev/null
+++ b/arch/sh/boards/hp6xx/pm.c
@@ -0,0 +1,88 @@
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
23static 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 */
77static struct pm_ops hp6x0_pm_ops = {
78 .pm_disk_mode = PM_DISK_FIRMWARE,
79 .enter = hp6x0_pm_enter,
80};
81
82static int __init hp6x0_pm_init(void)
83{
84 pm_set_ops(&hp6x0_pm_ops);
85 return 0;
86}
87
88late_initcall(hp6x0_pm_init);
diff --git a/arch/sh/boards/hp6xx/pm_wakeup.S b/arch/sh/boards/hp6xx/pm_wakeup.S
new file mode 100644
index 000000000000..45e9bf0b9115
--- /dev/null
+++ b/arch/sh/boards/hp6xx/pm_wakeup.S
@@ -0,0 +1,58 @@
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
31ENTRY(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
501: .long EXPEVT
512: .long INTEVT
523: .long ret_from_irq
534: .long handle_exception
545: .long 0xffffff68
556: .word 0x0524
56
57ENTRY(wakeup_end)
58 nop
diff --git a/arch/sh/boards/hp6xx/setup.c b/arch/sh/boards/hp6xx/setup.c
index 71f315663cc9..5fc00f0727c4 100644
--- a/arch/sh/boards/hp6xx/setup.c
+++ b/arch/sh/boards/hp6xx/setup.c
@@ -15,6 +15,9 @@
15#include <asm/hp6xx/hp6xx.h> 15#include <asm/hp6xx/hp6xx.h>
16#include <asm/cpu/dac.h> 16#include <asm/cpu/dac.h>
17 17
18#define SCPCR 0xa4000116
19#define SCPDR 0xa4000136
20
18const char *get_system_type(void) 21const char *get_system_type(void)
19{ 22{
20 return "HP6xx"; 23 return "HP6xx";
@@ -24,6 +27,7 @@ int __init platform_setup(void)
24{ 27{
25 u8 v8; 28 u8 v8;
26 u16 v; 29 u16 v;
30
27 v = inw(HD64461_STBCR); 31 v = inw(HD64461_STBCR);
28 v |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST | 32 v |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST |
29 HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST | 33 HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST |
@@ -50,5 +54,15 @@ int __init platform_setup(void)
50 v8 &= ~DACR_DAE; 54 v8 &= ~DACR_DAE;
51 ctrl_outb(v8,DACR); 55 ctrl_outb(v8,DACR);
52 56
57 v8 = ctrl_inb(SCPDR);
58 v8 |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
59 v8 &= ~SCPDR_TS_SCAN_ENABLE;
60 ctrl_outb(v8, SCPDR);
61
62 v = ctrl_inw(SCPCR);
63 v &= ~SCPCR_TS_MASK;
64 v |= SCPCR_TS_ENABLE;
65 ctrl_outw(v, SCPCR);
66
53 return 0; 67 return 0;
54} 68}