linux/arch/x86/platform/olpc/olpc_dt.c
<<
>>
Prefs
   1/*
   2 * OLPC-specific OFW device tree support code.
   3 *
   4 * Paul Mackerras       August 1996.
   5 * Copyright (C) 1996-2005 Paul Mackerras.
   6 *
   7 *  Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner.
   8 *    {engebret|bergner}@us.ibm.com
   9 *
  10 *  Adapted for sparc by David S. Miller davem@davemloft.net
  11 *  Adapted for x86/OLPC by Andres Salomon <dilinger@queued.net>
  12 *
  13 *      This program is free software; you can redistribute it and/or
  14 *      modify it under the terms of the GNU General Public License
  15 *      as published by the Free Software Foundation; either version
  16 *      2 of the License, or (at your option) any later version.
  17 */
  18
  19#include <linux/kernel.h>
  20#include <linux/bootmem.h>
  21#include <linux/of.h>
  22#include <linux/of_platform.h>
  23#include <linux/of_pdt.h>
  24#include <asm/olpc.h>
  25#include <asm/olpc_ofw.h>
  26
  27static phandle __init olpc_dt_getsibling(phandle node)
  28{
  29        const void *args[] = { (void *)node };
  30        void *res[] = { &node };
  31
  32        if ((s32)node == -1)
  33                return 0;
  34
  35        if (olpc_ofw("peer", args, res) || (s32)node == -1)
  36                return 0;
  37
  38        return node;
  39}
  40
  41static phandle __init olpc_dt_getchild(phandle node)
  42{
  43        const void *args[] = { (void *)node };
  44        void *res[] = { &node };
  45
  46        if ((s32)node == -1)
  47                return 0;
  48
  49        if (olpc_ofw("child", args, res) || (s32)node == -1) {
  50                pr_err("PROM: %s: fetching child failed!\n", __func__);
  51                return 0;
  52        }
  53
  54        return node;
  55}
  56
  57static int __init olpc_dt_getproplen(phandle node, const char *prop)
  58{
  59        const void *args[] = { (void *)node, prop };
  60        int len;
  61        void *res[] = { &len };
  62
  63        if ((s32)node == -1)
  64                return -1;
  65
  66        if (olpc_ofw("getproplen", args, res)) {
  67                pr_err("PROM: %s: getproplen failed!\n", __func__);
  68                return -1;
  69        }
  70
  71        return len;
  72}
  73
  74static int __init olpc_dt_getproperty(phandle node, const char *prop,
  75                char *buf, int bufsize)
  76{
  77        int plen;
  78
  79        plen = olpc_dt_getproplen(node, prop);
  80        if (plen > bufsize || plen < 1) {
  81                return -1;
  82        } else {
  83                const void *args[] = { (void *)node, prop, buf, (void *)plen };
  84                void *res[] = { &plen };
  85
  86                if (olpc_ofw("getprop", args, res)) {
  87                        pr_err("PROM: %s: getprop failed!\n", __func__);
  88                        return -1;
  89                }
  90        }
  91
  92        return plen;
  93}
  94
  95static int __init olpc_dt_nextprop(phandle node, char *prev, char *buf)
  96{
  97        const void *args[] = { (void *)node, prev, buf };
  98        int success;
  99        void *res[] = { &success };
 100
 101        buf[0] = '\0';
 102
 103        if ((s32)node == -1)
 104                return -1;
 105
 106        if (olpc_ofw("nextprop", args, res) || success != 1)
 107                return -1;
 108
 109        return 0;
 110}
 111
 112static int __init olpc_dt_pkg2path(phandle node, char *buf,
 113                const int buflen, int *len)
 114{
 115        const void *args[] = { (void *)node, buf, (void *)buflen };
 116        void *res[] = { len };
 117
 118        if ((s32)node == -1)
 119                return -1;
 120
 121        if (olpc_ofw("package-to-path", args, res) || *len < 1)
 122                return -1;
 123
 124        return 0;
 125}
 126
 127static unsigned int prom_early_allocated __initdata;
 128
 129void * __init prom_early_alloc(unsigned long size)
 130{
 131        static u8 *mem;
 132        static size_t free_mem;
 133        void *res;
 134
 135        if (free_mem < size) {
 136                const size_t chunk_size = max(PAGE_SIZE, size);
 137
 138                /*
 139                 * To mimimize the number of allocations, grab at least
 140                 * PAGE_SIZE of memory (that's an arbitrary choice that's
 141                 * fast enough on the platforms we care about while minimizing
 142                 * wasted bootmem) and hand off chunks of it to callers.
 143                 */
 144                res = alloc_bootmem(chunk_size);
 145                BUG_ON(!res);
 146                prom_early_allocated += chunk_size;
 147                memset(res, 0, chunk_size);
 148                free_mem = chunk_size;
 149                mem = res;
 150        }
 151
 152        /* allocate from the local cache */
 153        free_mem -= size;
 154        res = mem;
 155        mem += size;
 156        return res;
 157}
 158
 159static struct of_pdt_ops prom_olpc_ops __initdata = {
 160        .nextprop = olpc_dt_nextprop,
 161        .getproplen = olpc_dt_getproplen,
 162        .getproperty = olpc_dt_getproperty,
 163        .getchild = olpc_dt_getchild,
 164        .getsibling = olpc_dt_getsibling,
 165        .pkg2path = olpc_dt_pkg2path,
 166};
 167
 168static phandle __init olpc_dt_finddevice(const char *path)
 169{
 170        phandle node;
 171        const void *args[] = { path };
 172        void *res[] = { &node };
 173
 174        if (olpc_ofw("finddevice", args, res)) {
 175                pr_err("olpc_dt: finddevice failed!\n");
 176                return 0;
 177        }
 178
 179        if ((s32) node == -1)
 180                return 0;
 181
 182        return node;
 183}
 184
 185static int __init olpc_dt_interpret(const char *words)
 186{
 187        int result;
 188        const void *args[] = { words };
 189        void *res[] = { &result };
 190
 191        if (olpc_ofw("interpret", args, res)) {
 192                pr_err("olpc_dt: interpret failed!\n");
 193                return -1;
 194        }
 195
 196        return result;
 197}
 198
 199/*
 200 * Extract board revision directly from OFW device tree.
 201 * We can't use olpc_platform_info because that hasn't been set up yet.
 202 */
 203static u32 __init olpc_dt_get_board_revision(void)
 204{
 205        phandle node;
 206        __be32 rev;
 207        int r;
 208
 209        node = olpc_dt_finddevice("/");
 210        if (!node)
 211                return 0;
 212
 213        r = olpc_dt_getproperty(node, "board-revision-int",
 214                                (char *) &rev, sizeof(rev));
 215        if (r < 0)
 216                return 0;
 217
 218        return be32_to_cpu(rev);
 219}
 220
 221void __init olpc_dt_fixup(void)
 222{
 223        int r;
 224        char buf[64];
 225        phandle node;
 226        u32 board_rev;
 227
 228        node = olpc_dt_finddevice("/battery@0");
 229        if (!node)
 230                return;
 231
 232        /*
 233         * If the battery node has a compatible property, we are running a new
 234         * enough firmware and don't have fixups to make.
 235         */
 236        r = olpc_dt_getproperty(node, "compatible", buf, sizeof(buf));
 237        if (r > 0)
 238                return;
 239
 240        pr_info("PROM DT: Old firmware detected, applying fixes\n");
 241
 242        /* Add olpc,xo1-battery compatible marker to battery node */
 243        olpc_dt_interpret("\" /battery@0\" find-device"
 244                " \" olpc,xo1-battery\" +compatible"
 245                " device-end");
 246
 247        board_rev = olpc_dt_get_board_revision();
 248        if (!board_rev)
 249                return;
 250
 251        if (board_rev >= olpc_board_pre(0xd0)) {
 252                /* XO-1.5: add dcon device */
 253                olpc_dt_interpret("\" /pci/display@1\" find-device"
 254                        " new-device"
 255                        " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
 256                        " finish-device device-end");
 257        } else {
 258                /* XO-1: add dcon device, mark RTC as olpc,xo1-rtc */
 259                olpc_dt_interpret("\" /pci/display@1,1\" find-device"
 260                        " new-device"
 261                        " \" dcon\" device-name \" olpc,xo1-dcon\" +compatible"
 262                        " finish-device device-end"
 263                        " \" /rtc\" find-device"
 264                        " \" olpc,xo1-rtc\" +compatible"
 265                        " device-end");
 266        }
 267}
 268
 269void __init olpc_dt_build_devicetree(void)
 270{
 271        phandle root;
 272
 273        if (!olpc_ofw_is_installed())
 274                return;
 275
 276        olpc_dt_fixup();
 277
 278        root = olpc_dt_getsibling(0);
 279        if (!root) {
 280                pr_err("PROM: unable to get root node from OFW!\n");
 281                return;
 282        }
 283        of_pdt_build_devicetree(root, &prom_olpc_ops);
 284
 285        pr_info("PROM DT: Built device tree with %u bytes of memory.\n",
 286                        prom_early_allocated);
 287}
 288
 289/* A list of DT node/bus matches that we want to expose as platform devices */
 290static struct of_device_id __initdata of_ids[] = {
 291        { .compatible = "olpc,xo1-battery" },
 292        { .compatible = "olpc,xo1-dcon" },
 293        { .compatible = "olpc,xo1-rtc" },
 294        {},
 295};
 296
 297static int __init olpc_create_platform_devices(void)
 298{
 299        if (machine_is_olpc())
 300                return of_platform_bus_probe(NULL, of_ids, NULL);
 301        else
 302                return 0;
 303}
 304device_initcall(olpc_create_platform_devices);
 305