xref: /plan9/sys/src/9/kw/devtwsi.c (revision 7365b686ae7154552580a79fd89a0ba5dc9297c4)
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
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