]> CyberLeo.Net >> Repos - FreeBSD/releng/8.1.git/blob - sys/dev/iicbus/iicsmb.c
Copy stable/8 to releng/8.1 in preparation for 8.1-RC1.
[FreeBSD/releng/8.1.git] / sys / dev / iicbus / iicsmb.c
1 /*-
2  * Copyright (c) 1998, 2001 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$
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/bus.h>
50 #include <sys/kernel.h>
51 #include <sys/lock.h>
52 #include <sys/module.h>
53 #include <sys/mutex.h>
54 #include <sys/systm.h>
55 #include <sys/uio.h>
56
57 #include <dev/iicbus/iiconf.h>
58 #include <dev/iicbus/iicbus.h>
59
60 #include <dev/smbus/smbconf.h>
61
62 #include "iicbus_if.h"
63 #include "smbus_if.h"
64
65 struct iicsmb_softc {
66
67 #define SMB_WAITING_ADDR        0x0
68 #define SMB_WAITING_LOW         0x1
69 #define SMB_WAITING_HIGH        0x2
70 #define SMB_DONE                0x3
71         int state;
72
73         u_char devaddr;                 /* slave device address */
74
75         char low;                       /* low byte received first */
76         char high;                      /* high byte */
77
78         struct mtx lock;
79         device_t smbus;
80 };
81
82 static int iicsmb_probe(device_t);
83 static int iicsmb_attach(device_t);
84 static int iicsmb_detach(device_t);
85 static void iicsmb_identify(driver_t *driver, device_t parent);
86
87 static int iicsmb_intr(device_t dev, int event, char *buf);
88 static int iicsmb_callback(device_t dev, int index, void *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_identify,      iicsmb_identify),
105         DEVMETHOD(device_probe,         iicsmb_probe),
106         DEVMETHOD(device_attach,        iicsmb_attach),
107         DEVMETHOD(device_detach,        iicsmb_detach),
108
109         /* bus interface */
110         DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
111         DEVMETHOD(bus_print_child,      bus_generic_print_child),
112
113         /* iicbus interface */
114         DEVMETHOD(iicbus_intr,          iicsmb_intr),
115
116         /* smbus interface */
117         DEVMETHOD(smbus_callback,       iicsmb_callback),
118         DEVMETHOD(smbus_quick,          iicsmb_quick),
119         DEVMETHOD(smbus_sendb,          iicsmb_sendb),
120         DEVMETHOD(smbus_recvb,          iicsmb_recvb),
121         DEVMETHOD(smbus_writeb,         iicsmb_writeb),
122         DEVMETHOD(smbus_writew,         iicsmb_writew),
123         DEVMETHOD(smbus_readb,          iicsmb_readb),
124         DEVMETHOD(smbus_readw,          iicsmb_readw),
125         DEVMETHOD(smbus_pcall,          iicsmb_pcall),
126         DEVMETHOD(smbus_bwrite,         iicsmb_bwrite),
127         DEVMETHOD(smbus_bread,          iicsmb_bread),
128         
129         { 0, 0 }
130 };
131
132 static driver_t iicsmb_driver = {
133         "iicsmb",
134         iicsmb_methods,
135         sizeof(struct iicsmb_softc),
136 };
137
138 #define IICBUS_TIMEOUT  100     /* us */
139
140 static void
141 iicsmb_identify(driver_t *driver, device_t parent)
142 {
143
144         if (device_find_child(parent, "iicsmb", -1) == NULL)
145                 BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
146 }
147
148 static int
149 iicsmb_probe(device_t dev)
150 {
151         device_set_desc(dev, "SMBus over I2C bridge");
152         return (BUS_PROBE_NOWILDCARD);
153 }
154
155 static int
156 iicsmb_attach(device_t dev)
157 {
158         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
159
160         mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
161
162         sc->smbus = device_add_child(dev, "smbus", -1);
163
164         /* probe and attach the smbus */
165         bus_generic_attach(dev);
166
167         return (0);
168 }
169
170 static int
171 iicsmb_detach(device_t dev)
172 {
173         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
174         
175         bus_generic_detach(dev);
176         if (sc->smbus) {
177                 device_delete_child(dev, sc->smbus);
178         }
179         mtx_destroy(&sc->lock);
180
181         return (0);
182 }
183
184 /*
185  * iicsmb_intr()
186  *
187  * iicbus interrupt handler
188  */
189 static int
190 iicsmb_intr(device_t dev, int event, char *buf)
191 {
192         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
193
194         mtx_lock(&sc->lock);
195         switch (event) {
196         case INTR_GENERAL:
197         case INTR_START:
198                 sc->state = SMB_WAITING_ADDR;
199                 break;
200
201         case INTR_STOP:
202                 /* call smbus intr handler */
203                 smbus_intr(sc->smbus, sc->devaddr,
204                                 sc->low, sc->high, SMB_ENOERR);
205                 break;
206
207         case INTR_RECEIVE:
208                 switch (sc->state) {
209                 case SMB_DONE:
210                         /* XXX too much data, discard */
211                         printf("%s: too much data from 0x%x\n", __func__,
212                                 sc->devaddr & 0xff);
213                         goto end;
214
215                 case SMB_WAITING_ADDR:
216                         sc->devaddr = (u_char)*buf;
217                         sc->state = SMB_WAITING_LOW;
218                         break;
219
220                 case SMB_WAITING_LOW:
221                         sc->low = *buf;
222                         sc->state = SMB_WAITING_HIGH;
223                         break;
224
225                 case SMB_WAITING_HIGH:
226                         sc->high = *buf;
227                         sc->state = SMB_DONE;
228                         break;
229                 }
230 end:
231                 break;
232
233         case INTR_TRANSMIT:
234         case INTR_NOACK:
235                 break;
236
237         case INTR_ERROR:
238                 switch (*buf) {
239                 case IIC_EBUSERR:
240                         smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
241                         break;
242
243                 default:
244                         printf("%s unknown error 0x%x!\n", __func__,
245                                                                 (int)*buf);
246                         break;
247                 }
248                 break;
249
250         default:
251                 panic("%s: unknown event (%d)!", __func__, event);
252         }
253         mtx_unlock(&sc->lock);
254
255         return (0);
256 }
257
258 static int
259 iicsmb_callback(device_t dev, int index, void *data)
260 {
261         device_t parent = device_get_parent(dev);
262         int error = 0;
263         int how;
264
265         switch (index) {
266         case SMB_REQUEST_BUS:
267                 /* request underlying iicbus */
268                 how = *(int *)data;
269                 error = iicbus_request_bus(parent, dev, how);
270                 break;
271
272         case SMB_RELEASE_BUS:
273                 /* release underlying iicbus */
274                 error = iicbus_release_bus(parent, dev);
275                 break;
276
277         default:
278                 error = EINVAL;
279         }
280
281         return (error);
282 }
283
284 static int
285 iicsmb_quick(device_t dev, u_char slave, int how)
286 {
287         device_t parent = device_get_parent(dev);
288         int error;
289
290         switch (how) {
291         case SMB_QWRITE:
292                 error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
293                 break;
294
295         case SMB_QREAD:
296                 error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT);
297                 break;
298
299         default:
300                 error = EINVAL;
301                 break;
302         }
303
304         if (!error)
305                 error = iicbus_stop(parent);
306                 
307         return (error);
308 }
309
310 static int
311 iicsmb_sendb(device_t dev, u_char slave, char byte)
312 {
313         device_t parent = device_get_parent(dev);
314         int error, sent;
315
316         error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
317
318         if (!error) {
319                 error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
320
321                 iicbus_stop(parent);
322         }
323
324         return (error);
325 }
326
327 static int
328 iicsmb_recvb(device_t dev, u_char slave, char *byte)
329 {
330         device_t parent = device_get_parent(dev);
331         int error, read;
332
333         error = iicbus_start(parent, slave | LSB, 0);
334
335         if (!error) {
336                 error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT);
337
338                 iicbus_stop(parent);
339         }
340
341         return (error);
342 }
343
344 static int
345 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
346 {
347         device_t parent = device_get_parent(dev);
348         int error, sent;
349
350         error = iicbus_start(parent, slave & ~LSB, 0);
351
352         if (!error) {
353                 if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
354                         error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
355
356                 iicbus_stop(parent);
357         }
358
359         return (error);
360 }
361
362 static int
363 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
364 {
365         device_t parent = device_get_parent(dev);
366         int error, sent;
367
368         char low = (char)(word & 0xff);
369         char high = (char)((word & 0xff00) >> 8);
370
371         error = iicbus_start(parent, slave & ~LSB, 0);
372
373         if (!error) {
374                 if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
375                   if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT)))
376                     error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT);
377
378                 iicbus_stop(parent);
379         }
380
381         return (error);
382 }
383
384 static int
385 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
386 {
387         device_t parent = device_get_parent(dev);
388         int error, sent, read;
389
390         if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
391                 return (error);
392
393         if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
394                 goto error;
395
396         if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
397                 goto error;
398
399         if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
400                 goto error;
401
402 error:
403         iicbus_stop(parent);
404         return (error);
405 }
406
407 #define BUF2SHORT(low,high) \
408         ((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
409
410 static int
411 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
412 {
413         device_t parent = device_get_parent(dev);
414         int error, sent, read;
415         char buf[2];
416
417         if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
418                 return (error);
419
420         if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
421                 goto error;
422
423         if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
424                 goto error;
425
426         if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
427                 goto error;
428
429         /* first, receive low, then high byte */
430         *word = BUF2SHORT(buf[0], buf[1]);
431
432 error:
433         iicbus_stop(parent);
434         return (error);
435 }
436
437 static int
438 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
439 {
440         device_t parent = device_get_parent(dev);
441         int error, sent, read;
442         char buf[2];
443
444         if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
445                 return (error);
446
447         if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
448                 goto error;
449
450         /* first, send low, then high byte */
451         buf[0] = (char)(sdata & 0xff);
452         buf[1] = (char)((sdata & 0xff00) >> 8);
453
454         if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT)))
455                 goto error;
456
457         if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
458                 goto error;
459
460         if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
461                 goto error;
462
463         /* first, receive low, then high byte */
464         *rdata = BUF2SHORT(buf[0], buf[1]);
465
466 error:
467         iicbus_stop(parent);
468         return (error);
469 }
470
471 static int
472 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
473 {
474         device_t parent = device_get_parent(dev);
475         int error, sent;
476
477         if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
478                 goto error;
479
480         if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
481                 goto error;
482
483         if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
484                 goto error;
485
486         if ((error = iicbus_stop(parent)))
487                 goto error;
488
489 error:
490         return (error);
491 }
492
493 static int
494 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
495 {
496         device_t parent = device_get_parent(dev);
497         int error, sent, read;
498
499         if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
500                 return (error);
501
502         if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
503                 goto error;
504
505         if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
506                 goto error;
507
508         if ((error = iicbus_read(parent, buf, (int)*count, &read,
509                                                 IIC_LAST_READ, IICBUS_TIMEOUT)))
510                 goto error;
511         *count = read;
512
513 error:
514         iicbus_stop(parent);
515         return (error);
516 }
517
518 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
519 DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
520 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
521 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
522 MODULE_VERSION(iicsmb, 1);