xref: /plan9-contrib/sys/src/9/kw/devtwsi.c (revision 7365b686ae7154552580a79fd89a0ba5dc9297c4)
182962f4bSDavid du Colombier /*
282962f4bSDavid du Colombier  * kirkwood two-wire serial interface (TWSI) and
382962f4bSDavid du Colombier  * inter-integrated circuit (I⁲C) driver
482962f4bSDavid du Colombier  */
582962f4bSDavid du Colombier #include "u.h"
682962f4bSDavid du Colombier #include "../port/lib.h"
782962f4bSDavid du Colombier #include "mem.h"
882962f4bSDavid du Colombier #include "dat.h"
982962f4bSDavid du Colombier #include "fns.h"
1082962f4bSDavid du Colombier #include "../port/error.h"
1182962f4bSDavid du Colombier #include "io.h"
1282962f4bSDavid du Colombier 
1382962f4bSDavid du Colombier enum {
1482962f4bSDavid du Colombier 	Qdir,
1582962f4bSDavid du Colombier 	Qtwsi,
1682962f4bSDavid du Colombier };
1782962f4bSDavid du Colombier 
1882962f4bSDavid du Colombier typedef struct Kwtwsi Kwtwsi;
1982962f4bSDavid du Colombier typedef struct Twsi Twsi;
2082962f4bSDavid du Colombier 
2182962f4bSDavid du Colombier struct Kwtwsi {				/* device registers */
2282962f4bSDavid du Colombier 	ulong	saddr;
2382962f4bSDavid du Colombier 	ulong	data;
2482962f4bSDavid du Colombier 	ulong	ctl;
2582962f4bSDavid du Colombier 	union {
2682962f4bSDavid du Colombier 		ulong	status;		/* ro */
2782962f4bSDavid du Colombier 		ulong	rate;		/* wo: baud rate */
2882962f4bSDavid du Colombier 	};
2982962f4bSDavid du Colombier 
3082962f4bSDavid du Colombier 	ulong	saddrext;
3182962f4bSDavid du Colombier 	uchar	_pad0[0x1c-0x14];
3282962f4bSDavid du Colombier 	ulong	reset;
3382962f4bSDavid du Colombier 	uchar	_pad1[0x98-0x20];
3482962f4bSDavid du Colombier 	ulong	initlastdata;
3582962f4bSDavid du Colombier };
3682962f4bSDavid du Colombier 
3782962f4bSDavid du Colombier enum {
3882962f4bSDavid du Colombier 	Twsidowrite,
3982962f4bSDavid du Colombier 	Twsidoread,
4082962f4bSDavid du Colombier 
4182962f4bSDavid du Colombier 	/* ctl bits */
4282962f4bSDavid du Colombier 	Twsiack		= 1<<2,		/* recv'd data; clear to ack */
4382962f4bSDavid du Colombier 	Twsiint		= 1<<3,		/* interrupt conditions true */
4482962f4bSDavid du Colombier 	Twsistop	= 1<<4,
4582962f4bSDavid du Colombier 	Twsistart	= 1<<5,
4682962f4bSDavid du Colombier 	Twsislaveen	= 1<<6,
4782962f4bSDavid du Colombier 	Twsiinten	= 1<<7,		/* interrupts enabled */
4882962f4bSDavid du Colombier 
4982962f4bSDavid du Colombier 	/* status codes */
5082962f4bSDavid du Colombier 	SStart	= 0x08,
5182962f4bSDavid du Colombier 	SWa	= 0x18,
5282962f4bSDavid du Colombier 	SWda	= 0x28,
5382962f4bSDavid du Colombier 	SRa	= 0x40,
5482962f4bSDavid du Colombier 	SRda	= 0x50,
5582962f4bSDavid du Colombier 	SRna	= 0x58,
5682962f4bSDavid du Colombier };
5782962f4bSDavid du Colombier 
5882962f4bSDavid du Colombier struct Twsi {
5982962f4bSDavid du Colombier 	QLock;
6082962f4bSDavid du Colombier 	Rendez	nextbyte;
6182962f4bSDavid du Colombier 
6282962f4bSDavid du Colombier 	/* remainder is state needed to track the operation in progress */
6382962f4bSDavid du Colombier 	int	intr;
6482962f4bSDavid du Colombier 	int	done;
6582962f4bSDavid du Colombier 
6682962f4bSDavid du Colombier 	uchar	*bp;			/* current ptr into buf */
6782962f4bSDavid du Colombier 	uchar	*end;
6882962f4bSDavid du Colombier 
6982962f4bSDavid du Colombier 	ulong	addr;			/* device address */
7082962f4bSDavid du Colombier 	char	*error;
7182962f4bSDavid du Colombier };
7282962f4bSDavid du Colombier 
73*7365b686SDavid du Colombier static Twsi twsi;
7482962f4bSDavid du Colombier 
7582962f4bSDavid du Colombier static Dirtab twsidir[] = {
7682962f4bSDavid du Colombier 	".",	{Qdir, 0, QTDIR},	0,	DMDIR|0555,
7782962f4bSDavid du Colombier 	"twsi",	{Qtwsi},		0,	0660,
7882962f4bSDavid du Colombier };
7982962f4bSDavid du Colombier 
8082962f4bSDavid du Colombier static char Eabsts[] = "abnormal status";
8182962f4bSDavid du Colombier 
8282962f4bSDavid du Colombier static void
twsifinish(void)8382962f4bSDavid du Colombier twsifinish(void)
8482962f4bSDavid du Colombier {
85*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
86*7365b686SDavid du Colombier 
8782962f4bSDavid du Colombier 	twsi.done = 1;
88*7365b686SDavid du Colombier 	krp->ctl |= Twsistop;
8982962f4bSDavid du Colombier 	coherence();
9082962f4bSDavid du Colombier }
9182962f4bSDavid du Colombier 
9282962f4bSDavid du Colombier static void
twsidoread(void)9382962f4bSDavid du Colombier twsidoread(void)
9482962f4bSDavid du Colombier {
95*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
9682962f4bSDavid du Colombier 
9782962f4bSDavid du Colombier 	switch(krp->status){
9882962f4bSDavid du Colombier 	case SStart:
9982962f4bSDavid du Colombier 		krp->data = twsi.addr << 1 | Twsidoread;
10082962f4bSDavid du Colombier 		break;
10182962f4bSDavid du Colombier 	case SRa:
10282962f4bSDavid du Colombier 		krp->ctl |= Twsiack;
10382962f4bSDavid du Colombier 		break;
10482962f4bSDavid du Colombier 	case SRda:
10582962f4bSDavid du Colombier 		if(twsi.bp < twsi.end) {
10682962f4bSDavid du Colombier 			*twsi.bp++ = krp->data;
10782962f4bSDavid du Colombier 			krp->ctl |= Twsiack;
10882962f4bSDavid du Colombier 		} else
10982962f4bSDavid du Colombier 			krp->ctl &= ~Twsiack;
11082962f4bSDavid du Colombier 		break;
11182962f4bSDavid du Colombier 	case SRna:
11282962f4bSDavid du Colombier 		twsifinish();
11382962f4bSDavid du Colombier 		break;
11482962f4bSDavid du Colombier 	default:
11582962f4bSDavid du Colombier 		twsifinish();
11682962f4bSDavid du Colombier 		twsi.error = Eabsts;
11782962f4bSDavid du Colombier 		break;
11882962f4bSDavid du Colombier 	}
11982962f4bSDavid du Colombier }
12082962f4bSDavid du Colombier 
12182962f4bSDavid du Colombier static void
twsidowrite(void)12282962f4bSDavid du Colombier twsidowrite(void)
12382962f4bSDavid du Colombier {
124*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
12582962f4bSDavid du Colombier 
12682962f4bSDavid du Colombier 	switch(krp->status){
12782962f4bSDavid du Colombier 	case SStart:
12882962f4bSDavid du Colombier 		krp->data = twsi.addr << 1 | Twsidowrite;
12982962f4bSDavid du Colombier 		break;
13082962f4bSDavid du Colombier 	case SWa:
13182962f4bSDavid du Colombier 	case SWda:
13282962f4bSDavid du Colombier 		if(twsi.bp < twsi.end)
13382962f4bSDavid du Colombier 			krp->data = *twsi.bp++;
13482962f4bSDavid du Colombier 		else
13582962f4bSDavid du Colombier 			twsifinish();
13682962f4bSDavid du Colombier 		break;
13782962f4bSDavid du Colombier 	default:
13882962f4bSDavid du Colombier 		twsifinish();
13982962f4bSDavid du Colombier 		twsi.error = Eabsts;
14082962f4bSDavid du Colombier 		break;
14182962f4bSDavid du Colombier 	}
14282962f4bSDavid du Colombier }
14382962f4bSDavid du Colombier 
14482962f4bSDavid du Colombier static int
twsigotintr(void *)14582962f4bSDavid du Colombier twsigotintr(void *)
14682962f4bSDavid du Colombier {
14782962f4bSDavid du Colombier 	return twsi.intr;
14882962f4bSDavid du Colombier }
14982962f4bSDavid du Colombier 
15082962f4bSDavid du Colombier static long
twsixfer(uchar * buf,ulong len,ulong offset,void (* op)(void))15182962f4bSDavid du Colombier twsixfer(uchar *buf, ulong len, ulong offset, void (*op)(void))
15282962f4bSDavid du Colombier {
15382962f4bSDavid du Colombier 	ulong off;
15482962f4bSDavid du Colombier 	char *err;
155*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
15682962f4bSDavid du Colombier 
15782962f4bSDavid du Colombier 	qlock(&twsi);
15882962f4bSDavid du Colombier 	twsi.bp = buf;
15982962f4bSDavid du Colombier 	twsi.end = buf + len;
16082962f4bSDavid du Colombier 
16182962f4bSDavid du Colombier 	twsi.addr = offset;
16282962f4bSDavid du Colombier 	twsi.done = twsi.intr = 0;
16382962f4bSDavid du Colombier 	twsi.error = nil;
16482962f4bSDavid du Colombier 
16582962f4bSDavid du Colombier 	krp->ctl = (krp->ctl & ~Twsiint) | Twsistart;
16682962f4bSDavid du Colombier 	coherence();
16782962f4bSDavid du Colombier 	while (!twsi.done) {
16882962f4bSDavid du Colombier 		sleep(&twsi.nextbyte, twsigotintr, 0);
16982962f4bSDavid du Colombier 		twsi.intr = 0;
17082962f4bSDavid du Colombier 		(*op)();
17182962f4bSDavid du Colombier 		/* signal to start new op & extinguish intr source */
17282962f4bSDavid du Colombier 		krp->ctl &= ~Twsiint;
17382962f4bSDavid du Colombier 		coherence();
17482962f4bSDavid du Colombier 		krp->ctl |= Twsiinten;
17582962f4bSDavid du Colombier 		coherence();
17682962f4bSDavid du Colombier 	}
17782962f4bSDavid du Colombier 	twsifinish();
17882962f4bSDavid du Colombier 	err = twsi.error;
17982962f4bSDavid du Colombier 	off = twsi.bp - buf;
18082962f4bSDavid du Colombier 	twsi.bp = nil;				/* prevent accidents */
18182962f4bSDavid du Colombier 	qunlock(&twsi);
18282962f4bSDavid du Colombier 
18382962f4bSDavid du Colombier 	if(err)
18482962f4bSDavid du Colombier 		error(err);
18582962f4bSDavid du Colombier 	return off;
18682962f4bSDavid du Colombier }
18782962f4bSDavid du Colombier 
18882962f4bSDavid du Colombier static void
interrupt(Ureg *,void *)18982962f4bSDavid du Colombier interrupt(Ureg *, void *)
19082962f4bSDavid du Colombier {
191*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
19282962f4bSDavid du Colombier 
19382962f4bSDavid du Colombier 	twsi.intr = 1;
19482962f4bSDavid du Colombier 	wakeup(&twsi.nextbyte);
19582962f4bSDavid du Colombier 
19682962f4bSDavid du Colombier 	krp->ctl &= ~Twsiinten;			/* stop further interrupts */
19782962f4bSDavid du Colombier 	coherence();
19882962f4bSDavid du Colombier 	intrclear(Irqlo, IRQ0twsi);
19982962f4bSDavid du Colombier }
20082962f4bSDavid du Colombier 
20182962f4bSDavid du Colombier static void
twsiinit(void)20282962f4bSDavid du Colombier twsiinit(void)
20382962f4bSDavid du Colombier {
204*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
20582962f4bSDavid du Colombier 
20682962f4bSDavid du Colombier 	intrenable(Irqlo, IRQ0twsi, interrupt, nil, "twsi");
20782962f4bSDavid du Colombier 	krp->ctl &= ~Twsiint;
20882962f4bSDavid du Colombier 	krp->ctl |= Twsiinten;
20982962f4bSDavid du Colombier 	coherence();
21082962f4bSDavid du Colombier }
21182962f4bSDavid du Colombier 
21282962f4bSDavid du Colombier static void
twsishutdown(void)21382962f4bSDavid du Colombier twsishutdown(void)
21482962f4bSDavid du Colombier {
215*7365b686SDavid du Colombier 	Kwtwsi *krp = (Kwtwsi *)soc.twsi;
216*7365b686SDavid du Colombier 
217*7365b686SDavid du Colombier 	krp->ctl &= ~Twsiinten;
21882962f4bSDavid du Colombier 	coherence();
21982962f4bSDavid du Colombier 	intrdisable(Irqlo, IRQ0twsi, interrupt, nil, "twsi");
22082962f4bSDavid du Colombier }
22182962f4bSDavid du Colombier 
22282962f4bSDavid du Colombier static Chan*
twsiattach(char * param)22382962f4bSDavid du Colombier twsiattach(char *param)
22482962f4bSDavid du Colombier {
22582962f4bSDavid du Colombier 	return devattach(L'⁲', param);
22682962f4bSDavid du Colombier }
22782962f4bSDavid du Colombier 
22882962f4bSDavid du Colombier static Walkqid*
twsiwalk(Chan * c,Chan * nc,char ** name,int nname)22982962f4bSDavid du Colombier twsiwalk(Chan *c, Chan *nc, char **name, int nname)
23082962f4bSDavid du Colombier {
23182962f4bSDavid du Colombier 	return devwalk(c, nc, name, nname, twsidir, nelem(twsidir), devgen);
23282962f4bSDavid du Colombier }
23382962f4bSDavid du Colombier 
23482962f4bSDavid du Colombier static int
twsistat(Chan * c,uchar * db,int n)23582962f4bSDavid du Colombier twsistat(Chan *c, uchar *db, int n)
23682962f4bSDavid du Colombier {
23782962f4bSDavid du Colombier 	return devstat(c, db, n, twsidir, nelem(twsidir), devgen);
23882962f4bSDavid du Colombier }
23982962f4bSDavid du Colombier 
24082962f4bSDavid du Colombier static Chan*
twsiopen(Chan * c,int omode)24182962f4bSDavid du Colombier twsiopen(Chan *c, int omode)
24282962f4bSDavid du Colombier {
24382962f4bSDavid du Colombier 	switch((ulong)c->qid.path){
24482962f4bSDavid du Colombier 	default:
24582962f4bSDavid du Colombier 		error(Eperm);
24682962f4bSDavid du Colombier 	case Qdir:
24782962f4bSDavid du Colombier 	case Qtwsi:
24882962f4bSDavid du Colombier 		break;
24982962f4bSDavid du Colombier 	}
25082962f4bSDavid du Colombier 	c = devopen(c, omode, twsidir, nelem(twsidir), devgen);
25182962f4bSDavid du Colombier 	c->mode = openmode(omode);
25282962f4bSDavid du Colombier 	c->flag |= COPEN;
25382962f4bSDavid du Colombier 	c->offset = 0;
25482962f4bSDavid du Colombier 	return c;
25582962f4bSDavid du Colombier }
25682962f4bSDavid du Colombier 
25782962f4bSDavid du Colombier static void
twsiclose(Chan *)25882962f4bSDavid du Colombier twsiclose(Chan *)
25982962f4bSDavid du Colombier {
26082962f4bSDavid du Colombier }
26182962f4bSDavid du Colombier 
26282962f4bSDavid du Colombier static long
twsiread(Chan * c,void * v,long n,vlong off)26382962f4bSDavid du Colombier twsiread(Chan *c, void *v, long n, vlong off)
26482962f4bSDavid du Colombier {
26582962f4bSDavid du Colombier 	switch((ulong)c->qid.path){
26682962f4bSDavid du Colombier 	default:
26782962f4bSDavid du Colombier 		error(Eperm);
26882962f4bSDavid du Colombier 	case Qdir:
26982962f4bSDavid du Colombier 		return devdirread(c, v, n, twsidir, nelem(twsidir), devgen);
27082962f4bSDavid du Colombier 	case Qtwsi:
27182962f4bSDavid du Colombier 		return twsixfer(v, n, off, twsidoread);
27282962f4bSDavid du Colombier 	}
27382962f4bSDavid du Colombier }
27482962f4bSDavid du Colombier 
27582962f4bSDavid du Colombier static long
twsiwrite(Chan * c,void * v,long n,vlong off)27682962f4bSDavid du Colombier twsiwrite(Chan *c, void *v, long n, vlong off)
27782962f4bSDavid du Colombier {
27882962f4bSDavid du Colombier 	switch((ulong)c->qid.path){
27982962f4bSDavid du Colombier 	default:
28082962f4bSDavid du Colombier 		error(Eperm);
28182962f4bSDavid du Colombier 	case Qtwsi:
28282962f4bSDavid du Colombier 		return twsixfer(v, n, off, twsidowrite);
28382962f4bSDavid du Colombier 	}
28482962f4bSDavid du Colombier }
28582962f4bSDavid du Colombier 
28682962f4bSDavid du Colombier Dev twsidevtab = {
28782962f4bSDavid du Colombier 	L'⁲',
28882962f4bSDavid du Colombier 	"twsi",
28982962f4bSDavid du Colombier 
29082962f4bSDavid du Colombier 	devreset,
29182962f4bSDavid du Colombier 	twsiinit,
29282962f4bSDavid du Colombier 	twsishutdown,
29382962f4bSDavid du Colombier 	twsiattach,
29482962f4bSDavid du Colombier 	twsiwalk,
29582962f4bSDavid du Colombier 	twsistat,
29682962f4bSDavid du Colombier 	twsiopen,
29782962f4bSDavid du Colombier 	devcreate,
29882962f4bSDavid du Colombier 	twsiclose,
29982962f4bSDavid du Colombier 	twsiread,
30082962f4bSDavid du Colombier 	devbread,
30182962f4bSDavid du Colombier 	twsiwrite,
30282962f4bSDavid du Colombier 	devbwrite,
30382962f4bSDavid du Colombier 	devremove,
30482962f4bSDavid du Colombier 	devwstat,
30582962f4bSDavid du Colombier };
306