xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 641a6cfb86023499caafe26a4d821a0b885cf00b)
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);
519