linux/arch/sparc/kernel/prom.c
<<
>>
Prefs
   1/*
   2 * Procedures for creating, accessing and interpreting the device tree.
   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 sparc32 by David S. Miller davem@davemloft.net
  11 *
  12 *      This program is free software; you can redistribute it and/or
  13 *      modify it under the terms of the GNU General Public License
  14 *      as published by the Free Software Foundation; either version
  15 *      2 of the License, or (at your option) any later version.
  16 */
  17
  18#include <linux/kernel.h>
  19#include <linux/types.h>
  20#include <linux/string.h>
  21#include <linux/mm.h>
  22#include <linux/bootmem.h>
  23#include <linux/module.h>
  24
  25#include <asm/prom.h>
  26#include <asm/oplib.h>
  27
  28extern struct device_node *allnodes;    /* temporary while merging */
  29
  30extern rwlock_t devtree_lock;   /* temporary while merging */
  31
  32struct device_node *of_find_node_by_phandle(phandle handle)
  33{
  34        struct device_node *np;
  35
  36        for (np = allnodes; np != 0; np = np->allnext)
  37                if (np->node == handle)
  38                        break;
  39
  40        return np;
  41}
  42EXPORT_SYMBOL(of_find_node_by_phandle);
  43
  44int of_getintprop_default(struct device_node *np, const char *name, int def)
  45{
  46        struct property *prop;
  47        int len;
  48
  49        prop = of_find_property(np, name, &len);
  50        if (!prop || len != 4)
  51                return def;
  52
  53        return *(int *) prop->value;
  54}
  55EXPORT_SYMBOL(of_getintprop_default);
  56
  57int of_set_property(struct device_node *dp, const char *name, void *val, int len)
  58{
  59        struct property **prevp;
  60        void *new_val;
  61        int err;
  62
  63        new_val = kmalloc(len, GFP_KERNEL);
  64        if (!new_val)
  65                return -ENOMEM;
  66
  67        memcpy(new_val, val, len);
  68
  69        err = -ENODEV;
  70
  71        write_lock(&devtree_lock);
  72        prevp = &dp->properties;
  73        while (*prevp) {
  74                struct property *prop = *prevp;
  75
  76                if (!strcasecmp(prop->name, name)) {
  77                        void *old_val = prop->value;
  78                        int ret;
  79
  80                        ret = prom_setprop(dp->node, (char *) name, val, len);
  81                        err = -EINVAL;
  82                        if (ret >= 0) {
  83                                prop->value = new_val;
  84                                prop->length = len;
  85
  86                                if (OF_IS_DYNAMIC(prop))
  87                                        kfree(old_val);
  88
  89                                OF_MARK_DYNAMIC(prop);
  90
  91                                err = 0;
  92                        }
  93                        break;
  94                }
  95                prevp = &(*prevp)->next;
  96        }
  97        write_unlock(&devtree_lock);
  98
  99        /* XXX Upate procfs if necessary... */
 100
 101        return err;
 102}
 103EXPORT_SYMBOL(of_set_property);
 104
 105int of_find_in_proplist(const char *list, const char *match, int len)
 106{
 107        while (len > 0) {
 108                int l;
 109
 110                if (!strcmp(list, match))
 111                        return 1;
 112                l = strlen(list) + 1;
 113                list += l;
 114                len -= l;
 115        }
 116        return 0;
 117}
 118EXPORT_SYMBOL(of_find_in_proplist);
 119
 120static unsigned int prom_early_allocated;
 121
 122static void * __init prom_early_alloc(unsigned long size)
 123{
 124        void *ret;
 125
 126        ret = __alloc_bootmem(size, SMP_CACHE_BYTES, 0UL);
 127        if (ret != NULL)
 128                memset(ret, 0, size);
 129
 130        prom_early_allocated += size;
 131
 132        return ret;
 133}
 134
 135static int is_root_node(const struct device_node *dp)
 136{
 137        if (!dp)
 138                return 0;
 139
 140        return (dp->parent == NULL);
 141}
 142
 143/* The following routines deal with the black magic of fully naming a
 144 * node.
 145 *
 146 * Certain well known named nodes are just the simple name string.
 147 *
 148 * Actual devices have an address specifier appended to the base name
 149 * string, like this "foo@addr".  The "addr" can be in any number of
 150 * formats, and the platform plus the type of the node determine the
 151 * format and how it is constructed.
 152 *
 153 * For children of the ROOT node, the naming convention is fixed and
 154 * determined by whether this is a sun4u or sun4v system.
 155 *
 156 * For children of other nodes, it is bus type specific.  So
 157 * we walk up the tree until we discover a "device_type" property
 158 * we recognize and we go from there.
 159 */
 160static void __init sparc32_path_component(struct device_node *dp, char *tmp_buf)
 161{
 162        struct linux_prom_registers *regs;
 163        struct property *rprop;
 164
 165        rprop = of_find_property(dp, "reg", NULL);
 166        if (!rprop)
 167                return;
 168
 169        regs = rprop->value;
 170        sprintf(tmp_buf, "%s@%x,%x",
 171                dp->name,
 172                regs->which_io, regs->phys_addr);
 173}
 174
 175/* "name@slot,offset"  */
 176static void __init sbus_path_component(struct device_node *dp, char *tmp_buf)
 177{
 178        struct linux_prom_registers *regs;
 179        struct property *prop;
 180
 181        prop = of_find_property(dp, "reg", NULL);
 182        if (!prop)
 183                return;
 184
 185        regs = prop->value;
 186        sprintf(tmp_buf, "%s@%x,%x",
 187                dp->name,
 188                regs->which_io,
 189                regs->phys_addr);
 190}
 191
 192/* "name@devnum[,func]" */
 193static void __init pci_path_component(struct device_node *dp, char *tmp_buf)
 194{
 195        struct linux_prom_pci_registers *regs;
 196        struct property *prop;
 197        unsigned int devfn;
 198
 199        prop = of_find_property(dp, "reg", NULL);
 200        if (!prop)
 201                return;
 202
 203        regs = prop->value;
 204        devfn = (regs->phys_hi >> 8) & 0xff;
 205        if (devfn & 0x07) {
 206                sprintf(tmp_buf, "%s@%x,%x",
 207                        dp->name,
 208                        devfn >> 3,
 209                        devfn & 0x07);
 210        } else {
 211                sprintf(tmp_buf, "%s@%x",
 212                        dp->name,
 213                        devfn >> 3);
 214        }
 215}
 216
 217/* "name@addrhi,addrlo" */
 218static void __init ebus_path_component(struct device_node *dp, char *tmp_buf)
 219{
 220        struct linux_prom_registers *regs;
 221        struct property *prop;
 222
 223        prop = of_find_property(dp, "reg", NULL);
 224        if (!prop)
 225                return;
 226
 227        regs = prop->value;
 228
 229        sprintf(tmp_buf, "%s@%x,%x",
 230                dp->name,
 231                regs->which_io, regs->phys_addr);
 232}
 233
 234static void __init __build_path_component(struct device_node *dp, char *tmp_buf)
 235{
 236        struct device_node *parent = dp->parent;
 237
 238        if (parent != NULL) {
 239                if (!strcmp(parent->type, "pci") ||
 240                    !strcmp(parent->type, "pciex"))
 241                        return pci_path_component(dp, tmp_buf);
 242                if (!strcmp(parent->type, "sbus"))
 243                        return sbus_path_component(dp, tmp_buf);
 244                if (!strcmp(parent->type, "ebus"))
 245                        return ebus_path_component(dp, tmp_buf);
 246
 247                /* "isa" is handled with platform naming */
 248        }
 249
 250        /* Use platform naming convention.  */
 251        return sparc32_path_component(dp, tmp_buf);
 252}
 253
 254static char * __init build_path_component(struct device_node *dp)
 255{
 256        char tmp_buf[64], *n;
 257
 258        tmp_buf[0] = '\0';
 259        __build_path_component(dp, tmp_buf);
 260        if (tmp_buf[0] == '\0')
 261                strcpy(tmp_buf, dp->name);
 262
 263        n = prom_early_alloc(strlen(tmp_buf) + 1);
 264        strcpy(n, tmp_buf);
 265
 266        return n;
 267}
 268
 269static char * __init build_full_name(struct device_node *dp)
 270{
 271        int len, ourlen, plen;
 272        char *n;
 273
 274        plen = strlen(dp->parent->full_name);
 275        ourlen = strlen(dp->path_component_name);
 276        len = ourlen + plen + 2;
 277
 278        n = prom_early_alloc(len);
 279        strcpy(n, dp->parent->full_name);
 280        if (!is_root_node(dp->parent)) {
 281                strcpy(n + plen, "/");
 282                plen++;
 283        }
 284        strcpy(n + plen, dp->path_component_name);
 285
 286        return n;
 287}
 288
 289static unsigned int unique_id;
 290
 291static struct property * __init build_one_prop(phandle node, char *prev, char *special_name, void *special_val, int special_len)
 292{
 293        static struct property *tmp = NULL;
 294        struct property *p;
 295        int len;
 296        const char *name;
 297
 298        if (tmp) {
 299                p = tmp;
 300                memset(p, 0, sizeof(*p) + 32);
 301                tmp = NULL;
 302        } else {
 303                p = prom_early_alloc(sizeof(struct property) + 32);
 304                p->unique_id = unique_id++;
 305        }
 306
 307        p->name = (char *) (p + 1);
 308        if (special_name) {
 309                strcpy(p->name, special_name);
 310                p->length = special_len;
 311                p->value = prom_early_alloc(special_len);
 312                memcpy(p->value, special_val, special_len);
 313        } else {
 314                if (prev == NULL) {
 315                        name = prom_firstprop(node, NULL);
 316                } else {
 317                        name = prom_nextprop(node, prev, NULL);
 318                }
 319                if (strlen(name) == 0) {
 320                        tmp = p;
 321                        return NULL;
 322                }
 323                strcpy(p->name, name);
 324                p->length = prom_getproplen(node, p->name);
 325                if (p->length <= 0) {
 326                        p->length = 0;
 327                } else {
 328                        p->value = prom_early_alloc(p->length + 1);
 329                        len = prom_getproperty(node, p->name, p->value,
 330                                               p->length);
 331                        if (len <= 0)
 332                                p->length = 0;
 333                        ((unsigned char *)p->value)[p->length] = '\0';
 334                }
 335        }
 336        return p;
 337}
 338
 339static struct property * __init build_prop_list(phandle node)
 340{
 341        struct property *head, *tail;
 342
 343        head = tail = build_one_prop(node, NULL,
 344                                     ".node", &node, sizeof(node));
 345
 346        tail->next = build_one_prop(node, NULL, NULL, NULL, 0);
 347        tail = tail->next;
 348        while(tail) {
 349                tail->next = build_one_prop(node, tail->name,
 350                                            NULL, NULL, 0);
 351                tail = tail->next;
 352        }
 353
 354        return head;
 355}
 356
 357static char * __init get_one_property(phandle node, char *name)
 358{
 359        char *buf = "<NULL>";
 360        int len;
 361
 362        len = prom_getproplen(node, name);
 363        if (len > 0) {
 364                buf = prom_early_alloc(len);
 365                len = prom_getproperty(node, name, buf, len);
 366        }
 367
 368        return buf;
 369}
 370
 371static struct device_node * __init create_node(phandle node)
 372{
 373        struct device_node *dp;
 374
 375        if (!node)
 376                return NULL;
 377
 378        dp = prom_early_alloc(sizeof(*dp));
 379        dp->unique_id = unique_id++;
 380
 381        kref_init(&dp->kref);
 382
 383        dp->name = get_one_property(node, "name");
 384        dp->type = get_one_property(node, "device_type");
 385        dp->node = node;
 386
 387        /* Build interrupts later... */
 388
 389        dp->properties = build_prop_list(node);
 390
 391        return dp;
 392}
 393
 394static struct device_node * __init build_tree(struct device_node *parent, phandle node, struct device_node ***nextp)
 395{
 396        struct device_node *dp;
 397
 398        dp = create_node(node);
 399        if (dp) {
 400                *(*nextp) = dp;
 401                *nextp = &dp->allnext;
 402
 403                dp->parent = parent;
 404                dp->path_component_name = build_path_component(dp);
 405                dp->full_name = build_full_name(dp);
 406
 407                dp->child = build_tree(dp, prom_getchild(node), nextp);
 408
 409                dp->sibling = build_tree(parent, prom_getsibling(node), nextp);
 410        }
 411
 412        return dp;
 413}
 414
 415struct device_node *of_console_device;
 416EXPORT_SYMBOL(of_console_device);
 417
 418char *of_console_path;
 419EXPORT_SYMBOL(of_console_path);
 420
 421char *of_console_options;
 422EXPORT_SYMBOL(of_console_options);
 423
 424extern void restore_current(void);
 425
 426static void __init of_console_init(void)
 427{
 428        char *msg = "OF stdout device is: %s\n";
 429        struct device_node *dp;
 430        unsigned long flags;
 431        const char *type;
 432        phandle node;
 433        int skip, tmp, fd;
 434
 435        of_console_path = prom_early_alloc(256);
 436
 437        switch (prom_vers) {
 438        case PROM_V0:
 439        case PROM_SUN4:
 440                skip = 0;
 441                switch (*romvec->pv_stdout) {
 442                case PROMDEV_SCREEN:
 443                        type = "display";
 444                        break;
 445
 446                case PROMDEV_TTYB:
 447                        skip = 1;
 448                        /* FALLTHRU */
 449
 450                case PROMDEV_TTYA:
 451                        type = "serial";
 452                        break;
 453
 454                default:
 455                        prom_printf("Invalid PROM_V0 stdout value %u\n",
 456                                    *romvec->pv_stdout);
 457                        prom_halt();
 458                }
 459
 460                tmp = skip;
 461                for_each_node_by_type(dp, type) {
 462                        if (!tmp--)
 463                                break;
 464                }
 465                if (!dp) {
 466                        prom_printf("Cannot find PROM_V0 console node.\n");
 467                        prom_halt();
 468                }
 469                of_console_device = dp;
 470
 471                strcpy(of_console_path, dp->full_name);
 472                if (!strcmp(type, "serial")) {
 473                        strcat(of_console_path,
 474                               (skip ? ":b" : ":a"));
 475                }
 476                break;
 477
 478        default:
 479        case PROM_V2:
 480        case PROM_V3:
 481                fd = *romvec->pv_v2bootargs.fd_stdout;
 482
 483                spin_lock_irqsave(&prom_lock, flags);
 484                node = (*romvec->pv_v2devops.v2_inst2pkg)(fd);
 485                restore_current();
 486                spin_unlock_irqrestore(&prom_lock, flags);
 487
 488                if (!node) {
 489                        prom_printf("Cannot resolve stdout node from "
 490                                    "instance %08x.\n", fd);
 491                        prom_halt();
 492                }
 493                dp = of_find_node_by_phandle(node);
 494                type = of_get_property(dp, "device_type", NULL);
 495
 496                if (!type) {
 497                        prom_printf("Console stdout lacks "
 498                                    "device_type property.\n");
 499                        prom_halt();
 500                }
 501
 502                if (strcmp(type, "display") && strcmp(type, "serial")) {
 503                        prom_printf("Console device_type is neither display "
 504                                    "nor serial.\n");
 505                        prom_halt();
 506                }
 507
 508                of_console_device = dp;
 509
 510                if (prom_vers == PROM_V2) {
 511                        strcpy(of_console_path, dp->full_name);
 512                        switch (*romvec->pv_stdout) {
 513                        case PROMDEV_TTYA:
 514                                strcat(of_console_path, ":a");
 515                                break;
 516                        case PROMDEV_TTYB:
 517                                strcat(of_console_path, ":b");
 518                                break;
 519                        }
 520                } else {
 521                        const char *path;
 522
 523                        dp = of_find_node_by_path("/");
 524                        path = of_get_property(dp, "stdout-path", NULL);
 525                        if (!path) {
 526                                prom_printf("No stdout-path in root node.\n");
 527                                prom_halt();
 528                        }
 529                        strcpy(of_console_path, path);
 530                }
 531                break;
 532        }
 533
 534        of_console_options = strrchr(of_console_path, ':');
 535        if (of_console_options) {
 536                of_console_options++;
 537                if (*of_console_options == '\0')
 538                        of_console_options = NULL;
 539        }
 540
 541        prom_printf(msg, of_console_path);
 542        printk(msg, of_console_path);
 543}
 544
 545void __init prom_build_devicetree(void)
 546{
 547        struct device_node **nextp;
 548
 549        allnodes = create_node(prom_root_node);
 550        allnodes->path_component_name = "";
 551        allnodes->full_name = "/";
 552
 553        nextp = &allnodes->allnext;
 554        allnodes->child = build_tree(allnodes,
 555                                     prom_getchild(allnodes->node),
 556                                     &nextp);
 557        of_console_init();
 558
 559        printk("PROM: Built device tree with %u bytes of memory.\n",
 560               prom_early_allocated);
 561}
 562