ofw_machdep.c revision 186347
1/*-
2 * Copyright (C) 1996 Wolfgang Solfrank.
3 * Copyright (C) 1996 TooLs GmbH.
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 * 3. All advertising materials mentioning features or use of this software
15 *    must display the following acknowledgement:
16 *	This product includes software developed by TooLs GmbH.
17 * 4. The name of TooLs GmbH may not be used to endorse or promote products
18 *    derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * $NetBSD: ofw_machdep.c,v 1.5 2000/05/23 13:25:43 tsubai Exp $
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/powerpc/aim/ofw_machdep.c 186347 2008-12-20 00:33:10Z nwhitehorn $");
36
37#include <sys/param.h>
38#include <sys/bus.h>
39#include <sys/systm.h>
40#include <sys/conf.h>
41#include <sys/disk.h>
42#include <sys/fcntl.h>
43#include <sys/malloc.h>
44#include <sys/stat.h>
45
46#include <net/ethernet.h>
47
48#include <dev/ofw/openfirm.h>
49#include <dev/ofw/ofw_pci.h>
50#include <dev/ofw/ofw_bus.h>
51
52#include <vm/vm.h>
53#include <vm/vm_param.h>
54#include <vm/vm_page.h>
55
56#include <machine/bus.h>
57#include <machine/md_var.h>
58#include <machine/powerpc.h>
59#include <machine/ofw_machdep.h>
60
61#define	OFMEM_REGIONS	32
62static struct mem_region OFmem[OFMEM_REGIONS + 1], OFavail[OFMEM_REGIONS + 3];
63static struct mem_region OFfree[OFMEM_REGIONS + 3];
64
65extern register_t ofmsr[5];
66extern struct	pmap ofw_pmap;
67static int	(*ofwcall)(void *);
68static void	*fdt;
69int		ofw_real_mode;
70
71static int	openfirmware(void *args);
72
73/*
74 * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback.
75 */
76register_t	ofw_sprg0_save;
77
78static __inline void
79ofw_sprg_prepare(void)
80{
81	/*
82	 * Assume that interrupt are disabled at this point, or
83	 * SPRG1-3 could be trashed
84	 */
85	__asm __volatile("mfsprg0 %0\n\t"
86			 "mtsprg0 %1\n\t"
87	    		 "mtsprg1 %2\n\t"
88	    		 "mtsprg2 %3\n\t"
89			 "mtsprg3 %4\n\t"
90			 : "=&r"(ofw_sprg0_save)
91			 : "r"(ofmsr[1]),
92			 "r"(ofmsr[2]),
93			 "r"(ofmsr[3]),
94			 "r"(ofmsr[4]));
95}
96
97static __inline void
98ofw_sprg_restore(void)
99{
100	/*
101	 * Note that SPRG1-3 contents are irrelevant. They are scratch
102	 * registers used in the early portion of trap handling when
103	 * interrupts are disabled.
104	 *
105	 * PCPU data cannot be used until this routine is called !
106	 */
107	__asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save));
108}
109
110/*
111 * Memory region utilities: determine if two regions overlap,
112 * and merge two overlapping regions into one
113 */
114static int
115memr_overlap(struct mem_region *r1, struct mem_region *r2)
116{
117	if ((r1->mr_start + r1->mr_size) < r2->mr_start ||
118	    (r2->mr_start + r2->mr_size) < r1->mr_start)
119		return (FALSE);
120
121	return (TRUE);
122}
123
124static void
125memr_merge(struct mem_region *from, struct mem_region *to)
126{
127	int end;
128	end = imax(to->mr_start + to->mr_size, from->mr_start + from->mr_size);
129	to->mr_start = imin(from->mr_start, to->mr_start);
130	to->mr_size = end - to->mr_start;
131}
132
133/*
134 * This is called during powerpc_init, before the system is really initialized.
135 * It shall provide the total and the available regions of RAM.
136 * Both lists must have a zero-size entry as terminator.
137 * The available regions need not take the kernel into account, but needs
138 * to provide space for two additional entry beyond the terminating one.
139 */
140void
141mem_regions(struct mem_region **memp, int *memsz,
142		struct mem_region **availp, int *availsz)
143{
144	int phandle;
145	int asz, msz, fsz;
146	int i, j;
147	int still_merging;
148
149	/*
150	 * Get memory.
151	 */
152	if ((phandle = OF_finddevice("/memory")) == -1
153	    || (msz = OF_getprop(phandle, "reg",
154			  OFmem, sizeof OFmem[0] * OFMEM_REGIONS))
155	       <= 0
156	    || (asz = OF_getprop(phandle, "available",
157			  OFavail, sizeof OFavail[0] * OFMEM_REGIONS))
158	       <= 0)
159		panic("no memory?");
160	*memp = OFmem;
161	*memsz = msz / sizeof(struct mem_region);
162
163	/*
164	 * OFavail may have overlapping regions - collapse these
165	 * and copy out remaining regions to OFfree
166	 */
167	asz /= sizeof(struct mem_region);
168	do {
169		still_merging = FALSE;
170		for (i = 0; i < asz; i++) {
171			if (OFavail[i].mr_size == 0)
172				continue;
173			for (j = i+1; j < asz; j++) {
174				if (OFavail[j].mr_size == 0)
175					continue;
176				if (memr_overlap(&OFavail[j], &OFavail[i])) {
177					memr_merge(&OFavail[j], &OFavail[i]);
178					/* mark inactive */
179					OFavail[j].mr_size = 0;
180					still_merging = TRUE;
181				}
182			}
183		}
184	} while (still_merging == TRUE);
185
186	/* evict inactive ranges */
187	for (i = 0, fsz = 0; i < asz; i++) {
188		if (OFavail[i].mr_size != 0) {
189			OFfree[fsz] = OFavail[i];
190			fsz++;
191		}
192	}
193
194	*availp = OFfree;
195	*availsz = fsz;
196}
197
198void
199OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *))
200{
201	if (ofmsr[0] & PSL_DR)
202		ofw_real_mode = 0;
203	else
204		ofw_real_mode = 1;
205
206	ofwcall = openfirm;
207	fdt = fdt_ptr;
208}
209
210boolean_t
211OF_bootstrap()
212{
213	boolean_t status = FALSE;
214
215	if (ofwcall != NULL) {
216		if (ofw_real_mode)
217			status = OF_install(OFW_STD_REAL, 0);
218		else
219			status = OF_install(OFW_STD_DIRECT, 0);
220
221		if (status != TRUE)
222			return status;
223
224		OF_init(openfirmware);
225	} else {
226		status = OF_install(OFW_FDT, 0);
227
228		if (status != TRUE)
229			return status;
230
231		OF_init(fdt);
232	}
233
234	return (status);
235}
236
237static int
238openfirmware(void *args)
239{
240	long	oldmsr;
241	int	result;
242	u_int	srsave[16];
243	u_int   i;
244
245	if (pmap_bootstrapped && ofw_real_mode)
246		args = (void *)pmap_kextract((vm_offset_t)args);
247
248	__asm __volatile(	"\t"
249		"sync\n\t"
250		"mfmsr  %0\n\t"
251		"mtmsr  %1\n\t"
252		"isync\n"
253		: "=r" (oldmsr)
254		: "r" (ofmsr[0])
255	);
256
257	ofw_sprg_prepare();
258
259	if (pmap_bootstrapped && !ofw_real_mode) {
260		/*
261		 * Swap the kernel's address space with Open Firmware's
262		 */
263		for (i = 0; i < 16; i++) {
264			srsave[i] = mfsrin(i << ADDR_SR_SHFT);
265			mtsrin(i << ADDR_SR_SHFT, ofw_pmap.pm_sr[i]);
266		}
267
268		/*
269		 * Clear battable[] translations
270		 */
271		__asm __volatile("mtdbatu 2, %0\n"
272				 "mtdbatu 3, %0" : : "r" (0));
273		isync();
274	}
275
276       	result = ofwcall(args);
277
278	if (pmap_bootstrapped && !ofw_real_mode) {
279		/*
280		 * Restore the kernel's addr space. The isync() doesn;t
281		 * work outside the loop unless mtsrin() is open-coded
282		 * in an asm statement :(
283		 */
284		for (i = 0; i < 16; i++) {
285			mtsrin(i << ADDR_SR_SHFT, srsave[i]);
286			isync();
287		}
288	}
289
290	ofw_sprg_restore();
291
292	__asm(	"\t"
293		"mtmsr  %0\n\t"
294		"isync\n"
295		: : "r" (oldmsr)
296	);
297
298	return (result);
299}
300
301void
302OF_halt()
303{
304	int retval;	/* dummy, this may not be needed */
305
306	OF_interpret("shut-down", 1, &retval);
307	for (;;);	/* just in case */
308}
309
310void
311OF_reboot()
312{
313	int retval;	/* dummy, this may not be needed */
314
315	OF_interpret("reset-all", 1, &retval);
316	for (;;);	/* just in case */
317}
318
319void
320OF_getetheraddr(device_t dev, u_char *addr)
321{
322	phandle_t	node;
323
324	node = ofw_bus_get_node(dev);
325	OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN);
326}
327
328/*
329 * Return a bus handle and bus tag that corresponds to the register
330 * numbered regno for the device referenced by the package handle
331 * dev. This function is intended to be used by console drivers in
332 * early boot only. It works by mapping the address of the device's
333 * register in the address space of its parent and recursively walk
334 * the device tree upward this way.
335 */
336static void
337OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip)
338{
339	char name[16];
340	uint32_t addr, size;
341	int pci, res;
342
343	res = OF_getprop(node, "#address-cells", &addr, sizeof(addr));
344	if (res == -1)
345		addr = 2;
346	res = OF_getprop(node, "#size-cells", &size, sizeof(size));
347	if (res == -1)
348		size = 1;
349	pci = 0;
350	if (addr == 3 && size == 2) {
351		res = OF_getprop(node, "name", name, sizeof(name));
352		if (res != -1) {
353			name[sizeof(name) - 1] = '\0';
354			pci = (strcmp(name, "pci") == 0) ? 1 : 0;
355		}
356	}
357	if (addrp != NULL)
358		*addrp = addr;
359	if (sizep != NULL)
360		*sizep = size;
361	if (pcip != NULL)
362		*pcip = pci;
363}
364
365int
366OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag,
367    bus_space_handle_t *handle)
368{
369	uint32_t cell[32];
370	bus_addr_t addr, raddr, baddr;
371	bus_size_t size, rsize;
372	uint32_t c, nbridge, naddr, nsize;
373	phandle_t bridge, parent;
374	u_int spc, rspc;
375	int pci, pcib, res;
376
377	/* Sanity checking. */
378	if (dev == 0)
379		return (EINVAL);
380	bridge = OF_parent(dev);
381	if (bridge == 0)
382		return (EINVAL);
383	if (regno < 0)
384		return (EINVAL);
385	if (tag == NULL || handle == NULL)
386		return (EINVAL);
387
388	/* Get the requested register. */
389	OF_get_addr_props(bridge, &naddr, &nsize, &pci);
390	res = OF_getprop(dev, (pci) ? "assigned-addresses" : "reg",
391	    cell, sizeof(cell));
392	if (res == -1)
393		return (ENXIO);
394	if (res % sizeof(cell[0]))
395		return (ENXIO);
396	res /= sizeof(cell[0]);
397	regno *= naddr + nsize;
398	if (regno + naddr + nsize > res)
399		return (EINVAL);
400	spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0;
401	addr = 0;
402	for (c = 0; c < naddr; c++)
403		addr = ((uint64_t)addr << 32) | cell[regno++];
404	size = 0;
405	for (c = 0; c < nsize; c++)
406		size = ((uint64_t)size << 32) | cell[regno++];
407
408	/*
409	 * Map the address range in the bridge's decoding window as given
410	 * by the "ranges" property. If a node doesn't have such property
411	 * then no mapping is done.
412	 */
413	parent = OF_parent(bridge);
414	while (parent != 0) {
415		OF_get_addr_props(parent, &nbridge, NULL, &pcib);
416		res = OF_getprop(bridge, "ranges", cell, sizeof(cell));
417		if (res == -1)
418			goto next;
419		if (res % sizeof(cell[0]))
420			return (ENXIO);
421		res /= sizeof(cell[0]);
422		regno = 0;
423		while (regno < res) {
424			rspc = (pci)
425			    ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
426			    : ~0;
427			if (rspc != spc) {
428				regno += naddr + nbridge + nsize;
429				continue;
430			}
431			raddr = 0;
432			for (c = 0; c < naddr; c++)
433				raddr = ((uint64_t)raddr << 32) | cell[regno++];
434			rspc = (pcib)
435			    ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
436			    : ~0;
437			baddr = 0;
438			for (c = 0; c < nbridge; c++)
439				baddr = ((uint64_t)baddr << 32) | cell[regno++];
440			rsize = 0;
441			for (c = 0; c < nsize; c++)
442				rsize = ((uint64_t)rsize << 32) | cell[regno++];
443			if (addr < raddr || addr >= raddr + rsize)
444				continue;
445			addr = addr - raddr + baddr;
446			if (rspc != ~0)
447				spc = rspc;
448		}
449
450	next:
451		bridge = parent;
452		parent = OF_parent(bridge);
453		OF_get_addr_props(bridge, &naddr, &nsize, &pci);
454	}
455
456	*tag = &bs_le_tag;
457	return (bus_space_map(*tag, addr, size, 0, handle));
458}
459
460int
461mem_valid(vm_offset_t addr, int len)
462{
463	int i;
464
465	for (i = 0; i < OFMEM_REGIONS; i++)
466		if ((addr >= OFmem[i].mr_start)
467		    && (addr + len < OFmem[i].mr_start + OFmem[i].mr_size))
468			return (0);
469
470	return (EFAULT);
471}
472