xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
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/kernel.h>
50 #include <sys/systm.h>
51 #include <sys/module.h>
52 #include <sys/bus.h>
53 #include <sys/uio.h>
54 
55 
56 #include <dev/iicbus/iiconf.h>
57 #include <dev/iicbus/iicbus.h>
58 
59 #include <dev/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 void 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 	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 (0);
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 	sc->smbus = device_add_child(dev, "smbus", -1);
157 
158 	/* probe and attach the smbus */
159 	bus_generic_attach(dev);
160 
161 	return (0);
162 }
163 
164 static int
165 iicsmb_detach(device_t dev)
166 {
167 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
168 
169 	bus_generic_detach(dev);
170 	if (sc->smbus) {
171 		device_delete_child(dev, sc->smbus);
172 	}
173 
174 	return (0);
175 }
176 
177 /*
178  * iicsmb_intr()
179  *
180  * iicbus interrupt handler
181  */
182 static void
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 	switch (event) {
188 	case INTR_GENERAL:
189 	case INTR_START:
190 		sc->state = SMB_WAITING_ADDR;
191 		break;
192 
193 	case INTR_STOP:
194 		/* call smbus intr handler */
195 		smbus_intr(sc->smbus, sc->devaddr,
196 				sc->low, sc->high, SMB_ENOERR);
197 		break;
198 
199 	case INTR_RECEIVE:
200 		switch (sc->state) {
201 		case SMB_DONE:
202 			/* XXX too much data, discard */
203 			printf("%s: too much data from 0x%x\n", __func__,
204 				sc->devaddr & 0xff);
205 			goto end;
206 
207 		case SMB_WAITING_ADDR:
208 			sc->devaddr = (u_char)*buf;
209 			sc->state = SMB_WAITING_LOW;
210 			break;
211 
212 		case SMB_WAITING_LOW:
213 			sc->low = *buf;
214 			sc->state = SMB_WAITING_HIGH;
215 			break;
216 
217 		case SMB_WAITING_HIGH:
218 			sc->high = *buf;
219 			sc->state = SMB_DONE;
220 			break;
221 		}
222 end:
223 		break;
224 
225 	case INTR_TRANSMIT:
226 	case INTR_NOACK:
227 		break;
228 
229 	case INTR_ERROR:
230 		switch (*buf) {
231 		case IIC_EBUSERR:
232 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
233 			break;
234 
235 		default:
236 			printf("%s unknown error 0x%x!\n", __func__,
237 								(int)*buf);
238 			break;
239 		}
240 		break;
241 
242 	default:
243 		panic("%s: unknown event (%d)!", __func__, event);
244 	}
245 
246 	return;
247 }
248 
249 static int
250 iicsmb_callback(device_t dev, int index, void *data)
251 {
252 	device_t parent = device_get_parent(dev);
253 	int error = 0;
254 	int how;
255 
256 	switch (index) {
257 	case SMB_REQUEST_BUS:
258 		/* request underlying iicbus */
259 		how = *(int *)data;
260 		error = iicbus_request_bus(parent, dev, how);
261 		break;
262 
263 	case SMB_RELEASE_BUS:
264 		/* release underlying iicbus */
265 		error = iicbus_release_bus(parent, dev);
266 		break;
267 
268 	default:
269 		error = EINVAL;
270 	}
271 
272 	return (error);
273 }
274 
275 static int
276 iicsmb_quick(device_t dev, u_char slave, int how)
277 {
278 	device_t parent = device_get_parent(dev);
279 	int error;
280 
281 	switch (how) {
282 	case SMB_QWRITE:
283 		error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
284 		break;
285 
286 	case SMB_QREAD:
287 		error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT);
288 		break;
289 
290 	default:
291 		error = EINVAL;
292 		break;
293 	}
294 
295 	if (!error)
296 		error = iicbus_stop(parent);
297 
298 	return (error);
299 }
300 
301 static int
302 iicsmb_sendb(device_t dev, u_char slave, char byte)
303 {
304 	device_t parent = device_get_parent(dev);
305 	int error, sent;
306 
307 	error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
308 
309 	if (!error) {
310 		error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
311 
312 		iicbus_stop(parent);
313 	}
314 
315 	return (error);
316 }
317 
318 static int
319 iicsmb_recvb(device_t dev, u_char slave, char *byte)
320 {
321 	device_t parent = device_get_parent(dev);
322 	int error, read;
323 
324 	error = iicbus_start(parent, slave | LSB, 0);
325 
326 	if (!error) {
327 		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT);
328 
329 		iicbus_stop(parent);
330 	}
331 
332 	return (error);
333 }
334 
335 static int
336 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
337 {
338 	device_t parent = device_get_parent(dev);
339 	int error, sent;
340 
341 	error = iicbus_start(parent, slave & ~LSB, 0);
342 
343 	if (!error) {
344 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
345 			error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
346 
347 		iicbus_stop(parent);
348 	}
349 
350 	return (error);
351 }
352 
353 static int
354 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
355 {
356 	device_t parent = device_get_parent(dev);
357 	int error, sent;
358 
359 	char low = (char)(word & 0xff);
360 	char high = (char)((word & 0xff00) >> 8);
361 
362 	error = iicbus_start(parent, slave & ~LSB, 0);
363 
364 	if (!error) {
365 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
366 		  if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT)))
367 		    error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT);
368 
369 		iicbus_stop(parent);
370 	}
371 
372 	return (error);
373 }
374 
375 static int
376 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
377 {
378 	device_t parent = device_get_parent(dev);
379 	int error, sent, read;
380 
381 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
382 		return (error);
383 
384 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
385 		goto error;
386 
387 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
388 		goto error;
389 
390 	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
391 		goto error;
392 
393 error:
394 	iicbus_stop(parent);
395 	return (error);
396 }
397 
398 #define BUF2SHORT(low,high) \
399 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
400 
401 static int
402 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
403 {
404 	device_t parent = device_get_parent(dev);
405 	int error, sent, read;
406 	char buf[2];
407 
408 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
409 		return (error);
410 
411 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
412 		goto error;
413 
414 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
415 		goto error;
416 
417 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
418 		goto error;
419 
420 	/* first, receive low, then high byte */
421 	*word = BUF2SHORT(buf[0], buf[1]);
422 
423 error:
424 	iicbus_stop(parent);
425 	return (error);
426 }
427 
428 static int
429 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
430 {
431 	device_t parent = device_get_parent(dev);
432 	int error, sent, read;
433 	char buf[2];
434 
435 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
436 		return (error);
437 
438 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
439 		goto error;
440 
441 	/* first, send low, then high byte */
442 	buf[0] = (char)(sdata & 0xff);
443 	buf[1] = (char)((sdata & 0xff00) >> 8);
444 
445 	if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT)))
446 		goto error;
447 
448 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
449 		goto error;
450 
451 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
452 		goto error;
453 
454 	/* first, receive low, then high byte */
455 	*rdata = BUF2SHORT(buf[0], buf[1]);
456 
457 error:
458 	iicbus_stop(parent);
459 	return (error);
460 }
461 
462 static int
463 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
464 {
465 	device_t parent = device_get_parent(dev);
466 	int error, sent;
467 
468 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
469 		goto error;
470 
471 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
472 		goto error;
473 
474 	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
475 		goto error;
476 
477 	if ((error = iicbus_stop(parent)))
478 		goto error;
479 
480 error:
481 	return (error);
482 }
483 
484 static int
485 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
486 {
487 	device_t parent = device_get_parent(dev);
488 	int error, sent, read;
489 
490 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
491 		return (error);
492 
493 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
494 		goto error;
495 
496 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
497 		goto error;
498 
499 	if ((error = iicbus_read(parent, buf, (int)*count, &read,
500 						IIC_LAST_READ, IICBUS_TIMEOUT)))
501 		goto error;
502 	*count = read;
503 
504 error:
505 	iicbus_stop(parent);
506 	return (error);
507 }
508 
509 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
510 DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
511 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
512 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
513 MODULE_VERSION(iicsmb, 1);
514