ofw_machdep.c revision 141753
186229Stmm/*-
286229Stmm * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
3141753Smarius * Copyright (c) 2005 by Marius Strobl <marius@FreeBSD.org>.
486229Stmm * All rights reserved.
586229Stmm *
686229Stmm * Redistribution and use in source and binary forms, with or without
786229Stmm * modification, are permitted provided that the following conditions
886229Stmm * are met:
986229Stmm * 1. Redistributions of source code must retain the above copyright
1086229Stmm *    notice, this list of conditions and the following disclaimer.
1186229Stmm * 2. Redistributions in binary form must reproduce the above copyright
1286229Stmm *    notice, this list of conditions and the following disclaimer in the
1386229Stmm *    documentation and/or other materials provided with the distribution.
1486229Stmm *
1586229Stmm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1686229Stmm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1786229Stmm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1886229Stmm * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
1986229Stmm * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
2086229Stmm * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
2186229Stmm * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
2286229Stmm * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2386229Stmm * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
2486229Stmm * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2586229Stmm *
2686229Stmm * $FreeBSD: head/sys/sparc64/sparc64/ofw_machdep.c 141753 2005-02-12 19:13:51Z marius $
2786229Stmm */
2886229Stmm
2986229Stmm/*
30133862Smarius * Some Open Firmware helper functions that are likely machine dependent.
3186229Stmm */
3286229Stmm
3386229Stmm#include <sys/param.h>
34133589Smarius#include <sys/bus.h>
3586229Stmm#include <sys/systm.h>
3686229Stmm
3788370Stmm#include <net/ethernet.h>
3888370Stmm
39133589Smarius#include <dev/ofw/ofw_bus.h>
40141753Smarius#include <dev/ofw/ofw_pci.h>
41119338Simp#include <dev/ofw/openfirm.h>
4286229Stmm
43119697Smarcel#include <machine/bus.h>
4486229Stmm#include <machine/idprom.h>
45141753Smarius#include <machine/ofw_bus.h>
4686229Stmm#include <machine/ofw_machdep.h>
4786229Stmm
4886229Stmmvoid
4986229StmmOF_getetheraddr(device_t dev, u_char *addr)
5086229Stmm{
51133728Smarius	char buf[sizeof("true")];
5286229Stmm	phandle_t node;
5386229Stmm	struct idprom idp;
5486229Stmm
55133728Smarius	if ((node = OF_finddevice("/options")) > 0 &&
56133728Smarius	    OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) {
57133728Smarius		buf[sizeof(buf) - 1] = '\0';
58133728Smarius		if (strcmp(buf, "true") == 0 &&
59133728Smarius		    (node = ofw_bus_get_node(dev)) > 0 &&
60133728Smarius		    OF_getprop(node, "local-mac-address", addr,
61133728Smarius		    ETHER_ADDR_LEN) == ETHER_ADDR_LEN)
62133728Smarius			return;
63133728Smarius	}
64133728Smarius
6586229Stmm	node = OF_peer(0);
6686229Stmm	if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1)
6786229Stmm		panic("Could not determine the machine ethernet address");
6888370Stmm	bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN);
6986229Stmm}
70119697Smarcel
71141753Smariusstatic __inline uint32_t
72141753Smariusphys_hi_mask_space(const char *bus, uint32_t phys_hi)
73141753Smarius{
74141753Smarius	uint32_t space;
75141753Smarius
76141753Smarius	space = phys_hi;
77141753Smarius	if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0)
78141753Smarius		space &= 0x1;
79141753Smarius	else if (strcmp(bus, "pci") == 0)
80141753Smarius		space &= OFW_PCI_PHYS_HI_SPACEMASK;
81141753Smarius	/* The phys.hi cells of the other busses only contain space bits. */
82141753Smarius	return (space);
83141753Smarius}
84141753Smarius
85141753Smarius/*
86141753Smarius * Return the physical address and the bus space to use for a node
87141753Smarius * referenced by its package handle and the index of the register bank
88141753Smarius * to decode. Intended to be used to together with sparc64_fake_bustag()
89141753Smarius * by console drivers in early boot only.
90141753Smarius * Works by mapping the address of the node's bank given in the address
91141753Smarius * space of its parent upward in the device tree at each bridge along the
92141753Smarius * path.
93141753Smarius * Currently only really deals with max. 64-bit addresses, i.e. addresses
94141753Smarius * consisting of max. 2 phys cells (phys.hi and phys.lo). If we encounter
95141753Smarius * a 3 phys cells address (as with PCI addresses) we assume phys.hi can
96141753Smarius * be ignored except for the space bits (generally contained in phys.hi)
97141753Smarius * and treat phys.mid as phys.hi.
98141753Smarius */
99119697Smarcelint
100141753SmariusOF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr)
101119697Smarcel{
102119697Smarcel	char name[32];
103141753Smarius	uint64_t cend, cstart, end, phys, sz, start;
104141753Smarius	pcell_t addrc, szc, paddrc;
105141753Smarius	phandle_t bus, lbus, pbus;
106141753Smarius	uint32_t banks[10 * 5];	/* 10 PCI banks */
107141753Smarius	uint32_t cspace, spc;
108141753Smarius	int i, j, nbank;
109119697Smarcel
110141753Smarius	/*
111141753Smarius	 * In general the addresses are contained in the "reg" property
112141753Smarius	 * of a node. The first address in the "reg" property of a PCI
113141753Smarius	 * node however is the address of its configuration registers in
114141753Smarius	 * the configuration space of the host bridge. Additional entries
115141753Smarius	 * denote the memory and I/O addresses. For relocatable addresses
116141753Smarius	 * the "reg" property contains the BAR, for non-relocatable
117141753Smarius	 * addresses it contains the absolute PCI address. The PCI-only
118141753Smarius	 * "assigned-addresses" property however always contains the
119141753Smarius	 * absolute PCI addresses.
120141753Smarius	 * The "assigned-addresses" and "reg" properties are arrays of
121141753Smarius	 * address structures consisting of #address-cells 32-bit phys
122141753Smarius	 * cells and #size-cells 32-bit size cells. If a parent lacks
123141753Smarius	 * the "#address-cells" or "#size-cells" property the default
124141753Smarius	 * for #address-cells to use is 2 and for #size-cells 1.
125141753Smarius	 */
126119697Smarcel	bus = OF_parent(node);
127123866Sobrien	if (bus == 0)
128119697Smarcel		return (ENXIO);
129119697Smarcel	if (OF_getprop(bus, "name", name, sizeof(name)) == -1)
130119697Smarcel		return (ENXIO);
131119697Smarcel	name[sizeof(name) - 1] = '\0';
132141753Smarius	if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1)
133141753Smarius		addrc = 2;
134141753Smarius	if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
135141753Smarius		szc = 1;
136141753Smarius	if (szc > 2)
137141753Smarius		return (ENXIO);
138141753Smarius	if (strcmp(name, "pci") == 0) {
139141753Smarius		if (addrc > 3)
140119697Smarcel			return (ENXIO);
141141753Smarius		nbank = OF_getprop(node, "assigned-addresses", &banks,
142141753Smarius		    sizeof(banks));
143141753Smarius	} else {
144141753Smarius		if (addrc > 2)
145119697Smarcel			return (ENXIO);
146141753Smarius		nbank = OF_getprop(node, "reg", &banks, sizeof(banks));
147141753Smarius	}
148141753Smarius	if (nbank == -1)
149141753Smarius		return (ENXIO);
150141753Smarius	nbank /= sizeof(banks[0]) * (addrc + szc);
151141753Smarius	if (bank < 0 || bank > nbank - 1)
152141753Smarius		return (ENXIO);
153141753Smarius	phys = 0;
154141753Smarius	for (i = 0; i < MIN(2, addrc); i++)
155141753Smarius		phys |= (uint64_t)banks[(addrc + szc) * bank + addrc - 2 + i] <<
156141753Smarius		    32 * (MIN(2, addrc) - i - 1);
157141753Smarius	sz = 0;
158141753Smarius	for (i = 0; i < szc; i++)
159141753Smarius		sz |= (uint64_t)banks[(addrc + szc) * bank + addrc + i] <<
160141753Smarius		    32 * (szc - i - 1);
161141753Smarius	start = phys;
162141753Smarius	end = phys + sz - 1;
163141753Smarius	spc = phys_hi_mask_space(name, banks[(addrc + szc) * bank]);
164119697Smarcel
165141753Smarius	/*
166141753Smarius	 * Map upward in the device tree at every bridge we encounter
167141753Smarius	 * using their "ranges" properties.
168141753Smarius	 * The "ranges" property of a bridge is an array of a structure
169141753Smarius	 * consisting of that bridge's #address-cells 32-bit child-phys
170141753Smarius	 * cells, its parent bridge #address-cells 32-bit parent-phys
171141753Smarius	 * cells and that bridge's #size-cells 32-bit size cells.
172141753Smarius	 * If a bridge doesn't have a "ranges" property no mapping is
173141753Smarius	 * necessary at that bridge.
174141753Smarius	 */
175141753Smarius	cspace = 0;
176141753Smarius	lbus = bus;
177141753Smarius	while ((pbus = OF_parent(bus)) != 0) {
178141753Smarius		if (OF_getprop(pbus, "#address-cells", &paddrc,
179141753Smarius		    sizeof(paddrc)) == -1)
180141753Smarius			paddrc = 2;
181141753Smarius		if (paddrc > 3)
182141753Smarius			return (ENXIO);
183141753Smarius		nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks));
184141753Smarius		if (nbank == -1) {
185141753Smarius			if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
186119697Smarcel				return (ENXIO);
187141753Smarius			name[sizeof(name) - 1] = '\0';
188141753Smarius			goto skip;
189141753Smarius		}
190141753Smarius		if (lbus != bus) {
191141753Smarius			if (OF_getprop(bus, "#size-cells", &szc,
192141753Smarius			    sizeof(szc)) == -1)
193141753Smarius				szc = 1;
194141753Smarius			if (szc > 2)
195141753Smarius				return (ENXIO);
196141753Smarius		}
197141753Smarius		nbank /= sizeof(banks[0]) * (addrc + paddrc + szc);
198141753Smarius		for (i = 0; i < nbank; i++) {
199141753Smarius			cspace = phys_hi_mask_space(name,
200141753Smarius			    banks[(addrc + paddrc + szc) * i]);
201141753Smarius			if (cspace != spc)
202141753Smarius				continue;
203141753Smarius			phys = 0;
204141753Smarius			for (j = 0; j < MIN(2, addrc); j++)
205141753Smarius				phys |= (uint64_t)banks[
206141753Smarius				    (addrc + paddrc + szc) * i +
207141753Smarius				    addrc - 2 + j] <<
208141753Smarius				    32 * (MIN(2, addrc) - j - 1);
209141753Smarius			sz = 0;
210141753Smarius			for (j = 0; j < szc; j++)
211141753Smarius				sz |= (uint64_t)banks[
212141753Smarius				    (addrc + paddrc + szc) * i + addrc +
213141753Smarius				    paddrc + j] <<
214141753Smarius				    32 * (szc - j - 1);
215141753Smarius			cstart = phys;
216141753Smarius			cend = phys + sz - 1;
217141753Smarius			if (start < cstart || start > cend)
218141753Smarius				continue;
219141753Smarius			if (end < cstart || end > cend)
220141753Smarius				return (ENXIO);
221141753Smarius			phys = 0;
222141753Smarius			for (j = 0; j < MIN(2, paddrc); j++)
223141753Smarius				phys |= (uint64_t)banks[
224141753Smarius				    (addrc + paddrc + szc) * i + addrc +
225141753Smarius				    paddrc - 2 + j] <<
226141753Smarius				    32 * (MIN(2, paddrc) - j - 1);
227141753Smarius			start += phys - cstart;
228141753Smarius			end += phys - cstart;
229119697Smarcel			if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
230119697Smarcel				return (ENXIO);
231119697Smarcel			name[sizeof(name) - 1] = '\0';
232141753Smarius			spc = phys_hi_mask_space(name,
233141753Smarius			    banks[(addrc + paddrc + szc) * i + addrc]);
234141753Smarius			break;
235119697Smarcel		}
236141753Smarius		if (i == nbank)
237119697Smarcel			return (ENXIO);
238141753Smarius skip:
239141753Smarius		addrc = paddrc;
240141753Smarius		lbus = bus;
241141753Smarius		bus = pbus;
242141753Smarius	}
243119697Smarcel
244141753Smarius	/* Done with mapping. Return the bus space as used by FreeBSD. */
245141753Smarius	*addr = start;
246141753Smarius	if (OF_getprop(lbus, "name", name, sizeof(name)) == -1)
247141753Smarius		return (ENXIO);
248141753Smarius	name[sizeof(name) - 1] = '\0';
249141753Smarius	if (strcmp(name, "central") == 0) {
250141753Smarius		*space = UPA_BUS_SPACE;
251141753Smarius		return (0);
252141753Smarius	} else if (strcmp(name, "pci") == 0) {
253141753Smarius		switch (cspace) {
254141753Smarius		case OFW_PCI_PHYS_HI_SPACE_IO:
255141753Smarius			*space = PCI_IO_BUS_SPACE;
256141753Smarius			return (0);
257141753Smarius		case OFW_PCI_PHYS_HI_SPACE_MEM32:
258141753Smarius			*space = PCI_MEMORY_BUS_SPACE;
259141753Smarius			return (0);
260119697Smarcel		}
261119697Smarcel	} else if (strcmp(name, "sbus") == 0) {
262141753Smarius		*space = SBUS_BUS_SPACE;
263141753Smarius		return (0);
264119697Smarcel	}
265119697Smarcel	return (ENXIO);
266119697Smarcel}
267