x86, olpc: Add missing elements to device tree
authorDaniel Drake <dsd@laptop.org>
Sat, 25 Jun 2011 16:34:08 +0000 (17:34 +0100)
committerH. Peter Anvin <hpa@linux.intel.com>
Wed, 6 Jul 2011 21:44:19 +0000 (14:44 -0700)
In response to new device tree code in the kernel, OLPC will start
using it for probing of certain devices. However, some firmware fixes
are needed to put the devicetree into a usable state.

Retain compatibility with old firmware by fixing up the device tree
at boot-time if it does not contain the new nodes/properties that
we need for probing. This is the same approach taken on PPC platforms.

Signed-off-by: Daniel Drake <dsd@laptop.org>
Link: http://lkml.kernel.org/r/1309019658-1712-2-git-send-email-dsd@laptop.org
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Andres Salomon <dilinger@queued.net>
Cc: devicetree-discuss@lists.ozlabs.org
Signed-off-by: H. Peter Anvin <hpa@linux.intel.com>
arch/x86/platform/olpc/olpc_dt.c

index d39f63d..d6ee929 100644 (file)
@@ -165,6 +165,107 @@ static struct of_pdt_ops prom_olpc_ops __initdata = {
        .pkg2path = olpc_dt_pkg2path,
 };
 
+static phandle __init olpc_dt_finddevice(const char *path)
+{
+       phandle node;
+       const void *args[] = { path };
+       void *res[] = { &node };
+
+       if (olpc_ofw("finddevice", args, res)) {
+               pr_err("olpc_dt: finddevice failed!\n");
+               return 0;
+       }
+
+       if ((s32) node == -1)
+               return 0;
+
+       return node;
+}
+
+static int __init olpc_dt_interpret(const char *words)
+{
+       int result;
+       const void *args[] = { words };
+       void *res[] = { &result };
+
+       if (olpc_ofw("interpret", args, res)) {
+               pr_err("olpc_dt: interpret failed!\n");
+               return -1;
+       }
+
+       return result;
+}
+
+/*
+ * Extract board revision directly from OFW device tree.
+ * We can't use olpc_platform_info because that hasn't been set up yet.
+ */
+static u32 __init olpc_dt_get_board_revision(void)
+{
+       phandle node;
+       __be32 rev;
+       int r;
+
+       node = olpc_dt_finddevice("/");
+       if (!node)
+               return 0;
+
+       r = olpc_dt_getproperty(node, "board-revision-int",
+                               (char *) &rev, sizeof(rev));
+       if (r < 0)
+               return 0;
+
+       return be32_to_cpu(rev);
+}
+
+void __init olpc_dt_fixup(void)
+{
+       int r;
+       char buf[64];
+       phandle node;
+       u32 board_rev;
+
+       node = olpc_dt_finddevice("/battery@0");
+       if (!node)
+               return;
+
+       /*
+        * If the battery node has a compatible property, we are running a new
+        * enough firmware and don't have fixups to make.
+        */
+       r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf));
+       if (r > 0)
+               return;
+
+       pr_info("PROM DT: Old firmware detected, applying fixes\n");
+
+       /* Add olpc,xo1-battery compatible marker to battery node */
+       olpc_dt_interpret("\" /battery@0\" find-device"
+               " \" olpc,xo1-battery\" +compatible"
+               " device-end");
+
+       board_rev = olpc_dt_get_board_revision();
+       if (!board_rev)
+               return;
+
+       if (board_rev >= olpc_board_pre(0xd0)) {
+               /* XO-1.5: add dcon device */
+               olpc_dt_interpret("\" /pci/display@1\" find-device"
+                       " new-device"
+                       " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
+                       " finish-device device-end");
+       } else {
+               /* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */
+               olpc_dt_interpret("\" /pci/display@1,1\" find-device"
+                       " new-device"
+                       " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
+                       " finish-device device-end"
+                       " \" /rtc\" find-device"
+                       " \" olpc,xo1-rtc\" +compatible"
+                       " device-end");
+       }
+}
+
 void __init olpc_dt_build_devicetree(void)
 {
        phandle root;
@@ -172,6 +273,8 @@ void __init olpc_dt_build_devicetree(void)
        if (!olpc_ofw_is_installed())
                return;
 
+       olpc_dt_fixup();
+
        root = olpc_dt_getsibling(0);
        if (!root) {
                pr_err("PROM: unable to get root node from OFW!\n");