[PATCH 1/3] [PATCH] geode: Update OLPC power management
Jordan Crouse
jordan.crouse at amd.com
Fri Mar 2 16:36:37 EST 2007
From: Jordan Crouse <jordan.crouse at amd.com>
Update the OLPC power managment code to account for the new OFW handler.
Also, add support for restoring the MFGPT clock event timer.
Signed-off-by: Jordan Crouse <jordan.crouse at amd.com>
---
arch/i386/kernel/geode.c | 266 ++++++++++++++++++++++++++++-----------
arch/i386/kernel/olpc-pm.c | 98 +++-----------
arch/i386/kernel/olpc-wakeup.S | 233 ++++------------------------------
arch/i386/kernel/setup.c | 5 -
drivers/clocksource/mfgpt.c | 44 +++++-
drivers/i2c/busses/scx200_acb.c | 162 +++++++++++-------------
include/asm-i386/geode.h | 54 ++++++--
7 files changed, 388 insertions(+), 474 deletions(-)
diff --git a/arch/i386/kernel/geode.c b/arch/i386/kernel/geode.c
index 9ee54b6..8a64e0c 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
@@ -10,86 +10,105 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/ioport.h>
+#include <linux/pci.h>
#include <asm/io.h>
#include <asm/msr.h>
#include <asm/geode.h>
+#define MFGPT_NAME "geode-mfgpt"
+
static struct {
- char *name;
- u32 msr;
- int size;
- u32 base;
-} lbars[] = {
- { "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 }
+ const char *name;
+ void __iomem *base;
+ int (*suspend)(pm_message_t, void *);
+ int (*resume)(void *);
+ void *data;
+} geode_devices[] = {
+ { "geode-smb" },
+ { "geode-gpio" },
+ { MFGPT_NAME },
+ { "geode-interrupt" },
+ { "geode-pms" },
+ { "geode-acpi" }
};
-int
-geode_get_dev_base(unsigned int dev) {
- if (dev < ARRAY_SIZE(lbars))
- return lbars[dev].base;
- return 0;
+void __iomem * geode_get_dev_base(unsigned int dev) {
+ if (dev < ARRAY_SIZE(geode_devices))
+ return geode_devices[dev].base;
+
+ return NULL;
}
EXPORT_SYMBOL_GPL(geode_get_dev_base);
+void geode_set_pm_hooks(unsigned int dev,
+ int (*suspend)(pm_message_t, void *),
+ int (*resume)(void *),
+ void *data)
+{
+ if (dev < ARRAY_SIZE(geode_devices)) {
+ geode_devices[dev].suspend = suspend;
+ geode_devices[dev].resume = resume;
+ geode_devices[dev].data = data;
+ }
+}
+
+EXPORT_SYMBOL_GPL(geode_set_pm_hooks);
+
/* === 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);
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_GPIO);
- if (!base)
+ if (base == NULL)
return;
if (gpio < 16)
- outl(1 << gpio, base + reg);
+ iowrite32(1 << gpio, base + reg);
else
- outl(1 << (gpio - 16), base + 0x80 + reg);
+ iowrite32(1 << (gpio - 16), base + 0x80 + reg);
}
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);
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_GPIO);
- if (!base)
+ if (base == NULL)
return;
if (gpio < 16)
- outl(1 << (gpio + 16), base + reg);
+ iowrite32(1 << (gpio + 16), base + reg);
else
- outl(1 << gpio, base + 0x80 + reg);
+ iowrite32(1 << gpio, base + 0x80 + reg);
}
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);
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_GPIO);
- if (!base)
+ if (base == NULL)
return 0;
if (gpio < 16)
- return (inl(base + reg) & (1 << gpio)) ? 1 :0;
+ return (ioread32(base + reg) & (1 << gpio)) ? 1 :0;
else
- return (inl(base + 0x80 + reg) & (1 << (gpio - 16))) ? 1 : 0;
+ return (ioread32(base + 0x80 + reg) & (1 << (gpio - 16))) ? 1 : 0;
}
EXPORT_SYMBOL_GPL(geode_gpio_isset);
-void
-geode_gpio_set_pme(unsigned int gpio, int pair) {
- u32 base = geode_get_dev_base(GEODE_DEV_GPIO);
+void geode_gpio_set_pme(unsigned int gpio, int pair) {
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_GPIO);
u32 offset, shift, val;
+ if (base == NULL)
+ return;
+
if (gpio >= 24)
offset = GPIO_MAP_W;
else if (gpio >= 16)
@@ -101,9 +120,9 @@ geode_gpio_set_pme(unsigned int gpio, in
shift = (gpio % 8) * 4;
- val = inl(base + offset);
+ val = ioread32(base + offset);
val |= ((pair & 7) << shift) | (1 << (shift + 3));
- outl(val, base + offset);
+ iowrite32(val, base + offset);
}
/* === MFGPT API === */
@@ -119,10 +138,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(pm_message_t state, void *data)
+{
+ 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(void *data)
+{
+ 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 +197,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 +218,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);
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_MFGPT);
- /* If they requested a specific timer, try to honor that */
- if (!base)
+ if (base == NULL)
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 +256,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 +271,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;
@@ -261,8 +302,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,29 +311,100 @@ #endif
for(i = 0; i < MFGPT_MAX_TIMERS; i++)
count += mfgpt_setup_timer(i);
- printk(KERN_INFO "geode-mfgpt: %d timers available.\n", count);
+
+ geode_set_pm_hooks(GEODE_DEV_MFGPT, geode_mfgpt_suspend,
+ geode_mfgpt_resume, NULL);
+
+ if (count > 0)
+ 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) {
+#ifdef CONFIG_PM
- u32 lo, hi;
+static int geode_dev_suspend(struct pci_dev *pdev, pm_message_t state)
+{
int i;
- if (!is_geode())
- return -1;
+ if (pdev->dev.power.power_state.event == state.event)
+ return 0;
+
+ for(i = 0; i < ARRAY_SIZE(geode_devices); i++)
+ if (geode_devices[i].suspend)
+ geode_devices[i].suspend(state,
+ geode_devices[i].data);
+
+ pdev->dev.power.power_state = state;
+ return 0;
+}
+
+static int geode_dev_resume(struct pci_dev *pdev)
+{
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(geode_devices); i++)
+ if (geode_devices[i].resume)
+ geode_devices[i].resume(geode_devices[i].data);
+
+ pdev->dev.power.power_state = PMSG_ON;
+ return 0;
+}
+
+#endif
+
+static int __init geode_dev_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ int ret, i;
+
+ if ((ret = pci_enable_device(dev)) < 0)
+ return ret;
+
+ /* Allocate the bars for the device */
+
+ for(i = 0; i < ARRAY_SIZE(geode_devices); i++) {
+ ret = pci_request_region(dev, i, geode_devices[i].name);
+ if (ret < 0) {
+ printk(KERN_ERR "geode: Unable to request region for %s\n",
+ geode_devices[i].name);
+ continue;
+ }
- for(i = 0; i < ARRAY_SIZE(lbars); i++) {
- rdmsr(lbars[i].msr, lo, hi);
- if (hi & 0x01)
- lbars[i].base = lo & 0x0000ffff;
+ geode_devices[i].base = pci_iomap(dev, i, 0);
- if (lbars[i].base == 0)
- printk(KERN_ERR "geode: Couldn't initialize '%s'\n", lbars[i].name);
+ if (!geode_devices[i].base)
+ printk(KERN_ERR "geode: Unable to allocate memory for %s\n",
+ geode_devices[i].name);
}
geode_mfgpt_init();
return 0;
}
-subsys_initcall(geode_southbridge_init);
+static struct pci_device_id geode_dev_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) },
+};
+
+MODULE_DEVICE_TABLE(pci, geode_sb_id_table);
+
+static struct pci_driver geode_dev_driver = {
+ .name = "geode-southbridge",
+ .id_table = geode_dev_id_table,
+ .probe = geode_dev_probe,
+#ifdef CONFIG_PM
+ .suspend = geode_dev_suspend,
+ .resume = geode_dev_resume
+#endif
+};
+
+static int __init geode_southbridge_init(void) {
+
+ if (is_geode())
+ return pci_register_driver(&geode_dev_driver);
+ else
+ return -1;
+}
+
+fs_initcall(geode_southbridge_init);
diff --git a/arch/i386/kernel/olpc-pm.c b/arch/i386/kernel/olpc-pm.c
index 4655b13..a7f0c55 100644
--- a/arch/i386/kernel/olpc-pm.c
+++ b/arch/i386/kernel/olpc-pm.c
@@ -37,8 +37,8 @@ extern char wakeup_start, wakeup_end;
extern void do_olpc_suspend_lowlevel(void);
extern unsigned long FASTCALL(olpc_copy_wakeup_routine(unsigned long));
-static unsigned long acpi_base;
-static unsigned long pms_base;
+static void __iomem * acpi_base;
+static void __iomem * pms_base;
static int sci;
static int olpc_lid_flag;
@@ -50,12 +50,12 @@ olpc_pm_interrupt(int irq, void *id)
{
uint32_t sts, gpe = 0;
- sts = inl(acpi_base + PM1_STS);
- outl(sts | 0xFFFF, acpi_base + PM1_STS);
+ sts = ioread32(acpi_base + PM1_STS);
+ iowrite32(sts | 0xFFFF, acpi_base + PM1_STS);
if (olpc_get_rev() == OLPC_REV_B2) {
- gpe = inl(acpi_base + PM_GPE0_STS);
- outl(gpe | 0xFFFFFFFF, acpi_base + PM_GPE0_STS);
+ gpe = ioread32(acpi_base + PM_GPE0_STS);
+ iowrite32(gpe | 0xFFFFFFFF, acpi_base + PM_GPE0_STS);
}
if (sts & CS5536_PM_PWRBTN) {
@@ -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 the SCI bits we want to wake up on here */
- /* FIXME: Set any other SCI events that we might want 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();
+ iowrite32((CS5536_PM_PWRBTN << 16) | 0xFFFF, acpi_base + PM1_STS);
+ /* 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;
@@ -268,7 +212,7 @@ static int __init olpc_pm_init(void)
acpi_base = geode_acpi_base();
pms_base = geode_pms_base();
- if (!acpi_base || !pms_base)
+ if (acpi_base == NULL || pms_base == NULL)
return -ENODEV;
pm_inputdev = input_allocate_device();
@@ -278,10 +222,10 @@ static int __init olpc_pm_init(void)
olpc_fixup_bios();
- lo = inl(pms_base + PM_FSD);
+ lo = ioread32(pms_base + PM_FSD);
/* Lock, enable failsafe, 4 seconds */
- outl(0xc001f400, pms_base + PM_FSD);
+ iowrite32(0xc001f400, pms_base + PM_FSD);
rdmsr(0x51400020, lo, hi);
sci = (lo >> 20) & 15;
@@ -323,11 +267,11 @@ static int __init olpc_pm_init(void)
* sense, so set up the power button
*/
- outl(inl(acpi_base) | ((CS5536_PM_PWRBTN) << 16), acpi_base);
+ iowrite32(ioread32(acpi_base) | ((CS5536_PM_PWRBTN) << 16), acpi_base);
/* Set up the GPE events */
- val = inl(acpi_base + PM_GPE0_EN);
+ val = ioread32(acpi_base + PM_GPE0_EN);
if (olpc_get_rev() == OLPC_REV_B2) {
val |= (1 << 30);
@@ -350,7 +294,7 @@ #ifdef ENABLE_EC_SCI
val |= (1 << 31);
#endif
- outl(val, acpi_base + PM_GPE0_EN);
+ iowrite32(val, acpi_base + PM_GPE0_EN);
/* Select level triggered in PIC */
@@ -365,7 +309,7 @@ #endif
outb(lo, 0x4d1);
}
/* Clear pending interrupt */
- outl(inl(acpi_base) | 0xFFFF, acpi_base);
+ iowrite32(ioread32(acpi_base) | 0xFFFF, acpi_base);
pm_set_ops(&olpc_pm_ops);
@@ -375,7 +319,7 @@ #endif
static void olpc_pm_exit(void)
{
/* Clear any pending events, and disable them */
- outl(0xFFFF, acpi_base+2);
+ iowrite32(0xFFFF, acpi_base+2);
free_irq(sci, &acpi_base);
input_unregister_device(pm_inputdev);
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..4f2cecf 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/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index e0eb06b..3d0988a 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -35,6 +35,7 @@ #include <linux/mutex.h>
#include <asm/io.h>
#include <linux/scx200.h>
+#include <asm/geode.h>
#define NAME "scx200_acb"
@@ -73,6 +74,9 @@ static const char *scx200_acb_state_name
"write",
};
+#define IFACE_TYPE_ISA 0
+#define IFACE_TYPE_PCI 1
+
/* Physical interface */
struct scx200_acb_iface {
struct scx200_acb_iface *next;
@@ -89,9 +93,7 @@ struct scx200_acb_iface {
char needs_reset;
unsigned len;
- /* PCI device info */
- struct pci_dev *pdev;
- int bar;
+ int type;
};
/* Register Definitions */
@@ -488,41 +490,44 @@ static int __init scx200_acb_create(stru
return 0;
}
-static __init int scx200_create_pci(const char *text, struct pci_dev *pdev,
- int bar)
+/* We don't need a suspend function -- the state will be retored on resume */
+
+static int scx200_resume(void *data)
+{
+ struct scx200_acb_iface *iface = (struct scx200_acb_iface *) data;
+
+ if (iface)
+ scx200_acb_reset(iface);
+
+ return 0;
+}
+
+static __init int scx200_create_geode_dev(const char *text)
{
struct scx200_acb_iface *iface;
+ unsigned long base = (unsigned long) geode_smbus_base();
int rc;
- iface = scx200_create_iface(text, &pdev->dev, 0);
+ if (base == 0)
+ return -ENODEV;
+
+ iface = scx200_create_iface(text, NULL, 0);
if (iface == NULL)
return -ENOMEM;
- iface->pdev = pdev;
- iface->bar = bar;
-
- rc = pci_enable_device_bars(iface->pdev, 1 << iface->bar);
- if (rc)
- goto errout_free;
-
- rc = pci_request_region(iface->pdev, iface->bar, iface->adapter.name);
- if (rc) {
- printk(KERN_ERR NAME ": can't allocate PCI BAR %d\n",
- iface->bar);
- goto errout_free;
- }
+ iface->base = base;
+ iface->type = IFACE_TYPE_PCI;
- iface->base = pci_resource_start(iface->pdev, iface->bar);
rc = scx200_acb_create(iface);
+ /* Set up the power management hooks for the device */
+
if (rc == 0)
- return 0;
+ geode_set_pm_hooks(GEODE_DEV_SMB, NULL, scx200_resume, iface);
+ else
+ kfree(iface);
- pci_release_region(iface->pdev, iface->bar);
- pci_dev_put(iface->pdev);
- errout_free:
- kfree(iface);
return rc;
}
@@ -545,6 +550,8 @@ static int __init scx200_create_isa(cons
}
iface->base = base;
+ iface->type = IFACE_TYPE_ISA;
+
rc = scx200_acb_create(iface);
if (rc == 0)
@@ -556,84 +563,67 @@ static int __init scx200_create_isa(cons
return rc;
}
-/* Driver data is an index into the scx200_data array that indicates
- * the name and the BAR where the I/O address resource is located. ISA
- * devices are flagged with a bar value of -1 */
-
-static struct pci_device_id scx200_pci[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_BRIDGE),
- .driver_data = 0 },
- { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SC1100_BRIDGE),
- .driver_data = 0 },
- { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA),
- .driver_data = 1 },
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA),
- .driver_data = 2 }
-};
-
-static struct {
- const char *name;
- int bar;
-} scx200_data[] = {
- { "SCx200", -1 },
- { "CS5535", 0 },
- { "CS5536", 0 }
-};
-
-static __init int scx200_scan_pci(void)
+static __init int scx200_do_scan(void)
{
- int data, dev;
- int rc = -ENODEV;
struct pci_dev *pdev;
+ int ret;
- for(dev = 0; dev < ARRAY_SIZE(scx200_pci); dev++) {
- pdev = pci_get_device(scx200_pci[dev].vendor,
- scx200_pci[dev].device, NULL);
+ /* Search for a PCI device that will give us a clue as to which
+ SMBUS block we are dealing with. */
- if (pdev == NULL)
- continue;
+ /* First - search for the SC1100/SC1200 device */
- data = scx200_pci[dev].driver_data;
+ pdev = pci_get_device(PCI_VENDOR_ID_NS,
+ PCI_DEVICE_ID_NS_SCx200_BRIDGE, 0);
+ if (pdev == NULL)
+ pci_get_device(PCI_VENDOR_ID_NS,
+ PCI_DEVICE_ID_NS_SC1100_BRIDGE, 0);
- /* if .bar is greater or equal to zero, this is a
- * PCI device - otherwise, we assume
- that the ports are ISA based
- */
+ /* For SC1100/SC1200 devices - use the ISA ports */
- if (scx200_data[data].bar >= 0)
- rc = scx200_create_pci(scx200_data[data].name, pdev,
- scx200_data[data].bar);
- else {
- int i;
+ if (pdev != NULL) {
+ int i;
- for (i = 0; i < MAX_DEVICES; ++i) {
- if (base[i] == 0)
- continue;
+ for (i = 0; i < MAX_DEVICES; ++i) {
+ if (base[i] == 0)
+ continue;
- rc = scx200_create_isa(scx200_data[data].name,
- base[i],
- i);
- }
+ ret = scx200_create_isa("SCx200", base[i], i);
}
- break;
+ return ret;
}
- return rc;
+ /* Now - we're on the hunt for a 5535 or 5536. Since both chips
+ combine the SMBUS resources on the PCI header with 5 other
+ different devices, we can't just take over the PCI device.
+ Use the special Geode API to get the resources
+ */
+
+ pdev = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_CS5536_ISA, 0);
+ if (pdev == NULL)
+ pdev = pci_get_device(PCI_VENDOR_ID_NS,
+ PCI_DEVICE_ID_NS_CS5535_ISA, 0);
+
+ /* Found a 5535 / 5536 */
+
+ if (pdev != NULL)
+ return scx200_create_geode_dev("CS553X");
+
+ /* Nothing suitable was found */
+ return -ENODEV;
}
static int __init scx200_acb_init(void)
{
int rc;
- pr_debug(NAME ": NatSemi SCx200 ACCESS.bus Driver\n");
+ pr_debug(NAME ": Geode SCx200/CS553x ACCESS.bus driver\n");
- rc = scx200_scan_pci();
+ rc = scx200_do_scan();
- /* If at least one bus was created, init must succeed */
- if (scx200_acb_list)
- return 0;
- return rc;
+ return (scx200_acb_list) ? 0 : rc;
}
static void __exit scx200_acb_cleanup(void)
@@ -647,12 +637,10 @@ static void __exit scx200_acb_cleanup(vo
i2c_del_adapter(&iface->adapter);
- if (iface->pdev) {
- pci_release_region(iface->pdev, iface->bar);
- pci_dev_put(iface->pdev);
- }
- else
+ if (iface->type == IFACE_TYPE_ISA)
release_region(iface->base, 8);
+ else
+ geode_set_pm_hooks(GEODE_DEV_SMB, NULL, NULL, NULL);
kfree(iface);
down(&scx200_acb_list_mutex);
diff --git a/include/asm-i386/geode.h b/include/asm-i386/geode.h
index f9f29ec..2c5e53d 100644
--- a/include/asm-i386/geode.h
+++ b/include/asm-i386/geode.h
@@ -10,24 +10,31 @@
#ifndef _ASM_GEODE_H_
#define _ASM_GEODE_H_
+#include <linux/io.h>
#include <asm/processor.h>
#include <asm/io.h>
/* Generic southbridge functions */
-#define GEODE_DEV_PMS 0
-#define GEODE_DEV_ACPI 1
-#define GEODE_DEV_GPIO 2
-#define GEODE_DEV_MFGPT 3
+#define GEODE_DEV_SMB 0
+#define GEODE_DEV_GPIO 1
+#define GEODE_DEV_MFGPT 2
+#define GEODE_DEV_IRQ 3
+#define GEODE_DEV_PMS 4
+#define GEODE_DEV_ACPI 5
/* Useful macros */
-extern int geode_get_dev_base(unsigned int);
+void __iomem * geode_get_dev_base(unsigned int);
+void geode_set_pm_hooks(unsigned int,
+ int (*suspend)(pm_message_t, void *),
+ int (*resume)(void *), void *);
#define geode_pms_base() geode_get_dev_base(GEODE_DEV_PMS)
#define geode_acpi_base() geode_get_dev_base(GEODE_DEV_ACPI)
#define geode_gpio_base() geode_get_dev_base(GEODE_DEV_GPIO)
#define geode_mfgpt_base() geode_get_dev_base(GEODE_DEV_MFGPT)
+#define geode_smbus_base() geode_get_dev_base(GEODE_DEV_SMB)
/* MSRS */
@@ -71,7 +78,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 +142,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,26 +153,52 @@ #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)
{
- u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
- outw(v, base + r + (i * 8));
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_MFGPT);
+ iowrite16(v, base + r + (i * 8));
}
static inline u16
geode_mfgpt_read(int i, u16 r)
{
- u32 base = geode_get_dev_base(GEODE_DEV_MFGPT);
- return inw(base + r + (i * 8));
+ void __iomem *base = geode_get_dev_base(GEODE_DEV_MFGPT);
+ return ioread16(base + r + (i * 8));
}
+void geode_mfgpt_set_irq(int, int, int, int);
+int geode_mfgpt_alloc_timer(struct geode_mfgpt_timer_t *);
+void geode_mfgpt_toggle_event(int, int, int, int);
+
#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)
+/* 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