[OLPC] Add basic power management handling for CS5536

David Woodhouse dwmw2 at infradead.unroutableorg
Sun Nov 12 02:15:14 EST 2006


Commit:     9771ebdee3875356d50bbadc0bc8f06cc0dd4e73
Parent:     e9c07c37bc2ab224c13c54fa5d78f141ea748ad6
commit 9771ebdee3875356d50bbadc0bc8f06cc0dd4e73
Author:     David Woodhouse <dwmw2 at infradead.org>
AuthorDate: Sun Nov 12 15:16:13 2006 +0800
Commit:     David Woodhouse <dwmw2 at infradead.org>
CommitDate: Sun Nov 12 15:16:13 2006 +0800

    [OLPC] Add basic power management handling for CS5536
    
    Just power button handling for now.
    
    Signed-off-by: David Woodhouse <dwmw2 at infradead.org>
---
 arch/i386/Kconfig          |   10 +++
 arch/i386/kernel/Makefile  |    1 
 arch/i386/kernel/olpc-pm.c |  143 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 153 insertions(+), 1 deletions(-)

diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig
index 14f642d..3a2eccf 100644
--- a/arch/i386/Kconfig
+++ b/arch/i386/Kconfig
@@ -1132,7 +1132,15 @@ config OLPC
 	help
 	  Add support for detecting the unique features of the OLPC 
 	  Childrens Machine
-	  
+
+config OLPC_PM
+       tristate "OLPC power management support"
+       default y
+       depends on OLPC
+       help
+         Add support for the Geode power management facilities on the
+	 OLPC Childrens Machine
+
 source "drivers/pcmcia/Kconfig"
 
 source "drivers/pci/hotplug/Kconfig"
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index b834d3d..8b365db 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -44,6 +44,7 @@ EXTRA_AFLAGS   := -traditional
 
 obj-$(CONFIG_SCx200)		+= scx200.o
 obj-$(CONFIG_OLPC)		+= olpc.o
+obj-$(CONFIG_OLPC_PM)		+= olpc-pm.o
 
 # vsyscall.o contains the vsyscall DSO images as __initdata.
 # We must build both images before we can assemble it.
diff --git a/arch/i386/kernel/olpc-pm.c b/arch/i386/kernel/olpc-pm.c
new file mode 100644
index 0000000..01c8adc
--- /dev/null
+++ b/arch/i386/kernel/olpc-pm.c
@@ -0,0 +1,143 @@
+/* olpc-pm.c
+ * © 2006 Red Hat, Inc.
+ * GPLv2
+ */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <asm/io.h>
+
+extern int machine_is_olpc;
+#define PM_IRQ 3
+
+
+#define MSR_LBAR_ACPI		0x5140000E
+#define MSR_LBAR_PMS		0x5140000F
+
+static unsigned long acpi_base;
+static unsigned long pms_base;
+static int sci;
+
+static struct input_dev *pm_inputdev;
+
+static int olpc_pm_interrupt(int irq, void *id)
+{
+	uint16_t sts = inw(acpi_base);
+	outw(sts, acpi_base);
+
+	/* It has to be 0x100 for now because we don't permit anything else */
+	if (sts & 0x100) {
+		input_report_key(pm_inputdev, KEY_POWER, 1);
+		input_sync(pm_inputdev);
+		/* Do we need to delay this (and hence schedule_work)? */
+		input_report_key(pm_inputdev, KEY_POWER, 0);
+		input_sync(pm_inputdev);
+	} else {
+		printk(KERN_WARNING "Strange PM1_STS %x\n", sts);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int __init olpc_pm_init(void)
+{
+	uint32_t lo, hi;
+	int ret;
+
+	if (!machine_is_olpc)
+		return -ENODEV;
+
+	pm_inputdev = input_allocate_device();
+	if (!pm_inputdev)
+		return -ENOMEM;
+
+	rdmsr(MSR_LBAR_ACPI, lo, hi);
+	/* Check the mask and whether GPIO is enabled (sanity check) */
+	if (hi != 0x0000f001) {
+		printk(KERN_ERR "ACPI registers not enabled -- cannot use OLPC power management\n");
+		return -ENODEV;
+	}
+	acpi_base = lo & 0x0000ffff;
+
+	rdmsr(MSR_LBAR_PMS, lo, hi);
+	/* Check the mask and whether GPIO is enabled (sanity check) */
+	if (hi != 0x0000f001) {
+		printk(KERN_ERR "PM Support not enabled -- cannot use OLPC power management\n");
+		return -ENODEV;
+	}
+	pms_base = lo & 0x0000ffff;
+
+	lo = inl(pms_base + 0x40);
+	printk("PM_FSD was %08x\n", lo);
+	/* Lock, enable failsafe, 4 seconds */
+	outl(0xc001f400, pms_base + 0x40);
+
+	rdmsr(0x51400020, lo, hi);
+	sci = (lo >> 20) & 15;
+	if (sci) {
+		printk(KERN_INFO "SCI is mapped to IRQ %d\n", sci);
+	} else {
+		/* Zero doesn't mean zero -- it means masked */
+		printk(KERN_INFO "SCI unmapped. Mapping to IRQ 3\n");
+		sci = 3;
+		lo |= 0x00300000;
+		wrmsrl(0x51400020, lo);
+	}
+
+	pm_inputdev->name = "Geode PM";
+	pm_inputdev->phys = "geode_pmc/input0";
+
+	pm_inputdev->evbit[0] = BIT(EV_KEY);
+	set_bit(KEY_POWER, pm_inputdev->keybit);
+	
+	ret = input_register_device(pm_inputdev);
+	if (ret < 0) {
+		printk(KERN_ERR "Unable to register OLPC PM input device: %d\n",
+		       ret);
+		return ret;
+	}
+
+	ret = request_irq(sci, &olpc_pm_interrupt, 0, "SCI", &acpi_base);
+	if (ret) {
+		printk(KERN_ERR "Error registering SCI: %d\n", ret);
+		return ret;
+	}
+
+	/* Set only power button to generate SCI */
+	outw(0x100, acpi_base + 2);
+
+	/* Select level triggered in PIC */
+	if (sci < 8) {
+		lo = inb(0x4d0);
+		lo |= 1 << sci;
+		outb(lo, 0x4d0);
+	} else {
+		lo = inb(0x4d1);
+		lo |= 1 << (sci - 8);
+		outb(lo, 0x4d1);
+	}
+	/* Clear pending interrupt */
+	outw(inw(acpi_base), acpi_base);
+
+	return 0;
+}
+
+
+static void olpc_pm_exit(void)
+{
+	/* Disable all events */
+	outw(0, acpi_base+2);
+
+	free_irq(sci, &acpi_base);
+	input_unregister_device(pm_inputdev);
+}
+
+module_init(olpc_pm_init);
+module_exit(olpc_pm_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2 at infradead.org>");
+MODULE_DESCRIPTION("AMD Geode power management for OLPC CL1");


More information about the Commits-kernel mailing list