3567 lines
84 KiB
C
3567 lines
84 KiB
C
#ident "@(#)autoconf.c 1.1 94/10/31 SMI"
|
|
|
|
/*
|
|
* Copyright (c) 1987-1991 by Sun Microsystems, Inc.
|
|
*/
|
|
|
|
/*
|
|
* Setup the system to run on the current machine.
|
|
*
|
|
* Configure() is called at boot time and initializes the Mainbus
|
|
* device tables and the memory controller monitoring. Available
|
|
* devices are determined by PROM and passed into configure(), where
|
|
* the drivers are initialized.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/user.h>
|
|
#include <sys/vfs.h>
|
|
#include <sys/vfs_stat.h>
|
|
#include <sys/vnode.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/map.h>
|
|
#include <sys/buf.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/dk.h>
|
|
#include <sys/vm.h>
|
|
#include <sys/file.h>
|
|
#include <sys/termios.h>
|
|
#include <sys/termio.h>
|
|
#include <sys/ttold.h>
|
|
#include <sys/stropts.h>
|
|
#include <sys/stream.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/uio.h>
|
|
#include <sys/mman.h>
|
|
#undef NFS
|
|
#include <sys/mount.h>
|
|
#include <sys/bootconf.h>
|
|
#include <specfs/snode.h>
|
|
#include <net/if.h>
|
|
#include <net/if_arp.h>
|
|
#include <netinet/in.h>
|
|
#include <netinet/if_ether.h>
|
|
#include <vm/seg.h>
|
|
|
|
#include <machine/pte.h>
|
|
#include <machine/mmu.h>
|
|
#include <machine/cpu.h>
|
|
#include <machine/scb.h>
|
|
#include <machine/trap.h>
|
|
#include <machine/psl.h>
|
|
#include <machine/seg_kmem.h>
|
|
#include <sun/autoconf.h>
|
|
#include <sun/consdev.h>
|
|
#include <machine/devr.h>
|
|
#include <sundev/mbvar.h>
|
|
#include <sundev/kbio.h>
|
|
#include <sundev/zsvar.h>
|
|
#include <mon/idprom.h>
|
|
#include <sys/debug.h>
|
|
#include <sys/reboot.h>
|
|
#include <machine/module.h>
|
|
|
|
#if defined(SUN4M_35)
|
|
extern int no_vme;
|
|
#endif
|
|
|
|
extern int swap_present;
|
|
/*
|
|
* The following several variables are related to
|
|
* the configuration process, and are used in initializing
|
|
* the machine.
|
|
*/
|
|
|
|
int dkn; /* number of iostat dk numbers assigned so far */
|
|
/*
|
|
* array of "name"/unit pairs that map to dk numbers
|
|
*/
|
|
struct dk_ivec dk_ivec[DK_NDRIVE];
|
|
|
|
/*
|
|
* pointer to DVMA resource map
|
|
*/
|
|
|
|
#ifdef IOMMU
|
|
struct map *vme32map;
|
|
struct map *vme24map;
|
|
struct map *altsbusmap;
|
|
struct map *sbusmap;
|
|
struct map *bigsbusmap;
|
|
struct map *mbutlmap;
|
|
|
|
/* this is here to keep non-OBP driver happay */
|
|
struct mb_hd mb_hd;
|
|
#endif
|
|
struct map *dvmamap;
|
|
|
|
struct dev_opslist *dev_opslist = 0;
|
|
extern struct dev_opslist av_opstab[];
|
|
struct dev_info *top_devinfo;
|
|
struct dev_info *options_devinfo;
|
|
|
|
#ifdef VME
|
|
struct dev_info *vme_devinfo; /* To store vme dev_info pointer */
|
|
int found_vme = 0;
|
|
struct vme_spaces {
|
|
char *name; /* */
|
|
unsigned cookie;
|
|
unsigned space;
|
|
unsigned base;
|
|
} vme_spaces[] = {
|
|
{ "vme16d16", SP_VME16D16, 0xC, 0xFFFF0000 },
|
|
{ "vme24d16", SP_VME24D16, 0xC, 0xFF000000 },
|
|
{ "vme32d16", SP_VME32D16, 0xC, 0x00000000 },
|
|
{ "vme16d32", SP_VME16D32, 0xD, 0xFFFF0000 },
|
|
{ "vme24d32", SP_VME24D32, 0xD, 0xFF000000 },
|
|
{ "vme32d32", SP_VME32D32, 0xD, 0x00000000 },
|
|
{ NULL },
|
|
};
|
|
void add_ipi_drive();
|
|
#endif VME
|
|
|
|
int svimap[] = { 0, 0, 1, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 0 };
|
|
#define LPL2SPL(n) ((n)&15)
|
|
#define LPL2SVL(n) svimap[LPL2SPL(n)]
|
|
|
|
|
|
static char *out_of_range =
|
|
"Warning: Out of range %s specification in device node <%s>\n";
|
|
|
|
|
|
/*
|
|
* Static function to determine if a reg prop is enclosed within
|
|
* a given a range spec.
|
|
*/
|
|
static int
|
|
reg_is_enclosed_in_range(rp, rangep)
|
|
struct dev_reg *rp;
|
|
struct dev_rng *rangep;
|
|
{
|
|
if (rp->reg_bustype != rangep->rng_cbustype)
|
|
return (0);
|
|
|
|
if ((int)rp->reg_addr < rangep->rng_coffset)
|
|
return (0);
|
|
|
|
if (rangep->rng_size == 0)
|
|
return (1); /* size is really 2**(bits_per_word) */
|
|
|
|
if (((int)rp->reg_addr + rp->reg_size - 1) <=
|
|
(rangep->rng_offset + rangep->rng_size - 1))
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
|
|
void
|
|
apply_range_to_reg(name, nrange, rangep, nreg, regp)
|
|
char *name;
|
|
int nrange;
|
|
struct dev_rng *rangep;
|
|
int nreg;
|
|
struct dev_reg *regp;
|
|
{
|
|
register int i;
|
|
register struct dev_rng *rp;
|
|
|
|
if (nrange>0)
|
|
while (nreg-->0) {
|
|
for (i = 0, rp = rangep; i < nrange; ++i, ++rp) {
|
|
if (reg_is_enclosed_in_range(regp, rp))
|
|
break;
|
|
}
|
|
if (i == nrange) {
|
|
printf(out_of_range, "register", name);
|
|
continue;
|
|
}
|
|
regp->reg_addr += (u_int)rp->rng_offset;
|
|
regp->reg_bustype = (int)rp->rng_bustype;
|
|
++regp;
|
|
}
|
|
}
|
|
|
|
void
|
|
apply_range_to_range(name, nr, pp, nc, cp)
|
|
char *name;
|
|
int nr; /* Number of Parent ranges */
|
|
struct dev_rng *pp; /* Parent ranges */
|
|
int nc; /* Number of child ranges */
|
|
struct dev_rng *cp; /* Child ranges */
|
|
{
|
|
register int i;
|
|
register struct dev_rng *rp;
|
|
|
|
if (nr>0)
|
|
while (nc-->0) {
|
|
for (i = 0, rp = pp; i < nr; ++i, ++rp)
|
|
if (cp->rng_bustype == rp->rng_cbustype)
|
|
break;
|
|
if (i == nr) {
|
|
printf(out_of_range, "range", name);
|
|
continue;
|
|
}
|
|
cp->rng_offset += rp->rng_offset;
|
|
cp->rng_bustype = rp->rng_bustype;
|
|
++cp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine fills in the common information for a given dev_info struct.
|
|
* Using the nodeid that is already initialized, it pulls the following
|
|
* information out of the PROMS:
|
|
* name, reg, range, intr
|
|
* If any of these properties are not found, the value of that field is
|
|
* left at its initial value.
|
|
*
|
|
* nreg && nintr are inferred from the property length of reg && intr resp.
|
|
*/
|
|
|
|
void
|
|
fill_devinfo(dev_info)
|
|
struct dev_info *dev_info;
|
|
{
|
|
int nodeid;
|
|
static int curzs = 0;
|
|
|
|
nodeid = dev_info->devi_nodeid;
|
|
dev_info->devi_name = (char *)getlongprop(nodeid, "name");
|
|
|
|
#ifdef SUN4M_690
|
|
/*
|
|
* XXX: Since we're using the generic attach_devs function for
|
|
* XXX: children of the "SUNW,pn" node, and it does not follow the
|
|
* XXX: "obio" model, we need to escape at this point...
|
|
* XXX: (The reg property in the "ipi3sc" node does not describe
|
|
* XXX: byte addressable space in it's parent's space.)
|
|
*/
|
|
|
|
if (strcmp(dev_info->devi_name, "ipi3sc") == 0)
|
|
return;
|
|
#endif SUN4M_690
|
|
|
|
/*
|
|
* We try to match stdin/stdin (which is the "second" zs,
|
|
* if console/kbd) while attaching the first zs device.
|
|
* We need the unit number initialized.
|
|
*/
|
|
if (strcmp("zs", dev_info->devi_name) == 0)
|
|
dev_info->devi_unit = curzs++;
|
|
|
|
dev_info->devi_nintr = getproplen(nodeid, "intr") /
|
|
sizeof (struct dev_intr);
|
|
if (dev_info->devi_nintr > 0)
|
|
dev_info->devi_intr =
|
|
(struct dev_intr *)getlongprop(nodeid, "intr");
|
|
|
|
dev_info->devi_nreg = getproplen(nodeid, "reg") /
|
|
sizeof (struct dev_reg);
|
|
if (dev_info->devi_nreg > 0) {
|
|
dev_info->devi_reg =
|
|
(struct dev_reg *)getlongprop(nodeid, "reg");
|
|
/*
|
|
* The "reg" property is relative to the
|
|
* range specified by the node's parent.
|
|
* Convert the relative data from the prom into
|
|
* absolute data by combining with the absolute
|
|
* range information stored in the parent.
|
|
*/
|
|
if ((dev_info->devi_parent) &&
|
|
(dev_info->devi_parent->devi_nrng > 0) &&
|
|
(dev_info->devi_parent->devi_rng))
|
|
apply_range_to_reg(dev_info->devi_name,
|
|
dev_info->devi_parent->devi_nrng,
|
|
dev_info->devi_parent->devi_rng,
|
|
dev_info->devi_nreg,
|
|
dev_info->devi_reg);
|
|
}
|
|
dev_info->devi_nrng = getproplen(nodeid, "ranges") /
|
|
sizeof (struct dev_rng);
|
|
if (dev_info->devi_nrng > 0) {
|
|
dev_info->devi_rng =
|
|
(struct dev_rng *)getlongprop(nodeid, "ranges");
|
|
|
|
/*
|
|
* The "ranges" property is relative to the
|
|
* range specified by the node's parent.
|
|
* Convert the relative data from the prom into
|
|
* absolute data by combining with the absolute
|
|
* range information stored in the parent.
|
|
*/
|
|
if ((dev_info->devi_parent) &&
|
|
(dev_info->devi_parent->devi_nrng > 0) &&
|
|
(dev_info->devi_parent->devi_rng))
|
|
apply_range_to_range(dev_info->devi_name,
|
|
dev_info->devi_parent->devi_nrng,
|
|
dev_info->devi_parent->devi_rng,
|
|
dev_info->devi_nrng,
|
|
dev_info->devi_rng);
|
|
} else if (dev_info->devi_parent) {
|
|
/* inherit range from parent */
|
|
dev_info->devi_nrng = dev_info->devi_parent->devi_nrng;
|
|
dev_info->devi_rng = dev_info->devi_parent->devi_rng;
|
|
} else {
|
|
/* root node. default is "nothing". */
|
|
dev_info->devi_nrng = 0;
|
|
dev_info->devi_rng = (struct dev_rng *)0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This routine attempts to match a device name to a device driver. It
|
|
* walks the list of driver ops_vectors, calling the identify routines
|
|
* until one of the drivers claims the device.
|
|
*/
|
|
struct dev_ops *
|
|
match_driver(name)
|
|
char *name;
|
|
{
|
|
struct dev_opslist *ops;
|
|
|
|
for (ops = dev_opslist; ops->devl_ops != NULL; ops = ops->devl_next)
|
|
if (ops->devl_ops->devo_identify(name))
|
|
return (ops->devl_ops);
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* This routine checks to see if the given nodeid is already listed as a
|
|
* child of the given device. It is used to prevent duplicate dev_info
|
|
* structs from being created.
|
|
*/
|
|
static int
|
|
check_dupdev(dev_info, nodeid)
|
|
struct dev_info *dev_info;
|
|
int nodeid;
|
|
{
|
|
struct dev_info *dev;
|
|
|
|
for (dev = dev_info->devi_slaves; dev != NULL; dev = dev->devi_next) {
|
|
if (dev->devi_nodeid == nodeid)
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
#ifdef VME
|
|
/* Check if device already a child of vme */
|
|
/*ARGSUSED*/
|
|
static int
|
|
vme_check_dupdev(dev_info, name, unit)
|
|
struct dev_info *dev_info;
|
|
char *name;
|
|
int unit;
|
|
{
|
|
struct dev_info *dev;
|
|
|
|
#ifdef DEBUG_IPI
|
|
printf("vme_check_dupdev: looking for dup of %s, unit#0x%X\n",
|
|
name, unit);
|
|
#endif
|
|
|
|
/*
|
|
* %%% HACK for building the ipi related dev_info
|
|
* nodes. We are throwing out everything we dont need.
|
|
* The IPI related devinfo nodes are created via the pseudo
|
|
* (sbus style) drivers included in this file.
|
|
*/
|
|
if ((strcmp(name, "isc") == 0) ||
|
|
(strcmp(name, "is") == 0) ||
|
|
(strcmp(name, "idc") == 0) ||
|
|
(strcmp(name, "id") == 0)) {
|
|
#ifdef DEBUG_IPI
|
|
printf("vme_check_dupdev: THROWING OUT %s, unit#0x%X\n",
|
|
name, unit);
|
|
#endif
|
|
return (1);
|
|
}
|
|
|
|
for (dev = dev_info->devi_slaves; dev != NULL; dev = dev->devi_next) {
|
|
if (strcmp(dev->devi_name, name) == 0) {
|
|
if (dev->devi_unit == unit) {
|
|
#ifdef DEBUG_IPI
|
|
printf("vme_check_dupdev: FOUND dup of %s, unit#0x%X\n",
|
|
name, unit);
|
|
#endif
|
|
return (1); /* found a dup */
|
|
}
|
|
}
|
|
}
|
|
#ifdef DEBUG_IPI
|
|
printf("vme_check_dupdev: found NO dup of %s, unit#0x%X\n",
|
|
name, unit);
|
|
#endif
|
|
return (0);
|
|
}
|
|
#endif VME
|
|
|
|
|
|
/*
|
|
* This routine appends the second argument onto the slave list for the
|
|
* first argument. It always appends it to the end of the list.
|
|
*/
|
|
void
|
|
append_dev(dev_parent, dev_slave)
|
|
struct dev_info *dev_parent, *dev_slave;
|
|
{
|
|
register struct dev_info *dev;
|
|
|
|
if (dev_parent->devi_slaves == NULL)
|
|
dev_parent->devi_slaves = dev_slave;
|
|
else {
|
|
for (dev = dev_parent->devi_slaves; dev->devi_next != NULL;
|
|
dev = dev->devi_next)
|
|
;
|
|
dev->devi_next = dev_slave;
|
|
}
|
|
}
|
|
|
|
#ifndef VDDRV
|
|
/*
|
|
* This routine removes the second argument from the slave list for the
|
|
* first argument. It returns -1 if the slave isn't found on the list.
|
|
*/
|
|
static int
|
|
remove_dev(dev_parent, dev_slave)
|
|
struct dev_info *dev_parent, *dev_slave;
|
|
{
|
|
register struct dev_info *dev;
|
|
|
|
if (dev_parent->devi_slaves == dev_slave) {
|
|
dev_parent->devi_slaves = dev_slave->devi_next;
|
|
return (0);
|
|
} else
|
|
for (dev = dev_parent->devi_slaves; dev != NULL;
|
|
dev = dev->devi_next) {
|
|
if (dev->devi_next == dev_slave) {
|
|
dev->devi_next = dev_slave->devi_next;
|
|
return (0);
|
|
}
|
|
}
|
|
return (-1);
|
|
}
|
|
#endif VDDRV
|
|
|
|
/*
|
|
* Create a list of ops vectors out of the array config produced.
|
|
*/
|
|
static void
|
|
init_opslist()
|
|
{
|
|
register struct dev_opslist *dp;
|
|
register int i = 0;
|
|
|
|
while ((dp = &av_opstab[i])->devl_ops != NULL)
|
|
dp->devl_next = &av_opstab[++i];
|
|
dev_opslist = av_opstab;
|
|
}
|
|
|
|
#define DEFAULT_SPLVM (ipltospl(9))
|
|
/*
|
|
* Maximum interrupt priority used by Mainbus DMA.
|
|
* This value is determined as each driver that uses DMA calls adddma
|
|
* with the hardware interrupt priority level that they use. Adddma
|
|
* increases splvm_val as necessary, and computes SPLMB based on it.
|
|
*
|
|
* XXX SPLMB is for backward compatibility only.
|
|
* XXX Drivers should call splvm() instead of splr(pritospl(SPLMB)).
|
|
*/
|
|
int SPLMB = spltopri(DEFAULT_SPLVM); /* reasonable default */
|
|
|
|
/*
|
|
* We use splvmval to implement splvm().
|
|
*/
|
|
int splvm_val = DEFAULT_SPLVM; /* reasonable default */
|
|
|
|
extern struct machinfo mach_info;
|
|
/*
|
|
* Machine type we are running on.
|
|
*/
|
|
int cpu;
|
|
/*
|
|
* Determine mass storage and memory configuration for a machine.
|
|
* Get cpu type, and then switch out to machine specific procedures
|
|
* which will determine what is out there.
|
|
*/
|
|
configure()
|
|
{
|
|
extern int fpu_exists; /* set or cleared by fpu_probe */
|
|
|
|
idprom();
|
|
fpu_probe();
|
|
|
|
if (!fpu_exists)
|
|
printf("No FPU in configuration\n");
|
|
/*
|
|
* Configure devinfo-style devices.
|
|
*/
|
|
sbus_config();
|
|
|
|
/*
|
|
* Configure VME (mainbus-style) devices.
|
|
*/
|
|
mbconfig();
|
|
|
|
/*
|
|
* Attach pseudo-devices
|
|
*/
|
|
pseudoconfig();
|
|
}
|
|
|
|
/*
|
|
* Initialize devices on the machine.
|
|
* Uses configuration tree built by the PROMs to determine what
|
|
* is present. The attach_devs() routine is used in conjuction with
|
|
* the sbus routines to cause the appropriate initialization
|
|
* to occur. All we do here is start the ball rolling.
|
|
*/
|
|
|
|
|
|
#ifndef NOPROM
|
|
sbus_config()
|
|
{
|
|
|
|
extern dnode_t prom_nextnode();
|
|
|
|
/*
|
|
* chain together the opslist that we're built with
|
|
*/
|
|
init_opslist();
|
|
|
|
/*
|
|
* build the dev_info tree, attaching devices along the way
|
|
*/
|
|
top_devinfo = (struct dev_info *) kmem_zalloc(sizeof (*top_devinfo));
|
|
top_devinfo->devi_nodeid = (int)prom_nextnode((dnode_t)0);
|
|
fill_devinfo(top_devinfo);
|
|
attach_devs(top_devinfo);
|
|
}
|
|
#else NOPROM
|
|
/*
|
|
* SAS doesn't not simulate the open prom, and only has a
|
|
* simulated console and simulated disk. The attach and probe
|
|
* routines of these simulated devices don't do much, so there
|
|
* is probably not much to do here. May have to add more here
|
|
* though, so that data structures get initialized?
|
|
* console:
|
|
* simcprobe: does nothing.
|
|
* simcattach: does a little bit of initialization.
|
|
* simdprobe: doesn't exist.
|
|
* simdattach: does nothing.
|
|
*/
|
|
sbus_config()
|
|
{
|
|
#ifdef SAS
|
|
simcattach();
|
|
/* probably will have to do more for SAS */
|
|
#endif
|
|
}
|
|
#endif NOPROM
|
|
|
|
int
|
|
obio_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "obio"))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
obio_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
attach_devs(devi);
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops obio_ops = {
|
|
1,
|
|
obio_identify,
|
|
obio_attach,
|
|
};
|
|
|
|
int
|
|
iommu_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "iommu"))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
iommu_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
attach_devs(devi);
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops iommu_ops = {
|
|
1,
|
|
iommu_identify,
|
|
iommu_attach,
|
|
};
|
|
|
|
int
|
|
sbus_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "sbus"))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
int
|
|
sbus_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
attach_devs(devi);
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops sbus_ops = {
|
|
1,
|
|
sbus_identify,
|
|
sbus_attach,
|
|
};
|
|
|
|
#ifdef VME
|
|
int
|
|
vme_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "vme"))
|
|
return (0);
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
vme_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
attach_devs(devi);
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops vme_ops = {
|
|
1,
|
|
vme_identify,
|
|
vme_attach,
|
|
};
|
|
#endif VME
|
|
|
|
#ifdef SUN4M_690
|
|
/*
|
|
* Pseudo device drivers for Self-identifying VME/IPI
|
|
*/
|
|
int
|
|
pn_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "SUNW,pn") == 0)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
pn_attach(devi)
|
|
register struct dev_info *devi;
|
|
{
|
|
/*
|
|
* XXX: Stop report_dev from printing all 5 sets of regs and intrs.
|
|
*/
|
|
|
|
register int nregs = devi->devi_nreg;
|
|
register int nintr = devi->devi_nintr;
|
|
|
|
devi->devi_nreg = (nregs > 0 ? 1 : 0);
|
|
devi->devi_nintr = (nintr > 0 ? 1 : 0);
|
|
report_dev(devi);
|
|
devi->devi_nreg = nregs;
|
|
devi->devi_nintr = nintr;
|
|
|
|
attach_devs(devi);
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops pn_ops = {
|
|
1,
|
|
pn_identify,
|
|
pn_attach,
|
|
};
|
|
|
|
int
|
|
idpseudo_identify(name)
|
|
char *name;
|
|
{
|
|
if (strcmp(name, "id") == 0)
|
|
return (1);
|
|
return (0);
|
|
}
|
|
|
|
/*ARGSUSED*/
|
|
int
|
|
idpseudo_attach(devi)
|
|
register struct dev_info *devi;
|
|
{
|
|
#ifdef IPI_DEBUG
|
|
register struct dev_info *pdp = devi->devi_parent;
|
|
|
|
printf("%s%d at %s%d", devi->devi_name, devi->devi_unit,
|
|
pdp->devi_name, pdp->devi_unit);
|
|
#endif IPI_DEBUG
|
|
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops idpseudo_ops = {
|
|
1,
|
|
idpseudo_identify,
|
|
idpseudo_attach,
|
|
};
|
|
|
|
int
|
|
ipi3sc_identify(name)
|
|
char *name;
|
|
{
|
|
#ifdef IPI_DEBUG
|
|
prom_printf("ipi3sc_identify: name %s\n", name);
|
|
#endif IPI_DEBUG
|
|
|
|
if (strcmp(name, "ipi3sc") == 0)
|
|
return (1);
|
|
#ifndef NO_IDC_COMPATIBILITY
|
|
/*
|
|
* XXX: For compatability with pre-FCS PROMS, also identify "idc".
|
|
*/
|
|
|
|
if (strcmp(name, "idc") == 0)
|
|
return (1);
|
|
#endif !NO_IDC_COMPATIBILITY
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* XXX: Fill in the unit number, properly, and create "id" children.
|
|
* XXX: This is a terrible hack, that augments the mbconfig stuff,
|
|
* XXX: basically, so we can ignore it, later. Assumption is 8 drives
|
|
* XXX: per slave controller.
|
|
*/
|
|
|
|
int
|
|
ipi3sc_attach(devi)
|
|
struct dev_info *devi;
|
|
{
|
|
register struct dev_reg *rp;
|
|
register struct dev_info *dp;
|
|
int n, length;
|
|
|
|
#ifdef IPI_DEBUG
|
|
prom_printf("ipi3sc_attach...\n");
|
|
#endif IPI_DEBUG
|
|
|
|
/*
|
|
* For device "ipi3sc", the channel number is expressed in the
|
|
* "bustype" field of the device's reg property (which does not
|
|
* refer to byte addressable space).
|
|
*/
|
|
|
|
length = getproplen(devi->devi_nodeid, "reg");
|
|
if (length == -1)
|
|
length = 0;
|
|
ASSERT(length != 0);
|
|
rp = (struct dev_reg *)getlongprop(devi->devi_nodeid, "reg");
|
|
|
|
n = devi->devi_unit = rp->reg_bustype;
|
|
|
|
#ifndef NO_IDC_COMPATIBILITY
|
|
if (strcmp("ipi3sc", devi->devi_name) != 0) { /* For idc */
|
|
/*
|
|
* For device "idc", * the channel number is computed from the
|
|
* address field.
|
|
*/
|
|
n = devi->devi_unit = (int)(((int)rp->reg_addr & 0x1c00) >> 10);
|
|
}
|
|
#endif NO_IDC_COMPATIBILITY
|
|
|
|
(void)kmem_free((caddr_t)rp, (u_int)length);
|
|
dp = devi->devi_parent;
|
|
|
|
#ifndef NO_IDC_COMPATIBILITY
|
|
if (strcmp("ipi3sc", devi->devi_name) == 0)
|
|
#endif NO_IDC_COMPATIBILITY
|
|
printf("%s channel #%d at %s%d\n", devi->devi_name, n,
|
|
dp->devi_name, dp->devi_unit);
|
|
#ifndef NO_IDC_COMPATIBILITY
|
|
else
|
|
report_dev(devi);
|
|
#endif NO_IDC_COMPATIBILITY
|
|
return (0);
|
|
}
|
|
|
|
struct dev_ops ipisc_ops = {
|
|
1,
|
|
ipi3sc_identify,
|
|
ipi3sc_attach,
|
|
};
|
|
#endif SUN4M_690
|
|
|
|
/* For VME/Old style drivers */
|
|
static struct dev_ops pseudo_ops = { 0 };
|
|
|
|
/*
|
|
* This routine identifies and initializes all the slaves of the given
|
|
* device. A dev_info struct is created for each slave, and the common
|
|
* values initialized. It is tested against the device driver
|
|
* identify routines, and a match causes the appropriate dev_ops pointer
|
|
* to be linked to the device. After all the slaves have been identified,
|
|
* the appropriate attach routines are called for each one. Doing it in this
|
|
* order allows a driver to count how many devices it will have before the
|
|
* first attach routine is called. If an attach fails for any reason, the
|
|
* dev_info struct is marked unattached, by setting the dev_ops pointer
|
|
* to NULL.
|
|
* If no identify match is made for a device, it is also marked
|
|
* unattached and an error message is printed.
|
|
* This routine should not be called after the system is running to initialize
|
|
* a device that was added on the fly; add_drv should be used for that
|
|
* purpose.
|
|
*
|
|
* It carefully checks each device the
|
|
* prom returns to avoid creating duplicate dev_info entries,
|
|
* but we may not need to do that anymore since we don't use this to add
|
|
* devices after boot.
|
|
*/
|
|
attach_devs(dev_info)
|
|
struct dev_info *dev_info;
|
|
{
|
|
register struct dev_info *dev;
|
|
struct dev_info *newdevs = NULL;
|
|
register int curnode;
|
|
#ifdef TRACK_RANGES_IN_DEV_INFO
|
|
struct dev_reg *nrp;
|
|
int nrng, mrng, i;
|
|
int npr = 0;
|
|
struct dev_reg *prp = 0; /* parent's range info */
|
|
struct dev_reg *rp = 0;
|
|
#endif
|
|
extern dnode_t prom_nextnode(), prom_childnode();
|
|
|
|
curnode = (int)prom_childnode((dnode_t)dev_info->devi_nodeid);
|
|
if (curnode == 0)
|
|
return;
|
|
for (; curnode != 0; curnode = (int)prom_nextnode((dnode_t)curnode)) {
|
|
/*
|
|
* If it's already on the list, skip it.
|
|
*/
|
|
if (check_dupdev(dev_info, curnode) != 0)
|
|
continue;
|
|
/*
|
|
* Allocate it, fill it in and add it to the list.
|
|
*/
|
|
dev = (struct dev_info *)kmem_zalloc(sizeof (*dev));
|
|
append_dev(dev_info, dev);
|
|
dev->devi_nodeid = curnode;
|
|
dev->devi_parent = dev_info;
|
|
fill_devinfo(dev);
|
|
dev->devi_driver = match_driver(dev->devi_name);
|
|
#ifdef VME
|
|
/* Save away the dev_info pointer for vme */
|
|
if (!found_vme) {
|
|
if (strcmp(dev->devi_name, "vme") == 0) {
|
|
vme_devinfo = dev;
|
|
found_vme = 1;
|
|
}
|
|
}
|
|
#endif VME
|
|
/*
|
|
* Note the first new struct on the list.
|
|
*/
|
|
if (newdevs == NULL)
|
|
newdevs = dev;
|
|
}
|
|
/*
|
|
* Try to attach the new devs. If any of the identifies or attaches
|
|
* fail, throw out the dev_info struct.
|
|
* We track this loop funny because structs can get deallocated in
|
|
* the middle.
|
|
*/
|
|
for (dev = newdevs; dev != NULL; dev = newdevs) {
|
|
newdevs = dev->devi_next;
|
|
if (dev->devi_driver == NULL) {
|
|
#if !defined(VDDRV) && !defined(MUNIX) && !defined(MINIROOT)
|
|
if (remove_dev(dev_info, dev))
|
|
panic("attach_devs remove_dev 1");
|
|
(void) kmem_free((caddr_t) dev, sizeof (*dev));
|
|
#endif VDDRV && MUNIX && MINIROOT
|
|
continue;
|
|
}
|
|
if ((*dev->devi_driver->devo_attach)(dev) < 0) {
|
|
#if defined(VDDRV) || defined(MUNIX) || defined(MINIROOT)
|
|
dev->devi_driver = NULL;
|
|
#else VDDRV || MUNIX || MINIROOT
|
|
if (remove_dev(dev_info, dev))
|
|
panic("attach_devs remove_dev 2");
|
|
(void) kmem_free((caddr_t) dev, sizeof (*dev));
|
|
#endif VDDRV || MUNIX || MINIROOT
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef VDDRV
|
|
struct add_drv_info {
|
|
struct dev_ops *adi_ops;
|
|
int adi_count;
|
|
};
|
|
|
|
struct new_devlist {
|
|
struct new_devlist *nd_next;
|
|
struct dev_info *nd_dev;
|
|
int nd_attachme;
|
|
};
|
|
|
|
struct new_devlist_head {
|
|
struct new_devlist *ndh_head;
|
|
struct new_devlist *ndh_tail;
|
|
};
|
|
|
|
static struct new_devlist_head new_devlist_head;
|
|
|
|
|
|
/*
|
|
* Pick up devices for a just-loaded driver:
|
|
* 1. Initialize the add_drv_info structure.
|
|
* 2. Kick off add_drv_layer on the top layer of devices.
|
|
* 3. Return the number of devices attached.
|
|
*/
|
|
int
|
|
add_drv(dev_ops)
|
|
struct dev_ops *dev_ops;
|
|
{
|
|
struct add_drv_info adi;
|
|
register struct add_drv_info *adip = &adi;
|
|
void add_drv_layer();
|
|
register struct new_devlist *ndp;
|
|
int attached = 0;
|
|
|
|
adi.adi_ops = dev_ops;
|
|
adi.adi_count = 0;
|
|
|
|
new_devlist_head.ndh_head = new_devlist_head.ndh_tail = NULL;
|
|
|
|
/*
|
|
* build up a list of drivers
|
|
*/
|
|
walk_slaves(top_devinfo,
|
|
(int (*)()) add_drv_layer, (caddr_t) &adi);
|
|
|
|
/*
|
|
* identify the drivers in the list
|
|
*/
|
|
for (ndp = new_devlist_head.ndh_head; ndp; ndp = ndp->nd_next)
|
|
ndp->nd_attachme = (*adip->adi_ops->devo_identify)
|
|
(ndp->nd_dev->devi_name);
|
|
|
|
|
|
/*
|
|
* attach the drivers int the list and cleanup
|
|
*/
|
|
for (ndp = new_devlist_head.ndh_head; ndp;
|
|
ndp = new_devlist_head.ndh_head) {
|
|
if (ndp->nd_attachme) {
|
|
ndp->nd_dev->devi_driver = adip->adi_ops;
|
|
if ((*adip->adi_ops->devo_attach)
|
|
(ndp->nd_dev) < 0)
|
|
ndp->nd_dev->devi_driver = NULL;
|
|
else
|
|
attached++;
|
|
}
|
|
new_devlist_head.ndh_head = ndp->nd_next;
|
|
(void) kmem_free((caddr_t) ndp, sizeof (*ndp));
|
|
}
|
|
adip->adi_count += attached;
|
|
|
|
return (adi.adi_count);
|
|
}
|
|
|
|
/*
|
|
* Pick up devices in a given layer for a just-loaded driver:
|
|
* 1. Walk the layer, finding any unattached devices.
|
|
* 2. Call the identify routine for each unattached device, and mark
|
|
* those that we should attempt to attach.
|
|
* 3. Attach those that have been so marked.
|
|
* 4. Free the link of new_devlist structures.
|
|
* 5. Update the count of attached devices.
|
|
* 6. Recurse over any slaves of this layer.
|
|
*/
|
|
static void
|
|
add_drv_layer(dev, adip)
|
|
struct dev_info *dev;
|
|
struct add_drv_info *adip;
|
|
{
|
|
void add_a_device();
|
|
|
|
walk_layer(dev,
|
|
(int (*)()) add_a_device, (caddr_t) &new_devlist_head);
|
|
|
|
/* Now see if any slaves of this layer need identifying */
|
|
walk_slaves(dev, (int (*)()) add_drv_layer, (caddr_t) adip);
|
|
}
|
|
|
|
/*
|
|
* If a dev_info is unattached, add it to the new_devlist being built.
|
|
* Keep the list in FIFO order.
|
|
*/
|
|
static void
|
|
add_a_device(dev, ndhp)
|
|
struct dev_info *dev;
|
|
struct new_devlist_head *ndhp;
|
|
{
|
|
struct new_devlist *ndp;
|
|
|
|
if (dev->devi_driver == NULL) {
|
|
ndp = (struct new_devlist *) kmem_alloc(sizeof (*ndp));
|
|
ndp->nd_dev = dev;
|
|
if (ndhp->ndh_head == NULL)
|
|
ndhp->ndh_head = ndp;
|
|
else /* if head is set, so is tail */
|
|
ndhp->ndh_tail->nd_next = ndp;
|
|
ndp->nd_next = NULL;
|
|
ndhp->ndh_tail = ndp;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Add a driver to the list of ops vectors. Don't allow duplicates.
|
|
*/
|
|
int
|
|
add_ops(ops)
|
|
struct dev_ops *ops;
|
|
{
|
|
register struct dev_opslist *dp;
|
|
|
|
for (dp = dev_opslist; dp->devl_ops != NULL; dp = dp->devl_next) {
|
|
if (dp->devl_ops == ops)
|
|
return (EINVAL);
|
|
}
|
|
|
|
dp = (struct dev_opslist *) kmem_alloc(sizeof (*dp));
|
|
dp->devl_ops = ops;
|
|
dp->devl_next = dev_opslist;
|
|
dev_opslist = dp;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Unattach any devices attached to this driver.
|
|
*/
|
|
void
|
|
rem_drv(dev_ops)
|
|
struct dev_ops *dev_ops;
|
|
{
|
|
void rem_a_device();
|
|
|
|
walk_devs(top_devinfo,
|
|
(int (*)()) rem_a_device, (caddr_t) dev_ops);
|
|
}
|
|
|
|
/*
|
|
* If this dev is linked to this driver, remove it.
|
|
*/
|
|
static void
|
|
rem_a_device(dev, ops)
|
|
struct dev_info *dev;
|
|
struct dev_ops *ops;
|
|
{
|
|
|
|
if (dev->devi_driver == ops)
|
|
dev->devi_driver = NULL;
|
|
}
|
|
|
|
/*
|
|
* Remove a driver from the list of ops vectors.
|
|
*/
|
|
int
|
|
rem_ops(ops)
|
|
struct dev_ops *ops;
|
|
{
|
|
register struct dev_opslist *dp;
|
|
register struct dev_opslist *prev = NULL;
|
|
|
|
for (dp = dev_opslist; dp->devl_ops != NULL;
|
|
prev = dp, dp = dp->devl_next)
|
|
if (dp->devl_ops == ops) {
|
|
if (prev == NULL)
|
|
dev_opslist = dp->devl_next;
|
|
else
|
|
prev->devl_next = dp->devl_next;
|
|
(void) kmem_free((caddr_t) dp, sizeof (*dp));
|
|
return (0);
|
|
}
|
|
|
|
return (EINVAL);
|
|
}
|
|
#endif VDDRV
|
|
|
|
/*
|
|
* This general-purpose routine walks down the tree of devices, calling
|
|
* the given function for each one it finds, and passing the pointer arg
|
|
* which can point to a structure of information that the function
|
|
* needs.
|
|
* It is useful when searches need to be made.
|
|
* It needs to walk all devices, attached or not, as it is used before
|
|
* any devices are attached. The function called should check if the
|
|
* device is attached if it cares about such things.
|
|
* It does the walk a layer at a time, not depth-first.
|
|
*/
|
|
walk_devs(dev_info, f, arg)
|
|
struct dev_info *dev_info;
|
|
int (*f)();
|
|
caddr_t arg;
|
|
{
|
|
register struct dev_info *dev;
|
|
|
|
/*
|
|
* Call the function for all the devices on this layer.
|
|
* Then, for each device that has a slave, call walk_devs on
|
|
* the slave.
|
|
*/
|
|
walk_layer(dev_info, f, arg);
|
|
for (dev = dev_info; dev != (struct dev_info *)NULL;
|
|
dev = dev->devi_next) {
|
|
if (dev->devi_slaves)
|
|
walk_devs(dev->devi_slaves, f, arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This general-purpose routine walks one layer of the dev_info tree,
|
|
* calling the given function for each dev_info it finds, and passing
|
|
* the pointer arg which can point to a structure of information that
|
|
* the function needs.
|
|
* It is useful when searches need to be made.
|
|
* It walks all devices, attached or not. The function called should
|
|
* check if the device is attached if it cares about such things.
|
|
*/
|
|
walk_layer(dev_info, f, arg)
|
|
struct dev_info *dev_info;
|
|
int (*f)();
|
|
caddr_t arg;
|
|
{
|
|
register struct dev_info *dev;
|
|
|
|
/*
|
|
* Call the function for this dev_info.
|
|
* Go to the next device.
|
|
*/
|
|
for (dev = dev_info; dev != (struct dev_info *)NULL;
|
|
dev = dev->devi_next) {
|
|
(*f)(dev, arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This general-purpose routine walks one layer of the dev_info tree,
|
|
* calling the given function for each dev_info that has slaves, passing
|
|
* it the slave layer and the pointer arg which can point to a
|
|
* structure of information that the function needs.
|
|
* It is useful when searches need to be made.
|
|
* It needs to walk all devices, attached or not, as it is used before
|
|
* any devices are attached. The function called should check if the
|
|
* device is attached if it cares about such things.
|
|
*/
|
|
walk_slaves(dev_info, f, arg)
|
|
struct dev_info *dev_info;
|
|
int (*f)();
|
|
caddr_t arg;
|
|
{
|
|
register struct dev_info *dev;
|
|
|
|
/*
|
|
* For each device on this layer that has a slave, call the
|
|
* function on that slave layer.
|
|
*/
|
|
for (dev = dev_info; dev != (struct dev_info *)NULL;
|
|
dev = dev->devi_next) {
|
|
if (dev->devi_slaves)
|
|
(*f)(dev->devi_slaves, arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Given a physical address, a bus type, and a size, return a virtual
|
|
* address that maps the registers.
|
|
* Returns NULL on any error.
|
|
* NOTE: for Sun-4M, the allowable values for "space" are 0..15
|
|
* specifying the upper four bits of the 36-bit physical address,
|
|
* or SPO_VIRTUAL. Other values will result in an error.
|
|
*/
|
|
addr_t
|
|
map_regs(addr, size, space)
|
|
addr_t addr;
|
|
u_int size;
|
|
int space;
|
|
{
|
|
register addr_t reg = NULL;
|
|
u_int pageval;
|
|
int s;
|
|
int offset = (int)addr & PGOFSET;
|
|
long a = 0;
|
|
int extent;
|
|
|
|
if (space == SPO_VIRTUAL)
|
|
return addr;
|
|
else if (space < 16)
|
|
pageval = btop(addr) + (btop(space<<28)<<4);
|
|
else
|
|
return 0;
|
|
|
|
extent = btoc(size + offset);
|
|
if (extent == 0)
|
|
extent = 1;
|
|
s = splhigh();
|
|
a = rmalloc(kernelmap, (long)extent);
|
|
(void)splx(s);
|
|
if (a == 0)
|
|
panic("out of kernelmap for devices");
|
|
reg = (addr_t)((int)kmxtob(a) | offset);
|
|
segkmem_mapin(&kseg, (addr_t)((int)reg&PAGEMASK),
|
|
(u_int)mmu_ptob(extent), PROT_READ | PROT_WRITE,
|
|
pageval, 0);
|
|
return (reg);
|
|
}
|
|
|
|
/*
|
|
* Supposedly, this routine is the only place that enumerates details
|
|
* of the encoding of the "bustype" part of the physical address.
|
|
* This routine depends on details of the physical address map, and
|
|
* will probably be somewhat different for different machines.
|
|
*
|
|
* Future implementations of Sun-4M machines may do this differently.
|
|
*
|
|
* Sun-4M treats space and addr as the upper and lower 32 bits
|
|
* of a 64-bit physical address; SP0_VIRTUAL is special-cased
|
|
* as noting that this is a 'virtual' connection.
|
|
*/
|
|
char ukbuf[] = "space #";
|
|
|
|
decode_address(space, addr)
|
|
unsigned space, addr;
|
|
{
|
|
char *name;
|
|
#ifdef VME
|
|
struct vme_spaces *vp;
|
|
#endif VME
|
|
#if defined(SUN4M_35)
|
|
extern int swift;
|
|
#endif
|
|
|
|
if (space == SPO_VIRTUAL) /* weird name */
|
|
name = "virtual";
|
|
else {
|
|
ukbuf[6] = "0123456789ABCDEF"[space & 15];
|
|
name = ukbuf;
|
|
switch(cpu) {
|
|
#if defined(SUN4M_690) || defined(SUN4M_50)
|
|
case CPU_SUN4M_690:
|
|
case CPU_SUN4M_50:
|
|
switch(space) {
|
|
case 0x0:
|
|
name = "obmem";
|
|
break;
|
|
|
|
case 0xE:
|
|
name = "SBus slot #";
|
|
name[10] = "0123456789abcdef"[addr>>28];
|
|
addr &= 0x0FFFFFFF;
|
|
break;
|
|
|
|
case 0xF:
|
|
/* XXX - magic numbers are ugly. */
|
|
if (addr < 0xF1000000)
|
|
name = "sys";
|
|
else {
|
|
name = "obio";
|
|
addr -= 0xF1000000;
|
|
}
|
|
break;
|
|
}
|
|
#ifdef VME
|
|
for (vp = vme_spaces; vp->name; ++vp)
|
|
if ((vp->space == space) &&
|
|
(vp->base <= addr)) {
|
|
name = vp->name;
|
|
addr -= vp->base;
|
|
break;
|
|
}
|
|
#endif VME
|
|
break;
|
|
#endif (SUN4M_690 || SUN4M_50)
|
|
|
|
#if defined(SUN4M_35)
|
|
/* tsunami / swift */
|
|
case CPU_SUN4M_35:
|
|
ukbuf[6] = "01234567"[addr >> 28];
|
|
switch(addr >> 28) {
|
|
case 0x0:
|
|
name = "obmem";
|
|
break;
|
|
case 0x1:
|
|
name = "obio";
|
|
break;
|
|
case 0x2:
|
|
if (!swift)
|
|
break;
|
|
|
|
case 0x3:
|
|
case 0x4:
|
|
case 0x5:
|
|
case 0x6:
|
|
case 0x7:
|
|
name = "SBus slot #";
|
|
name[10] =
|
|
"012345"[(addr>>28) - (swift ? 2 : 3)];
|
|
break;
|
|
}
|
|
addr &= 0x0FFFFFFF;
|
|
break;
|
|
#endif (SUN4M_35)
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
printf(" %s 0x%x", name, addr);
|
|
}
|
|
|
|
/*
|
|
* Compute the address of an I/O device within standard address
|
|
* ranges and return the result. This is used by DKIOCINFO
|
|
* ioctl to get the best guess possible for the actual address
|
|
* the card is at.
|
|
*/
|
|
getdevaddr(addr)
|
|
caddr_t addr;
|
|
{
|
|
int off = (int)addr & MMU_PAGEOFFSET;
|
|
struct pte pte;
|
|
int paddr, pageno, space;
|
|
#ifdef VME
|
|
struct vme_spaces *vp;
|
|
#endif VME
|
|
|
|
mmu_getkpte(addr, &pte);
|
|
pageno = pte.PhysicalPageNumber;
|
|
paddr = mmu_ptob(pageno);
|
|
space = pageno >> 20;
|
|
|
|
switch(cpu) {
|
|
#if defined(SUN4M_690) || defined(SUN4M_50)
|
|
case CPU_SUN4M_690:
|
|
case CPU_SUN4M_50:
|
|
if ((pageno >= 0xFF1000) && (pageno < 0xFF2000)) {
|
|
paddr -= 0xF1000000;
|
|
break;
|
|
}
|
|
#ifdef VME
|
|
for (vp = vme_spaces; vp->name; ++vp)
|
|
if ((vp->space == space) &&
|
|
(vp->base <= paddr)) {
|
|
paddr -= vp->base;
|
|
break;
|
|
}
|
|
#endif VME
|
|
break;
|
|
#endif
|
|
#if defined(SUN4M_35)
|
|
case CPU_SUN4M_35:
|
|
/* offset from SBus base */
|
|
switch((paddr >> 28) & 7) {
|
|
case 0x3:
|
|
case 0x4:
|
|
case 0x5:
|
|
case 0x6:
|
|
case 0x7:
|
|
paddr -= 0x30000000;
|
|
break;
|
|
}
|
|
break;
|
|
#endif defined(SUN4M_35)
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (paddr + off);
|
|
}
|
|
|
|
/*
|
|
* figure out what type of bus a page frame number points at.
|
|
* NOTE: returns a BT_ macro, not the top four bits. most
|
|
* things called "bustype" are actually just the top four
|
|
* bits of the pte, which are part of the physical address
|
|
* space as defined in the archetecture and which change between
|
|
* various Sun-4m models.
|
|
*/
|
|
bustype(pageno)
|
|
int pageno;
|
|
{
|
|
unsigned space = pageno >> (32 - MMU_PAGESHIFT); /* %%% */
|
|
|
|
switch(cpu) {
|
|
#if defined(SUN4M_690)
|
|
case CPU_SUN4M_690:
|
|
switch(space) {
|
|
case 0x0:
|
|
return BT_OBMEM;
|
|
case 0x9:
|
|
return BT_VIDEO;
|
|
case 0xA: case 0xB: case 0xC: case 0xD:
|
|
return BT_VME; /* do we need more detail? */
|
|
case 0xE:
|
|
return BT_SBUS;
|
|
case 0xF:
|
|
return BT_OBIO;
|
|
}
|
|
|
|
break;
|
|
#endif
|
|
#if defined(SUN4M_50)
|
|
case CPU_SUN4M_50: /* campus 2 */
|
|
switch(space) {
|
|
case 0x0:
|
|
return BT_OBMEM;
|
|
case 0x9:
|
|
return BT_VIDEO;
|
|
case 0xE:
|
|
return BT_SBUS;
|
|
case 0xF:
|
|
return BT_OBIO;
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(SUN4M_35)
|
|
case CPU_SUN4M_35:
|
|
space = pageno >> (28 - MMU_PAGESHIFT);
|
|
switch(space) {
|
|
case 0x0:
|
|
return BT_OBMEM;
|
|
case 0x1:
|
|
return BT_OBIO;
|
|
case 0x3:
|
|
case 0x4:
|
|
case 0x5:
|
|
case 0x6:
|
|
case 0x7:
|
|
return BT_SBUS;
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
break;
|
|
}
|
|
return BT_UNKNOWN;
|
|
}
|
|
|
|
#ifdef OLD
|
|
/*
|
|
* Say that a device is here
|
|
*/
|
|
|
|
void
|
|
report_dev(dev)
|
|
register struct dev_info *dev;
|
|
{
|
|
int i, pri;
|
|
register int nregs = dev->devi_nreg;
|
|
register int nintr = dev->devi_nintr;
|
|
|
|
/* char *ty; */ /* Not used anywhere */
|
|
|
|
printf("%s%d", dev->devi_name, dev->devi_unit);
|
|
for (i = 0; i < nregs; i++) {
|
|
if (i == 0)
|
|
printf(" at");
|
|
else
|
|
printf(" and");
|
|
decode_address((unsigned) dev->devi_reg[i].reg_bustype,
|
|
(unsigned) dev->devi_reg[i].reg_addr);
|
|
}
|
|
for (i = 0; i < nintr; i++) {
|
|
if (i == 0)
|
|
printf(" ");
|
|
else
|
|
printf(", ");
|
|
/* ty = ""; */ /* Don't know why this is useful */
|
|
pri = LPL2SPL(dev->devi_intr[i].int_pri);
|
|
printf("pri %d", pri);
|
|
switch (dev->devi_intr[i].int_pri & ~15) {
|
|
case INTLEVEL_SOFT:
|
|
printf(" (soft)");
|
|
break;
|
|
case INTLEVEL_ONBOARD:
|
|
printf(" (onboard)");
|
|
break;
|
|
case INTLEVEL_SBUS:
|
|
printf(" (sbus level %d)", LPL2SVL(dev->devi_intr[i].int_pri));
|
|
break;
|
|
#ifdef VME
|
|
case INTLEVEL_VME:
|
|
printf(" (vme level %d)", LPL2SVL(dev->devi_intr[i].int_pri));
|
|
break;
|
|
#endif VME
|
|
}
|
|
if (dev->devi_intr[i].int_vec)
|
|
printf(" vec 0x%x", dev->devi_intr[i].int_vec);
|
|
}
|
|
printf("\n");
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Unmap the virtual address range from addr to addr+size.
|
|
* panics on any error (but at a lower level).
|
|
*/
|
|
void
|
|
unmap_regs(addr, size)
|
|
addr_t addr;
|
|
u_int size;
|
|
{
|
|
long a;
|
|
int extent;
|
|
int offset;
|
|
int s;
|
|
|
|
offset = (int)addr & PGOFSET;
|
|
extent = btoc(size + offset);
|
|
if (extent == 0)
|
|
extent = 1;
|
|
segkmem_mapout(&kseg, (addr_t)((int)addr&PAGEMASK),
|
|
(u_int)mmu_ptob(extent));
|
|
a=btokmx(addr);
|
|
s = splhigh();
|
|
rmfree(kernelmap, (long)extent, (u_long)a);
|
|
(void)splx(s);
|
|
}
|
|
|
|
/*
|
|
* This routine returns the length of the property value for the named
|
|
* property.
|
|
*/
|
|
int
|
|
getproplen(id, name)
|
|
int id;
|
|
char *name;
|
|
{
|
|
return prom_getproplen((dnode_t) id, (caddr_t) name);
|
|
}
|
|
|
|
/*
|
|
* This routine allows drivers to easily retrieve the value of a property
|
|
* for int-sized values. The value of 'name' in the property list for 'devi'
|
|
* is returned. If no value is found, 'def' is returned.
|
|
*
|
|
* As a special case, if a zero length property is found, 1 is returned.
|
|
*/
|
|
int
|
|
getprop(id, name, def)
|
|
int id;
|
|
char *name;
|
|
int def;
|
|
{
|
|
int value;
|
|
|
|
switch (getproplen(id, name)) {
|
|
case 0:
|
|
return (1);
|
|
|
|
case sizeof (int):
|
|
(void) prom_getprop((dnode_t)id, name, (caddr_t) &value);
|
|
return (value);
|
|
|
|
case -1:
|
|
default:
|
|
return (def);
|
|
}
|
|
/*NOTREACHED*/
|
|
}
|
|
|
|
/*
|
|
* This routine allows drivers to easily retrieve the value of a property
|
|
* for arbitrary sized properties. A pointer to the value of 'name' in
|
|
* the property list for 'devi' is returned. If no value is found, NULL
|
|
* is returned. The space is allocated using kmem_zalloc, so it is assumed
|
|
* that this routine is NOT called from an interrupt routine.
|
|
*/
|
|
addr_t
|
|
getlongprop(id, name)
|
|
int id;
|
|
char *name;
|
|
{
|
|
int size;
|
|
addr_t value;
|
|
|
|
size = getproplen(id, name);
|
|
if (size <= 0)
|
|
return (NULL);
|
|
value = kmem_zalloc((u_int) size);
|
|
if (value == NULL) {
|
|
#ifdef MULTIPROCESSOR
|
|
mpprom_eprintf("getlongprop: unable to allocate %d bytes\n", size);
|
|
#endif MULTIPROCESSOR
|
|
panic("getlongprop: kmem_zalloc failed");
|
|
}
|
|
(void)prom_getprop((dnode_t) id, name, value);
|
|
return (value);
|
|
}
|
|
|
|
/*
|
|
* Spurious interrupt messages
|
|
*
|
|
* Probably better if the scanning code calls
|
|
* not_serviced manually if nothing in the list
|
|
* mananged to handle the interrupt, so we
|
|
* don't call it by mistake when servicing
|
|
* soft interrupts (which MUST call everything
|
|
* in the entire list).
|
|
*/
|
|
|
|
#define SPURIOUS -1 /* recognized in locore.s */
|
|
int
|
|
not_serviced(counter, level, bus)
|
|
int *counter, level;
|
|
char *bus;
|
|
{
|
|
if ((*counter)++ % 100 == 1)
|
|
printf("Level %d %sinterrupt not serviced\n", level, bus);
|
|
return (SPURIOUS);
|
|
}
|
|
|
|
char busname_ovec[] = "onboard ";
|
|
char busname_svec[] = "SBus ";
|
|
char busname_vvec[] = "VME ";
|
|
char busname_vec[] = "";
|
|
|
|
#define OVECTOR(n) \
|
|
int olvl/**/n/**/_spurious; \
|
|
struct autovec olvl/**/n[NVECT]
|
|
|
|
#define SVECTOR(n) \
|
|
int slvl/**/n/**/_spurious; \
|
|
struct autovec slvl/**/n[NVECT]
|
|
|
|
#define VVECTOR(n) \
|
|
int vlvl/**/n/**/_spurious; \
|
|
struct autovec vlvl/**/n[NVECT]
|
|
|
|
#define XVECTOR(n) \
|
|
struct autovec xlvl/**/n[NVECT]
|
|
|
|
#define VECTOR(n) \
|
|
int level/**/n/**/_spurious; \
|
|
struct autovec level/**/n[NVECT]
|
|
|
|
typedef int (*func)();
|
|
|
|
extern int softint();
|
|
extern int process_aflt();
|
|
extern int hardlevel10();
|
|
|
|
/*
|
|
* These structures are used in locore.s to jump to device interrupt routines.
|
|
* They also provide vmstat assistance.
|
|
* They will index into the string table generated by autoconfig
|
|
* but in the exact order addintr sees them. This allows IOINTR to quickly
|
|
* find the right counter to increment.
|
|
* (We use the fact that the arrays are initialized to 0 by default).
|
|
*/
|
|
|
|
/*
|
|
* Initial interrupt vector information.
|
|
* Each of these macros defines both the "spurious-int" counter and
|
|
* the list of autovec structures that will be used by locore.s
|
|
* to distribute interrupts to the interrupt requestors.
|
|
* Each list is terminated by a null.
|
|
* Lists are scanned only as needed: hard ints
|
|
* stop scanning when the int is claimed; soft ints
|
|
* scan the entire list. If nobody on the list claims the
|
|
* interrupt, then a spurious interrupt is reported.
|
|
*
|
|
* These should all be initialized to zero, except for the
|
|
* few interrupts that we have handlers for built into the
|
|
* kernel that are not installed by calling "addintr".
|
|
* I would like to eventually get everything going through
|
|
* the "addintr" path.
|
|
|
|
* It might be a good idea to remove VECTORs that are not
|
|
* actually processed by locore.s
|
|
*/
|
|
|
|
/* onboard interrupts */
|
|
/* levels 1..13: lock acquired before calling service */
|
|
OVECTOR(1) = {{0}};
|
|
OVECTOR(2) = {{0}};
|
|
OVECTOR(3) = {{0}};
|
|
OVECTOR(4) = {{0}}; /* scsi */
|
|
OVECTOR(5) = {{0}};
|
|
OVECTOR(6) = {{0}}; /* ethernet */
|
|
OVECTOR(7) = {{0}};
|
|
OVECTOR(8) = {{0}}; /* video */
|
|
OVECTOR(9) = {{0}}; /* module int */
|
|
OVECTOR(10) = {{0}}; /* system timer */
|
|
OVECTOR(11) = {{0}};
|
|
OVECTOR(12) = {{0}}; /* serial ports */
|
|
OVECTOR(13) = {{0}};
|
|
/* levels 14..15: lock not entered */
|
|
OVECTOR(14) = {{0}}; /* processor timer */
|
|
OVECTOR(15) = {{0}}; /* async errors */
|
|
|
|
/* SBus Interrupts */
|
|
/* all levels: lock held before calling service routines */
|
|
SVECTOR(1) = {{0}};
|
|
SVECTOR(2) = {{0}};
|
|
SVECTOR(3) = {{0}};
|
|
SVECTOR(4) = {{0}};
|
|
SVECTOR(5) = {{0}};
|
|
SVECTOR(6) = {{0}};
|
|
SVECTOR(7) = {{0}};
|
|
|
|
/* VME Interrupts */
|
|
/* all levels: lock held before calling service routines */
|
|
VVECTOR(1) = {{0}};
|
|
VVECTOR(2) = {{0}};
|
|
VVECTOR(3) = {{0}};
|
|
VVECTOR(4) = {{0}};
|
|
VVECTOR(5) = {{0}};
|
|
VVECTOR(6) = {{0}};
|
|
VVECTOR(7) = {{0}};
|
|
|
|
/* software vectored interrupts */
|
|
/* levels 1..9: lock held before calling service routines */
|
|
XVECTOR(1) = {{softint},{0}}; /* time-scheduled tasks */
|
|
XVECTOR(2) = {{0}};
|
|
XVECTOR(3) = {{0}};
|
|
XVECTOR(4) = {{0}};
|
|
XVECTOR(5) = {{0}};
|
|
XVECTOR(6) = {{0}};
|
|
XVECTOR(7) = {{0}};
|
|
XVECTOR(8) = {{0}};
|
|
XVECTOR(9) = {{0}};
|
|
/* levels 10..13: forwarding is responsibility of service routine */
|
|
XVECTOR(10) = {{0}};
|
|
XVECTOR(11) = {{0}};
|
|
XVECTOR(12) = {{process_aflt},{0}}; /* process async. fault */
|
|
XVECTOR(13) = {{0}};
|
|
XVECTOR(14) = {{0}};
|
|
XVECTOR(15) = {{0}};
|
|
/* otherwise unclaimed sparc interrupts */
|
|
/* levels 1..13: lock held before calling service routine */
|
|
VECTOR(1) = {{0}};
|
|
VECTOR(2) = {{0}};
|
|
VECTOR(3) = {{0}};
|
|
VECTOR(4) = {{0}};
|
|
VECTOR(5) = {{0}};
|
|
VECTOR(6) = {{0}};
|
|
VECTOR(7) = {{0}};
|
|
VECTOR(8) = {{0}};
|
|
VECTOR(9) = {{0}};
|
|
VECTOR(10) = {{0}};
|
|
VECTOR(11) = {{0}};
|
|
VECTOR(12) = {{0}};
|
|
VECTOR(13) = {{0}};
|
|
/* levels 14..15: lock not entered. */
|
|
VECTOR(14) = {{0}};
|
|
VECTOR(15) = {{0}};
|
|
|
|
/*
|
|
* indirection table, to save us some large switch statements
|
|
* NOTE: This must agree with "INTLEVEL_foo" constants in
|
|
* <sun/autoconf.h>
|
|
*/
|
|
struct autovec *vectorlist[] = {
|
|
/*
|
|
* otherwise unidentified interrupts at SPARC levels 1..15
|
|
*/
|
|
0, level1, level2, level3, level4, level5, level6, level7,
|
|
level8, level9, level10,level11,level12,level13,level14,level15,
|
|
/*
|
|
* interrupts identified as "soft"
|
|
*/
|
|
0, xlvl1, xlvl2, xlvl3, xlvl4, xlvl5, xlvl6, xlvl7,
|
|
xlvl8, xlvl9, xlvl10, xlvl11, xlvl12, xlvl13, xlvl14, xlvl15,
|
|
/*
|
|
* interrupts identified as "onboard"
|
|
*/
|
|
0, olvl1, olvl2, olvl3, olvl4, olvl5, olvl6, olvl7,
|
|
olvl8, olvl9, olvl10, olvl11, olvl12, olvl13, olvl14, olvl15,
|
|
/*
|
|
* interrupts identified as "sbus" -- note that sbus has only level 1
|
|
* through 7; these are mapped to the proper sparc levels via the
|
|
* boot prom forth word "???".
|
|
*/
|
|
0, 0, slvl1, slvl2, 0, slvl3, 0, slvl4,
|
|
0, slvl5, 0, slvl6, 0, slvl7, 0, 0,
|
|
/*
|
|
* interrupts identified as "vme" -- note that vme has only level 1
|
|
* through 7; these are mapped to the proper sparc levels via the
|
|
* boot prom forth word "???".
|
|
*/
|
|
0, 0, vlvl1, vlvl2, 0, vlvl3, 0, vlvl4,
|
|
0, vlvl5, 0, vlvl6, 0, vlvl7, 0, 0,
|
|
};
|
|
#define MAXAUTOVEC (sizeof vectorlist / sizeof vectorlist[0])
|
|
|
|
/*
|
|
* vmstat -i support for autovectored interrupts.
|
|
* add the autovectored interrupt to the table of mappings of
|
|
* interrupt counts to device names. We can't do much about
|
|
* a device that has multiple units, so we print '?' for such.
|
|
* Otherwise, we try to use the real unit number.
|
|
*/
|
|
#ifndef oldstuff
|
|
/*ARGSUSED */
|
|
#endif oldstuff
|
|
void
|
|
addioname(levelp, name, unit)
|
|
register struct autovec *levelp;
|
|
char *name;
|
|
int unit;
|
|
{
|
|
if (levelp->av_name == NULL)
|
|
levelp->av_name = name;
|
|
else if (strcmp(levelp->av_name, name)) {
|
|
printf("addioname: can't change %s to %s!\n",
|
|
levelp->av_name, name);
|
|
panic("addioname");
|
|
}
|
|
levelp->av_devcnt++;
|
|
}
|
|
|
|
/*
|
|
* Arrange for a driver to be called when a particular
|
|
* auto-vectored interrupt occurs.
|
|
* NOTE: if a device can generate interrupts on more than
|
|
* one level, or if a driver services devices that interrupt
|
|
* on more than one level, then the driver should install
|
|
* itself on each of those levels.
|
|
|
|
* devices may also install themselves on the VME, Internal, or SBUS
|
|
* vector lists using inerrupt level offsets, see <sun/autoconf.h>.
|
|
|
|
* On Hard-ints, order of evaluation of the chains is:
|
|
* if onboard int at this level active in SIPR,
|
|
* scan "onboard" chain; if nobody claims,
|
|
* remember bus-type is onboard.
|
|
* if sbus int at this level active in SIPR,
|
|
* scan "sbus" chain; if nobody claims,
|
|
* remember bus-type is sbus.
|
|
* if vme int at this level active in SIPR,
|
|
* scan "vme" chain; if nobody claims,
|
|
* remember bus-type is vme.
|
|
* scan "unspecified" chain; if nobody claims,
|
|
* report spurious interrupt and remembered bus-type if any.
|
|
* Scanning terminates with the first driver that claims it has
|
|
* serviced the interrupt.
|
|
*
|
|
* On Soft-ints, order of evaulation of the chains is:
|
|
* scan the "soft" chain
|
|
* scan the "unspecified" chain
|
|
* Scanning continues until all service routines have been called.
|
|
* If nobody claims the interrupt, report a spurious soft interrupt.
|
|
*/
|
|
|
|
addintr(lvl, xxintr, name, unit)
|
|
func xxintr;
|
|
char *name;
|
|
int unit;
|
|
{
|
|
register func f;
|
|
register struct autovec *levelp;
|
|
register int i;
|
|
|
|
if ((f = xxintr) == NULL)
|
|
return;
|
|
if (lvl >= MAXAUTOVEC)
|
|
panic("addintr: vector number out of range");
|
|
levelp = vectorlist[lvl];
|
|
if (levelp == (struct autovec *)0)
|
|
panic("addintr: specified vector can not be polled");
|
|
|
|
/* If devcnt == -1, settrap() claimed this trap */
|
|
if (levelp->av_devcnt == -1) {
|
|
/* XXX - addintr() should return success/failure */
|
|
panic("addintr: interrupt trap vector busy\n");
|
|
}
|
|
|
|
for (i = 0; i < NVECT; i++) {
|
|
if (levelp->av_vector == NULL) { /* found an open slot */
|
|
bzero((caddr_t) levelp, sizeof (*levelp));
|
|
levelp->av_vector = f; /* add f to list */
|
|
addioname(levelp, name, unit);
|
|
return;
|
|
}
|
|
if (levelp->av_vector == f) { /* already in list */
|
|
addioname(levelp, name, unit);
|
|
return;
|
|
}
|
|
levelp++;
|
|
}
|
|
panic("addintr: too many devices");
|
|
}
|
|
|
|
|
|
/*
|
|
* Opcodes for instructions in scb entries
|
|
*/
|
|
#define MOVPSRL0 0xa1480000 /* mov %psr, %l0 */
|
|
#define MOVL4 0xa8102000 /* mov (+/-0xfff), %l4 */
|
|
#define MOVL6 0xac102000 /* mov (+/-0xfff), %l6 */
|
|
#define BRANCH 0x10800000 /* ba (+/-0x1fffff)*4 */
|
|
#define SETHIL3 0x27000000 /* sethi %hi(0xfffffc00), %l3 */
|
|
#define JMPL3 0x81c4e000 /* jmp %l3+%lo(0x3ff) */
|
|
#define NO_OP 0x01000000 /* nop */
|
|
|
|
#define BRANCH_RANGE 0x001fffff /* max branch displacement */
|
|
#define BRANCH_MASK 0x003fffff /* branch displacement mask */
|
|
#define SETHI_HI(x) (((x) >> 10) & 0x003fffff) /* %hi(x) */
|
|
#define JMP_LO(x) ((x) & 0x000003ff) /* %lo(x) */
|
|
|
|
/*
|
|
* Dynamically set a hard trap vector in the scb table.
|
|
* This routine allows device drivers, such as fd and audio_79C30,
|
|
* to dynamically configure assembly-language interrupt handlers.
|
|
* If 'xxintr' is NULL, the trap vector is set back to the default system trap.
|
|
*
|
|
* If this trap can be autovectored, set the av_devcnt field to -1 when a
|
|
* direct trap vector is set, so that two devices can't claim the same
|
|
* interrupt. If av_devcnt > 0, this trap is already autovectored, so don't
|
|
* give the trap vector away.
|
|
*
|
|
* For sun4m machines interrupt level assignments are handled differently.
|
|
* (see ../sun/autoconf.h for details). As such the interrupt level passed
|
|
* to settrap "lvl" is INTLEVEL_XXX+n. Where n is the normal level (1..15)
|
|
* and XXX represent one of (SOFT, ONBOARD, SBUS, VME). This logical
|
|
* interrupt number gets translated into the real SPARC level to select
|
|
* which interrupt vector will be modified.
|
|
*
|
|
* Return 0 if the trap vector could not be set according to the request.
|
|
* Otherwise, return 1.
|
|
*/
|
|
|
|
int
|
|
settrap(lvl, xxintr)
|
|
int lvl;
|
|
func xxintr;
|
|
{
|
|
struct autovec *levelp;
|
|
trapvec vec;
|
|
extern sys_trap();
|
|
extern int nwindows; /* number of register windows */
|
|
|
|
if (lvl >= MAXAUTOVEC)
|
|
panic("settrap: vector number out of range\n");
|
|
lvl = LPL2SPL(lvl);
|
|
|
|
if ((lvl == 10) || /* reserved for system clock */
|
|
(lvl == 12) || /* reserved for serial ports */
|
|
(lvl == 14) || /* reserved for monitor clock */
|
|
(lvl == 15)) { /* reserved for internal sys communication */
|
|
printf("settrap: cannot set trap for processor level %d\n", lvl);
|
|
return(0);
|
|
}
|
|
levelp = vectorlist[lvl];
|
|
|
|
if (!levelp) {
|
|
printf("settrap: cannot set trap for processor level %d\n", lvl);
|
|
return(0);
|
|
}
|
|
/*
|
|
* Check to see whether trap is available.
|
|
* If devcnt == 0, this is a system trap with no autovectors.
|
|
* If devcnt > 0, this is a system trap with autovectors set.
|
|
* If devcnt == -1, this is a direct trap (addintr() will complain).
|
|
*/
|
|
if (levelp != NULL) {
|
|
if (xxintr != NULL) {
|
|
/* If trying to set a direct trap handler */
|
|
if (levelp->av_devcnt == -1) {
|
|
printf("settrap: level %d trap busy\n", lvl);
|
|
return (0);
|
|
|
|
} else if (levelp->av_devcnt != 0) {
|
|
printf("settrap: level %d trap is autovectored\n", lvl);
|
|
return (0);
|
|
}
|
|
levelp->av_devcnt = -1; /* don't autovector */
|
|
} else {
|
|
/* Setting back to system trap */
|
|
levelp->av_devcnt = 0;
|
|
}
|
|
}
|
|
|
|
if (xxintr == NULL) {
|
|
/* Reset the trap vector to a default system trap */
|
|
vec.instr[0] = MOVPSRL0;
|
|
vec.instr[1] = SETHIL3 | SETHI_HI((int)sys_trap);
|
|
vec.instr[2] = JMPL3 | JMP_LO((int)sys_trap);
|
|
vec.instr[3] = MOVL4 | T_INTERRUPT | lvl;
|
|
} else {
|
|
vec.instr[0] = MOVPSRL0;
|
|
vec.instr[1] = SETHIL3 | SETHI_HI((int)xxintr);
|
|
vec.instr[2] = JMPL3 | JMP_LO((int)xxintr);
|
|
vec.instr[3] = MOVL6 | (nwindows - 1);
|
|
}
|
|
|
|
write_scb_int(lvl, &vec); /* set the new vector */
|
|
return (1);
|
|
}
|
|
|
|
|
|
/*
|
|
* Increase splvm_val, if necessary, for a device that uses DMA.
|
|
* Level is usually dev->devi_intr->int_pri.
|
|
*
|
|
* XXX keep SPLMB in step.
|
|
*/
|
|
adddma(level)
|
|
int level;
|
|
{
|
|
int spl;
|
|
|
|
spl = ipltospl(LPL2SPL(level));
|
|
if (spl > splvm_val) {
|
|
splvm_val = spl;
|
|
SPLMB = spltopri(spl);
|
|
/*
|
|
* note: splr(pritospl(SPLMB)) might be one level
|
|
* higher than splvm(). This is important if we
|
|
* get an adddma for SPARC Level-9, where the splr
|
|
* would lock out the clock at Level-10 as well.
|
|
*/
|
|
}
|
|
}
|
|
|
|
/*
|
|
* store dk name && unit number info
|
|
* reuse empty slots, if possible (loadable drivers, or
|
|
* the case of drives coming and going off line)
|
|
*/
|
|
int
|
|
newdk(name, unit)
|
|
char *name;
|
|
int unit;
|
|
{
|
|
int i;
|
|
int j = -1;
|
|
|
|
for (i = 0; i < dkn; i++) {
|
|
char *p;
|
|
|
|
if ((p = dk_ivec[i].dk_name) == NULL || *p == '\0') {
|
|
j = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (j < 0) {
|
|
if (dkn >= DK_NDRIVE) {
|
|
return (-1);
|
|
} else {
|
|
j = dkn++;
|
|
}
|
|
}
|
|
|
|
dk_ivec[j].dk_name = name;
|
|
dk_ivec[j].dk_unit = unit;
|
|
return (j);
|
|
}
|
|
|
|
/*
|
|
* remove dk name && unit number info
|
|
*/
|
|
int
|
|
remdk(num)
|
|
int num;
|
|
{
|
|
if (num >= dkn || num < 0)
|
|
return (-1);
|
|
else {
|
|
dk_ivec[num].dk_name = "";
|
|
dk_ivec[num].dk_unit = 0;
|
|
dk_time[num] = dk_seek[num] = dk_xfer[num] =
|
|
dk_wds[num] = dk_bps[num] = dk_read[num] = 0;
|
|
/* free up the last one */
|
|
if ((num+1) == dkn)
|
|
dkn = num;
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
#ifdef VDDRV
|
|
/*
|
|
* These routines are the inverse of the previous three routines;
|
|
* they allow a driver to be unloaded.
|
|
*/
|
|
|
|
/*
|
|
* remove the autovectored interrupt from the table of mappings of
|
|
* interrupt counts to device names.
|
|
*/
|
|
/*ARGSUSED*/
|
|
void
|
|
remioname(levelp)
|
|
struct autovec *levelp;
|
|
{
|
|
|
|
/*
|
|
* We could try to remove the name from the av_nametab list, but
|
|
* that wouldn't buy us much. We're about to overwrite this
|
|
* entry anyway, so there is nothing to do here.
|
|
* So do nothing.
|
|
*/
|
|
}
|
|
|
|
/*
|
|
* Remove a driver from the autovector list.
|
|
* NOTE: if a driver has installed itself on more than one level,
|
|
* then the driver should remove itself from each of those levels.
|
|
*/
|
|
remintr(lvl, xxintr)
|
|
func xxintr;
|
|
{
|
|
register func f;
|
|
register struct autovec *levelp;
|
|
register int i;
|
|
|
|
if ((f = xxintr) == NULL)
|
|
return (0);
|
|
|
|
if (lvl >= MAXAUTOVEC)
|
|
panic("remintr: vector number out of range");
|
|
levelp = vectorlist[lvl];
|
|
if (levelp == (struct autovec *)0)
|
|
panic("remintr: specified vector can not be polled");
|
|
|
|
for (i = 0; i < NVECT; i++, levelp++) {
|
|
if (levelp->av_vector == NULL) {
|
|
/* end of list reached */
|
|
break;
|
|
}
|
|
if (levelp->av_vector == f) {
|
|
/* found it */
|
|
remioname(levelp);
|
|
/* move them all down one */
|
|
for (i++; i < NVECT; i++, levelp++)
|
|
levelp[0] = levelp[1];
|
|
return (0);
|
|
}
|
|
}
|
|
|
|
/* one way or another, it isn't in the list */
|
|
printf("remintr: driver not installed on level %d\n", lvl);
|
|
return (-1);
|
|
}
|
|
|
|
#endif VDDRV
|
|
|
|
/*
|
|
* Configure swap space and related parameters.
|
|
*/
|
|
swapconf()
|
|
{
|
|
#ifndef SAS
|
|
register struct bootobj *swp;
|
|
register int nblks;
|
|
struct vattr vattr;
|
|
struct vfs *vfsp;
|
|
struct vfssw *vsw;
|
|
int error;
|
|
extern char *strcpy();
|
|
extern u_int swapwarn; /* from param.c */
|
|
|
|
swp = &swapfile;
|
|
error = ENODEV;
|
|
vfsp = NULL;
|
|
/*
|
|
* No filesystem type specified, try to get default
|
|
*/
|
|
if (((boothowto & RB_ASKNAME) || (*(swp->bo_fstype) == '\0')) &&
|
|
(vsw = getfstype("swap", swp->bo_fstype))) {
|
|
(void) strcpy(swp->bo_fstype, vsw->vsw_name);
|
|
vfsp = (struct vfs *)kmem_alloc(sizeof (*vfsp));
|
|
VFS_INIT(vfsp, vsw->vsw_ops, 0);
|
|
error = VFS_SWAPVP(vfsp, &swp->bo_vp, swp->bo_name);
|
|
} else if (*swp->bo_name == '/') {
|
|
/*
|
|
* file name begins with a slash, try to look it up
|
|
* as a pathname starting at the root.
|
|
*/
|
|
error = lookupname(swp->bo_name, UIO_SYSSPACE,
|
|
FOLLOW_LINK, (struct vnode **)0, &swp->bo_vp);
|
|
printf("swapconf: looked up %s, got error %d\n",
|
|
swp->bo_name, error);
|
|
if (!error) {
|
|
(void) strcpy(swp->bo_fstype, rootfs.bo_fstype);
|
|
}
|
|
} else {
|
|
/*
|
|
* Look through all supported filesystem types for one
|
|
* that matches the type of swp, or if swp has no
|
|
* fstype just take the first filesystem type that
|
|
* will supply a swap vp
|
|
*/
|
|
for (vsw = &vfssw[0]; vsw <= &vfssw[MOUNT_MAXTYPE];
|
|
vsw++) {
|
|
if (vsw->vsw_name == 0) {
|
|
continue;
|
|
}
|
|
if (*(swp->bo_fstype) == '\0' ||
|
|
(strcmp(swp->bo_fstype, vsw->vsw_name) == 0)) {
|
|
vfsp = (struct vfs *)kmem_alloc(sizeof (*vfsp));
|
|
VFS_INIT(vfsp, vsw->vsw_ops, 0);
|
|
error = VFS_SWAPVP(vfsp, &swp->bo_vp,
|
|
swp->bo_name);
|
|
if (error == 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (error || swp->bo_vp == NULL) {
|
|
if (error == ENODEV)
|
|
printf("swap device '%s': Not Present\n", swp->bo_name);
|
|
else
|
|
printf("bad swap device '%s': error %d\n", swp->bo_name,
|
|
error);
|
|
bad:
|
|
if (swp->bo_vp) {
|
|
VN_RELE(swp->bo_vp);
|
|
swp->bo_vp = NULL;
|
|
}
|
|
if (vfsp) {
|
|
kmem_free((char *)vfsp, sizeof (*vfsp));
|
|
}
|
|
if (error)
|
|
return;
|
|
} else {
|
|
error = VOP_OPEN(&swp->bo_vp, FREAD|FWRITE, u.u_cred);
|
|
if (error) {
|
|
printf("error %d in opening swap file %s\n",
|
|
error, swp->bo_name);
|
|
goto bad;
|
|
}
|
|
error = VOP_GETATTR(swp->bo_vp, &vattr, u.u_cred);
|
|
if (error) {
|
|
printf("error %d getting size of swapfile %s\n",
|
|
error, swp->bo_name);
|
|
goto bad;
|
|
}
|
|
nblks = btodb(vattr.va_size);
|
|
if (swp->bo_size == 0 || swp->bo_size > nblks)
|
|
swp->bo_size = nblks;
|
|
if (*swp->bo_fstype == '\0') {
|
|
(void) strcpy(swp->bo_fstype, vsw->vsw_name);
|
|
}
|
|
#ifdef VFSSTATS
|
|
if (vfs_add(swp->bo_vp, vfsp, 0) == 0 &&
|
|
vfsp->vfs_stats == 0) {
|
|
struct vfsstats *vs;
|
|
|
|
vs = (struct vfsstats *)
|
|
kmem_zalloc(sizeof (struct vfsstats));
|
|
vs->vs_time = time.tv_sec;
|
|
vfsp->vfs_stats = (caddr_t)vs;
|
|
}
|
|
#endif
|
|
printf("swap on %s fstype %s size %dK\n",
|
|
swp->bo_name, swp->bo_fstype,
|
|
dbtob(swp->bo_size) / 1024);
|
|
if (nblks < swapwarn)
|
|
printf("WARNING: swap space probably too small\n");
|
|
swp->bo_flags |= BO_VALID;
|
|
swap_present = 1;
|
|
}
|
|
#else SAS
|
|
register struct bootobj *swp;
|
|
struct vattr vattr;
|
|
int error;
|
|
|
|
swp = &swapfile;
|
|
if (strcmp(swp->bo_name, "sim0") != 0) {
|
|
printf("bad swap name '%s', should be 'sim0'\n",
|
|
swp->bo_name);
|
|
panic("bad swap device");
|
|
}
|
|
/*
|
|
* Previously was making the call, makespecvp(makedev(1, 1), VBLK), instead of
|
|
* making the call, bdevvp(makedev(1, 1)). Calling bdevvp is what the non-SAS
|
|
* call to VFS_SWAPVP (see above) essentially does, so it is the right thing for
|
|
* SAS to do simulate the real world as close as possible, given the way the sim0
|
|
* device is currently implemented. Calling makespecvp would result in it first
|
|
* creating a shadow snode (and vnode contained inside), then creating the real
|
|
* vnode via calling bdevvp, and having the s_bdevvp field of the shadow snode
|
|
* point to this real vnode. The problem with this approach is that when the
|
|
* shaddow vnode is passed to spec_getpage to perform a swapin, spec_getpage is
|
|
* expecting the real vnode, and panics when VTOS(vp)->b_devvp != vp.
|
|
*/
|
|
swp->bo_vp = bdevvp(makedev(1, 1));
|
|
error = VOP_OPEN(&swp->bo_vp, FREAD+FWRITE, u.u_cred);
|
|
if (error) {
|
|
printf("error %d opening swap\n",
|
|
error);
|
|
panic("cannot open swap device");
|
|
}
|
|
error = VOP_GETATTR(swp->bo_vp, &vattr, u.u_cred);
|
|
if (error) {
|
|
printf("error %d getting size of swap\n",
|
|
error);
|
|
panic("cannot get swap size");
|
|
}
|
|
swp->bo_size = btodb(vattr.va_size);
|
|
if (*swp->bo_fstype == '\0') {
|
|
(void) strcpy(swp->bo_fstype, "spec");
|
|
}
|
|
printf("swap on %s fstype %s size %dK\n",
|
|
swp->bo_name, swp->bo_fstype,
|
|
dbtob(swp->bo_size) / 1024);
|
|
swp->bo_flags |= BO_VALID;
|
|
swap_present = 1; /* main checks for this */
|
|
#endif SAS
|
|
}
|
|
|
|
dev_t kbddev = NODEV;
|
|
dev_t mousedev = NODEV;
|
|
#define NOUNIT -1
|
|
|
|
#ifdef SAS
|
|
#define SIMCMAJOR 12 /* simulated console */
|
|
#else !SAS
|
|
#define WSCONSMAJOR 1 /* "workstation console" */
|
|
#define ZSMAJOR 12 /* serial */
|
|
#define KBDMINOR 0
|
|
#define MOUSEMINOR 1
|
|
#define CONSKBDMAJOR 29 /* console keyboard driver */
|
|
#define CONSMOUSEMAJOR 13 /* console mouse driver */
|
|
#endif !SAS
|
|
|
|
extern struct fileops vnodefops;
|
|
|
|
struct consinfo {
|
|
int kmunit; /* keyboard/mouse unit number */
|
|
dev_t fbdev; /* frame buffer device */
|
|
struct dev_info *options; /* options dev_info (for keyclick et al.) */
|
|
};
|
|
|
|
/*
|
|
* Configure keyboard, mouse, and frame buffer using
|
|
* monitor provided values
|
|
* configuration flags
|
|
* N.B. some console devices are statically initialized by the
|
|
* drivers to NODEV.
|
|
*/
|
|
|
|
consconfig()
|
|
{
|
|
int e;
|
|
#ifndef SAS
|
|
struct consinfo consinfo;
|
|
|
|
struct termios termios;
|
|
int kbdtranslatable = TR_CANNOT;
|
|
int kbdspeed = B9600;
|
|
struct vnode *kbdvp;
|
|
struct vnode *conskbdvp;
|
|
struct file *fp;
|
|
int i;
|
|
char devname[OBP_MAXDEVNAME];
|
|
int unit;
|
|
int findcons(/* (struct dev_info *), (struct consinfo *) */);
|
|
int zsminor;
|
|
char *p;
|
|
extern char *prom_get_stdin_subunit();
|
|
|
|
|
|
stop_mon_clock(); /* turn off monitor polling clock */
|
|
|
|
consinfo.kmunit = NOUNIT;
|
|
consinfo.fbdev = NODEV;
|
|
consinfo.options = NULL;
|
|
|
|
/*
|
|
* check for console on same ascii port to allow full speed
|
|
* output by using the UNIX driver and avoiding the monitor.
|
|
*/
|
|
|
|
if (prom_stdin_stdout_equivalence() == 0)
|
|
goto namedone;
|
|
|
|
if (prom_get_stdin_dev_name(devname, sizeof devname) != 0)
|
|
goto namedone;
|
|
|
|
if (strcmp("zs", devname) != 0)
|
|
goto namedone;
|
|
|
|
if ((unit = prom_get_stdin_unit()) == -1)
|
|
goto namedone;
|
|
|
|
zsminor = 0;
|
|
if ((p = prom_get_stdin_subunit()) != (char *)0)
|
|
if ((*p) != (char)0)
|
|
zsminor = (*p - 'a') & 1;
|
|
|
|
zsminor = (unit << 1) + zsminor;
|
|
|
|
rconsdev = makedev(ZSMAJOR, zsminor);
|
|
|
|
#else SAS
|
|
rconsdev = makedev(SIMCMAJOR, 0);
|
|
#endif SAS
|
|
|
|
namedone:
|
|
|
|
if (rconsdev) {
|
|
/*
|
|
* Console is a CPU serial port.
|
|
*/
|
|
rconsvp = makespecvp(rconsdev, VCHR);
|
|
kbddev = rconsdev;
|
|
/*
|
|
* Opening causes interrupts, etc. to be initialized.
|
|
* Console device drivers must be able to do output
|
|
* after being closed!
|
|
*/
|
|
if (e = VOP_OPEN(&rconsvp, FREAD|FWRITE|FNOCTTY|FNDELAY, u.u_cred))
|
|
printf("console open failed: error %d\n", e);
|
|
/* now we must close it to make console logins happy */
|
|
VOP_CLOSE(rconsvp, FREAD|FWRITE, 1, u.u_cred);
|
|
consdev = rconsdev;
|
|
consvp = rconsvp;
|
|
#ifndef SAS
|
|
/* Still need to find options dev_info */
|
|
walk_devs(top_devinfo, findcons, (char *) &consinfo);
|
|
options_devinfo = consinfo.options;
|
|
set_keyclick(options_devinfo);
|
|
#endif /* SAS */
|
|
return;
|
|
}
|
|
#ifndef SAS
|
|
|
|
/*
|
|
* The following code assumes mappings for ZS minor devices:
|
|
*
|
|
* ttya/zs devinfo unit 0/Channel a --> zs minor device #0
|
|
* ttyb/zs devinfo unit 0/Channel b --> zs minor device #1
|
|
*
|
|
* See comments concerning zs minor dev mappings in zs_async.c
|
|
*/
|
|
|
|
if (prom_stdin_is_keyboard())
|
|
goto kbddone;
|
|
|
|
if (prom_get_stdin_dev_name(devname, sizeof devname) != 0)
|
|
goto kbddone;
|
|
|
|
if (strcmp("zs", devname) != 0)
|
|
goto kbddone;
|
|
|
|
if ((unit = prom_get_stdin_unit()) != -1) {
|
|
|
|
zsminor = 0;
|
|
if ((p = prom_get_stdin_subunit()) != (char *)0)
|
|
if (*p != (char)0)
|
|
zsminor = (*p - 'a') & 1;
|
|
|
|
zsminor = (unit << 1) + zsminor;
|
|
|
|
kbddev = makedev(ZSMAJOR, zsminor);
|
|
}
|
|
|
|
kbddone:
|
|
|
|
if (kbddev != NODEV)
|
|
kbdspeed = zsgetspeed(kbddev);
|
|
|
|
/*
|
|
* Look for the [last] kbd/ms and matching fbtype.
|
|
*/
|
|
walk_devs(top_devinfo, findcons, (char *) &consinfo);
|
|
options_devinfo = consinfo.options;
|
|
set_keyclick(options_devinfo);
|
|
|
|
/*
|
|
* Use serial keyboard and mouse if found flagged uart
|
|
*/
|
|
if (consinfo.kmunit != NOUNIT) {
|
|
if (mousedev == NODEV)
|
|
mousedev = makedev(ZSMAJOR,
|
|
consinfo.kmunit * 2 + MOUSEMINOR);
|
|
if (kbddev == NODEV) {
|
|
kbddev = makedev(ZSMAJOR,
|
|
consinfo.kmunit * 2 + KBDMINOR);
|
|
kbdtranslatable = TR_CAN;
|
|
}
|
|
}
|
|
if (kbddev == NODEV)
|
|
panic("no keyboard found");
|
|
/*
|
|
* Open the keyboard device.
|
|
*/
|
|
kbdvp = makespecvp(kbddev, VCHR);
|
|
if (e = VOP_OPEN(&kbdvp, FREAD|FNOCTTY, u.u_cred)) {
|
|
printf("keyboard open failed: error %d\n", e);
|
|
panic("can't open keyboard");
|
|
}
|
|
e = FLUSHRW; /* Flush both read and write */
|
|
strioctl(kbdvp, I_FLUSH, (caddr_t)&e, FREAD);
|
|
u.u_error = 0; /* Just In Case */
|
|
|
|
if (!ldreplace(kbdvp, kbdvp->v_stream, KBDLDISC)) {
|
|
u.u_error = EINVAL;
|
|
}
|
|
|
|
if (u.u_error != 0) {
|
|
printf("consconfig: can't set line discipline: error %d\n",
|
|
u.u_error);
|
|
panic("can't set up keyboard");
|
|
}
|
|
strioctl(kbdvp, KIOCTRANSABLE, (caddr_t)&kbdtranslatable, 0);
|
|
if (kbdtranslatable == TR_CANNOT) {
|
|
/*
|
|
* For benefit of serial port keyboard.
|
|
*/
|
|
strioctl(kbdvp, TCGETS, (caddr_t)&termios, FREAD);
|
|
termios.c_cflag &= ~(CBAUD|CIBAUD);
|
|
termios.c_cflag |= kbdspeed;
|
|
strioctl(kbdvp, TCSETSF, (caddr_t)&termios, FREAD);
|
|
if (u.u_error != 0) {
|
|
printf("consconfig: TCSETSF error %d\n", u.u_error);
|
|
u.u_error = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Open the "console keyboard" device, and link the keyboard
|
|
* device under it.
|
|
* XXX - there MUST be a better way to do this!
|
|
*/
|
|
conskbdvp = makespecvp(makedev(CONSKBDMAJOR, 1), VCHR);
|
|
if (e = VOP_OPEN(&conskbdvp, FREAD|FWRITE|FNOCTTY, u.u_cred)) {
|
|
printf("console keyboard device open failed: error %d\n", e);
|
|
panic("can't open keyboard");
|
|
}
|
|
if ((fp = falloc()) == NULL)
|
|
panic("can't get file descriptor for keyboard");
|
|
i = u.u_r.r_val1; /* XXX - get FD number */
|
|
fp->f_flag = FREAD;
|
|
fp->f_type = DTYPE_VNODE;
|
|
fp->f_data = (caddr_t)kbdvp;
|
|
fp->f_ops = &vnodefops;
|
|
strioctl(conskbdvp, I_PLINK, (caddr_t)&i, FREAD);
|
|
if (u.u_error != 0) {
|
|
printf("I_PLINK failed: error %d\n", u.u_error);
|
|
panic("can't link kbddev under /dev/kbd");
|
|
}
|
|
u.u_ofile[i] = NULL;
|
|
closef(fp); /* don't need this any more */
|
|
|
|
kbddevopen = 1;
|
|
|
|
/* try to configure mouse */
|
|
if (mousedev != NODEV && mouseconfig(mousedev)) {
|
|
#if !defined(MUNIX) && !defined(MINIROOT)
|
|
printf("No mouse found\n");
|
|
#endif
|
|
mousedev = NODEV;
|
|
u.u_error = 0;
|
|
}
|
|
|
|
/*
|
|
* Set up default frame buffer.
|
|
*/
|
|
if (fbdev == NODEV && (fbdev = consinfo.fbdev) == NODEV) {
|
|
#if !defined(MUNIX) && !defined(MINIROOT)
|
|
printf("No default frame buffer found\n");
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Open the "workstation console" device, and link the "console
|
|
* keyboard" device under it.
|
|
* XXX - there MUST be a better way to do this!
|
|
*/
|
|
rconsdev = makedev(WSCONSMAJOR, 0);
|
|
rconsvp = makespecvp(rconsdev, VCHR);
|
|
if (e = VOP_OPEN(&rconsvp, FREAD|FWRITE|FNOCTTY, u.u_cred)) {
|
|
printf("console open failed: error %d\n", e);
|
|
panic("can't open console");
|
|
}
|
|
if ((fp = falloc()) == NULL)
|
|
panic("can't get file descriptor for console keyboard");
|
|
i = u.u_r.r_val1; /* XXX - get FD number */
|
|
fp->f_flag = FREAD;
|
|
fp->f_type = DTYPE_VNODE;
|
|
fp->f_data = (caddr_t)conskbdvp;
|
|
fp->f_ops = &vnodefops;
|
|
strioctl(rconsvp, I_PLINK, (caddr_t)&i, FREAD);
|
|
if (u.u_error != 0) {
|
|
printf("consconfig: I_PLINK failed: error %d\n", u.u_error);
|
|
panic("can't link /dev/kbd under workstation console");
|
|
}
|
|
u.u_ofile[i] = NULL;
|
|
closef(fp); /* don't need this any more */
|
|
/* now we must close it to make console logins happy */
|
|
VOP_CLOSE(rconsvp, FREAD|FWRITE, 1, u.u_cred);
|
|
consdev = rconsdev;
|
|
consvp = rconsvp;
|
|
#else SAS
|
|
panic("no keyboard found");
|
|
#endif SAS
|
|
}
|
|
|
|
#ifndef SAS
|
|
static
|
|
mouseconfig(msdev)
|
|
dev_t msdev;
|
|
{
|
|
int e;
|
|
int flushrw = FLUSHRW;
|
|
struct vnode *mousevp;
|
|
struct vnode *consmousevp;
|
|
struct file *fp;
|
|
int fd;
|
|
|
|
/* Open the mouse device. */
|
|
mousevp = makespecvp(msdev, VCHR);
|
|
if (e = VOP_OPEN(&mousevp, FREAD|FNOCTTY, u.u_cred))
|
|
return (e);
|
|
strioctl(mousevp, I_FLUSH, (caddr_t) &flushrw, FREAD);
|
|
u.u_error = e = 0; /* Just In Case */
|
|
|
|
if (!ldreplace(mousevp, mousevp->v_stream, MOUSELDISC))
|
|
e = EINVAL;
|
|
if (e != 0) {
|
|
(void) VOP_CLOSE(mousevp, FREAD|FWRITE, 1, u.u_cred);
|
|
return (e);
|
|
}
|
|
|
|
/*
|
|
* Open the "console mouse" device, and link the mouse device under it.
|
|
* XXX - there MUST be a better way to do this!
|
|
*/
|
|
|
|
consmousevp = makespecvp(makedev(CONSMOUSEMAJOR, 0), VCHR);
|
|
if (e = VOP_OPEN(&consmousevp, FREAD|FWRITE|FNOCTTY, u.u_cred)) {
|
|
(void) VOP_CLOSE(mousevp, FREAD, 1, u.u_cred);
|
|
return (e);
|
|
}
|
|
if ((fp = falloc()) == NULL) {
|
|
(void) VOP_CLOSE(consmousevp, FREAD|FWRITE, 1, u.u_cred);
|
|
(void) VOP_CLOSE(mousevp, FREAD, 1, u.u_cred);
|
|
return (e = EINVAL);
|
|
}
|
|
fd = u.u_r.r_val1; /* XXX - get FD number */
|
|
fp->f_flag = FREAD;
|
|
fp->f_type = DTYPE_VNODE;
|
|
fp->f_data = (caddr_t) mousevp;
|
|
fp->f_ops = &vnodefops;
|
|
strioctl(consmousevp, I_PLINK, (caddr_t) &fd, FREAD);
|
|
if ((e = u.u_error) != 0) {
|
|
(void) VOP_CLOSE(consmousevp, FREAD|FWRITE, 1, u.u_cred);
|
|
(void) VOP_CLOSE(mousevp, FREAD, 1, u.u_cred);
|
|
return (e);
|
|
}
|
|
u.u_ofile[fd] = NULL;
|
|
|
|
/* don't need this any more */
|
|
closef(fp);
|
|
|
|
/* we're done with this, as well */
|
|
(void) VOP_CLOSE(consmousevp, FREAD|FWRITE, 1, u.u_cred);
|
|
|
|
VN_RELE(consmousevp);
|
|
|
|
return (e);
|
|
}
|
|
|
|
/*
|
|
* Look for the [last] kbd/ms and default frame buffer.
|
|
*/
|
|
findcons(dev, p)
|
|
struct dev_info *dev;
|
|
struct consinfo *p;
|
|
{
|
|
struct dev_info *fbdevi;
|
|
dev_t finddev();
|
|
static dev_t fbdevno = NODEV;
|
|
extern char *prom_stdoutpath();
|
|
extern struct dev_info *path_to_devi();
|
|
|
|
/* options doesn't necessarily have a driver */
|
|
if (strcmp(dev->devi_name, "options") == 0) {
|
|
p->options = dev;
|
|
/* assume can't also be a frame buffer or zs */
|
|
return;
|
|
}
|
|
|
|
if (dev->devi_driver == NULL)
|
|
return;
|
|
|
|
if (strncmp(dev->devi_name, "zs", 2) == 0) {
|
|
/*
|
|
* production proms use "keyboard" property.
|
|
* XXX pre-production proms use "flags".
|
|
* XXX kernel must accept either, at least until FCS.
|
|
*/
|
|
if (getprop(dev->devi_nodeid, "keyboard", 0) ||
|
|
(getprop(dev->devi_nodeid, "flags", 0) & ZS_KBDMS)) {
|
|
p->kmunit = dev->devi_unit;
|
|
}
|
|
/* assume that serial ports are not frame buffers */
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* It is possible for "path_to_devi" to return to us
|
|
* a reference to a node that has no driver. If this
|
|
* happens, finddev() will return NODEV, and things
|
|
* will at least continue to function.
|
|
*/
|
|
if ((fbdevno == NODEV) && (prom_stdout_is_framebuffer()))
|
|
if ((fbdevi = path_to_devi(prom_stdoutpath())) != 0)
|
|
fbdevno = finddev('c', fbdevi);
|
|
if (fbdevno != NODEV)
|
|
p->fbdev = fbdevno;
|
|
}
|
|
#endif SAS
|
|
|
|
/* convert dev_info pointer to device number */
|
|
dev_t
|
|
finddev(type, info)
|
|
int type;
|
|
struct dev_info *info;
|
|
{
|
|
int majnum = -1;
|
|
struct dev_ops *driver;
|
|
int (*openfun)();
|
|
|
|
if (info == (struct dev_info *)0)
|
|
return NODEV;
|
|
|
|
driver = info->devi_driver;
|
|
if (driver == (struct dev_ops *)0)
|
|
return NODEV;
|
|
|
|
openfun = driver->devo_open;
|
|
if (openfun == (int (*)())0)
|
|
return NODEV;
|
|
|
|
if (type == 'b') {
|
|
#ifdef notdef /* no one is using this at the moment */
|
|
struct bdevsw *swp = bdevsw;
|
|
|
|
for (majnum = 0; majnum < nblkdev; majnum++, swp++)
|
|
if (swp->d_open == openfun)
|
|
return (makedev(majnum, info->devi_unit));
|
|
#endif
|
|
} else {
|
|
struct cdevsw *swp = cdevsw;
|
|
|
|
for (majnum = 0; majnum < nchrdev; majnum++, swp++)
|
|
if (swp->d_open == openfun)
|
|
return (makedev(majnum, info->devi_unit));
|
|
}
|
|
|
|
return (NODEV);
|
|
}
|
|
|
|
int keyclick;
|
|
|
|
set_keyclick(dev)
|
|
struct dev_info *dev;
|
|
{
|
|
addr_t key_click;
|
|
int opt_nodeid;
|
|
static char key_click_name[] = "keyboard-click?";
|
|
int size;
|
|
|
|
if (dev == NULL)
|
|
return;
|
|
opt_nodeid = dev->devi_nodeid;
|
|
size = getproplen(opt_nodeid, key_click_name);
|
|
if (size <= 0) {
|
|
printf("No \"%s\" property\n", key_click_name);
|
|
return;
|
|
}
|
|
|
|
key_click = getlongprop(opt_nodeid, key_click_name);
|
|
if (key_click) {
|
|
if (strcmp(key_click, "true") == 0)
|
|
keyclick = 1;
|
|
kmem_free(key_click, (u_int)size);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Some things, like cputype, are contained in the idprom, but are
|
|
* needed and obtained earlier; hence they are not set (again) here.
|
|
*/
|
|
idprom()
|
|
{
|
|
register u_char *cp, val = 0;
|
|
register int i;
|
|
struct idprom id;
|
|
|
|
getidprom((char *)&id);
|
|
cp = (u_char *)&id;
|
|
for (i = 0; i < 16; i++)
|
|
val ^= *cp++;
|
|
if (val != 0)
|
|
printf("Warning: NVRAM checksum error [%x]\n", val);
|
|
if (id.id_format == 1) {
|
|
(void) localetheraddr((struct ether_addr *)id.id_ether,
|
|
(struct ether_addr *)NULL);
|
|
} else
|
|
printf("Invalid format code (%d) in NVRAM\n", id.id_format);
|
|
}
|
|
|
|
#ifdef NOPROM
|
|
char *cpu_str = 0;
|
|
char *mod_str = 0;
|
|
extern int module_type;
|
|
|
|
/*
|
|
* We set the cpu type from the idprom machine ID.
|
|
* XXX CPU name should be PROM property.
|
|
*/
|
|
setcputype()
|
|
{
|
|
struct idprom id;
|
|
|
|
cpu = -1; /* not possible value for id.id_machine */
|
|
getidprom((char *)&id);
|
|
|
|
if (id.id_format == 1)
|
|
cpu = id.id_machine;
|
|
else
|
|
printf("Invalid format type (%d) in NVRAM\n", id.id_format);
|
|
|
|
/*
|
|
* This is just for pretty printing for now.
|
|
*/
|
|
switch (module_type) {
|
|
case ROSS604:
|
|
mod_str = "ross604";
|
|
break;
|
|
case ROSS605:
|
|
mod_str = "ross605";
|
|
break;
|
|
case ROSS605_D:
|
|
mod_str = "ross605d";
|
|
break;
|
|
case VIKING:
|
|
mod_str = "viking";
|
|
break;
|
|
case VIKING_M:
|
|
mod_str = "viking+mxcc";
|
|
break;
|
|
case SSPARC:
|
|
mod_str = "ssparc";
|
|
break;
|
|
default:
|
|
mod_str = "unknown";
|
|
break;
|
|
}
|
|
|
|
switch(cpu) {
|
|
default:
|
|
case -1:
|
|
printf("machine type 0x%x in NVRAM\n", cpu);
|
|
panic("No known machine types configured in!\n");
|
|
|
|
case CPU_SUN4M_690:
|
|
cpu_str = "sun-4m/690";
|
|
break;
|
|
|
|
case CPU_SUN4M_50: /* campus2 */
|
|
cpu_str = "sun-4m/50";
|
|
break;
|
|
|
|
#ifdef SUN4M_35
|
|
case CPU_SUN4M_35: /* SPARCclassic & SPARCstation-LX */
|
|
cpu_str = "sun-4m/35";
|
|
break;
|
|
#endif SUN4M_35
|
|
|
|
}
|
|
}
|
|
#else NOPROM
|
|
setcputype()
|
|
{
|
|
struct idprom id;
|
|
cpu = -1; /* not possible value for id.id_machine */
|
|
getidprom((char *)&id);
|
|
|
|
if (id.id_format == 1)
|
|
cpu = id.id_machine;
|
|
else
|
|
printf("Invalid format type (%d) in NVRAM\n", id.id_format);
|
|
|
|
switch(cpu) {
|
|
default:
|
|
case -1:
|
|
printf("machine type 0x%x in NVRAM\n", cpu);
|
|
panic("No known machine types configured in!\n");
|
|
|
|
case CPU_SUN4M_690:
|
|
case CPU_SUN4M_50: /* campus2 */
|
|
#if defined(SUN4M_35)
|
|
case CPU_SUN4M_35:
|
|
#endif
|
|
break;
|
|
}
|
|
mach_info.sys_type = cpu;
|
|
}
|
|
#endif NOPROM
|
|
|
|
machineid()
|
|
{
|
|
struct idprom id;
|
|
register int x;
|
|
|
|
getidprom((char *)&id);
|
|
x = id.id_machine << 24;
|
|
x += id.id_serial;
|
|
return (x);
|
|
}
|
|
|
|
/*
|
|
* Initialize pseudo-devices.
|
|
* Reads count and init routine from table in ioconf.c
|
|
* created by 'init blah' syntax on pseudo-device line in config file.
|
|
* Calls init routine once for each unit configured
|
|
*/
|
|
|
|
extern struct pseudo_init {
|
|
int ps_count;
|
|
int (*ps_func)();
|
|
} pseudo_inits[];
|
|
|
|
pseudoconfig()
|
|
{
|
|
register struct pseudo_init *ps;
|
|
int unit;
|
|
|
|
for (ps = pseudo_inits; ps->ps_count > 0; ps++)
|
|
for (unit = 0; unit < ps->ps_count; unit++)
|
|
(*ps->ps_func)(unit);
|
|
}
|
|
|
|
extern unsigned sbus_numslots;
|
|
extern unsigned sbus_basepage[];
|
|
extern unsigned sbus_slotsize[];
|
|
|
|
/*
|
|
* a word about the INRANGE macro:
|
|
* If addr < size, then the difference becomes a huge unsigned
|
|
* number, which will compare true with the range if and only
|
|
* if the size is big enough to wrap around across the end of
|
|
* the number system.
|
|
*
|
|
* If addr > size, then we see if it is within the limit, which
|
|
* is one *less* than the size -- this way, a size of zero becomes
|
|
* a limit of 0xFFFFFFFF, corresponding to 4gig of space.
|
|
*
|
|
* Some simpler ways of doing this barf when the range ends at zero,
|
|
* or when the size is 4gig, or when the range wraps across zero, or
|
|
* when the range wraps across 0x80000000. Think carefully before you
|
|
* change this macro.
|
|
*
|
|
* With all that work, maybe the macro should be used elsewhere!
|
|
*/
|
|
#define UNS(x) ((unsigned)(x))
|
|
#define INRANGE(addr, base, size) \
|
|
(UNS(UNS(addr) - UNS(base)) <= UNS(UNS(size)-1))
|
|
/*
|
|
* INPAGE uses INRANGE to decide if an address falls inside
|
|
* an area described by a page number and a size in bytes.
|
|
* Note that page information above PA31 are lost, so space
|
|
* comparisons (if any) must be done elsewhere.
|
|
*/
|
|
#define INPAGE(addr, page, bytes) \
|
|
INRANGE(addr, mmu_ptob(page), bytes)
|
|
|
|
/*
|
|
* INSBUSSLOT uses the INPAGE macro and the sbus layout information
|
|
* from sbus_basepage and sbus_slotsize to decide if a physical
|
|
* address is in a specific sbus slot. Since the physical address
|
|
* is given only as a 32-bit number, the remainder of the PA must
|
|
* be checked elsewhere.
|
|
*/
|
|
#define INSBUSSLOT(addr, slot) \
|
|
INPAGE(addr, sbus_basepage[slot], sbus_slotsize[slot])
|
|
|
|
/*
|
|
* sbusslot returns the slot number for a given
|
|
* physical address. Since only the low 32 bits
|
|
* of the slot are given, that's all we check.
|
|
*/
|
|
int
|
|
sbusslot(addr)
|
|
unsigned addr;
|
|
{
|
|
int slot;
|
|
|
|
for (slot=0; slot<sbus_numslots; ++slot)
|
|
if (INSBUSSLOT(addr, slot))
|
|
return slot;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Return slot number if given physical address corresponds to
|
|
* slave-only SBus slot, e.g. 4/60 slot 3; return -1 if OK.
|
|
*
|
|
* NOTE: this is only the low 32 bits of the physaddr, so the
|
|
* assumption is made that (a) the address is somewhere in
|
|
* sbus space, and (b) the low 32 bits of the physical address
|
|
* of two sbus slots do not overlap.
|
|
*/
|
|
|
|
int
|
|
slaveslot(addr)
|
|
addr_t addr;
|
|
{
|
|
int slot;
|
|
unsigned slaves;
|
|
|
|
slaves = getprop(0, "slave-only", 0);
|
|
if (slaves) {
|
|
slot = sbusslot((u_int)addr);
|
|
if (slot != -1)
|
|
return (slaves & (1 << slot)) ? slot : -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Find devices on the Mainbus.
|
|
*
|
|
* Note: This is no longer called to find SBus devices.
|
|
*
|
|
* Uses per-driver routine to probe for existence of the device
|
|
* and then fills in the tables, with help from a per-driver
|
|
* slave initialization routine.
|
|
*/
|
|
mbconfig()
|
|
{
|
|
register struct mb_device *md;
|
|
register struct mb_ctlr *mc;
|
|
struct mb_driver *mdr;
|
|
|
|
/*
|
|
* Grab some memory to record the Mainbus address space in use,
|
|
* so we can be sure not to place two devices at the same address.
|
|
* If we run out of kernelmap space, we could reuse the mapped
|
|
* pages if we did all probes first to determine the target
|
|
* locations and sizes, and then remucked with the kernelmap to
|
|
* share spaces, then did all the attaches.
|
|
*
|
|
* We could use just 1/8 of this (we only want a 1 bit flag) but
|
|
* we are going to give it back anyway, and that would make the
|
|
* code here bigger (which we can't give back), so ...
|
|
*/
|
|
|
|
/*
|
|
* Check each Mainbus mass storage controller.
|
|
* See if it is really there, and if it is record it and
|
|
* then go looking for slaves.
|
|
*/
|
|
for (mc = mbcinit; mdr = mc->mc_driver; ++mc)
|
|
(void)mbconfctrl(mc, mdr);
|
|
|
|
|
|
/*
|
|
* Now look for non-mass storage peripherals.
|
|
*/
|
|
|
|
for (md = mbdinit; mdr = md->md_driver; ++md)
|
|
{
|
|
if (md->md_alive || md->md_slave != -1)
|
|
continue;
|
|
(void)mbconfnonms(md, mdr); /* config non-mass storage device */
|
|
}
|
|
} /* end of mbconfig */
|
|
|
|
#ifdef VME
|
|
/* The routine to fill in the dev_info struct for VME devices
|
|
* If configuring a controller the mb_device pointer will be
|
|
* NULL, and vice-versa.
|
|
* Common routine for mbconfctrl and mbconfnonms.
|
|
* Using names passed from driver structures now, this will change.
|
|
*/
|
|
|
|
static void
|
|
vme_fill_devinfo(mc, md, mdr, dev)
|
|
register struct mb_ctlr *mc;
|
|
register struct mb_device *md;
|
|
struct mb_driver *mdr;
|
|
struct dev_info *dev;
|
|
{
|
|
u_int space;
|
|
|
|
/*
|
|
* Allocate space for reg and intr structures
|
|
* Dont have the luxury of calling getlongprop()
|
|
*/
|
|
dev->devi_reg = (struct dev_reg *)kmem_zalloc(sizeof(*dev->devi_reg));
|
|
dev->devi_intr = (struct dev_intr *)kmem_zalloc(sizeof(*dev->devi_intr));
|
|
/* %%% following may not always be true, but for now... */
|
|
dev->devi_nreg = 1;
|
|
dev->devi_nintr = 1;
|
|
/* Having a non-NULL devi_driver entry is necessary for boot */
|
|
dev->devi_driver = &pseudo_ops;
|
|
|
|
if(mc) { /* configuring a controller */
|
|
dev->devi_name = mdr->mdr_cname;
|
|
dev->devi_reg->reg_addr = mc->mc_addr;
|
|
dev->devi_reg->reg_size = mdr->mdr_size;
|
|
space = mc->mc_space;
|
|
dev->devi_unit = mc->mc_ctlr;
|
|
dev->devi_intr->int_pri = mc->mc_intpri;
|
|
if (mc->mc_intr) /* %%% for IPI, idc doesnt intr */
|
|
dev->devi_intr->int_vec = mc->mc_intr->v_vec;
|
|
} else if (md) { /* configuring a device */
|
|
dev->devi_name = mdr->mdr_dname;
|
|
dev->devi_reg->reg_addr = md->md_addr;
|
|
dev->devi_reg->reg_size = mdr->mdr_size;
|
|
space = md->md_space;
|
|
dev->devi_unit = md->md_unit;
|
|
dev->devi_intr->int_pri = md->md_intpri;
|
|
if (md->md_intr)
|
|
dev->devi_intr->int_vec = md->md_intr->v_vec;
|
|
} else { /* should never happen */
|
|
printf("vme_fill_devinfo: specified no controller/device\n");
|
|
}
|
|
|
|
/* To get bustype from the space field */
|
|
switch (space & SP_BUSMASK) {
|
|
|
|
case SP_VME16D16:
|
|
case SP_VME24D16:
|
|
case SP_VME32D16:
|
|
dev->devi_reg->reg_bustype = 0xC;
|
|
break;
|
|
|
|
case SP_VME16D32:
|
|
case SP_VME24D32:
|
|
case SP_VME32D32:
|
|
dev->devi_reg->reg_bustype = 0xD;
|
|
break;
|
|
|
|
default: /* This is where IPI falls */
|
|
dev->devi_reg->reg_bustype = 0x0;
|
|
}
|
|
|
|
} /* End of vme_fill_devinfo() */
|
|
#endif VME
|
|
/*
|
|
* Configure a controller
|
|
*/
|
|
|
|
mbconfctrl(mc, mdr)
|
|
register struct mb_ctlr *mc;
|
|
struct mb_driver *mdr;
|
|
{
|
|
struct mb_device *md;
|
|
u_short *reg;
|
|
int err = 0;
|
|
int stat;
|
|
u_short *doprobe();
|
|
struct dev_info *dev = NULL;
|
|
|
|
if ((reg = doprobe((u_long)mc->mc_addr, (u_long)mc->mc_space,
|
|
mdr, mdr->mdr_cname, mc->mc_ctlr, mc->mc_intpri,
|
|
mc->mc_intr)) == 0)
|
|
return (-1);
|
|
|
|
#ifdef VME
|
|
|
|
#if defined(SUN4M_35)
|
|
if (no_vme)
|
|
return (-1);
|
|
#endif
|
|
|
|
/* Check if already have controller listed in dev_info tree */
|
|
if (!vme_check_dupdev(vme_devinfo, mdr->mdr_cname, mc->mc_ctlr)) {
|
|
|
|
/*
|
|
* Do the work of adding device to dev_info tree
|
|
*/
|
|
dev = (struct dev_info *)kmem_zalloc(sizeof (*dev));
|
|
append_dev(vme_devinfo, dev);
|
|
|
|
/* no nodeid to add, since not working with the PROM */
|
|
|
|
dev->devi_parent = vme_devinfo;
|
|
vme_fill_devinfo(mc, (struct mb_device *) NULL, mdr, dev);
|
|
|
|
/* no devops structure since not an sbus/fcode driver */
|
|
}
|
|
|
|
if ((mdr->mdr_flags & (MDR_BIODMA | MDR_DMA)) != 0)
|
|
adddma(mc->mc_intpri);
|
|
|
|
mc->mc_alive = 1;
|
|
mc->mc_mh = &mb_hd;
|
|
mc->mc_addr = (caddr_t)reg;
|
|
if (mdr->mdr_cinfo)
|
|
mdr->mdr_cinfo[mc->mc_ctlr] = mc;
|
|
for (md = mbdinit; md->md_driver; ++md) {
|
|
/*
|
|
* Dont check for alive here, so can run disks thru the
|
|
* fill_devinfo code in mbconfdev
|
|
*/
|
|
if (md->md_driver != mdr || md->md_ctlr != mc->mc_ctlr &&
|
|
md->md_ctlr != '?')
|
|
continue;
|
|
stat = mbconfdev(md, mc, mdr, dev); /* configure the device */
|
|
if (err == 0)
|
|
err = stat;
|
|
}
|
|
return (err);
|
|
#else VME
|
|
return (-1);
|
|
#endif VME
|
|
|
|
} /* end of mbconfctrl */
|
|
|
|
#ifdef VME
|
|
add_ipi_drives(md, mdr)
|
|
register struct mb_device *md;
|
|
struct mb_driver *mdr;
|
|
{
|
|
struct dev_info *pd, *id;
|
|
|
|
if (!found_vme)
|
|
return (0);
|
|
#ifdef lint
|
|
mdr = mdr;
|
|
#endif lint
|
|
#ifdef IPI_DEBUG
|
|
printf("Node %s unit %d at %s%d\n", mdr->mdr_dname, md->md_unit,
|
|
mdr->mdr_cname, md->md_ctlr);
|
|
#endif IPI_DEBUG
|
|
for (pd = vme_devinfo->devi_slaves; pd != NULL; pd = pd->devi_next) {
|
|
if (pn_identify(pd->devi_name)) {
|
|
for (id = pd->devi_slaves; id != NULL;
|
|
id = id->devi_next) {
|
|
if ((ipi3sc_identify(id->devi_name)) &&
|
|
(md->md_ctlr == id->devi_unit))
|
|
(void) add_ipi_drive(id, md->md_unit);
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
add_ipi_drive(devi, unit)
|
|
struct dev_info *devi;
|
|
short unit;
|
|
{
|
|
register struct dev_info *dp, *ndp;
|
|
|
|
#ifdef IPI_DEBUG
|
|
printf("Parent Node %s%d\n", devi->devi_name, devi->devi_unit);
|
|
#endif IPI_DEBUG
|
|
dp = (struct dev_info *)kmem_zalloc(sizeof (struct dev_info));
|
|
dp->devi_name = "id";
|
|
dp->devi_unit = unit;
|
|
dp->devi_nodeid = -1;
|
|
dp->devi_driver = match_driver(dp->devi_name);
|
|
dp->devi_parent = devi;
|
|
|
|
if (devi->devi_slaves == NULL) {
|
|
devi->devi_slaves = dp;
|
|
} else {
|
|
for (ndp = devi->devi_slaves; ndp != NULL;
|
|
ndp = ndp->devi_next)
|
|
if (ndp->devi_next == NULL) {
|
|
ndp->devi_next = dp;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif VME
|
|
|
|
/*
|
|
* Configure a mass storage device.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Added "ctrl_dev" as argument passwd to this routine in order
|
|
* to add this device as a child to it controller in dev_info tree.
|
|
*/
|
|
mbconfdev(md, mc, mdr, ctrl_dev)
|
|
register struct mb_device *md;
|
|
register struct mb_ctlr *mc;
|
|
struct mb_driver *mdr;
|
|
struct dev_info *ctrl_dev;
|
|
{
|
|
struct dev_info *dev_dev;
|
|
|
|
if (((*mdr->mdr_slave)(md, mc->mc_addr)) && !(md->md_alive)) {
|
|
md->md_alive = 1;
|
|
md->md_ctlr = mc->mc_ctlr;
|
|
md->md_hd = &mb_hd;
|
|
md->md_addr = (caddr_t)mc->mc_addr;
|
|
if (md->md_dk && dkn < DK_NDRIVE)
|
|
md->md_dk = dkn++;
|
|
else md->md_dk = -1;
|
|
md->md_mc = mc;
|
|
/* md_type comes from driver */
|
|
if (mdr->mdr_dinfo)
|
|
mdr->mdr_dinfo[md->md_unit] = md;
|
|
printf("%s%d at %s%d slave %d\n",
|
|
mdr->mdr_dname, md->md_unit, mdr->mdr_cname, mc->mc_ctlr, md->md_slave);
|
|
|
|
/*
|
|
* If we were passed a ctrl_dev, add its children to the tree.
|
|
*/
|
|
if (ctrl_dev) {
|
|
|
|
#ifdef VME
|
|
/*
|
|
* Check if already have device listed in dev_info tree
|
|
*/
|
|
if (!vme_check_dupdev(ctrl_dev, mdr->mdr_dname, md->md_unit)) {
|
|
|
|
/*
|
|
* Add the device to dev_info tree as a child of its controller.
|
|
*/
|
|
dev_dev = (struct dev_info *)kmem_zalloc(sizeof (*dev_dev));
|
|
append_dev(ctrl_dev, dev_dev);
|
|
|
|
/* no nodeid */
|
|
|
|
dev_dev->devi_parent = ctrl_dev;
|
|
vme_fill_devinfo(( struct mb_ctlr *) NULL, md, mdr, dev_dev);
|
|
|
|
/* no devops structure */
|
|
}
|
|
#endif VME
|
|
}
|
|
if (mdr->mdr_attach)
|
|
(*mdr->mdr_attach)(md);
|
|
return (0);
|
|
|
|
}
|
|
return (-1);
|
|
} /* end of mbconfdev */
|
|
|
|
|
|
/*
|
|
* Configure a non-mass storage device
|
|
*/
|
|
mbconfnonms(md, mdr)
|
|
register struct mb_device *md;
|
|
register struct mb_driver *mdr;
|
|
{
|
|
u_short *reg;
|
|
struct dev_info *dev;
|
|
|
|
if ((reg = doprobe((u_long)md->md_addr, (u_long)md->md_space,
|
|
mdr, mdr->mdr_dname, md->md_unit, md->md_intpri,
|
|
md->md_intr)) == 0)
|
|
return (-1);
|
|
|
|
#ifdef VME
|
|
#if defined(SUN4M_35)
|
|
if (no_vme)
|
|
return (-1);
|
|
#endif
|
|
/* Check if already have controller listed in dev_info tree */
|
|
if (vme_check_dupdev(vme_devinfo, mdr->mdr_dname, md->md_unit) == 0) {
|
|
|
|
/*
|
|
* Do the work of adding device to VME dev_info tree
|
|
*/
|
|
dev = (struct dev_info *)kmem_zalloc(sizeof (*dev));
|
|
append_dev(vme_devinfo, dev);
|
|
|
|
/* no nodeid to add, since not working with the PROM */
|
|
|
|
dev->devi_parent = vme_devinfo;
|
|
vme_fill_devinfo(( struct mb_ctlr *) NULL, md, mdr, dev);
|
|
|
|
/* no devops structure since not an sbus/fcode driver */
|
|
}
|
|
|
|
if ((mdr->mdr_flags & (MDR_BIODMA | MDR_DMA)) != 0)
|
|
adddma(md->md_intpri);
|
|
|
|
md->md_hd = &mb_hd;
|
|
md->md_alive = 1;
|
|
md->md_addr = (caddr_t)reg;
|
|
md->md_dk = -1;
|
|
/* md_type comes from driver */
|
|
if (mdr->mdr_dinfo)
|
|
mdr->mdr_dinfo[md->md_unit] = md;
|
|
if (mdr->mdr_attach)
|
|
(*mdr->mdr_attach)(md);
|
|
return (0);
|
|
#else VME
|
|
return (-1);
|
|
#endif VME
|
|
} /* end of mbconfnonms */
|
|
|
|
|
|
/*
|
|
* Probe for a device or controller at the specified addr.
|
|
* The space argument give the page type and cpu type for the device.
|
|
*
|
|
* This code is now only for VME devices.
|
|
* *****We can no longer probe for SBus devices.*****
|
|
*/
|
|
u_short *doprobe(addr, space, mdr, dname, unit, br, vp)
|
|
u_long addr;
|
|
u_long space;
|
|
struct mb_driver *mdr;
|
|
char *dname;
|
|
int unit;
|
|
int br;
|
|
register struct vec *vp;
|
|
{
|
|
register u_short *reg = NULL;
|
|
char *name;
|
|
unsigned base = 0;
|
|
int i;
|
|
int is_ipi = 1;
|
|
#ifdef VME
|
|
|
|
#if defined(SUN4M_35)
|
|
if (no_vme)
|
|
return (0);
|
|
#endif
|
|
/*
|
|
* %%% this routine "knows" about the space and base
|
|
* addresses of all the VME mapping areas. Ugh.
|
|
*/
|
|
switch (space & SP_BUSMASK) {
|
|
|
|
case SP_VME16D16:
|
|
name = "vme16d16";
|
|
space = 0xC;
|
|
base = 0xFFFF0000;
|
|
break;
|
|
|
|
case SP_VME16D32:
|
|
name = "vme16d32";
|
|
space = 0xD;
|
|
base = 0xFFFF0000;
|
|
break;
|
|
|
|
case SP_VME24D16:
|
|
name = "vme24d16";
|
|
space = 0xC;
|
|
base = 0xFF000000;
|
|
break;
|
|
|
|
case SP_VME24D32:
|
|
name = "vme24d32";
|
|
space = 0xD;
|
|
base = 0xFF000000;
|
|
break;
|
|
|
|
case SP_VME32D16:
|
|
name = "vme32d16";
|
|
space = 0xC;
|
|
break;
|
|
|
|
case SP_VME32D32:
|
|
name = "vme32d32";
|
|
space = 0xD;
|
|
break;
|
|
|
|
case SP_IPI:
|
|
name = "ipi";
|
|
reg = (u_short *)addr; /* IPI device address */
|
|
br = 0; /* IPI interrupts thru host adaptor */
|
|
break;
|
|
|
|
default:
|
|
return (0);
|
|
}
|
|
|
|
|
|
if (reg == NULL) {
|
|
reg = (u_short *)map_regs((addr_t)(base + addr), (u_int) mdr->mdr_size, (int) space);
|
|
is_ipi = 0;
|
|
}
|
|
i = (*mdr->mdr_probe)(reg, unit);
|
|
if (i == 0) {
|
|
if (!is_ipi) {
|
|
unmap_regs((addr_t) reg, (u_int) mdr->mdr_size);
|
|
}
|
|
return (0);
|
|
}
|
|
printf("%s%d at %s 0x%x ", dname, unit, name, addr);
|
|
if (br < 0 || br >= 7)
|
|
{
|
|
printf("bad priority (%d)\n", br);
|
|
unmap_regs((addr_t) reg, (u_int) mdr->mdr_size);
|
|
return (0);
|
|
}
|
|
|
|
|
|
/*
|
|
* If br is 0, then no priority was specified in the
|
|
* config file and the device cannot use interrupts.
|
|
*/
|
|
if (br != 0)
|
|
{
|
|
/*
|
|
* now set up VME vectored interrupts if conditions are right
|
|
*/
|
|
if (vp != (struct vec *)0)
|
|
{
|
|
for (; vp->v_func; vp++)
|
|
{
|
|
void vme_vecinstall();
|
|
|
|
printf("vec 0x%x ", vp->v_vec);
|
|
vme_vecinstall(vp);
|
|
}
|
|
}
|
|
}
|
|
printf("\n");
|
|
return (reg);
|
|
#else VME
|
|
return (0);
|
|
#endif VME
|
|
}
|
|
|
|
#ifdef VME
|
|
void vme_vecinstall(vp)
|
|
struct vec *vp;
|
|
{
|
|
int vec_num;
|
|
|
|
if (vp->v_vec < VEC_MIN || vp->v_vec > VEC_MAX)
|
|
panic("vme_vecinstall: bad vector");
|
|
|
|
vec_num = vp->v_vec - VEC_MIN;
|
|
if (vme_vector[vec_num].func != 0)
|
|
panic("vme_vecinstall: vector in use");
|
|
vme_vector[vec_num].func = vp->v_func;
|
|
vme_vector[vec_num].arg = vp->v_vptr;
|
|
} /* end of vme_vecinstall */
|
|
#endif VME
|