Initial import from FreeBSD RELENG_4:
[games.git] / sys / bus / iicbus / iicsmb.c
1 /*-
2  * Copyright (c) 1998 Nicolas Souchu
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/iicbus/iicsmb.c,v 1.5.2.2 2000/08/09 00:59:27 peter Exp $
27  *
28  */
29
30 /*
31  * I2C to SMB bridge
32  *
33  * Example:
34  *
35  *     smb bttv
36  *       \ /
37  *      smbus
38  *       /  \
39  *    iicsmb bti2c
40  *       |
41  *     iicbus
42  *     /  |  \
43  *  iicbb pcf ...
44  *    |
45  *  lpbb
46  */
47
48 #include <sys/param.h>
49 #include <sys/kernel.h>
50 #include <sys/systm.h>
51 #include <sys/module.h>
52 #include <sys/bus.h>
53 #include <sys/conf.h>
54 #include <sys/buf.h>
55 #include <sys/uio.h>
56 #include <sys/malloc.h>
57
58 #include <machine/clock.h>
59
60 #include <dev/iicbus/iiconf.h>
61 #include <dev/iicbus/iicbus.h>
62
63 #include <dev/smbus/smbconf.h>
64
65 #include "iicbus_if.h"
66 #include "smbus_if.h"
67
68 struct iicsmb_softc {
69
70 #define SMB_WAITING_ADDR        0x0
71 #define SMB_WAITING_LOW         0x1
72 #define SMB_WAITING_HIGH        0x2
73 #define SMB_DONE                0x3
74         int state;
75
76         u_char devaddr;                 /* slave device address */
77
78         char low;                       /* low byte received first */
79         char high;                      /* high byte */
80
81         device_t smbus;
82 };
83
84 static int iicsmb_probe(device_t);
85 static int iicsmb_attach(device_t);
86
87 static void iicsmb_intr(device_t dev, int event, char *buf);
88 static int iicsmb_callback(device_t dev, int index, caddr_t data);
89 static int iicsmb_quick(device_t dev, u_char slave, int how);
90 static int iicsmb_sendb(device_t dev, u_char slave, char byte);
91 static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
92 static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
93 static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
94 static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
95 static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
96 static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
97 static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
98 static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
99
100 static devclass_t iicsmb_devclass;
101
102 static device_method_t iicsmb_methods[] = {
103         /* device interface */
104         DEVMETHOD(device_probe,         iicsmb_probe),
105         DEVMETHOD(device_attach,        iicsmb_attach),
106         DEVMETHOD(device_detach,        bus_generic_detach),
107
108         /* bus interface */
109         DEVMETHOD(bus_print_child,      bus_generic_print_child),
110
111         /* iicbus interface */
112         DEVMETHOD(iicbus_intr,          iicsmb_intr),
113
114         /* smbus interface */
115         DEVMETHOD(smbus_callback,       iicsmb_callback),
116         DEVMETHOD(smbus_quick,          iicsmb_quick),
117         DEVMETHOD(smbus_sendb,          iicsmb_sendb),
118         DEVMETHOD(smbus_recvb,          iicsmb_recvb),
119         DEVMETHOD(smbus_writeb,         iicsmb_writeb),
120         DEVMETHOD(smbus_writew,         iicsmb_writew),
121         DEVMETHOD(smbus_readb,          iicsmb_readb),
122         DEVMETHOD(smbus_readw,          iicsmb_readw),
123         DEVMETHOD(smbus_pcall,          iicsmb_pcall),
124         DEVMETHOD(smbus_bwrite,         iicsmb_bwrite),
125         DEVMETHOD(smbus_bread,          iicsmb_bread),
126         
127         { 0, 0 }
128 };
129
130 static driver_t iicsmb_driver = {
131         "iicsmb",
132         iicsmb_methods,
133         sizeof(struct iicsmb_softc),
134 };
135
136 static int
137 iicsmb_probe(device_t dev)
138 {
139         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
140
141         sc->smbus = smbus_alloc_bus(dev);
142
143         if (!sc->smbus)
144                 return (EINVAL);        /* XXX don't know what to return else */
145                 
146         return (0);
147 }
148
149 static int
150 iicsmb_attach(device_t dev)
151 {
152         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
153
154         /* probe and attach the smbus */
155         device_probe_and_attach(sc->smbus);
156
157         return (0);
158 }
159
160 /*
161  * iicsmb_intr()
162  *
163  * iicbus interrupt handler
164  */
165 static void
166 iicsmb_intr(device_t dev, int event, char *buf)
167 {
168         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
169
170         switch (event) {
171         case INTR_GENERAL:
172         case INTR_START:
173                 sc->state = SMB_WAITING_ADDR;
174                 break;
175
176         case INTR_STOP:
177                 /* call smbus intr handler */
178                 smbus_intr(sc->smbus, sc->devaddr,
179                                 sc->low, sc->high, SMB_ENOERR);
180                 break;
181
182         case INTR_RECEIVE:
183                 switch (sc->state) {
184                 case SMB_DONE:
185                         /* XXX too much data, discard */
186                         printf("%s: too much data from 0x%x\n", __FUNCTION__,
187                                 sc->devaddr & 0xff);
188                         goto end;
189
190                 case SMB_WAITING_ADDR:
191                         sc->devaddr = (u_char)*buf;
192                         sc->state = SMB_WAITING_LOW;
193                         break;
194
195                 case SMB_WAITING_LOW:
196                         sc->low = *buf;
197                         sc->state = SMB_WAITING_HIGH;
198                         break;
199
200                 case SMB_WAITING_HIGH:
201                         sc->high = *buf;
202                         sc->state = SMB_DONE;
203                         break;
204                 }
205 end:
206                 break;
207
208         case INTR_TRANSMIT:
209         case INTR_NOACK:
210                 break;
211
212         case INTR_ERROR:
213                 switch (*buf) {
214                 case IIC_EBUSERR:
215                         smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
216                         break;
217
218                 default:
219                         printf("%s unknown error 0x%x!\n", __FUNCTION__,
220                                                                 (int)*buf);
221                         break;
222                 }
223                 break;
224
225         default:
226                 panic("%s: unknown event (%d)!", __FUNCTION__, event);
227         }
228
229         return;
230 }
231
232 static int
233 iicsmb_callback(device_t dev, int index, caddr_t data)
234 {
235         device_t parent = device_get_parent(dev);
236         int error = 0;
237         int how;
238
239         switch (index) {
240         case SMB_REQUEST_BUS:
241                 /* request underlying iicbus */
242                 how = *(int *)data;
243                 error = iicbus_request_bus(parent, dev, how);
244                 break;
245
246         case SMB_RELEASE_BUS:
247                 /* release underlying iicbus */
248                 error = iicbus_release_bus(parent, dev);
249                 break;
250
251         default:
252                 error = EINVAL;
253         }
254
255         return (error);
256 }
257
258 static int
259 iicsmb_quick(device_t dev, u_char slave, int how)
260 {
261         device_t parent = device_get_parent(dev);
262         int error;
263
264         switch (how) {
265         case SMB_QWRITE:
266                 error = iicbus_start(parent, slave & ~LSB, 0);
267                 break;
268
269         case SMB_QREAD:
270                 error = iicbus_start(parent, slave | LSB, 0);
271                 break;
272
273         default:
274                 error = EINVAL;
275                 break;
276         }
277
278         if (!error)
279                 error = iicbus_stop(parent);
280
281         return (error);
282 }
283
284 static int
285 iicsmb_sendb(device_t dev, u_char slave, char byte)
286 {
287         device_t parent = device_get_parent(dev);
288         int error, sent;
289
290         error = iicbus_start(parent, slave & ~LSB, 0);
291
292         if (!error) {
293                 error = iicbus_write(parent, &byte, 1, &sent, 0);
294
295                 iicbus_stop(parent);
296         }
297
298         return (error);
299 }
300
301 static int
302 iicsmb_recvb(device_t dev, u_char slave, char *byte)
303 {
304         device_t parent = device_get_parent(dev);
305         int error, read;
306
307         error = iicbus_start(parent, slave | LSB, 0);
308
309         if (!error) {
310                 error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0);
311
312                 iicbus_stop(parent);
313         }
314
315         return (error);
316 }
317
318 static int
319 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
320 {
321         device_t parent = device_get_parent(dev);
322         int error, sent;
323
324         error = iicbus_start(parent, slave & ~LSB, 0);
325
326         if (!error) {
327                 if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
328                         error = iicbus_write(parent, &byte, 1, &sent, 0);
329
330                 iicbus_stop(parent);
331         }
332
333         return (error);
334 }
335
336 static int
337 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
338 {
339         device_t parent = device_get_parent(dev);
340         int error, sent;
341
342         char low = (char)(word & 0xff);
343         char high = (char)((word & 0xff00) >> 8);
344
345         error = iicbus_start(parent, slave & ~LSB, 0);
346
347         if (!error) {
348                 if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
349                   if (!(error = iicbus_write(parent, &low, 1, &sent, 0)))
350                     error = iicbus_write(parent, &high, 1, &sent, 0);
351
352                 iicbus_stop(parent);
353         }
354
355         return (error);
356 }
357
358 static int
359 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
360 {
361         device_t parent = device_get_parent(dev);
362         int error, sent, read;
363
364         if ((error = iicbus_start(parent, slave & ~LSB, 0)))
365                 return (error);
366
367         if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
368                 goto error;
369
370         if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
371                 goto error;
372
373         if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0)))
374                 goto error;
375
376 error:
377         iicbus_stop(parent);
378         return (error);
379 }
380
381 #define BUF2SHORT(low,high) \
382         ((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
383
384 static int
385 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
386 {
387         device_t parent = device_get_parent(dev);
388         int error, sent, read;
389         char buf[2];
390
391         if ((error = iicbus_start(parent, slave & ~LSB, 0)))
392                 return (error);
393
394         if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
395                 goto error;
396
397         if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
398                 goto error;
399
400         if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
401                 goto error;
402
403         /* first, receive low, then high byte */
404         *word = BUF2SHORT(buf[0], buf[1]);
405
406 error:
407         iicbus_stop(parent);
408         return (error);
409 }
410
411 static int
412 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
413 {
414         device_t parent = device_get_parent(dev);
415         int error, sent, read;
416         char buf[2];
417
418         if ((error = iicbus_start(parent, slave & ~LSB, 0)))
419                 return (error);
420
421         if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
422                 goto error;
423
424         /* first, send low, then high byte */
425         buf[0] = (char)(sdata & 0xff);
426         buf[1] = (char)((sdata & 0xff00) >> 8);
427
428         if ((error = iicbus_write(parent, buf, 2, &sent, 0)))
429                 goto error;
430
431         if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
432                 goto error;
433
434         if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
435                 goto error;
436
437         /* first, receive low, then high byte */
438         *rdata = BUF2SHORT(buf[0], buf[1]);
439
440 error:
441         iicbus_stop(parent);
442         return (error);
443 }
444
445 static int
446 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
447 {
448         device_t parent = device_get_parent(dev);
449         int error, sent;
450
451         if ((error = iicbus_start(parent, slave & ~LSB, 0)))
452                 goto error;
453
454         if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
455                 goto error;
456
457         if ((error = iicbus_write(parent, buf, (int)count, &sent, 0)))
458                 goto error;
459
460         if ((error = iicbus_stop(parent)))
461                 goto error;
462
463 error:
464         return (error);
465 }
466
467 static int
468 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
469 {
470         device_t parent = device_get_parent(dev);
471         int error, sent, read;
472
473         if ((error = iicbus_start(parent, slave & ~LSB, 0)))
474                 return (error);
475
476         if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
477                 goto error;
478
479         if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
480                 goto error;
481
482         if ((error = iicbus_read(parent, buf, (int)count, &read,
483                                                         IIC_LAST_READ, 0)))
484                 goto error;
485
486 error:
487         iicbus_stop(parent);
488         return (error);
489 }
490
491 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);