OLPC PM - the early years
Jordan Crouse
jordan.crouse at amd.com
Wed Feb 28 19:32:15 EST 2007
Now that Mitch has his fast path resume working, it is time for the kernel
guys to earn their keep too. OFW now handles the actual suspend and
resume of the core - we call into the subroutine at 0xf0000, the system
suspends, and upon resume, control comes back to us. OFW even nicely
restores us back to protected mode, which simplifies things greatly.
So, replacing what we had previously, we just set up the SCI wakeup bits
(and soon, GPE0 bits, right EC hackers?), save our current processor state,
call into the routine, and the do it all backwards.
I also added suspend/resume functionality for the MFGPT timers, so that
the MFGPT clock event gets correctly restored for NOHZ mode. The patch is
attached - I'll push it into unstable if nobody cries too hard.
Next up, KFB suspend / resume support and I2C suspend/resume.
Jordan
--
Jordan Crouse
Senior Linux Engineer
Advanced Micro Devices, Inc.
<www.amd.com/embeddedprocessors>
-------------- next part --------------
diff --git a/arch/i386/kernel/geode.c b/arch/i386/kernel/geode.c
index 9ee54b6..2e6ec95 100644
--- a/arch/i386/kernel/geode.c
+++ b/arch/i386/kernel/geode.c
@@ -1,5 +1,5 @@
/* AMD Geode southbridge support code
- * Copyright (C) 2006, Advanced Micro Devices, Inc.
+ * Copyright (C) 2006-2007, Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -14,6 +14,8 @@ #include <asm/io.h>
#include <asm/msr.h>
#include <asm/geode.h>
+#define MFGPT_NAME "geode-mfgpt"
+
static struct {
char *name;
u32 msr;
@@ -23,11 +25,10 @@ static struct {
{ "geode-pms", MSR_LBAR_PMS, LBAR_PMS_SIZE, 0 },
{ "geode-acpi", MSR_LBAR_ACPI, LBAR_ACPI_SIZE, 0 },
{ "geode-gpio", MSR_LBAR_GPIO, LBAR_GPIO_SIZE, 0 },
- { "geode-mfgpt", MSR_LBAR_MFGPT, LBAR_MFGPT_SIZE, 0 }
+ { MFGPT_NAME, MSR_LBAR_MFGPT, LBAR_MFGPT_SIZE, 0 }
};
-int
-geode_get_dev_base(unsigned int dev) {
+int geode_get_dev_base(unsigned int dev) {
if (dev < ARRAY_SIZE(lbars))
return lbars[dev].base;
return 0;
@@ -37,8 +38,7 @@ EXPORT_SYMBOL_GPL(geode_get_dev_base);
/* === GPIO API === */
-void
-geode_gpio_set(unsigned int gpio, unsigned int reg)
+void geode_gpio_set(unsigned int gpio, unsigned int reg)
{
u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
@@ -53,8 +53,7 @@ geode_gpio_set(unsigned int gpio, unsign
EXPORT_SYMBOL_GPL(geode_gpio_set);
-void
-geode_gpio_clear(unsigned int gpio, unsigned int reg)
+void geode_gpio_clear(unsigned int gpio, unsigned int reg)
{
u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
@@ -69,8 +68,7 @@ geode_gpio_clear(unsigned int gpio, unsi
EXPORT_SYMBOL_GPL(geode_gpio_clear);
-int
-geode_gpio_isset(unsigned int gpio, unsigned int reg)
+int geode_gpio_isset(unsigned int gpio, unsigned int reg)
{
u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
@@ -85,8 +83,7 @@ geode_gpio_isset(unsigned int gpio, unsi
EXPORT_SYMBOL_GPL(geode_gpio_isset);
-void
-geode_gpio_set_pme(unsigned int gpio, int pair) {
+void geode_gpio_set_pme(unsigned int gpio, int pair) {
u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
u32 offset, shift, val;
@@ -119,10 +116,29 @@ static struct mfgpt_timer_t {
int index;
int flags;
struct module *owner;
+ void (*suspend)(int);
+ void (*resume)(int);
} mfgpt_timers[MFGPT_MAX_TIMERS];
-void
-geode_mfgpt_toggle_event(int timer, int cmp, int event, int setup)
+static int geode_mfgpt_suspend(struct sys_device *dev, pm_message_t state)
+{
+ int i;
+ for(i = 0; i < MFGPT_MAX_TIMERS; i++)
+ if (mfgpt_timers[i].owner && mfgpt_timers[i].suspend)
+ mfgpt_timers[i].suspend(i);
+ return 0;
+}
+
+static int geode_mfgpt_resume(struct sys_device *dev)
+{
+ int i;
+ for(i = 0; i < MFGPT_MAX_TIMERS; i++)
+ if (mfgpt_timers[i].owner && mfgpt_timers[i].resume)
+ mfgpt_timers[i].resume(i);
+ return 0;
+}
+
+void geode_mfgpt_toggle_event(int timer, int cmp, int event, int setup)
{
u32 msr, mask, value, dummy;
int shift = (cmp == MFGPT_CMP1) ? 0 : 8;
@@ -159,8 +175,7 @@ geode_mfgpt_toggle_event(int timer, int
EXPORT_SYMBOL(geode_mfgpt_toggle_event);
-void
-geode_mfgpt_set_irq(int timer, int cmp, int irq, int setup)
+void geode_mfgpt_set_irq(int timer, int cmp, int irq, int setup)
{
u32 val, dummy;
int offset;
@@ -181,26 +196,33 @@ geode_mfgpt_set_irq(int timer, int cmp,
wrmsr(0x51400022, val, dummy);
}
-int
-geode_mfgpt_alloc_timer(int timer, int domain, struct module *owner)
+static inline void geode_mfgpt_init_timer(int timer,
+ struct geode_mfgpt_timer_t *req)
+{
+ mfgpt_timers[timer].flags &= ~F_AVAIL;
+ mfgpt_timers[timer].owner = req->owner;
+ mfgpt_timers[timer].suspend = req->suspend;
+ mfgpt_timers[timer].resume = req->resume;
+}
+
+int geode_mfgpt_alloc_timer(struct geode_mfgpt_timer_t *req)
{
int i;
u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
- /* If they requested a specific timer, try to honor that */
if (!base)
return -ENODEV;
- if (timer != MFGPT_TIMER_ANY) {
- if (mfgpt_timers[timer].flags & F_AVAIL) {
- mfgpt_timers[timer].flags &= ~F_AVAIL;
- mfgpt_timers[timer].owner = owner;
+ /* If they requested a specific timer, try to honor that */
- printk("geode-mfgpt: Registered timer %d\n", timer);
- return timer;
+ if (req->timer != MFGPT_TIMER_ANY) {
+ if (mfgpt_timers[req->timer].flags & F_AVAIL) {
+ geode_mfgpt_init_timer(req->timer, req);
+ printk(KERN_INFO MFGPT_NAME ": Registered timer %d\n", req->timer);
+ return req->timer;
}
- else if (mfgpt_timers[timer].owner == owner)
- return timer;
+ else if (mfgpt_timers[req->timer].owner == req->owner)
+ return req->timer;
/* Request failed - somebody else owns it */
return -1;
@@ -212,14 +234,12 @@ geode_mfgpt_alloc_timer(int timer, int d
if ((mfgpt_timers[i].flags & F_AVAIL) &&
!(mfgpt_timers[i].flags & F_RESERVED)) {
- mfgpt_timers[i].flags &= ~F_AVAIL;
- mfgpt_timers[i].owner = owner;
-
- printk("geode-mfgpt: Registered timer %d\n", i);
+ geode_mfgpt_init_timer(i, req);
+ printk(KERN_INFO MFGPT_NAME ": Registered timer %d\n", i);
return i;
}
- if (i == 5 && domain == MFGPT_DOMAIN_WORKING)
+ if (i == 5 && req->domain == MFGPT_DOMAIN_WORKING)
break;
}
@@ -229,8 +249,7 @@ geode_mfgpt_alloc_timer(int timer, int d
EXPORT_SYMBOL(geode_mfgpt_alloc_timer);
-static int
-mfgpt_setup_timer(int timer)
+static int mfgpt_setup_timer(int timer)
{
u16 val = geode_mfgpt_read(timer, MFGPT_REG_SETUP);
mfgpt_timers[timer].index = timer;
@@ -246,10 +265,22 @@ mfgpt_setup_timer(int timer)
return 0;
}
+static struct sysdev_class mfgpt_class = {
+ set_kset_name(MFGPT_NAME),
+ .resume = geode_mfgpt_resume,
+ .suspend = geode_mfgpt_suspend
+};
+
+static struct sys_device device_mfgpt = {
+ .id = 0,
+ .cls = &mfgpt_class
+};
+
static void __init
geode_mfgpt_init(void)
{
int count = 0, i;
+ int ret;
#ifdef CONFIG_OLPC
/* On the OLPC, we want to be able to use all the timers. VSA
@@ -261,8 +292,8 @@ #ifdef CONFIG_OLPC
*/
{
- u64 val = 0xFF;
- if (geode_mfgpt_read(1, MFGPT_REG_SETUP) & (1 << 12))
+ u64 val = 0xFF;
+ if (geode_mfgpt_read(1, MFGPT_REG_SETUP) & (1 << 12))
wrmsrl(0x5140002B, val);
}
#endif
@@ -270,11 +301,20 @@ #endif
for(i = 0; i < MFGPT_MAX_TIMERS; i++)
count += mfgpt_setup_timer(i);
- printk(KERN_INFO "geode-mfgpt: %d timers available.\n", count);
+ if (count > 0) {
+ ret = sysdev_class_register(&mfgpt_class);
+ if (!ret)
+ ret = sysdev_register(&device_mfgpt);
+
+ if (ret)
+ printk(KERN_ERR MFGPT_NAME ": Unable to register the system device (%d)\n", ret);
+
+ printk(KERN_INFO MFGPT_NAME ": %d timers available.\n", count);
+ } else
+ printk(KERN_ERR MFGPT_NAME ": No timers are available. Your BIOS is broken.\n");
}
-static int __init
-geode_southbridge_init(void) {
+static int __init geode_southbridge_init(void) {
u32 lo, hi;
int i;
@@ -284,7 +324,7 @@ geode_southbridge_init(void) {
for(i = 0; i < ARRAY_SIZE(lbars); i++) {
rdmsr(lbars[i].msr, lo, hi);
- if (hi & 0x01)
+ if (hi & 0x01)
lbars[i].base = lo & 0x0000ffff;
if (lbars[i].base == 0)
diff --git a/arch/i386/kernel/olpc-pm.c b/arch/i386/kernel/olpc-pm.c
index 4655b13..f0e4d97 100644
--- a/arch/i386/kernel/olpc-pm.c
+++ b/arch/i386/kernel/olpc-pm.c
@@ -125,63 +125,18 @@ static int olpc_pm_enter (suspend_state_
return 0;
}
-/* Put the memory into self refresh and go to sleep */
-
-static inline void olpc_sleep_asm(void)
-{
- __asm__ __volatile__( "add $0x08, %%bx\n\t"
- "movw %%bx, %%dx\n\t"
- "inl %%dx, %%eax\n\t"
- "or $0x2000, %%ax\n\t"
- "movw %%ax, %%di\n\t"
- "wbinvd\n\t"
- "movl $0x20000018, %%ecx\n\t"
- "rdmsr\n\t"
- "and $0xFF0000FF, %%eax\n\t"
- "wrmsr\n\t"
- "movw $0x2004, %%cx\n\t"
- "xor %%edx, %%edx\n\t"
- "xor %%eax, %%eax\n\t"
- "movb $0x04, %%al\n\t"
- "wrmsr\n\t"
- "movw %%bx, %%dx\n\t"
- "movzx %%di, %%eax\n\t"
- "outl %%eax, %%dx\n\t"
- : : "b" (acpi_base));
-}
-
-#define PM_SCLK_VAL 0x40000e00 /* [29:0] is the delay */
-#define PM_SED_VAL 0x40004601 /* [29:0] is the delay */
-#define PM_WKXD_VAL 0x040000a0 /* [19:0] is the delay */
+/* This function handles any last minute configuration that we want
+ to do before we go to sleep. Mainly this will involve setting
+ wake events. This is called from do_olpc_suspend_lowlevel */
int asmlinkage
-olpc_enter_sleep_state(u8 sleep_state)
+olpc_setup_sleep_state(u8 sleep_state)
{
- /* Set up the PMC sleep clock */
- outl(PM_SCLK_VAL, pms_base + PM_SCLK);
-
- /* Set up the Sleep End Delay */
- outl(PM_SED_VAL, pms_base + PM_SED);
-
- /* Set up the WORK_AUX delay */
- outl(PM_WKXD_VAL, pms_base + PM_WKXD);
-
- /* Clear PM_SSC */
- outl(0x2FFFF, pms_base + PM_SSC);
-
- /* Save ourselves a read by setting the SCI events again here -
- * we have to write the register anyway */
-
- /* FIXME: Set any other SCI events that we might want here */
+ /* FIXME: Set the SCI bits we want to wake up on here */
outl((CS5536_PM_PWRBTN << 16) | 0xFFFF, acpi_base + PM1_STS);
- /* FIXME: Set any GPE events that we want here */
- /* FIXME: Clear pending GPE events if we end up using any */
-
- /* Actually go to sleep */
- olpc_sleep_asm();
-
+ /* FIXME: Set the GPE0 bits we want to wake on here */
return 0;
}
@@ -245,17 +200,6 @@ #ifdef ENABLE_EC_SCI
#endif
}
-void __init
-olpc_reserve_bootmem(void)
-{
- olpc_wakeup_address = (unsigned long)alloc_bootmem_low(PAGE_SIZE);
-
- if (!olpc_wakeup_address) {
- printk(KERN_ERR "olpc-pm: Could not allocate lowmem - suspend is disabled.\n");
- return;
- }
-}
-
static int __init olpc_pm_init(void)
{
uint32_t lo, hi;
diff --git a/arch/i386/kernel/olpc-wakeup.S b/arch/i386/kernel/olpc-wakeup.S
index 21ef6b2..c344a7f 100644
--- a/arch/i386/kernel/olpc-wakeup.S
+++ b/arch/i386/kernel/olpc-wakeup.S
@@ -3,226 +3,49 @@ #include <linux/linkage.h>
#include <asm/segment.h>
#include <asm/page.h>
-#
-# wakeup_code runs in real mode, and at unknown address (determined at run-time).
-# Therefore it must only use relative jumps/calls.
-#
-# Do we need to deal with A20? It is okay: ACPI specs says A20 must be enabled
-#
-# If physical address of wakeup_code is 0x12345, BIOS should call us with
-# cs = 0x1234, eip = 0x05
-#
-
-ALIGN
- .align 4096
-ENTRY(wakeup_start)
-wakeup_code:
- wakeup_code_start = .
- .code16
-
- movw $0xb800, %ax
- movw %ax,%fs
-
- cli
- cld
-
- # setup data segment
- movw %cs, %ax
- movw %ax, %ds # Make ds:0 point to wakeup_start
- movw %ax, %ss
- mov $(wakeup_stack - wakeup_code), %sp # Private stack is needed for ASUS board
-
- pushl $0 # Kill any dangerous flags
- popfl
-
- movl real_magic - wakeup_code, %eax
- cmpl $0x12345678, %eax
- jne bogus_real_magic
-
- # set up page table
- movl $swsusp_pg_dir-__PAGE_OFFSET, %eax
- movl %eax, %cr3
-
- testl $1, real_efer_save_restore - wakeup_code
- jz 4f
- # restore efer setting
- movl real_save_efer_edx - wakeup_code, %edx
- movl real_save_efer_eax - wakeup_code, %eax
- mov $0xc0000080, %ecx
- wrmsr
-4:
- # make sure %cr4 is set correctly (features, etc)
- movl real_save_cr4 - wakeup_code, %eax
- movl %eax, %cr4
- movw $0xb800, %ax
- movw %ax,%fs
-
- # need a gdt -- use lgdtl to force 32-bit operands, in case
- # the GDT is located past 16 megabytes.
- lgdtl real_save_gdt - wakeup_code
-
- movl real_save_cr0 - wakeup_code, %eax
- movl %eax, %cr0
- jmp 1f
-1:
-
- movl real_magic - wakeup_code, %eax
- cmpl $0x12345678, %eax
- jne bogus_real_magic
-
- ljmpl $__KERNEL_CS,$wakeup_pmode_return
-
-real_save_gdt: .word 0
- .long 0
-real_save_cr0: .long 0
-real_save_cr3: .long 0
-real_save_cr4: .long 0
-real_magic: .long 0
-real_efer_save_restore: .long 0
-real_save_efer_edx: .long 0
-real_save_efer_eax: .long 0
-
-bogus_real_magic:
- jmp bogus_real_magic
-
-setbad: clc
- ret
-
-_setbad: jmp setbad
-
- .code32
- ALIGN
-
-.org 0x800
-wakeup_stack_begin: # Stack grows down
-
-.org 0xff0 # Just below end of page
-wakeup_stack:
-ENTRY(wakeup_end)
-
-.org 0x1000
-
-wakeup_pmode_return:
- movw $__KERNEL_DS, %ax
- movw %ax, %ss
- movw %ax, %ds
- movw %ax, %es
- movw %ax, %fs
- movw %ax, %gs
-
- # reload the gdt, as we need the full 32 bit address
- lgdt saved_gdt
- lidt saved_idt
- lldt saved_ldt
- ljmp $(__KERNEL_CS),$1f
-1:
- movl %cr3, %eax
- movl %eax, %cr3
- wbinvd
-
- # and restore the stack ... but you need gdt for this to work
- movl saved_context_esp, %esp
-
- movl %cs:saved_magic, %eax
- cmpl $0x12345678, %eax
- jne bogus_magic
-
- # jump to place where we left off
- movl saved_eip,%eax
- jmp *%eax
-
-bogus_magic:
- jmp bogus_magic
-
-
-##
-# olpc_copy_wakeup_routine
-#
-# Copy the above routine to low memory.
-#
-# Parameters:
-# %eax: place to copy wakeup routine to
-#
-# Returned address is location of code in low memory (past data and stack)
-#
-ENTRY(olpc_copy_wakeup_routine)
-
- sgdt saved_gdt
- sidt saved_idt
- sldt saved_ldt
- str saved_tss
-
- movl nx_enabled, %edx
- movl %edx, real_efer_save_restore - wakeup_start (%eax)
- testl $1, real_efer_save_restore - wakeup_start (%eax)
- jz 2f
- # save efer setting
- pushl %eax
- movl %eax, %ebx
- mov $0xc0000080, %ecx
- rdmsr
- movl %edx, real_save_efer_edx - wakeup_start (%ebx)
- movl %eax, real_save_efer_eax - wakeup_start (%ebx)
- popl %eax
-2:
-
- movl %cr3, %edx
- movl %edx, real_save_cr3 - wakeup_start (%eax)
- movl %cr4, %edx
- movl %edx, real_save_cr4 - wakeup_start (%eax)
- movl %cr0, %edx
- movl %edx, real_save_cr0 - wakeup_start (%eax)
- sgdt real_save_gdt - wakeup_start (%eax)
-
- movl $0x12345678, real_magic - wakeup_start (%eax)
- movl $0x12345678, saved_magic
- ret
+# OFW will remember most of the protected mode settings - we just need
+# to worry about saving the current state, and then pass control to
+# OFW. The very last C setup we do will be done by olpc_setup_sleep_state()
+# which will configure the wakeup events
save_registers:
- leal 4(%esp), %eax
- movl %eax, saved_context_esp
- movl %ebx, saved_context_ebx
- movl %ebp, saved_context_ebp
- movl %esi, saved_context_esi
- movl %edi, saved_context_edi
- pushfl ; popl saved_context_eflags
-
- movl $ret_point, saved_eip
- ret
+ leal (%esp), %eax
+ movl %eax, saved_context_esp
+ movl %ebx, saved_context_ebx
+ movl %ebp, saved_context_ebp
+ movl %esi, saved_context_esi
+ movl %edi, saved_context_edi
+ pushfl ; popl saved_context_eflags
+ ret
restore_registers:
- movl saved_context_ebp, %ebp
- movl saved_context_ebx, %ebx
- movl saved_context_esi, %esi
- movl saved_context_edi, %edi
- pushl saved_context_eflags ; popfl
- ret
+ movl saved_context_ebp, %ebp
+ movl saved_context_ebx, %ebx
+ movl saved_context_esi, %esi
+ movl saved_context_edi, %edi
+ pushl saved_context_eflags ; popfl
+ ret
+
ENTRY(do_olpc_suspend_lowlevel)
call save_processor_state
call save_registers
pushl $3
- call olpc_enter_sleep_state
+ call olpc_setup_sleep_state
addl $4, %esp
-# In case of S3 failure, we'll emerge here. Jump
-# to ret_point to recover
+# We're ready to sleep - go, go, gadget sleep
+ mov $0xf0000,%eax
+ lcall *(%eax)
+
+# We are back - restore the stack
+ movl saved_context_esp, %esp
+
+# Jump to the return point and restore our state
jmp ret_point
.p2align 4,,7
ret_point:
call restore_registers
call restore_processor_state
ret
-
-.data
-ALIGN
-ENTRY(saved_magic) .long 0
-ENTRY(saved_eip) .long 0
-
-# saved registers
-saved_gdt: .long 0,0
-saved_idt: .long 0,0
-saved_ldt: .long 0
-saved_tss: .long 0
-
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index 0b476e1..122623d 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -437,11 +437,6 @@ #ifdef CONFIG_ACPI_SLEEP
*/
acpi_reserve_bootmem();
#endif
-#ifdef CONFIG_OLPC_PM
- /* Reserve low memory for OLPC resume
- */
- olpc_reserve_bootmem();
-#endif
#ifdef CONFIG_X86_FIND_SMP_CONFIG
/*
* Find and reserve possible boot-time SMP configuration:
diff --git a/drivers/clocksource/mfgpt.c b/drivers/clocksource/mfgpt.c
index 67f3e33..03842a7 100644
--- a/drivers/clocksource/mfgpt.c
+++ b/drivers/clocksource/mfgpt.c
@@ -1,6 +1,6 @@
/* Driver/API for AMD Geode Multi-Function General Purpose Timers (MFGPT)
*
- * Copyright (C) 2006, Advanced Micro Devices, Inc.
+ * Copyright (C) 2006-2007, Advanced Micro Devices, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -125,6 +125,22 @@ static irqreturn_t mfgpt_tick(int irq, v
return IRQ_HANDLED;
}
+static void mfgpt_timer_do_setup(void)
+{
+ geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP,
+ MFGPT_SCALE | MFGPT_SETUP_CMP2_EVENT);
+
+ geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq);
+}
+
+static void mfgpt_timer_resume(int timer)
+{
+ if (timer != mfgpt_event_clock)
+ return;
+
+ mfgpt_timer_do_setup();
+}
+
static struct irqaction mfgptirq = {
.handler = mfgpt_tick,
.flags = IRQF_DISABLED | IRQF_NOBALANCING,
@@ -132,14 +148,21 @@ static struct irqaction mfgptirq = {
.name = "mfgpt-timer"
};
-static int __init
-mfgpt_timer_setup(void)
+/* Note that we don't have a suspend hook - the setup value is known, so we don't
+ really need to save it off.
+*/
+
+struct geode_mfgpt_timer_t tick_timer = {
+ .timer = MFGPT_TIMER_ANY,
+ .domain = MFGPT_DOMAIN_WORKING,
+ .owner = THIS_MODULE,
+ .resume = mfgpt_timer_resume
+};
+
+static int __init mfgpt_timer_setup(void)
{
int ret;
- u16 val;
-
- int timer = geode_mfgpt_alloc_timer(MFGPT_TIMER_ANY,
- MFGPT_DOMAIN_WORKING, THIS_MODULE);
+ int timer = geode_mfgpt_alloc_timer(&tick_timer);
if (timer < 0) {
printk(KERN_ERR "mfgpt-timer: Could not allocate a MFPGT timer\n");
@@ -147,13 +170,8 @@ mfgpt_timer_setup(void)
}
mfgpt_event_clock = timer;
- /* Set the clock scale and enable the event mode for CMP2 */
- val = MFGPT_SCALE | (3 << 8);
-
- geode_mfgpt_write(mfgpt_event_clock, MFGPT_REG_SETUP, val);
- /* Set up the IRQ on the MFGPT side */
- geode_mfgpt_setup_irq(mfgpt_event_clock, MFGPT_CMP2, irq);
+ mfgpt_timer_do_setup();
/* And register it with the kernel */
ret = setup_irq(irq, &mfgptirq);
diff --git a/include/asm-i386/geode.h b/include/asm-i386/geode.h
index f9f29ec..50ec31f 100644
--- a/include/asm-i386/geode.h
+++ b/include/asm-i386/geode.h
@@ -71,7 +71,7 @@ #define PM_SCXA 0x04
#define PM_SCYA 0x08
#define PM_OUT_SLPCTL 0x0C
#define PM_SCLK 0x10
-#define PM_SED 0x1
+#define PM_SED 0x14
#define PM_SCXD 0x18
#define PM_SCYD 0x1C
#define PM_IN_SLPCTL 0x20
@@ -135,6 +135,7 @@ #define MFGPT_EVENT_NMI 1
#define MFGPT_EVENT_RESET 3
#define MFGPT_REG_CMP1 0
+
#define MFGPT_REG_CMP2 2
#define MFGPT_REG_COUNTER 4
#define MFGPT_REG_SETUP 6
@@ -145,9 +146,31 @@ #define MFGPT_SETUP_CMP1 (1 << 13)
#define MFGPT_SETUP_SETUP (1 << 12)
#define MFGPT_SETUP_STOPEN (1 << 11)
#define MFGPT_SETUP_EXTEN (1 << 10)
+
+#define MFGPT_SETUP_CMP2_DISABLE 0
+#define MFGPT_SETUP_CMP2_EQUAL (1 << 8)
+#define MFGPT_SETUP_CMP2_GE (2 << 8)
+#define MFGPT_SETUP_CMP2_EVENT (3 << 8)
+
+#define MFGPT_SETUP_CMP1_DISABLE 0
+#define MFGPT_SETUP_CMP1_EQUAL (1 << 6)
+#define MFGPT_SETUP_CMP1_GE (2 << 6)
+#define MFGPT_SETUP_CMP1_EVENT (3 << 6)
+
+
#define MFGPT_SETUP_REVEN (1 << 5)
#define MFGPT_SETUP_CLKSEL (1 << 4)
+
+
+struct geode_mfgpt_timer_t {
+ int timer;
+ int domain;
+ struct module *owner;
+ void (*suspend)(int);
+ void (*resume)(int);
+};
+
static inline void
geode_mfgpt_write(int i, u16 r, u16 v)
{
@@ -165,6 +188,10 @@ geode_mfgpt_read(int i, u16 r)
#define geode_mfgpt_setup_irq(t,c,i) geode_mfgpt_set_irq((t),(c),(i),1)
#define geode_mfgpt_release_irq(t,c,i) geode_mfgpt_set_irq((t),(c),(i),0)
+int geode_mfgpt_alloc_timer(struct geode_mfgpt_timer_t *);
+
+/* Generic Geode macros */
+
static inline int
is_geode_gx(void) {
return ((boot_cpu_data.x86_vendor == X86_VENDOR_NSC) &&
More information about the Devel
mailing list