186229Stmm/*-
286229Stmm * Copyright (c) 2001 by Thomas Moestl <tmm@FreeBSD.org>.
3207243Smarius * Copyright (c) 2005 - 2010 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 */
26200924Smarius
27146473Smarius#include <sys/cdefs.h>
28146473Smarius__FBSDID("$FreeBSD$");
2986229Stmm
3086229Stmm/*
31133862Smarius * Some Open Firmware helper functions that are likely machine dependent.
3286229Stmm */
3386229Stmm
3486229Stmm#include <sys/param.h>
35133589Smarius#include <sys/bus.h>
3686229Stmm#include <sys/systm.h>
3786229Stmm
3888370Stmm#include <net/ethernet.h>
3988370Stmm
40133589Smarius#include <dev/ofw/ofw_bus.h>
41141753Smarius#include <dev/ofw/ofw_pci.h>
42119338Simp#include <dev/ofw/openfirm.h>
4386229Stmm
44119697Smarcel#include <machine/bus.h>
4586229Stmm#include <machine/idprom.h>
4686229Stmm#include <machine/ofw_machdep.h>
47230632Smarius#include <machine/stdarg.h>
4886229Stmm
4986229Stmmvoid
5086229StmmOF_getetheraddr(device_t dev, u_char *addr)
5186229Stmm{
52133728Smarius	char buf[sizeof("true")];
5386229Stmm	phandle_t node;
5486229Stmm	struct idprom idp;
5586229Stmm
56228201Sjchandra	if ((node = OF_finddevice("/options")) != -1 &&
57133728Smarius	    OF_getprop(node, "local-mac-address?", buf, sizeof(buf)) > 0) {
58133728Smarius		buf[sizeof(buf) - 1] = '\0';
59133728Smarius		if (strcmp(buf, "true") == 0 &&
60133728Smarius		    (node = ofw_bus_get_node(dev)) > 0 &&
61133728Smarius		    OF_getprop(node, "local-mac-address", addr,
62133728Smarius		    ETHER_ADDR_LEN) == ETHER_ADDR_LEN)
63133728Smarius			return;
64133728Smarius	}
65133728Smarius
6686229Stmm	node = OF_peer(0);
6786229Stmm	if (node <= 0 || OF_getprop(node, "idprom", &idp, sizeof(idp)) == -1)
68200924Smarius		panic("Could not determine the machine Ethernet address");
6988370Stmm	bcopy(&idp.id_ether, addr, ETHER_ADDR_LEN);
7086229Stmm}
71119697Smarcel
72207243Smariusu_int
73207243SmariusOF_getscsinitid(device_t dev)
74207243Smarius{
75207243Smarius	phandle_t node;
76207243Smarius	uint32_t id;
77207243Smarius
78207243Smarius	for (node = ofw_bus_get_node(dev); node != 0; node = OF_parent(node))
79207243Smarius		if (OF_getprop(node, "scsi-initiator-id", &id,
80207243Smarius		    sizeof(id)) > 0)
81207243Smarius			return (id);
82207243Smarius	return (7);
83207243Smarius}
84207243Smarius
85230632Smariusvoid
86230632SmariusOF_panic(const char *fmt, ...)
87230632Smarius{
88230632Smarius	char buf[256];
89230632Smarius	va_list ap;
90230632Smarius
91230632Smarius	va_start(ap, fmt);
92230632Smarius	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
93230632Smarius	OF_printf("OF_panic: %s\n", buf);
94230632Smarius	va_end(ap);
95230632Smarius	OF_exit();
96230632Smarius}
97230632Smarius
98141753Smariusstatic __inline uint32_t
99141753Smariusphys_hi_mask_space(const char *bus, uint32_t phys_hi)
100141753Smarius{
101141753Smarius
102141753Smarius	if (strcmp(bus, "ebus") == 0 || strcmp(bus, "isa") == 0)
103200924Smarius		phys_hi &= 0x1;
104141753Smarius	else if (strcmp(bus, "pci") == 0)
105200924Smarius		phys_hi &= OFW_PCI_PHYS_HI_SPACEMASK;
106141753Smarius	/* The phys.hi cells of the other busses only contain space bits. */
107200924Smarius	return (phys_hi);
108141753Smarius}
109141753Smarius
110141753Smarius/*
111141753Smarius * Return the physical address and the bus space to use for a node
112141753Smarius * referenced by its package handle and the index of the register bank
113200924Smarius * to decode.  Intended to be used to together with sparc64_fake_bustag()
114141753Smarius * by console drivers in early boot only.
115141753Smarius * Works by mapping the address of the node's bank given in the address
116141753Smarius * space of its parent upward in the device tree at each bridge along the
117141753Smarius * path.
118141753Smarius * Currently only really deals with max. 64-bit addresses, i.e. addresses
119200924Smarius * consisting of max. 2 phys cells (phys.hi and phys.lo).  If we encounter
120141753Smarius * a 3 phys cells address (as with PCI addresses) we assume phys.hi can
121141753Smarius * be ignored except for the space bits (generally contained in phys.hi)
122141753Smarius * and treat phys.mid as phys.hi.
123141753Smarius */
124119697Smarcelint
125141753SmariusOF_decode_addr(phandle_t node, int bank, int *space, bus_addr_t *addr)
126119697Smarcel{
127119697Smarcel	char name[32];
128200925Smarius	uint64_t cend, cstart, end, phys, pphys, sz, start;
129141753Smarius	pcell_t addrc, szc, paddrc;
130141753Smarius	phandle_t bus, lbus, pbus;
131141753Smarius	uint32_t banks[10 * 5];	/* 10 PCI banks */
132200925Smarius	uint32_t cspc, pspc, spc;
133141753Smarius	int i, j, nbank;
134119697Smarcel
135141753Smarius	/*
136141753Smarius	 * In general the addresses are contained in the "reg" property
137200924Smarius	 * of a node.  The first address in the "reg" property of a PCI
138141753Smarius	 * node however is the address of its configuration registers in
139200924Smarius	 * the configuration space of the host bridge.  Additional entries
140200924Smarius	 * denote the memory and I/O addresses.  For relocatable addresses
141141753Smarius	 * the "reg" property contains the BAR, for non-relocatable
142200924Smarius	 * addresses it contains the absolute PCI address.  The PCI-only
143141753Smarius	 * "assigned-addresses" property however always contains the
144141753Smarius	 * absolute PCI addresses.
145141753Smarius	 * The "assigned-addresses" and "reg" properties are arrays of
146141753Smarius	 * address structures consisting of #address-cells 32-bit phys
147200924Smarius	 * cells and #size-cells 32-bit size cells.  If a parent lacks
148141753Smarius	 * the "#address-cells" or "#size-cells" property the default
149141753Smarius	 * for #address-cells to use is 2 and for #size-cells 1.
150141753Smarius	 */
151119697Smarcel	bus = OF_parent(node);
152123866Sobrien	if (bus == 0)
153119697Smarcel		return (ENXIO);
154119697Smarcel	if (OF_getprop(bus, "name", name, sizeof(name)) == -1)
155119697Smarcel		return (ENXIO);
156119697Smarcel	name[sizeof(name) - 1] = '\0';
157141753Smarius	if (OF_getprop(bus, "#address-cells", &addrc, sizeof(addrc)) == -1)
158141753Smarius		addrc = 2;
159141753Smarius	if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
160141753Smarius		szc = 1;
161146473Smarius	if (addrc < 2 || addrc > 3 || szc < 1 || szc > 2)
162141753Smarius		return (ENXIO);
163141753Smarius	if (strcmp(name, "pci") == 0) {
164141753Smarius		if (addrc > 3)
165119697Smarcel			return (ENXIO);
166141753Smarius		nbank = OF_getprop(node, "assigned-addresses", &banks,
167141753Smarius		    sizeof(banks));
168141753Smarius	} else {
169141753Smarius		if (addrc > 2)
170119697Smarcel			return (ENXIO);
171141753Smarius		nbank = OF_getprop(node, "reg", &banks, sizeof(banks));
172141753Smarius	}
173141753Smarius	if (nbank == -1)
174141753Smarius		return (ENXIO);
175141753Smarius	nbank /= sizeof(banks[0]) * (addrc + szc);
176141753Smarius	if (bank < 0 || bank > nbank - 1)
177141753Smarius		return (ENXIO);
178200925Smarius	bank *= addrc + szc;
179200925Smarius	spc = phys_hi_mask_space(name, banks[bank]);
180200925Smarius	/* Skip the high cell for 3-cell addresses. */
181200925Smarius	bank += addrc - 2;
182141753Smarius	phys = 0;
183141753Smarius	for (i = 0; i < MIN(2, addrc); i++)
184200925Smarius		phys = ((uint64_t)phys << 32) | banks[bank++];
185141753Smarius	sz = 0;
186141753Smarius	for (i = 0; i < szc; i++)
187200925Smarius		sz = ((uint64_t)sz << 32) | banks[bank++];
188141753Smarius	start = phys;
189141753Smarius	end = phys + sz - 1;
190119697Smarcel
191141753Smarius	/*
192141753Smarius	 * Map upward in the device tree at every bridge we encounter
193141753Smarius	 * using their "ranges" properties.
194141753Smarius	 * The "ranges" property of a bridge is an array of a structure
195141753Smarius	 * consisting of that bridge's #address-cells 32-bit child-phys
196141753Smarius	 * cells, its parent bridge #address-cells 32-bit parent-phys
197141753Smarius	 * cells and that bridge's #size-cells 32-bit size cells.
198141753Smarius	 * If a bridge doesn't have a "ranges" property no mapping is
199141753Smarius	 * necessary at that bridge.
200141753Smarius	 */
201200925Smarius	cspc = 0;
202141753Smarius	lbus = bus;
203141753Smarius	while ((pbus = OF_parent(bus)) != 0) {
204141753Smarius		if (OF_getprop(pbus, "#address-cells", &paddrc,
205141753Smarius		    sizeof(paddrc)) == -1)
206141753Smarius			paddrc = 2;
207146473Smarius		if (paddrc < 2 || paddrc > 3)
208141753Smarius			return (ENXIO);
209141753Smarius		nbank = OF_getprop(bus, "ranges", &banks, sizeof(banks));
210141753Smarius		if (nbank == -1) {
211141753Smarius			if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
212119697Smarcel				return (ENXIO);
213141753Smarius			name[sizeof(name) - 1] = '\0';
214141753Smarius			goto skip;
215141753Smarius		}
216206448Smarius		if (OF_getprop(bus, "#size-cells", &szc, sizeof(szc)) == -1)
217206448Smarius			szc = 1;
218206448Smarius		if (szc < 1 || szc > 2)
219206448Smarius			return (ENXIO);
220141753Smarius		nbank /= sizeof(banks[0]) * (addrc + paddrc + szc);
221200925Smarius		bank = 0;
222141753Smarius		for (i = 0; i < nbank; i++) {
223200925Smarius			cspc = phys_hi_mask_space(name, banks[bank]);
224200925Smarius			if (cspc != spc) {
225200925Smarius				bank += addrc + paddrc + szc;
226141753Smarius				continue;
227200925Smarius			}
228200925Smarius			/* Skip the high cell for 3-cell addresses. */
229200925Smarius			bank += addrc - 2;
230141753Smarius			phys = 0;
231141753Smarius			for (j = 0; j < MIN(2, addrc); j++)
232200925Smarius				phys = ((uint64_t)phys << 32) | banks[bank++];
233200925Smarius			pspc = banks[bank];
234200925Smarius			/* Skip the high cell for 3-cell addresses. */
235200925Smarius			bank += paddrc - 2;
236200925Smarius			pphys = 0;
237200925Smarius			for (j = 0; j < MIN(2, paddrc); j++)
238200925Smarius				pphys =
239200925Smarius				    ((uint64_t)pphys << 32) | banks[bank++];
240141753Smarius			sz = 0;
241141753Smarius			for (j = 0; j < szc; j++)
242200925Smarius				sz = ((uint64_t)sz << 32) | banks[bank++];
243141753Smarius			cstart = phys;
244141753Smarius			cend = phys + sz - 1;
245141753Smarius			if (start < cstart || start > cend)
246141753Smarius				continue;
247141753Smarius			if (end < cstart || end > cend)
248141753Smarius				return (ENXIO);
249119697Smarcel			if (OF_getprop(pbus, "name", name, sizeof(name)) == -1)
250119697Smarcel				return (ENXIO);
251119697Smarcel			name[sizeof(name) - 1] = '\0';
252200925Smarius			spc = phys_hi_mask_space(name, pspc);
253200925Smarius			start += pphys - cstart;
254200925Smarius			end += pphys - cstart;
255141753Smarius			break;
256119697Smarcel		}
257141753Smarius		if (i == nbank)
258119697Smarcel			return (ENXIO);
259206448Smarius		lbus = bus;
260141753Smarius skip:
261141753Smarius		addrc = paddrc;
262141753Smarius		bus = pbus;
263141753Smarius	}
264119697Smarcel
265141753Smarius	*addr = start;
266200925Smarius	/* Determine the bus space based on the last bus we mapped. */
267166058Smarius	if (OF_parent(lbus) == 0) {
268166096Smarius		*space = NEXUS_BUS_SPACE;
269166058Smarius		return (0);
270166058Smarius	}
271141753Smarius	if (OF_getprop(lbus, "name", name, sizeof(name)) == -1)
272141753Smarius		return (ENXIO);
273141753Smarius	name[sizeof(name) - 1] = '\0';
274200925Smarius	if (strcmp(name, "central") == 0 || strcmp(name, "ebus") == 0 ||
275200925Smarius	    strcmp(name, "upa") == 0) {
276166096Smarius		*space = NEXUS_BUS_SPACE;
277141753Smarius		return (0);
278141753Smarius	} else if (strcmp(name, "pci") == 0) {
279200925Smarius		switch (cspc) {
280141753Smarius		case OFW_PCI_PHYS_HI_SPACE_IO:
281141753Smarius			*space = PCI_IO_BUS_SPACE;
282141753Smarius			return (0);
283141753Smarius		case OFW_PCI_PHYS_HI_SPACE_MEM32:
284141753Smarius			*space = PCI_MEMORY_BUS_SPACE;
285141753Smarius			return (0);
286119697Smarcel		}
287119697Smarcel	} else if (strcmp(name, "sbus") == 0) {
288141753Smarius		*space = SBUS_BUS_SPACE;
289141753Smarius		return (0);
290119697Smarcel	}
291119697Smarcel	return (ENXIO);
292119697Smarcel}
293