xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 39beb93c3f8bdbf72a61fda42300b5ebed7390c8)
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);
523