freebsd-skq/sys/sparc64/sparc64/ofw_machdep.c
Marius Strobl 820992ecfd - Make sure that the OFW address properties that are going to be decode
consist of the expected number of address and size cells (we can't use
  dynamic arrays here because at the point in the boot process when this
  code is used malloc() doesn't work, yet). This fixes a Fast Data Access
  MMU Miss when uart(4) (erroneously) calls OF_decode_addr() to decode
  the address of PS/2 keyboards. PS/2 keyboards use a different and also
  undocumented scheme at the first parent node than mapping at 'ranges'
  properties. It's however not worth implementing that other scheme and
  actually also fits atkbdc(4) better to just start at the first parent
  node of PS/2 keyboards which is the 8042 controller (I have atkbdc(4)
  working that way).
- Use FBSDID.

MFC after:	1 month
2005-05-21 20:17:01 +00:00

268 lines
8.6 KiB
C

/*-
* 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
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 THE AUTHOR OR CONTRIBUTORS 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Some Open Firmware helper functions that are likely machine dependent.
*/
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/systm.h>
#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>
void
OF_getetheraddr(device_t dev, u_char *addr)
{
char buf[sizeof("true")];
phandle_t node;
struct idprom idp;
if ((node = OF_finddevice("/options")) > 0 &&
OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) {
buf[sizeof(buf) - 1] = '\0';
if (strcmp(buf, "true") == 0 &&
(node = ofw_bus_get_node(dev)) > 0 &&
OF_getprop(node, "local-mac-address", addr,
ETHER_ADDR_LEN) == ETHER_ADDR_LEN)
return;
}
node = OF_peer(0);
if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1)
panic("Could not determine the machine ethernet address");
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 bank, int *space, bus_addr_t *addr)
{
char name[32];
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 (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1)
addrc = 2;
if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
szc = 1;
if (addrc < 2 || addrc > 3 || szc < 1 || szc > 2)
return (ENXIO);
if (strcmp(name, "pci") == 0) {
if (addrc > 3)
return (ENXIO);
nbank = OF_getprop(node, "assigned-addresses", &banks,
sizeof(banks));
} else {
if (addrc > 2)
return (ENXIO);
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]);
/*
* 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 < 2 || 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';
goto skip;
}
if (lbus != bus) {
if (OF_getprop(bus, "#size-cells", &szc,
sizeof(szc)) == -1)
szc = 1;
if (szc < 1 || szc > 2)
return (ENXIO);
}
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;
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;
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);
}