182962f4bSDavid du Colombier /*
282962f4bSDavid du Colombier * kirkwood two-wire serial interface (TWSI) and
382962f4bSDavid du Colombier * inter-integrated circuit (IC) 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