summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/uart/uart_cpu_sparc64.c2
-rw-r--r--sys/sparc64/include/ofw_machdep.h3
-rw-r--r--sys/sparc64/sparc64/ofw_machdep.c257
3 files changed, 176 insertions, 86 deletions
diff --git a/sys/dev/uart/uart_cpu_sparc64.c b/sys/dev/uart/uart_cpu_sparc64.c
index efb8ba7..2b4abb7 100644
--- a/sys/dev/uart/uart_cpu_sparc64.c
+++ b/sys/dev/uart/uart_cpu_sparc64.c
@@ -200,7 +200,7 @@ uart_cpu_getdev(int devtype, struct uart_devinfo *di)
}
if (input == -1)
return (ENXIO);
- error = OF_decode_addr(input, &space, &addr);
+ error = OF_decode_addr(input, 0, &space, &addr);
if (error)
return (error);
diff --git a/sys/sparc64/include/ofw_machdep.h b/sys/sparc64/include/ofw_machdep.h
index f7062f7..ac1fcc1 100644
--- a/sys/sparc64/include/ofw_machdep.h
+++ b/sys/sparc64/include/ofw_machdep.h
@@ -30,10 +30,9 @@
#include <sys/bus.h>
-int OF_decode_addr(phandle_t, int *, bus_addr_t *);
+int OF_decode_addr(phandle_t, int, int *, bus_addr_t *);
void OF_getetheraddr(device_t, u_char *);
void cpu_shutdown(void *);
void openfirmware_exit(void *);
#endif /* _MACHINE_OFW_MACHDEP_H_ */
-
diff --git a/sys/sparc64/sparc64/ofw_machdep.c b/sys/sparc64/sparc64/ofw_machdep.c
index 3bd7394..391af4c 100644
--- a/sys/sparc64/sparc64/ofw_machdep.c
+++ b/sys/sparc64/sparc64/ofw_machdep.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
+ * Copyright (c) 2005 by Marius Strobl <marius@FreeBSD.org>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -36,17 +37,13 @@
#include <net/ethernet.h>
#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_pci.h>
#include <dev/ofw/openfirm.h>
#include <machine/bus.h>
#include <machine/idprom.h>
+#include <machine/ofw_bus.h>
#include <machine/ofw_machdep.h>
-#include <machine/ofw_upa.h>
-#include <machine/resource.h>
-
-#include <sparc64/pci/ofw_pci.h>
-#include <sparc64/isa/ofw_isa.h>
-#include <sparc64/sbus/ofw_sbus.h>
void
OF_getetheraddr(device_t dev, u_char *addr)
@@ -71,105 +68,199 @@ OF_getetheraddr(device_t dev, u_char *addr)
bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN);
}
+static __inline uint32_t
+phys_hi_mask_space(const char *bus, uint32_t phys_hi)
+{
+ uint32_t space;
+
+ space = phys_hi;
+ if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0)
+ space &= 0x1;
+ else if (strcmp(bus, "pci") == 0)
+ space &= OFW_PCI_PHYS_HI_SPACEMASK;
+ /* The phys.hi cells of the other busses only contain space bits. */
+ return (space);
+}
+
+/*
+ * Return the physical address and the bus space to use for a node
+ * referenced by its package handle and the index of the register bank
+ * to decode. Intended to be used to together with sparc64_fake_bustag()
+ * by console drivers in early boot only.
+ * Works by mapping the address of the node's bank given in the address
+ * space of its parent upward in the device tree at each bridge along the
+ * path.
+ * Currently only really deals with max. 64-bit addresses, i.e. addresses
+ * consisting of max. 2 phys cells (phys.hi and phys.lo). If we encounter
+ * a 3 phys cells address (as with PCI addresses) we assume phys.hi can
+ * be ignored except for the space bits (generally contained in phys.hi)
+ * and treat phys.mid as phys.hi.
+ */
int
-OF_decode_addr(phandle_t node, int *space, bus_addr_t *addr)
+OF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr)
{
char name[32];
- union {
- struct isa_ranges isa[4];
- struct sbus_ranges sbus[8];
- struct upa_ranges upa[4];
- } range;
- union {
- struct isa_regs isa;
- struct sbus_regs sbus;
- } reg;
- phandle_t bus, pbus;
- u_long child, dummy, phys;
- int cs, i, rsz, type;
+ uint64_t cend, cstart, end, phys, sz, start;
+ pcell_t addrc, szc, paddrc;
+ phandle_t bus, lbus, pbus;
+ uint32_t banks[10 * 5]; /* 10 PCI banks */
+ uint32_t cspace, spc;
+ int i, j, nbank;
+ /*
+ * In general the addresses are contained in the "reg" property
+ * of a node. The first address in the "reg" property of a PCI
+ * node however is the address of its configuration registers in
+ * the configuration space of the host bridge. Additional entries
+ * denote the memory and I/O addresses. For relocatable addresses
+ * the "reg" property contains the BAR, for non-relocatable
+ * addresses it contains the absolute PCI address. The PCI-only
+ * "assigned-addresses" property however always contains the
+ * absolute PCI addresses.
+ * The "assigned-addresses" and "reg" properties are arrays of
+ * address structures consisting of #address-cells 32-bit phys
+ * cells and #size-cells 32-bit size cells. If a parent lacks
+ * the "#address-cells" or "#size-cells" property the default
+ * for #address-cells to use is 2 and for #size-cells 1.
+ */
bus = OF_parent(node);
if (bus == 0)
return (ENXIO);
if (OF_getprop(bus, "name", name, sizeof(name)) == -1)
return (ENXIO);
name[sizeof(name) - 1] = '\0';
- if (strcmp(name, "ebus") == 0 || strcmp(name, "isa") == 0) {
- if (OF_getprop(node, "reg", &reg.isa, sizeof(reg.isa)) == -1)
+ if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1)
+ addrc = 2;
+ if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
+ szc = 1;
+ if (szc > 2)
+ return (ENXIO);
+ if (strcmp(name, "pci") == 0) {
+ if (addrc > 3)
return (ENXIO);
- rsz = OF_getprop(bus, "ranges", range.isa, sizeof(range.isa));
- if (rsz == -1)
+ nbank = OF_getprop(node, "assigned-addresses", &banks,
+ sizeof(banks));
+ } else {
+ if (addrc > 2)
return (ENXIO);
- phys = ISA_REG_PHYS(&reg.isa);
- dummy = phys + 1;
- type = ofw_isa_range_map(range.isa, rsz / sizeof(*range.isa),
- &phys, &dummy, NULL);
- if (type == SYS_RES_MEMORY) {
- cs = PCI_CS_MEM32;
- *space = PCI_MEMORY_BUS_SPACE;
- } else {
- cs = PCI_CS_IO;
- *space = PCI_IO_BUS_SPACE;
- }
+ nbank = OF_getprop(node, "reg", &banks, sizeof(banks));
+ }
+ if (nbank == -1)
+ return (ENXIO);
+ nbank /= sizeof(banks[0]) * (addrc + szc);
+ if (bank < 0 || bank > nbank - 1)
+ return (ENXIO);
+ phys = 0;
+ for (i = 0; i < MIN(2, addrc); i++)
+ phys |= (uint64_t)banks[(addrc + szc) * bank + addrc - 2 + i] <<
+ 32 * (MIN(2, addrc) - i - 1);
+ sz = 0;
+ for (i = 0; i < szc; i++)
+ sz |= (uint64_t)banks[(addrc + szc) * bank + addrc + i] <<
+ 32 * (szc - i - 1);
+ start = phys;
+ end = phys + sz - 1;
+ spc = phys_hi_mask_space(name, banks[(addrc + szc) * bank]);
- /* Find the topmost PCI node (the host bridge) */
- while (1) {
- pbus = OF_parent(bus);
- if (pbus == 0)
- return (ENXIO);
+ /*
+ * Map upward in the device tree at every bridge we encounter
+ * using their "ranges" properties.
+ * The "ranges" property of a bridge is an array of a structure
+ * consisting of that bridge's #address-cells 32-bit child-phys
+ * cells, its parent bridge #address-cells 32-bit parent-phys
+ * cells and that bridge's #size-cells 32-bit size cells.
+ * If a bridge doesn't have a "ranges" property no mapping is
+ * necessary at that bridge.
+ */
+ cspace = 0;
+ lbus = bus;
+ while ((pbus = OF_parent(bus)) != 0) {
+ if (OF_getprop(pbus, "#address-cells", &paddrc,
+ sizeof(paddrc)) == -1)
+ paddrc = 2;
+ if (paddrc > 3)
+ return (ENXIO);
+ nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks));
+ if (nbank == -1) {
if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
return (ENXIO);
name[sizeof(name) - 1] = '\0';
- if (strcmp(name, "pci") != 0)
- break;
- bus = pbus;
+ goto skip;
}
-
- /* There wasn't a PCI bridge. */
- if (bus == OF_parent(node))
- return (ENXIO);
-
- /* Make sure we reached the UPA/PCI node. */
- if (OF_getprop(pbus, "device_type", name, sizeof(name)) == -1)
- return (ENXIO);
- name[sizeof(name) - 1] = '\0';
- if (strcmp(name, "upa") != 0)
- return (ENXIO);
-
- rsz = OF_getprop(bus, "ranges", range.upa, sizeof(range.upa));
- if (rsz == -1)
- return (ENXIO);
- for (i = 0; i < (rsz / sizeof(range.upa[0])); i++) {
- child = UPA_RANGE_CHILD(&range.upa[i]);
- if (UPA_RANGE_CS(&range.upa[i]) == cs &&
- phys >= child &&
- phys - child < UPA_RANGE_SIZE(&range.upa[i])) {
- *addr = UPA_RANGE_PHYS(&range.upa[i]) + phys;
- return (0);
- }
+ if (lbus != bus) {
+ if (OF_getprop(bus, "#size-cells", &szc,
+ sizeof(szc)) == -1)
+ szc = 1;
+ if (szc > 2)
+ return (ENXIO);
}
- } else if (strcmp(name, "sbus") == 0) {
- if (OF_getprop(node, "reg", &reg.sbus, sizeof(reg.sbus)) == -1)
- return (ENXIO);
- rsz = OF_getprop(bus, "ranges", range.sbus,
- sizeof(range.sbus));
- if (rsz == -1)
- return (ENXIO);
- for (i = 0; i < (rsz / sizeof(range.sbus[0])); i++) {
- if (reg.sbus.sbr_slot != range.sbus[i].cspace)
+ nbank /= sizeof(banks[0]) * (addrc + paddrc + szc);
+ for (i = 0; i < nbank; i++) {
+ cspace = phys_hi_mask_space(name,
+ banks[(addrc + paddrc + szc) * i]);
+ if (cspace != spc)
continue;
- if (reg.sbus.sbr_offset < range.sbus[i].coffset ||
- reg.sbus.sbr_offset >= range.sbus[i].coffset +
- range.sbus[i].size)
+ phys = 0;
+ for (j = 0; j < MIN(2, addrc); j++)
+ phys |= (uint64_t)banks[
+ (addrc + paddrc + szc) * i +
+ addrc - 2 + j] <<
+ 32 * (MIN(2, addrc) - j - 1);
+ sz = 0;
+ for (j = 0; j < szc; j++)
+ sz |= (uint64_t)banks[
+ (addrc + paddrc + szc) * i + addrc +
+ paddrc + j] <<
+ 32 * (szc - j - 1);
+ cstart = phys;
+ cend = phys + sz - 1;
+ if (start < cstart || start > cend)
continue;
- /* Found it... */
- phys = range.sbus[i].poffset |
- ((bus_addr_t)range.sbus[i].pspace << 32);
- phys += reg.sbus.sbr_offset - range.sbus[i].coffset;
- *addr = phys;
- *space = SBUS_BUS_SPACE;
+ if (end < cstart || end > cend)
+ return (ENXIO);
+ phys = 0;
+ for (j = 0; j < MIN(2, paddrc); j++)
+ phys |= (uint64_t)banks[
+ (addrc + paddrc + szc) * i + addrc +
+ paddrc - 2 + j] <<
+ 32 * (MIN(2, paddrc) - j - 1);
+ start += phys - cstart;
+ end += phys - cstart;
+ if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
+ return (ENXIO);
+ name[sizeof(name) - 1] = '\0';
+ spc = phys_hi_mask_space(name,
+ banks[(addrc + paddrc + szc) * i + addrc]);
+ break;
+ }
+ if (i == nbank)
+ return (ENXIO);
+ skip:
+ addrc = paddrc;
+ lbus = bus;
+ bus = pbus;
+ }
+
+ /* Done with mapping. Return the bus space as used by FreeBSD. */
+ *addr = start;
+ if (OF_getprop(lbus, "name", name, sizeof(name)) == -1)
+ return (ENXIO);
+ name[sizeof(name) - 1] = '\0';
+ if (strcmp(name, "central") == 0) {
+ *space = UPA_BUS_SPACE;
+ return (0);
+ } else if (strcmp(name, "pci") == 0) {
+ switch (cspace) {
+ case OFW_PCI_PHYS_HI_SPACE_IO:
+ *space = PCI_IO_BUS_SPACE;
+ return (0);
+ case OFW_PCI_PHYS_HI_SPACE_MEM32:
+ *space = PCI_MEMORY_BUS_SPACE;
return (0);
}
+ } else if (strcmp(name, "sbus") == 0) {
+ *space = SBUS_BUS_SPACE;
+ return (0);
}
return (ENXIO);
}
OpenPOWER on IntegriCloud