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