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