023864f69c
property such as those found on some real and emulated IBM systems. The approach, which is taken from Linux, is to scan through the PCI bars until we find one large enough to contain the linear framebuffer and which is ideally prefetchable if no "address" property can be found. This makes the graphical console work with the pSeries target in QEMU. Approved by: re (delphij)
771 lines
18 KiB
C
771 lines
18 KiB
C
/*-
|
|
* Copyright (C) 1996 Wolfgang Solfrank.
|
|
* Copyright (C) 1996 TooLs GmbH.
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by TooLs GmbH.
|
|
* 4. The name of TooLs GmbH may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/disk.h>
|
|
#include <sys/fcntl.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/smp.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <dev/ofw/openfirm.h>
|
|
#include <dev/ofw/ofw_pci.h>
|
|
#include <dev/ofw/ofw_bus.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/vm_param.h>
|
|
#include <vm/vm_page.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/md_var.h>
|
|
#include <machine/platform.h>
|
|
#include <machine/ofw_machdep.h>
|
|
|
|
static struct mem_region OFmem[PHYS_AVAIL_SZ], OFavail[PHYS_AVAIL_SZ];
|
|
static struct mem_region OFfree[PHYS_AVAIL_SZ];
|
|
|
|
extern register_t ofmsr[5];
|
|
extern void *openfirmware_entry;
|
|
static void *fdt;
|
|
int ofw_real_mode;
|
|
|
|
int ofwcall(void *);
|
|
static void ofw_quiesce(void);
|
|
static int openfirmware(void *args);
|
|
|
|
/*
|
|
* Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback.
|
|
*/
|
|
register_t ofw_sprg0_save;
|
|
|
|
static __inline void
|
|
ofw_sprg_prepare(void)
|
|
{
|
|
/*
|
|
* Assume that interrupt are disabled at this point, or
|
|
* SPRG1-3 could be trashed
|
|
*/
|
|
__asm __volatile("mfsprg0 %0\n\t"
|
|
"mtsprg0 %1\n\t"
|
|
"mtsprg1 %2\n\t"
|
|
"mtsprg2 %3\n\t"
|
|
"mtsprg3 %4\n\t"
|
|
: "=&r"(ofw_sprg0_save)
|
|
: "r"(ofmsr[1]),
|
|
"r"(ofmsr[2]),
|
|
"r"(ofmsr[3]),
|
|
"r"(ofmsr[4]));
|
|
}
|
|
|
|
static __inline void
|
|
ofw_sprg_restore(void)
|
|
{
|
|
/*
|
|
* Note that SPRG1-3 contents are irrelevant. They are scratch
|
|
* registers used in the early portion of trap handling when
|
|
* interrupts are disabled.
|
|
*
|
|
* PCPU data cannot be used until this routine is called !
|
|
*/
|
|
__asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save));
|
|
}
|
|
|
|
/*
|
|
* Memory region utilities: determine if two regions overlap,
|
|
* and merge two overlapping regions into one
|
|
*/
|
|
static int
|
|
memr_overlap(struct mem_region *r1, struct mem_region *r2)
|
|
{
|
|
if ((r1->mr_start + r1->mr_size) < r2->mr_start ||
|
|
(r2->mr_start + r2->mr_size) < r1->mr_start)
|
|
return (FALSE);
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
static void
|
|
memr_merge(struct mem_region *from, struct mem_region *to)
|
|
{
|
|
vm_offset_t end;
|
|
end = ulmax(to->mr_start + to->mr_size, from->mr_start + from->mr_size);
|
|
to->mr_start = ulmin(from->mr_start, to->mr_start);
|
|
to->mr_size = end - to->mr_start;
|
|
}
|
|
|
|
/*
|
|
* Quick sort callout for comparing memory regions.
|
|
*/
|
|
static int mr_cmp(const void *a, const void *b);
|
|
|
|
static int
|
|
mr_cmp(const void *a, const void *b)
|
|
{
|
|
const struct mem_region *regiona;
|
|
const struct mem_region *regionb;
|
|
|
|
regiona = a;
|
|
regionb = b;
|
|
if (regiona->mr_start < regionb->mr_start)
|
|
return (-1);
|
|
else if (regiona->mr_start > regionb->mr_start)
|
|
return (1);
|
|
else
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
|
|
{
|
|
cell_t address_cells, size_cells;
|
|
cell_t OFmem[4 * PHYS_AVAIL_SZ];
|
|
int sz, i, j;
|
|
int apple_hack_mode;
|
|
phandle_t phandle;
|
|
|
|
sz = 0;
|
|
apple_hack_mode = 0;
|
|
|
|
/*
|
|
* Get #address-cells from root node, defaulting to 1 if it cannot
|
|
* be found.
|
|
*/
|
|
phandle = OF_finddevice("/");
|
|
if (OF_getprop(phandle, "#address-cells", &address_cells,
|
|
sizeof(address_cells)) < (ssize_t)sizeof(address_cells))
|
|
address_cells = 1;
|
|
if (OF_getprop(phandle, "#size-cells", &size_cells,
|
|
sizeof(size_cells)) < (ssize_t)sizeof(size_cells))
|
|
size_cells = 1;
|
|
|
|
/*
|
|
* On Apple hardware, address_cells is always 1 for "available",
|
|
* even when it is explicitly set to 2. Then all memory above 4 GB
|
|
* should be added by hand to the available list. Detect Apple hardware
|
|
* by seeing if ofw_real_mode is set -- only Apple seems to use
|
|
* virtual-mode OF.
|
|
*/
|
|
if (strcmp(prop, "available") == 0 && !ofw_real_mode)
|
|
apple_hack_mode = 1;
|
|
|
|
if (apple_hack_mode)
|
|
address_cells = 1;
|
|
|
|
/*
|
|
* Get memory.
|
|
*/
|
|
if (node == -1 || (sz = OF_getprop(node, prop,
|
|
OFmem, sizeof(OFmem))) <= 0)
|
|
panic("Physical memory map not found");
|
|
|
|
i = 0;
|
|
j = 0;
|
|
while (i < sz/sizeof(cell_t)) {
|
|
#ifndef __powerpc64__
|
|
/* On 32-bit PPC, ignore regions starting above 4 GB */
|
|
if (address_cells > 1 && OFmem[i] > 0) {
|
|
i += address_cells + size_cells;
|
|
continue;
|
|
}
|
|
#endif
|
|
|
|
output[j].mr_start = OFmem[i++];
|
|
if (address_cells == 2) {
|
|
#ifdef __powerpc64__
|
|
output[j].mr_start <<= 32;
|
|
#endif
|
|
output[j].mr_start += OFmem[i++];
|
|
}
|
|
|
|
output[j].mr_size = OFmem[i++];
|
|
if (size_cells == 2) {
|
|
#ifdef __powerpc64__
|
|
output[j].mr_size <<= 32;
|
|
#endif
|
|
output[j].mr_size += OFmem[i++];
|
|
}
|
|
|
|
#ifndef __powerpc64__
|
|
/*
|
|
* Check for memory regions extending above 32-bit
|
|
* memory space, and restrict them to stay there.
|
|
*/
|
|
if (((uint64_t)output[j].mr_start +
|
|
(uint64_t)output[j].mr_size) >
|
|
BUS_SPACE_MAXADDR_32BIT) {
|
|
output[j].mr_size = BUS_SPACE_MAXADDR_32BIT -
|
|
output[j].mr_start;
|
|
}
|
|
#endif
|
|
|
|
j++;
|
|
}
|
|
sz = j*sizeof(output[0]);
|
|
|
|
#ifdef __powerpc64__
|
|
if (apple_hack_mode) {
|
|
/* Add in regions above 4 GB to the available list */
|
|
struct mem_region himem[16];
|
|
int hisz;
|
|
|
|
hisz = parse_ofw_memory(node, "reg", himem);
|
|
for (i = 0; i < hisz/sizeof(himem[0]); i++) {
|
|
if (himem[i].mr_start > BUS_SPACE_MAXADDR_32BIT) {
|
|
output[j].mr_start = himem[i].mr_start;
|
|
output[j].mr_size = himem[i].mr_size;
|
|
j++;
|
|
}
|
|
}
|
|
sz = j*sizeof(output[0]);
|
|
}
|
|
#endif
|
|
|
|
return (sz);
|
|
}
|
|
|
|
static int
|
|
parse_drconf_memory(int *msz, int *asz, struct mem_region *ofmem,
|
|
struct mem_region *ofavail)
|
|
{
|
|
phandle_t phandle;
|
|
vm_offset_t base;
|
|
int i, idx, len, lasz, lmsz, res;
|
|
uint32_t lmb_size[2];
|
|
unsigned long *dmem, flags;
|
|
|
|
lmsz = *msz;
|
|
lasz = *asz;
|
|
|
|
phandle = OF_finddevice("/ibm,dynamic-reconfiguration-memory");
|
|
if (phandle == -1)
|
|
/* No drconf node, return. */
|
|
return (0);
|
|
|
|
res = OF_getprop(phandle, "ibm,lmb-size", lmb_size, sizeof(lmb_size));
|
|
if (res == -1)
|
|
return (0);
|
|
|
|
/* Parse the /ibm,dynamic-memory.
|
|
The first position gives the # of entries. The next two words
|
|
reflect the address of the memory block. The next four words are
|
|
the DRC index, reserved, list index and flags.
|
|
(see PAPR C.6.6.2 ibm,dynamic-reconfiguration-memory)
|
|
|
|
#el Addr DRC-idx res list-idx flags
|
|
-------------------------------------------------
|
|
| 4 | 8 | 4 | 4 | 4 | 4 |....
|
|
-------------------------------------------------
|
|
*/
|
|
|
|
len = OF_getproplen(phandle, "ibm,dynamic-memory");
|
|
if (len > 0) {
|
|
|
|
/* We have to use a variable length array on the stack
|
|
since we have very limited stack space.
|
|
*/
|
|
cell_t arr[len/sizeof(cell_t)];
|
|
|
|
res = OF_getprop(phandle, "ibm,dynamic-memory", &arr,
|
|
sizeof(arr));
|
|
if (res == -1)
|
|
return (0);
|
|
|
|
/* Number of elements */
|
|
idx = arr[0];
|
|
|
|
/* First address. */
|
|
dmem = (void*)&arr[1];
|
|
|
|
for (i = 0; i < idx; i++) {
|
|
base = *dmem;
|
|
dmem += 2;
|
|
flags = *dmem;
|
|
/* Use region only if available and not reserved. */
|
|
if ((flags & 0x8) && !(flags & 0x80)) {
|
|
ofmem[lmsz].mr_start = base;
|
|
ofmem[lmsz].mr_size = (vm_size_t)lmb_size[1];
|
|
ofavail[lasz].mr_start = base;
|
|
ofavail[lasz].mr_size = (vm_size_t)lmb_size[1];
|
|
lmsz++;
|
|
lasz++;
|
|
}
|
|
dmem++;
|
|
}
|
|
}
|
|
|
|
*msz = lmsz;
|
|
*asz = lasz;
|
|
|
|
return (1);
|
|
}
|
|
/*
|
|
* This is called during powerpc_init, before the system is really initialized.
|
|
* It shall provide the total and the available regions of RAM.
|
|
* Both lists must have a zero-size entry as terminator.
|
|
* The available regions need not take the kernel into account, but needs
|
|
* to provide space for two additional entry beyond the terminating one.
|
|
*/
|
|
void
|
|
ofw_mem_regions(struct mem_region **memp, int *memsz,
|
|
struct mem_region **availp, int *availsz)
|
|
{
|
|
phandle_t phandle;
|
|
vm_offset_t maxphysaddr;
|
|
int asz, msz, fsz;
|
|
int i, j, res;
|
|
int still_merging;
|
|
char name[31];
|
|
|
|
asz = msz = 0;
|
|
|
|
/*
|
|
* Get memory from all the /memory nodes.
|
|
*/
|
|
for (phandle = OF_child(OF_peer(0)); phandle != 0;
|
|
phandle = OF_peer(phandle)) {
|
|
if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0)
|
|
continue;
|
|
if (strncmp(name, "memory", sizeof(name)) != 0)
|
|
continue;
|
|
|
|
res = parse_ofw_memory(phandle, "reg", &OFmem[msz]);
|
|
msz += res/sizeof(struct mem_region);
|
|
if (OF_getproplen(phandle, "available") >= 0)
|
|
res = parse_ofw_memory(phandle, "available",
|
|
&OFavail[asz]);
|
|
else
|
|
res = parse_ofw_memory(phandle, "reg", &OFavail[asz]);
|
|
asz += res/sizeof(struct mem_region);
|
|
}
|
|
|
|
/* Check for memory in ibm,dynamic-reconfiguration-memory */
|
|
parse_drconf_memory(&msz, &asz, OFmem, OFavail);
|
|
|
|
qsort(OFmem, msz, sizeof(*OFmem), mr_cmp);
|
|
qsort(OFavail, asz, sizeof(*OFavail), mr_cmp);
|
|
|
|
*memp = OFmem;
|
|
*memsz = msz;
|
|
|
|
/*
|
|
* On some firmwares (SLOF), some memory may be marked available that
|
|
* doesn't actually exist. This manifests as an extension of the last
|
|
* available segment past the end of physical memory, so truncate that
|
|
* one.
|
|
*/
|
|
maxphysaddr = 0;
|
|
for (i = 0; i < msz; i++)
|
|
if (OFmem[i].mr_start + OFmem[i].mr_size > maxphysaddr)
|
|
maxphysaddr = OFmem[i].mr_start + OFmem[i].mr_size;
|
|
|
|
if (OFavail[asz - 1].mr_start + OFavail[asz - 1].mr_size > maxphysaddr)
|
|
OFavail[asz - 1].mr_size = maxphysaddr -
|
|
OFavail[asz - 1].mr_start;
|
|
|
|
/*
|
|
* OFavail may have overlapping regions - collapse these
|
|
* and copy out remaining regions to OFfree
|
|
*/
|
|
do {
|
|
still_merging = FALSE;
|
|
for (i = 0; i < asz; i++) {
|
|
if (OFavail[i].mr_size == 0)
|
|
continue;
|
|
for (j = i+1; j < asz; j++) {
|
|
if (OFavail[j].mr_size == 0)
|
|
continue;
|
|
if (memr_overlap(&OFavail[j], &OFavail[i])) {
|
|
memr_merge(&OFavail[j], &OFavail[i]);
|
|
/* mark inactive */
|
|
OFavail[j].mr_size = 0;
|
|
still_merging = TRUE;
|
|
}
|
|
}
|
|
}
|
|
} while (still_merging == TRUE);
|
|
|
|
/* evict inactive ranges */
|
|
for (i = 0, fsz = 0; i < asz; i++) {
|
|
if (OFavail[i].mr_size != 0) {
|
|
OFfree[fsz] = OFavail[i];
|
|
fsz++;
|
|
}
|
|
}
|
|
|
|
*availp = OFfree;
|
|
*availsz = fsz;
|
|
}
|
|
|
|
void
|
|
OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *))
|
|
{
|
|
if (ofmsr[0] & PSL_DR)
|
|
ofw_real_mode = 0;
|
|
else
|
|
ofw_real_mode = 1;
|
|
|
|
fdt = fdt_ptr;
|
|
|
|
#ifdef FDT_DTB_STATIC
|
|
/* Check for a statically included blob */
|
|
if (fdt == NULL)
|
|
fdt = &fdt_static_dtb;
|
|
#endif
|
|
}
|
|
|
|
boolean_t
|
|
OF_bootstrap()
|
|
{
|
|
boolean_t status = FALSE;
|
|
|
|
if (openfirmware_entry != NULL) {
|
|
if (ofw_real_mode) {
|
|
status = OF_install(OFW_STD_REAL, 0);
|
|
} else {
|
|
#ifdef __powerpc64__
|
|
status = OF_install(OFW_STD_32BIT, 0);
|
|
#else
|
|
status = OF_install(OFW_STD_DIRECT, 0);
|
|
#endif
|
|
}
|
|
|
|
if (status != TRUE)
|
|
return status;
|
|
|
|
OF_init(openfirmware);
|
|
|
|
/*
|
|
* On some machines, we need to quiesce OF to turn off
|
|
* background processes.
|
|
*/
|
|
ofw_quiesce();
|
|
} else if (fdt != NULL) {
|
|
status = OF_install(OFW_FDT, 0);
|
|
|
|
if (status != TRUE)
|
|
return status;
|
|
|
|
OF_init(fdt);
|
|
}
|
|
|
|
return (status);
|
|
}
|
|
|
|
static void
|
|
ofw_quiesce(void)
|
|
{
|
|
phandle_t rootnode;
|
|
char model[32];
|
|
struct {
|
|
cell_t name;
|
|
cell_t nargs;
|
|
cell_t nreturns;
|
|
} args;
|
|
|
|
/*
|
|
* Only quiesce Open Firmware on PowerMac11,2 and 12,1. It is
|
|
* necessary there to shut down a background thread doing fan
|
|
* management, and is harmful on other machines.
|
|
*
|
|
* Note: we don't need to worry about which OF module we are
|
|
* using since this is called only from very early boot, within
|
|
* OF's boot context.
|
|
*/
|
|
|
|
rootnode = OF_finddevice("/");
|
|
if (OF_getprop(rootnode, "model", model, sizeof(model)) > 0) {
|
|
if (strcmp(model, "PowerMac11,2") == 0 ||
|
|
strcmp(model, "PowerMac12,1") == 0) {
|
|
args.name = (cell_t)(uintptr_t)"quiesce";
|
|
args.nargs = 0;
|
|
args.nreturns = 0;
|
|
openfirmware(&args);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
openfirmware_core(void *args)
|
|
{
|
|
int result;
|
|
register_t oldmsr;
|
|
|
|
/*
|
|
* Turn off exceptions - we really don't want to end up
|
|
* anywhere unexpected with PCPU set to something strange
|
|
* or the stack pointer wrong.
|
|
*/
|
|
oldmsr = intr_disable();
|
|
|
|
ofw_sprg_prepare();
|
|
|
|
#if defined(AIM) && !defined(__powerpc64__)
|
|
/*
|
|
* Clear battable[] translations
|
|
*/
|
|
if (!(cpu_features & PPC_FEATURE_64))
|
|
__asm __volatile("mtdbatu 2, %0\n"
|
|
"mtdbatu 3, %0" : : "r" (0));
|
|
isync();
|
|
#endif
|
|
|
|
result = ofwcall(args);
|
|
ofw_sprg_restore();
|
|
|
|
intr_restore(oldmsr);
|
|
|
|
return (result);
|
|
}
|
|
|
|
#ifdef SMP
|
|
struct ofw_rv_args {
|
|
void *args;
|
|
int retval;
|
|
volatile int in_progress;
|
|
};
|
|
|
|
static void
|
|
ofw_rendezvous_dispatch(void *xargs)
|
|
{
|
|
struct ofw_rv_args *rv_args = xargs;
|
|
|
|
/* NOTE: Interrupts are disabled here */
|
|
|
|
if (PCPU_GET(cpuid) == 0) {
|
|
/*
|
|
* Execute all OF calls on CPU 0
|
|
*/
|
|
rv_args->retval = openfirmware_core(rv_args->args);
|
|
rv_args->in_progress = 0;
|
|
} else {
|
|
/*
|
|
* Spin with interrupts off on other CPUs while OF has
|
|
* control of the machine.
|
|
*/
|
|
while (rv_args->in_progress)
|
|
cpu_spinwait();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static int
|
|
openfirmware(void *args)
|
|
{
|
|
int result;
|
|
#ifdef SMP
|
|
struct ofw_rv_args rv_args;
|
|
|
|
rv_args.args = args;
|
|
rv_args.in_progress = 1;
|
|
smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch,
|
|
smp_no_rendevous_barrier, &rv_args);
|
|
result = rv_args.retval;
|
|
#else
|
|
result = openfirmware_core(args);
|
|
#endif
|
|
|
|
return (result);
|
|
}
|
|
|
|
void
|
|
OF_reboot()
|
|
{
|
|
struct {
|
|
cell_t name;
|
|
cell_t nargs;
|
|
cell_t nreturns;
|
|
cell_t arg;
|
|
} args;
|
|
|
|
args.name = (cell_t)(uintptr_t)"interpret";
|
|
args.nargs = 1;
|
|
args.nreturns = 0;
|
|
args.arg = (cell_t)(uintptr_t)"reset-all";
|
|
openfirmware_core(&args); /* Don't do rendezvous! */
|
|
|
|
for (;;); /* just in case */
|
|
}
|
|
|
|
void
|
|
OF_getetheraddr(device_t dev, u_char *addr)
|
|
{
|
|
phandle_t node;
|
|
|
|
node = ofw_bus_get_node(dev);
|
|
OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN);
|
|
}
|
|
|
|
/*
|
|
* Return a bus handle and bus tag that corresponds to the register
|
|
* numbered regno for the device referenced by the package handle
|
|
* dev. This function is intended to be used by console drivers in
|
|
* early boot only. It works by mapping the address of the device's
|
|
* register in the address space of its parent and recursively walk
|
|
* the device tree upward this way.
|
|
*/
|
|
static void
|
|
OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip)
|
|
{
|
|
char name[16];
|
|
uint32_t addr, size;
|
|
int pci, res;
|
|
|
|
res = OF_getprop(node, "#address-cells", &addr, sizeof(addr));
|
|
if (res == -1)
|
|
addr = 2;
|
|
res = OF_getprop(node, "#size-cells", &size, sizeof(size));
|
|
if (res == -1)
|
|
size = 1;
|
|
pci = 0;
|
|
if (addr == 3 && size == 2) {
|
|
res = OF_getprop(node, "name", name, sizeof(name));
|
|
if (res != -1) {
|
|
name[sizeof(name) - 1] = '\0';
|
|
pci = (strcmp(name, "pci") == 0) ? 1 : 0;
|
|
}
|
|
}
|
|
if (addrp != NULL)
|
|
*addrp = addr;
|
|
if (sizep != NULL)
|
|
*sizep = size;
|
|
if (pcip != NULL)
|
|
*pcip = pci;
|
|
}
|
|
|
|
int
|
|
OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag,
|
|
bus_space_handle_t *handle)
|
|
{
|
|
uint32_t cell[32];
|
|
bus_addr_t addr, raddr, baddr;
|
|
bus_size_t size, rsize;
|
|
uint32_t c, nbridge, naddr, nsize;
|
|
phandle_t bridge, parent;
|
|
u_int spc, rspc, prefetch;
|
|
int pci, pcib, res;
|
|
|
|
/* Sanity checking. */
|
|
if (dev == 0)
|
|
return (EINVAL);
|
|
bridge = OF_parent(dev);
|
|
if (bridge == 0)
|
|
return (EINVAL);
|
|
if (regno < 0)
|
|
return (EINVAL);
|
|
if (tag == NULL || handle == NULL)
|
|
return (EINVAL);
|
|
|
|
/* Get the requested register. */
|
|
OF_get_addr_props(bridge, &naddr, &nsize, &pci);
|
|
res = OF_getprop(dev, (pci) ? "assigned-addresses" : "reg",
|
|
cell, sizeof(cell));
|
|
if (res == -1)
|
|
return (ENXIO);
|
|
if (res % sizeof(cell[0]))
|
|
return (ENXIO);
|
|
res /= sizeof(cell[0]);
|
|
regno *= naddr + nsize;
|
|
if (regno + naddr + nsize > res)
|
|
return (EINVAL);
|
|
spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0;
|
|
prefetch = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_PREFETCHABLE : 0;
|
|
addr = 0;
|
|
for (c = 0; c < naddr; c++)
|
|
addr = ((uint64_t)addr << 32) | cell[regno++];
|
|
size = 0;
|
|
for (c = 0; c < nsize; c++)
|
|
size = ((uint64_t)size << 32) | cell[regno++];
|
|
|
|
/*
|
|
* Map the address range in the bridge's decoding window as given
|
|
* by the "ranges" property. If a node doesn't have such property
|
|
* then no mapping is done.
|
|
*/
|
|
parent = OF_parent(bridge);
|
|
while (parent != 0) {
|
|
OF_get_addr_props(parent, &nbridge, NULL, &pcib);
|
|
res = OF_getprop(bridge, "ranges", cell, sizeof(cell));
|
|
if (res == -1)
|
|
goto next;
|
|
if (res % sizeof(cell[0]))
|
|
return (ENXIO);
|
|
res /= sizeof(cell[0]);
|
|
regno = 0;
|
|
while (regno < res) {
|
|
rspc = (pci)
|
|
? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
|
|
: ~0;
|
|
if (rspc != spc) {
|
|
regno += naddr + nbridge + nsize;
|
|
continue;
|
|
}
|
|
raddr = 0;
|
|
for (c = 0; c < naddr; c++)
|
|
raddr = ((uint64_t)raddr << 32) | cell[regno++];
|
|
rspc = (pcib)
|
|
? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
|
|
: ~0;
|
|
baddr = 0;
|
|
for (c = 0; c < nbridge; c++)
|
|
baddr = ((uint64_t)baddr << 32) | cell[regno++];
|
|
rsize = 0;
|
|
for (c = 0; c < nsize; c++)
|
|
rsize = ((uint64_t)rsize << 32) | cell[regno++];
|
|
if (addr < raddr || addr >= raddr + rsize)
|
|
continue;
|
|
addr = addr - raddr + baddr;
|
|
if (rspc != ~0)
|
|
spc = rspc;
|
|
}
|
|
|
|
next:
|
|
bridge = parent;
|
|
parent = OF_parent(bridge);
|
|
OF_get_addr_props(bridge, &naddr, &nsize, &pci);
|
|
}
|
|
|
|
*tag = &bs_le_tag;
|
|
return (bus_space_map(*tag, addr, size,
|
|
prefetch ? BUS_SPACE_MAP_PREFETCHABLE : 0, handle));
|
|
}
|
|
|