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