1 /*
2 * kirkwood two-wire serial interface (TWSI) and
3 * inter-integrated circuit (IC) 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
twsifinish(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
twsidoread(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
twsidowrite(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
twsigotintr(void *)145 twsigotintr(void *)
146 {
147 return twsi.intr;
148 }
149
150 static long
twsixfer(uchar * buf,ulong len,ulong offset,void (* op)(void))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
interrupt(Ureg *,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
twsiinit(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
twsishutdown(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*
twsiattach(char * param)223 twsiattach(char *param)
224 {
225 return devattach(L'', param);
226 }
227
228 static Walkqid*
twsiwalk(Chan * c,Chan * nc,char ** name,int nname)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
twsistat(Chan * c,uchar * db,int n)235 twsistat(Chan *c, uchar *db, int n)
236 {
237 return devstat(c, db, n, twsidir, nelem(twsidir), devgen);
238 }
239
240 static Chan*
twsiopen(Chan * c,int omode)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
twsiclose(Chan *)258 twsiclose(Chan *)
259 {
260 }
261
262 static long
twsiread(Chan * c,void * v,long n,vlong off)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
twsiwrite(Chan * c,void * v,long n,vlong off)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 };
306