xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 5dae51da3da0cc94d17bd67b308fad304ebec7e0)
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 static void
135 iicsmb_identify(driver_t *driver, device_t parent)
136 {
137 
138 	if (device_find_child(parent, "iicsmb", -1) == NULL)
139 		BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
140 }
141 
142 static int
143 iicsmb_probe(device_t dev)
144 {
145 	device_set_desc(dev, "SMBus over I2C bridge");
146 	return (BUS_PROBE_NOWILDCARD);
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 	mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
155 
156 	sc->smbus = device_add_child(dev, "smbus", -1);
157 
158 	/* probe and attach the smbus */
159 	bus_generic_attach(dev);
160 
161 	return (0);
162 }
163 
164 static int
165 iicsmb_detach(device_t dev)
166 {
167 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
168 
169 	bus_generic_detach(dev);
170 	device_delete_children(dev);
171 	mtx_destroy(&sc->lock);
172 
173 	return (0);
174 }
175 
176 /*
177  * iicsmb_intr()
178  *
179  * iicbus interrupt handler
180  */
181 static int
182 iicsmb_intr(device_t dev, int event, char *buf)
183 {
184 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
185 
186 	mtx_lock(&sc->lock);
187 	switch (event) {
188 	case INTR_GENERAL:
189 	case INTR_START:
190 		sc->state = SMB_WAITING_ADDR;
191 		break;
192 
193 	case INTR_STOP:
194 		/* call smbus intr handler */
195 		smbus_intr(sc->smbus, sc->devaddr,
196 				sc->low, sc->high, SMB_ENOERR);
197 		break;
198 
199 	case INTR_RECEIVE:
200 		switch (sc->state) {
201 		case SMB_DONE:
202 			/* XXX too much data, discard */
203 			printf("%s: too much data from 0x%x\n", __func__,
204 				sc->devaddr & 0xff);
205 			goto end;
206 
207 		case SMB_WAITING_ADDR:
208 			sc->devaddr = (u_char)*buf;
209 			sc->state = SMB_WAITING_LOW;
210 			break;
211 
212 		case SMB_WAITING_LOW:
213 			sc->low = *buf;
214 			sc->state = SMB_WAITING_HIGH;
215 			break;
216 
217 		case SMB_WAITING_HIGH:
218 			sc->high = *buf;
219 			sc->state = SMB_DONE;
220 			break;
221 		}
222 end:
223 		break;
224 
225 	case INTR_TRANSMIT:
226 	case INTR_NOACK:
227 		break;
228 
229 	case INTR_ERROR:
230 		switch (*buf) {
231 		case IIC_EBUSERR:
232 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
233 			break;
234 
235 		default:
236 			printf("%s unknown error 0x%x!\n", __func__,
237 								(int)*buf);
238 			break;
239 		}
240 		break;
241 
242 	default:
243 		panic("%s: unknown event (%d)!", __func__, event);
244 	}
245 	mtx_unlock(&sc->lock);
246 
247 	return (0);
248 }
249 
250 static int
251 iicsmb_callback(device_t dev, int index, void *data)
252 {
253 	device_t parent = device_get_parent(dev);
254 	int error = 0;
255 	int how;
256 
257 	switch (index) {
258 	case SMB_REQUEST_BUS:
259 		/* request underlying iicbus */
260 		how = *(int *)data;
261 		error = iicbus_request_bus(parent, dev, how);
262 		break;
263 
264 	case SMB_RELEASE_BUS:
265 		/* release underlying iicbus */
266 		error = iicbus_release_bus(parent, dev);
267 		break;
268 
269 	default:
270 		error = EINVAL;
271 	}
272 
273 	return (error);
274 }
275 
276 static int
277 iic2smb_error(int error)
278 {
279 	switch (error) {
280 	case IIC_NOERR:
281 		return (SMB_ENOERR);
282 	case IIC_EBUSERR:
283 		return (SMB_EBUSERR);
284 	case IIC_ENOACK:
285 		return (SMB_ENOACK);
286 	case IIC_ETIMEOUT:
287 		return (SMB_ETIMEOUT);
288 	case IIC_EBUSBSY:
289 		return (SMB_EBUSY);
290 	case IIC_ESTATUS:
291 		return (SMB_EBUSERR);
292 	case IIC_EUNDERFLOW:
293 		return (SMB_EBUSERR);
294 	case IIC_EOVERFLOW:
295 		return (SMB_EBUSERR);
296 	case IIC_ENOTSUPP:
297 		return (SMB_ENOTSUPP);
298 	case IIC_ENOADDR:
299 		return (SMB_EBUSERR);
300 	case IIC_ERESOURCE:
301 		return (SMB_EBUSERR);
302 	default:
303 		return (SMB_EBUSERR);
304 	}
305 }
306 
307 #define	TRANSFER_MSGS(dev, msgs)	iicbus_transfer(dev, msgs, nitems(msgs))
308 
309 static int
310 iicsmb_quick(device_t dev, u_char slave, int how)
311 {
312 	struct iic_msg msgs[] = {
313 	     { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
314 	};
315 	int error;
316 
317 	switch (how) {
318 	case SMB_QWRITE:
319 	case SMB_QREAD:
320 		break;
321 	default:
322 		return (SMB_EINVAL);
323 	}
324 
325 	error = TRANSFER_MSGS(dev, msgs);
326 	return (iic2smb_error(error));
327 }
328 
329 static int
330 iicsmb_sendb(device_t dev, u_char slave, char byte)
331 {
332 	struct iic_msg msgs[] = {
333 	     { slave, IIC_M_WR, 1, &byte },
334 	};
335 	int error;
336 
337 	error = TRANSFER_MSGS(dev, msgs);
338 	return (iic2smb_error(error));
339 }
340 
341 static int
342 iicsmb_recvb(device_t dev, u_char slave, char *byte)
343 {
344 	struct iic_msg msgs[] = {
345 	     { slave, IIC_M_RD, 1, byte },
346 	};
347 	int error;
348 
349 	error = TRANSFER_MSGS(dev, msgs);
350 	return (iic2smb_error(error));
351 }
352 
353 static int
354 iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
355 {
356 	uint8_t bytes[] = { cmd, byte };
357 	struct iic_msg msgs[] = {
358 	     { slave, IIC_M_WR, nitems(bytes), bytes },
359 	};
360 	int error;
361 
362 	error = TRANSFER_MSGS(dev, msgs);
363 	return (iic2smb_error(error));
364 }
365 
366 static int
367 iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
368 {
369 	uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
370 	struct iic_msg msgs[] = {
371 	     { slave, IIC_M_WR, nitems(bytes), bytes },
372 	};
373 	int error;
374 
375 	error = TRANSFER_MSGS(dev, msgs);
376 	return (iic2smb_error(error));
377 }
378 
379 static int
380 iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
381 {
382 	struct iic_msg msgs[] = {
383 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
384 	     { slave, IIC_M_RD, 1, byte },
385 	};
386 	int error;
387 
388 	error = TRANSFER_MSGS(dev, msgs);
389 	return (iic2smb_error(error));
390 }
391 
392 static int
393 iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
394 {
395 	uint8_t buf[2];
396 	struct iic_msg msgs[] = {
397 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
398 	     { slave, IIC_M_RD, nitems(buf), buf },
399 	};
400 	int error;
401 
402 	error = TRANSFER_MSGS(dev, msgs);
403 	if (error == 0)
404 		*word = ((uint16_t)buf[1] << 8) | buf[0];
405 	return (iic2smb_error(error));
406 }
407 
408 static int
409 iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
410 {
411 	uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
412 	uint8_t out[2];
413 	struct iic_msg msgs[] = {
414 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
415 	     { slave, IIC_M_RD, nitems(out), out },
416 	};
417 	int error;
418 
419 	error = TRANSFER_MSGS(dev, msgs);
420 	if (error == 0)
421 		*rdata = ((uint16_t)out[1] << 8) | out[0];
422 	return (iic2smb_error(error));
423 }
424 
425 static int
426 iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
427 {
428 	uint8_t bytes[2] = { cmd, count };
429 	struct iic_msg msgs[] = {
430 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
431 	     { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
432 	};
433 	int error;
434 
435 	if (count > 32 || count == 0)
436 		return (SMB_EINVAL);
437 	error = TRANSFER_MSGS(dev, msgs);
438 	return (iic2smb_error(error));
439 }
440 
441 static int
442 iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
443 {
444 	struct iic_msg msgs[] = {
445 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
446 	     { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
447 	};
448 	struct iic_msg block_msg[] = {
449 	     { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
450 	};
451 	device_t parent = device_get_parent(dev);
452 	int error;
453 	u_char bufsz;
454 
455 	/* Stash output buffer size before overwriting it. */
456 	bufsz = *count;
457 	if (bufsz == 0)
458 		return (SMB_EINVAL);
459 
460 	/* Have to do this because the command is split in two transfers. */
461 	error = iicbus_request_bus(parent, dev, IIC_WAIT);
462 	if (error == 0)
463 		error = TRANSFER_MSGS(dev, msgs);
464 	if (error == 0) {
465 		/*
466 		 * If the slave offers an empty or a too long reply,
467 		 * read one byte to generate the stop or abort.
468 		 * XXX 32 is hardcoded until SMB_MAXBLOCKSIZE is restored
469 		 * to sanity.
470 		 */
471 		if (*count > 32 || *count == 0)
472 			block_msg[0].len = 1;
473 		/* If longer than the buffer, then clamp at the buffer size. */
474 		if (*count > bufsz)
475 			block_msg[0].len = bufsz;
476 		else
477 			block_msg[0].len = *count;
478 		error = TRANSFER_MSGS(dev, block_msg);
479 		if (*count > 32 || *count == 0)
480 			error = SMB_EINVAL;
481 	}
482 	(void)iicbus_release_bus(parent, dev);
483 	return (iic2smb_error(error));
484 }
485 
486 DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
487 DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
488 MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
489 MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
490 MODULE_VERSION(iicsmb, 1);
491