Commit 3aa770e7 authored by Andriy Skulysh's avatar Andriy Skulysh Committed by Paul Mundt

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: default avatarAndriy Skulysh <askulysh@gmail.com>
Signed-off-by: default avatarPaul Mundt <lethal@linux-sh.org>
parent ef48e8e3
......@@ -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
/*
* 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");
/*
* 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);
/*
* 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
This diff is collapsed.
......@@ -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.
!
......
/*
* 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;
......
/*
* 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 */
/*
* 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);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment