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/powermac/platform_powermac.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  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2008 Marcel Moolenaar
    5  * Copyright (c) 2009 Nathan Whitehorn
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  *
   12  * 1. Redistributions of source code must retain the above copyright
   13  *    notice, this list of conditions and the following disclaimer.
   14  * 2. Redistributions in binary form must reproduce the above copyright
   15  *    notice, this list of conditions and the following disclaimer in the
   16  *    documentation and/or other materials provided with the distribution.
   17  *
   18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   19  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   20  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   21  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   22  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   23  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   28  */
   29 
   30 #include <sys/cdefs.h>
   31 __FBSDID("$FreeBSD$");
   32 
   33 #include <sys/param.h>
   34 #include <sys/systm.h>
   35 #include <sys/kernel.h>
   36 #include <sys/bus.h>
   37 #include <sys/pcpu.h>
   38 #include <sys/proc.h>
   39 #include <sys/smp.h>
   40 #include <vm/vm.h>
   41 #include <vm/pmap.h>
   42 
   43 #include <machine/altivec.h>    /* For save_vec() */
   44 #include <machine/bus.h>
   45 #include <machine/cpu.h>
   46 #include <machine/fpu.h>        /* For save_fpu() */
   47 #include <machine/hid.h>
   48 #include <machine/platformvar.h>
   49 #include <machine/setjmp.h>
   50 #include <machine/smp.h>
   51 #include <machine/spr.h>
   52 
   53 #include <dev/ofw/openfirm.h>
   54 #include <machine/ofw_machdep.h>
   55 
   56 #include <powerpc/powermac/platform_powermac.h>
   57 
   58 #include "platform_if.h"
   59 
   60 extern volatile void *ap_pcpu;
   61 
   62 static void dummy_timebase(device_t, bool);
   63 static device_t powermac_tb_dev;
   64 static void (*freeze_timebase)(device_t, bool) = dummy_timebase;
   65 
   66 static int powermac_probe(platform_t);
   67 static int powermac_attach(platform_t);
   68 void powermac_mem_regions(platform_t, struct mem_region *phys, int *physsz,
   69     struct mem_region *avail, int *availsz);
   70 static u_long powermac_timebase_freq(platform_t, struct cpuref *cpuref);
   71 static int powermac_smp_first_cpu(platform_t, struct cpuref *cpuref);
   72 static int powermac_smp_next_cpu(platform_t, struct cpuref *cpuref);
   73 static int powermac_smp_get_bsp(platform_t, struct cpuref *cpuref);
   74 static int powermac_smp_start_cpu(platform_t, struct pcpu *cpu);
   75 static void powermac_smp_timebase_sync(platform_t, u_long tb, int ap);
   76 static void powermac_reset(platform_t);
   77 #ifndef __powerpc64__
   78 static void powermac_sleep(platform_t);
   79 #endif
   80 
   81 static platform_method_t powermac_methods[] = {
   82         PLATFORMMETHOD(platform_probe,          powermac_probe),
   83         PLATFORMMETHOD(platform_attach,         powermac_attach),
   84         PLATFORMMETHOD(platform_mem_regions,    powermac_mem_regions),
   85         PLATFORMMETHOD(platform_timebase_freq,  powermac_timebase_freq),
   86 
   87         PLATFORMMETHOD(platform_smp_first_cpu,  powermac_smp_first_cpu),
   88         PLATFORMMETHOD(platform_smp_next_cpu,   powermac_smp_next_cpu),
   89         PLATFORMMETHOD(platform_smp_get_bsp,    powermac_smp_get_bsp),
   90         PLATFORMMETHOD(platform_smp_start_cpu,  powermac_smp_start_cpu),
   91         PLATFORMMETHOD(platform_smp_timebase_sync, powermac_smp_timebase_sync),
   92 
   93         PLATFORMMETHOD(platform_reset,          powermac_reset),
   94 #ifndef __powerpc64__
   95         PLATFORMMETHOD(platform_sleep,          powermac_sleep),
   96 #endif
   97 
   98         PLATFORMMETHOD_END
   99 };
  100 
  101 static platform_def_t powermac_platform = {
  102         "powermac",
  103         powermac_methods,
  104         0
  105 };
  106 
  107 PLATFORM_DEF(powermac_platform);
  108 
  109 static int
  110 powermac_probe(platform_t plat)
  111 {
  112         char compat[255];
  113         ssize_t compatlen;
  114         char *curstr;
  115         phandle_t root;
  116 
  117         root = OF_peer(0);
  118         if (root == 0)
  119                 return (ENXIO);
  120 
  121         compatlen = OF_getprop(root, "compatible", compat, sizeof(compat));
  122 
  123         for (curstr = compat; curstr < compat + compatlen;
  124             curstr += strlen(curstr) + 1) {
  125                 if (strncmp(curstr, "MacRISC", 7) == 0)
  126                         return (BUS_PROBE_SPECIFIC);
  127         }
  128 
  129         return (ENXIO);
  130 }
  131 
  132 void
  133 powermac_mem_regions(platform_t plat, struct mem_region *phys, int *physsz,
  134     struct mem_region *avail, int *availsz)
  135 {
  136         phandle_t memory;
  137         cell_t memoryprop[PHYS_AVAIL_SZ * 2];
  138         ssize_t propsize, i, j;
  139         int physacells = 1;
  140 
  141         memory = OF_finddevice("/memory");
  142         if (memory == -1)
  143                 memory = OF_finddevice("/memory@0");
  144 
  145         /* "reg" has variable #address-cells, but #size-cells is always 1 */
  146         OF_getprop(OF_parent(memory), "#address-cells", &physacells,
  147             sizeof(physacells));
  148 
  149         propsize = OF_getprop(memory, "reg", memoryprop, sizeof(memoryprop));
  150         propsize /= sizeof(cell_t);
  151         for (i = 0, j = 0; i < propsize; i += physacells+1, j++) {
  152                 phys[j].mr_start = memoryprop[i];
  153                 if (physacells == 2) {
  154 #ifndef __powerpc64__
  155                         /* On 32-bit PPC, ignore regions starting above 4 GB */
  156                         if (memoryprop[i] != 0) {
  157                                 j--;
  158                                 continue;
  159                         }
  160 #else
  161                         phys[j].mr_start <<= 32;
  162 #endif
  163                         phys[j].mr_start |= memoryprop[i+1];
  164                 }
  165                 phys[j].mr_size = memoryprop[i + physacells];
  166         }
  167         *physsz = j;
  168 
  169         /* "available" always has #address-cells = 1 */
  170         propsize = OF_getprop(memory, "available", memoryprop,
  171             sizeof(memoryprop));
  172         if (propsize <= 0) {
  173                 for (i = 0; i < *physsz; i++) {
  174                         avail[i].mr_start = phys[i].mr_start;
  175                         avail[i].mr_size = phys[i].mr_size;
  176                 }
  177 
  178                 *availsz = *physsz;
  179         } else {
  180                 propsize /= sizeof(cell_t);
  181                 for (i = 0, j = 0; i < propsize; i += 2, j++) {
  182                         avail[j].mr_start = memoryprop[i];
  183                         avail[j].mr_size = memoryprop[i + 1];
  184                 }
  185 
  186 #ifdef __powerpc64__
  187                 /* Add in regions above 4 GB to the available list */
  188                 for (i = 0; i < *physsz; i++) {
  189                         if (phys[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
  190                                 avail[j].mr_start = phys[i].mr_start;
  191                                 avail[j].mr_size = phys[i].mr_size;
  192                                 j++;
  193                         }
  194                 }
  195 #endif
  196                 *availsz = j;
  197         }
  198 }
  199 
  200 static int
  201 powermac_attach(platform_t plat)
  202 {
  203         phandle_t rootnode;
  204         char model[32];
  205 
  206         /*
  207          * Quiesce Open Firmware on PowerMac11,2 and 12,1. It is
  208          * necessary there to shut down a background thread doing fan
  209          * management, and is harmful on other machines (it will make OF
  210          * shut off power to various system components it had turned on).
  211          *
  212          * Note: we don't need to worry about which OF module we are
  213          * using since this is called only from very early boot, within
  214          * OF's boot context.
  215          */
  216 
  217         rootnode = OF_finddevice("/");
  218         if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
  219                 if (strcmp(model, "PowerMac11,2") == 0 ||
  220                     strcmp(model, "PowerMac12,1") == 0) {
  221                         ofw_quiesce();
  222                 }
  223         }
  224 
  225         return (0);
  226 }
  227 
  228 static u_long
  229 powermac_timebase_freq(platform_t plat, struct cpuref *cpuref)
  230 {
  231         phandle_t phandle;
  232         int32_t ticks = -1;
  233 
  234         phandle = cpuref->cr_hwref;
  235 
  236         OF_getprop(phandle, "timebase-frequency", &ticks, sizeof(ticks));
  237 
  238         if (ticks <= 0)
  239                 panic("Unable to determine timebase frequency!");
  240 
  241         return (ticks);
  242 }
  243 
  244 static int
  245 powermac_smp_fill_cpuref(struct cpuref *cpuref, phandle_t cpu)
  246 {
  247         cell_t cpuid;
  248         int res;
  249 
  250         cpuref->cr_hwref = cpu;
  251         res = OF_getprop(cpu, "reg", &cpuid, sizeof(cpuid));
  252 
  253         /*
  254          * psim doesn't have a reg property, so assume 0 as for the
  255          * uniprocessor case in the CHRP spec. 
  256          */
  257         if (res < 0) {
  258                 cpuid = 0;
  259         }
  260 
  261         cpuref->cr_cpuid = cpuid & 0xff;
  262         return (0);
  263 }
  264 
  265 static int
  266 powermac_smp_first_cpu(platform_t plat, struct cpuref *cpuref)
  267 {
  268         char buf[8];
  269         phandle_t cpu, dev, root;
  270         int res;
  271 
  272         root = OF_peer(0);
  273 
  274         dev = OF_child(root);
  275         while (dev != 0) {
  276                 res = OF_getprop(dev, "name", buf, sizeof(buf));
  277                 if (res > 0 && strcmp(buf, "cpus") == 0)
  278                         break;
  279                 dev = OF_peer(dev);
  280         }
  281         if (dev == 0) {
  282                 /*
  283                  * psim doesn't have a name property on the /cpus node,
  284                  * but it can be found directly
  285                  */
  286                 dev = OF_finddevice("/cpus");
  287                 if (dev == -1)
  288                         return (ENOENT);
  289         }
  290 
  291         cpu = OF_child(dev);
  292 
  293         while (cpu != 0) {
  294                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
  295                 if (res > 0 && strcmp(buf, "cpu") == 0)
  296                         break;
  297                 cpu = OF_peer(cpu);
  298         }
  299         if (cpu == 0)
  300                 return (ENOENT);
  301 
  302         return (powermac_smp_fill_cpuref(cpuref, cpu));
  303 }
  304 
  305 static int
  306 powermac_smp_next_cpu(platform_t plat, struct cpuref *cpuref)
  307 {
  308         char buf[8];
  309         phandle_t cpu;
  310         int res;
  311 
  312         cpu = OF_peer(cpuref->cr_hwref);
  313         while (cpu != 0) {
  314                 res = OF_getprop(cpu, "device_type", buf, sizeof(buf));
  315                 if (res > 0 && strcmp(buf, "cpu") == 0)
  316                         break;
  317                 cpu = OF_peer(cpu);
  318         }
  319         if (cpu == 0)
  320                 return (ENOENT);
  321 
  322         return (powermac_smp_fill_cpuref(cpuref, cpu));
  323 }
  324 
  325 static int
  326 powermac_smp_get_bsp(platform_t plat, struct cpuref *cpuref)
  327 {
  328         ihandle_t inst;
  329         phandle_t bsp, chosen;
  330         int res;
  331 
  332         chosen = OF_finddevice("/chosen");
  333         if (chosen == -1)
  334                 return (ENXIO);
  335 
  336         res = OF_getprop(chosen, "cpu", &inst, sizeof(inst));
  337         if (res < 0)
  338                 return (ENXIO);
  339 
  340         bsp = OF_instance_to_package(inst);
  341         return (powermac_smp_fill_cpuref(cpuref, bsp));
  342 }
  343 
  344 static int
  345 powermac_smp_start_cpu(platform_t plat, struct pcpu *pc)
  346 {
  347 #ifdef SMP
  348         phandle_t cpu;
  349         volatile uint8_t *rstvec;
  350         static volatile uint8_t *rstvec_virtbase = NULL;
  351         int res, reset, timeout;
  352 
  353         cpu = pc->pc_hwref;
  354         res = OF_getprop(cpu, "soft-reset", &reset, sizeof(reset));
  355         if (res < 0) {
  356                 reset = 0x58;
  357 
  358                 switch (pc->pc_cpuid) {
  359                 case 0:
  360                         reset += 0x03;
  361                         break;
  362                 case 1:
  363                         reset += 0x04;
  364                         break;
  365                 case 2:
  366                         reset += 0x0f;
  367                         break;
  368                 case 3:
  369                         reset += 0x10;
  370                         break;
  371                 default:
  372                         return (ENXIO);
  373                 }
  374         }
  375 
  376         ap_pcpu = pc;
  377 
  378         if (rstvec_virtbase == NULL)
  379                 rstvec_virtbase = pmap_mapdev(0x80000000, PAGE_SIZE);
  380 
  381         rstvec = rstvec_virtbase + reset;
  382 
  383         *rstvec = 4;
  384         powerpc_sync();
  385         (void)(*rstvec);
  386         powerpc_sync();
  387         DELAY(1);
  388         *rstvec = 0;
  389         powerpc_sync();
  390         (void)(*rstvec);
  391         powerpc_sync();
  392 
  393         timeout = 10000;
  394         while (!pc->pc_awake && timeout--)
  395                 DELAY(100);
  396 
  397         return ((pc->pc_awake) ? 0 : EBUSY);
  398 #else
  399         /* No SMP support */
  400         return (ENXIO);
  401 #endif
  402 }
  403 
  404 void
  405 powermac_register_timebase(device_t dev, powermac_tb_disable_t cb)
  406 {
  407         powermac_tb_dev = dev;
  408         freeze_timebase = cb;
  409 }
  410 
  411 static void
  412 powermac_smp_timebase_sync(platform_t plat, u_long tb, int ap)
  413 {
  414         static volatile bool tb_ready;
  415         static volatile int cpu_done;
  416 
  417         /*
  418          * XXX Temporary fallback for platforms we don't know how to freeze.
  419          *
  420          * This needs to be replaced with a cpu-to-cpu software sync
  421          * protocol, because this is not a consistent way to sync timebase.
  422          */
  423         mttb(tb);
  424         if (freeze_timebase == dummy_timebase)
  425                 return;
  426 
  427         if (ap) {
  428                 /* APs.  Hold off until we get a stable timebase. */
  429                 critical_enter();
  430                 while (!tb_ready)
  431                         atomic_thread_fence_seq_cst();
  432                 mttb(tb);
  433                 atomic_add_int(&cpu_done, 1);
  434                 while (cpu_done < mp_ncpus)
  435                         atomic_thread_fence_seq_cst();
  436                 critical_exit();
  437         } else {
  438                 /* BSP */
  439                 critical_enter();
  440                 /* Ensure cpu_done is zeroed so we can resync at runtime */
  441                 atomic_set_int(&cpu_done, 0);
  442                 freeze_timebase(powermac_tb_dev, true);
  443                 tb_ready = true;
  444                 mttb(tb);
  445                 atomic_add_int(&cpu_done, 1);
  446                 while (cpu_done < mp_ncpus)
  447                         atomic_thread_fence_seq_cst();
  448                 freeze_timebase(powermac_tb_dev, false);
  449                 /* Reset tb_ready so we can resync at runtime */
  450                 tb_ready = false;
  451                 critical_exit();
  452         }
  453 }
  454 
  455 /* Fallback freeze. In case no real handler is found in the device tree. */
  456 static void
  457 dummy_timebase(device_t dev, bool freeze)
  458 {
  459         /* Nothing to do here, move along. */
  460 }
  461 
  462 static void
  463 powermac_reset(platform_t platform)
  464 {
  465         OF_reboot();
  466 }
  467 
  468 #ifndef __powerpc64__
  469 void
  470 powermac_sleep(platform_t platform)
  471 {
  472         /* Only supports MPC745x for now. */
  473         if (!MPC745X_P(mfspr(SPR_PVR) >> 16)) {
  474                 printf("sleep only supported for G4 PowerMac hardware.\n");
  475                 return;
  476         }
  477 
  478         *(unsigned long *)0x80 = 0x100;
  479         mpc745x_sleep();
  480 }
  481 #endif

Cache object: 99a7c4366a5317739f93a1fb6a32573c


[ 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.