linux-old/arch/ppc/kernel/prom.c
<<
>>
Prefs
   1/*
   2 * Procedures for interfacing to the Open Firmware PROM on
   3 * Power Macintosh computers.
   4 *
   5 * In particular, we are interested in the device tree
   6 * and in using some of its services (exit, write to stdout).
   7 *
   8 * Paul Mackerras       August 1996.
   9 * Copyright (C) 1996 Paul Mackerras.
  10 */
  11#include <linux/config.h>
  12#include <linux/kernel.h>
  13#include <linux/string.h>
  14#include <linux/init.h>
  15#include <linux/version.h>
  16#include <linux/threads.h>
  17#include <linux/spinlock.h>
  18#include <linux/pci.h>
  19#include <linux/slab.h>
  20
  21#include <asm/sections.h>
  22#include <asm/prom.h>
  23#include <asm/page.h>
  24#include <asm/processor.h>
  25#include <asm/irq.h>
  26#include <asm/open_pic.h>
  27#include <asm/system.h>
  28#include <asm/btext.h>
  29#include <asm/pci-bridge.h>
  30
  31struct pci_address {
  32        unsigned a_hi;
  33        unsigned a_mid;
  34        unsigned a_lo;
  35};
  36
  37struct pci_reg_property {
  38        struct pci_address addr;
  39        unsigned size_hi;
  40        unsigned size_lo;
  41};
  42
  43struct isa_reg_property {
  44        unsigned space;
  45        unsigned address;
  46        unsigned size;
  47};
  48
  49typedef unsigned long interpret_func(struct device_node *, unsigned long,
  50                                     int, int);
  51static interpret_func interpret_pci_props;
  52static interpret_func interpret_dbdma_props;
  53static interpret_func interpret_isa_props;
  54static interpret_func interpret_macio_props;
  55static interpret_func interpret_root_props;
  56
  57extern char *klimit;
  58
  59/* Set for a newworld or CHRP machine */
  60int use_of_interrupt_tree;
  61struct device_node *dflt_interrupt_controller;
  62int num_interrupt_controllers;
  63
  64int pmac_newworld;
  65
  66extern unsigned int rtas_entry;  /* physical pointer */
  67
  68extern struct device_node *allnodes;
  69
  70static unsigned long finish_node(struct device_node *, unsigned long,
  71                                 interpret_func *, int, int);
  72static unsigned long finish_node_interrupts(struct device_node *, unsigned long);
  73
  74extern void enter_rtas(void *);
  75void phys_call_rtas(int, int, int, ...);
  76
  77extern char cmd_line[512];      /* XXX */
  78extern boot_infos_t *boot_infos;
  79unsigned long dev_tree_size;
  80
  81void __openfirmware
  82phys_call_rtas(int service, int nargs, int nret, ...)
  83{
  84        va_list list;
  85        union {
  86                unsigned long words[16];
  87                double align;
  88        } u;
  89        void (*rtas)(void *, unsigned long);
  90        int i;
  91
  92        u.words[0] = service;
  93        u.words[1] = nargs;
  94        u.words[2] = nret;
  95        va_start(list, nret);
  96        for (i = 0; i < nargs; ++i)
  97                u.words[i+3] = va_arg(list, unsigned long);
  98        va_end(list);
  99
 100        rtas = (void (*)(void *, unsigned long)) rtas_entry;
 101        rtas(&u, rtas_data);
 102}
 103
 104/*
 105 * finish_device_tree is called once things are running normally
 106 * (i.e. with text and data mapped to the address they were linked at).
 107 * It traverses the device tree and fills in the name, type,
 108 * {n_}addrs and {n_}intrs fields of each node.
 109 */
 110void __init
 111finish_device_tree(void)
 112{
 113        unsigned long mem = (unsigned long) klimit;
 114        struct device_node *np;
 115
 116        /* All newworld pmac machines and CHRPs now use the interrupt tree */
 117        for (np = allnodes; np != NULL; np = np->allnext) {
 118                if (get_property(np, "interrupt-parent", 0)) {
 119                        use_of_interrupt_tree = 1;
 120                        break;
 121                }
 122        }
 123        if (_machine == _MACH_Pmac && use_of_interrupt_tree)
 124                pmac_newworld = 1;
 125
 126#ifdef CONFIG_BOOTX_TEXT
 127        if (boot_infos && pmac_newworld) {
 128                prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n");
 129                prom_print("          You should use an Open Firmware bootloader\n");
 130        }
 131#endif /* CONFIG_BOOTX_TEXT */
 132
 133        if (use_of_interrupt_tree) {
 134                /*
 135                 * We want to find out here how many interrupt-controller
 136                 * nodes there are, and if we are booted from BootX,
 137                 * we need a pointer to the first (and hopefully only)
 138                 * such node.  But we can't use find_devices here since
 139                 * np->name has not been set yet.  -- paulus
 140                 */
 141                int n = 0;
 142                char *name, *ic;
 143                int iclen;
 144
 145                for (np = allnodes; np != NULL; np = np->allnext) {
 146                        ic = get_property(np, "interrupt-controller", &iclen);
 147                        name = get_property(np, "name", NULL);
 148                        /* checking iclen makes sure we don't get a false
 149                           match on /chosen.interrupt_controller */
 150                        if ((name != NULL
 151                             && strcmp(name, "interrupt-controller") == 0)
 152                            || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) {
 153                                if (n == 0)
 154                                        dflt_interrupt_controller = np;
 155                                ++n;
 156                        }
 157                }
 158                num_interrupt_controllers = n;
 159        }
 160
 161        mem = finish_node(allnodes, mem, NULL, 1, 1);
 162        dev_tree_size = mem - (unsigned long) allnodes;
 163        klimit = (char *) mem;
 164}
 165
 166static unsigned long __init
 167finish_node(struct device_node *np, unsigned long mem_start,
 168            interpret_func *ifunc, int naddrc, int nsizec)
 169{
 170        struct device_node *child;
 171        int *ip;
 172
 173        np->name = get_property(np, "name", 0);
 174        np->type = get_property(np, "device_type", 0);
 175
 176        if (!np->name)
 177                np->name = "<NULL>";
 178        if (!np->type)
 179                np->type = "<NULL>";
 180
 181        /* get the device addresses and interrupts */
 182        if (ifunc != NULL)
 183                mem_start = ifunc(np, mem_start, naddrc, nsizec);
 184
 185        if (use_of_interrupt_tree)
 186                mem_start = finish_node_interrupts(np, mem_start);
 187
 188        /* Look for #address-cells and #size-cells properties. */
 189        ip = (int *) get_property(np, "#address-cells", 0);
 190        if (ip != NULL)
 191                naddrc = *ip;
 192        ip = (int *) get_property(np, "#size-cells", 0);
 193        if (ip != NULL)
 194                nsizec = *ip;
 195
 196        if (np->parent == NULL)
 197                ifunc = interpret_root_props;
 198        else if (np->type == 0)
 199                ifunc = NULL;
 200        else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci"))
 201                ifunc = interpret_pci_props;
 202        else if (!strcmp(np->type, "dbdma"))
 203                ifunc = interpret_dbdma_props;
 204        else if (!strcmp(np->type, "mac-io")
 205                 || ifunc == interpret_macio_props)
 206                ifunc = interpret_macio_props;
 207        else if (!strcmp(np->type, "isa"))
 208                ifunc = interpret_isa_props;
 209        else if (!strcmp(np->name, "uni-n"))
 210                ifunc = interpret_root_props;
 211        else if (!((ifunc == interpret_dbdma_props
 212                    || ifunc == interpret_macio_props)
 213                   && (!strcmp(np->type, "escc")
 214                       || !strcmp(np->type, "media-bay"))))
 215                ifunc = NULL;
 216
 217        /* if we were booted from BootX, convert the full name */
 218        if (boot_infos
 219            && strncmp(np->full_name, "Devices:device-tree", 19) == 0) {
 220                if (np->full_name[19] == 0) {
 221                        strcpy(np->full_name, "/");
 222                } else if (np->full_name[19] == ':') {
 223                        char *p = np->full_name + 19;
 224                        np->full_name = p;
 225                        for (; *p; ++p)
 226                                if (*p == ':')
 227                                        *p = '/';
 228                }
 229        }
 230
 231        for (child = np->child; child != NULL; child = child->sibling)
 232                mem_start = finish_node(child, mem_start, ifunc,
 233                                        naddrc, nsizec);
 234
 235        return mem_start;
 236}
 237
 238/*
 239 * Find the interrupt parent of a node.
 240 */
 241static struct device_node * __init
 242intr_parent(struct device_node *p)
 243{
 244        phandle *parp;
 245
 246        parp = (phandle *) get_property(p, "interrupt-parent", NULL);
 247        if (parp == NULL)
 248                return p->parent;
 249        p = find_phandle(*parp);
 250        if (p != NULL)
 251                return p;
 252        /*
 253         * On a powermac booted with BootX, we don't get to know the
 254         * phandles for any nodes, so find_phandle will return NULL.
 255         * Fortunately these machines only have one interrupt controller
 256         * so there isn't in fact any ambiguity.  -- paulus
 257         */
 258        if (num_interrupt_controllers == 1)
 259                p = dflt_interrupt_controller;
 260        return p;
 261}
 262
 263/*
 264 * Find out the size of each entry of the interrupts property
 265 * for a node.
 266 */
 267static int __init
 268prom_n_intr_cells(struct device_node *np)
 269{
 270        struct device_node *p;
 271        unsigned int *icp;
 272
 273        for (p = np; (p = intr_parent(p)) != NULL; ) {
 274                icp = (unsigned int *)
 275                        get_property(p, "#interrupt-cells", NULL);
 276                if (icp != NULL)
 277                        return *icp;
 278                if (get_property(p, "interrupt-controller", NULL) != NULL
 279                    || get_property(p, "interrupt-map", NULL) != NULL) {
 280                        printk("oops, node %s doesn't have #interrupt-cells\n",
 281                               p->full_name);
 282                        return 1;
 283                }
 284        }
 285        printk("prom_n_intr_cells failed for %s\n", np->full_name);
 286        return 1;
 287}
 288
 289/*
 290 * Map an interrupt from a device up to the platform interrupt
 291 * descriptor.
 292 */
 293static int __init
 294map_interrupt(unsigned int **irq, struct device_node **ictrler,
 295              struct device_node *np, unsigned int *ints, int nintrc)
 296{
 297        struct device_node *p, *ipar;
 298        unsigned int *imap, *imask, *ip;
 299        int i, imaplen, match;
 300        int newintrc, newaddrc;
 301        unsigned int *reg;
 302        int naddrc;
 303
 304        reg = (unsigned int *) get_property(np, "reg", NULL);
 305        naddrc = prom_n_addr_cells(np);
 306        p = intr_parent(np);
 307        while (p != NULL) {
 308                if (get_property(p, "interrupt-controller", NULL) != NULL)
 309                        /* this node is an interrupt controller, stop here */
 310                        break;
 311                imap = (unsigned int *)
 312                        get_property(p, "interrupt-map", &imaplen);
 313                if (imap == NULL) {
 314                        p = intr_parent(p);
 315                        continue;
 316                }
 317                imask = (unsigned int *)
 318                        get_property(p, "interrupt-map-mask", NULL);
 319                if (imask == NULL) {
 320                        printk("oops, %s has interrupt-map but no mask\n",
 321                               p->full_name);
 322                        return 0;
 323                }
 324                imaplen /= sizeof(unsigned int);
 325                match = 0;
 326                ipar = NULL;
 327                while (imaplen > 0 && !match) {
 328                        /* check the child-interrupt field */
 329                        match = 1;
 330                        for (i = 0; i < naddrc && match; ++i)
 331                                match = ((reg[i] ^ imap[i]) & imask[i]) == 0;
 332                        for (; i < naddrc + nintrc && match; ++i)
 333                                match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0;
 334                        imap += naddrc + nintrc;
 335                        imaplen -= naddrc + nintrc;
 336                        /* grab the interrupt parent */
 337                        ipar = find_phandle((phandle) *imap++);
 338                        --imaplen;
 339                        if (ipar == NULL && num_interrupt_controllers == 1)
 340                                /* cope with BootX not giving us phandles */
 341                                ipar = dflt_interrupt_controller;
 342                        if (ipar == NULL) {
 343                                printk("oops, no int parent %x in map of %s\n",
 344                                       imap[-1], p->full_name);
 345                                return 0;
 346                        }
 347                        /* find the parent's # addr and intr cells */
 348                        ip = (unsigned int *)
 349                                get_property(ipar, "#interrupt-cells", NULL);
 350                        if (ip == NULL) {
 351                                printk("oops, no #interrupt-cells on %s\n",
 352                                       ipar->full_name);
 353                                return 0;
 354                        }
 355                        newintrc = *ip;
 356                        ip = (unsigned int *)
 357                                get_property(ipar, "#address-cells", NULL);
 358                        newaddrc = (ip == NULL)? 0: *ip;
 359                        imap += newaddrc + newintrc;
 360                        imaplen -= newaddrc + newintrc;
 361                }
 362                if (imaplen < 0) {
 363                        printk("oops, error decoding int-map on %s, len=%d\n",
 364                               p->full_name, imaplen);
 365                        return 0;
 366                }
 367                if (!match) {
 368                        printk("oops, no match in %s int-map for %s\n",
 369                               p->full_name, np->full_name);
 370                        return 0;
 371                }
 372                p = ipar;
 373                naddrc = newaddrc;
 374                nintrc = newintrc;
 375                ints = imap - nintrc;
 376                reg = ints - naddrc;
 377        }
 378        if (p == NULL)
 379                printk("hmmm, int tree for %s doesn't have ctrler\n",
 380                       np->full_name);
 381        *irq = ints;
 382        *ictrler = p;
 383        return nintrc;
 384}
 385
 386/*
 387 * New version of finish_node_interrupts.
 388 */
 389static unsigned long __init
 390finish_node_interrupts(struct device_node *np, unsigned long mem_start)
 391{
 392        unsigned int *ints;
 393        int intlen, intrcells;
 394        int i, j, n, offset;
 395        unsigned int *irq;
 396        struct device_node *ic;
 397
 398        ints = (unsigned int *) get_property(np, "interrupts", &intlen);
 399        if (ints == NULL)
 400                return mem_start;
 401        intrcells = prom_n_intr_cells(np);
 402        intlen /= intrcells * sizeof(unsigned int);
 403        np->n_intrs = intlen;
 404        np->intrs = (struct interrupt_info *) mem_start;
 405        mem_start += intlen * sizeof(struct interrupt_info);
 406
 407        for (i = 0; i < intlen; ++i) {
 408                np->intrs[i].line = 0;
 409                np->intrs[i].sense = 1;
 410                n = map_interrupt(&irq, &ic, np, ints, intrcells);
 411                if (n <= 0)
 412                        continue;
 413                offset = 0;
 414                /*
 415                 * On a CHRP we have an 8259 which is subordinate to
 416                 * the openpic in the interrupt tree, but we want the
 417                 * openpic's interrupt numbers offsetted, not the 8259's.
 418                 * So we apply the offset if the controller is at the
 419                 * root of the interrupt tree, i.e. has no interrupt-parent.
 420                 * This doesn't cope with the general case of multiple
 421                 * cascaded interrupt controllers, but then neither will
 422                 * irq.c at the moment either.  -- paulus
 423                 */
 424                if (num_interrupt_controllers > 1 && ic != NULL
 425                    && get_property(ic, "interrupt-parent", NULL) == NULL)
 426                        offset = 16;
 427                np->intrs[i].line = irq[0] + offset;
 428                if (n > 1)
 429                        np->intrs[i].sense = irq[1];
 430                if (n > 2) {
 431                        printk("hmmm, got %d intr cells for %s:", n,
 432                               np->full_name);
 433                        for (j = 0; j < n; ++j)
 434                                printk(" %d", irq[j]);
 435                        printk("\n");
 436                }
 437                ints += intrcells;
 438        }
 439
 440        return mem_start;
 441}
 442
 443/*
 444 * When BootX makes a copy of the device tree from the MacOS
 445 * Name Registry, it is in the format we use but all of the pointers
 446 * are offsets from the start of the tree.
 447 * This procedure updates the pointers.
 448 */
 449void __init
 450relocate_nodes(void)
 451{
 452        unsigned long base;
 453        struct device_node *np;
 454        struct property *pp;
 455
 456#define ADDBASE(x)      (x = (x)? ((typeof (x))((unsigned long)(x) + base)): 0)
 457
 458        base = (unsigned long) boot_infos + boot_infos->deviceTreeOffset;
 459        allnodes = (struct device_node *)(base + 4);
 460        for (np = allnodes; np != 0; np = np->allnext) {
 461                ADDBASE(np->full_name);
 462                ADDBASE(np->properties);
 463                ADDBASE(np->parent);
 464                ADDBASE(np->child);
 465                ADDBASE(np->sibling);
 466                ADDBASE(np->allnext);
 467                for (pp = np->properties; pp != 0; pp = pp->next) {
 468                        ADDBASE(pp->name);
 469                        ADDBASE(pp->value);
 470                        ADDBASE(pp->next);
 471                }
 472        }
 473}
 474
 475int
 476prom_n_addr_cells(struct device_node* np)
 477{
 478        int* ip;
 479        do {
 480                if (np->parent)
 481                        np = np->parent;
 482                ip = (int *) get_property(np, "#address-cells", 0);
 483                if (ip != NULL)
 484                        return *ip;
 485        } while (np->parent);
 486        /* No #address-cells property for the root node, default to 1 */
 487        return 1;
 488}
 489
 490int
 491prom_n_size_cells(struct device_node* np)
 492{
 493        int* ip;
 494        do {
 495                if (np->parent)
 496                        np = np->parent;
 497                ip = (int *) get_property(np, "#size-cells", 0);
 498                if (ip != NULL)
 499                        return *ip;
 500        } while (np->parent);
 501        /* No #size-cells property for the root node, default to 1 */
 502        return 1;
 503}
 504
 505static unsigned long __init
 506map_addr(struct device_node *np, unsigned long space, unsigned long addr)
 507{
 508        int na;
 509        unsigned int *ranges;
 510        int rlen = 0;
 511        unsigned int type;
 512
 513        type = (space >> 24) & 3;
 514        if (type == 0)
 515                return addr;
 516
 517        while ((np = np->parent) != NULL) {
 518                if (strcmp(np->type, "pci") != 0)
 519                        continue;
 520                /* PCI bridge: map the address through the ranges property */
 521                na = prom_n_addr_cells(np);
 522                ranges = (unsigned int *) get_property(np, "ranges", &rlen);
 523                while ((rlen -= (na + 5) * sizeof(unsigned int)) >= 0) {
 524                        if (((ranges[0] >> 24) & 3) == type
 525                            && ranges[2] <= addr
 526                            && addr - ranges[2] < ranges[na+4]) {
 527                                /* ok, this matches, translate it */
 528                                addr += ranges[na+2] - ranges[2];
 529                                break;
 530                        }
 531                        ranges += na + 5;
 532                }
 533        }
 534        return addr;
 535}
 536
 537static unsigned long __init
 538interpret_pci_props(struct device_node *np, unsigned long mem_start,
 539                    int naddrc, int nsizec)
 540{
 541        struct address_range *adr;
 542        struct pci_reg_property *pci_addrs;
 543        int i, l, *ip;
 544
 545        pci_addrs = (struct pci_reg_property *)
 546                get_property(np, "assigned-addresses", &l);
 547        if (pci_addrs != 0 && l >= sizeof(struct pci_reg_property)) {
 548                i = 0;
 549                adr = (struct address_range *) mem_start;
 550                while ((l -= sizeof(struct pci_reg_property)) >= 0) {
 551                        adr[i].space = pci_addrs[i].addr.a_hi;
 552                        adr[i].address = map_addr(np, pci_addrs[i].addr.a_hi,
 553                                                  pci_addrs[i].addr.a_lo);
 554                        adr[i].size = pci_addrs[i].size_lo;
 555                        ++i;
 556                }
 557                np->addrs = adr;
 558                np->n_addrs = i;
 559                mem_start += i * sizeof(struct address_range);
 560        }
 561
 562        if (use_of_interrupt_tree)
 563                return mem_start;
 564
 565        ip = (int *) get_property(np, "AAPL,interrupts", &l);
 566        if (ip == 0 && np->parent)
 567                ip = (int *) get_property(np->parent, "AAPL,interrupts", &l);
 568        if (ip == 0)
 569                ip = (int *) get_property(np, "interrupts", &l);
 570        if (ip != 0) {
 571                np->intrs = (struct interrupt_info *) mem_start;
 572                np->n_intrs = l / sizeof(int);
 573                mem_start += np->n_intrs * sizeof(struct interrupt_info);
 574                for (i = 0; i < np->n_intrs; ++i) {
 575                        np->intrs[i].line = *ip++;
 576                        np->intrs[i].sense = 1;
 577                }
 578        }
 579
 580        return mem_start;
 581}
 582
 583static unsigned long __init
 584interpret_dbdma_props(struct device_node *np, unsigned long mem_start,
 585                      int naddrc, int nsizec)
 586{
 587        struct reg_property *rp;
 588        struct address_range *adr;
 589        unsigned long base_address;
 590        int i, l, *ip;
 591        struct device_node *db;
 592
 593        base_address = 0;
 594        for (db = np->parent; db != NULL; db = db->parent) {
 595                if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) {
 596                        base_address = db->addrs[0].address;
 597                        break;
 598                }
 599        }
 600
 601        rp = (struct reg_property *) get_property(np, "reg", &l);
 602        if (rp != 0 && l >= sizeof(struct reg_property)) {
 603                i = 0;
 604                adr = (struct address_range *) mem_start;
 605                while ((l -= sizeof(struct reg_property)) >= 0) {
 606                        adr[i].space = 2;
 607                        adr[i].address = rp[i].address + base_address;
 608                        adr[i].size = rp[i].size;
 609                        ++i;
 610                }
 611                np->addrs = adr;
 612                np->n_addrs = i;
 613                mem_start += i * sizeof(struct address_range);
 614        }
 615
 616        if (use_of_interrupt_tree)
 617                return mem_start;
 618
 619        ip = (int *) get_property(np, "AAPL,interrupts", &l);
 620        if (ip == 0)
 621                ip = (int *) get_property(np, "interrupts", &l);
 622        if (ip != 0) {
 623                np->intrs = (struct interrupt_info *) mem_start;
 624                np->n_intrs = l / sizeof(int);
 625                mem_start += np->n_intrs * sizeof(struct interrupt_info);
 626                for (i = 0; i < np->n_intrs; ++i) {
 627                        np->intrs[i].line = *ip++;
 628                        np->intrs[i].sense = 1;
 629                }
 630        }
 631
 632        return mem_start;
 633}
 634
 635static unsigned long __init
 636interpret_macio_props(struct device_node *np, unsigned long mem_start,
 637                      int naddrc, int nsizec)
 638{
 639        struct reg_property *rp;
 640        struct address_range *adr;
 641        unsigned long base_address;
 642        int i, l, *ip;
 643        struct device_node *db;
 644
 645        base_address = 0;
 646        for (db = np->parent; db != NULL; db = db->parent) {
 647                if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) {
 648                        base_address = db->addrs[0].address;
 649                        break;
 650                }
 651        }
 652
 653        rp = (struct reg_property *) get_property(np, "reg", &l);
 654        if (rp != 0 && l >= sizeof(struct reg_property)) {
 655                i = 0;
 656                adr = (struct address_range *) mem_start;
 657                while ((l -= sizeof(struct reg_property)) >= 0) {
 658                        adr[i].space = 2;
 659                        adr[i].address = rp[i].address + base_address;
 660                        adr[i].size = rp[i].size;
 661                        ++i;
 662                }
 663                np->addrs = adr;
 664                np->n_addrs = i;
 665                mem_start += i * sizeof(struct address_range);
 666        }
 667
 668        if (use_of_interrupt_tree)
 669                return mem_start;
 670
 671        ip = (int *) get_property(np, "interrupts", &l);
 672        if (ip == 0)
 673                ip = (int *) get_property(np, "AAPL,interrupts", &l);
 674        if (ip != 0) {
 675                np->intrs = (struct interrupt_info *) mem_start;
 676                np->n_intrs = l / sizeof(int);
 677                for (i = 0; i < np->n_intrs; ++i) {
 678                        np->intrs[i].line = *ip++;
 679                        np->intrs[i].sense = 1;
 680                }
 681                mem_start += np->n_intrs * sizeof(struct interrupt_info);
 682        }
 683
 684        return mem_start;
 685}
 686
 687static unsigned long __init
 688interpret_isa_props(struct device_node *np, unsigned long mem_start,
 689                    int naddrc, int nsizec)
 690{
 691        struct isa_reg_property *rp;
 692        struct address_range *adr;
 693        int i, l, *ip;
 694
 695        rp = (struct isa_reg_property *) get_property(np, "reg", &l);
 696        if (rp != 0 && l >= sizeof(struct isa_reg_property)) {
 697                i = 0;
 698                adr = (struct address_range *) mem_start;
 699                while ((l -= sizeof(struct reg_property)) >= 0) {
 700                        adr[i].space = rp[i].space;
 701                        adr[i].address = rp[i].address
 702                                + (adr[i].space? 0: _ISA_MEM_BASE);
 703                        adr[i].size = rp[i].size;
 704                        ++i;
 705                }
 706                np->addrs = adr;
 707                np->n_addrs = i;
 708                mem_start += i * sizeof(struct address_range);
 709        }
 710
 711        if (use_of_interrupt_tree)
 712                return mem_start;
 713
 714        ip = (int *) get_property(np, "interrupts", &l);
 715        if (ip != 0) {
 716                np->intrs = (struct interrupt_info *) mem_start;
 717                np->n_intrs = l / (2 * sizeof(int));
 718                mem_start += np->n_intrs * sizeof(struct interrupt_info);
 719                for (i = 0; i < np->n_intrs; ++i) {
 720                        np->intrs[i].line = *ip++;
 721                        np->intrs[i].sense = *ip++;
 722                }
 723        }
 724
 725        return mem_start;
 726}
 727
 728static unsigned long __init
 729interpret_root_props(struct device_node *np, unsigned long mem_start,
 730                     int naddrc, int nsizec)
 731{
 732        struct address_range *adr;
 733        int i, l, *ip;
 734        unsigned int *rp;
 735        int rpsize = (naddrc + nsizec) * sizeof(unsigned int);
 736
 737        rp = (unsigned int *) get_property(np, "reg", &l);
 738        if (rp != 0 && l >= rpsize) {
 739                i = 0;
 740                adr = (struct address_range *) mem_start;
 741                while ((l -= rpsize) >= 0) {
 742                        adr[i].space = (naddrc >= 2? rp[naddrc-2]: 2);
 743                        adr[i].address = rp[naddrc - 1];
 744                        adr[i].size = rp[naddrc + nsizec - 1];
 745                        ++i;
 746                        rp += naddrc + nsizec;
 747                }
 748                np->addrs = adr;
 749                np->n_addrs = i;
 750                mem_start += i * sizeof(struct address_range);
 751        }
 752
 753        if (use_of_interrupt_tree)
 754                return mem_start;
 755
 756        ip = (int *) get_property(np, "AAPL,interrupts", &l);
 757        if (ip == 0)
 758                ip = (int *) get_property(np, "interrupts", &l);
 759        if (ip != 0) {
 760                np->intrs = (struct interrupt_info *) mem_start;
 761                np->n_intrs = l / sizeof(int);
 762                mem_start += np->n_intrs * sizeof(struct interrupt_info);
 763                for (i = 0; i < np->n_intrs; ++i) {
 764                        np->intrs[i].line = *ip++;
 765                        np->intrs[i].sense = 1;
 766                }
 767        }
 768
 769        return mem_start;
 770}
 771
 772/*
 773 * Work out the sense (active-low level / active-high edge)
 774 * of each interrupt from the device tree.
 775 */
 776void __init
 777prom_get_irq_senses(unsigned char *senses, int off, int max)
 778{
 779        struct device_node *np;
 780        int i, j;
 781
 782        /* default to level-triggered */
 783        memset(senses, 1, max - off);
 784        if (!use_of_interrupt_tree)
 785                return;
 786
 787        for (np = allnodes; np != 0; np = np->allnext) {
 788                for (j = 0; j < np->n_intrs; j++) {
 789                        i = np->intrs[j].line;
 790                        if (i >= off && i < max) {
 791                                if (np->intrs[j].sense == 1) {
 792                                        senses[i-off] = (IRQ_SENSE_LEVEL |
 793                                                        IRQ_POLARITY_NEGATIVE);
 794                                } else {
 795                                        senses[i-off] = (IRQ_SENSE_EDGE |
 796                                                        IRQ_POLARITY_POSITIVE);
 797                                }
 798                        }
 799                }
 800        }
 801}
 802
 803/*
 804 * Construct and return a list of the device_nodes with a given name.
 805 */
 806struct device_node *
 807find_devices(const char *name)
 808{
 809        struct device_node *head, **prevp, *np;
 810
 811        prevp = &head;
 812        for (np = allnodes; np != 0; np = np->allnext) {
 813                if (np->name != 0 && strcasecmp(np->name, name) == 0) {
 814                        *prevp = np;
 815                        prevp = &np->next;
 816                }
 817        }
 818        *prevp = 0;
 819        return head;
 820}
 821
 822/*
 823 * Construct and return a list of the device_nodes with a given type.
 824 */
 825struct device_node *
 826find_type_devices(const char *type)
 827{
 828        struct device_node *head, **prevp, *np;
 829
 830        prevp = &head;
 831        for (np = allnodes; np != 0; np = np->allnext) {
 832                if (np->type != 0 && strcasecmp(np->type, type) == 0) {
 833                        *prevp = np;
 834                        prevp = &np->next;
 835                }
 836        }
 837        *prevp = 0;
 838        return head;
 839}
 840
 841/*
 842 * Returns all nodes linked together
 843 */
 844struct device_node * __openfirmware
 845find_all_nodes(void)
 846{
 847        struct device_node *head, **prevp, *np;
 848
 849        prevp = &head;
 850        for (np = allnodes; np != 0; np = np->allnext) {
 851                *prevp = np;
 852                prevp = &np->next;
 853        }
 854        *prevp = 0;
 855        return head;
 856}
 857
 858/* Checks if the given "compat" string matches one of the strings in
 859 * the device's "compatible" property
 860 */
 861int
 862device_is_compatible(struct device_node *device, const char *compat)
 863{
 864        const char* cp;
 865        int cplen, l;
 866
 867        cp = (char *) get_property(device, "compatible", &cplen);
 868        if (cp == NULL)
 869                return 0;
 870        while (cplen > 0) {
 871                if (strncasecmp(cp, compat, strlen(compat)) == 0)
 872                        return 1;
 873                l = strlen(cp) + 1;
 874                cp += l;
 875                cplen -= l;
 876        }
 877
 878        return 0;
 879}
 880
 881
 882/*
 883 * Indicates whether the root node has a given value in its
 884 * compatible property.
 885 */
 886int
 887machine_is_compatible(const char *compat)
 888{
 889        struct device_node *root;
 890
 891        root = find_path_device("/");
 892        if (root == 0)
 893                return 0;
 894        return device_is_compatible(root, compat);
 895}
 896
 897/*
 898 * Construct and return a list of the device_nodes with a given type
 899 * and compatible property.
 900 */
 901struct device_node *
 902find_compatible_devices(const char *type, const char *compat)
 903{
 904        struct device_node *head, **prevp, *np;
 905
 906        prevp = &head;
 907        for (np = allnodes; np != 0; np = np->allnext) {
 908                if (type != NULL
 909                    && !(np->type != 0 && strcasecmp(np->type, type) == 0))
 910                        continue;
 911                if (device_is_compatible(np, compat)) {
 912                        *prevp = np;
 913                        prevp = &np->next;
 914                }
 915        }
 916        *prevp = 0;
 917        return head;
 918}
 919
 920/*
 921 * Find the device_node with a given full_name.
 922 */
 923struct device_node *
 924find_path_device(const char *path)
 925{
 926        struct device_node *np;
 927
 928        for (np = allnodes; np != 0; np = np->allnext)
 929                if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0)
 930                        return np;
 931        return NULL;
 932}
 933
 934/*
 935 * Find the device_node with a given phandle.
 936 */
 937struct device_node * __init
 938find_phandle(phandle ph)
 939{
 940        struct device_node *np;
 941
 942        for (np = allnodes; np != 0; np = np->allnext)
 943                if (np->node == ph)
 944                        return np;
 945        return NULL;
 946}
 947
 948/*
 949 * Find a property with a given name for a given node
 950 * and return the value.
 951 */
 952unsigned char *
 953get_property(struct device_node *np, const char *name, int *lenp)
 954{
 955        struct property *pp;
 956
 957        for (pp = np->properties; pp != 0; pp = pp->next)
 958                if (pp->name != NULL && strcmp(pp->name, name) == 0) {
 959                        if (lenp != 0)
 960                                *lenp = pp->length;
 961                        return pp->value;
 962                }
 963        return 0;
 964}
 965
 966/*
 967 * Add a property to a node
 968 */
 969void __openfirmware
 970prom_add_property(struct device_node* np, struct property* prop)
 971{
 972        struct property **next = &np->properties;
 973
 974        prop->next = NULL;
 975        while (*next)
 976                next = &(*next)->next;
 977        *next = prop;
 978}
 979
 980/* I quickly hacked that one, check against spec ! */
 981static inline unsigned long __openfirmware
 982bus_space_to_resource_flags(unsigned int bus_space)
 983{
 984        u8 space = (bus_space >> 24) & 0xf;
 985        if (space == 0)
 986                space = 0x02;
 987        if (space == 0x02)
 988                return IORESOURCE_MEM;
 989        else if (space == 0x01)
 990                return IORESOURCE_IO;
 991        else {
 992                printk(KERN_WARNING "prom.c: bus_space_to_resource_flags(), space: %x\n",
 993                        bus_space);
 994                return 0;
 995        }
 996}
 997
 998static struct resource* __openfirmware
 999find_parent_pci_resource(struct pci_dev* pdev, struct address_range *range)
1000{
1001        unsigned long mask;
1002        int i;
1003
1004        /* Check this one */
1005        mask = bus_space_to_resource_flags(range->space);
1006        for (i=0; i<DEVICE_COUNT_RESOURCE; i++) {
1007                if ((pdev->resource[i].flags & mask) == mask &&
1008                        pdev->resource[i].start <= range->address &&
1009                        pdev->resource[i].end > range->address) {
1010                                if ((range->address + range->size - 1) > pdev->resource[i].end) {
1011                                        /* Add better message */
1012                                        printk(KERN_WARNING "PCI/OF resource overlap !\n");
1013                                        return NULL;
1014                                }
1015                                break;
1016                        }
1017        }
1018        if (i == DEVICE_COUNT_RESOURCE)
1019                return NULL;
1020        return &pdev->resource[i];
1021}
1022
1023/*
1024 * Request an OF device resource. Currently handles child of PCI devices,
1025 * or other nodes attached to the root node. Ultimately, put some
1026 * link to resources in the OF node.
1027 * WARNING: out_resource->name should be initialized before calling this
1028 * function.
1029 */
1030struct resource* __openfirmware
1031request_OF_resource(struct device_node* node, int index, const char* name_postfix)
1032{
1033        struct pci_dev* pcidev;
1034        u8 pci_bus, pci_devfn;
1035        unsigned long iomask;
1036        struct device_node* nd;
1037        struct resource* parent;
1038        struct resource *res = NULL;
1039        int nlen, plen;
1040
1041        if (index >= node->n_addrs)
1042                goto fail;
1043
1044        /* Sanity check on bus space */
1045        iomask = bus_space_to_resource_flags(node->addrs[index].space);
1046        if (iomask & IORESOURCE_MEM)
1047                parent = &iomem_resource;
1048        else if (iomask & IORESOURCE_IO)
1049                parent = &ioport_resource;
1050        else
1051                goto fail;
1052
1053        /* Find a PCI parent if any */
1054        nd = node;
1055        pcidev = NULL;
1056        while(nd) {
1057                if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
1058                        pcidev = pci_find_slot(pci_bus, pci_devfn);
1059                if (pcidev) break;
1060                nd = nd->parent;
1061        }
1062        if (pcidev)
1063                parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
1064        if (!parent) {
1065                printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
1066                        node->name);
1067                goto fail;
1068        }
1069
1070        res = __request_region(parent, node->addrs[index].address, node->addrs[index].size, NULL);
1071        if (!res)
1072                goto fail;
1073        nlen = strlen(node->name);
1074        plen = name_postfix ? strlen(name_postfix) : 0;
1075        res->name = (const char *)kmalloc(nlen+plen+1, GFP_KERNEL);
1076        if (res->name) {
1077                strcpy((char *)res->name, node->name);
1078                if (plen)
1079                        strcpy((char *)res->name+nlen, name_postfix);
1080        }
1081        return res;
1082fail:
1083        return NULL;
1084}
1085
1086int __openfirmware
1087release_OF_resource(struct device_node* node, int index)
1088{
1089        struct pci_dev* pcidev;
1090        u8 pci_bus, pci_devfn;
1091        unsigned long iomask;
1092        struct device_node* nd;
1093        struct resource* parent;
1094        struct resource *res = NULL;
1095
1096        if (index >= node->n_addrs)
1097                return -EINVAL;
1098
1099        /* Sanity check on bus space */
1100        iomask = bus_space_to_resource_flags(node->addrs[index].space);
1101        if (iomask & IORESOURCE_MEM)
1102                parent = &iomem_resource;
1103        else if (iomask & IORESOURCE_IO)
1104                parent = &ioport_resource;
1105        else
1106                return -EINVAL;
1107
1108        /* Find a PCI parent if any */
1109        nd = node;
1110        pcidev = NULL;
1111        while(nd) {
1112                if (!pci_device_from_OF_node(nd, &pci_bus, &pci_devfn))
1113                        pcidev = pci_find_slot(pci_bus, pci_devfn);
1114                if (pcidev) break;
1115                nd = nd->parent;
1116        }
1117        if (pcidev)
1118                parent = find_parent_pci_resource(pcidev, &node->addrs[index]);
1119        if (!parent) {
1120                printk(KERN_WARNING "request_OF_resource(%s), parent not found\n",
1121                        node->name);
1122                return -ENODEV;
1123        }
1124
1125        /* Find us in the parent */
1126        res = parent->child;
1127        while (res) {
1128                if (res->start == node->addrs[index].address &&
1129                    res->end == (res->start + node->addrs[index].size - 1))
1130                        break;
1131                res = res->sibling;
1132        }
1133        if (!res)
1134                return -ENODEV;
1135
1136        if (res->name) {
1137                kfree(res->name);
1138                res->name = NULL;
1139        }
1140        release_resource(res);
1141        kfree(res);
1142
1143        return 0;
1144}
1145
1146#if 0
1147void __openfirmware
1148print_properties(struct device_node *np)
1149{
1150        struct property *pp;
1151        char *cp;
1152        int i, n;
1153
1154        for (pp = np->properties; pp != 0; pp = pp->next) {
1155                printk(KERN_INFO "%s", pp->name);
1156                for (i = strlen(pp->name); i < 16; ++i)
1157                        printk(" ");
1158                cp = (char *) pp->value;
1159                for (i = pp->length; i > 0; --i, ++cp)
1160                        if ((i > 1 && (*cp < 0x20 || *cp > 0x7e))
1161                            || (i == 1 && *cp != 0))
1162                                break;
1163                if (i == 0 && pp->length > 1) {
1164                        /* looks like a string */
1165                        printk(" %s\n", (char *) pp->value);
1166                } else {
1167                        /* dump it in hex */
1168                        n = pp->length;
1169                        if (n > 64)
1170                                n = 64;
1171                        if (pp->length % 4 == 0) {
1172                                unsigned int *p = (unsigned int *) pp->value;
1173
1174                                n /= 4;
1175                                for (i = 0; i < n; ++i) {
1176                                        if (i != 0 && (i % 4) == 0)
1177                                                printk("\n                ");
1178                                        printk(" %08x", *p++);
1179                                }
1180                        } else {
1181                                unsigned char *bp = pp->value;
1182
1183                                for (i = 0; i < n; ++i) {
1184                                        if (i != 0 && (i % 16) == 0)
1185                                                printk("\n                ");
1186                                        printk(" %02x", *bp++);
1187                                }
1188                        }
1189                        printk("\n");
1190                        if (pp->length > 64)
1191                                printk("                 ... (length = %d)\n",
1192                                       pp->length);
1193                }
1194        }
1195}
1196#endif
1197
1198static spinlock_t rtas_lock = SPIN_LOCK_UNLOCKED;
1199
1200/* this can be called after setup -- Cort */
1201int __openfirmware
1202call_rtas(const char *service, int nargs, int nret,
1203          unsigned long *outputs, ...)
1204{
1205        va_list list;
1206        int i;
1207        unsigned long s;
1208        struct device_node *rtas;
1209        int *tokp;
1210        union {
1211                unsigned long words[16];
1212                double align;
1213        } u;
1214
1215        rtas = find_devices("rtas");
1216        if (rtas == NULL)
1217                return -1;
1218        tokp = (int *) get_property(rtas, service, NULL);
1219        if (tokp == NULL) {
1220                printk(KERN_ERR "No RTAS service called %s\n", service);
1221                return -1;
1222        }
1223        u.words[0] = *tokp;
1224        u.words[1] = nargs;
1225        u.words[2] = nret;
1226        va_start(list, outputs);
1227        for (i = 0; i < nargs; ++i)
1228                u.words[i+3] = va_arg(list, unsigned long);
1229        va_end(list);
1230
1231        /*
1232         * RTAS doesn't use floating point.
1233         * Or at least, according to the CHRP spec we enter RTAS
1234         * with FP disabled, and it doesn't change the FP registers.
1235         *  -- paulus.
1236         */
1237        spin_lock_irqsave(&rtas_lock, s);
1238        enter_rtas((void *)__pa(&u));
1239        spin_unlock_irqrestore(&rtas_lock, s);
1240
1241        if (nret > 1 && outputs != NULL)
1242                for (i = 0; i < nret-1; ++i)
1243                        outputs[i] = u.words[i+nargs+4];
1244        return u.words[nargs+3];
1245}
1246
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.