x86: Move olpc to platform
authorThomas Gleixner <tglx@linutronix.de>
Sat, 23 Oct 2010 09:28:42 +0000 (11:28 +0200)
committerIngo Molnar <mingo@elte.hu>
Wed, 27 Oct 2010 15:22:16 +0000 (17:22 +0200)
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Andres Salomon <dilinger@queued.net>
arch/x86/kernel/Makefile
arch/x86/kernel/olpc-xo1.c [deleted file]
arch/x86/kernel/olpc.c [deleted file]
arch/x86/kernel/olpc_ofw.c [deleted file]
arch/x86/platform/Makefile
arch/x86/platform/olpc/Makefile [new file with mode: 0644]
arch/x86/platform/olpc/olpc-xo1.c [new file with mode: 0644]
arch/x86/platform/olpc/olpc.c [new file with mode: 0644]
arch/x86/platform/olpc/olpc_ofw.c [new file with mode: 0644]

index 08e2e4b..9e13763 100644 (file)
@@ -101,10 +101,6 @@ obj-$(CONFIG_PARAVIRT_CLOCK)       += pvclock.o
 
 obj-$(CONFIG_PCSPKR_PLATFORM)  += pcspeaker.o
 
-obj-$(CONFIG_OLPC)             += olpc.o
-obj-$(CONFIG_OLPC_XO1)         += olpc-xo1.o
-obj-$(CONFIG_OLPC_OPENFIRMWARE)        += olpc_ofw.o
-
 microcode-y                            := microcode_core.o
 microcode-$(CONFIG_MICROCODE_INTEL)    += microcode_intel.o
 microcode-$(CONFIG_MICROCODE_AMD)      += microcode_amd.o
diff --git a/arch/x86/kernel/olpc-xo1.c b/arch/x86/kernel/olpc-xo1.c
deleted file mode 100644 (file)
index f5442c0..0000000
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Support for features of the OLPC XO-1 laptop
- *
- * Copyright (C) 2010 One Laptop per Child
- * Copyright (C) 2006 Red Hat, Inc.
- * Copyright (C) 2006 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 as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/pci_ids.h>
-#include <linux/platform_device.h>
-#include <linux/pm.h>
-
-#include <asm/io.h>
-#include <asm/olpc.h>
-
-#define DRV_NAME "olpc-xo1"
-
-#define PMS_BAR                4
-#define ACPI_BAR       5
-
-/* PMC registers (PMS block) */
-#define PM_SCLK                0x10
-#define PM_IN_SLPCTL   0x20
-#define PM_WKXD                0x34
-#define PM_WKD         0x30
-#define PM_SSC         0x54
-
-/* PM registers (ACPI block) */
-#define PM1_CNT                0x08
-#define PM_GPE0_STS    0x18
-
-static unsigned long acpi_base;
-static unsigned long pms_base;
-
-static void xo1_power_off(void)
-{
-       printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
-
-       /* Enable all of these controls with 0 delay */
-       outl(0x40000000, pms_base + PM_SCLK);
-       outl(0x40000000, pms_base + PM_IN_SLPCTL);
-       outl(0x40000000, pms_base + PM_WKXD);
-       outl(0x40000000, pms_base + PM_WKD);
-
-       /* Clear status bits (possibly unnecessary) */
-       outl(0x0002ffff, pms_base  + PM_SSC);
-       outl(0xffffffff, acpi_base + PM_GPE0_STS);
-
-       /* Write SLP_EN bit to start the machinery */
-       outl(0x00002000, acpi_base + PM1_CNT);
-}
-
-/* Read the base addresses from the PCI BAR info */
-static int __devinit setup_bases(struct pci_dev *pdev)
-{
-       int r;
-
-       r = pci_enable_device_io(pdev);
-       if (r) {
-               dev_err(&pdev->dev, "can't enable device IO\n");
-               return r;
-       }
-
-       r = pci_request_region(pdev, ACPI_BAR, DRV_NAME);
-       if (r) {
-               dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", ACPI_BAR);
-               return r;
-       }
-
-       r = pci_request_region(pdev, PMS_BAR, DRV_NAME);
-       if (r) {
-               dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", PMS_BAR);
-               pci_release_region(pdev, ACPI_BAR);
-               return r;
-       }
-
-       acpi_base = pci_resource_start(pdev, ACPI_BAR);
-       pms_base = pci_resource_start(pdev, PMS_BAR);
-
-       return 0;
-}
-
-static int __devinit olpc_xo1_probe(struct platform_device *pdev)
-{
-       struct pci_dev *pcidev;
-       int r;
-
-       pcidev = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
-                               NULL);
-       if (!pdev)
-               return -ENODEV;
-
-       r = setup_bases(pcidev);
-       if (r)
-               return r;
-
-       pm_power_off = xo1_power_off;
-
-       printk(KERN_INFO "OLPC XO-1 support registered\n");
-       return 0;
-}
-
-static int __devexit olpc_xo1_remove(struct platform_device *pdev)
-{
-       pm_power_off = NULL;
-       return 0;
-}
-
-static struct platform_driver olpc_xo1_driver = {
-       .driver = {
-               .name = DRV_NAME,
-               .owner = THIS_MODULE,
-       },
-       .probe = olpc_xo1_probe,
-       .remove = __devexit_p(olpc_xo1_remove),
-};
-
-static int __init olpc_xo1_init(void)
-{
-       return platform_driver_register(&olpc_xo1_driver);
-}
-
-static void __exit olpc_xo1_exit(void)
-{
-       platform_driver_unregister(&olpc_xo1_driver);
-}
-
-MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:olpc-xo1");
-
-module_init(olpc_xo1_init);
-module_exit(olpc_xo1_exit);
diff --git a/arch/x86/kernel/olpc.c b/arch/x86/kernel/olpc.c
deleted file mode 100644 (file)
index edaf3fe..0000000
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Support for the OLPC DCON and OLPC EC access
- *
- * Copyright © 2006  Advanced Micro Devices, Inc.
- * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/spinlock.h>
-#include <linux/io.h>
-#include <linux/string.h>
-#include <linux/platform_device.h>
-
-#include <asm/geode.h>
-#include <asm/setup.h>
-#include <asm/olpc.h>
-#include <asm/olpc_ofw.h>
-
-struct olpc_platform_t olpc_platform_info;
-EXPORT_SYMBOL_GPL(olpc_platform_info);
-
-static DEFINE_SPINLOCK(ec_lock);
-
-/* what the timeout *should* be (in ms) */
-#define EC_BASE_TIMEOUT 20
-
-/* the timeout that bugs in the EC might force us to actually use */
-static int ec_timeout = EC_BASE_TIMEOUT;
-
-static int __init olpc_ec_timeout_set(char *str)
-{
-       if (get_option(&str, &ec_timeout) != 1) {
-               ec_timeout = EC_BASE_TIMEOUT;
-               printk(KERN_ERR "olpc-ec:  invalid argument to "
-                               "'olpc_ec_timeout=', ignoring!\n");
-       }
-       printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
-                       ec_timeout);
-       return 1;
-}
-__setup("olpc_ec_timeout=", olpc_ec_timeout_set);
-
-/*
- * These {i,o}bf_status functions return whether the buffers are full or not.
- */
-
-static inline unsigned int ibf_status(unsigned int port)
-{
-       return !!(inb(port) & 0x02);
-}
-
-static inline unsigned int obf_status(unsigned int port)
-{
-       return inb(port) & 0x01;
-}
-
-#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
-static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
-{
-       unsigned int timeo;
-       int state = ibf_status(port);
-
-       for (timeo = ec_timeout; state != desired && timeo; timeo--) {
-               mdelay(1);
-               state = ibf_status(port);
-       }
-
-       if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
-                       timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
-               printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
-                               line, ec_timeout - timeo);
-       }
-
-       return !(state == desired);
-}
-
-#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
-static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
-{
-       unsigned int timeo;
-       int state = obf_status(port);
-
-       for (timeo = ec_timeout; state != desired && timeo; timeo--) {
-               mdelay(1);
-               state = obf_status(port);
-       }
-
-       if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
-                       timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
-               printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
-                               line, ec_timeout - timeo);
-       }
-
-       return !(state == desired);
-}
-
-/*
- * This allows the kernel to run Embedded Controller commands.  The EC is
- * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
- * available EC commands are here:
- * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
- * OpenFirmware's source is available, the EC's is not.
- */
-int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
-               unsigned char *outbuf,  size_t outlen)
-{
-       unsigned long flags;
-       int ret = -EIO;
-       int i;
-       int restarts = 0;
-
-       spin_lock_irqsave(&ec_lock, flags);
-
-       /* Clear OBF */
-       for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
-               inb(0x68);
-       if (i == 10) {
-               printk(KERN_ERR "olpc-ec:  timeout while attempting to "
-                               "clear OBF flag!\n");
-               goto err;
-       }
-
-       if (wait_on_ibf(0x6c, 0)) {
-               printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
-                               "quiesce!\n");
-               goto err;
-       }
-
-restart:
-       /*
-        * Note that if we time out during any IBF checks, that's a failure;
-        * we have to return.  There's no way for the kernel to clear that.
-        *
-        * If we time out during an OBF check, we can restart the command;
-        * reissuing it will clear the OBF flag, and we should be alright.
-        * The OBF flag will sometimes misbehave due to what we believe
-        * is a hardware quirk..
-        */
-       pr_devel("olpc-ec:  running cmd 0x%x\n", cmd);
-       outb(cmd, 0x6c);
-
-       if (wait_on_ibf(0x6c, 0)) {
-               printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
-                               "command!\n");
-               goto err;
-       }
-
-       if (inbuf && inlen) {
-               /* write data to EC */
-               for (i = 0; i < inlen; i++) {
-                       if (wait_on_ibf(0x6c, 0)) {
-                               printk(KERN_ERR "olpc-ec:  timeout waiting for"
-                                               " EC accept data!\n");
-                               goto err;
-                       }
-                       pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
-                       outb(inbuf[i], 0x68);
-               }
-       }
-       if (outbuf && outlen) {
-               /* read data from EC */
-               for (i = 0; i < outlen; i++) {
-                       if (wait_on_obf(0x6c, 1)) {
-                               printk(KERN_ERR "olpc-ec:  timeout waiting for"
-                                               " EC to provide data!\n");
-                               if (restarts++ < 10)
-                                       goto restart;
-                               goto err;
-                       }
-                       outbuf[i] = inb(0x68);
-                       pr_devel("olpc-ec:  received 0x%x\n", outbuf[i]);
-               }
-       }
-
-       ret = 0;
-err:
-       spin_unlock_irqrestore(&ec_lock, flags);
-       return ret;
-}
-EXPORT_SYMBOL_GPL(olpc_ec_cmd);
-
-static bool __init check_ofw_architecture(void)
-{
-       size_t propsize;
-       char olpc_arch[5];
-       const void *args[] = { NULL, "architecture", olpc_arch, (void *)5 };
-       void *res[] = { &propsize };
-
-       if (olpc_ofw("getprop", args, res)) {
-               printk(KERN_ERR "ofw: getprop call failed!\n");
-               return false;
-       }
-       return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
-}
-
-static u32 __init get_board_revision(void)
-{
-       size_t propsize;
-       __be32 rev;
-       const void *args[] = { NULL, "board-revision-int", &rev, (void *)4 };
-       void *res[] = { &propsize };
-
-       if (olpc_ofw("getprop", args, res) || propsize != 4) {
-               printk(KERN_ERR "ofw: getprop call failed!\n");
-               return cpu_to_be32(0);
-       }
-       return be32_to_cpu(rev);
-}
-
-static bool __init platform_detect(void)
-{
-       if (!check_ofw_architecture())
-               return false;
-       olpc_platform_info.flags |= OLPC_F_PRESENT;
-       olpc_platform_info.boardrev = get_board_revision();
-       return true;
-}
-
-static int __init add_xo1_platform_devices(void)
-{
-       struct platform_device *pdev;
-
-       pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
-       if (IS_ERR(pdev))
-               return PTR_ERR(pdev);
-
-       pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
-       if (IS_ERR(pdev))
-               return PTR_ERR(pdev);
-
-       return 0;
-}
-
-static int __init olpc_init(void)
-{
-       int r = 0;
-
-       if (!olpc_ofw_present() || !platform_detect())
-               return 0;
-
-       spin_lock_init(&ec_lock);
-
-       /* assume B1 and above models always have a DCON */
-       if (olpc_board_at_least(olpc_board(0xb1)))
-               olpc_platform_info.flags |= OLPC_F_DCON;
-
-       /* get the EC revision */
-       olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
-                       (unsigned char *) &olpc_platform_info.ecver, 1);
-
-#ifdef CONFIG_PCI_OLPC
-       /* If the VSA exists let it emulate PCI, if not emulate in kernel.
-        * XO-1 only. */
-       if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
-                       !cs5535_has_vsa2())
-               x86_init.pci.arch_init = pci_olpc_init;
-#endif
-
-       printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
-                       ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
-                       olpc_platform_info.boardrev >> 4,
-                       olpc_platform_info.ecver);
-
-       if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
-               r = add_xo1_platform_devices();
-               if (r)
-                       return r;
-       }
-
-       return 0;
-}
-
-postcore_initcall(olpc_init);
diff --git a/arch/x86/kernel/olpc_ofw.c b/arch/x86/kernel/olpc_ofw.c
deleted file mode 100644 (file)
index 7873204..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <asm/page.h>
-#include <asm/setup.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/olpc_ofw.h>
-
-/* address of OFW callback interface; will be NULL if OFW isn't found */
-static int (*olpc_ofw_cif)(int *);
-
-/* page dir entry containing OFW's pgdir table; filled in by head_32.S */
-u32 olpc_ofw_pgd __initdata;
-
-static DEFINE_SPINLOCK(ofw_lock);
-
-#define MAXARGS 10
-
-void __init setup_olpc_ofw_pgd(void)
-{
-       pgd_t *base, *ofw_pde;
-
-       if (!olpc_ofw_cif)
-               return;
-
-       /* fetch OFW's PDE */
-       base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
-       if (!base) {
-               printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n");
-               olpc_ofw_cif = NULL;
-               return;
-       }
-       ofw_pde = &base[OLPC_OFW_PDE_NR];
-
-       /* install OFW's PDE permanently into the kernel's pgtable */
-       set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
-       /* implicit optimization barrier here due to uninline function return */
-
-       early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
-}
-
-int __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res,
-               void **res)
-{
-       int ofw_args[MAXARGS + 3];
-       unsigned long flags;
-       int ret, i, *p;
-
-       BUG_ON(nr_args + nr_res > MAXARGS);
-
-       if (!olpc_ofw_cif)
-               return -EIO;
-
-       ofw_args[0] = (int)name;
-       ofw_args[1] = nr_args;
-       ofw_args[2] = nr_res;
-
-       p = &ofw_args[3];
-       for (i = 0; i < nr_args; i++, p++)
-               *p = (int)args[i];
-
-       /* call into ofw */
-       spin_lock_irqsave(&ofw_lock, flags);
-       ret = olpc_ofw_cif(ofw_args);
-       spin_unlock_irqrestore(&ofw_lock, flags);
-
-       if (!ret) {
-               for (i = 0; i < nr_res; i++, p++)
-                       *((int *)res[i]) = *p;
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(__olpc_ofw);
-
-bool olpc_ofw_present(void)
-{
-       return olpc_ofw_cif != NULL;
-}
-EXPORT_SYMBOL_GPL(olpc_ofw_present);
-
-/* OFW cif _should_ be above this address */
-#define OFW_MIN 0xff000000
-
-/* OFW starts on a 1MB boundary */
-#define OFW_BOUND (1<<20)
-
-void __init olpc_ofw_detect(void)
-{
-       struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header;
-       unsigned long start;
-
-       /* ensure OFW booted us by checking for "OFW " string */
-       if (hdr->ofw_magic != OLPC_OFW_SIG)
-               return;
-
-       olpc_ofw_cif = (int (*)(int *))hdr->cif_handler;
-
-       if ((unsigned long)olpc_ofw_cif < OFW_MIN) {
-               printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n",
-                               (unsigned long)olpc_ofw_cif);
-               olpc_ofw_cif = NULL;
-               return;
-       }
-
-       /* determine where OFW starts in memory */
-       start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND);
-       printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n",
-                       (unsigned long)olpc_ofw_cif, (-start) >> 20);
-       reserve_top_address(-start);
-}
index 8519b01..7bf70b8 100644 (file)
@@ -1,6 +1,7 @@
 # Platform specific code goes here
 obj-y  += efi/
 obj-y  += mrst/
+obj-y  += olpc/
 obj-y  += scx200/
 obj-y  += sfi/
 obj-y  += visws/
diff --git a/arch/x86/platform/olpc/Makefile b/arch/x86/platform/olpc/Makefile
new file mode 100644 (file)
index 0000000..c31b8fc
--- /dev/null
@@ -0,0 +1,3 @@
+obj-$(CONFIG_OLPC)             += olpc.o
+obj-$(CONFIG_OLPC_XO1)         += olpc-xo1.o
+obj-$(CONFIG_OLPC_OPENFIRMWARE)        += olpc_ofw.o
diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c
new file mode 100644 (file)
index 0000000..f5442c0
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Support for features of the OLPC XO-1 laptop
+ *
+ * Copyright (C) 2010 One Laptop per Child
+ * Copyright (C) 2006 Red Hat, Inc.
+ * Copyright (C) 2006 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 as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+
+#include <asm/io.h>
+#include <asm/olpc.h>
+
+#define DRV_NAME "olpc-xo1"
+
+#define PMS_BAR                4
+#define ACPI_BAR       5
+
+/* PMC registers (PMS block) */
+#define PM_SCLK                0x10
+#define PM_IN_SLPCTL   0x20
+#define PM_WKXD                0x34
+#define PM_WKD         0x30
+#define PM_SSC         0x54
+
+/* PM registers (ACPI block) */
+#define PM1_CNT                0x08
+#define PM_GPE0_STS    0x18
+
+static unsigned long acpi_base;
+static unsigned long pms_base;
+
+static void xo1_power_off(void)
+{
+       printk(KERN_INFO "OLPC XO-1 power off sequence...\n");
+
+       /* Enable all of these controls with 0 delay */
+       outl(0x40000000, pms_base + PM_SCLK);
+       outl(0x40000000, pms_base + PM_IN_SLPCTL);
+       outl(0x40000000, pms_base + PM_WKXD);
+       outl(0x40000000, pms_base + PM_WKD);
+
+       /* Clear status bits (possibly unnecessary) */
+       outl(0x0002ffff, pms_base  + PM_SSC);
+       outl(0xffffffff, acpi_base + PM_GPE0_STS);
+
+       /* Write SLP_EN bit to start the machinery */
+       outl(0x00002000, acpi_base + PM1_CNT);
+}
+
+/* Read the base addresses from the PCI BAR info */
+static int __devinit setup_bases(struct pci_dev *pdev)
+{
+       int r;
+
+       r = pci_enable_device_io(pdev);
+       if (r) {
+               dev_err(&pdev->dev, "can't enable device IO\n");
+               return r;
+       }
+
+       r = pci_request_region(pdev, ACPI_BAR, DRV_NAME);
+       if (r) {
+               dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", ACPI_BAR);
+               return r;
+       }
+
+       r = pci_request_region(pdev, PMS_BAR, DRV_NAME);
+       if (r) {
+               dev_err(&pdev->dev, "can't alloc PCI BAR #%d\n", PMS_BAR);
+               pci_release_region(pdev, ACPI_BAR);
+               return r;
+       }
+
+       acpi_base = pci_resource_start(pdev, ACPI_BAR);
+       pms_base = pci_resource_start(pdev, PMS_BAR);
+
+       return 0;
+}
+
+static int __devinit olpc_xo1_probe(struct platform_device *pdev)
+{
+       struct pci_dev *pcidev;
+       int r;
+
+       pcidev = pci_get_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA,
+                               NULL);
+       if (!pdev)
+               return -ENODEV;
+
+       r = setup_bases(pcidev);
+       if (r)
+               return r;
+
+       pm_power_off = xo1_power_off;
+
+       printk(KERN_INFO "OLPC XO-1 support registered\n");
+       return 0;
+}
+
+static int __devexit olpc_xo1_remove(struct platform_device *pdev)
+{
+       pm_power_off = NULL;
+       return 0;
+}
+
+static struct platform_driver olpc_xo1_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+       },
+       .probe = olpc_xo1_probe,
+       .remove = __devexit_p(olpc_xo1_remove),
+};
+
+static int __init olpc_xo1_init(void)
+{
+       return platform_driver_register(&olpc_xo1_driver);
+}
+
+static void __exit olpc_xo1_exit(void)
+{
+       platform_driver_unregister(&olpc_xo1_driver);
+}
+
+MODULE_AUTHOR("Daniel Drake <dsd@laptop.org>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:olpc-xo1");
+
+module_init(olpc_xo1_init);
+module_exit(olpc_xo1_exit);
diff --git a/arch/x86/platform/olpc/olpc.c b/arch/x86/platform/olpc/olpc.c
new file mode 100644 (file)
index 0000000..edaf3fe
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Support for the OLPC DCON and OLPC EC access
+ *
+ * Copyright © 2006  Advanced Micro Devices, Inc.
+ * Copyright © 2007-2008  Andres Salomon <dilinger@debian.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/string.h>
+#include <linux/platform_device.h>
+
+#include <asm/geode.h>
+#include <asm/setup.h>
+#include <asm/olpc.h>
+#include <asm/olpc_ofw.h>
+
+struct olpc_platform_t olpc_platform_info;
+EXPORT_SYMBOL_GPL(olpc_platform_info);
+
+static DEFINE_SPINLOCK(ec_lock);
+
+/* what the timeout *should* be (in ms) */
+#define EC_BASE_TIMEOUT 20
+
+/* the timeout that bugs in the EC might force us to actually use */
+static int ec_timeout = EC_BASE_TIMEOUT;
+
+static int __init olpc_ec_timeout_set(char *str)
+{
+       if (get_option(&str, &ec_timeout) != 1) {
+               ec_timeout = EC_BASE_TIMEOUT;
+               printk(KERN_ERR "olpc-ec:  invalid argument to "
+                               "'olpc_ec_timeout=', ignoring!\n");
+       }
+       printk(KERN_DEBUG "olpc-ec:  using %d ms delay for EC commands.\n",
+                       ec_timeout);
+       return 1;
+}
+__setup("olpc_ec_timeout=", olpc_ec_timeout_set);
+
+/*
+ * These {i,o}bf_status functions return whether the buffers are full or not.
+ */
+
+static inline unsigned int ibf_status(unsigned int port)
+{
+       return !!(inb(port) & 0x02);
+}
+
+static inline unsigned int obf_status(unsigned int port)
+{
+       return inb(port) & 0x01;
+}
+
+#define wait_on_ibf(p, d) __wait_on_ibf(__LINE__, (p), (d))
+static int __wait_on_ibf(unsigned int line, unsigned int port, int desired)
+{
+       unsigned int timeo;
+       int state = ibf_status(port);
+
+       for (timeo = ec_timeout; state != desired && timeo; timeo--) {
+               mdelay(1);
+               state = ibf_status(port);
+       }
+
+       if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
+                       timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
+               printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for IBF!\n",
+                               line, ec_timeout - timeo);
+       }
+
+       return !(state == desired);
+}
+
+#define wait_on_obf(p, d) __wait_on_obf(__LINE__, (p), (d))
+static int __wait_on_obf(unsigned int line, unsigned int port, int desired)
+{
+       unsigned int timeo;
+       int state = obf_status(port);
+
+       for (timeo = ec_timeout; state != desired && timeo; timeo--) {
+               mdelay(1);
+               state = obf_status(port);
+       }
+
+       if ((state == desired) && (ec_timeout > EC_BASE_TIMEOUT) &&
+                       timeo < (ec_timeout - EC_BASE_TIMEOUT)) {
+               printk(KERN_WARNING "olpc-ec:  %d: waited %u ms for OBF!\n",
+                               line, ec_timeout - timeo);
+       }
+
+       return !(state == desired);
+}
+
+/*
+ * This allows the kernel to run Embedded Controller commands.  The EC is
+ * documented at <http://wiki.laptop.org/go/Embedded_controller>, and the
+ * available EC commands are here:
+ * <http://wiki.laptop.org/go/Ec_specification>.  Unfortunately, while
+ * OpenFirmware's source is available, the EC's is not.
+ */
+int olpc_ec_cmd(unsigned char cmd, unsigned char *inbuf, size_t inlen,
+               unsigned char *outbuf,  size_t outlen)
+{
+       unsigned long flags;
+       int ret = -EIO;
+       int i;
+       int restarts = 0;
+
+       spin_lock_irqsave(&ec_lock, flags);
+
+       /* Clear OBF */
+       for (i = 0; i < 10 && (obf_status(0x6c) == 1); i++)
+               inb(0x68);
+       if (i == 10) {
+               printk(KERN_ERR "olpc-ec:  timeout while attempting to "
+                               "clear OBF flag!\n");
+               goto err;
+       }
+
+       if (wait_on_ibf(0x6c, 0)) {
+               printk(KERN_ERR "olpc-ec:  timeout waiting for EC to "
+                               "quiesce!\n");
+               goto err;
+       }
+
+restart:
+       /*
+        * Note that if we time out during any IBF checks, that's a failure;
+        * we have to return.  There's no way for the kernel to clear that.
+        *
+        * If we time out during an OBF check, we can restart the command;
+        * reissuing it will clear the OBF flag, and we should be alright.
+        * The OBF flag will sometimes misbehave due to what we believe
+        * is a hardware quirk..
+        */
+       pr_devel("olpc-ec:  running cmd 0x%x\n", cmd);
+       outb(cmd, 0x6c);
+
+       if (wait_on_ibf(0x6c, 0)) {
+               printk(KERN_ERR "olpc-ec:  timeout waiting for EC to read "
+                               "command!\n");
+               goto err;
+       }
+
+       if (inbuf && inlen) {
+               /* write data to EC */
+               for (i = 0; i < inlen; i++) {
+                       if (wait_on_ibf(0x6c, 0)) {
+                               printk(KERN_ERR "olpc-ec:  timeout waiting for"
+                                               " EC accept data!\n");
+                               goto err;
+                       }
+                       pr_devel("olpc-ec:  sending cmd arg 0x%x\n", inbuf[i]);
+                       outb(inbuf[i], 0x68);
+               }
+       }
+       if (outbuf && outlen) {
+               /* read data from EC */
+               for (i = 0; i < outlen; i++) {
+                       if (wait_on_obf(0x6c, 1)) {
+                               printk(KERN_ERR "olpc-ec:  timeout waiting for"
+                                               " EC to provide data!\n");
+                               if (restarts++ < 10)
+                                       goto restart;
+                               goto err;
+                       }
+                       outbuf[i] = inb(0x68);
+                       pr_devel("olpc-ec:  received 0x%x\n", outbuf[i]);
+               }
+       }
+
+       ret = 0;
+err:
+       spin_unlock_irqrestore(&ec_lock, flags);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(olpc_ec_cmd);
+
+static bool __init check_ofw_architecture(void)
+{
+       size_t propsize;
+       char olpc_arch[5];
+       const void *args[] = { NULL, "architecture", olpc_arch, (void *)5 };
+       void *res[] = { &propsize };
+
+       if (olpc_ofw("getprop", args, res)) {
+               printk(KERN_ERR "ofw: getprop call failed!\n");
+               return false;
+       }
+       return propsize == 5 && strncmp("OLPC", olpc_arch, 5) == 0;
+}
+
+static u32 __init get_board_revision(void)
+{
+       size_t propsize;
+       __be32 rev;
+       const void *args[] = { NULL, "board-revision-int", &rev, (void *)4 };
+       void *res[] = { &propsize };
+
+       if (olpc_ofw("getprop", args, res) || propsize != 4) {
+               printk(KERN_ERR "ofw: getprop call failed!\n");
+               return cpu_to_be32(0);
+       }
+       return be32_to_cpu(rev);
+}
+
+static bool __init platform_detect(void)
+{
+       if (!check_ofw_architecture())
+               return false;
+       olpc_platform_info.flags |= OLPC_F_PRESENT;
+       olpc_platform_info.boardrev = get_board_revision();
+       return true;
+}
+
+static int __init add_xo1_platform_devices(void)
+{
+       struct platform_device *pdev;
+
+       pdev = platform_device_register_simple("xo1-rfkill", -1, NULL, 0);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
+
+       pdev = platform_device_register_simple("olpc-xo1", -1, NULL, 0);
+       if (IS_ERR(pdev))
+               return PTR_ERR(pdev);
+
+       return 0;
+}
+
+static int __init olpc_init(void)
+{
+       int r = 0;
+
+       if (!olpc_ofw_present() || !platform_detect())
+               return 0;
+
+       spin_lock_init(&ec_lock);
+
+       /* assume B1 and above models always have a DCON */
+       if (olpc_board_at_least(olpc_board(0xb1)))
+               olpc_platform_info.flags |= OLPC_F_DCON;
+
+       /* get the EC revision */
+       olpc_ec_cmd(EC_FIRMWARE_REV, NULL, 0,
+                       (unsigned char *) &olpc_platform_info.ecver, 1);
+
+#ifdef CONFIG_PCI_OLPC
+       /* If the VSA exists let it emulate PCI, if not emulate in kernel.
+        * XO-1 only. */
+       if (olpc_platform_info.boardrev < olpc_board_pre(0xd0) &&
+                       !cs5535_has_vsa2())
+               x86_init.pci.arch_init = pci_olpc_init;
+#endif
+
+       printk(KERN_INFO "OLPC board revision %s%X (EC=%x)\n",
+                       ((olpc_platform_info.boardrev & 0xf) < 8) ? "pre" : "",
+                       olpc_platform_info.boardrev >> 4,
+                       olpc_platform_info.ecver);
+
+       if (olpc_platform_info.boardrev < olpc_board_pre(0xd0)) { /* XO-1 */
+               r = add_xo1_platform_devices();
+               if (r)
+                       return r;
+       }
+
+       return 0;
+}
+
+postcore_initcall(olpc_init);
diff --git a/arch/x86/platform/olpc/olpc_ofw.c b/arch/x86/platform/olpc/olpc_ofw.c
new file mode 100644 (file)
index 0000000..7873204
--- /dev/null
@@ -0,0 +1,112 @@
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <asm/page.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/olpc_ofw.h>
+
+/* address of OFW callback interface; will be NULL if OFW isn't found */
+static int (*olpc_ofw_cif)(int *);
+
+/* page dir entry containing OFW's pgdir table; filled in by head_32.S */
+u32 olpc_ofw_pgd __initdata;
+
+static DEFINE_SPINLOCK(ofw_lock);
+
+#define MAXARGS 10
+
+void __init setup_olpc_ofw_pgd(void)
+{
+       pgd_t *base, *ofw_pde;
+
+       if (!olpc_ofw_cif)
+               return;
+
+       /* fetch OFW's PDE */
+       base = early_ioremap(olpc_ofw_pgd, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
+       if (!base) {
+               printk(KERN_ERR "failed to remap OFW's pgd - disabling OFW!\n");
+               olpc_ofw_cif = NULL;
+               return;
+       }
+       ofw_pde = &base[OLPC_OFW_PDE_NR];
+
+       /* install OFW's PDE permanently into the kernel's pgtable */
+       set_pgd(&swapper_pg_dir[OLPC_OFW_PDE_NR], *ofw_pde);
+       /* implicit optimization barrier here due to uninline function return */
+
+       early_iounmap(base, sizeof(olpc_ofw_pgd) * PTRS_PER_PGD);
+}
+
+int __olpc_ofw(const char *name, int nr_args, const void **args, int nr_res,
+               void **res)
+{
+       int ofw_args[MAXARGS + 3];
+       unsigned long flags;
+       int ret, i, *p;
+
+       BUG_ON(nr_args + nr_res > MAXARGS);
+
+       if (!olpc_ofw_cif)
+               return -EIO;
+
+       ofw_args[0] = (int)name;
+       ofw_args[1] = nr_args;
+       ofw_args[2] = nr_res;
+
+       p = &ofw_args[3];
+       for (i = 0; i < nr_args; i++, p++)
+               *p = (int)args[i];
+
+       /* call into ofw */
+       spin_lock_irqsave(&ofw_lock, flags);
+       ret = olpc_ofw_cif(ofw_args);
+       spin_unlock_irqrestore(&ofw_lock, flags);
+
+       if (!ret) {
+               for (i = 0; i < nr_res; i++, p++)
+                       *((int *)res[i]) = *p;
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(__olpc_ofw);
+
+bool olpc_ofw_present(void)
+{
+       return olpc_ofw_cif != NULL;
+}
+EXPORT_SYMBOL_GPL(olpc_ofw_present);
+
+/* OFW cif _should_ be above this address */
+#define OFW_MIN 0xff000000
+
+/* OFW starts on a 1MB boundary */
+#define OFW_BOUND (1<<20)
+
+void __init olpc_ofw_detect(void)
+{
+       struct olpc_ofw_header *hdr = &boot_params.olpc_ofw_header;
+       unsigned long start;
+
+       /* ensure OFW booted us by checking for "OFW " string */
+       if (hdr->ofw_magic != OLPC_OFW_SIG)
+               return;
+
+       olpc_ofw_cif = (int (*)(int *))hdr->cif_handler;
+
+       if ((unsigned long)olpc_ofw_cif < OFW_MIN) {
+               printk(KERN_ERR "OFW detected, but cif has invalid address 0x%lx - disabling.\n",
+                               (unsigned long)olpc_ofw_cif);
+               olpc_ofw_cif = NULL;
+               return;
+       }
+
+       /* determine where OFW starts in memory */
+       start = round_down((unsigned long)olpc_ofw_cif, OFW_BOUND);
+       printk(KERN_INFO "OFW detected in memory, cif @ 0x%lx (reserving top %ldMB)\n",
+                       (unsigned long)olpc_ofw_cif, (-start) >> 20);
+       reserve_top_address(-start);
+}