V9/sys/sundev/mb.c
#ifndef lint
static char sccsid[] = "@(#)mb.c 1.1 86/02/03 Copyr 1983 Sun Micro";
#endif
/*
* Copyright (c) 1985 by Sun Microsystems, Inc.
*/
/*
* Mainbus support routines.
*/
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/vmmac.h"
#include "../h/vmmeter.h"
#include "../h/vmparam.h"
#include "../h/map.h"
#include "../h/buf.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/conf.h"
/*#include "../h/kernel.h"*/
#include "../machine/mmu.h"
#include "../machine/cpu.h"
#include "../machine/psl.h"
#include "../machine/pte.h"
#include "../machine/reg.h"
#include "../sundev/mbvar.h"
/*
* Do transfer on controller argument.
* We queue for resource wait in the Mainbus code if necessary.
* We return 1 if the transfer was started, 0 if it was not.
* If you call this routine with the head of the queue for a
* Mainbus, it will automatically remove the controller from the Mainbus
* queue before it returns. If some other controller is given
* as argument, it will be added to the request queue if the
* request cannot be started immediately. This means that
* passing a controller which is on the queue but not at the head
* of the request queue is likely to be a disaster.
*/
mbgo(mc)
register struct mb_ctlr *mc;
{
register struct mb_hd *mh = mc->mc_mh;
register struct buf *bp = mc->mc_tab.b_actf->b_actf;
register int s;
s = splx(pritospl(SPLMB));
if ((mc->mc_driver->mdr_flags & MDR_XCLU) &&
mh->mh_users > 0 || mh->mh_xclu)
goto rwait;
mc->mc_mbinfo = mbsetup(mh, bp, MB_CANTWAIT);
if (mc->mc_mbinfo == 0)
goto rwait;
mh->mh_users++;
if (mc->mc_driver->mdr_flags & MDR_XCLU)
mh->mh_xclu = 1;
(void) splx(s);
if (mh->mh_actf == mc)
mh->mh_actf = mc->mc_forw;
if ((mc->mc_driver->mdr_flags & MDR_SWAB) && (bp->b_flags&B_READ)==0)
swab(bp->b_un.b_addr, bp->b_un.b_addr, (int)bp->b_bcount);
(*mc->mc_driver->mdr_go)(mc);
return (1);
rwait:
if (mh->mh_actf != mc) {
mc->mc_forw = NULL;
if (mh->mh_actf == NULL)
mh->mh_actf = mc;
else
mh->mh_actl->mc_forw = mc;
mh->mh_actl = mc;
}
(void) splx(s);
return (0);
}
mbdone(mc)
register struct mb_ctlr *mc;
{
register struct mb_hd *mh = mc->mc_mh;
register struct buf *bp = mc->mc_tab.b_actf->b_actf;
if (mc->mc_driver->mdr_flags & MDR_XCLU)
mh->mh_xclu = 0;
mh->mh_users--;
mbrelse(mh, &mc->mc_mbinfo);
if (mc->mc_driver->mdr_flags & MDR_SWAB)
swab(bp->b_un.b_addr, bp->b_un.b_addr, (int)bp->b_bcount);
(*mc->mc_driver->mdr_done)(mc);
}
/*
* Allocate and setup Mainbus map registers.
* Flags says whether the caller can't
* wait (e.g. if the caller is at interrupt level).
*
* We also allow DMA to memory already mapped at the Mainbus
* (e.g., for Sun Ethernet board memory) and denote this with
* a zero in the MBI_NMR field.
*/
mbsetup(mh, bp, flags)
register struct mb_hd *mh;
struct buf *bp;
int flags;
{
int npf, reg;
register struct pte *pte;
register char *addr;
int s, o;
struct mbcookie mbcookie;
#ifdef sun2
int uc;
#endif
o = (int)bp->b_un.b_addr & PGOFSET;
if ((bp->b_flags & B_PHYS) == 0)
pte = &Sysmap[btop((int)bp->b_un.b_addr - KERNELBASE)];
else {
if (bp->b_kmx == 0)
panic("mbsetup: zero kmx");
pte = &Usrptmap[bp->b_kmx];
}
npf = btoc(bp->b_bcount + o);
if (buscheck(pte, npf)) {
mbcookie.mbi_mapreg = pte->pg_pfnum;
mbcookie.mbi_offset = o;
return (*(int *)&mbcookie);
}
/* Defensively invalidate the page following the allocation */
npf++;
s = splx(pritospl(SPLMB));
while ((reg = (int)rmalloc(mh->mh_map, (long)npf)) == 0) {
if (flags & MB_CANTWAIT) {
(void) splx(s);
return (0);
}
mh->mh_mrwant++;
sleep((caddr_t)&mh->mh_mrwant, PSWP);
}
(void) splx(s);
mbcookie.mbi_mapreg = reg;
mbcookie.mbi_offset = o;
addr = &DVMA[ctob(reg)];
#ifdef sun2
uc = getusercontext();
setusercontext(KCONTEXT);
#endif
while (--npf > 0) {
register int pfnum;
switch (*(int *)pte & PGT_MASK) {
default:
/* may not go from dvma back out to the bus */
panic("mbsetup: bad PGT");
case PGT_OBMEM:
case PGT_OBIO:
if ((pfnum = pte->pg_pfnum) == 0)
panic("mbsetup: zero pfnum");
setpgmap(addr, (long)(PG_V | PG_KW | pfnum));
addr += NBPG;
pte++;
}
}
setpgmap(addr, (long)0);
#ifdef sun2
setusercontext(uc);
#endif
return (*(int *)&mbcookie);
}
/*
* Non buffer setup interface... set up a buffer and call mbsetup.
*/
mballoc(mh, addr, bcnt, flags)
struct mb_hd *mh;
caddr_t addr;
int bcnt, flags;
{
struct buf mbbuf;
mbbuf.b_un.b_addr = addr;
mbbuf.b_flags = B_BUSY;
mbbuf.b_bcount = bcnt;
/* that's all the fields mbsetup() needs */
return (mbsetup(mh, &mbbuf, flags));
}
/*
* Release resources on Mainbus, and then unblock resource waiters.
* The map register parameter is by value since we need to block
* against Mainbus resets.
*/
mbrelse(mh, amr)
register struct mb_hd *mh;
int *amr;
{
register int reg, s;
register char *addr;
int mr;
#ifdef sun2
int uc;
#endif
/*
* Carefully see if we should release the space, since
* it may be released asynchronously at Mainbus reset time.
*/
s = splx(pritospl(SPLMB));
mr = *amr;
if (mr == 0) {
printf("mbrelse: MR == 0!!!\n");
(void) splx(s);
return;
}
*amr = 0;
(void) splx(s); /* let interrupts in, we're safe for a while */
if ((reg = MBI_MR(mr)) < dvmasize) { /* DVMA memory */
long getpgmap();
register int npf = 1; /* plus one for last entry */
#ifdef sun2
uc = getusercontext();
setusercontext(KCONTEXT);
#endif
for (addr = &DVMA[ctob(reg)]; getpgmap(addr) != (long)0;
addr += NBPG, npf++)
setpgmap(addr, (long)0);
#ifdef sun2
setusercontext(uc);
#endif
/*
* Put back the registers in the resource map.
* The map code must not be reentered, so we do this
* at high spl.
*/
s = splx(pritospl(SPLMB));
rmfree(mh->mh_map, (long)npf, (long)reg);
(void) splx(s);
/*
* Wakeup sleepers for map registers,
* and also, if there are processes blocked in mbgo(),
* give them a chance at the Mainbus.
*/
if (mh->mh_mrwant) {
mh->mh_mrwant = 0;
wakeup((caddr_t)&mh->mh_mrwant);
}
}
while (mh->mh_actf && mbgo(mh->mh_actf))
;
}
/*
* Swap bytes in 16-bit [half-]words
* for going between the 11 and the interdata
*/
swab(pf, pt, n)
register caddr_t pf, pt;
register int n;
{
register char temp;
n = (n+1)>>1;
while (--n >= 0) {
temp = *pf++;
*pt++ = *pf++;
*pt++ = temp;
}
}