xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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 	device_delete_children(dev);
173 	mtx_destroy(&sc->lock);
174 
175 	return (0);
176 }
177 
178 /*
179  * iicsmb_intr()
180  *
181  * iicbus interrupt handler
182  */
183 static int
184 iicsmb_intr(device_t dev, int event, char *buf)
185 {
186 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
187 
188 	mtx_lock(&sc->lock);
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 			printf("%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 			printf("%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 	mtx_unlock(&sc->lock);
248 
249 	return (0);
250 }
251 
252 static int
253 iicsmb_callback(device_t dev, int index, void *data)
254 {
255 	device_t parent = device_get_parent(dev);
256 	int error = 0;
257 	int how;
258 
259 	switch (index) {
260 	case SMB_REQUEST_BUS:
261 		/* request underlying iicbus */
262 		how = *(int *)data;
263 		error = iicbus_request_bus(parent, dev, how);
264 		break;
265 
266 	case SMB_RELEASE_BUS:
267 		/* release underlying iicbus */
268 		error = iicbus_release_bus(parent, dev);
269 		break;
270 
271 	default:
272 		error = EINVAL;
273 	}
274 
275 	return (error);
276 }
277 
278 static int
279 iicsmb_quick(device_t dev, u_char slave, int how)
280 {
281 	device_t parent = device_get_parent(dev);
282 	int error;
283 
284 	switch (how) {
285 	case SMB_QWRITE:
286 		error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
287 		break;
288 
289 	case SMB_QREAD:
290 		error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT);
291 		break;
292 
293 	default:
294 		error = EINVAL;
295 		break;
296 	}
297 
298 	if (!error)
299 		error = iicbus_stop(parent);
300 
301 	return (error);
302 }
303 
304 static int
305 iicsmb_sendb(device_t dev, u_char slave, char byte)
306 {
307 	device_t parent = device_get_parent(dev);
308 	int error, sent;
309 
310 	error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
311 
312 	if (!error) {
313 		error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
314 
315 		iicbus_stop(parent);
316 	}
317 
318 	return (error);
319 }
320 
321 static int
322 iicsmb_recvb(device_t dev, u_char slave, char *byte)
323 {
324 	device_t parent = device_get_parent(dev);
325 	int error, read;
326 
327 	error = iicbus_start(parent, slave | LSB, 0);
328 
329 	if (!error) {
330 		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT);
331 
332 		iicbus_stop(parent);
333 	}
334 
335 	return (error);
336 }
337 
338 static int
339 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
340 {
341 	device_t parent = device_get_parent(dev);
342 	int error, sent;
343 
344 	error = iicbus_start(parent, slave & ~LSB, 0);
345 
346 	if (!error) {
347 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
348 			error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
349 
350 		iicbus_stop(parent);
351 	}
352 
353 	return (error);
354 }
355 
356 static int
357 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
358 {
359 	device_t parent = device_get_parent(dev);
360 	int error, sent;
361 
362 	char low = (char)(word & 0xff);
363 	char high = (char)((word & 0xff00) >> 8);
364 
365 	error = iicbus_start(parent, slave & ~LSB, 0);
366 
367 	if (!error) {
368 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
369 		  if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT)))
370 		    error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT);
371 
372 		iicbus_stop(parent);
373 	}
374 
375 	return (error);
376 }
377 
378 static int
379 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
380 {
381 	device_t parent = device_get_parent(dev);
382 	int error, sent, read;
383 
384 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
385 		return (error);
386 
387 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
388 		goto error;
389 
390 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
391 		goto error;
392 
393 	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
394 		goto error;
395 
396 error:
397 	iicbus_stop(parent);
398 	return (error);
399 }
400 
401 #define BUF2SHORT(low,high) \
402 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
403 
404 static int
405 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
406 {
407 	device_t parent = device_get_parent(dev);
408 	int error, sent, read;
409 	char buf[2];
410 
411 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
412 		return (error);
413 
414 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
415 		goto error;
416 
417 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
418 		goto error;
419 
420 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
421 		goto error;
422 
423 	/* first, receive low, then high byte */
424 	*word = BUF2SHORT(buf[0], buf[1]);
425 
426 error:
427 	iicbus_stop(parent);
428 	return (error);
429 }
430 
431 static int
432 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
433 {
434 	device_t parent = device_get_parent(dev);
435 	int error, sent, read;
436 	char buf[2];
437 
438 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
439 		return (error);
440 
441 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
442 		goto error;
443 
444 	/* first, send low, then high byte */
445 	buf[0] = (char)(sdata & 0xff);
446 	buf[1] = (char)((sdata & 0xff00) >> 8);
447 
448 	if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT)))
449 		goto error;
450 
451 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
452 		goto error;
453 
454 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
455 		goto error;
456 
457 	/* first, receive low, then high byte */
458 	*rdata = BUF2SHORT(buf[0], buf[1]);
459 
460 error:
461 	iicbus_stop(parent);
462 	return (error);
463 }
464 
465 static int
466 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
467 {
468 	device_t parent = device_get_parent(dev);
469 	int error, sent;
470 
471 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
472 		goto error;
473 
474 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
475 		goto error;
476 
477 	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
478 		goto error;
479 
480 	if ((error = iicbus_stop(parent)))
481 		goto error;
482 
483 error:
484 	return (error);
485 }
486 
487 static int
488 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
489 {
490 	device_t parent = device_get_parent(dev);
491 	int error, sent, read;
492 
493 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
494 		return (error);
495 
496 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
497 		goto error;
498 
499 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
500 		goto error;
501 
502 	if ((error = iicbus_read(parent, buf, (int)*count, &read,
503 						IIC_LAST_READ, IICBUS_TIMEOUT)))
504 		goto error;
505 	*count = read;
506 
507 error:
508 	iicbus_stop(parent);
509 	return (error);
510 }
511 
512 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
513 DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
514 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
515 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
516 MODULE_VERSION(iicsmb, 1);
517