/* $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 */

#ident	"$Header: autoconf.c 1.51 90/10/22 $"

/*
 * autoconf.c
 *	Auto-configuration.
 */

/* $Log:	autoconf.c,v $
 */

#include "sys/types.h"
#include "sys/devsw.h"
#include "sys/sysmacros.h"
#include "sys/signal.h"
#include "sys/param.h"
#include "sys/debug.h"
#include "sys/mutex.h"
#include "sys/systm.h"
#include "sys/user.h"
#include "sys/map.h"
#include "sys/vm.h"
#include "sys/timer.h"
#include "sys/proc.h"
#include "sys/buf.h"
#include "sys/conf.h"
#include "sys/swap.h"
#include "sys/cmn_err.h"
#include "sys/arch.h"

#include "sys/clock.h"
#include "sys/engine.h"
#include "sys/slic.h"
#include "sys/slicreg.h"
#include "sys/clkarb.h"
#include "sys/cfg.h"
#include "sys/sysmacros.h"

#include "sys/pte.h"
#include "sys/hwparam.h"
#include "sys/plocal.h"
#include "sys/mmu.h"
#include "sys/gate.h"
#include "sys/intctl.h"
#include "sys/autoconf.h"
#include "sys/sec.h"			  /* needed for conf_sec */
#include "sys/sec_device.h"		  /* needed for conf_sec */

unsigned	Nengine;			/* # processors */
struct	engine	*engine;			/* base of engine array */
struct	engine	*engine_Nengine;		/* end of engine array */
int		NFPA = 0;			/* # processors with FPA's */
short		fp_lights;			/* Front panel lights */
unchar		cons_scsi = 0xff;		/* console SCSI's SLIC id */
unchar		cons_slic = 0xff;		/* console printf SLIC id */
unsigned	boothowto;			/* boot flags */
unsigned	sys_clock_rate;			/* # Mega Hz system runs at */

dev_t smem_devnum = -1;				/* for smem pseudo driver */
dev_t cons_devnum = -1;				/* for cons pseudo driver */
char	cons_kill = 0;				/* for ld0 - kill char    */
char	cons_erase = 0;				/* for ld0 - erase char   */
char	cons_intr = 0;				/* for ld0 - intr char    */
struct	firmwaresw	*firmwaresw;		/* firmware interface table */
struct	bin_header int_bin_table[SLICBINS];	/* Interrupt Bin Table */
int	bin_alloc[SLICBINS];			/* for allocating vectors */

/*
 * slic_to_config[] maps SLIC number to configuration information about
 * that SLIC.
 */

struct	ctlr_desc *slic_to_config[MAX_NUM_SLIC];

/*
 * sec0eaddr encodes a system unique 24-bit number.  Name is historic.
 * This value is the low-order 24-bits of the ether address on SCED[0] for
 * a B8k; on a B21k it's the system-ID value from the backplane (readable thru
 * the CADM).  After system is up and /etc/init has the magic system ID number,
 * sec0eaddr holds the number of users the system can legally support.
 */

unsigned	sec0eaddr = -1;		/* system ID number */

static void	conf_sec();
static void	strayint();
static long	*busnum_by_type;	  /* indexed by busnum */
#ifdef DEBUG
extern int	boot_debug;
#endif /* DEBUG */

/*
 * sb8000_configure()
 *
 * Scan the HW configuration information, do probes, etc,
 * all in the name of determining what's out there.
 */

sb8000_configure()
{
	register struct ctlr_desc *cd;

	/*
	 * Determine boot flags and system clock rate.
	 */

	boothowto = CD_LOC->c_boot_flag;
	sys_clock_rate = CD_LOC->c_clock_rate;

	/*
	 * Build slic to config information map.
	 */

	for (cd = CD_LOC->c_ctlrs; cd < CD_LOC->c_end_ctlrs; cd++)
		slic_to_config[cd->cd_slic] = cd;

	/*
	 * Do configuration/allocation of basic system components.
	 */

	conf_console();

	/*
	 * Allow GENERIC configure to set root and swap configuration.
	 * Also, sets boot_debug with -D flag.
	 */

	setconf();

	conf_clkarb();
	conf_proc();
	conf_mem();


	/*
	 * Allocate interrupt table, set up pseudo devices, and
	 * probe IO controllers (includes MBAd's, SCED's, ZDC's, etc).
	 */

	conf_intr();
	autoconfigure();		  /* the new autoconfiguration */
	conf_pseudo();
	conf_sec();

}

/*
 * autoconfigure()
 * This is the new autoconfiguration code.  It will eventually be folded back
 * into configure.
 */
static long *probe_array;
static unsigned long sizeof_probe_array;
static long nbusses;
static int rootbus;

autoconfigure()
{
	int driver, bustype;
	struct gen_device *device;
	char *devlimit;
	struct dev_on_bus *on;
	struct device_config *config;
	long maxunit, nspaces, devmax, notfound;
	struct gen_device fake_device; /* fakeout parent/root */
	struct driver_module fake_driver;
	long maxtypebus = 0, *bustype2max;
	char *s = "";

	/* Determine the bus type to boot */
	switch (arch_type) {

	default:
	case SYSTYP_S27:
	case SYSTYP_S81:
	case SYSTYP_S16:
		/*
		 * SYMMETRY hardware platforms
		 */
		s = "sb8000";
		break;

	case SYSTYP_S1:
		/*
		 * PC/3AT hardware platforms
		 */
		s = "at386";
		break;
	}
	for ( rootbus = 0; bus_type[rootbus] != NULL; rootbus++ ) {
		if ( strcmp(s, bus_type[rootbus]) == 0 )
			break;
	}
	if (bus_type[rootbus] == NULL)
		cmn_err( CE_PANIC,
			"autoconfigure: no valid bus type found");
	/*
	 *+ No match was found between the bus type(s) generated
	 *+ by config and the actual bus type of the system.
	 */

	/* Calculate number of busses and largest bus type */
	for ( nbusses = 0; busses[nbusses].bustype; nbusses++ ) {
		if ( busses[nbusses].bustype > maxtypebus )
			maxtypebus = busses[nbusses].bustype;
		/* 
		 * Fake out probe by making the root bus node appear booted.
		 */
		if ( busses[nbusses].bustype == rootbus )
		{
			busses[nbusses].owner = &fake_device;
			fake_device.driver = &fake_driver;
		}
	}

	bustype2max = (long *)calloc(maxtypebus * sizeof(long));
	busnum_by_type = (long *)calloc(nbusses * sizeof(long));

	/* Fill in busnum_by_type array */
	for (nbusses = 0; busses[nbusses].bustype; nbusses++){
		busnum_by_type[nbusses] = bustype2max[busses[nbusses].bustype];
		bustype2max[busses[nbusses].bustype] += 1;
	}

	/* Calculate maximum unit number */
	maxunit = 0;
	for ( driver = 0; drivers[driver].numdev; driver++ )
		for ( on = drivers[driver].on_busses; on->bustype; on++ )
		{
			nspaces = on->nspaces;
			for ( config = on->config_array;
			      config->dev_len != -1;
			      config += nspaces )
				;
			devmax = (config - on->config_array)/nspaces - 1;
			if ( maxunit < devmax )
				maxunit = devmax;
		}

#ifdef DEBUG
	if (boot_debug) {
		if ( nbusses == 1 )
			printf( "# There is 1 bus in the system.  ");
		else	printf( "# There are %d busses in the system.  ", nbusses);
		printf( "# The maximum possible unit number is %d.\n", maxunit);
		printf( "# boot_debug (-D) is %s\n", boot_debug ? "on" : "off" );
	}
#endif	/* DEBUG */

	sizeof_probe_array = nbusses * maxunit;	/* in bits */
	sizeof_probe_array = (sizeof_probe_array + NBBY - 1)/NBBY; /* bytes */

	callocrnd( sizeof (long) );
	probe_array = (long *)calloc((int)sizeof_probe_array );

	/* For each driver...*/
	for ( driver = 0; drivers[driver].numdev; driver++ )
	{
		/* clear probe_array.  */
		bzero( (caddr_t) probe_array, (unsigned) sizeof_probe_array);

		/* figure out the limits for the loop */
		devlimit = (char *)drivers[driver].devices
			+ drivers[driver].numdev * drivers[driver].size_dev;

		/*
		 * Each device table is scanned 4 times to limit name space
		 * slippage.
		 */
		   
		/*
		 * This first loop is special.  It sets up the fields of the
		 * device record that are true regardless of wildcarding.  It
		 * also proceeds to probe, but only devices that are not at all
		 * wildcarded.
		 */

		/* For each device, no wildcards... */
		for ( device=drivers[driver].devices;
		     (char *) device < devlimit;
		     device = (struct gen_device *)
		     ((char *)device + drivers[driver].size_dev) )
		{
			/* once per device setup */
			device->driver = drivers + driver;

			if ( device->busnum < 0 ) {
				device->bus = busses + 0;
				bustype = -device->busnum;
			} else {
				device->bus = busses + device->busnum;
				bustype = device->bus->bustype;
			}
			
			for ( on=drivers[driver].on_busses; on->bustype; on++)
				if ( on->bustype == bustype )
					break;

			if ( on->bustype == NOBUS ) {
				cmn_err( CE_WARN,
					"%s%d on %s unsupported, not probed.",
					drivers[driver].name,
					device - drivers[driver].devices,
					bus_type[bustype]);
				/*
				 *+ While the kernel was probing to find
				 *+ devices, it located a device that was
				 *+ configured on a bus of a type not supported
				 *+ by the driver.  The kernel needs to be
				 *+ reconfigured for that device to be used.
				 */
				
				device->driver = (struct driver_module *)0;
				continue;
			}
			
			device->on_bus = on;
			device->nspaces = on->nspaces;
			device->config = on->config_array;
			if ( device->unit >= 0 ) {
				/*
				 * Validate that there are enough
				 * records in the config table so 
				 * we don't go off the end it while
				 * accessing this unit.  The loop leaves
				 * the config address as the last record
				 * for the desired unit or one that is
				 * erroneous. If no errors then set
				 * the device' config addr to the
				 * appropriate place in the table.
				 */
				devmax = (device->unit+1) * device->nspaces - 1;
				for (; devmax--; device->config++) 
					if (device->config->dev_len == -1) 
						break;

				if ( device->config->dev_len == -1 ) {
					cmn_err( CE_WARN,
					    "%s%d unit too big, not probed.",
					    drivers[driver].name,
					    device - drivers[driver].devices);
					/*
					 *+ While the kernel was probing to locate 
					 *+ devices, it found a device that
					 *+ was specified with a unit number
					 *+ larger than the largest legal unit
					 *+ for this device.  The kernel needs
					 *+ to be reconfigured before this
					 *+ device can be used.
					 */

					device->driver
						= (struct driver_module *)0;
					continue;
				}
				device->config -= device->nspaces - 1;
			}

			if ( device->busnum < 0 || device->unit < 0 ) {
				if ( !drivers[driver].probe ) {
					cmn_err( CE_WARN,
					    "Can't wildcard %s%d, not probed.",
					    drivers[driver].name,
					    device - drivers[driver].devices);
					/*
					 *+ While the kernel was probing to
					 *+ find devices, it located a device 
					 *+ that was wildcarded and for which
					 *+ there is no probe routine.  There is
					 *+ no way for the kernel to find this
					 *+ device.  Corrective action:  for the 
					 *+ system to use this device, you need to reconfigure
					 *+ the kernel and fully specify the bus
					 *+ and unit number.
					 */
					device->driver
						= (struct driver_module *)0;
				}
				continue;
			}

			/* Non-wildcarded only */
			if ( !probe( device ) )
				device->driver = (struct driver_module *) 0;
		}

		/* For each device, unit wild... */
		for ( device=drivers[driver].devices;
		     (char *) device < devlimit;
		     device = (struct gen_device *)
		     ((char *)device + drivers[driver].size_dev) )
		{
			if ( device->busnum < 0 || device->unit >= 0
			    || !device->driver)
				continue;

			/* Find the right unit */
			config = device->config;
			for ( ; device->config->dev_len >= 0;
			     device->config += device->nspaces)
				if ( probe( device ) ) {
					device->unit = (device->config-config)
						/ device->nspaces;
					break;
				}
			if ( device->unit < 0 )
				device->driver = (struct driver_module *) 0;
				
		}

		/* For each device, busses wild... */
		for ( device=drivers[driver].devices;
		     (char *) device < devlimit;
		     device = (struct gen_device *)
		     ((char *)device + drivers[driver].size_dev) )
		{
			if ( device->busnum >= 0 || device->unit < 0
			    || !device->driver )
				continue;
			bustype = -device->busnum;
			for ( ; device->bus->bustype != NOBUS; device->bus++) {
				if ( device->bus->bustype != bustype )
					continue;
				if ( probe( device ) ) {
					device->busnum = device->bus - busses;
					break;
				}
			}
			if ( device->busnum < 0 )
				device->driver = (struct driver_module *) 0;
		}

		/* For each device, double wild... */
		for ( device=drivers[driver].devices;
		     (char *) device < devlimit;
		     device = (struct gen_device *)
		     ((char *)device + drivers[driver].size_dev) )
		{
			if ( device->busnum >= 0 || device->unit >= 0
			    || !device->driver)
				continue;
			/* For each bus of the given type... */
			bustype = -device->busnum;
			for ( ; device->bus->bustype != NOBUS; device->bus++) {
				if ( device->bus->bustype != bustype )
					continue;

				/* For each unit.... */
				config = device->config;
				for ( ; device->config->dev_len >= 0;
				     device->config += device->nspaces )
					if ( probe( device ) ) {
						device->unit = (device->config
								- config)
							/ device->nspaces;
						device->busnum = device->bus
							- busses;
						break;
					}
				if ( device->busnum >= 0 )
					break;
				else device->config = config;
			}		  /* end for each bus... */
			if ( device->busnum < 0 )
				device->driver = (struct driver_module *) 0;
		}

		/* All the devices of this type found, init them */
		if (drivers[driver].nalive && drivers[driver].init)
			(*drivers[driver].init)(drivers + driver);

		notfound = drivers[driver].numdev - drivers[driver].nalive;
		if (notfound) {
			/* not all of this type of unit found */
			CPRINTF("        # %d %s%s not found \n", notfound,
			       drivers[driver].name, (notfound==1) ? "" : "s");
		}
	}				  /* end of "For each driver..." */

	/* undo the ownership of the root bus */
	for ( nbusses = 0; busses[nbusses].bustype; nbusses++ )
		if ( busses[nbusses].bustype == rootbus )
			busses[nbusses].owner = (struct gen_device *) 0;
}

/*
 * probe(device)
 * probes the given device, returning 1 on success and zero on failure.  On
 * success, the following fields in the device record are filled out:
 *	next, map_origin, bus_map_addr, map_extent, n_maps, and devreg.
 * On failure, the value of driver is set to zero.
 *
 * NOTE that probe knows that in the double wild case, unit is in the inner
 * loop. If a bus controller isn't present and the unit is wild, it will short
 * circuit the probe. 
 *
 * NOTE that this routine only fills out next by itself--the bus specific
 * probe_setup routine must fill in the rest.
 */
probe(device)
	struct gen_device *device;
{
	struct driver_module *driver = device->driver;
	struct gen_device *chain;
	int busnum = device->bus - busses;
	int unit = (device->config - device->on_bus->config_array)
		/ device->nspaces;
	int index, mask, i, j;

	index = (unit * nbusses + busnum)/(NBBY * sizeof (long));
	mask = 1 << ((unit * nbusses + busnum) % (NBBY* sizeof (long)));

	/* If this bus's controller isn't alive, don't bother */
	if ( !device->bus->owner || !device->bus->owner->driver ) {
#ifdef DEBUG
		if (boot_debug)
			printf("PROBE: bus controller for bus %d not alive!\n",
			       busnum);
#endif /* DEBUG */
		if ( device->unit < 0 ) {
			/* wild card unit, short circuit */
			while ( device->config->dev_len != -1 )
				device->config++;
			/*
			 * -- to counter the impending ++ in autoconfigure
			 */
			device->config -= device->nspaces;
#ifdef DEBUG
			if (boot_debug)
				printf( "probe: unit wild, short-circuited\n");
#endif /* DEBUG */

		}			
		return 0;
	}

	/*
	 * If this busnum/unit combination has already probed alive, it is an
	 * automatic probe failure
	 */
#ifdef DEBUG
	if (boot_debug)
		printf( "PROBE: unit = %d index = %d, mask = %x%s\n",
		       unit, index, mask,
		       (probe_array[ index ] & mask)? ", already probed" : "");
#endif /* DEBUG */

	if ( probe_array[ index ] & mask )
		return 0;

	/*
	 * If this device configuration overlaps another device that has been
	 * previously found on this bus, it is an automatic probe failure.
	 */
	for ( chain = device->bus->devices; chain; chain = chain->next )
	    for ( i = 0; i < device->nspaces; i++ ) {
#ifdef DEBUG
      if (boot_debug)
       printf("\tDevice space[%d]: space = %x addr = %x, len = %x, int = %x\n",
	      i, device->config[i].space,
	      device->config[i].dev_addr,
	      device->config[i].dev_len,
	      device->config[i].busint);
#endif /* DEBUG */
		if ( device->config[i].dev_len == 0 )
			continue;
		for ( j = 0; j < chain->nspaces; j++ ) {
#ifdef DEBUG
  if (boot_debug)
    printf("\tBus Chain space[%d]: space = %x addr = %x, len = %x, int = %x\n",
	   j, chain->config[j].space,
	   chain->config[j].dev_addr,
	   chain->config[j].dev_len,
	   chain->config[j].busint);
#endif /* DEBUG */
		    if ( chain->config[j].dev_len == 0 )
			    continue;
		    if ( device->config[i].space == chain->config[j].space
			&& !(   (device->config[i].dev_addr
				 >= chain->config[j].dev_len
				 + chain->config[j].dev_addr)
			     || (chain->config[j].dev_addr
				 >= device->config[i].dev_len
				 + device->config[i].dev_addr) ) )
			    return 0;
	    }
	}

	/*
	 * If probe setup fails, it is responsible for printing out any
	 * appropriate error messages. If it succeeds, it is responsible for
	 * calling the driver probe routine (if it exists) and failing if the
	 * driver probe fails.  A non-existant driver probe routine is always
	 * counted as succeeding.
	 *
	 * Note that probe_setup should fill in the following device record
	 * fields if the probe was successful.
	 *	map_origin, bus_map_addr, map_extent, n_maps, and devreg
	 */
	if ( device->bus->probe_setup ) {
		if ( !(*device->bus->probe_setup)(device) ) {
#ifdef DEBUG
			if (boot_debug)
				printf( "probe failed, unit = %d\n",
				 (device->config-device->on_bus->config_array)
				       / device->nspaces );
#endif /* DEBUG */
			       
			/*
			 * if dev_len == -1, this is a probe short circuit.
			 * -- to counter the impending ++ in autoconfigure
			 */
			if ( device->config->dev_len == -1 ) {
				device->config -= device->nspaces;
#ifdef DEBUG
				if (boot_debug)
					printf( "probe: short-circuited\n");
#endif /* DEBUG */
			}
			return 0;
		}
	} else if ( driver->probe )
		if ( !(*driver->probe)(device) ) {
#ifdef DEBUG
			if (boot_debug)
				printf( "probe failed, unit = %d\n",
				  (device->config-device->on_bus->config_array)
				       / device->nspaces );
#endif /* DEBUG */
			       
			/*
			 * if dev_len == -1, this is a probe short circuit.
			 * -- to counter the impending ++ in autoconfigure
			 */
			if ( device->config->dev_len == -1 ) {
				device->config -= device->nspaces;
#ifdef DEBUG
				if (boot_debug)
					printf( "probe: short-circuited\n");
#endif /* DEBUG */
			}
			return 0;
		}
	
	/*
	 * Successful probe, fill in data structures.  We need to recompute
	 * unit, index and mask here in case either the bus probe setup or the
	 * driver probe routine changes device->config.  This allows those
	 * routines to short circuit the exhaustive search that autoconfigure
	 * does. For example, for devices on the sb8000 bus, there is a table
	 * for each kind of device that is present.
	 * 		device - device->driver->devices
	 * is the index of this particular device in that table.  The record
	 * thus described gives the configuration of the device (including the
	 * slic address.)
	 *
	 * NOTE that a lower level routine may choose to call probe recursively
	 * to validate the changed index.  In that case, on return we may have
	 * already found the device.  Check to be sure.
	 */
	unit = (device->config-device->on_bus->config_array) / device->nspaces;
	index = (unit * nbusses + busnum)/(NBBY * sizeof (long));
	mask = 1 << ((unit * nbusses + busnum) % (NBBY* sizeof (long)));

#ifdef DEBUG
	if (boot_debug)
		printf( "Probe successful, unit = %d\n", unit);
#endif /* DEBUG */

	if ( probe_array[index] & mask )
		return 1;		  /* old news, don't announce. */

	probe_array[index] |= mask;
	driver->nalive++;

	/* add this device to the bus it was found on */
	device->next = device->bus->devices;
	device->bus->devices = device;
	device->bus->devcount++;

	/*
	 * Tell the World what we found! Each record printed must be usable as
	 * input to the config utility
	 */
	CPRINTF("%s %d found on %s ",
	       driver->name, DEVICEN(device),
	       bus_type[busses[busnum].bustype]);
	if ( busses[busnum].bustype != rootbus ) {
		/* Except for the system bus, tell which bus of its type */
		CPRINTF("%d unit %d  #  on %s%d",
		       busnum_by_type[busnum], unit,
		       busses[busnum].owner->driver->name,
		       DEVICEN(busses[busnum].owner));
	} else
		CPRINTF(" unit %d  # slic %d", unit, device->config->dev_addr);
	
	if ( device->n_maps > 0 )
		CPRINTF( " using %d map%s\n", device->n_maps,
		       (device->n_maps == 1) ? "" : "s" );
	else   CPRINTF( "\n" );

	/* announce busses that go with this board, too, indented 8 spaces. */

	for (i = 0; busses[i].bustype; i++)                 /* for each bus */
		if (busses[i].owner == device) {            /* if we own it */
			CPRINTF("        %s %d on %s %d\n",
			       bus_type[busses[i].bustype], busnum_by_type[i],
			       driver->name, DEVICEN(device));
		}
	return 1;
}
/*
 * conf_console()
 *	Determine Console SCSI card for putchar()'s and "boot" console device.
 */

static
conf_console()
{
	cons_scsi = CD_LOC->c_cons->cd_slic;
	cons_slic = CD_LOC->c_cons->cd_slic;
}

/*
 * conf_clkarb()
 *	Determine if clock arbiter is present. If present,
 *	set flag for front panel lights. And determine bus priority
 *	for slots 16-20. If all are processors then set priority to low
 *	otherwise set high (default).
 */

#ifdef	ns32000
/*
 * Data-mover only used in 032 systems.
 */
unchar	cadm_hrev_data_mover = 1;
extern	bool_t	use_data_mover;
#endif	/* ns32000  */

static
conf_clkarb()
{
	register struct ctlr_toc *toc = &CD_LOC->c_toc[SLB_CLKARBBOARD];
	register struct ctlr_desc *cd;
	extern	int	light_show;
	extern	lock_t	diskoff_lock;

	init_lock(&diskoff_lock);	/* mutex # offline disks cnt */

	if (toc->ct_count == 0) {
#ifdef	ns32000
		use_data_mover = 0;		/* not gots one! */
#endif	/* ns32000  */
		return;
	}

	/*
	 * We're a B21k ==> set up to use front-panel LED's and get system ID
	 * from clock-arbiter board.
	 */

	cd = &CD_LOC->c_ctlrs[toc->ct_start];		/* clkarb ctlr_desc */

	fp_lights = 1;			/* use front panel LEDs */
	if (light_show > 1)
		fp_lights = -1;		/* use front-panel and processor LEDs */
	FP_IO_ONLINE;			/* ASSUME all drives online */

	sec0eaddr = cd->cd_ca_sysid & 0x00FFFFFF;

#ifdef	ns32000
	/*
	 * If CADM isn't late enough rev, don't use the data-mover.
	 * Also don't use data-mover if it failed any diagnostic.
	 */

	if (cd->cd_hrev < cadm_hrev_data_mover ||
	    (cd->cd_diag_flag & CFG_C_BAD_DM))
		use_data_mover = 0;

	/*
	 * If running on old SCED firmware, firmware didn't adjust
	 * b21k bus priority if all processors in high priority slots.
	 * If anything in a high priority bus slot needs the high priority,
	 * leave settings alone, else set lower priority.
	 *
	 * This is only necessary for 032 systems, since SGS systems run
	 * on new SCED FW.
	 */

	for (cd = CD_LOC->c_ctlrs; cd < CD_LOC->c_end_ctlrs; cd++) {
		if ((cd->cd_flags & CTF_HIPRI)
		&&  cd->cd_slic >= CA_FOL && cd->cd_slic <= CA_LOL)
			return;
	}
	printf("Setting optional priority slots to LOW priority\n");
	wrslave(CLKARB_SLIC, SL_C_DIAG_CTRL, SLB_OPT_PRI | SLB_EN_BE);
#endif	/* ns32000  */
}

/*
 * conf_proc()
 *	Configure processors.
 *
 * Allocate engine table for all possible processors, but only remember
 * alive and configured processors.
 *
 * We only fill out the slic addresses in the engine structures;
 * sysinit() fills out the rest.
 *
 * We also set `Nengine' here to the # of desired processors.
 */

#ifdef	ns32000
int	min_proc_rev = 0x7fffffff;	/* minimum processor board HW rev */
#endif	/* ns32000  */

#if	defined(MODELD) && defined(i386)
/*
 * This is a general hint to the SSM handler whether there are
 * SGS2 processor boards present (i.e. if the interrupting version of the
 * SSM firmware has been installed).
 */
int	nsgs2 = 0;
#endif	/* defined(MODELD) && defined(i386) */

static
conf_proc()
{
	extern	int	cpurate;		/* mips rate for this cpu */
	extern	int	lcpuspeed;		/* mips rate the chip set */
	register struct ctlr_toc *toc;
	register struct	ctlr_desc *cd;
	register struct engine *eng;
	register int i;
#ifdef	MODELD
	int flags = 0;
#endif	/* MODELD */

	/*
	 * Get table of contents pointer for processor board.
	 */

#ifdef	ns32000
	toc = &CD_LOC->c_toc[SLB_PROCBOARD];		/* 032 processors */
#endif	/* ns32000  */

#ifdef	i386
#ifdef MODELD
	/*
	 * Determine the number of processors on the system.
	 * For the i386 family, there can be two types:  SGS (modelB, modelC)
	 * and SGS2 (modelD, modelE).
	 */
	i = CD_LOC->c_toc[SLB_SGS2PROCBOARD].ct_count;	/* SGS2 processors */
	nsgs2 = i;

	toc = &CD_LOC->c_toc[SLB_SGSPROCBOARD];		/* SGS processors */
	i += toc->ct_count;
#else	/* !MODELD */
	toc = &CD_LOC->c_toc[SLB_SGSPROCBOARD];		/* SGS processors */
#endif	/* MODELD */
#endif	/* i386  */

#ifdef	MODELD
	engine = (struct engine *)calloc((int)i * sizeof(struct engine));

	CPRINTF("# %d processors; slic", i);
again:
#else	/* !MODELD */
	engine = (struct engine *)calloc((int)toc->ct_count
					 * sizeof(struct engine));

	CPRINTF("# %d processors; slic", toc->ct_count);
#endif	/* MODELD */

	cd = &CD_LOC->c_ctlrs[toc->ct_start];
	for (i = 0; i < toc->ct_count; i++, cd++) {
		CPRINTF(" %d", cd->cd_slic);
		if (cd->cd_diag_flag & (CFG_FAIL|CFG_DECONF))
			continue;
		eng = &engine[Nengine++];
		eng->e_diag_flag = cd->cd_diag_flag;
		eng->e_slicaddr = cd->cd_slic;
		/*
		 * Set the engine rate and find the slowest proccesor
		 * if not set assume minimum.
		 * Note: cpurate is recalulated to be in MIPS at the
		 * end of this routine.
		 */
#if defined(MODELD)
		eng->e_cpu_speed = cd->cd_p_speed;
		eng->e_flags = flags;
#else	/* !MODELD */
		if (eng->e_cpu_speed == 0 )
			eng->e_cpu_speed = cpurate;
#endif	/* MODELD */
		if (eng->e_cpu_speed < cpurate)
			cpurate = eng->e_cpu_speed;
		ASSERT(eng->e_cpu_speed <= MHz,
		       "Kernel not configured for fast cpu");
		/*
		 *+ One of the CPUs in the system is faster than the kernel is
		 *+ configured to support.  Corrective action:  reconfigure 
		 *+ the kernel for a faster CPU. 
		 */
#ifdef	E_FPU387
		/*
		 * Set E_FPU387 and E_FPA in e_flags as appropriate.
		 * Bump NFPA for those available processors that have FPA's.
		 * Only set E_FPA if there is an FPA and it passed diagnostics.
		 */
		if (cd->cd_p_fp & SLP_387)
			eng->e_flags |= E_FPU387;
		if (cd->cd_p_fp & SLP_FPA) {
			if ((cd->cd_diag_flag & CFG_SP_FPA) == 0) {
				eng->e_flags |= E_FPA;
				++NFPA;
			}
		}
#endif	/* E_FPU387  */
#ifdef	ns32000
		/*
		 * Keep track of minimum processor board rev, so certain
		 * 032 chip-bug work-arounds can be backed out if processor
		 * boards have late enough revision chips.
		 */
		if (cd->cd_hrev < min_proc_rev)
			min_proc_rev = cd->cd_hrev;
#endif	/* ns32000  */
	}
#ifdef	MODELD
	if (flags == 0) {
		/*
		 * Go get all the SGS2 processors.
		 */
		flags = E_SGS2|E_FPU387;
		toc = &CD_LOC->c_toc[SLB_SGS2PROCBOARD];
		goto again;
	}
#endif	/* MODELD */
	engine_Nengine = &engine[Nengine];
	CPRINTF(".\n");

	if (Nengine < toc->ct_count) {
		CPRINTF("#        Not using processors: slic");
		cd = &CD_LOC->c_ctlrs[toc->ct_start];
		for (i = 0; i < toc->ct_count; i++, cd++) {
			if (cd->cd_diag_flag & (CFG_FAIL|CFG_DECONF))
				CPRINTF(" %d", cd->cd_slic);
		}
		CPRINTF(".\n");
	}
	/*
	 * compute cpurate for delay loops. The vaule should 
	 * correspond to the "mips" rateing for the processor 
	 * at its running rate. 
	 * cd_p_speed: is an actual boards running rate
	 * e_cpu_speed: is the number of mips if 
	 * sys_clock_rate = 100
	 * cpurate is only used for probe routines running on the
	 * "boot" processor.
	 * Note this gets replaced in selfinit() with l.cpu_speed.
	 */
#ifdef	MODELD
	/*
	 * Cpurate is used only as a default value until the per
	 * processor rates are computed at selfinit time.
	 */
#endif	/* MODELD */
	cpurate = (lcpuspeed * cpurate) / 100;
}

/*
 * conf_intr()
 *	Allocate and initialize interrupt vector table.
 * NOTE:  We now allocate the full, maximum table size always....
 * Sysinit() already set up master HW vector table.
 * Don't allocate anything for bin[0] (SW -- doesn't use this table).
 */

static
conf_intr()
{
	register int i;
	register int vec;
	extern	void todclock();		/* tod clock handler */

	/*
	 * Allocate int_bin_table, init all entries to point at strayint().
	 */

	for (i = 1; i < SLICBINS; i++) {
		int_bin_table[i].bh_size = MSGSPERBIN;
		int_bin_table[i].bh_hdlrtab
			= (void(**)())calloc(MSGSPERBIN*sizeof(void (*)()));
		for (vec = 0; vec < int_bin_table[i].bh_size; vec++)
			ivecinit(i, vec, strayint);
	}

	/*
	 * Set up vectors for local clock and tod clock.
	 * Must do LCLKBIN first, to insure it gets vector 0.
	 */

#ifdef	ns32000
	/*
	 * 032 kernel vectors to clkint() (machine/locore.s) which
	 * arranges the stack for a call to hardclock().  This should
	 * eventually be modified to a direct call to hardclock() ala SGS.
	 */
	{
		extern	clkint();
		ivecinit(LCLKBIN, ivecall(LCLKBIN), clkint);
	}
#endif	/* ns32000  */
#ifdef	i386
	/*
	 * i386 kernel vectors directly to hardclock().
	 */
	{
		extern	void hardclock();
		ivecinit(LCLKBIN, ivecall(LCLKBIN), hardclock);
	}
#endif	/* i386  */
	ivecinit(TODCLKBIN, ivecall(TODCLKBIN), todclock);
}

/*
 * conf_pseudo()
 *	Call the boot procedures of pseudo-devices.
 */

static
conf_pseudo()
{
	register struct pseudo_dev *pd;

	for (pd = pseudo_dev; pd->pd_name; pd++) {
		CPRINTF("pseudo-device %s %d\n", pd->pd_name, pd->pd_flags);
		if ( pd->pd_boot )
			(*pd->pd_boot)(pd->pd_flags);
	}
}

/*
 * ivecall()
 *	Allocate a vector from a given bin.
 *
 * Insures sequential values returned per bin.
 */

unchar
ivecall(bin)
	unchar	bin;
{
	if (bin_alloc[bin] >= int_bin_table[bin].bh_size) {
		cmn_err( CE_CONT, "Too many vectors in bin %d.", bin);
#ifdef DEBUG
		cmn_err( CE_CONT, "Already allocated %d, max is %d.",
		       bin_alloc[bin], int_bin_table[bin].bh_size);
#endif /* DEBUG */
		cmn_err(CE_PANIC, "ivecall");
		/*
		 *+ Too many vectors were allocated from the given bin.  Corrective
		 *+ action:  reconfigure the system so that fewer SLIC interrupt
		 *+ vectors are allocated from the referenced bin.
		 */
		/*NOTREACHED*/
	}
	return(bin_alloc[bin]++);
}

/*
 * strayint()
 *	Stray interrupt catcher.
 *
 * Doesn't report bin #; instead reports current value of SLIC local
 * mask which allows inference of interrupting bin.
 */

static void
strayint(vec)
	int	vec;			/* vector number within bin */
{
	cmn_err(CE_WARN,
		"Stray intr, vector %d ipl 0x%x.", vec, va_slic->sl_lmask);
	/*
	 *+ A stray interrupt was caught.  
	 *+ The interrupt occurred at a level not expected by the system.
	 */
}


/*
 * conf_sec()
 * This routine is called from configure.  The sec is the only board that needs
 * to have any initialilzation done after all devices that are on its busses
 * have been found, but before much else has happened in the system.  This
 * routine accomplishes this initialization (program queue initialization) for
 * all secs that are configured and alive in the system.
 *
 * Issue the INIT command to all SEC controller
 * after collecting all the relevant data for each channel.
 */
static void
conf_sec()
{
	int i;
	struct bus *bus;
	register struct sec_desc *desc;
	struct sec_cib *cibs;
	unchar errvec;
	volatile struct sec_init_chan_data data;
	register struct sec_chan_descr *cp;
	register struct sec_dev *dev;
	struct sec_scsi_device *device;
	int j, delay;
	struct driver_module *driver;
	struct sec_device *sec_device;

	/* Inform the SCSI/Ether controller of the queues/cib/etc. */

	for (driver = drivers; driver->numdev; driver++)
		if ( !strcmp(driver->name, "sec") )
			break;
	if ( !driver->nalive )
		return;			  /* No alive sec to init! */

	for ( i=0, sec_device = (struct sec_device *)driver->devices;
	     i < driver->numdev; i++, sec_device++ ) {
		if ( !sec_device->driver )
			continue;	  /* this one's not alive */

		desc = sec_device->desc;
		cibs = sec_device->cibs;
		errvec = sec_device->errvec;

		ASSERT((int) &data < 4*1024*1024,
		       "conf_sec: address of struct \"data\" exceeds 4Mb");
		/*
		 *+ The dma maps for SCED devices have been allocated
		 *+ too high in memory for the SCED to use.
		 */
		ASSERT(desc->sec_powerup != 0,
		       "conf_sec: powerup queues are null.");
		/*
		 *+ Sec powerup queues are set up by the sec.  They should never
		 *+ be zero.
		 */

		data.sic_status = 0;
		data.sic_cib = cibs;
		for (j = 0; j < SDEV_NUM_DEVICES; ++j) {
			data.sic_chans[j].scd_requestq = 0;
			data.sic_chans[j].scd_doneq = 0;
			data.sic_chans[j].scd_bin = 0;
			data.sic_chans[j].scd_vector = 0;
			data.sic_chans[j].scd_destslic = 0;
		}
#ifdef DEBUG
		if (boot_debug)
			CPRINTF("conf_sec: cibs @ 0x%x, data @ 0x%x\n",
				data.sic_cib, &data);
#endif	/* DEBUG  */
		/*
		 * Here we find the beginning of the chain of devices coming
		 * off this SEC's SCSI bus and graft it on to the end of the
		 * sec_device_chain (which is the chain of integral devices
		 * which probed alive).
		 */
		device = sec_device->sec_device_chain;

		for ( bus = busses; bus->bustype; bus++ )
			if ((struct sec_device *)bus->owner == sec_device)
				break;

		if ( bus->bustype ) {
			if (sec_device->sec_device_chain) {
				for ( device = sec_device->sec_device_chain;
				     device->next;
				     device = (struct sec_scsi_device *)
				     device->next)
					;
				device->next = bus->devices;
				device = sec_device->sec_device_chain;
			} else	device = (struct sec_scsi_device *)
				bus->devices;
		}
		
		/* Now, scan down this SEC's device list and init channels */
		for (; device; device=(struct sec_scsi_device *)device->next) {
			dev = device->sec_dev;
			if ( !dev->sd_alive )
				continue;
			
			cp = &data.sic_chans[dev->sd_chan];
			ASSERT(dev->sd_cib != 0,
			       "conf_sec: channel cib null");
			/*
			 *+ The channel cib should have been assigned at device
			 *+ init time.
			 */
			ASSERT(dev->sd_requestq != 0,
			       "conf_sec: channel requestq null");
			/*
			 *+ The channel request queue should have been assigned
			 *+ at device init time.
			 */
			ASSERT(dev->sd_doneq != 0,
			       "conf_sec: channel doneq null");
			/*
			 *+ The channel done queue should have been assigned at
			 *+ device init time.
			 */
			ASSERT(dev->sd_bin != 0,
			       "conf_sec: channel bin 0");
			/*
			 *+ The interrupt bin for the channel can't be zero and
			 *+ should have been set to something else during
			 *+ driver init. 
			 */
			cp->scd_requestq = dev->sd_requestq;
			cp->scd_requestq->pq_head
				= cp->scd_requestq->pq_tail
					= dev->sd_req_size;
			cp->scd_doneq = dev->sd_doneq;
			cp->scd_doneq->pq_head
				= cp->scd_doneq->pq_tail
					= dev->sd_doneq_size;
			cp->scd_bin = dev->sd_bin;
			cp->scd_vector = dev->sd_vector;
			cp->scd_destslic = dev->sd_destslic;
#ifdef DEBUG
			if (boot_debug) {
				CPRINTF("conf_sec: add 0x%x: dev %s%d unit %d",
					cp, device->driver->name,
					DEVICEN(device),
					dev->sd_unit);
				CPRINTF( " chan %d cib 0x%x rq 0x%x dq 0x%x ",
					dev->sd_chan,
					&data.sic_cib[dev->sd_chan],
					cp->scd_requestq,
					cp->scd_doneq);
				CPRINTF( "bin %d vector 0x%x\n", cp->scd_bin,
					cp->scd_vector);
			}
#endif	/* DEBUG  */
		}

		/*
		 * Initialize TOD interrupts from console SCED.
		 */
		if (desc->sec_is_cons) {
			data.sic_chans[SDEV_TOD].scd_bin = TODCLKBIN;
			data.sic_chans[SDEV_TOD].scd_vector = TODCLKVEC;
			data.sic_chans[SDEV_TOD].scd_destslic
				= SL_GROUP|TMPOS_GROUP;
		}
		
		/*
		 * Initialize this SEC's error bin, vector. Also, tell SEC
		 * where to place the copy of its access error register for
		 * the kernel.
		 */
		data.sic_chans[SDEV_SCSIBOARD].scd_bin = SEC_ERROR_BIN;
		data.sic_chans[SDEV_SCSIBOARD].scd_vector = errvec;
		data.sic_chans[SDEV_SCSIBOARD].scd_destslic
			= SL_GROUP|TMPOS_GROUP;
		data.sic_chans[SDEV_SCSIBOARD].scd_doneq
			= (struct sec_progq *)sec_device->accerr;

		/*
		 * Initialize the channels.
		 */
		desc->sec_powerup->pu_cib.cib_inst = SINST_INIT;
		desc->sec_powerup->pu_cib.cib_status = (int *)&data;
#ifdef DEBUG
		if (boot_debug) {
			CPRINTF("SINST_INIT: cib 0x%x, cib_status 0x%x\n",
				&desc->sec_powerup->pu_cib,
				desc->sec_powerup->pu_cib.cib_status);
			CPRINTF( "chans:\n");
			for (j = 0; j < SDEV_NUM_DEVICES; ++j) {
				if (data.sic_chans[j].scd_requestq
				    == (struct sec_progq *)0)
					continue;
				cp = &data.sic_chans[j];
				CPRINTF("   %d (0x%x): reqq 0x%x doneq 0x%x ",
					j, cp, cp->scd_requestq,
					cp->scd_doneq);
				CPRINTF("bin %d vector %d\n",
					cp->scd_bin, cp->scd_vector);
			}
			CPRINTF("mIntr(%d, 7, 0): ", desc->sec_slicaddr);
		}
#endif	/* DEBUG  */
		mIntr(desc->sec_slicaddr, 7, 0);
		delay = calc_delay(SEC_DELAY_TIME);
		while ((data.sic_status & SINST_INSDONE) == 0) {
			if (delay-- <= 0) {
				CPRINTF("sec%d: no response after %d peeks\n",
					i, calc_delay(SEC_DELAY_TIME));
				break;
			}
		}
		
		data.sic_status &= ~SINST_INSDONE;
#ifdef DEBUG
		if (boot_debug) {
			CPRINTF("init_chan: status %d\n", data.sic_status);
			if (data.sic_status != 0) {
				CPRINTF("can't init SEC %d: status %d\n",  j,
					data.sic_status); 
			}
			CPRINTF("conf_sec: done\n");
		}
#endif	/* DEBUG  */
		/*
		 * These are no longer valid.
		 */
		desc->sec_powerup = (struct sec_powerup *)0;
		if (sec_device->desc->sec_is_cons)
			*sec_device->conscsi_yet = 1;
	}
}
