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