xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 4b7ec27007570e0ce4fe9dbb447e7afc131b487a)
1c3e2dc6bSNicolas Souchu /*-
2c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
3c3e2dc6bSNicolas Souchu  * All rights reserved.
4c3e2dc6bSNicolas Souchu  *
5c3e2dc6bSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
6c3e2dc6bSNicolas Souchu  * modification, are permitted provided that the following conditions
7c3e2dc6bSNicolas Souchu  * are met:
8c3e2dc6bSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
9c3e2dc6bSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
10c3e2dc6bSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
11c3e2dc6bSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
12c3e2dc6bSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
13c3e2dc6bSNicolas Souchu  *
14c3e2dc6bSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15c3e2dc6bSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16c3e2dc6bSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17c3e2dc6bSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18c3e2dc6bSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19c3e2dc6bSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20c3e2dc6bSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21c3e2dc6bSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22c3e2dc6bSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23c3e2dc6bSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24c3e2dc6bSNicolas Souchu  * SUCH DAMAGE.
25c3e2dc6bSNicolas Souchu  */
26c3e2dc6bSNicolas Souchu 
27*4b7ec270SMarius Strobl #include <sys/cdefs.h>
28*4b7ec270SMarius Strobl __FBSDID("$FreeBSD$");
29*4b7ec270SMarius Strobl 
30c3e2dc6bSNicolas Souchu /*
31c3e2dc6bSNicolas Souchu  * I2C to SMB bridge
3204f89a63SNicolas Souchu  *
3304f89a63SNicolas Souchu  * Example:
3404f89a63SNicolas Souchu  *
3504f89a63SNicolas Souchu  *     smb bttv
3604f89a63SNicolas Souchu  *       \ /
3704f89a63SNicolas Souchu  *      smbus
3804f89a63SNicolas Souchu  *       /  \
3904f89a63SNicolas Souchu  *    iicsmb bti2c
4004f89a63SNicolas Souchu  *       |
4104f89a63SNicolas Souchu  *     iicbus
4204f89a63SNicolas Souchu  *     /  |  \
4304f89a63SNicolas Souchu  *  iicbb pcf ...
4404f89a63SNicolas Souchu  *    |
4504f89a63SNicolas Souchu  *  lpbb
46c3e2dc6bSNicolas Souchu  */
47c3e2dc6bSNicolas Souchu 
48c3e2dc6bSNicolas Souchu #include <sys/param.h>
49c3e2dc6bSNicolas Souchu #include <sys/bus.h>
500df8f081SJohn Baldwin #include <sys/kernel.h>
510df8f081SJohn Baldwin #include <sys/lock.h>
520df8f081SJohn Baldwin #include <sys/module.h>
530df8f081SJohn Baldwin #include <sys/mutex.h>
540df8f081SJohn Baldwin #include <sys/systm.h>
55c3e2dc6bSNicolas Souchu #include <sys/uio.h>
56c3e2dc6bSNicolas Souchu 
57c3e2dc6bSNicolas Souchu #include <dev/iicbus/iiconf.h>
58c3e2dc6bSNicolas Souchu #include <dev/iicbus/iicbus.h>
59c3e2dc6bSNicolas Souchu 
60c3e2dc6bSNicolas Souchu #include <dev/smbus/smbconf.h>
61c3e2dc6bSNicolas Souchu 
62c3e2dc6bSNicolas Souchu #include "iicbus_if.h"
63c3e2dc6bSNicolas Souchu #include "smbus_if.h"
64c3e2dc6bSNicolas Souchu 
65c3e2dc6bSNicolas Souchu struct iicsmb_softc {
66c3e2dc6bSNicolas Souchu 
67c3e2dc6bSNicolas Souchu #define SMB_WAITING_ADDR	0x0
68c3e2dc6bSNicolas Souchu #define SMB_WAITING_LOW		0x1
69c3e2dc6bSNicolas Souchu #define SMB_WAITING_HIGH	0x2
70c3e2dc6bSNicolas Souchu #define SMB_DONE		0x3
71c3e2dc6bSNicolas Souchu 	int state;
72c3e2dc6bSNicolas Souchu 
73c3e2dc6bSNicolas Souchu 	u_char devaddr;			/* slave device address */
74c3e2dc6bSNicolas Souchu 
75c3e2dc6bSNicolas Souchu 	char low;			/* low byte received first */
76c3e2dc6bSNicolas Souchu 	char high;			/* high byte */
77c3e2dc6bSNicolas Souchu 
78313f8941SJohn Baldwin 	struct mtx lock;
79c3e2dc6bSNicolas Souchu 	device_t smbus;
80c3e2dc6bSNicolas Souchu };
81c3e2dc6bSNicolas Souchu 
82c3e2dc6bSNicolas Souchu static int iicsmb_probe(device_t);
83c3e2dc6bSNicolas Souchu static int iicsmb_attach(device_t);
84c17d4340SNicolas Souchu static int iicsmb_detach(device_t);
85c17d4340SNicolas Souchu static void iicsmb_identify(driver_t *driver, device_t parent);
86c3e2dc6bSNicolas Souchu 
87b23193a5SWarner Losh static int iicsmb_intr(device_t dev, int event, char *buf);
887048a99cSJohn Baldwin static int iicsmb_callback(device_t dev, int index, void *data);
89c3e2dc6bSNicolas Souchu static int iicsmb_quick(device_t dev, u_char slave, int how);
90c3e2dc6bSNicolas Souchu static int iicsmb_sendb(device_t dev, u_char slave, char byte);
91c3e2dc6bSNicolas Souchu static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
92c3e2dc6bSNicolas Souchu static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
93c3e2dc6bSNicolas Souchu static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
94c3e2dc6bSNicolas Souchu static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
95c3e2dc6bSNicolas Souchu static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
96c3e2dc6bSNicolas Souchu static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
97c3e2dc6bSNicolas Souchu static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
987048a99cSJohn Baldwin static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
99c3e2dc6bSNicolas Souchu 
100c3e2dc6bSNicolas Souchu static devclass_t iicsmb_devclass;
101c3e2dc6bSNicolas Souchu 
102c3e2dc6bSNicolas Souchu static device_method_t iicsmb_methods[] = {
103c3e2dc6bSNicolas Souchu 	/* device interface */
104c17d4340SNicolas Souchu 	DEVMETHOD(device_identify,	iicsmb_identify),
105c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_probe,		iicsmb_probe),
106c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_attach,	iicsmb_attach),
107c17d4340SNicolas Souchu 	DEVMETHOD(device_detach,	iicsmb_detach),
108c3e2dc6bSNicolas Souchu 
109c3e2dc6bSNicolas Souchu 	/* iicbus interface */
110c3e2dc6bSNicolas Souchu 	DEVMETHOD(iicbus_intr,		iicsmb_intr),
111c3e2dc6bSNicolas Souchu 
112c3e2dc6bSNicolas Souchu 	/* smbus interface */
11304f89a63SNicolas Souchu 	DEVMETHOD(smbus_callback,	iicsmb_callback),
114c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_quick,		iicsmb_quick),
115c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_sendb,		iicsmb_sendb),
116c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_recvb,		iicsmb_recvb),
117c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_writeb,		iicsmb_writeb),
118c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_writew,		iicsmb_writew),
119c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_readb,		iicsmb_readb),
120c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_readw,		iicsmb_readw),
121c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_pcall,		iicsmb_pcall),
122c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_bwrite,		iicsmb_bwrite),
123c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_bread,		iicsmb_bread),
124c3e2dc6bSNicolas Souchu 
125*4b7ec270SMarius Strobl 	DEVMETHOD_END
126c3e2dc6bSNicolas Souchu };
127c3e2dc6bSNicolas Souchu 
128c3e2dc6bSNicolas Souchu static driver_t iicsmb_driver = {
129c3e2dc6bSNicolas Souchu 	"iicsmb",
130c3e2dc6bSNicolas Souchu 	iicsmb_methods,
131c3e2dc6bSNicolas Souchu 	sizeof(struct iicsmb_softc),
132c3e2dc6bSNicolas Souchu };
133c3e2dc6bSNicolas Souchu 
134c17d4340SNicolas Souchu #define IICBUS_TIMEOUT	100	/* us */
135c17d4340SNicolas Souchu 
136c17d4340SNicolas Souchu static void
137c17d4340SNicolas Souchu iicsmb_identify(driver_t *driver, device_t parent)
138c17d4340SNicolas Souchu {
139313f8941SJohn Baldwin 
140313f8941SJohn Baldwin 	if (device_find_child(parent, "iicsmb", -1) == NULL)
141b08a61b0SBernd Walter 		BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
142c17d4340SNicolas Souchu }
143c17d4340SNicolas Souchu 
144c3e2dc6bSNicolas Souchu static int
145c3e2dc6bSNicolas Souchu iicsmb_probe(device_t dev)
146c3e2dc6bSNicolas Souchu {
147c17d4340SNicolas Souchu 	device_set_desc(dev, "SMBus over I2C bridge");
148789c4b9dSNathan Whitehorn 	return (BUS_PROBE_NOWILDCARD);
149c3e2dc6bSNicolas Souchu }
150c3e2dc6bSNicolas Souchu 
151c3e2dc6bSNicolas Souchu static int
152c3e2dc6bSNicolas Souchu iicsmb_attach(device_t dev)
153c3e2dc6bSNicolas Souchu {
154c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
155c3e2dc6bSNicolas Souchu 
156313f8941SJohn Baldwin 	mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
157313f8941SJohn Baldwin 
158c17d4340SNicolas Souchu 	sc->smbus = device_add_child(dev, "smbus", -1);
159c17d4340SNicolas Souchu 
160c3e2dc6bSNicolas Souchu 	/* probe and attach the smbus */
161c17d4340SNicolas Souchu 	bus_generic_attach(dev);
162c17d4340SNicolas Souchu 
163c17d4340SNicolas Souchu 	return (0);
164c17d4340SNicolas Souchu }
165c17d4340SNicolas Souchu 
166c17d4340SNicolas Souchu static int
167c17d4340SNicolas Souchu iicsmb_detach(device_t dev)
168c17d4340SNicolas Souchu {
169c17d4340SNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
170c17d4340SNicolas Souchu 
171c17d4340SNicolas Souchu 	bus_generic_detach(dev);
172c17d4340SNicolas Souchu 	if (sc->smbus) {
173c17d4340SNicolas Souchu 		device_delete_child(dev, sc->smbus);
174c17d4340SNicolas Souchu 	}
175313f8941SJohn Baldwin 	mtx_destroy(&sc->lock);
176c3e2dc6bSNicolas Souchu 
177c3e2dc6bSNicolas Souchu 	return (0);
178c3e2dc6bSNicolas Souchu }
179c3e2dc6bSNicolas Souchu 
180c3e2dc6bSNicolas Souchu /*
181c3e2dc6bSNicolas Souchu  * iicsmb_intr()
182c3e2dc6bSNicolas Souchu  *
183c3e2dc6bSNicolas Souchu  * iicbus interrupt handler
184c3e2dc6bSNicolas Souchu  */
185b23193a5SWarner Losh static int
186c3e2dc6bSNicolas Souchu iicsmb_intr(device_t dev, int event, char *buf)
187c3e2dc6bSNicolas Souchu {
188c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
189c3e2dc6bSNicolas Souchu 
190313f8941SJohn Baldwin 	mtx_lock(&sc->lock);
191c3e2dc6bSNicolas Souchu 	switch (event) {
192c3e2dc6bSNicolas Souchu 	case INTR_GENERAL:
193c3e2dc6bSNicolas Souchu 	case INTR_START:
194c3e2dc6bSNicolas Souchu 		sc->state = SMB_WAITING_ADDR;
195c3e2dc6bSNicolas Souchu 		break;
196c3e2dc6bSNicolas Souchu 
197c3e2dc6bSNicolas Souchu 	case INTR_STOP:
198c3e2dc6bSNicolas Souchu 		/* call smbus intr handler */
199c3e2dc6bSNicolas Souchu 		smbus_intr(sc->smbus, sc->devaddr,
200c3e2dc6bSNicolas Souchu 				sc->low, sc->high, SMB_ENOERR);
201c3e2dc6bSNicolas Souchu 		break;
202c3e2dc6bSNicolas Souchu 
203c3e2dc6bSNicolas Souchu 	case INTR_RECEIVE:
204c3e2dc6bSNicolas Souchu 		switch (sc->state) {
205c3e2dc6bSNicolas Souchu 		case SMB_DONE:
206c3e2dc6bSNicolas Souchu 			/* XXX too much data, discard */
2076e551fb6SDavid E. O'Brien 			printf("%s: too much data from 0x%x\n", __func__,
208c3e2dc6bSNicolas Souchu 				sc->devaddr & 0xff);
209c3e2dc6bSNicolas Souchu 			goto end;
210c3e2dc6bSNicolas Souchu 
211c3e2dc6bSNicolas Souchu 		case SMB_WAITING_ADDR:
212c3e2dc6bSNicolas Souchu 			sc->devaddr = (u_char)*buf;
213c3e2dc6bSNicolas Souchu 			sc->state = SMB_WAITING_LOW;
214c3e2dc6bSNicolas Souchu 			break;
215c3e2dc6bSNicolas Souchu 
216c3e2dc6bSNicolas Souchu 		case SMB_WAITING_LOW:
217c3e2dc6bSNicolas Souchu 			sc->low = *buf;
218c3e2dc6bSNicolas Souchu 			sc->state = SMB_WAITING_HIGH;
219c3e2dc6bSNicolas Souchu 			break;
220c3e2dc6bSNicolas Souchu 
221c3e2dc6bSNicolas Souchu 		case SMB_WAITING_HIGH:
222c3e2dc6bSNicolas Souchu 			sc->high = *buf;
223c3e2dc6bSNicolas Souchu 			sc->state = SMB_DONE;
224c3e2dc6bSNicolas Souchu 			break;
225c3e2dc6bSNicolas Souchu 		}
226c3e2dc6bSNicolas Souchu end:
227c3e2dc6bSNicolas Souchu 		break;
228c3e2dc6bSNicolas Souchu 
229c3e2dc6bSNicolas Souchu 	case INTR_TRANSMIT:
230c3e2dc6bSNicolas Souchu 	case INTR_NOACK:
231c3e2dc6bSNicolas Souchu 		break;
232c3e2dc6bSNicolas Souchu 
233c3e2dc6bSNicolas Souchu 	case INTR_ERROR:
234c3e2dc6bSNicolas Souchu 		switch (*buf) {
235c3e2dc6bSNicolas Souchu 		case IIC_EBUSERR:
236c3e2dc6bSNicolas Souchu 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
237c3e2dc6bSNicolas Souchu 			break;
238c3e2dc6bSNicolas Souchu 
239c3e2dc6bSNicolas Souchu 		default:
2406e551fb6SDavid E. O'Brien 			printf("%s unknown error 0x%x!\n", __func__,
241c3e2dc6bSNicolas Souchu 								(int)*buf);
242c3e2dc6bSNicolas Souchu 			break;
243c3e2dc6bSNicolas Souchu 		}
244c3e2dc6bSNicolas Souchu 		break;
245c3e2dc6bSNicolas Souchu 
246c3e2dc6bSNicolas Souchu 	default:
2476e551fb6SDavid E. O'Brien 		panic("%s: unknown event (%d)!", __func__, event);
248c3e2dc6bSNicolas Souchu 	}
249313f8941SJohn Baldwin 	mtx_unlock(&sc->lock);
250c3e2dc6bSNicolas Souchu 
251b23193a5SWarner Losh 	return (0);
252c3e2dc6bSNicolas Souchu }
253c3e2dc6bSNicolas Souchu 
254c3e2dc6bSNicolas Souchu static int
2557048a99cSJohn Baldwin iicsmb_callback(device_t dev, int index, void *data)
25604f89a63SNicolas Souchu {
25704f89a63SNicolas Souchu 	device_t parent = device_get_parent(dev);
25804f89a63SNicolas Souchu 	int error = 0;
25904f89a63SNicolas Souchu 	int how;
26004f89a63SNicolas Souchu 
26104f89a63SNicolas Souchu 	switch (index) {
26204f89a63SNicolas Souchu 	case SMB_REQUEST_BUS:
26304f89a63SNicolas Souchu 		/* request underlying iicbus */
26404f89a63SNicolas Souchu 		how = *(int *)data;
26504f89a63SNicolas Souchu 		error = iicbus_request_bus(parent, dev, how);
26604f89a63SNicolas Souchu 		break;
26704f89a63SNicolas Souchu 
26804f89a63SNicolas Souchu 	case SMB_RELEASE_BUS:
26904f89a63SNicolas Souchu 		/* release underlying iicbus */
27004f89a63SNicolas Souchu 		error = iicbus_release_bus(parent, dev);
27104f89a63SNicolas Souchu 		break;
27204f89a63SNicolas Souchu 
27304f89a63SNicolas Souchu 	default:
27404f89a63SNicolas Souchu 		error = EINVAL;
27504f89a63SNicolas Souchu 	}
27604f89a63SNicolas Souchu 
27704f89a63SNicolas Souchu 	return (error);
27804f89a63SNicolas Souchu }
27904f89a63SNicolas Souchu 
28004f89a63SNicolas Souchu static int
281c3e2dc6bSNicolas Souchu iicsmb_quick(device_t dev, u_char slave, int how)
282c3e2dc6bSNicolas Souchu {
283c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
284c3e2dc6bSNicolas Souchu 	int error;
285c3e2dc6bSNicolas Souchu 
286c3e2dc6bSNicolas Souchu 	switch (how) {
287c3e2dc6bSNicolas Souchu 	case SMB_QWRITE:
288c17d4340SNicolas Souchu 		error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
289c3e2dc6bSNicolas Souchu 		break;
290c3e2dc6bSNicolas Souchu 
291c3e2dc6bSNicolas Souchu 	case SMB_QREAD:
292c17d4340SNicolas Souchu 		error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT);
293c3e2dc6bSNicolas Souchu 		break;
294c3e2dc6bSNicolas Souchu 
295c3e2dc6bSNicolas Souchu 	default:
296c3e2dc6bSNicolas Souchu 		error = EINVAL;
297c3e2dc6bSNicolas Souchu 		break;
298c3e2dc6bSNicolas Souchu 	}
299c3e2dc6bSNicolas Souchu 
300c3e2dc6bSNicolas Souchu 	if (!error)
301c3e2dc6bSNicolas Souchu 		error = iicbus_stop(parent);
302c3e2dc6bSNicolas Souchu 
303c3e2dc6bSNicolas Souchu 	return (error);
304c3e2dc6bSNicolas Souchu }
305c3e2dc6bSNicolas Souchu 
306c3e2dc6bSNicolas Souchu static int
307c3e2dc6bSNicolas Souchu iicsmb_sendb(device_t dev, u_char slave, char byte)
308c3e2dc6bSNicolas Souchu {
309c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
310c3e2dc6bSNicolas Souchu 	int error, sent;
311c3e2dc6bSNicolas Souchu 
312c17d4340SNicolas Souchu 	error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
313c3e2dc6bSNicolas Souchu 
314c3e2dc6bSNicolas Souchu 	if (!error) {
315c17d4340SNicolas Souchu 		error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
316c3e2dc6bSNicolas Souchu 
317c3e2dc6bSNicolas Souchu 		iicbus_stop(parent);
318c3e2dc6bSNicolas Souchu 	}
319c3e2dc6bSNicolas Souchu 
320c3e2dc6bSNicolas Souchu 	return (error);
321c3e2dc6bSNicolas Souchu }
322c3e2dc6bSNicolas Souchu 
323c3e2dc6bSNicolas Souchu static int
324c3e2dc6bSNicolas Souchu iicsmb_recvb(device_t dev, u_char slave, char *byte)
325c3e2dc6bSNicolas Souchu {
326c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
327c3e2dc6bSNicolas Souchu 	int error, read;
328c3e2dc6bSNicolas Souchu 
32904f89a63SNicolas Souchu 	error = iicbus_start(parent, slave | LSB, 0);
330c3e2dc6bSNicolas Souchu 
33104f89a63SNicolas Souchu 	if (!error) {
332c17d4340SNicolas Souchu 		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT);
33304f89a63SNicolas Souchu 
33404f89a63SNicolas Souchu 		iicbus_stop(parent);
33504f89a63SNicolas Souchu 	}
336c3e2dc6bSNicolas Souchu 
337c3e2dc6bSNicolas Souchu 	return (error);
338c3e2dc6bSNicolas Souchu }
339c3e2dc6bSNicolas Souchu 
340c3e2dc6bSNicolas Souchu static int
341c3e2dc6bSNicolas Souchu iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
342c3e2dc6bSNicolas Souchu {
343c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
344c3e2dc6bSNicolas Souchu 	int error, sent;
345c3e2dc6bSNicolas Souchu 
34604f89a63SNicolas Souchu 	error = iicbus_start(parent, slave & ~LSB, 0);
347c3e2dc6bSNicolas Souchu 
348c3e2dc6bSNicolas Souchu 	if (!error) {
349c17d4340SNicolas Souchu 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
350c17d4340SNicolas Souchu 			error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
351c3e2dc6bSNicolas Souchu 
352c3e2dc6bSNicolas Souchu 		iicbus_stop(parent);
353c3e2dc6bSNicolas Souchu 	}
354c3e2dc6bSNicolas Souchu 
355c3e2dc6bSNicolas Souchu 	return (error);
356c3e2dc6bSNicolas Souchu }
357c3e2dc6bSNicolas Souchu 
358c3e2dc6bSNicolas Souchu static int
359c3e2dc6bSNicolas Souchu iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
360c3e2dc6bSNicolas Souchu {
361c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
362c3e2dc6bSNicolas Souchu 	int error, sent;
363c3e2dc6bSNicolas Souchu 
364c3e2dc6bSNicolas Souchu 	char low = (char)(word & 0xff);
365c3e2dc6bSNicolas Souchu 	char high = (char)((word & 0xff00) >> 8);
366c3e2dc6bSNicolas Souchu 
36704f89a63SNicolas Souchu 	error = iicbus_start(parent, slave & ~LSB, 0);
368c3e2dc6bSNicolas Souchu 
369c3e2dc6bSNicolas Souchu 	if (!error) {
370c17d4340SNicolas Souchu 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
371c17d4340SNicolas Souchu 		  if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT)))
372c17d4340SNicolas Souchu 		    error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT);
373c3e2dc6bSNicolas Souchu 
374c3e2dc6bSNicolas Souchu 		iicbus_stop(parent);
375c3e2dc6bSNicolas Souchu 	}
376c3e2dc6bSNicolas Souchu 
377c3e2dc6bSNicolas Souchu 	return (error);
378c3e2dc6bSNicolas Souchu }
379c3e2dc6bSNicolas Souchu 
380c3e2dc6bSNicolas Souchu static int
381c3e2dc6bSNicolas Souchu iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
382c3e2dc6bSNicolas Souchu {
383c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
384c3e2dc6bSNicolas Souchu 	int error, sent, read;
385c3e2dc6bSNicolas Souchu 
386313f8941SJohn Baldwin 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
38704f89a63SNicolas Souchu 		return (error);
38804f89a63SNicolas Souchu 
389c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
390c3e2dc6bSNicolas Souchu 		goto error;
391c3e2dc6bSNicolas Souchu 
392c17d4340SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
393c3e2dc6bSNicolas Souchu 		goto error;
394c3e2dc6bSNicolas Souchu 
395c17d4340SNicolas Souchu 	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
396c3e2dc6bSNicolas Souchu 		goto error;
397c3e2dc6bSNicolas Souchu 
398c3e2dc6bSNicolas Souchu error:
39904f89a63SNicolas Souchu 	iicbus_stop(parent);
400c3e2dc6bSNicolas Souchu 	return (error);
401c3e2dc6bSNicolas Souchu }
402c3e2dc6bSNicolas Souchu 
403c3e2dc6bSNicolas Souchu #define BUF2SHORT(low,high) \
404c3e2dc6bSNicolas Souchu 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
405c3e2dc6bSNicolas Souchu 
406c3e2dc6bSNicolas Souchu static int
407c3e2dc6bSNicolas Souchu iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
408c3e2dc6bSNicolas Souchu {
409c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
410c3e2dc6bSNicolas Souchu 	int error, sent, read;
411c3e2dc6bSNicolas Souchu 	char buf[2];
412c3e2dc6bSNicolas Souchu 
413313f8941SJohn Baldwin 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
41404f89a63SNicolas Souchu 		return (error);
41504f89a63SNicolas Souchu 
416c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
417c3e2dc6bSNicolas Souchu 		goto error;
418c3e2dc6bSNicolas Souchu 
419c17d4340SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
420c3e2dc6bSNicolas Souchu 		goto error;
421c3e2dc6bSNicolas Souchu 
422c17d4340SNicolas Souchu 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
423c3e2dc6bSNicolas Souchu 		goto error;
424c3e2dc6bSNicolas Souchu 
425c3e2dc6bSNicolas Souchu 	/* first, receive low, then high byte */
426c3e2dc6bSNicolas Souchu 	*word = BUF2SHORT(buf[0], buf[1]);
427c3e2dc6bSNicolas Souchu 
428c3e2dc6bSNicolas Souchu error:
42904f89a63SNicolas Souchu 	iicbus_stop(parent);
430c3e2dc6bSNicolas Souchu 	return (error);
431c3e2dc6bSNicolas Souchu }
432c3e2dc6bSNicolas Souchu 
433c3e2dc6bSNicolas Souchu static int
434c3e2dc6bSNicolas Souchu iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
435c3e2dc6bSNicolas Souchu {
436c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
437c3e2dc6bSNicolas Souchu 	int error, sent, read;
438c3e2dc6bSNicolas Souchu 	char buf[2];
439c3e2dc6bSNicolas Souchu 
440313f8941SJohn Baldwin 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
44104f89a63SNicolas Souchu 		return (error);
442c3e2dc6bSNicolas Souchu 
443c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
444c3e2dc6bSNicolas Souchu 		goto error;
445c3e2dc6bSNicolas Souchu 
446c3e2dc6bSNicolas Souchu 	/* first, send low, then high byte */
447c3e2dc6bSNicolas Souchu 	buf[0] = (char)(sdata & 0xff);
448c3e2dc6bSNicolas Souchu 	buf[1] = (char)((sdata & 0xff00) >> 8);
449c3e2dc6bSNicolas Souchu 
450c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT)))
451c3e2dc6bSNicolas Souchu 		goto error;
452c3e2dc6bSNicolas Souchu 
453c17d4340SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
454c3e2dc6bSNicolas Souchu 		goto error;
455c3e2dc6bSNicolas Souchu 
456c17d4340SNicolas Souchu 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
457c3e2dc6bSNicolas Souchu 		goto error;
458c3e2dc6bSNicolas Souchu 
459c3e2dc6bSNicolas Souchu 	/* first, receive low, then high byte */
460c3e2dc6bSNicolas Souchu 	*rdata = BUF2SHORT(buf[0], buf[1]);
461c3e2dc6bSNicolas Souchu 
462c3e2dc6bSNicolas Souchu error:
46304f89a63SNicolas Souchu 	iicbus_stop(parent);
464c3e2dc6bSNicolas Souchu 	return (error);
465c3e2dc6bSNicolas Souchu }
466c3e2dc6bSNicolas Souchu 
467c3e2dc6bSNicolas Souchu static int
468c3e2dc6bSNicolas Souchu iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
469c3e2dc6bSNicolas Souchu {
470c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
471c3e2dc6bSNicolas Souchu 	int error, sent;
472c3e2dc6bSNicolas Souchu 
473c17d4340SNicolas Souchu 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
474c3e2dc6bSNicolas Souchu 		goto error;
475c3e2dc6bSNicolas Souchu 
476c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
477c3e2dc6bSNicolas Souchu 		goto error;
478c3e2dc6bSNicolas Souchu 
479c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
480c3e2dc6bSNicolas Souchu 		goto error;
481c3e2dc6bSNicolas Souchu 
482c3e2dc6bSNicolas Souchu 	if ((error = iicbus_stop(parent)))
483c3e2dc6bSNicolas Souchu 		goto error;
484c3e2dc6bSNicolas Souchu 
485c3e2dc6bSNicolas Souchu error:
486c3e2dc6bSNicolas Souchu 	return (error);
487c3e2dc6bSNicolas Souchu }
488c3e2dc6bSNicolas Souchu 
489c3e2dc6bSNicolas Souchu static int
4907048a99cSJohn Baldwin iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
491c3e2dc6bSNicolas Souchu {
492c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
493c3e2dc6bSNicolas Souchu 	int error, sent, read;
494c3e2dc6bSNicolas Souchu 
495313f8941SJohn Baldwin 	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
49604f89a63SNicolas Souchu 		return (error);
49704f89a63SNicolas Souchu 
498c17d4340SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
499c3e2dc6bSNicolas Souchu 		goto error;
500c3e2dc6bSNicolas Souchu 
501c17d4340SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
502c3e2dc6bSNicolas Souchu 		goto error;
503c3e2dc6bSNicolas Souchu 
5047048a99cSJohn Baldwin 	if ((error = iicbus_read(parent, buf, (int)*count, &read,
505c17d4340SNicolas Souchu 						IIC_LAST_READ, IICBUS_TIMEOUT)))
506c3e2dc6bSNicolas Souchu 		goto error;
5077048a99cSJohn Baldwin 	*count = read;
508c3e2dc6bSNicolas Souchu 
509c3e2dc6bSNicolas Souchu error:
51004f89a63SNicolas Souchu 	iicbus_stop(parent);
511c3e2dc6bSNicolas Souchu 	return (error);
512c3e2dc6bSNicolas Souchu }
513c3e2dc6bSNicolas Souchu 
514c3e2dc6bSNicolas Souchu DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
5157048a99cSJohn Baldwin DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
516c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
517c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
518c17d4340SNicolas Souchu MODULE_VERSION(iicsmb, 1);
519