rminnichmerge57758a716 years agoHistory
442 lines · 7.8 KB · C
RawDownload
/* * * Copyright (C) 2007-2009, IBM Corporation,  *                     Eric Van Hensbergen (bergevan@us.ibm.com) * * Description: global interrupts and barriers * * All rights reserved * */ #include	"u.h"#include	"../port/lib.h"#include	"mem.h"#include	"dat.h"#include	"fns.h"#include	"../port/error.h" #include	"io.h" enum{	/* shift for 4-bit fields in `read data' */	Rsticky_s=	28,		/* mode register (1=sticky) */	Rstatus_s=	24,		/* sticky interrupt status (1=true) */	Rarmed_s=	20,		/* ARM active (1=armed) */	Rand_s=		16,		/* ARM type (0=OR, 1=AND) */	Rdiag=	1<<15,		/* 0=normal mode */	Rpu0err=	1<<14,		/* gi_pu0_add_err */	Rpu1err=	1<<13,		/* gi_pu1_add_err */	Rparity=	1<<12,		/* gi_parity_err */	Rglobin_s=	8,		/* gi_ch_glob_int_in_r[0:3] */	Ruser_s=		4,		/* gi_user_enables[0:3] when gi_read_mode=0, otherwise out[0:3] */	Rgloben_s=	0,		/* gi_ch_glob_int_out_en[0:3] */}; enum{	GibDCR=	0x660,		/* base DCR number */	Wstatus=		0x0,		/* read for status register (in fact any offset will do) */						/* offsets to writable registers */	Wenable0=	0x0,		/* set global in/out enable */	Wenable1=	0x1,	Wenable2=	0x2,	Wenable3=	0x3,	Wsetmode=	0x4,		/* 1=diagnostic read mode; 0=normal */	Wreset=		0x5,		/* low order 4 bits are resets for 0 to 3 (L to R) */	Wuser=		0x6,		/* low order 4 bits enable user access for 0 to 3 (L to R) */	/* 7 is reserved */	Wset0=		0x8,		/* 1= send interrupt using ARM type; 0=clear sticky int, clear armed */	Wset1=		0x9,	Wset2=		0xA,	Wset3=		0xB,	Warm0=		0xC,		/* 0=or; 1=and */	Warm1=		0xD,	Warm2=		0xE,	Warm3=		0xF,}; typedef struct GIport GIport;struct GIport {	Lock;	int		init;	u32int	set;	u32int	arm;	u32int	intrmask;	u32int	enable; 	int	n;			/* 0 to 3 */	uchar	mode;	/* None, Intr, Barrier */	uchar	sig;		/* had an interrupt */	uchar	kernel;	/* kernel owns this one */	QLock	rl;		/* protects r */	Rendez	r;		/* wait for interrupt or barrier */}; enum{	/* current mode */	None= 0,	Intr,	Barrier,}; static voidginit(GIport *p, int n){	ilock(p);	if(p->init == 0){		p->set = GibDCR + Wset0 + n;		p->arm = GibDCR + Warm0 + n;		p->enable = GibDCR + Wenable0 + n;		p->n = n;		p->intrmask = (0x8 >> n) << Rstatus_s;		p->mode = None;		p->sig = 0;		p->kernel = 0;		p->init = 1;	}	iunlock(p);} /* * follow the procedures on p. 9 of ``BG/L Global Interrupts'' */ static voidclearandarm(GIport *p, int inv){	ilock(p);	p->sig = 0;	dcrput(p->set, 0);	dcrput(p->arm, inv);	/* inv=1 => AND; =0, OR (and drive 0) */	imb();	microdelay(1);		/* TO DO: 50 processor clocks */	iunlock(p);} static voidgbarrier(GIport *p){	ilock(p);	p->sig = 0;	dcrput(p->set, 0);	dcrput(p->set, 1);	iunlock(p);} static voidgenable(GIport *p, int on){	dcrput(p->enable, on);	/* TO DO: !on? */} enum{	Ngi=	4,	/* hardware limit */ 	Qdir = 0,	Qbarrier,	Qintr,	Nfile=	Qintr-Qdir,	/* files per tree */	Ndir=	1+(Ngi*Nfile),		/* size of directory */};#define	GQID(x, f)	(((x)<<4)|(f))#define	GPORT(p)	((ulong)(p)>>4)#define	GFILE(p)	((ulong)(p) & 0xF) static GIport	ports[Ngi];static Dirtab	gibdir[Ndir];static Lock	giblock;		/* unused but would control write access to resets and enables */ static voidgibfile(Dirtab *d, char *name, int i, int path, ulong perm){	snprint(d->name, sizeof(d->name), "gib%d%s", i, name);	mkqid(&d->qid, GQID(i, path), 0, QTFILE);	d->perm = perm;	d->length = 0;} /* * there are two sets of interrupt lines: * the first four are not inverted (OR interrupts, signals); * the next four are inverted (AND interrupts, barriers). * we switch this function between vectors depending on which file is opened. */static voidinterrupt(Ureg*, void *a){	GIport *p;	int i; 	p = a;	ilock(p);	p->sig = 1;	/* record it */	dcrput(p->set, 0);	/* reset it, and ensure it goes before continuing */	for(i = 0; dcrget(GibDCR+Wstatus) & p->intrmask && i < 100; i++){		microdelay(1);		dcrput(p->set, 0);	}	iunlock(p);	wakeup(&p->r);} static intwaitintr(void *a){	GIport *p; 	p = a;	return p->sig;} voidgibinit(void){	Dirtab *d;	int i; 	d = gibdir;	strcpy(d->name, ".");	mkqid(&d->qid, Qdir, 0, QTDIR);	d->perm = DMDIR|0555;	d++;	for(i = 0; i < Ngi; i++){		gibfile(d, "barrier", i, Qbarrier, 0222);		d++;		gibfile(d, "intr", i, Qintr, 0666);		d++; 		ginit(&ports[i], i);		genable(&ports[i], 1);		/* disable this port TO DO: active high or active low */	}} static Chan*gibattach(char* spec){	return devattach('!', spec);} static Walkqid*	 gibwalk(Chan* c, Chan *nc, char** name, int nname){	return devwalk(c, nc, name, nname, gibdir, nelem(gibdir), devgen);} static long	 gibstat(Chan* c, uchar* dp, long n){	return devstat(c, dp, n, gibdir, nelem(gibdir), devgen);} static voidsetmode(GIport *p, int mode){	ilock(p);	if(p->mode != None){		unlock(p);		error(Einuse);	}	p->mode = mode;	iunlock(p);} static Chan*gibopen(Chan* c, int omode){	GIport *p; 	c = devopen(c, omode, gibdir, nelem(gibdir), devgen);	if(waserror()){		c->flag &= ~COPEN;		nexterror();	}	p = &ports[GPORT(c->qid.path)];	switch(GFILE(c->qid.path)){	case Qintr:		setmode(p, Intr);		clearandarm(p, 0);	/* OR */		intrenable(VectorGlobalInt0+p->n, interrupt, p, "gib", 0);		break; 	case Qbarrier:		setmode(p, Barrier);		clearandarm(p, 1);	/* AND */		intrenable(VectorGlobalInt0+4+p->n, interrupt, p, "gib", 0);		break;	}	poperror();	return c;} static void	 gibclose(Chan *c){	GIport *p;	int n; 	if((c->flag & COPEN) == 0)		return;	n = 0;	p = &ports[GPORT(c->qid.path)];	switch(GFILE(c->qid.path)){	case Qbarrier:		n = 4;		/* fall through */	case Qintr:		ilock(p);		p->mode = None;		iunlock(p);		intrdisable(VectorGlobalInt0+n+p->n, interrupt, p, "gib");		break;	}} static long	 gibread(Chan* c, void* buf, long n, vlong offset){	GIport *p; 	if(c->qid.type & QTDIR)		return devdirread(c, buf, n, gibdir, nelem(gibdir), devgen); 	p = &ports[GPORT(c->qid.path)];	switch(GFILE(c->qid.path)){	default:		error(Egreg); 	case Qintr:		qlock(&p->rl);		if(waserror()){			qunlock(&p->rl);			nexterror();		}		sleep(&p->r, waitintr, p);		poperror();		qunlock(&p->rl);		if((offset==0)&&(n)) {			*(char*)buf = '1';			return 1;		} else			return 0;	}} static long	 gibwrite(Chan* c, void*, long n, vlong){	GIport *p; 	p = &ports[GPORT(c->qid.path)];	switch(GFILE(c->qid.path)){	default:		error(Egreg); 	case Qbarrier:		qlock(&p->rl);		if(waserror()){			qunlock(&p->rl);			nexterror();		}		gbarrier(p);		sleep(&p->r, waitintr, p);		poperror();		qunlock(&p->rl);		break; 	case Qintr:		ilock(p);		p->sig = 0;		dcrput(p->set, 1);	/* signal */		iunlock(p);		break;	}	return n;} Dev gibdevtab = {	'!',	"gib", 	devreset,	gibinit,	devshutdown,	gibattach,	gibwalk,	gibstat,	gibopen,	devcreate,	gibclose,	gibread,	devbread,	gibwrite,	devbwrite,	devremove,	devwstat,}; /* * kernel interface to barriers */ voidclaimbarrier(int n){	GIport *p; 	p = &ports[n];	if(p->init == 0){		ginit(p, n);		genable(p, 1);	}	ilock(p);	if(p->mode == None){		p->mode = Barrier;		p->kernel = 1;	}	iunlock(p);	if(!p->kernel)		panic("barrier %d in use", n);	clearandarm(p, 1);	/* AND */} voidreleasebarrier(int n){	GIport *p; 	p = &ports[n];	if(!p->kernel)		panic("barrier %d not claimed", n);	ilock(p);	p->kernel = 0;	p->mode = None;	iunlock(p);} intglobalbarrier(int n, ulong msec){	GIport *p;	int i, r;	ulong tout; 	p = &ports[n];	if(!p->kernel)		panic("barrier %d not claimed", n);	gbarrier(p);	r = 0;	tout = MACHP(0)->ticks + ms2tk(msec);	while((dcrget(GibDCR+Wstatus) & p->intrmask) == 0){		/* wait */		if(tout < MACHP(0)->ticks){			r = -1;			break;		}	}	dcrput(p->set, 0);	/* reset it, and ensure it goes before continuing */	for(i = 0; dcrget(GibDCR+Wstatus) & p->intrmask && i < 100; i++){		microdelay(1);		dcrput(p->set, 0);	}	return r;}