diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/kw/devtwsi.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/kw/devtwsi.c')
-rwxr-xr-x | sys/src/9/kw/devtwsi.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/sys/src/9/kw/devtwsi.c b/sys/src/9/kw/devtwsi.c new file mode 100755 index 000000000..ec4a268c9 --- /dev/null +++ b/sys/src/9/kw/devtwsi.c @@ -0,0 +1,305 @@ +/* + * kirkwood two-wire serial interface (TWSI) and + * inter-integrated circuit (IC) driver + */ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +enum { + Qdir, + Qtwsi, +}; + +typedef struct Kwtwsi Kwtwsi; +typedef struct Twsi Twsi; + +struct Kwtwsi { /* device registers */ + ulong saddr; + ulong data; + ulong ctl; + union { + ulong status; /* ro */ + ulong rate; /* wo: baud rate */ + }; + + ulong saddrext; + uchar _pad0[0x1c-0x14]; + ulong reset; + uchar _pad1[0x98-0x20]; + ulong initlastdata; +}; + +enum { + Twsidowrite, + Twsidoread, + + /* ctl bits */ + Twsiack = 1<<2, /* recv'd data; clear to ack */ + Twsiint = 1<<3, /* interrupt conditions true */ + Twsistop = 1<<4, + Twsistart = 1<<5, + Twsislaveen = 1<<6, + Twsiinten = 1<<7, /* interrupts enabled */ + + /* status codes */ + SStart = 0x08, + SWa = 0x18, + SWda = 0x28, + SRa = 0x40, + SRda = 0x50, + SRna = 0x58, +}; + +struct Twsi { + QLock; + Rendez nextbyte; + + /* remainder is state needed to track the operation in progress */ + int intr; + int done; + + uchar *bp; /* current ptr into buf */ + uchar *end; + + ulong addr; /* device address */ + char *error; +}; + +static Twsi twsi; + +static Dirtab twsidir[] = { + ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, + "twsi", {Qtwsi}, 0, 0660, +}; + +static char Eabsts[] = "abnormal status"; + +static void +twsifinish(void) +{ + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + twsi.done = 1; + krp->ctl |= Twsistop; + coherence(); +} + +static void +twsidoread(void) +{ + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + switch(krp->status){ + case SStart: + krp->data = twsi.addr << 1 | Twsidoread; + break; + case SRa: + krp->ctl |= Twsiack; + break; + case SRda: + if(twsi.bp < twsi.end) { + *twsi.bp++ = krp->data; + krp->ctl |= Twsiack; + } else + krp->ctl &= ~Twsiack; + break; + case SRna: + twsifinish(); + break; + default: + twsifinish(); + twsi.error = Eabsts; + break; + } +} + +static void +twsidowrite(void) +{ + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + switch(krp->status){ + case SStart: + krp->data = twsi.addr << 1 | Twsidowrite; + break; + case SWa: + case SWda: + if(twsi.bp < twsi.end) + krp->data = *twsi.bp++; + else + twsifinish(); + break; + default: + twsifinish(); + twsi.error = Eabsts; + break; + } +} + +static int +twsigotintr(void *) +{ + return twsi.intr; +} + +static long +twsixfer(uchar *buf, ulong len, ulong offset, void (*op)(void)) +{ + ulong off; + char *err; + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + qlock(&twsi); + twsi.bp = buf; + twsi.end = buf + len; + + twsi.addr = offset; + twsi.done = twsi.intr = 0; + twsi.error = nil; + + krp->ctl = (krp->ctl & ~Twsiint) | Twsistart; + coherence(); + while (!twsi.done) { + sleep(&twsi.nextbyte, twsigotintr, 0); + twsi.intr = 0; + (*op)(); + /* signal to start new op & extinguish intr source */ + krp->ctl &= ~Twsiint; + coherence(); + krp->ctl |= Twsiinten; + coherence(); + } + twsifinish(); + err = twsi.error; + off = twsi.bp - buf; + twsi.bp = nil; /* prevent accidents */ + qunlock(&twsi); + + if(err) + error(err); + return off; +} + +static void +interrupt(Ureg *, void *) +{ + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + twsi.intr = 1; + wakeup(&twsi.nextbyte); + + krp->ctl &= ~Twsiinten; /* stop further interrupts */ + coherence(); + intrclear(Irqlo, IRQ0twsi); +} + +static void +twsiinit(void) +{ + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + intrenable(Irqlo, IRQ0twsi, interrupt, nil, "twsi"); + krp->ctl &= ~Twsiint; + krp->ctl |= Twsiinten; + coherence(); +} + +static void +twsishutdown(void) +{ + Kwtwsi *krp = (Kwtwsi *)soc.twsi; + + krp->ctl &= ~Twsiinten; + coherence(); + intrdisable(Irqlo, IRQ0twsi, interrupt, nil, "twsi"); +} + +static Chan* +twsiattach(char *param) +{ + return devattach(L'', param); +} + +static Walkqid* +twsiwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, twsidir, nelem(twsidir), devgen); +} + +static int +twsistat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, twsidir, nelem(twsidir), devgen); +} + +static Chan* +twsiopen(Chan *c, int omode) +{ + switch((ulong)c->qid.path){ + default: + error(Eperm); + case Qdir: + case Qtwsi: + break; + } + c = devopen(c, omode, twsidir, nelem(twsidir), devgen); + c->mode = openmode(omode); + c->flag |= COPEN; + c->offset = 0; + return c; +} + +static void +twsiclose(Chan *) +{ +} + +static long +twsiread(Chan *c, void *v, long n, vlong off) +{ + switch((ulong)c->qid.path){ + default: + error(Eperm); + case Qdir: + return devdirread(c, v, n, twsidir, nelem(twsidir), devgen); + case Qtwsi: + return twsixfer(v, n, off, twsidoread); + } +} + +static long +twsiwrite(Chan *c, void *v, long n, vlong off) +{ + switch((ulong)c->qid.path){ + default: + error(Eperm); + case Qtwsi: + return twsixfer(v, n, off, twsidowrite); + } +} + +Dev twsidevtab = { + L'', + "twsi", + + devreset, + twsiinit, + twsishutdown, + twsiattach, + twsiwalk, + twsistat, + twsiopen, + devcreate, + twsiclose, + twsiread, + devbread, + twsiwrite, + devbwrite, + devremove, + devwstat, +}; |