xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 6e8394b8baa7d5d9153ab90de6824bcd19b3b4e1)
1 /*-
2  * Copyright (c) 1998 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  *	$Id: iicsmb.c,v 1.2 1998/10/31 11:31:07 nsouch Exp $
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/conf.h>
54 #include <sys/buf.h>
55 #include <sys/uio.h>
56 #include <sys/malloc.h>
57 
58 #include <machine/clock.h>
59 
60 #include <dev/iicbus/iiconf.h>
61 #include <dev/iicbus/iicbus.h>
62 
63 #include <dev/smbus/smbconf.h>
64 
65 #include "iicbus_if.h"
66 #include "smbus_if.h"
67 
68 struct iicsmb_softc {
69 
70 #define SMB_WAITING_ADDR	0x0
71 #define SMB_WAITING_LOW		0x1
72 #define SMB_WAITING_HIGH	0x2
73 #define SMB_DONE		0x3
74 	int state;
75 
76 	u_char devaddr;			/* slave device address */
77 
78 	char low;			/* low byte received first */
79 	char high;			/* high byte */
80 
81 	device_t smbus;
82 };
83 
84 static int iicsmb_probe(device_t);
85 static int iicsmb_attach(device_t);
86 static void iicsmb_print_child(device_t, device_t);
87 
88 static void iicsmb_intr(device_t dev, int event, char *buf);
89 static int iicsmb_callback(device_t dev, int index, caddr_t data);
90 static int iicsmb_quick(device_t dev, u_char slave, int how);
91 static int iicsmb_sendb(device_t dev, u_char slave, char byte);
92 static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
93 static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
94 static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
95 static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
96 static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
97 static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
98 static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
99 static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
100 
101 static devclass_t iicsmb_devclass;
102 
103 static device_method_t iicsmb_methods[] = {
104 	/* device interface */
105 	DEVMETHOD(device_probe,		iicsmb_probe),
106 	DEVMETHOD(device_attach,	iicsmb_attach),
107 
108 	/* bus interface */
109 	DEVMETHOD(bus_print_child,	iicsmb_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 static int
137 iicsmb_probe(device_t dev)
138 {
139 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
140 
141 	sc->smbus = smbus_alloc_bus(dev);
142 
143 	if (!sc->smbus)
144 		return (EINVAL);	/* XXX don't know what to return else */
145 
146 	return (0);
147 }
148 
149 static int
150 iicsmb_attach(device_t dev)
151 {
152 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
153 
154 	/* probe and attach the smbus */
155 	device_probe_and_attach(sc->smbus);
156 
157 	return (0);
158 }
159 
160 static void
161 iicsmb_print_child(device_t bus, device_t dev)
162 {
163 	printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
164 
165 	return;
166 }
167 
168 /*
169  * iicsmb_intr()
170  *
171  * iicbus interrupt handler
172  */
173 static void
174 iicsmb_intr(device_t dev, int event, char *buf)
175 {
176 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
177 
178 	switch (event) {
179 	case INTR_GENERAL:
180 	case INTR_START:
181 		sc->state = SMB_WAITING_ADDR;
182 		break;
183 
184 	case INTR_STOP:
185 		/* call smbus intr handler */
186 		smbus_intr(sc->smbus, sc->devaddr,
187 				sc->low, sc->high, SMB_ENOERR);
188 		break;
189 
190 	case INTR_RECEIVE:
191 		switch (sc->state) {
192 		case SMB_DONE:
193 			/* XXX too much data, discard */
194 			printf("%s: too much data from 0x%x\n", __FUNCTION__,
195 				sc->devaddr & 0xff);
196 			goto end;
197 
198 		case SMB_WAITING_ADDR:
199 			sc->devaddr = (u_char)*buf;
200 			sc->state = SMB_WAITING_LOW;
201 			break;
202 
203 		case SMB_WAITING_LOW:
204 			sc->low = *buf;
205 			sc->state = SMB_WAITING_HIGH;
206 			break;
207 
208 		case SMB_WAITING_HIGH:
209 			sc->high = *buf;
210 			sc->state = SMB_DONE;
211 			break;
212 		}
213 end:
214 		break;
215 
216 	case INTR_TRANSMIT:
217 	case INTR_NOACK:
218 		break;
219 
220 	case INTR_ERROR:
221 		switch (*buf) {
222 		case IIC_EBUSERR:
223 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
224 			break;
225 
226 		default:
227 			printf("%s unknown error 0x%x!\n", __FUNCTION__,
228 								(int)*buf);
229 			break;
230 		}
231 		break;
232 
233 	default:
234 		panic("%s: unknown event (%d)!", __FUNCTION__, event);
235 	}
236 
237 	return;
238 }
239 
240 static int
241 iicsmb_callback(device_t dev, int index, caddr_t data)
242 {
243 	device_t parent = device_get_parent(dev);
244 	int error = 0;
245 	int how;
246 
247 	switch (index) {
248 	case SMB_REQUEST_BUS:
249 		/* request underlying iicbus */
250 		how = *(int *)data;
251 		error = iicbus_request_bus(parent, dev, how);
252 		break;
253 
254 	case SMB_RELEASE_BUS:
255 		/* release underlying iicbus */
256 		error = iicbus_release_bus(parent, dev);
257 		break;
258 
259 	default:
260 		error = EINVAL;
261 	}
262 
263 	return (error);
264 }
265 
266 static int
267 iicsmb_quick(device_t dev, u_char slave, int how)
268 {
269 	device_t parent = device_get_parent(dev);
270 	int error;
271 
272 	switch (how) {
273 	case SMB_QWRITE:
274 		error = iicbus_start(parent, slave & ~LSB, 0);
275 		break;
276 
277 	case SMB_QREAD:
278 		error = iicbus_start(parent, slave | LSB, 0);
279 		break;
280 
281 	default:
282 		error = EINVAL;
283 		break;
284 	}
285 
286 	if (!error)
287 		error = iicbus_stop(parent);
288 
289 	return (error);
290 }
291 
292 static int
293 iicsmb_sendb(device_t dev, u_char slave, char byte)
294 {
295 	device_t parent = device_get_parent(dev);
296 	int error, sent;
297 
298 	error = iicbus_start(parent, slave & ~LSB, 0);
299 
300 	if (!error) {
301 		error = iicbus_write(parent, &byte, 1, &sent, 0);
302 
303 		iicbus_stop(parent);
304 	}
305 
306 	return (error);
307 }
308 
309 static int
310 iicsmb_recvb(device_t dev, u_char slave, char *byte)
311 {
312 	device_t parent = device_get_parent(dev);
313 	int error, read;
314 
315 	error = iicbus_start(parent, slave | LSB, 0);
316 
317 	if (!error) {
318 		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0);
319 
320 		iicbus_stop(parent);
321 	}
322 
323 	return (error);
324 }
325 
326 static int
327 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
328 {
329 	device_t parent = device_get_parent(dev);
330 	int error, sent;
331 
332 	error = iicbus_start(parent, slave & ~LSB, 0);
333 
334 	if (!error) {
335 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
336 			error = iicbus_write(parent, &byte, 1, &sent, 0);
337 
338 		iicbus_stop(parent);
339 	}
340 
341 	return (error);
342 }
343 
344 static int
345 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
346 {
347 	device_t parent = device_get_parent(dev);
348 	int error, sent;
349 
350 	char low = (char)(word & 0xff);
351 	char high = (char)((word & 0xff00) >> 8);
352 
353 	error = iicbus_start(parent, slave & ~LSB, 0);
354 
355 	if (!error) {
356 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
357 		  if (!(error = iicbus_write(parent, &low, 1, &sent, 0)))
358 		    error = iicbus_write(parent, &high, 1, &sent, 0);
359 
360 		iicbus_stop(parent);
361 	}
362 
363 	return (error);
364 }
365 
366 static int
367 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
368 {
369 	device_t parent = device_get_parent(dev);
370 	int error, sent, read;
371 
372 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
373 		return (error);
374 
375 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
376 		goto error;
377 
378 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
379 		goto error;
380 
381 	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0)))
382 		goto error;
383 
384 error:
385 	iicbus_stop(parent);
386 	return (error);
387 }
388 
389 #define BUF2SHORT(low,high) \
390 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
391 
392 static int
393 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
394 {
395 	device_t parent = device_get_parent(dev);
396 	int error, sent, read;
397 	char buf[2];
398 
399 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
400 		return (error);
401 
402 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
403 		goto error;
404 
405 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
406 		goto error;
407 
408 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
409 		goto error;
410 
411 	/* first, receive low, then high byte */
412 	*word = BUF2SHORT(buf[0], buf[1]);
413 
414 error:
415 	iicbus_stop(parent);
416 	return (error);
417 }
418 
419 static int
420 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
421 {
422 	device_t parent = device_get_parent(dev);
423 	int error, sent, read;
424 	char buf[2];
425 
426 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
427 		return (error);
428 
429 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
430 		goto error;
431 
432 	/* first, send low, then high byte */
433 	buf[0] = (char)(sdata & 0xff);
434 	buf[1] = (char)((sdata & 0xff00) >> 8);
435 
436 	if ((error = iicbus_write(parent, buf, 2, &sent, 0)))
437 		goto error;
438 
439 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
440 		goto error;
441 
442 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
443 		goto error;
444 
445 	/* first, receive low, then high byte */
446 	*rdata = BUF2SHORT(buf[0], buf[1]);
447 
448 error:
449 	iicbus_stop(parent);
450 	return (error);
451 }
452 
453 static int
454 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
455 {
456 	device_t parent = device_get_parent(dev);
457 	int error, sent;
458 
459 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
460 		goto error;
461 
462 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
463 		goto error;
464 
465 	if ((error = iicbus_write(parent, buf, (int)count, &sent, 0)))
466 		goto error;
467 
468 	if ((error = iicbus_stop(parent)))
469 		goto error;
470 
471 error:
472 	return (error);
473 }
474 
475 static int
476 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
477 {
478 	device_t parent = device_get_parent(dev);
479 	int error, sent, read;
480 
481 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
482 		return (error);
483 
484 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
485 		goto error;
486 
487 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
488 		goto error;
489 
490 	if ((error = iicbus_read(parent, buf, (int)count, &read,
491 							IIC_LAST_READ, 0)))
492 		goto error;
493 
494 error:
495 	iicbus_stop(parent);
496 	return (error);
497 }
498 
499 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
500