]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/iicsmb.c
Import tzdata 2017c
[FreeBSD/FreeBSD.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/smb.h>
61 #include <dev/smbus/smbconf.h>
62
63 #include "iicbus_if.h"
64 #include "smbus_if.h"
65
66 struct iicsmb_softc {
67
68 #define SMB_WAITING_ADDR        0x0
69 #define SMB_WAITING_LOW         0x1
70 #define SMB_WAITING_HIGH        0x2
71 #define SMB_DONE                0x3
72         int state;
73
74         u_char devaddr;                 /* slave device address */
75
76         char low;                       /* low byte received first */
77         char high;                      /* high byte */
78
79         struct mtx lock;
80         device_t smbus;
81 };
82
83 static int iicsmb_probe(device_t);
84 static int iicsmb_attach(device_t);
85 static int iicsmb_detach(device_t);
86 static void iicsmb_identify(driver_t *driver, device_t parent);
87
88 static int iicsmb_intr(device_t dev, int event, char *buf);
89 static int iicsmb_callback(device_t dev, int index, void *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_identify,      iicsmb_identify),
106         DEVMETHOD(device_probe,         iicsmb_probe),
107         DEVMETHOD(device_attach,        iicsmb_attach),
108         DEVMETHOD(device_detach,        iicsmb_detach),
109
110         /* iicbus interface */
111         DEVMETHOD(iicbus_intr,          iicsmb_intr),
112
113         /* smbus interface */
114         DEVMETHOD(smbus_callback,       iicsmb_callback),
115         DEVMETHOD(smbus_quick,          iicsmb_quick),
116         DEVMETHOD(smbus_sendb,          iicsmb_sendb),
117         DEVMETHOD(smbus_recvb,          iicsmb_recvb),
118         DEVMETHOD(smbus_writeb,         iicsmb_writeb),
119         DEVMETHOD(smbus_writew,         iicsmb_writew),
120         DEVMETHOD(smbus_readb,          iicsmb_readb),
121         DEVMETHOD(smbus_readw,          iicsmb_readw),
122         DEVMETHOD(smbus_pcall,          iicsmb_pcall),
123         DEVMETHOD(smbus_bwrite,         iicsmb_bwrite),
124         DEVMETHOD(smbus_bread,          iicsmb_bread),
125
126         DEVMETHOD_END
127 };
128
129 static driver_t iicsmb_driver = {
130         "iicsmb",
131         iicsmb_methods,
132         sizeof(struct iicsmb_softc),
133 };
134
135 static void
136 iicsmb_identify(driver_t *driver, device_t parent)
137 {
138
139         if (device_find_child(parent, "iicsmb", -1) == NULL)
140                 BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
141 }
142
143 static int
144 iicsmb_probe(device_t dev)
145 {
146         device_set_desc(dev, "SMBus over I2C bridge");
147         return (BUS_PROBE_NOWILDCARD);
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         mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
156
157         sc->smbus = device_add_child(dev, "smbus", -1);
158
159         /* probe and attach the smbus */
160         bus_generic_attach(dev);
161
162         return (0);
163 }
164
165 static int
166 iicsmb_detach(device_t dev)
167 {
168         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
169
170         bus_generic_detach(dev);
171         device_delete_children(dev);
172         mtx_destroy(&sc->lock);
173
174         return (0);
175 }
176
177 /*
178  * iicsmb_intr()
179  *
180  * iicbus interrupt handler
181  */
182 static int
183 iicsmb_intr(device_t dev, int event, char *buf)
184 {
185         struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
186
187         mtx_lock(&sc->lock);
188         switch (event) {
189         case INTR_GENERAL:
190         case INTR_START:
191                 sc->state = SMB_WAITING_ADDR;
192                 break;
193
194         case INTR_STOP:
195                 /* call smbus intr handler */
196                 smbus_intr(sc->smbus, sc->devaddr,
197                                 sc->low, sc->high, SMB_ENOERR);
198                 break;
199
200         case INTR_RECEIVE:
201                 switch (sc->state) {
202                 case SMB_DONE:
203                         /* XXX too much data, discard */
204                         printf("%s: too much data from 0x%x\n", __func__,
205                                 sc->devaddr & 0xff);
206                         goto end;
207
208                 case SMB_WAITING_ADDR:
209                         sc->devaddr = (u_char)*buf;
210                         sc->state = SMB_WAITING_LOW;
211                         break;
212
213                 case SMB_WAITING_LOW:
214                         sc->low = *buf;
215                         sc->state = SMB_WAITING_HIGH;
216                         break;
217
218                 case SMB_WAITING_HIGH:
219                         sc->high = *buf;
220                         sc->state = SMB_DONE;
221                         break;
222                 }
223 end:
224                 break;
225
226         case INTR_TRANSMIT:
227         case INTR_NOACK:
228                 break;
229
230         case INTR_ERROR:
231                 switch (*buf) {
232                 case IIC_EBUSERR:
233                         smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
234                         break;
235
236                 default:
237                         printf("%s unknown error 0x%x!\n", __func__,
238                                                                 (int)*buf);
239                         break;
240                 }
241                 break;
242
243         default:
244                 panic("%s: unknown event (%d)!", __func__, event);
245         }
246         mtx_unlock(&sc->lock);
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 iic2smb_error(int error)
279 {
280         switch (error) {
281         case IIC_NOERR:
282                 return (SMB_ENOERR);
283         case IIC_EBUSERR:
284                 return (SMB_EBUSERR);
285         case IIC_ENOACK:
286                 return (SMB_ENOACK);
287         case IIC_ETIMEOUT:
288                 return (SMB_ETIMEOUT);
289         case IIC_EBUSBSY:
290                 return (SMB_EBUSY);
291         case IIC_ESTATUS:
292                 return (SMB_EBUSERR);
293         case IIC_EUNDERFLOW:
294                 return (SMB_EBUSERR);
295         case IIC_EOVERFLOW:
296                 return (SMB_EBUSERR);
297         case IIC_ENOTSUPP:
298                 return (SMB_ENOTSUPP);
299         case IIC_ENOADDR:
300                 return (SMB_EBUSERR);
301         case IIC_ERESOURCE:
302                 return (SMB_EBUSERR);
303         default:
304                 return (SMB_EBUSERR);
305         }
306 }
307
308 #define TRANSFER_MSGS(dev, msgs)        iicbus_transfer(dev, msgs, nitems(msgs))
309
310 static int
311 iicsmb_quick(device_t dev, u_char slave, int how)
312 {
313         struct iic_msg msgs[] = {
314              { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
315         };
316         int error;
317
318         switch (how) {
319         case SMB_QWRITE:
320         case SMB_QREAD:
321                 break;
322         default:
323                 return (SMB_EINVAL);
324         }
325
326         error = TRANSFER_MSGS(dev, msgs);
327         return (iic2smb_error(error));
328 }
329
330 static int
331 iicsmb_sendb(device_t dev, u_char slave, char byte)
332 {
333         struct iic_msg msgs[] = {
334              { slave, IIC_M_WR, 1, &byte },
335         };
336         int error;
337
338         error = TRANSFER_MSGS(dev, msgs);
339         return (iic2smb_error(error));
340 }
341
342 static int
343 iicsmb_recvb(device_t dev, u_char slave, char *byte)
344 {
345         struct iic_msg msgs[] = {
346              { slave, IIC_M_RD, 1, byte },
347         };
348         int error;
349
350         error = TRANSFER_MSGS(dev, msgs);
351         return (iic2smb_error(error));
352 }
353
354 static int
355 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
356 {
357         uint8_t bytes[] = { cmd, byte };
358         struct iic_msg msgs[] = {
359              { slave, IIC_M_WR, nitems(bytes), bytes },
360         };
361         int error;
362
363         error = TRANSFER_MSGS(dev, msgs);
364         return (iic2smb_error(error));
365 }
366
367 static int
368 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
369 {
370         uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
371         struct iic_msg msgs[] = {
372              { slave, IIC_M_WR, nitems(bytes), bytes },
373         };
374         int error;
375
376         error = TRANSFER_MSGS(dev, msgs);
377         return (iic2smb_error(error));
378 }
379
380 static int
381 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
382 {
383         struct iic_msg msgs[] = {
384              { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
385              { slave, IIC_M_RD, 1, byte },
386         };
387         int error;
388
389         error = TRANSFER_MSGS(dev, msgs);
390         return (iic2smb_error(error));
391 }
392
393 static int
394 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
395 {
396         uint8_t buf[2];
397         struct iic_msg msgs[] = {
398              { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
399              { slave, IIC_M_RD, nitems(buf), buf },
400         };
401         int error;
402
403         error = TRANSFER_MSGS(dev, msgs);
404         if (error == 0)
405                 *word = ((uint16_t)buf[1] << 8) | buf[0];
406         return (iic2smb_error(error));
407 }
408
409 static int
410 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
411 {
412         uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
413         uint8_t out[2];
414         struct iic_msg msgs[] = {
415              { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
416              { slave, IIC_M_RD, nitems(out), out },
417         };
418         int error;
419
420         error = TRANSFER_MSGS(dev, msgs);
421         if (error == 0)
422                 *rdata = ((uint16_t)out[1] << 8) | out[0];
423         return (iic2smb_error(error));
424 }
425
426 static int
427 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
428 {
429         uint8_t bytes[2] = { cmd, count };
430         struct iic_msg msgs[] = {
431              { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
432              { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
433         };
434         int error;
435
436         if (count > SMB_MAXBLOCKSIZE || count == 0)
437                 return (SMB_EINVAL);
438         error = TRANSFER_MSGS(dev, msgs);
439         return (iic2smb_error(error));
440 }
441
442 static int
443 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
444 {
445         struct iic_msg msgs[] = {
446              { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
447              { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
448         };
449         struct iic_msg block_msg[] = {
450              { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
451         };
452         device_t parent = device_get_parent(dev);
453         int error;
454
455         /* Have to do this because the command is split in two transfers. */
456         error = iicbus_request_bus(parent, dev, IIC_WAIT);
457         if (error == 0)
458                 error = TRANSFER_MSGS(dev, msgs);
459         if (error == 0) {
460                 /*
461                  * If the slave offers an empty or a too long reply,
462                  * read one byte to generate the stop or abort.
463                  */
464                 if (*count > SMB_MAXBLOCKSIZE || *count == 0)
465                         block_msg[0].len = 1;
466                 else
467                         block_msg[0].len = *count;
468                 error = TRANSFER_MSGS(dev, block_msg);
469                 if (*count > SMB_MAXBLOCKSIZE || *count == 0)
470                         error = SMB_EINVAL;
471         }
472         (void)iicbus_release_bus(parent, dev);
473         return (iic2smb_error(error));
474 }
475
476 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
477 DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
478 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
479 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
480 MODULE_VERSION(iicsmb, 1);