xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 04f89a63cbf257b93a030fc153740e0fc37bdbd5)
1c3e2dc6bSNicolas Souchu /*-
2c3e2dc6bSNicolas Souchu  * Copyright (c) 1998 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  *
2604f89a63SNicolas Souchu  *	$Id: iicsmb.c,v 1.1.1.1 1998/09/03 20:51:50 nsouch Exp $
27c3e2dc6bSNicolas Souchu  *
28c3e2dc6bSNicolas Souchu  */
29c3e2dc6bSNicolas Souchu 
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/kernel.h>
50c3e2dc6bSNicolas Souchu #include <sys/systm.h>
51c3e2dc6bSNicolas Souchu #include <sys/module.h>
52c3e2dc6bSNicolas Souchu #include <sys/bus.h>
53c3e2dc6bSNicolas Souchu #include <sys/conf.h>
54c3e2dc6bSNicolas Souchu #include <sys/buf.h>
55c3e2dc6bSNicolas Souchu #include <sys/uio.h>
56c3e2dc6bSNicolas Souchu #include <sys/malloc.h>
57c3e2dc6bSNicolas Souchu 
58c3e2dc6bSNicolas Souchu #include <machine/clock.h>
59c3e2dc6bSNicolas Souchu 
60c3e2dc6bSNicolas Souchu #include <dev/iicbus/iiconf.h>
61c3e2dc6bSNicolas Souchu #include <dev/iicbus/iicbus.h>
62c3e2dc6bSNicolas Souchu 
63c3e2dc6bSNicolas Souchu #include <dev/smbus/smbconf.h>
64c3e2dc6bSNicolas Souchu 
65c3e2dc6bSNicolas Souchu #include "iicbus_if.h"
66c3e2dc6bSNicolas Souchu #include "smbus_if.h"
67c3e2dc6bSNicolas Souchu 
68c3e2dc6bSNicolas Souchu struct iicsmb_softc {
69c3e2dc6bSNicolas Souchu 
70c3e2dc6bSNicolas Souchu #define SMB_WAITING_ADDR	0x0
71c3e2dc6bSNicolas Souchu #define SMB_WAITING_LOW		0x1
72c3e2dc6bSNicolas Souchu #define SMB_WAITING_HIGH	0x2
73c3e2dc6bSNicolas Souchu #define SMB_DONE		0x3
74c3e2dc6bSNicolas Souchu 	int state;
75c3e2dc6bSNicolas Souchu 
76c3e2dc6bSNicolas Souchu 	u_char devaddr;			/* slave device address */
77c3e2dc6bSNicolas Souchu 
78c3e2dc6bSNicolas Souchu 	char low;			/* low byte received first */
79c3e2dc6bSNicolas Souchu 	char high;			/* high byte */
80c3e2dc6bSNicolas Souchu 
81c3e2dc6bSNicolas Souchu 	device_t smbus;
82c3e2dc6bSNicolas Souchu };
83c3e2dc6bSNicolas Souchu 
84c3e2dc6bSNicolas Souchu static int iicsmb_probe(device_t);
85c3e2dc6bSNicolas Souchu static int iicsmb_attach(device_t);
86c3e2dc6bSNicolas Souchu static void iicsmb_print_child(device_t, device_t);
87c3e2dc6bSNicolas Souchu 
88c3e2dc6bSNicolas Souchu static void iicsmb_intr(device_t dev, int event, char *buf);
8904f89a63SNicolas Souchu static int iicsmb_callback(device_t dev, int index, caddr_t data);
90c3e2dc6bSNicolas Souchu static int iicsmb_quick(device_t dev, u_char slave, int how);
91c3e2dc6bSNicolas Souchu static int iicsmb_sendb(device_t dev, u_char slave, char byte);
92c3e2dc6bSNicolas Souchu static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
93c3e2dc6bSNicolas Souchu static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
94c3e2dc6bSNicolas Souchu static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
95c3e2dc6bSNicolas Souchu static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
96c3e2dc6bSNicolas Souchu static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
97c3e2dc6bSNicolas Souchu static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
98c3e2dc6bSNicolas Souchu static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
99c3e2dc6bSNicolas Souchu static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf);
100c3e2dc6bSNicolas Souchu 
101c3e2dc6bSNicolas Souchu static devclass_t iicsmb_devclass;
102c3e2dc6bSNicolas Souchu 
103c3e2dc6bSNicolas Souchu static device_method_t iicsmb_methods[] = {
104c3e2dc6bSNicolas Souchu 	/* device interface */
105c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_probe,		iicsmb_probe),
106c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_attach,	iicsmb_attach),
107c3e2dc6bSNicolas Souchu 
108c3e2dc6bSNicolas Souchu 	/* bus interface */
109c3e2dc6bSNicolas Souchu 	DEVMETHOD(bus_print_child,	iicsmb_print_child),
110c3e2dc6bSNicolas Souchu 
111c3e2dc6bSNicolas Souchu 	/* iicbus interface */
112c3e2dc6bSNicolas Souchu 	DEVMETHOD(iicbus_intr,		iicsmb_intr),
113c3e2dc6bSNicolas Souchu 
114c3e2dc6bSNicolas Souchu 	/* smbus interface */
11504f89a63SNicolas Souchu 	DEVMETHOD(smbus_callback,	iicsmb_callback),
116c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_quick,		iicsmb_quick),
117c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_sendb,		iicsmb_sendb),
118c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_recvb,		iicsmb_recvb),
119c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_writeb,		iicsmb_writeb),
120c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_writew,		iicsmb_writew),
121c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_readb,		iicsmb_readb),
122c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_readw,		iicsmb_readw),
123c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_pcall,		iicsmb_pcall),
124c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_bwrite,		iicsmb_bwrite),
125c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_bread,		iicsmb_bread),
126c3e2dc6bSNicolas Souchu 
127c3e2dc6bSNicolas Souchu 	{ 0, 0 }
128c3e2dc6bSNicolas Souchu };
129c3e2dc6bSNicolas Souchu 
130c3e2dc6bSNicolas Souchu static driver_t iicsmb_driver = {
131c3e2dc6bSNicolas Souchu 	"iicsmb",
132c3e2dc6bSNicolas Souchu 	iicsmb_methods,
133c3e2dc6bSNicolas Souchu 	DRIVER_TYPE_MISC,
134c3e2dc6bSNicolas Souchu 	sizeof(struct iicsmb_softc),
135c3e2dc6bSNicolas Souchu };
136c3e2dc6bSNicolas Souchu 
137c3e2dc6bSNicolas Souchu static int
138c3e2dc6bSNicolas Souchu iicsmb_probe(device_t dev)
139c3e2dc6bSNicolas Souchu {
140c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
141c3e2dc6bSNicolas Souchu 
142c3e2dc6bSNicolas Souchu 	sc->smbus = smbus_alloc_bus(dev);
143c3e2dc6bSNicolas Souchu 
144c3e2dc6bSNicolas Souchu 	if (!sc->smbus)
145c3e2dc6bSNicolas Souchu 		return (EINVAL);	/* XXX don't know what to return else */
146c3e2dc6bSNicolas Souchu 
147c3e2dc6bSNicolas Souchu 	return (0);
148c3e2dc6bSNicolas Souchu }
149c3e2dc6bSNicolas Souchu 
150c3e2dc6bSNicolas Souchu static int
151c3e2dc6bSNicolas Souchu iicsmb_attach(device_t dev)
152c3e2dc6bSNicolas Souchu {
153c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
154c3e2dc6bSNicolas Souchu 
155c3e2dc6bSNicolas Souchu 	/* probe and attach the smbus */
156c3e2dc6bSNicolas Souchu 	device_probe_and_attach(sc->smbus);
157c3e2dc6bSNicolas Souchu 
158c3e2dc6bSNicolas Souchu 	return (0);
159c3e2dc6bSNicolas Souchu }
160c3e2dc6bSNicolas Souchu 
161c3e2dc6bSNicolas Souchu static void
162c3e2dc6bSNicolas Souchu iicsmb_print_child(device_t bus, device_t dev)
163c3e2dc6bSNicolas Souchu {
164c3e2dc6bSNicolas Souchu 	printf(" on %s%d", device_get_name(bus), device_get_unit(bus));
165c3e2dc6bSNicolas Souchu 
166c3e2dc6bSNicolas Souchu 	return;
167c3e2dc6bSNicolas Souchu }
168c3e2dc6bSNicolas Souchu 
169c3e2dc6bSNicolas Souchu /*
170c3e2dc6bSNicolas Souchu  * iicsmb_intr()
171c3e2dc6bSNicolas Souchu  *
172c3e2dc6bSNicolas Souchu  * iicbus interrupt handler
173c3e2dc6bSNicolas Souchu  */
174c3e2dc6bSNicolas Souchu static void
175c3e2dc6bSNicolas Souchu iicsmb_intr(device_t dev, int event, char *buf)
176c3e2dc6bSNicolas Souchu {
177c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
178c3e2dc6bSNicolas Souchu 
179c3e2dc6bSNicolas Souchu 	switch (event) {
180c3e2dc6bSNicolas Souchu 	case INTR_GENERAL:
181c3e2dc6bSNicolas Souchu 	case INTR_START:
182c3e2dc6bSNicolas Souchu 		sc->state = SMB_WAITING_ADDR;
183c3e2dc6bSNicolas Souchu 		break;
184c3e2dc6bSNicolas Souchu 
185c3e2dc6bSNicolas Souchu 	case INTR_STOP:
186c3e2dc6bSNicolas Souchu 		/* call smbus intr handler */
187c3e2dc6bSNicolas Souchu 		smbus_intr(sc->smbus, sc->devaddr,
188c3e2dc6bSNicolas Souchu 				sc->low, sc->high, SMB_ENOERR);
189c3e2dc6bSNicolas Souchu 		break;
190c3e2dc6bSNicolas Souchu 
191c3e2dc6bSNicolas Souchu 	case INTR_RECEIVE:
192c3e2dc6bSNicolas Souchu 		switch (sc->state) {
193c3e2dc6bSNicolas Souchu 		case SMB_DONE:
194c3e2dc6bSNicolas Souchu 			/* XXX too much data, discard */
195c3e2dc6bSNicolas Souchu 			printf("%s: too much data from 0x%x\n", __FUNCTION__,
196c3e2dc6bSNicolas Souchu 				sc->devaddr & 0xff);
197c3e2dc6bSNicolas Souchu 			goto end;
198c3e2dc6bSNicolas Souchu 
199c3e2dc6bSNicolas Souchu 		case SMB_WAITING_ADDR:
200c3e2dc6bSNicolas Souchu 			sc->devaddr = (u_char)*buf;
201c3e2dc6bSNicolas Souchu 			sc->state = SMB_WAITING_LOW;
202c3e2dc6bSNicolas Souchu 			break;
203c3e2dc6bSNicolas Souchu 
204c3e2dc6bSNicolas Souchu 		case SMB_WAITING_LOW:
205c3e2dc6bSNicolas Souchu 			sc->low = *buf;
206c3e2dc6bSNicolas Souchu 			sc->state = SMB_WAITING_HIGH;
207c3e2dc6bSNicolas Souchu 			break;
208c3e2dc6bSNicolas Souchu 
209c3e2dc6bSNicolas Souchu 		case SMB_WAITING_HIGH:
210c3e2dc6bSNicolas Souchu 			sc->high = *buf;
211c3e2dc6bSNicolas Souchu 			sc->state = SMB_DONE;
212c3e2dc6bSNicolas Souchu 			break;
213c3e2dc6bSNicolas Souchu 		}
214c3e2dc6bSNicolas Souchu end:
215c3e2dc6bSNicolas Souchu 		break;
216c3e2dc6bSNicolas Souchu 
217c3e2dc6bSNicolas Souchu 	case INTR_TRANSMIT:
218c3e2dc6bSNicolas Souchu 	case INTR_NOACK:
219c3e2dc6bSNicolas Souchu 		break;
220c3e2dc6bSNicolas Souchu 
221c3e2dc6bSNicolas Souchu 	case INTR_ERROR:
222c3e2dc6bSNicolas Souchu 		switch (*buf) {
223c3e2dc6bSNicolas Souchu 		case IIC_EBUSERR:
224c3e2dc6bSNicolas Souchu 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
225c3e2dc6bSNicolas Souchu 			break;
226c3e2dc6bSNicolas Souchu 
227c3e2dc6bSNicolas Souchu 		default:
228c3e2dc6bSNicolas Souchu 			printf("%s unknown error 0x%x!\n", __FUNCTION__,
229c3e2dc6bSNicolas Souchu 								(int)*buf);
230c3e2dc6bSNicolas Souchu 			break;
231c3e2dc6bSNicolas Souchu 		}
232c3e2dc6bSNicolas Souchu 		break;
233c3e2dc6bSNicolas Souchu 
234c3e2dc6bSNicolas Souchu 	default:
235c3e2dc6bSNicolas Souchu 		panic("%s: unknown event (%d)!", __FUNCTION__, event);
236c3e2dc6bSNicolas Souchu 	}
237c3e2dc6bSNicolas Souchu 
238c3e2dc6bSNicolas Souchu 	return;
239c3e2dc6bSNicolas Souchu }
240c3e2dc6bSNicolas Souchu 
241c3e2dc6bSNicolas Souchu static int
24204f89a63SNicolas Souchu iicsmb_callback(device_t dev, int index, caddr_t data)
24304f89a63SNicolas Souchu {
24404f89a63SNicolas Souchu 	device_t parent = device_get_parent(dev);
24504f89a63SNicolas Souchu 	int error = 0;
24604f89a63SNicolas Souchu 	int how;
24704f89a63SNicolas Souchu 
24804f89a63SNicolas Souchu 	switch (index) {
24904f89a63SNicolas Souchu 	case SMB_REQUEST_BUS:
25004f89a63SNicolas Souchu 		/* request underlying iicbus */
25104f89a63SNicolas Souchu 		how = *(int *)data;
25204f89a63SNicolas Souchu 		error = iicbus_request_bus(parent, dev, how);
25304f89a63SNicolas Souchu 		break;
25404f89a63SNicolas Souchu 
25504f89a63SNicolas Souchu 	case SMB_RELEASE_BUS:
25604f89a63SNicolas Souchu 		/* release underlying iicbus */
25704f89a63SNicolas Souchu 		error = iicbus_release_bus(parent, dev);
25804f89a63SNicolas Souchu 		break;
25904f89a63SNicolas Souchu 
26004f89a63SNicolas Souchu 	default:
26104f89a63SNicolas Souchu 		error = EINVAL;
26204f89a63SNicolas Souchu 	}
26304f89a63SNicolas Souchu 
26404f89a63SNicolas Souchu 	return (error);
26504f89a63SNicolas Souchu }
26604f89a63SNicolas Souchu 
26704f89a63SNicolas Souchu static int
268c3e2dc6bSNicolas Souchu iicsmb_quick(device_t dev, u_char slave, int how)
269c3e2dc6bSNicolas Souchu {
270c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
271c3e2dc6bSNicolas Souchu 	int error;
272c3e2dc6bSNicolas Souchu 
273c3e2dc6bSNicolas Souchu 	switch (how) {
274c3e2dc6bSNicolas Souchu 	case SMB_QWRITE:
27504f89a63SNicolas Souchu 		error = iicbus_start(parent, slave & ~LSB, 0);
276c3e2dc6bSNicolas Souchu 		break;
277c3e2dc6bSNicolas Souchu 
278c3e2dc6bSNicolas Souchu 	case SMB_QREAD:
27904f89a63SNicolas Souchu 		error = iicbus_start(parent, slave | LSB, 0);
280c3e2dc6bSNicolas Souchu 		break;
281c3e2dc6bSNicolas Souchu 
282c3e2dc6bSNicolas Souchu 	default:
283c3e2dc6bSNicolas Souchu 		error = EINVAL;
284c3e2dc6bSNicolas Souchu 		break;
285c3e2dc6bSNicolas Souchu 	}
286c3e2dc6bSNicolas Souchu 
287c3e2dc6bSNicolas Souchu 	if (!error)
288c3e2dc6bSNicolas Souchu 		error = iicbus_stop(parent);
289c3e2dc6bSNicolas Souchu 
290c3e2dc6bSNicolas Souchu 	return (error);
291c3e2dc6bSNicolas Souchu }
292c3e2dc6bSNicolas Souchu 
293c3e2dc6bSNicolas Souchu static int
294c3e2dc6bSNicolas Souchu iicsmb_sendb(device_t dev, u_char slave, char byte)
295c3e2dc6bSNicolas Souchu {
296c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
297c3e2dc6bSNicolas Souchu 	int error, sent;
298c3e2dc6bSNicolas Souchu 
29904f89a63SNicolas Souchu 	error = iicbus_start(parent, slave & ~LSB, 0);
300c3e2dc6bSNicolas Souchu 
301c3e2dc6bSNicolas Souchu 	if (!error) {
30204f89a63SNicolas Souchu 		error = iicbus_write(parent, &byte, 1, &sent, 0);
303c3e2dc6bSNicolas Souchu 
304c3e2dc6bSNicolas Souchu 		iicbus_stop(parent);
305c3e2dc6bSNicolas Souchu 	}
306c3e2dc6bSNicolas Souchu 
307c3e2dc6bSNicolas Souchu 	return (error);
308c3e2dc6bSNicolas Souchu }
309c3e2dc6bSNicolas Souchu 
310c3e2dc6bSNicolas Souchu static int
311c3e2dc6bSNicolas Souchu iicsmb_recvb(device_t dev, u_char slave, char *byte)
312c3e2dc6bSNicolas Souchu {
313c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
314c3e2dc6bSNicolas Souchu 	int error, read;
315c3e2dc6bSNicolas Souchu 
31604f89a63SNicolas Souchu 	error = iicbus_start(parent, slave | LSB, 0);
317c3e2dc6bSNicolas Souchu 
31804f89a63SNicolas Souchu 	if (!error) {
31904f89a63SNicolas Souchu 		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0);
32004f89a63SNicolas Souchu 
32104f89a63SNicolas Souchu 		iicbus_stop(parent);
32204f89a63SNicolas Souchu 	}
323c3e2dc6bSNicolas Souchu 
324c3e2dc6bSNicolas Souchu 	return (error);
325c3e2dc6bSNicolas Souchu }
326c3e2dc6bSNicolas Souchu 
327c3e2dc6bSNicolas Souchu static int
328c3e2dc6bSNicolas Souchu iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
329c3e2dc6bSNicolas Souchu {
330c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
331c3e2dc6bSNicolas Souchu 	int error, sent;
332c3e2dc6bSNicolas Souchu 
33304f89a63SNicolas Souchu 	error = iicbus_start(parent, slave & ~LSB, 0);
334c3e2dc6bSNicolas Souchu 
335c3e2dc6bSNicolas Souchu 	if (!error) {
33604f89a63SNicolas Souchu 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
33704f89a63SNicolas Souchu 			error = iicbus_write(parent, &byte, 1, &sent, 0);
338c3e2dc6bSNicolas Souchu 
339c3e2dc6bSNicolas Souchu 		iicbus_stop(parent);
340c3e2dc6bSNicolas Souchu 	}
341c3e2dc6bSNicolas Souchu 
342c3e2dc6bSNicolas Souchu 	return (error);
343c3e2dc6bSNicolas Souchu }
344c3e2dc6bSNicolas Souchu 
345c3e2dc6bSNicolas Souchu static int
346c3e2dc6bSNicolas Souchu iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
347c3e2dc6bSNicolas Souchu {
348c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
349c3e2dc6bSNicolas Souchu 	int error, sent;
350c3e2dc6bSNicolas Souchu 
351c3e2dc6bSNicolas Souchu 	char low = (char)(word & 0xff);
352c3e2dc6bSNicolas Souchu 	char high = (char)((word & 0xff00) >> 8);
353c3e2dc6bSNicolas Souchu 
35404f89a63SNicolas Souchu 	error = iicbus_start(parent, slave & ~LSB, 0);
355c3e2dc6bSNicolas Souchu 
356c3e2dc6bSNicolas Souchu 	if (!error) {
35704f89a63SNicolas Souchu 		if (!(error = iicbus_write(parent, &cmd, 1, &sent, 0)))
35804f89a63SNicolas Souchu 		  if (!(error = iicbus_write(parent, &low, 1, &sent, 0)))
35904f89a63SNicolas Souchu 		    error = iicbus_write(parent, &high, 1, &sent, 0);
360c3e2dc6bSNicolas Souchu 
361c3e2dc6bSNicolas Souchu 		iicbus_stop(parent);
362c3e2dc6bSNicolas Souchu 	}
363c3e2dc6bSNicolas Souchu 
364c3e2dc6bSNicolas Souchu 	return (error);
365c3e2dc6bSNicolas Souchu }
366c3e2dc6bSNicolas Souchu 
367c3e2dc6bSNicolas Souchu static int
368c3e2dc6bSNicolas Souchu iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
369c3e2dc6bSNicolas Souchu {
370c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
371c3e2dc6bSNicolas Souchu 	int error, sent, read;
372c3e2dc6bSNicolas Souchu 
37304f89a63SNicolas Souchu 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
37404f89a63SNicolas Souchu 		return (error);
37504f89a63SNicolas Souchu 
37604f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
377c3e2dc6bSNicolas Souchu 		goto error;
378c3e2dc6bSNicolas Souchu 
37904f89a63SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
380c3e2dc6bSNicolas Souchu 		goto error;
381c3e2dc6bSNicolas Souchu 
38204f89a63SNicolas Souchu 	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, 0)))
383c3e2dc6bSNicolas Souchu 		goto error;
384c3e2dc6bSNicolas Souchu 
385c3e2dc6bSNicolas Souchu error:
38604f89a63SNicolas Souchu 	iicbus_stop(parent);
387c3e2dc6bSNicolas Souchu 	return (error);
388c3e2dc6bSNicolas Souchu }
389c3e2dc6bSNicolas Souchu 
390c3e2dc6bSNicolas Souchu #define BUF2SHORT(low,high) \
391c3e2dc6bSNicolas Souchu 	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
392c3e2dc6bSNicolas Souchu 
393c3e2dc6bSNicolas Souchu static int
394c3e2dc6bSNicolas Souchu iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
395c3e2dc6bSNicolas Souchu {
396c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
397c3e2dc6bSNicolas Souchu 	int error, sent, read;
398c3e2dc6bSNicolas Souchu 	char buf[2];
399c3e2dc6bSNicolas Souchu 
40004f89a63SNicolas Souchu 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
40104f89a63SNicolas Souchu 		return (error);
40204f89a63SNicolas Souchu 
40304f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
404c3e2dc6bSNicolas Souchu 		goto error;
405c3e2dc6bSNicolas Souchu 
40604f89a63SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
407c3e2dc6bSNicolas Souchu 		goto error;
408c3e2dc6bSNicolas Souchu 
40904f89a63SNicolas Souchu 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
410c3e2dc6bSNicolas Souchu 		goto error;
411c3e2dc6bSNicolas Souchu 
412c3e2dc6bSNicolas Souchu 	/* first, receive low, then high byte */
413c3e2dc6bSNicolas Souchu 	*word = BUF2SHORT(buf[0], buf[1]);
414c3e2dc6bSNicolas Souchu 
415c3e2dc6bSNicolas Souchu error:
41604f89a63SNicolas Souchu 	iicbus_stop(parent);
417c3e2dc6bSNicolas Souchu 	return (error);
418c3e2dc6bSNicolas Souchu }
419c3e2dc6bSNicolas Souchu 
420c3e2dc6bSNicolas Souchu static int
421c3e2dc6bSNicolas Souchu iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
422c3e2dc6bSNicolas Souchu {
423c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
424c3e2dc6bSNicolas Souchu 	int error, sent, read;
425c3e2dc6bSNicolas Souchu 	char buf[2];
426c3e2dc6bSNicolas Souchu 
42704f89a63SNicolas Souchu 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
42804f89a63SNicolas Souchu 		return (error);
429c3e2dc6bSNicolas Souchu 
43004f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
431c3e2dc6bSNicolas Souchu 		goto error;
432c3e2dc6bSNicolas Souchu 
433c3e2dc6bSNicolas Souchu 	/* first, send low, then high byte */
434c3e2dc6bSNicolas Souchu 	buf[0] = (char)(sdata & 0xff);
435c3e2dc6bSNicolas Souchu 	buf[1] = (char)((sdata & 0xff00) >> 8);
436c3e2dc6bSNicolas Souchu 
43704f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, buf, 2, &sent, 0)))
438c3e2dc6bSNicolas Souchu 		goto error;
439c3e2dc6bSNicolas Souchu 
44004f89a63SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
441c3e2dc6bSNicolas Souchu 		goto error;
442c3e2dc6bSNicolas Souchu 
44304f89a63SNicolas Souchu 	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, 0)))
444c3e2dc6bSNicolas Souchu 		goto error;
445c3e2dc6bSNicolas Souchu 
446c3e2dc6bSNicolas Souchu 	/* first, receive low, then high byte */
447c3e2dc6bSNicolas Souchu 	*rdata = BUF2SHORT(buf[0], buf[1]);
448c3e2dc6bSNicolas Souchu 
449c3e2dc6bSNicolas Souchu error:
45004f89a63SNicolas Souchu 	iicbus_stop(parent);
451c3e2dc6bSNicolas Souchu 	return (error);
452c3e2dc6bSNicolas Souchu }
453c3e2dc6bSNicolas Souchu 
454c3e2dc6bSNicolas Souchu static int
455c3e2dc6bSNicolas Souchu iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
456c3e2dc6bSNicolas Souchu {
457c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
458c3e2dc6bSNicolas Souchu 	int error, sent;
459c3e2dc6bSNicolas Souchu 
46004f89a63SNicolas Souchu 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
461c3e2dc6bSNicolas Souchu 		goto error;
462c3e2dc6bSNicolas Souchu 
46304f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
464c3e2dc6bSNicolas Souchu 		goto error;
465c3e2dc6bSNicolas Souchu 
46604f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, buf, (int)count, &sent, 0)))
467c3e2dc6bSNicolas Souchu 		goto error;
468c3e2dc6bSNicolas Souchu 
469c3e2dc6bSNicolas Souchu 	if ((error = iicbus_stop(parent)))
470c3e2dc6bSNicolas Souchu 		goto error;
471c3e2dc6bSNicolas Souchu 
472c3e2dc6bSNicolas Souchu error:
473c3e2dc6bSNicolas Souchu 	return (error);
474c3e2dc6bSNicolas Souchu }
475c3e2dc6bSNicolas Souchu 
476c3e2dc6bSNicolas Souchu static int
477c3e2dc6bSNicolas Souchu iicsmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
478c3e2dc6bSNicolas Souchu {
479c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
480c3e2dc6bSNicolas Souchu 	int error, sent, read;
481c3e2dc6bSNicolas Souchu 
48204f89a63SNicolas Souchu 	if ((error = iicbus_start(parent, slave & ~LSB, 0)))
48304f89a63SNicolas Souchu 		return (error);
48404f89a63SNicolas Souchu 
48504f89a63SNicolas Souchu 	if ((error = iicbus_write(parent, &cmd, 1, &sent, 0)))
486c3e2dc6bSNicolas Souchu 		goto error;
487c3e2dc6bSNicolas Souchu 
48804f89a63SNicolas Souchu 	if ((error = iicbus_repeated_start(parent, slave | LSB, 0)))
489c3e2dc6bSNicolas Souchu 		goto error;
490c3e2dc6bSNicolas Souchu 
49104f89a63SNicolas Souchu 	if ((error = iicbus_read(parent, buf, (int)count, &read,
49204f89a63SNicolas Souchu 							IIC_LAST_READ, 0)))
493c3e2dc6bSNicolas Souchu 		goto error;
494c3e2dc6bSNicolas Souchu 
495c3e2dc6bSNicolas Souchu error:
49604f89a63SNicolas Souchu 	iicbus_stop(parent);
497c3e2dc6bSNicolas Souchu 	return (error);
498c3e2dc6bSNicolas Souchu }
499c3e2dc6bSNicolas Souchu 
500c3e2dc6bSNicolas Souchu DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
501