]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/kw/devtwsi.c
merge
[plan9front.git] / sys / src / 9 / kw / devtwsi.c
1 /*
2  * kirkwood two-wire serial interface (TWSI) and
3  * inter-integrated circuit (I²C) driver
4  */
5 #include "u.h"
6 #include "../port/lib.h"
7 #include "mem.h"
8 #include "dat.h"
9 #include "fns.h"
10 #include "../port/error.h"
11 #include "io.h"
12
13 enum {
14         Qdir,
15         Qtwsi,
16 };
17
18 typedef struct Kwtwsi Kwtwsi;
19 typedef struct Twsi Twsi;
20
21 struct Kwtwsi {                         /* device registers */
22         ulong   saddr;
23         ulong   data;
24         ulong   ctl;
25         union {
26                 ulong   status;         /* ro */
27                 ulong   rate;           /* wo: baud rate */
28         };
29
30         ulong   saddrext;
31         uchar   _pad0[0x1c-0x14];
32         ulong   reset;
33         uchar   _pad1[0x98-0x20];
34         ulong   initlastdata;
35 };
36
37 enum {
38         Twsidowrite,
39         Twsidoread,
40
41         /* ctl bits */
42         Twsiack         = 1<<2,         /* recv'd data; clear to ack */
43         Twsiint         = 1<<3,         /* interrupt conditions true */
44         Twsistop        = 1<<4,         
45         Twsistart       = 1<<5,
46         Twsislaveen     = 1<<6,
47         Twsiinten       = 1<<7,         /* interrupts enabled */
48
49         /* status codes */
50         SStart  = 0x08,
51         SWa     = 0x18,
52         SWda    = 0x28,
53         SRa     = 0x40,
54         SRda    = 0x50,
55         SRna    = 0x58,
56 };
57
58 struct Twsi {
59         QLock;
60         Rendez  nextbyte;
61
62         /* remainder is state needed to track the operation in progress */
63         int     intr;
64         int     done;
65
66         uchar   *bp;                    /* current ptr into buf */
67         uchar   *end;
68
69         ulong   addr;                   /* device address */
70         char    *error;
71 };
72
73 static Twsi twsi;
74
75 static Dirtab twsidir[] = {
76         ".",    {Qdir, 0, QTDIR},       0,      DMDIR|0555,
77         "twsi", {Qtwsi},                0,      0660,
78 };
79
80 static char Eabsts[] = "abnormal status";
81
82 static void
83 twsifinish(void)
84 {
85         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
86
87         twsi.done = 1;
88         krp->ctl |= Twsistop;
89         coherence();
90 }
91
92 static void
93 twsidoread(void)
94 {
95         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
96
97         switch(krp->status){
98         case SStart:
99                 krp->data = twsi.addr << 1 | Twsidoread;
100                 break;
101         case SRa:
102                 krp->ctl |= Twsiack;
103                 break;
104         case SRda:
105                 if(twsi.bp < twsi.end) {
106                         *twsi.bp++ = krp->data;
107                         krp->ctl |= Twsiack;
108                 } else
109                         krp->ctl &= ~Twsiack;
110                 break;
111         case SRna:
112                 twsifinish();
113                 break;
114         default:
115                 twsifinish();
116                 twsi.error = Eabsts;
117                 break;
118         }
119 }
120
121 static void
122 twsidowrite(void)
123 {
124         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
125
126         switch(krp->status){
127         case SStart:
128                 krp->data = twsi.addr << 1 | Twsidowrite;
129                 break;
130         case SWa:
131         case SWda:
132                 if(twsi.bp < twsi.end)
133                         krp->data = *twsi.bp++;
134                 else
135                         twsifinish();
136                 break;
137         default:
138                 twsifinish();
139                 twsi.error = Eabsts;
140                 break;
141         }
142 }
143
144 static int
145 twsigotintr(void *)
146 {
147         return twsi.intr;
148 }
149
150 static long
151 twsixfer(uchar *buf, ulong len, ulong offset, void (*op)(void))
152 {
153         ulong off;
154         char *err;
155         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
156
157         qlock(&twsi);
158         twsi.bp = buf;
159         twsi.end = buf + len;
160
161         twsi.addr = offset;
162         twsi.done = twsi.intr = 0;
163         twsi.error = nil;
164
165         krp->ctl = (krp->ctl & ~Twsiint) | Twsistart;
166         coherence();
167         while (!twsi.done) {
168                 sleep(&twsi.nextbyte, twsigotintr, 0);
169                 twsi.intr = 0;
170                 (*op)();
171                 /* signal to start new op & extinguish intr source */
172                 krp->ctl &= ~Twsiint;
173                 coherence();
174                 krp->ctl |= Twsiinten;
175                 coherence();
176         }
177         twsifinish();
178         err = twsi.error;
179         off = twsi.bp - buf;
180         twsi.bp = nil;                          /* prevent accidents */
181         qunlock(&twsi);
182
183         if(err)
184                 error(err);
185         return off;
186 }
187
188 static void
189 interrupt(Ureg *, void *)
190 {
191         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
192
193         twsi.intr = 1;
194         wakeup(&twsi.nextbyte);
195
196         krp->ctl &= ~Twsiinten;                 /* stop further interrupts */
197         coherence();
198         intrclear(Irqlo, IRQ0twsi);
199 }
200
201 static void
202 twsiinit(void)
203 {
204         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
205
206         intrenable(Irqlo, IRQ0twsi, interrupt, nil, "twsi");
207         krp->ctl &= ~Twsiint;
208         krp->ctl |= Twsiinten;
209         coherence();
210 }
211
212 static void
213 twsishutdown(void)
214 {
215         Kwtwsi *krp = (Kwtwsi *)soc.twsi;
216
217         krp->ctl &= ~Twsiinten;
218         coherence();
219         intrdisable(Irqlo, IRQ0twsi, interrupt, nil, "twsi");
220 }
221
222 static Chan*
223 twsiattach(char *param)
224 {
225         return devattach(L'²', param);
226 }
227
228 static Walkqid*
229 twsiwalk(Chan *c, Chan *nc, char **name, int nname)
230 {
231         return devwalk(c, nc, name, nname, twsidir, nelem(twsidir), devgen);
232 }
233
234 static int
235 twsistat(Chan *c, uchar *db, int n)
236 {
237         return devstat(c, db, n, twsidir, nelem(twsidir), devgen);
238 }
239
240 static Chan*
241 twsiopen(Chan *c, int omode)
242 {
243         switch((ulong)c->qid.path){
244         default:
245                 error(Eperm);
246         case Qdir:
247         case Qtwsi:
248                 break;
249         }
250         c = devopen(c, omode, twsidir, nelem(twsidir), devgen);
251         c->mode = openmode(omode);
252         c->flag |= COPEN;
253         c->offset = 0;
254         return c;
255 }
256
257 static void
258 twsiclose(Chan *)
259 {
260 }
261
262 static long
263 twsiread(Chan *c, void *v, long n, vlong off)
264 {
265         switch((ulong)c->qid.path){
266         default:
267                 error(Eperm);
268         case Qdir:
269                 return devdirread(c, v, n, twsidir, nelem(twsidir), devgen);
270         case Qtwsi:
271                 return twsixfer(v, n, off, twsidoread);
272         }
273 }
274
275 static long
276 twsiwrite(Chan *c, void *v, long n, vlong off)
277 {
278         switch((ulong)c->qid.path){
279         default:
280                 error(Eperm);
281         case Qtwsi:
282                 return twsixfer(v, n, off, twsidowrite);
283         }
284 }
285
286 Dev twsidevtab = {
287         L'²',
288         "twsi",
289
290         devreset,
291         twsiinit,
292         twsishutdown,
293         twsiattach,
294         twsiwalk,
295         twsistat,
296         twsiopen,
297         devcreate,
298         twsiclose,
299         twsiread,
300         devbread,
301         twsiwrite,
302         devbwrite,
303         devremove,
304         devwstat,
305 };