/*
 * Copyright 1991-1998 by Open Software Foundation, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */
/*
 * Copyright 1991-1998 by Apple Computer, Inc. 
 *              All Rights Reserved 
 *  
 * Permission to use, copy, modify, and distribute this software and 
 * its documentation for any purpose and without fee is hereby granted, 
 * provided that the above copyright notice appears in all copies and 
 * that both the copyright notice and this permission notice appear in 
 * supporting documentation. 
 *  
 * APPLE COMPUTER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE 
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 * FOR A PARTICULAR PURPOSE. 
 *  
 * IN NO EVENT SHALL APPLE COMPUTER BE LIABLE FOR ANY SPECIAL, INDIRECT, OR 
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 
 * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT, 
 * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION 
 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 
 */

/*
 * MkLinux
 */
/*
 *  (c) Copyright 1988 HEWLETT-PACKARD COMPANY
 *
 *  To anyone who acknowledges that this file is provided "AS IS"
 *  without any express or implied warranty:
 *      permission to use, copy, modify, and distribute this file
 *  for any purpose is hereby granted without fee, provided that
 *  the above copyright notice and this notice appears in all
 *  copies, and that the name of Hewlett-Packard Company not be
 *  used in advertising or publicity pertaining to distribution
 *  of the software without specific, written prior permission.
 *  Hewlett-Packard Company makes no representations about the
 *  suitability of this software for any purpose.
 */

/*
 * Copyright (c) 1990,1991,1992,1994 The University of Utah and
 * the Computer Systems Laboratory (CSL).  All rights reserved.
 *
 * THE UNIVERSITY OF UTAH AND CSL PROVIDE THIS SOFTWARE IN ITS "AS IS"
 * CONDITION, AND DISCLAIM ANY LIABILITY OF ANY KIND FOR ANY DAMAGES
 * WHATSOEVER RESULTING FROM ITS USE.
 *
 * CSL requests users of this software to return to csl-dist@cs.utah.edu any
 * improvements that they make and grant CSL redistribution rights.
 *
 * 	Utah $Hdr: model_dep.c 1.34 94/12/14$
 */

#include <mach_kgdb.h>
#include <debug.h>
#include <mach_kdb.h>
#include <db_machine_commands.h>
#include <cpus.h>

#include <kern/thread.h>
#include <machine/pmap.h>
#include <machine/mach_param.h>
#include <device/param.h>
#include <device/ds_routines.h>
#include <device/subrs.h>
#include <device/device_typedefs.h>
#include <device/conf.h>
#include <device/misc_protos.h>

#include <mach/vm_param.h>
#include <mach/clock_types.h>
#include <mach/machine.h>
#include <machine/spl.h>
#include <ppc/boot.h>

#include <kern/misc_protos.h>
#include <kern/startup.h>
#include <ppc/misc_protos.h>
#include <ppc/proc_reg.h>
#include <ppc/thread.h>
#include <ppc/asm.h>
#include <ppc/mem.h>
#include <ppc/POWERMAC/pci.h>
#if NCPUS > 1 
#include <ppc/FirmwareCalls.h>
#include <ppc/POWERMAC/mp/mp.h>
#endif /* NCPUS > 1 */

#include <kern/clock.h>
#include <machine/trap.h>
#include <kern/spl.h>
#include <ppc/POWERMAC/serial_io.h>

/* IO Region 2 for Gatwick support */
/* MUST be declared before powermac.h is included! */
int		io_size2;		/* IO region 2 */
vm_offset_t	io_base_phys2;		/* IO region 2 */
vm_offset_t	io_base_virt2;		/* IO region 2 */

#include <ppc/POWERMAC/powermac.h>
#include <ppc/POWERMAC/powermac_performa.h>
#include <ppc/POWERMAC/powermac_gestalt.h>
#include <ppc/POWERMAC/device_tree.h>

#if MACH_KGDB
#include <kgdb/kgdb_defs.h>
#endif

#if	MACH_KDB
#include <ddb/db_aout.h>
#include <ddb/db_output.h>
#include <ddb/db_command.h>
#include <machine/db_machdep.h>

extern struct db_command ppc_db_commands[];
#endif	/* MACH_KDB */

void parse_args(boot_args *args);

char kernel_args_buf[256] = "/mach_kernel";
char boot_args_buf[256] = "/mach_servers/bootstrap";
char env_buf[256];

/* These should all be exported from kern/bootstrap.c */
extern vm_offset_t	kern_args_start;
extern vm_size_t	kern_args_size;

extern vm_offset_t	boot_args_start;
extern vm_size_t	boot_args_size;		

extern vm_offset_t	env_start;
extern vm_offset_t	env_size;

int	boottype = 0;	/* bootstrap needs this defined! */

int 	halt_in_debugger = 0;

extern int console_cache;
extern int pb1400_expansion_bay;


extern const char version[];
/*
 *	Cpu initialization.  Running virtual, but without MACH VM
 *	set up (1-1 BAT mappings).  First C routine called.
 */
void
machine_startup(unsigned int memory_size, boot_args *args)
{

	/*
	 * Global mem_size initialisation, may be overridden by
	 * command line arguments
	 */
	if (mem_size == 0)
		mem_size = memory_size;

	/* meb 11/2/95 - temporary hack for serial */
	cons_find(0);

	/*
	 * VM initialization, after this we're using page tables...
	 */
	ppc_vm_init(mem_size, args);

#if	MACH_KDB

	/*
	 * Initialize the kernel debugger.
	 */
#if	DB_MACHINE_COMMANDS
	db_machine_commands_install(ppc_db_commands);
#endif	/* DB_MACHINE_COMMANDS */
	ddb_init();

#endif	/* MACH_KDB */

#if	MACH_KDB || MACH_KGDB
	/*
	 * Cause a breakpoint trap to the debugger before proceeding
	 * any further if the proper option bit was specified in
	 * the boot flags.
	 *
	 * XXX use -a switch to invoke kdb, since there's no
	 *     boot-program switch to turn on RB_HALT!
	 */
	if (halt_in_debugger) {
		printf("inline call to debugger(machine_startup)\n");
	        Debugger("");
	}
#endif /* MACH_KDB || MACH_KGDB */

	machine_conf();

	/*
	 * Start the system.
	 */
	setup_main();

	/* Should never return */
}

/*
 * Return boot information in buf.
 */
#include <device/disk_status.h>
#include <scsi/rz.h>

char *
machine_boot_info(char *buf, vm_size_t size)
{
	dev_ops_t disk_ops;
	int unit, len;

	strcpy(buf, "boot_device");

	if (dev_name_lookup(buf, &disk_ops, &unit)) {
	    char *p;

	   /*
 	    * The controller number is currently ignored,
	    * to logically "merge" the two busses into
	    * one for the POWERMAC.
	    */

#ifdef	SCSI_SEQUENTIAL_DISKS
	   /* Attempt to pass in the indirect name... */
	   if (!dev_find_indirect(disk_ops, (unit & ~(RZPARTITION_MASK)), buf)) 
#endif
	   {
	    	strcpy(buf, disk_ops->d_name);
	    	itoa(rzslave(unit), buf + strlen(buf));
	    }
		
	    p = buf + strlen(buf);
	    *p++ = rzpartition(unit) + 'a';
	    *p = 0;
	} 

	if (kern_args_size > 0) {
		char *dst = buf + strlen(buf);
		char *src = (char*)kern_args_start;
		while (src < (char*) (kern_args_start + kern_args_size)) {
			int len;
			*dst++ = ' ';
			len = strlen(src);
			memcpy(dst, src, len+1);
			dst += len;
			src += len+1;
		}
	}
	return buf;
}

extern vm_offset_t env_start;
extern vm_size_t env_size;

const char * getenv(const char *name);

const char * getenv(const char *name)
{
	int len = strlen(name);
	const char *p = (const char *)env_start;
	const char *endp = p + env_size;

	while (p < endp) {
		if (len >= endp - p)
			break;
		if (strncmp(name, p, len) == 0 && *(p + len) == '=')
			return p + len + 1;
		while (*p++)
			;
	}
	return NULL;
}

void
machine_conf(void)
{
	/* XXX - we assume 1 cpu */
	machine_slot[0].is_cpu = TRUE;
	machine_slot[0].running = TRUE;

	/*
	 * We return the processor info, nothing on the machine
	 */

	machine_slot[0].cpu_type = CPU_TYPE_POWERPC;
	machine_slot[0].cpu_subtype = mfpvr();

	machine_info.max_cpus = NCPUS;
	machine_info.avail_cpus = 1;
	machine_info.memory_size = mem_size;
}

void
machine_init(void)
{
	dev_ops_t boot_dev_ops;
	int unit;
	char bootdev_name[10];
	const char *p;
	int n;

	autoconf();

	if ((p = getenv("rootdev")) || (p = getenv("root"))) { /* bootx passes root=... */
		/*
		 * Translate from a Linux Style to
		 * a MACH style one..
		 * Linux           MACH
		 * -----------------------------
 		 * Unit A-Z  -->   0 - NN
		 * Part 1-9  -->   A - Z
		 */

		/* Skip over the /dev/ prefix if present */

		if (strncmp(p, "/dev/", 5) == 0)
			p += 5;

		if (strncmp(p, "cdrom", 5) == 0) {
			strcpy(bootdev_name, p);
			bootdev_name[5] = p[5] ? p[5] : '0';
		} else if (strncmp(p, "scd", 3) == 0) {
			strcpy(bootdev_name, "cdrom");
			bootdev_name[5] = p[3];
			bootdev_name[6] = 0;
		} else {
			bootdev_name[0] = p[0];
			bootdev_name[1] = p[1];
			bootdev_name[2] = (p[2] - 'a') + '0';
			bootdev_name[3] = (atoi((u_char *)&p[3]) +
				   	('a'-1));	/* from 1-N to a-z*/
			bootdev_name[4] = '\0';
		}

		if (dev_name_lookup(bootdev_name, &boot_dev_ops, &unit)) 
			dev_set_indirection("boot_device", boot_dev_ops, unit);
		else
			printf("Warning: unable to set boot_device '%s'\n",
			       bootdev_name);
	} else {
		/*
	 	* Set the boot device to sd7a or whatever
	 	*/
		if (p = getenv("BOOTDEV"))
			strcpy(bootdev_name, p);

		if (p = getenv("BOOTUNIT")) 
			strcpy(bootdev_name + strlen(bootdev_name), p);

		if (dev_name_lookup(bootdev_name, &boot_dev_ops, &unit)) {
	
			if (p = getenv("BOOTPART")) {
				n = 0;
				while (*p >= '0' && *p <= '9')
					n = (n * 10) + (*p++ - '0');
			} else
				n = ((boottype >> 8) & 0xff);

			dev_set_indirection("boot_device", boot_dev_ops, unit+n);
		} else
			printf("Warning: unable to set boot_device '%s'\n", bootdev_name);
	}
	
	clock_config();
}

/* Routine to pre-initialise anything in IO space once powermac_io_base_v
 * has been initialised to new value and DMA space has been allocated
 */
void powermac_io_init(vm_offset_t powermac_io_base_v)
{
	if (powermac_info.class == POWERMAC_CLASS_PDM) {
		/* Set the DMA controller up to point at our DMA memory */
		struct pdm_dma_base *pdm_dma_base =
			(struct pdm_dma_base *)
				POWERMAC_IO(PDM_DMA_CTRL_BASE_PHYS);

		pdm_dma_base->addr_1 =
			(powermac_info.dma_buffer_virt >> 24) & 0xff;
		pdm_dma_base->addr_2 =
			(powermac_info.dma_buffer_virt >> 16) & 0xff;
		pdm_dma_base->addr_3 =
			(powermac_info.dma_buffer_virt >>  8) & 0xff;
		pdm_dma_base->addr_4 =
			(powermac_info.dma_buffer_virt      ) & 0xff;

		eieio();
	}
}

#include <sys/ioctl.h>
#include <device/device_types.h>
#include <device/disk_status.h>

#ifdef PDCDUMP
void
dumpconf(void)
{
	struct partinfo pi;
	
	/*
	 * The device should already be opened, and the dump info setup,
	 * but savecore needs a bit more.
	 */
	(*dumpdev_ops->d_setstat)(dumpdev, DIOCGPART, (int *)&pi, sizeof(pi));
	dumplo = pi.part->p_size - ctod(dumpsize);

	printf("dumpconf: size = %d blocks, *disk* offset = %d blocks\n",
	       ctod(dumpsize), dumpoffset);

	if (dumpsize != btoc(mem_size))
		printf("\tWARNING: Dump partition is smaller than mem size\n");
}
#endif

#include <mach/vm_prot.h>
#include <vm/pmap.h>
#include <mach/time_value.h>
#include <machine/machparam.h>	/* for btop */

/*
 * Universal (Posix) time map. This should go away when the
 * kern/posixtime.c routine is removed from the kernel.
 */
vm_offset_t
utime_map(
	dev_t			dev,
	vm_offset_t		off,
	int			prot)
{
	extern time_value_t *mtime;

	if (prot & VM_PROT_WRITE)
		return (-1);

	return (btop(pmap_extract(pmap_kernel(), (vm_offset_t) mtime)));
}

int dofastboot = 0;		/* if set, always do a fastboot() */
int dotocdump = 1;		/* if set, do a crash dump on TOC's */

void
halt_all_cpus(boolean_t	reboot)
{
	if(reboot)
	{
		printf("MACH Reboot\n");
		powermac_reboot();
	}
	else
	{
		printf("CPU halted\n");
		powermac_powerdown();
	} 
	while(1);
}

void
halt_cpu(void)
{
        static int paniced = 0;
	Debugger("halt cpu");
        halt_all_cpus(0);
}

void parse_args(boot_args *args)
{
	char	*src, *dest;
	char	ch;

	/*
	 * Okay, first split the command line into
	 * arguments and environments... assume that
	 * environment starts with first arg that
	 * doesn't have a '-'.
	 */

	src = args->CommandLine;

	/* APPEND arguments to kernel_args_buf */
	dest = kernel_args_buf + strlen(kernel_args_buf)+1;

	while (*src == '-') {
		/* Copy argument ... */
		while (*src && *src != ' ') {
			*dest++ = *src++;
		}
		/* replace white space with NULL */
		*dest++ = '\0';
		while (*src == ' ')
			src++;
	}
	*(dest-1) = '\0';

	/* kern_args_start and kern_args_size refer to kernel args */
	kern_args_start = (vm_offset_t)&kernel_args_buf[0];
	kern_args_size  = dest - kernel_args_buf;

	/* Now copy environment */
	dest = env_buf;
	while (*src) {
		while (*src != ' ' && *src) 
			*dest++ = *src++;
		*dest++ = '\0';
		while (*src == ' ')
			src++;
	}
	*dest = '\0';
	env_start = (vm_offset_t) env_buf;
	env_size = dest - env_buf;

	/* Now scan through our kernel arguments and
	 * act on them, plus look for bootstrap args and
	 * append them to the bootstrap arguments...
	 */
	src  = kernel_args_buf;

	boot_args_size = strlen(boot_args_buf)+1;
	boot_args_start = (vm_offset_t) boot_args_buf;

	while (src < (kernel_args_buf + kern_args_size)) {
		/* skip over any arguments not starting with '-' */
		if (*src++ != '-') {
			/* Eat up this argument and move to next,
			 * make sure to step beyond the NULL
			 */
			while (*src++ != '\0')
				;
			continue;
		}
		while (ch = *src++) {
			switch (ch) {
			case 'a':	/* -a , ask bootstrap to prompt... */
				strcpy(&boot_args_buf[boot_args_size], "-a");
				boot_args_size += 3;
				break;
			case 'v':	/* -v , console caching */
				console_cache = 1;
				break;
			case 'e':	/* -e , expbay support... */
				pb1400_expansion_bay = 1;
				break;
			case 'h':
				halt_in_debugger = 1;
				break;
			case 'm':	/* -m??:  memory size Mbytes*/
				mem_size = atoi_term(src, &src)*1024*1024;
				break;
			case 'r':
				switch_to_serial_console();
				break;
			default:
#if	NCPUS > 1
				if (ch > '0' && ch <= '9')
					wncpu = ch - '0';
#endif	/* NCPUS > 1 */
				break;
			}
		}
	}
}

#if	MACH_ASSERT

/*
 * Machine-dependent routine to fill in an array with up to callstack_max
 * levels of return pc information.
 */
void machine_callstack(
	natural_t	*buf,
	vm_size_t	callstack_max)
{
}

#endif	/* MACH_ASSERT */

void
Debugger(
	const char	*message)
{
#if	NCONSFEED >= 1
	console_feed_cancel_and_flush();
#endif	/* NCONSFEED >= 1 */

#if	MACH_KGDB
	kgdb_printf("Debugger(%s)\n", message);
	kgdb_break();
#else	/* MACH_KGDB */
#if	MACH_KDB
	if (db_active
#if	NCPUS > 1
	    && kdb_active[cpu_number()]
#endif	/* NCPUS > 1 */
	    ) {
		/* we're already in kdb: don't enter it again... */
		return;
	}
	if (db_is_alive()) {
		db_printf("Debugger(%s)\n", message);
		__asm__("tw 4,1,1");
		return;
	} else
#endif	/* MACH_KDB */
	{
		unsigned int* stackptr;
		int i;

#if	!MACH_KDB
		printf("\nNo debugger configured - "
		       "dumping debug information\n");
		printf("\nversion string : %s\n",version);
#endif	/* !MACH_KDB */
		printf("backtrace: ");

		/* Get our stackpointer for backtrace */

		/* TODO NMGS wrap this neatly in a function */

		__asm__ volatile("mr %0,	1" : "=r" (stackptr));

#ifdef ONLY_THIRTY_TWO
		for (i = 0; i < 32; i++) {
#else
		for (i = 0;(1); i++) {
#endif

			/* Avoid causing page fault */
#if	MACH_KDB || MACH_KGDB
			if (db_find_or_allocate_pte(kernel_pmap->space,
						    trunc_page((vm_offset_t)
							       stackptr),
						    FALSE) == NULL)
				break;
#else	/* MACH_KDB || MACH_KGDB */
			if (pmap_extract(kernel_pmap, (vm_offset_t)stackptr)
			    == 0)
				break;
#endif	/* MACH_KDB || MACH_KGDB */

			stackptr = (unsigned int*)*stackptr;
#if	MACH_KDB || MACH_KGDB
			if (db_find_or_allocate_pte(kernel_pmap->space,
						    trunc_page((vm_offset_t)
							       stackptr+
							       (FM_LR_SAVE/
								sizeof(int))),
						    FALSE) == NULL)
				break;
#else	/* MACH_KDB || MACH_KGDB */
			if (pmap_extract(kernel_pmap,
					 (vm_offset_t)stackptr+
					 (FM_LR_SAVE/sizeof(int))) == 0)
				break;
#endif	/* MACH_KDB || MACH_KGDB */
			if (!(i%8)) printf("\n"); // eliminate wrap around missing letter bug
			printf("%08x ",*(stackptr + (FM_LR_SAVE/sizeof(int))));
		}

#if	!MACH_KDB
		printf("\n\nPlease report the above messages to\n");
		printf("mklinux-bugs@globegate.utm.edu and reboot your machine.\n");

		while(1);
#endif	/* !MACH_KDB */
	}
#endif	/* MACH_KGDB */
}

powermac_info_t	powermac_info;

static char *powermac_chips[] = {
	"gc",
	"ohare",
	"mac-io",	/* aka Heathrow, aka Gatwick */
	NULL
};

void
powermac_define_machine(int machineGestalt)
{
	int		i;
	device_node_t	*chipdev;
	const char *var;

	/* Everything starts out zeroed... */
	bzero((void*) &powermac_info, sizeof(powermac_info_t));

	powermac_info.machine = machineGestalt;

	switch (machineGestalt) {
	case gestaltPowerMac6100_60:
	case gestaltPDM66F:
	case gestaltPowerMac6100_66:
	case gestaltPowerMac6100_80:
	case gestaltPowerMac7100_66:
	case gestaltPowerMac7100_66_alt:
	case gestaltPDM80WLCD:
	case gestaltPowerMac7100_80_chipped:
	case gestaltPowerMac7100_80:
	case gestaltPowerMac8100_80:
	case gestaltPDM100F:
	case gestaltPowerMac8100_100:
	case gestaltPowerMac8100_110:
	case gestaltPowerMac8100_120:
	case gestaltAWS9150_80:
	case gestaltAWS9150_120:
		powermac_info.class   		   = POWERMAC_CLASS_PDM;
		powermac_info.io_base_phys	   = PDM_IO_BASE_ADDR;
		powermac_info.io_size		   = PDM_IO_SIZE;
		powermac_info.dma_buffer_size	   = PDM_DMA_BUFFER_SIZE;
		powermac_info.dma_buffer_alignment = PDM_DMA_BUFFER_ALIGNMENT;
#if 0
	// uncomment me for nubus support, but FIX the problem with
	// running out of PTEs in the kernel PTEG first!

		io_base_phys2			   = PDM_NUBUS_BASE_PHYS;
		io_size2			   = PDM_NUBUS_SIZE;
#else
		io_base_phys2			   = 0;
		io_size2			   = 0;
#endif
		break;

	case gestaltPowerMac5200:
	case gestaltPowerMac6200:
		powermac_info.class		   = POWERMAC_CLASS_PERFORMA;
		powermac_info.io_base_phys	   = PDM_IO_BASE_ADDR;
		powermac_info.io_size		   = PDM_IO_SIZE;

		// This is a little bogus, isn't it?  I mean... there's
		// no DMA engine in these machines....
		powermac_info.dma_buffer_size	   = PDM_DMA_BUFFER_SIZE;
		powermac_info.dma_buffer_alignment = PDM_DMA_BUFFER_ALIGNMENT;
		break;

	case gestaltPowerBook520:
	case gestaltPowerBook5300:
	case gestaltPowerBookDuo2300:
	case gestaltPowerBook1400:
		powermac_info.class		   = POWERMAC_CLASS_POWERBOOK;
		powermac_info.io_base_phys	   = PDM_IO_BASE_ADDR;
		powermac_info.io_size		   = PDM_IO_SIZE;

		// Bogus again....
		powermac_info.dma_buffer_size	   = PDM_DMA_BUFFER_SIZE;
		powermac_info.dma_buffer_alignment = PDM_DMA_BUFFER_ALIGNMENT;
		break;

	case gestaltPowerMac7200:
	case gestaltPowerMac7500:
	case gestaltPowerMac8500:
	case gestaltPowerMac9500:
	default:
#if 0
		if (machineGestalt!=gestaltPowerMac7200 &&
		    machineGestalt!=gestaltPowerMac7500 &&
		    machineGestalt!=gestaltPowerMac8500 &&
		    machineGestalt!=gestaltPowerMac9500)
			printf("Unknown gestalt %d: Assuming PCI machine.\n\r",
				machineGestalt);
#endif

		powermac_info.class		   = POWERMAC_CLASS_PCI;
		powermac_info.io_base_phys	   = PCI_IO_BASE_ADDR;
		powermac_info.io_size		   = PCI_IO_SIZE;

		for (i = 0; powermac_chips[i]  ; i ++)  {
			if ((chipdev = find_devices(powermac_chips[i])) == NULL)
				continue;

			powermac_info.io_base_phys = chipdev->addrs[0].address;
			powermac_info.io_size = chipdev->addrs[0].size;

			if ((i == 2) && chipdev->next) {
				if (chipdev->next->n_addrs && chipdev->next->n_intrs) {
					io_base_phys2 = chipdev->next->addrs[0].address;
					io_size2 = chipdev->next->addrs[0].size;
					if (io_size2 < PCI_IO_SIZE)
						io_size2 = PCI_IO_SIZE;
				}
				else {
					io_base_phys2 = 0;
					io_size2 = 0;
				}
			}
			break;
		}
		break;

	}

	/* Now set up the clock */
	var=getenv("bus_speed_hz");
	if (var !=NULL) {
		/* we'll assume that if they're setting the bus speed
		 * manually, that they know what they're doing, so
		 * we don't make the correction for NuBus PDM's....
		 * Instead, just adjust the bus speed until the clock
		 * doesn't drift.
		 */

		powermac_info.bus_clock_rate_hz = atoi((u_char*)var);
		/* Sanity check */
		if (powermac_info.bus_clock_rate_hz < 20000000)
			powermac_info.bus_clock_rate_hz	= 
				40000000; /* 40 MHz*/
		powermac_info.proc_clock_to_nsec_numerator = 1000 * 4;
		powermac_info.proc_clock_to_nsec_denominator = 
			powermac_info.bus_clock_rate_hz/1000000;
	}
	else {
		if (PROCESSOR_VERSION == PROCESSOR_VERSION_601) {
			if (powermac_info.class == POWERMAC_CLASS_PDM) {
				/* PDM 601 doesn't quite tick at nsec precision */
				powermac_info.proc_clock_to_nsec_numerator   = 4085;
				powermac_info.proc_clock_to_nsec_denominator = 4096;
			} else {
				powermac_info.proc_clock_to_nsec_numerator   = 1;
				powermac_info.proc_clock_to_nsec_denominator = 1;
			}
		} else {
			device_node_t *cpu;
			unsigned long *prop_freq;

			if ((cpu = find_type("cpu")) != NULL) {
				if (prop_freq = (unsigned long *)
				    		     get_property(cpu,
						     "timebase-frequency",
								  NULL)) {
					powermac_info.bus_clock_rate_hz = *prop_freq;
					powermac_info.proc_clock_to_nsec_numerator = 60000;
					powermac_info.proc_clock_to_nsec_denominator = (*prop_freq*60)/1000000;
				} 
			} else {
				/* Default to 40 MHz, 33.33 MHz for powerbook */
				if (powermac_info.class == POWERMAC_CLASS_POWERBOOK)
					powermac_info.bus_clock_rate_hz	= 33333333;
				else
					powermac_info.bus_clock_rate_hz	= 40000000;

				if (powermac_info.class == POWERMAC_CLASS_PERFORMA) {
					unsigned long *procid;
#ifndef FIX_BOGUS_GESTALTS
					unsigned long hwid=0xDEADBEEF;
#else
					extern unsigned long hwid;
#endif
					// procid = (unsigned long *)0x5ffffffc;
					procid = (unsigned long *)hwid;

					switch ((*procid) & 0xff) {
					    case 0x50:
					    case 0x58:
						// 75 MHz Performa
						powermac_info.bus_clock_rate_hz = 25000000;
						break;
					    case 0x51:
					    case 0x59:
						// 80 MHz Performa
						powermac_info.bus_clock_rate_hz = 40000000;
						break;
					    default:
						// PPC upgraded 68k Mac....
						// Guess a value.
						powermac_info.bus_clock_rate_hz = 33000000;
						break;
					}
				}
				if (powermac_info.class == POWERMAC_CLASS_PDM) {
					/* if we reach this point, it's a PDM with a
					 * processor upgrade card.
					 */
					switch (powermac_info.machine) {
						case gestaltPowerMac6100_60:
							powermac_info.bus_clock_rate_hz =
								30000000;
							break;
						case gestaltPDM66F:
						case gestaltPowerMac6100_66:
						case gestaltPowerMac7100_66:
						case gestaltPowerMac7100_66_alt:
							powermac_info.bus_clock_rate_hz =
								33000000;
							break;
						case gestaltPDM80WLCD:
						case gestaltPowerMac6100_80:
						case gestaltPowerMac7100_80:
						case gestaltPowerMac7100_80_chipped:
						case gestaltPowerMac8100_80:
						case gestaltAWS9150_80:
							// Unless I hear otherwise, it's
							// probably safe to assume that
							// the AWS9150 falls into this
							// category....  --dg

							powermac_info.bus_clock_rate_hz =
								40000000;
							break;
						case gestaltPDM100F:
						case gestaltPowerMac8100_100:
							powermac_info.bus_clock_rate_hz =
								33333333;
							break;
						case gestaltPowerMac8100_110:
							powermac_info.bus_clock_rate_hz =
								36666666;
							break;
						case gestaltPowerMac8100_120: /* ??? */
						case gestaltAWS9150_120: /* ??? */
							// I have no specs to confirm
							// these machines' bus speeds.

							powermac_info.bus_clock_rate_hz =
								40000000;
							break;
						default: /* Hopefully none.... */
							powermac_info.bus_clock_rate_hz =
								40000000;
						}

					powermac_info.proc_clock_to_nsec_numerator = 1000 * 4;
					powermac_info.proc_clock_to_nsec_denominator = 
						powermac_info.bus_clock_rate_hz/1000000;
					}
				else {
					/* NuBus PowerBooks, NuBus Performas */
					powermac_info.proc_clock_to_nsec_numerator = 1000 * 4;
					powermac_info.proc_clock_to_nsec_denominator = 
						powermac_info.bus_clock_rate_hz/1000000;
					}
			}
		}
	}

	/* IO mapping starts off as 1-1 on all machines */
	powermac_info.io_base_virt = powermac_info.io_base_phys;
	io_base_virt2 = io_base_phys2;

	powermac_info.io_coherent  = powermac_is_coherent();
}
