[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