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