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