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$");
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/smp.h>
45#include <sys/stat.h>
46
47#include <net/ethernet.h>
48
49#include <dev/ofw/openfirm.h>
50#include <dev/ofw/ofw_pci.h>
51#include <dev/ofw/ofw_bus.h>
52
53#include <vm/vm.h>
54#include <vm/vm_param.h>
55#include <vm/vm_page.h>
56
57#include <machine/bus.h>
58#include <machine/cpu.h>
59#include <machine/md_var.h>
60#include <machine/platform.h>
61#include <machine/ofw_machdep.h>
62#include <machine/trap.h>
63
64#ifdef AIM
65extern register_t ofmsr[5];
66extern void	*openfirmware_entry;
67static void	*fdt;
68int		ofw_real_mode;
69extern char     save_trap_init[0x2f00];          /* EXC_LAST */
70char            save_trap_of[0x2f00];            /* EXC_LAST */
71
72int		ofwcall(void *);
73static int	openfirmware(void *args);
74
75__inline void
76ofw_save_trap_vec(char *save_trap_vec)
77{
78	if (!ofw_real_mode)
79                return;
80
81	bcopy((void *)EXC_RST, save_trap_vec, EXC_LAST - EXC_RST);
82}
83
84static __inline void
85ofw_restore_trap_vec(char *restore_trap_vec)
86{
87	if (!ofw_real_mode)
88                return;
89
90	bcopy(restore_trap_vec, (void *)EXC_RST, EXC_LAST - EXC_RST);
91	__syncicache(EXC_RSVD, EXC_LAST - EXC_RSVD);
92}
93
94/*
95 * Saved SPRG0-3 from OpenFirmware. Will be restored prior to the callback.
96 */
97register_t	ofw_sprg0_save;
98
99static __inline void
100ofw_sprg_prepare(void)
101{
102	if (ofw_real_mode)
103		return;
104
105	/*
106	 * Assume that interrupt are disabled at this point, or
107	 * SPRG1-3 could be trashed
108	 */
109	__asm __volatile("mfsprg0 %0\n\t"
110			 "mtsprg0 %1\n\t"
111	    		 "mtsprg1 %2\n\t"
112	    		 "mtsprg2 %3\n\t"
113			 "mtsprg3 %4\n\t"
114			 : "=&r"(ofw_sprg0_save)
115			 : "r"(ofmsr[1]),
116			 "r"(ofmsr[2]),
117			 "r"(ofmsr[3]),
118			 "r"(ofmsr[4]));
119}
120
121static __inline void
122ofw_sprg_restore(void)
123{
124#if 0
125	if (ofw_real_mode)
126		return;
127#endif
128
129	/*
130	 * Note that SPRG1-3 contents are irrelevant. They are scratch
131	 * registers used in the early portion of trap handling when
132	 * interrupts are disabled.
133	 *
134	 * PCPU data cannot be used until this routine is called !
135	 */
136	__asm __volatile("mtsprg0 %0" :: "r"(ofw_sprg0_save));
137}
138#endif
139
140static int
141parse_ofw_memory(phandle_t node, const char *prop, struct mem_region *output)
142{
143	cell_t address_cells, size_cells;
144	cell_t OFmem[4 * PHYS_AVAIL_SZ];
145	int sz, i, j;
146	int apple_hack_mode;
147	phandle_t phandle;
148
149	sz = 0;
150	apple_hack_mode = 0;
151
152	/*
153	 * Get #address-cells from root node, defaulting to 1 if it cannot
154	 * be found.
155	 */
156	phandle = OF_finddevice("/");
157	if (OF_getprop(phandle, "#address-cells", &address_cells,
158	    sizeof(address_cells)) < (ssize_t)sizeof(address_cells))
159		address_cells = 1;
160	if (OF_getprop(phandle, "#size-cells", &size_cells,
161	    sizeof(size_cells)) < (ssize_t)sizeof(size_cells))
162		size_cells = 1;
163
164	/*
165	 * Get memory.
166	 */
167	if (node == -1 || (sz = OF_getprop(node, prop,
168	    OFmem, sizeof(OFmem))) <= 0)
169		panic("Physical memory map not found");
170
171	i = 0;
172	j = 0;
173	while (i < sz/sizeof(cell_t)) {
174	      #ifndef __powerpc64__
175		/* On 32-bit PPC, ignore regions starting above 4 GB */
176		if (address_cells > 1 && OFmem[i] > 0) {
177			i += address_cells + size_cells;
178			continue;
179		}
180	      #endif
181
182		output[j].mr_start = OFmem[i++];
183		if (address_cells == 2) {
184			#ifdef __powerpc64__
185			output[j].mr_start <<= 32;
186			#endif
187			output[j].mr_start += OFmem[i++];
188		}
189
190		output[j].mr_size = OFmem[i++];
191		if (size_cells == 2) {
192			#ifdef __powerpc64__
193			output[j].mr_size <<= 32;
194			#endif
195			output[j].mr_size += OFmem[i++];
196		}
197
198	      #ifndef __powerpc64__
199		/*
200		 * Check for memory regions extending above 32-bit
201		 * memory space, and restrict them to stay there.
202		 */
203		if (((uint64_t)output[j].mr_start +
204		    (uint64_t)output[j].mr_size) >
205		    BUS_SPACE_MAXADDR_32BIT) {
206			output[j].mr_size = BUS_SPACE_MAXADDR_32BIT -
207			    output[j].mr_start;
208		}
209	      #endif
210
211		j++;
212	}
213	sz = j*sizeof(output[0]);
214
215	return (sz);
216}
217
218/*
219 * This is called during powerpc_init, before the system is really initialized.
220 * It shall provide the total and the available regions of RAM.
221 * Both lists must have a zero-size entry as terminator.
222 * The available regions need not take the kernel into account, but needs
223 * to provide space for two additional entry beyond the terminating one.
224 */
225void
226ofw_mem_regions(struct mem_region *memp, int *memsz,
227		struct mem_region *availp, int *availsz)
228{
229	phandle_t phandle;
230	int asz, msz;
231	int res;
232	char name[31];
233
234	asz = msz = 0;
235
236	/*
237	 * Get memory from all the /memory nodes.
238	 */
239	for (phandle = OF_child(OF_peer(0)); phandle != 0;
240	    phandle = OF_peer(phandle)) {
241		if (OF_getprop(phandle, "name", name, sizeof(name)) <= 0)
242			continue;
243		if (strncmp(name, "memory", sizeof(name)) != 0)
244			continue;
245
246		res = parse_ofw_memory(phandle, "reg", &memp[msz]);
247		msz += res/sizeof(struct mem_region);
248		if (OF_getproplen(phandle, "available") >= 0)
249			res = parse_ofw_memory(phandle, "available",
250			    &availp[asz]);
251		else
252			res = parse_ofw_memory(phandle, "reg", &availp[asz]);
253		asz += res/sizeof(struct mem_region);
254	}
255
256	*memsz = msz;
257	*availsz = asz;
258}
259
260#ifdef AIM
261void
262OF_initial_setup(void *fdt_ptr, void *junk, int (*openfirm)(void *))
263{
264	if (ofmsr[0] & PSL_DR)
265		ofw_real_mode = 0;
266	else
267		ofw_real_mode = 1;
268
269	fdt = fdt_ptr;
270
271	#ifdef FDT_DTB_STATIC
272	/* Check for a statically included blob */
273	if (fdt == NULL)
274		fdt = &fdt_static_dtb;
275	#endif
276}
277
278boolean_t
279OF_bootstrap()
280{
281	boolean_t status = FALSE;
282
283	if (openfirmware_entry != NULL) {
284		if (ofw_real_mode) {
285			status = OF_install(OFW_STD_REAL, 0);
286		} else {
287			#ifdef __powerpc64__
288			status = OF_install(OFW_STD_32BIT, 0);
289			#else
290			status = OF_install(OFW_STD_DIRECT, 0);
291			#endif
292		}
293
294		if (status != TRUE)
295			return status;
296
297		OF_init(openfirmware);
298	} else if (fdt != NULL) {
299		status = OF_install(OFW_FDT, 0);
300
301		if (status != TRUE)
302			return status;
303
304		OF_init(fdt);
305	}
306
307	return (status);
308}
309
310void
311ofw_quiesce(void)
312{
313	struct {
314		cell_t name;
315		cell_t nargs;
316		cell_t nreturns;
317	} args;
318
319	KASSERT(!pmap_bootstrapped, ("Cannot call ofw_quiesce after VM is up"));
320
321	args.name = (cell_t)(uintptr_t)"quiesce";
322	args.nargs = 0;
323	args.nreturns = 0;
324	openfirmware(&args);
325}
326
327static int
328openfirmware_core(void *args)
329{
330	int		result;
331	register_t	oldmsr;
332
333	/*
334	 * Turn off exceptions - we really don't want to end up
335	 * anywhere unexpected with PCPU set to something strange
336	 * or the stack pointer wrong.
337	 */
338	oldmsr = intr_disable();
339
340	ofw_sprg_prepare();
341
342	/* Save trap vectors */
343	ofw_save_trap_vec(save_trap_of);
344
345	/* Restore initially saved trap vectors */
346	ofw_restore_trap_vec(save_trap_init);
347
348#if defined(AIM) && !defined(__powerpc64__)
349	/*
350	 * Clear battable[] translations
351	 */
352	if (!(cpu_features & PPC_FEATURE_64))
353		__asm __volatile("mtdbatu 2, %0\n"
354				 "mtdbatu 3, %0" : : "r" (0));
355	isync();
356#endif
357
358	result = ofwcall(args);
359
360	/* Restore trap vecotrs */
361	ofw_restore_trap_vec(save_trap_of);
362
363	ofw_sprg_restore();
364
365	intr_restore(oldmsr);
366
367	return (result);
368}
369
370#ifdef SMP
371struct ofw_rv_args {
372	void *args;
373	int retval;
374	volatile int in_progress;
375};
376
377static void
378ofw_rendezvous_dispatch(void *xargs)
379{
380	struct ofw_rv_args *rv_args = xargs;
381
382	/* NOTE: Interrupts are disabled here */
383
384	if (PCPU_GET(cpuid) == 0) {
385		/*
386		 * Execute all OF calls on CPU 0
387		 */
388		rv_args->retval = openfirmware_core(rv_args->args);
389		rv_args->in_progress = 0;
390	} else {
391		/*
392		 * Spin with interrupts off on other CPUs while OF has
393		 * control of the machine.
394		 */
395		while (rv_args->in_progress)
396			cpu_spinwait();
397	}
398}
399#endif
400
401static int
402openfirmware(void *args)
403{
404	int result;
405	#ifdef SMP
406	struct ofw_rv_args rv_args;
407
408	rv_args.args = args;
409	rv_args.in_progress = 1;
410	smp_rendezvous(smp_no_rendevous_barrier, ofw_rendezvous_dispatch,
411	    smp_no_rendevous_barrier, &rv_args);
412	result = rv_args.retval;
413	#else
414	result = openfirmware_core(args);
415	#endif
416
417	return (result);
418}
419
420void
421OF_reboot()
422{
423	struct {
424		cell_t name;
425		cell_t nargs;
426		cell_t nreturns;
427		cell_t arg;
428	} args;
429
430	args.name = (cell_t)(uintptr_t)"interpret";
431	args.nargs = 1;
432	args.nreturns = 0;
433	args.arg = (cell_t)(uintptr_t)"reset-all";
434	openfirmware_core(&args); /* Don't do rendezvous! */
435
436	for (;;);	/* just in case */
437}
438
439#endif /* AIM */
440
441void
442OF_getetheraddr(device_t dev, u_char *addr)
443{
444	phandle_t	node;
445
446	node = ofw_bus_get_node(dev);
447	OF_getprop(node, "local-mac-address", addr, ETHER_ADDR_LEN);
448}
449
450/*
451 * Return a bus handle and bus tag that corresponds to the register
452 * numbered regno for the device referenced by the package handle
453 * dev. This function is intended to be used by console drivers in
454 * early boot only. It works by mapping the address of the device's
455 * register in the address space of its parent and recursively walk
456 * the device tree upward this way.
457 */
458static void
459OF_get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep, int *pcip)
460{
461	char type[64];
462	uint32_t addr, size;
463	int pci, res;
464
465	res = OF_getprop(node, "#address-cells", &addr, sizeof(addr));
466	if (res == -1)
467		addr = 2;
468	res = OF_getprop(node, "#size-cells", &size, sizeof(size));
469	if (res == -1)
470		size = 1;
471	pci = 0;
472	if (addr == 3 && size == 2) {
473		res = OF_getprop(node, "device_type", type, sizeof(type));
474		if (res != -1) {
475			type[sizeof(type) - 1] = '\0';
476			pci = (strcmp(type, "pci") == 0) ? 1 : 0;
477		}
478	}
479	if (addrp != NULL)
480		*addrp = addr;
481	if (sizep != NULL)
482		*sizep = size;
483	if (pcip != NULL)
484		*pcip = pci;
485}
486
487int
488OF_decode_addr(phandle_t dev, int regno, bus_space_tag_t *tag,
489    bus_space_handle_t *handle)
490{
491	uint32_t cell[32];
492	bus_addr_t addr, raddr, baddr;
493	bus_size_t size, rsize;
494	uint32_t c, nbridge, naddr, nsize;
495	phandle_t bridge, parent;
496	u_int spc, rspc, prefetch;
497	int pci, pcib, res;
498
499	/* Sanity checking. */
500	if (dev == 0)
501		return (EINVAL);
502	bridge = OF_parent(dev);
503	if (bridge == 0)
504		return (EINVAL);
505	if (regno < 0)
506		return (EINVAL);
507	if (tag == NULL || handle == NULL)
508		return (EINVAL);
509
510	/* Assume big-endian unless we find a PCI device */
511	*tag = &bs_be_tag;
512
513	/* Get the requested register. */
514	OF_get_addr_props(bridge, &naddr, &nsize, &pci);
515	if (pci)
516		*tag = &bs_le_tag;
517	res = OF_getprop(dev, (pci) ? "assigned-addresses" : "reg",
518	    cell, sizeof(cell));
519	if (res == -1)
520		return (ENXIO);
521	if (res % sizeof(cell[0]))
522		return (ENXIO);
523	res /= sizeof(cell[0]);
524	regno *= naddr + nsize;
525	if (regno + naddr + nsize > res)
526		return (EINVAL);
527	spc = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK : ~0;
528	prefetch = (pci) ? cell[regno] & OFW_PCI_PHYS_HI_PREFETCHABLE : 0;
529	addr = 0;
530	for (c = 0; c < naddr; c++)
531		addr = ((uint64_t)addr << 32) | cell[regno++];
532	size = 0;
533	for (c = 0; c < nsize; c++)
534		size = ((uint64_t)size << 32) | cell[regno++];
535
536	/*
537	 * Map the address range in the bridge's decoding window as given
538	 * by the "ranges" property. If a node doesn't have such property
539	 * then no mapping is done.
540	 */
541	parent = OF_parent(bridge);
542	while (parent != 0) {
543		OF_get_addr_props(parent, &nbridge, NULL, &pcib);
544		if (pcib)
545			*tag = &bs_le_tag;
546		res = OF_getprop(bridge, "ranges", cell, sizeof(cell));
547		if (res == -1)
548			goto next;
549		if (res % sizeof(cell[0]))
550			return (ENXIO);
551		res /= sizeof(cell[0]);
552		regno = 0;
553		while (regno < res) {
554			rspc = (pci)
555			    ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
556			    : ~0;
557			if (rspc != spc) {
558				regno += naddr + nbridge + nsize;
559				continue;
560			}
561			raddr = 0;
562			for (c = 0; c < naddr; c++)
563				raddr = ((uint64_t)raddr << 32) | cell[regno++];
564			rspc = (pcib)
565			    ? cell[regno] & OFW_PCI_PHYS_HI_SPACEMASK
566			    : ~0;
567			baddr = 0;
568			for (c = 0; c < nbridge; c++)
569				baddr = ((uint64_t)baddr << 32) | cell[regno++];
570			rsize = 0;
571			for (c = 0; c < nsize; c++)
572				rsize = ((uint64_t)rsize << 32) | cell[regno++];
573			if (addr < raddr || addr >= raddr + rsize)
574				continue;
575			addr = addr - raddr + baddr;
576			if (rspc != ~0)
577				spc = rspc;
578		}
579
580	next:
581		bridge = parent;
582		parent = OF_parent(bridge);
583		OF_get_addr_props(bridge, &naddr, &nsize, &pci);
584	}
585
586	return (bus_space_map(*tag, addr, size,
587	    prefetch ? BUS_SPACE_MAP_PREFETCHABLE : 0, handle));
588}
589
590