The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/powerpc/ofw/ofw_machdep.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * Copyright (C) 1996 Wolfgang Solfrank.
    3  * Copyright (C) 1996 TooLs GmbH.
    4  * All rights reserved.
    5  *
    6  * Redistribution and use in source and binary forms, with or without
    7  * modification, are permitted provided that the following conditions
    8  * are met:
    9  * 1. Redistributions of source code must retain the above copyright
   10  *    notice, this list of conditions and the following disclaimer.
   11  * 2. Redistributions in binary form must reproduce the above copyright
   12  *    notice, this list of conditions and the following disclaimer in the
   13  *    documentation and/or other materials provided with the distribution.
   14  * 3. All advertising materials mentioning features or use of this software
   15  *    must display the following acknowledgement:
   16  *      This product includes software developed by TooLs GmbH.
   17  * 4. The name of TooLs GmbH may not be used to endorse or promote products
   18  *    derived from this software without specific prior written permission.
   19  *
   20  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
   21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   23  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
   26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
   27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
   28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
   29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   30  *
   31  * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $
   32  */
   33 
   34 #include <sys/cdefs.h>
   35 __FBSDID("$FreeBSD: releng/9.0/sys/powerpc/ofw/ofw_machdep.c 222667 2011-06-04 04:00:40Z nwhitehorn $");
   36 
   37 #include <sys/param.h>
   38 #include <sys/bus.h>
   39 #include <sys/systm.h>
   40 #include <sys/conf.h>
   41 #include <sys/disk.h>
   42 #include <sys/fcntl.h>
   43 #include <sys/malloc.h>
   44 #include <sys/smp.h>
   45 #include <sys/stat.h>
   46 
   47 #include <net/ethernet.h>
   48 
   49 #include <dev/ofw/openfirm.h>
   50 #include <dev/ofw/ofw_pci.h>
   51 #include <dev/ofw/ofw_bus.h>
   52 
   53 #include <vm/vm.h>
   54 #include <vm/vm_param.h>
   55 #include <vm/vm_page.h>
   56 
   57 #include <machine/bus.h>
   58 #include <machine/cpu.h>
   59 #include <machine/md_var.h>
   60 #include <machine/platform.h>
   61 #include <machine/ofw_machdep.h>
   62 
   63 static struct mem_region OFmem[PHYS_AVAIL_SZ], OFavail[PHYS_AVAIL_SZ];
   64 static struct mem_region OFfree[PHYS_AVAIL_SZ];
   65 
   66 extern register_t ofmsr[5];
   67 extern void     *openfirmware_entry;
   68 static void     *fdt;
   69 int             ofw_real_mode;
   70 
   71 int             ofwcall(void *);
   72 static void     ofw_quiesce(void);
   73 static int      openfirmware(void *args);
   74 
   75 /*
   76  * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback.
   77  */
   78 register_t      ofw_sprg0_save;
   79 
   80 static __inline void
   81 ofw_sprg_prepare(void)
   82 {
   83         /*
   84          * Assume that interrupt are disabled at this point, or
   85          * SPRG1-3 could be trashed
   86          */
   87         __asm __volatile("mfsprg0 %0\n\t"
   88                          "mtsprg0 %1\n\t"
   89                          "mtsprg1 %2\n\t"
   90                          "mtsprg2 %3\n\t"
   91                          "mtsprg3 %4\n\t"
   92                          : "=&r"(ofw_sprg0_save)
   93                          : "r"(ofmsr[1]),
   94                          "r"(ofmsr[2]),
   95                          "r"(ofmsr[3]),
   96                          "r"(ofmsr[4]));
   97 }
   98 
   99 static __inline void
  100 ofw_sprg_restore(void)
  101 {
  102         /*
  103          * Note that SPRG1-3 contents are irrelevant. They are scratch
  104          * registers used in the early portion of trap handling when
  105          * interrupts are disabled.
  106          *
  107          * PCPU data cannot be used until this routine is called !
  108          */
  109         __asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save));
  110 }
  111 
  112 /*
  113  * Memory region utilities: determine if two regions overlap,
  114  * and merge two overlapping regions into one
  115  */
  116 static int
  117 memr_overlap(struct mem_region *r1, struct mem_region *r2)
  118 {
  119         if ((r1->mr_start + r1->mr_size) < r2->mr_start ||
  120             (r2->mr_start + r2->mr_size) < r1->mr_start)
  121                 return (FALSE);
  122         
  123         return (TRUE);  
  124 }
  125 
  126 static void
  127 memr_merge(struct mem_region *from, struct mem_region *to)
  128 {
  129         vm_offset_t end;
  130         end = ulmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size);
  131         to->mr_start = ulmin(from->mr_start, to->mr_start);
  132         to->mr_size = end - to->mr_start;
  133 }
  134 
  135 /*
  136  * Quick sort callout for comparing memory regions.
  137  */
  138 static int      mr_cmp(const void *a, const void *b);
  139 
  140 static int
  141 mr_cmp(const void *a, const void *b)
  142 {
  143         const struct    mem_region *regiona;
  144         const struct    mem_region *regionb;
  145 
  146         regiona = a;
  147         regionb = b;
  148         if (regiona->mr_start < regionb->mr_start)
  149                 return (-1);
  150         else if (regiona->mr_start > regionb->mr_start)
  151                 return (1);
  152         else
  153                 return (0);
  154 }
  155 
  156 static int
  157 parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
  158 {
  159         cell_t address_cells, size_cells;
  160         cell_t OFmem[4 * PHYS_AVAIL_SZ];
  161         int sz, i, j;
  162         int apple_hack_mode;
  163         phandle_t phandle;
  164 
  165         sz = 0;
  166         apple_hack_mode = 0;
  167 
  168         /*
  169          * Get #address-cells from root node, defaulting to 1 if it cannot
  170          * be found.
  171          */
  172         phandle = OF_finddevice("/");
  173         if (OF_getprop(phandle, "#address-cells", &address_cells, 
  174             sizeof(address_cells)) < sizeof(address_cells))
  175                 address_cells = 1;
  176         if (OF_getprop(phandle, "#size-cells", &size_cells, 
  177             sizeof(size_cells)) < sizeof(size_cells))
  178                 size_cells = 1;
  179 
  180         /*
  181          * On Apple hardware, address_cells is always 1 for "available",
  182          * even when it is explicitly set to 2. Then all memory above 4 GB
  183          * should be added by hand to the available list. Detect Apple hardware
  184          * by seeing if ofw_real_mode is set -- only Apple seems to use
  185          * virtual-mode OF.
  186          */
  187         if (strcmp(prop, "available") == 0 && !ofw_real_mode)
  188                 apple_hack_mode = 1;
  189         
  190         if (apple_hack_mode)
  191                 address_cells = 1;
  192 
  193         /*
  194          * Get memory.
  195          */
  196         if (node == -1 || (sz = OF_getprop(node, prop,
  197             OFmem, sizeof(OFmem))) <= 0)
  198                 panic("Physical memory map not found");
  199 
  200         i = 0;
  201         j = 0;
  202         while (i < sz/sizeof(cell_t)) {
  203               #ifndef __powerpc64__
  204                 /* On 32-bit PPC, ignore regions starting above 4 GB */
  205                 if (address_cells > 1 && OFmem[i] > 0) {
  206                         i += address_cells + size_cells;
  207                         continue;
  208                 }
  209               #endif
  210 
  211                 output[j].mr_start = OFmem[i++];
  212                 if (address_cells == 2) {
  213                         #ifdef __powerpc64__
  214                         output[j].mr_start <<= 32;
  215                         #endif
  216                         output[j].mr_start += OFmem[i++];
  217                 }
  218                         
  219                 output[j].mr_size = OFmem[i++];
  220                 if (size_cells == 2) {
  221                         #ifdef __powerpc64__
  222                         output[j].mr_size <<= 32;
  223                         #endif
  224                         output[j].mr_size += OFmem[i++];
  225                 }
  226 
  227               #ifndef __powerpc64__
  228                 /*
  229                  * Check for memory regions extending above 32-bit
  230                  * memory space, and restrict them to stay there.
  231                  */
  232                 if (((uint64_t)output[j].mr_start +
  233                     (uint64_t)output[j].mr_size) >
  234                     BUS_SPACE_MAXADDR_32BIT) {
  235                         output[j].mr_size = BUS_SPACE_MAXADDR_32BIT -
  236                             output[j].mr_start;
  237                 }
  238               #endif
  239 
  240                 j++;
  241         }
  242         sz = j*sizeof(output[0]);
  243 
  244         #ifdef __powerpc64__
  245         if (apple_hack_mode) {
  246                 /* Add in regions above 4 GB to the available list */
  247                 struct mem_region himem[16];
  248                 int hisz;
  249 
  250                 hisz = parse_ofw_memory(node, "reg", himem);
  251                 for (i = 0; i < hisz/sizeof(himem[0]); i++) {
  252                         if (himem[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
  253                                 output[j].mr_start = himem[i].mr_start;
  254                                 output[j].mr_size = himem[i].mr_size;
  255                                 j++;
  256                         }
  257                 }
  258                 sz = j*sizeof(output[0]);
  259         }
  260         #endif
  261 
  262         return (sz);
  263 }
  264 
  265 static int
  266 parse_drconf_memory(int *msz, int *asz, struct mem_region *ofmem,
  267                     struct mem_region *ofavail)
  268 {
  269         phandle_t phandle;
  270         vm_offset_t base;
  271         int i, idx, len, lasz, lmsz, res;
  272         uint32_t lmb_size[2];
  273         unsigned long *dmem, flags;
  274 
  275         lmsz = *msz;
  276         lasz = *asz;
  277 
  278         phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
  279         if (phandle == -1)
  280                 /* No drconf node, return. */
  281                 return (0);
  282 
  283         res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size));
  284         if (res == -1)
  285                 return (0);
  286 
  287         /* Parse the /ibm,dynamic-memory.
  288            The first position gives the # of entries. The next two words
  289            reflect the address of the memory block. The next four words are
  290            the DRC index, reserved, list index and flags.
  291            (see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
  292            
  293             #el  Addr   DRC-idx  res   list-idx  flags
  294            -------------------------------------------------
  295            | 4 |   8   |   4   |   4   |   4   |   4   |....
  296            -------------------------------------------------
  297         */
  298 
  299         len = OF_getproplen(phandle, "ibm,dynamic-memory");
  300         if (len > 0) {
  301 
  302                 /* We have to use a variable length array on the stack
  303                    since we have very limited stack space.
  304                 */
  305                 cell_t arr[len/sizeof(cell_t)];
  306 
  307                 res = OF_getprop(phandle, "ibm,dynamic-memory", &arr,
  308                                  sizeof(arr));
  309                 if (res == -1)
  310                         return (0);
  311 
  312                 /* Number of elements */
  313                 idx = arr[0];
  314 
  315                 /* First address. */
  316                 dmem = (void*)&arr[1];
  317         
  318                 for (i = 0; i < idx; i++) {
  319                         base = *dmem;
  320                         dmem += 2;
  321                         flags = *dmem;
  322                         /* Use region only if available and not reserved. */
  323                         if ((flags & 0x8) && !(flags & 0x80)) {
  324                                 ofmem[lmsz].mr_start = base;
  325                                 ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
  326                                 ofavail[lasz].mr_start = base;
  327                                 ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
  328                                 lmsz++;
  329                                 lasz++;
  330                         }
  331                         dmem++;
  332                 }
  333         }
  334 
  335         *msz = lmsz;
  336         *asz = lasz;
  337 
  338         return (1);
  339 }
  340 /*
  341  * This is called during powerpc_init, before the system is really initialized.
  342  * It shall provide the total and the available regions of RAM.
  343  * Both lists must have a zero-size entry as terminator.
  344  * The available regions need not take the kernel into account, but needs
  345  * to provide space for two additional entry beyond the terminating one.
  346  */
  347 void
  348 ofw_mem_regions(struct mem_region **memp, int *memsz,
  349                 struct mem_region **availp, int *availsz)
  350 {
  351         phandle_t phandle;
  352         vm_offset_t maxphysaddr;
  353         int asz, msz, fsz;
  354         int i, j, res;
  355         int still_merging;
  356         char name[31];
  357 
  358         asz = msz = 0;
  359 
  360         /*
  361          * Get memory from all the /memory nodes.
  362          */
  363         for (phandle = OF_child(OF_peer(0)); phandle != 0;
  364             phandle = OF_peer(phandle)) {
  365                 if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0)
  366                         continue;
  367                 if (strncmp(name, "memory", sizeof(name)) != 0)
  368                         continue;
  369 
  370                 res = parse_ofw_memory(phandle, "reg", &OFmem[msz]);
  371                 msz += res/sizeof(struct mem_region);
  372                 if (OF_getproplen(phandle, "available") >= 0)
  373                         res = parse_ofw_memory(phandle, "available",
  374                             &OFavail[asz]);
  375                 else
  376                         res = parse_ofw_memory(phandle, "reg", &OFavail[asz]);
  377                 asz += res/sizeof(struct mem_region);
  378         }
  379 
  380         /* Check for memory in ibm,dynamic-reconfiguration-memory */
  381         parse_drconf_memory(&msz, &asz, OFmem, OFavail);
  382 
  383         qsort(OFmem, msz, sizeof(*OFmem), mr_cmp);
  384         qsort(OFavail, asz, sizeof(*OFavail), mr_cmp);
  385 
  386         *memp = OFmem;
  387         *memsz = msz;
  388 
  389         /*
  390          * On some firmwares (SLOF), some memory may be marked available that
  391          * doesn't actually exist. This manifests as an extension of the last
  392          * available segment past the end of physical memory, so truncate that
  393          * one.
  394          */
  395         maxphysaddr = 0;
  396         for (i = 0; i < msz; i++)
  397                 if (OFmem[i].mr_start + OFmem[i].mr_size > maxphysaddr)
  398                         maxphysaddr = OFmem[i].mr_start + OFmem[i].mr_size;
  399 
  400         if (OFavail[asz - 1].mr_start + OFavail[asz - 1].mr_size > maxphysaddr)
  401                 OFavail[asz - 1].mr_size = maxphysaddr -
  402                     OFavail[asz - 1].mr_start;
  403 
  404         /*
  405          * OFavail may have overlapping regions - collapse these
  406          * and copy out remaining regions to OFfree
  407          */
  408         do {
  409                 still_merging = FALSE;
  410                 for (i = 0; i < asz; i++) {
  411                         if (OFavail[i].mr_size == 0)
  412                                 continue;
  413                         for (j = i+1; j < asz; j++) {
  414                                 if (OFavail[j].mr_size == 0)
  415                                         continue;
  416                                 if (memr_overlap(&OFavail[j], &OFavail[i])) {
  417                                         memr_merge(&OFavail[j], &OFavail[i]);
  418                                         /* mark inactive */
  419                                         OFavail[j].mr_size = 0;
  420                                         still_merging = TRUE;
  421                                 }
  422                         }
  423                 }
  424         } while (still_merging == TRUE);
  425 
  426         /* evict inactive ranges */
  427         for (i = 0, fsz = 0; i < asz; i++) {
  428                 if (OFavail[i].mr_size != 0) {
  429                         OFfree[fsz] = OFavail[i];
  430                         fsz++;
  431                 }
  432         }
  433 
  434         *availp = OFfree;
  435         *availsz = fsz;
  436 }
  437 
  438 void
  439 OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *))
  440 {
  441         if (ofmsr[0] & PSL_DR)
  442                 ofw_real_mode = 0;
  443         else
  444                 ofw_real_mode = 1;
  445 
  446         fdt = fdt_ptr;
  447 
  448         #ifdef FDT_DTB_STATIC
  449         /* Check for a statically included blob */
  450         if (fdt == NULL)
  451                 fdt = &fdt_static_dtb;
  452         #endif
  453 }
  454 
  455 boolean_t
  456 OF_bootstrap()
  457 {
  458         boolean_t status = FALSE;
  459 
  460         if (openfirmware_entry != NULL) {
  461                 if (ofw_real_mode) {
  462                         status = OF_install(OFW_STD_REAL, 0);
  463                 } else {
  464                         #ifdef __powerpc64__
  465                         status = OF_install(OFW_STD_32BIT, 0);
  466                         #else
  467                         status = OF_install(OFW_STD_DIRECT, 0);
  468                         #endif
  469                 }
  470 
  471                 if (status != TRUE)
  472                         return status;
  473 
  474                 OF_init(openfirmware);
  475 
  476                 /*
  477                  * On some machines, we need to quiesce OF to turn off
  478                  * background processes.
  479                  */
  480                 ofw_quiesce();
  481         } else if (fdt != NULL) {
  482                 status = OF_install(OFW_FDT, 0);
  483 
  484                 if (status != TRUE)
  485                         return status;
  486 
  487                 OF_init(fdt);
  488         } 
  489 
  490         return (status);
  491 }
  492 
  493 static void
  494 ofw_quiesce(void)
  495 {
  496         phandle_t rootnode;
  497         char model[32];
  498         struct {
  499                 cell_t name;
  500                 cell_t nargs;
  501                 cell_t nreturns;
  502         } args;
  503 
  504         /*
  505          * Only quiesce Open Firmware on PowerMac11,2 and 12,1. It is
  506          * necessary there to shut down a background thread doing fan
  507          * management, and is harmful on other machines.
  508          *
  509          * Note: we don't need to worry about which OF module we are
  510          * using since this is called only from very early boot, within
  511          * OF's boot context.
  512          */
  513 
  514         rootnode = OF_finddevice("/");
  515         if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
  516                 if (strcmp(model, "PowerMac11,2") == 0 ||
  517                     strcmp(model, "PowerMac12,1") == 0) {
  518                         args.name = (cell_t)(uintptr_t)"quiesce";
  519                         args.nargs = 0;
  520                         args.nreturns = 0;
  521                         openfirmware(&args);
  522                 }
  523         }
  524 }
  525 
  526 static int
  527 openfirmware_core(void *args)
  528 {
  529         int             result;
  530         register_t      oldmsr;
  531 
  532         /*
  533          * Turn off exceptions - we really don't want to end up
  534          * anywhere unexpected with PCPU set to something strange
  535          * or the stack pointer wrong.
  536          */
  537         oldmsr = intr_disable();
  538 
  539         ofw_sprg_prepare();
  540 
  541 #if defined(AIM) && !defined(__powerpc64__)
  542         /*
  543          * Clear battable[] translations
  544          */
  545         if (!(cpu_features & PPC_FEATURE_64))
  546                 __asm __volatile("mtdbatu 2, %0\n"
  547                                  "mtdbatu 3, %0" : : "r" (0));
  548         isync();
  549 #endif
  550 
  551         result = ofwcall(args);
  552         ofw_sprg_restore();
  553 
  554         intr_restore(oldmsr);
  555 
  556         return (result);
  557 }
  558 
  559 #ifdef SMP
  560 struct ofw_rv_args {
  561         void *args;
  562         int retval;
  563         volatile int in_progress;
  564 };
  565 
  566 static void
  567 ofw_rendezvous_dispatch(void *xargs)
  568 {
  569         struct ofw_rv_args *rv_args = xargs;
  570 
  571         /* NOTE: Interrupts are disabled here */
  572 
  573         if (PCPU_GET(cpuid) == 0) {
  574                 /*
  575                  * Execute all OF calls on CPU 0
  576                  */
  577                 rv_args->retval = openfirmware_core(rv_args->args);
  578                 rv_args->in_progress = 0;
  579         } else {
  580                 /*
  581                  * Spin with interrupts off on other CPUs while OF has
  582                  * control of the machine.
  583                  */
  584                 while (rv_args->in_progress)
  585                         cpu_spinwait();
  586         }
  587 }
  588 #endif
  589 
  590 static int
  591 openfirmware(void *args)
  592 {
  593         int result;
  594         #ifdef SMP
  595         struct ofw_rv_args rv_args;
  596 
  597         rv_args.args = args;
  598         rv_args.in_progress = 1;
  599         smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch,
  600             smp_no_rendevous_barrier, &rv_args);
  601         result = rv_args.retval;
  602         #else
  603         result = openfirmware_core(args);
  604         #endif
  605 
  606         return (result);
  607 }
  608 
  609 void
  610 OF_reboot()
  611 {
  612         struct {
  613                 cell_t name;
  614                 cell_t nargs;
  615                 cell_t nreturns;
  616                 cell_t arg;
  617         } args;
  618 
  619         args.name = (cell_t)(uintptr_t)"interpret";
  620         args.nargs = 1;
  621         args.nreturns = 0;
  622         args.arg = (cell_t)(uintptr_t)"reset-all";
  623         openfirmware_core(&args); /* Don't do rendezvous! */
  624 
  625         for (;;);       /* just in case */
  626 }
  627 
  628 void
  629 OF_getetheraddr(device_t dev, u_char *addr)
  630 {
  631         phandle_t       node;
  632 
  633         node = ofw_bus_get_node(dev);
  634         OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN);
  635 }
  636 
  637 /*
  638  * Return a bus handle and bus tag that corresponds to the register
  639  * numbered regno for the device referenced by the package handle
  640  * dev. This function is intended to be used by console drivers in
  641  * early boot only. It works by mapping the address of the device's
  642  * register in the address space of its parent and recursively walk
  643  * the device tree upward this way.
  644  */
  645 static void
  646 OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip)
  647 {
  648         char name[16];
  649         uint32_t addr, size;
  650         int pci, res;
  651 
  652         res = OF_getprop(node, "#address-cells", &addr, sizeof(addr));
  653         if (res == -1)
  654                 addr = 2;
  655         res = OF_getprop(node, "#size-cells", &size, sizeof(size));
  656         if (res == -1)
  657                 size = 1;
  658         pci = 0;
  659         if (addr == 3 && size == 2) {
  660                 res = OF_getprop(node, "name", name, sizeof(name));
  661                 if (res != -1) {
  662                         name[sizeof(name) - 1] = '\0';
  663                         pci = (strcmp(name, "pci") == 0) ? 1 : 0;
  664                 }
  665         }
  666         if (addrp != NULL)
  667                 *addrp = addr;
  668         if (sizep != NULL)
  669                 *sizep = size;
  670         if (pcip != NULL)
  671                 *pcip = pci;
  672 }
  673 
  674 int
  675 OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag,
  676     bus_space_handle_t *handle)
  677 {
  678         uint32_t cell[32];
  679         bus_addr_t addr, raddr, baddr;
  680         bus_size_t size, rsize;
  681         uint32_t c, nbridge, naddr, nsize;
  682         phandle_t bridge, parent;
  683         u_int spc, rspc;
  684         int pci, pcib, res;
  685 
  686         /* Sanity checking. */
  687         if (dev == 0)
  688                 return (EINVAL);
  689         bridge = OF_parent(dev);
  690         if (bridge == 0)
  691                 return (EINVAL);
  692         if (regno < 0)
  693                 return (EINVAL);
  694         if (tag == NULL || handle == NULL)
  695                 return (EINVAL);
  696 
  697         /* Get the requested register. */
  698         OF_get_addr_props(bridge, &naddr, &nsize, &pci);
  699         res = OF_getprop(dev, (pci) ? "assigned-addresses" : "reg",
  700             cell, sizeof(cell));
  701         if (res == -1)
  702                 return (ENXIO);
  703         if (res % sizeof(cell[0]))
  704                 return (ENXIO);
  705         res /= sizeof(cell[0]);
  706         regno *= naddr + nsize;
  707         if (regno + naddr + nsize > res)
  708                 return (EINVAL);
  709         spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0;
  710         addr = 0;
  711         for (c = 0; c < naddr; c++)
  712                 addr = ((uint64_t)addr << 32) | cell[regno++];
  713         size = 0;
  714         for (c = 0; c < nsize; c++)
  715                 size = ((uint64_t)size << 32) | cell[regno++];
  716 
  717         /*
  718          * Map the address range in the bridge's decoding window as given
  719          * by the "ranges" property. If a node doesn't have such property
  720          * then no mapping is done.
  721          */
  722         parent = OF_parent(bridge);
  723         while (parent != 0) {
  724                 OF_get_addr_props(parent, &nbridge, NULL, &pcib);
  725                 res = OF_getprop(bridge, "ranges", cell, sizeof(cell));
  726                 if (res == -1)
  727                         goto next;
  728                 if (res % sizeof(cell[0]))
  729                         return (ENXIO);
  730                 res /= sizeof(cell[0]);
  731                 regno = 0;
  732                 while (regno < res) {
  733                         rspc = (pci)
  734                             ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
  735                             : ~0;
  736                         if (rspc != spc) {
  737                                 regno += naddr + nbridge + nsize;
  738                                 continue;
  739                         }
  740                         raddr = 0;
  741                         for (c = 0; c < naddr; c++)
  742                                 raddr = ((uint64_t)raddr << 32) | cell[regno++];
  743                         rspc = (pcib)
  744                             ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
  745                             : ~0;
  746                         baddr = 0;
  747                         for (c = 0; c < nbridge; c++)
  748                                 baddr = ((uint64_t)baddr << 32) | cell[regno++];
  749                         rsize = 0;
  750                         for (c = 0; c < nsize; c++)
  751                                 rsize = ((uint64_t)rsize << 32) | cell[regno++];
  752                         if (addr < raddr || addr >= raddr + rsize)
  753                                 continue;
  754                         addr = addr - raddr + baddr;
  755                         if (rspc != ~0)
  756                                 spc = rspc;
  757                 }
  758 
  759         next:
  760                 bridge = parent;
  761                 parent = OF_parent(bridge);
  762                 OF_get_addr_props(bridge, &naddr, &nsize, &pci);
  763         }
  764 
  765         *tag = &bs_le_tag;
  766         return (bus_space_map(*tag, addr, size, 0, handle));
  767 }
  768 

Cache object: e1fc2eefb4235a9b5177f1c8537bd4c7


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.