xref: /dragonfly/sys/bus/iicbus/iicsmb.c (revision 92fc8b5c)
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: src/sys/dev/iicbus/iicsmb.c,v 1.18 2009/02/10 22:50:23 imp Exp $
27  * $DragonFly: src/sys/bus/iicbus/iicsmb.c,v 1.5 2006/12/22 23:12:16 swildner Exp $
28  *
29  */
30 
31 /*
32  * I2C to SMB bridge
33  *
34  * Example:
35  *
36  *     smb bttv
37  *       \ /
38  *      smbus
39  *       /  \
40  *    iicsmb bti2c
41  *       |
42  *     iicbus
43  *     /  |  \
44  *  iicbb pcf ...
45  *    |
46  *  lpbb
47  */
48 
49 #include <sys/param.h>
50 #include <sys/bus.h>
51 #include <sys/kernel.h>
52 #include <sys/module.h>
53 #include <sys/systm.h>
54 #include <sys/uio.h>
55 
56 #include <bus/iicbus/iiconf.h>
57 #include <bus/iicbus/iicbus.h>
58 
59 #include <bus/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 int 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 
142 	if (device_find_child(parent, "iicsmb", -1) == NULL)
143 		BUS_ADD_CHILD(parent, parent, 0, "iicsmb", -1);
144 }
145 
146 static int
147 iicsmb_probe(device_t dev)
148 {
149 	device_set_desc(dev, "SMBus over I2C bridge");
150 	return (0);
151 }
152 
153 static int
154 iicsmb_attach(device_t dev)
155 {
156 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
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 
176 	return (0);
177 }
178 
179 /*
180  * iicsmb_intr()
181  *
182  * iicbus interrupt handler
183  */
184 static int
185 iicsmb_intr(device_t dev, int event, char *buf)
186 {
187 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
188 
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 			kprintf("%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 			kprintf("%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 
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 iicsmb_quick(device_t dev, u_char slave, int how)
279 {
280 	device_t parent = device_get_parent(dev);
281 	int error;
282 
283 	switch (how) {
284 	case SMB_QWRITE:
285 		error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
286 		break;
287 
288 	case SMB_QREAD:
289 		error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT);
290 		break;
291 
292 	default:
293 		error = EINVAL;
294 		break;
295 	}
296 
297 	if (!error)
298 		error = iicbus_stop(parent);
299 
300 	return (error);
301 }
302 
303 static int
304 iicsmb_sendb(device_t dev, u_char slave, char byte)
305 {
306 	device_t parent = device_get_parent(dev);
307 	int error, sent;
308 
309 	error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
310 
311 	if (!error) {
312 		error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
313 
314 		iicbus_stop(parent);
315 	}
316 
317 	return (error);
318 }
319 
320 static int
321 iicsmb_recvb(device_t dev, u_char slave, char *byte)
322 {
323 	device_t parent = device_get_parent(dev);
324 	int error, read;
325 
326 	error = iicbus_start(parent, slave | LSB, 0);
327 
328 	if (!error) {
329 		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT);
330 
331 		iicbus_stop(parent);
332 	}
333 
334 	return (error);
335 }
336 
337 static int
338 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
339 {
340 	device_t parent = device_get_parent(dev);
341 	int error, sent;
342 
343 	error = iicbus_start(parent, slave & ~LSB, 0);
344 
345 	if (!error) {
346 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
347 			error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
348 
349 		iicbus_stop(parent);
350 	}
351 
352 	return (error);
353 }
354 
355 static int
356 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
357 {
358 	device_t parent = device_get_parent(dev);
359 	int error, sent;
360 
361 	char low = (char)(word & 0xff);
362 	char high = (char)((word & 0xff00) >> 8);
363 
364 	error = iicbus_start(parent, slave & ~LSB, 0);
365 
366 	if (!error) {
367 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
368 		  if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT)))
369 		    error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT);
370 
371 		iicbus_stop(parent);
372 	}
373 
374 	return (error);
375 }
376 
377 static int
378 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
379 {
380 	device_t parent = device_get_parent(dev);
381 	int error, sent, read;
382 
383 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
384 		return (error);
385 
386 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
387 		goto error;
388 
389 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
390 		goto error;
391 
392 	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
393 		goto error;
394 
395 error:
396 	iicbus_stop(parent);
397 	return (error);
398 }
399 
400 #define BUF2SHORT(low,high) \
401 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
402 
403 static int
404 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
405 {
406 	device_t parent = device_get_parent(dev);
407 	int error, sent, read;
408 	char buf[2];
409 
410 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
411 		return (error);
412 
413 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
414 		goto error;
415 
416 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
417 		goto error;
418 
419 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
420 		goto error;
421 
422 	/* first, receive low, then high byte */
423 	*word = BUF2SHORT(buf[0], buf[1]);
424 
425 error:
426 	iicbus_stop(parent);
427 	return (error);
428 }
429 
430 static int
431 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
432 {
433 	device_t parent = device_get_parent(dev);
434 	int error, sent, read;
435 	char buf[2];
436 
437 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
438 		return (error);
439 
440 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
441 		goto error;
442 
443 	/* first, send low, then high byte */
444 	buf[0] = (char)(sdata & 0xff);
445 	buf[1] = (char)((sdata & 0xff00) >> 8);
446 
447 	if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT)))
448 		goto error;
449 
450 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
451 		goto error;
452 
453 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
454 		goto error;
455 
456 	/* first, receive low, then high byte */
457 	*rdata = BUF2SHORT(buf[0], buf[1]);
458 
459 error:
460 	iicbus_stop(parent);
461 	return (error);
462 }
463 
464 static int
465 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
466 {
467 	device_t parent = device_get_parent(dev);
468 	int error, sent;
469 
470 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
471 		goto error;
472 
473 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
474 		goto error;
475 
476 	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
477 		goto error;
478 
479 	if ((error = iicbus_stop(parent)))
480 		goto error;
481 
482 error:
483 	return (error);
484 }
485 
486 static int
487 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
488 {
489 	device_t parent = device_get_parent(dev);
490 	int error, sent, read;
491 
492 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
493 		return (error);
494 
495 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
496 		goto error;
497 
498 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
499 		goto error;
500 
501 	if ((error = iicbus_read(parent, buf, (int)*count, &read,
502 						IIC_LAST_READ, IICBUS_TIMEOUT)))
503 		goto error;
504 	*count = read;
505 
506 error:
507 	iicbus_stop(parent);
508 	return (error);
509 }
510 
511 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
512 DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
513 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
514 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
515 MODULE_VERSION(iicsmb, 1);
516