ofw_machdep.c revision 146473
1/*-
2 * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
3 * Copyright (c) 2005 by Marius Strobl <marius@FreeBSD.org>.
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 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
19 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
24 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/ofw_machdep.c 146473 2005-05-21 20:17:01Z marius $");
29
30/*
31 * Some Open Firmware helper functions that are likely machine dependent.
32 */
33
34#include <sys/param.h>
35#include <sys/bus.h>
36#include <sys/systm.h>
37
38#include <net/ethernet.h>
39
40#include <dev/ofw/ofw_bus.h>
41#include <dev/ofw/ofw_pci.h>
42#include <dev/ofw/openfirm.h>
43
44#include <machine/bus.h>
45#include <machine/idprom.h>
46#include <machine/ofw_bus.h>
47#include <machine/ofw_machdep.h>
48
49void
50OF_getetheraddr(device_t dev, u_char *addr)
51{
52	char buf[sizeof("true")];
53	phandle_t node;
54	struct idprom idp;
55
56	if ((node = OF_finddevice("/options")) > 0 &&
57	    OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) {
58		buf[sizeof(buf) - 1] = '\0';
59		if (strcmp(buf, "true") == 0 &&
60		    (node = ofw_bus_get_node(dev)) > 0 &&
61		    OF_getprop(node, "local-mac-address", addr,
62		    ETHER_ADDR_LEN) == ETHER_ADDR_LEN)
63			return;
64	}
65
66	node = OF_peer(0);
67	if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1)
68		panic("Could not determine the machine ethernet address");
69	bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN);
70}
71
72static __inline uint32_t
73phys_hi_mask_space(const char *bus, uint32_t phys_hi)
74{
75	uint32_t space;
76
77	space = phys_hi;
78	if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0)
79		space &= 0x1;
80	else if (strcmp(bus, "pci") == 0)
81		space &= OFW_PCI_PHYS_HI_SPACEMASK;
82	/* The phys.hi cells of the other busses only contain space bits. */
83	return (space);
84}
85
86/*
87 * Return the physical address and the bus space to use for a node
88 * referenced by its package handle and the index of the register bank
89 * to decode. Intended to be used to together with sparc64_fake_bustag()
90 * by console drivers in early boot only.
91 * Works by mapping the address of the node's bank given in the address
92 * space of its parent upward in the device tree at each bridge along the
93 * path.
94 * Currently only really deals with max. 64-bit addresses, i.e. addresses
95 * consisting of max. 2 phys cells (phys.hi and phys.lo). If we encounter
96 * a 3 phys cells address (as with PCI addresses) we assume phys.hi can
97 * be ignored except for the space bits (generally contained in phys.hi)
98 * and treat phys.mid as phys.hi.
99 */
100int
101OF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr)
102{
103	char name[32];
104	uint64_t cend, cstart, end, phys, sz, start;
105	pcell_t addrc, szc, paddrc;
106	phandle_t bus, lbus, pbus;
107	uint32_t banks[10 * 5];	/* 10 PCI banks */
108	uint32_t cspace, spc;
109	int i, j, nbank;
110
111	/*
112	 * In general the addresses are contained in the "reg" property
113	 * of a node. The first address in the "reg" property of a PCI
114	 * node however is the address of its configuration registers in
115	 * the configuration space of the host bridge. Additional entries
116	 * denote the memory and I/O addresses. For relocatable addresses
117	 * the "reg" property contains the BAR, for non-relocatable
118	 * addresses it contains the absolute PCI address. The PCI-only
119	 * "assigned-addresses" property however always contains the
120	 * absolute PCI addresses.
121	 * The "assigned-addresses" and "reg" properties are arrays of
122	 * address structures consisting of #address-cells 32-bit phys
123	 * cells and #size-cells 32-bit size cells. If a parent lacks
124	 * the "#address-cells" or "#size-cells" property the default
125	 * for #address-cells to use is 2 and for #size-cells 1.
126	 */
127	bus = OF_parent(node);
128	if (bus == 0)
129		return (ENXIO);
130	if (OF_getprop(bus, "name", name, sizeof(name)) == -1)
131		return (ENXIO);
132	name[sizeof(name) - 1] = '\0';
133	if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1)
134		addrc = 2;
135	if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
136		szc = 1;
137	if (addrc < 2 || addrc > 3 || szc < 1 || szc > 2)
138		return (ENXIO);
139	if (strcmp(name, "pci") == 0) {
140		if (addrc > 3)
141			return (ENXIO);
142		nbank = OF_getprop(node, "assigned-addresses", &banks,
143		    sizeof(banks));
144	} else {
145		if (addrc > 2)
146			return (ENXIO);
147		nbank = OF_getprop(node, "reg", &banks, sizeof(banks));
148	}
149	if (nbank == -1)
150		return (ENXIO);
151	nbank /= sizeof(banks[0]) * (addrc + szc);
152	if (bank < 0 || bank > nbank - 1)
153		return (ENXIO);
154	phys = 0;
155	for (i = 0; i < MIN(2, addrc); i++)
156		phys |= (uint64_t)banks[(addrc + szc) * bank + addrc - 2 + i] <<
157		    32 * (MIN(2, addrc) - i - 1);
158	sz = 0;
159	for (i = 0; i < szc; i++)
160		sz |= (uint64_t)banks[(addrc + szc) * bank + addrc + i] <<
161		    32 * (szc - i - 1);
162	start = phys;
163	end = phys + sz - 1;
164	spc = phys_hi_mask_space(name, banks[(addrc + szc) * bank]);
165
166	/*
167	 * Map upward in the device tree at every bridge we encounter
168	 * using their "ranges" properties.
169	 * The "ranges" property of a bridge is an array of a structure
170	 * consisting of that bridge's #address-cells 32-bit child-phys
171	 * cells, its parent bridge #address-cells 32-bit parent-phys
172	 * cells and that bridge's #size-cells 32-bit size cells.
173	 * If a bridge doesn't have a "ranges" property no mapping is
174	 * necessary at that bridge.
175	 */
176	cspace = 0;
177	lbus = bus;
178	while ((pbus = OF_parent(bus)) != 0) {
179		if (OF_getprop(pbus, "#address-cells", &paddrc,
180		    sizeof(paddrc)) == -1)
181			paddrc = 2;
182		if (paddrc < 2 || paddrc > 3)
183			return (ENXIO);
184		nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks));
185		if (nbank == -1) {
186			if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
187				return (ENXIO);
188			name[sizeof(name) - 1] = '\0';
189			goto skip;
190		}
191		if (lbus != bus) {
192			if (OF_getprop(bus, "#size-cells", &szc,
193			    sizeof(szc)) == -1)
194				szc = 1;
195			if (szc < 1 || szc > 2)
196				return (ENXIO);
197		}
198		nbank /= sizeof(banks[0]) * (addrc + paddrc + szc);
199		for (i = 0; i < nbank; i++) {
200			cspace = phys_hi_mask_space(name,
201			    banks[(addrc + paddrc + szc) * i]);
202			if (cspace != spc)
203				continue;
204			phys = 0;
205			for (j = 0; j < MIN(2, addrc); j++)
206				phys |= (uint64_t)banks[
207				    (addrc + paddrc + szc) * i +
208				    addrc - 2 + j] <<
209				    32 * (MIN(2, addrc) - j - 1);
210			sz = 0;
211			for (j = 0; j < szc; j++)
212				sz |= (uint64_t)banks[
213				    (addrc + paddrc + szc) * i + addrc +
214				    paddrc + j] <<
215				    32 * (szc - j - 1);
216			cstart = phys;
217			cend = phys + sz - 1;
218			if (start < cstart || start > cend)
219				continue;
220			if (end < cstart || end > cend)
221				return (ENXIO);
222			phys = 0;
223			for (j = 0; j < MIN(2, paddrc); j++)
224				phys |= (uint64_t)banks[
225				    (addrc + paddrc + szc) * i + addrc +
226				    paddrc - 2 + j] <<
227				    32 * (MIN(2, paddrc) - j - 1);
228			start += phys - cstart;
229			end += phys - cstart;
230			if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
231				return (ENXIO);
232			name[sizeof(name) - 1] = '\0';
233			spc = phys_hi_mask_space(name,
234			    banks[(addrc + paddrc + szc) * i + addrc]);
235			break;
236		}
237		if (i == nbank)
238			return (ENXIO);
239 skip:
240		addrc = paddrc;
241		lbus = bus;
242		bus = pbus;
243	}
244
245	/* Done with mapping. Return the bus space as used by FreeBSD. */
246	*addr = start;
247	if (OF_getprop(lbus, "name", name, sizeof(name)) == -1)
248		return (ENXIO);
249	name[sizeof(name) - 1] = '\0';
250	if (strcmp(name, "central") == 0) {
251		*space = UPA_BUS_SPACE;
252		return (0);
253	} else if (strcmp(name, "pci") == 0) {
254		switch (cspace) {
255		case OFW_PCI_PHYS_HI_SPACE_IO:
256			*space = PCI_IO_BUS_SPACE;
257			return (0);
258		case OFW_PCI_PHYS_HI_SPACE_MEM32:
259			*space = PCI_MEMORY_BUS_SPACE;
260			return (0);
261		}
262	} else if (strcmp(name, "sbus") == 0) {
263		*space = SBUS_BUS_SPACE;
264		return (0);
265	}
266	return (ENXIO);
267}
268