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