xref: /freebsd/sys/dev/iicbus/iicsmb.c (revision 718cf2ccb9956613756ab15d7a0e28f2c8e91cab)
1c3e2dc6bSNicolas Souchu /*-
2*718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3*718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
5c3e2dc6bSNicolas Souchu  * All rights reserved.
6c3e2dc6bSNicolas Souchu  *
7c3e2dc6bSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
8c3e2dc6bSNicolas Souchu  * modification, are permitted provided that the following conditions
9c3e2dc6bSNicolas Souchu  * are met:
10c3e2dc6bSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
11c3e2dc6bSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
12c3e2dc6bSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
13c3e2dc6bSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
14c3e2dc6bSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
15c3e2dc6bSNicolas Souchu  *
16c3e2dc6bSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c3e2dc6bSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c3e2dc6bSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c3e2dc6bSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c3e2dc6bSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c3e2dc6bSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c3e2dc6bSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c3e2dc6bSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c3e2dc6bSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c3e2dc6bSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c3e2dc6bSNicolas Souchu  * SUCH DAMAGE.
27c3e2dc6bSNicolas Souchu  */
28c3e2dc6bSNicolas Souchu 
294b7ec270SMarius Strobl #include <sys/cdefs.h>
304b7ec270SMarius Strobl __FBSDID("$FreeBSD$");
314b7ec270SMarius Strobl 
32c3e2dc6bSNicolas Souchu /*
33c3e2dc6bSNicolas Souchu  * I2C to SMB bridge
3404f89a63SNicolas Souchu  *
3504f89a63SNicolas Souchu  * Example:
3604f89a63SNicolas Souchu  *
3704f89a63SNicolas Souchu  *     smb bttv
3804f89a63SNicolas Souchu  *       \ /
3904f89a63SNicolas Souchu  *      smbus
4004f89a63SNicolas Souchu  *       /  \
4104f89a63SNicolas Souchu  *    iicsmb bti2c
4204f89a63SNicolas Souchu  *       |
4304f89a63SNicolas Souchu  *     iicbus
4404f89a63SNicolas Souchu  *     /  |  \
4504f89a63SNicolas Souchu  *  iicbb pcf ...
4604f89a63SNicolas Souchu  *    |
4704f89a63SNicolas Souchu  *  lpbb
48c3e2dc6bSNicolas Souchu  */
49c3e2dc6bSNicolas Souchu 
50c3e2dc6bSNicolas Souchu #include <sys/param.h>
51c3e2dc6bSNicolas Souchu #include <sys/bus.h>
520df8f081SJohn Baldwin #include <sys/kernel.h>
530df8f081SJohn Baldwin #include <sys/lock.h>
540df8f081SJohn Baldwin #include <sys/module.h>
550df8f081SJohn Baldwin #include <sys/mutex.h>
560df8f081SJohn Baldwin #include <sys/systm.h>
57c3e2dc6bSNicolas Souchu #include <sys/uio.h>
58c3e2dc6bSNicolas Souchu 
59c3e2dc6bSNicolas Souchu #include <dev/iicbus/iiconf.h>
60c3e2dc6bSNicolas Souchu #include <dev/iicbus/iicbus.h>
61c3e2dc6bSNicolas Souchu 
6288cc0bb9SAndriy Gapon #include <dev/smbus/smb.h>
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 
81313f8941SJohn Baldwin 	struct mtx lock;
82c3e2dc6bSNicolas Souchu 	device_t smbus;
83c3e2dc6bSNicolas Souchu };
84c3e2dc6bSNicolas Souchu 
85c3e2dc6bSNicolas Souchu static int iicsmb_probe(device_t);
86c3e2dc6bSNicolas Souchu static int iicsmb_attach(device_t);
87c17d4340SNicolas Souchu static int iicsmb_detach(device_t);
88c17d4340SNicolas Souchu static void iicsmb_identify(driver_t *driver, device_t parent);
89c3e2dc6bSNicolas Souchu 
90b23193a5SWarner Losh static int iicsmb_intr(device_t dev, int event, char *buf);
917048a99cSJohn Baldwin static int iicsmb_callback(device_t dev, int index, void *data);
92c3e2dc6bSNicolas Souchu static int iicsmb_quick(device_t dev, u_char slave, int how);
93c3e2dc6bSNicolas Souchu static int iicsmb_sendb(device_t dev, u_char slave, char byte);
94c3e2dc6bSNicolas Souchu static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
95c3e2dc6bSNicolas Souchu static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
96c3e2dc6bSNicolas Souchu static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
97c3e2dc6bSNicolas Souchu static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
98c3e2dc6bSNicolas Souchu static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
99c3e2dc6bSNicolas Souchu static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
100c3e2dc6bSNicolas Souchu static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
1017048a99cSJohn Baldwin static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
102c3e2dc6bSNicolas Souchu 
103c3e2dc6bSNicolas Souchu static devclass_t iicsmb_devclass;
104c3e2dc6bSNicolas Souchu 
105c3e2dc6bSNicolas Souchu static device_method_t iicsmb_methods[] = {
106c3e2dc6bSNicolas Souchu 	/* device interface */
107c17d4340SNicolas Souchu 	DEVMETHOD(device_identify,	iicsmb_identify),
108c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_probe,		iicsmb_probe),
109c3e2dc6bSNicolas Souchu 	DEVMETHOD(device_attach,	iicsmb_attach),
110c17d4340SNicolas Souchu 	DEVMETHOD(device_detach,	iicsmb_detach),
111c3e2dc6bSNicolas Souchu 
112c3e2dc6bSNicolas Souchu 	/* iicbus interface */
113c3e2dc6bSNicolas Souchu 	DEVMETHOD(iicbus_intr,		iicsmb_intr),
114c3e2dc6bSNicolas Souchu 
115c3e2dc6bSNicolas Souchu 	/* smbus interface */
11604f89a63SNicolas Souchu 	DEVMETHOD(smbus_callback,	iicsmb_callback),
117c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_quick,		iicsmb_quick),
118c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_sendb,		iicsmb_sendb),
119c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_recvb,		iicsmb_recvb),
120c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_writeb,		iicsmb_writeb),
121c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_writew,		iicsmb_writew),
122c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_readb,		iicsmb_readb),
123c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_readw,		iicsmb_readw),
124c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_pcall,		iicsmb_pcall),
125c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_bwrite,		iicsmb_bwrite),
126c3e2dc6bSNicolas Souchu 	DEVMETHOD(smbus_bread,		iicsmb_bread),
127c3e2dc6bSNicolas Souchu 
1284b7ec270SMarius Strobl 	DEVMETHOD_END
129c3e2dc6bSNicolas Souchu };
130c3e2dc6bSNicolas Souchu 
131c3e2dc6bSNicolas Souchu static driver_t iicsmb_driver = {
132c3e2dc6bSNicolas Souchu 	"iicsmb",
133c3e2dc6bSNicolas Souchu 	iicsmb_methods,
134c3e2dc6bSNicolas Souchu 	sizeof(struct iicsmb_softc),
135c3e2dc6bSNicolas Souchu };
136c3e2dc6bSNicolas Souchu 
137c17d4340SNicolas Souchu static void
138c17d4340SNicolas Souchu iicsmb_identify(driver_t *driver, device_t parent)
139c17d4340SNicolas Souchu {
140313f8941SJohn Baldwin 
141313f8941SJohn Baldwin 	if (device_find_child(parent, "iicsmb", -1) == NULL)
142b08a61b0SBernd Walter 		BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
143c17d4340SNicolas Souchu }
144c17d4340SNicolas Souchu 
145c3e2dc6bSNicolas Souchu static int
146c3e2dc6bSNicolas Souchu iicsmb_probe(device_t dev)
147c3e2dc6bSNicolas Souchu {
148c17d4340SNicolas Souchu 	device_set_desc(dev, "SMBus over I2C bridge");
149789c4b9dSNathan Whitehorn 	return (BUS_PROBE_NOWILDCARD);
150c3e2dc6bSNicolas Souchu }
151c3e2dc6bSNicolas Souchu 
152c3e2dc6bSNicolas Souchu static int
153c3e2dc6bSNicolas Souchu iicsmb_attach(device_t dev)
154c3e2dc6bSNicolas Souchu {
155c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
156c3e2dc6bSNicolas Souchu 
157313f8941SJohn Baldwin 	mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
158313f8941SJohn Baldwin 
159c17d4340SNicolas Souchu 	sc->smbus = device_add_child(dev, "smbus", -1);
160c17d4340SNicolas Souchu 
161c3e2dc6bSNicolas Souchu 	/* probe and attach the smbus */
162c17d4340SNicolas Souchu 	bus_generic_attach(dev);
163c17d4340SNicolas Souchu 
164c17d4340SNicolas Souchu 	return (0);
165c17d4340SNicolas Souchu }
166c17d4340SNicolas Souchu 
167c17d4340SNicolas Souchu static int
168c17d4340SNicolas Souchu iicsmb_detach(device_t dev)
169c17d4340SNicolas Souchu {
170c17d4340SNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
171c17d4340SNicolas Souchu 
172c17d4340SNicolas Souchu 	bus_generic_detach(dev);
173238b89fcSJean-Sébastien Pédron 	device_delete_children(dev);
174313f8941SJohn Baldwin 	mtx_destroy(&sc->lock);
175c3e2dc6bSNicolas Souchu 
176c3e2dc6bSNicolas Souchu 	return (0);
177c3e2dc6bSNicolas Souchu }
178c3e2dc6bSNicolas Souchu 
179c3e2dc6bSNicolas Souchu /*
180c3e2dc6bSNicolas Souchu  * iicsmb_intr()
181c3e2dc6bSNicolas Souchu  *
182c3e2dc6bSNicolas Souchu  * iicbus interrupt handler
183c3e2dc6bSNicolas Souchu  */
184b23193a5SWarner Losh static int
185c3e2dc6bSNicolas Souchu iicsmb_intr(device_t dev, int event, char *buf)
186c3e2dc6bSNicolas Souchu {
187c3e2dc6bSNicolas Souchu 	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
188c3e2dc6bSNicolas Souchu 
189313f8941SJohn Baldwin 	mtx_lock(&sc->lock);
190c3e2dc6bSNicolas Souchu 	switch (event) {
191c3e2dc6bSNicolas Souchu 	case INTR_GENERAL:
192c3e2dc6bSNicolas Souchu 	case INTR_START:
193c3e2dc6bSNicolas Souchu 		sc->state = SMB_WAITING_ADDR;
194c3e2dc6bSNicolas Souchu 		break;
195c3e2dc6bSNicolas Souchu 
196c3e2dc6bSNicolas Souchu 	case INTR_STOP:
197c3e2dc6bSNicolas Souchu 		/* call smbus intr handler */
198c3e2dc6bSNicolas Souchu 		smbus_intr(sc->smbus, sc->devaddr,
199c3e2dc6bSNicolas Souchu 				sc->low, sc->high, SMB_ENOERR);
200c3e2dc6bSNicolas Souchu 		break;
201c3e2dc6bSNicolas Souchu 
202c3e2dc6bSNicolas Souchu 	case INTR_RECEIVE:
203c3e2dc6bSNicolas Souchu 		switch (sc->state) {
204c3e2dc6bSNicolas Souchu 		case SMB_DONE:
205c3e2dc6bSNicolas Souchu 			/* XXX too much data, discard */
2066e551fb6SDavid E. O'Brien 			printf("%s: too much data from 0x%x\n", __func__,
207c3e2dc6bSNicolas Souchu 				sc->devaddr & 0xff);
208c3e2dc6bSNicolas Souchu 			goto end;
209c3e2dc6bSNicolas Souchu 
210c3e2dc6bSNicolas Souchu 		case SMB_WAITING_ADDR:
211c3e2dc6bSNicolas Souchu 			sc->devaddr = (u_char)*buf;
212c3e2dc6bSNicolas Souchu 			sc->state = SMB_WAITING_LOW;
213c3e2dc6bSNicolas Souchu 			break;
214c3e2dc6bSNicolas Souchu 
215c3e2dc6bSNicolas Souchu 		case SMB_WAITING_LOW:
216c3e2dc6bSNicolas Souchu 			sc->low = *buf;
217c3e2dc6bSNicolas Souchu 			sc->state = SMB_WAITING_HIGH;
218c3e2dc6bSNicolas Souchu 			break;
219c3e2dc6bSNicolas Souchu 
220c3e2dc6bSNicolas Souchu 		case SMB_WAITING_HIGH:
221c3e2dc6bSNicolas Souchu 			sc->high = *buf;
222c3e2dc6bSNicolas Souchu 			sc->state = SMB_DONE;
223c3e2dc6bSNicolas Souchu 			break;
224c3e2dc6bSNicolas Souchu 		}
225c3e2dc6bSNicolas Souchu end:
226c3e2dc6bSNicolas Souchu 		break;
227c3e2dc6bSNicolas Souchu 
228c3e2dc6bSNicolas Souchu 	case INTR_TRANSMIT:
229c3e2dc6bSNicolas Souchu 	case INTR_NOACK:
230c3e2dc6bSNicolas Souchu 		break;
231c3e2dc6bSNicolas Souchu 
232c3e2dc6bSNicolas Souchu 	case INTR_ERROR:
233c3e2dc6bSNicolas Souchu 		switch (*buf) {
234c3e2dc6bSNicolas Souchu 		case IIC_EBUSERR:
235c3e2dc6bSNicolas Souchu 			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
236c3e2dc6bSNicolas Souchu 			break;
237c3e2dc6bSNicolas Souchu 
238c3e2dc6bSNicolas Souchu 		default:
2396e551fb6SDavid E. O'Brien 			printf("%s unknown error 0x%x!\n", __func__,
240c3e2dc6bSNicolas Souchu 								(int)*buf);
241c3e2dc6bSNicolas Souchu 			break;
242c3e2dc6bSNicolas Souchu 		}
243c3e2dc6bSNicolas Souchu 		break;
244c3e2dc6bSNicolas Souchu 
245c3e2dc6bSNicolas Souchu 	default:
2466e551fb6SDavid E. O'Brien 		panic("%s: unknown event (%d)!", __func__, event);
247c3e2dc6bSNicolas Souchu 	}
248313f8941SJohn Baldwin 	mtx_unlock(&sc->lock);
249c3e2dc6bSNicolas Souchu 
250b23193a5SWarner Losh 	return (0);
251c3e2dc6bSNicolas Souchu }
252c3e2dc6bSNicolas Souchu 
253c3e2dc6bSNicolas Souchu static int
2547048a99cSJohn Baldwin iicsmb_callback(device_t dev, int index, void *data)
25504f89a63SNicolas Souchu {
25604f89a63SNicolas Souchu 	device_t parent = device_get_parent(dev);
25704f89a63SNicolas Souchu 	int error = 0;
25804f89a63SNicolas Souchu 	int how;
25904f89a63SNicolas Souchu 
26004f89a63SNicolas Souchu 	switch (index) {
26104f89a63SNicolas Souchu 	case SMB_REQUEST_BUS:
26204f89a63SNicolas Souchu 		/* request underlying iicbus */
26304f89a63SNicolas Souchu 		how = *(int *)data;
26404f89a63SNicolas Souchu 		error = iicbus_request_bus(parent, dev, how);
26504f89a63SNicolas Souchu 		break;
26604f89a63SNicolas Souchu 
26704f89a63SNicolas Souchu 	case SMB_RELEASE_BUS:
26804f89a63SNicolas Souchu 		/* release underlying iicbus */
26904f89a63SNicolas Souchu 		error = iicbus_release_bus(parent, dev);
27004f89a63SNicolas Souchu 		break;
27104f89a63SNicolas Souchu 
27204f89a63SNicolas Souchu 	default:
27304f89a63SNicolas Souchu 		error = EINVAL;
27404f89a63SNicolas Souchu 	}
27504f89a63SNicolas Souchu 
27604f89a63SNicolas Souchu 	return (error);
27704f89a63SNicolas Souchu }
27804f89a63SNicolas Souchu 
27904f89a63SNicolas Souchu static int
280f1519c01SAndriy Gapon iic2smb_error(int error)
281f1519c01SAndriy Gapon {
282f1519c01SAndriy Gapon 	switch (error) {
283f1519c01SAndriy Gapon 	case IIC_NOERR:
284f1519c01SAndriy Gapon 		return (SMB_ENOERR);
285f1519c01SAndriy Gapon 	case IIC_EBUSERR:
286f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
287f1519c01SAndriy Gapon 	case IIC_ENOACK:
288f1519c01SAndriy Gapon 		return (SMB_ENOACK);
289f1519c01SAndriy Gapon 	case IIC_ETIMEOUT:
290f1519c01SAndriy Gapon 		return (SMB_ETIMEOUT);
291f1519c01SAndriy Gapon 	case IIC_EBUSBSY:
292f1519c01SAndriy Gapon 		return (SMB_EBUSY);
293f1519c01SAndriy Gapon 	case IIC_ESTATUS:
294f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
295f1519c01SAndriy Gapon 	case IIC_EUNDERFLOW:
296f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
297f1519c01SAndriy Gapon 	case IIC_EOVERFLOW:
298f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
299f1519c01SAndriy Gapon 	case IIC_ENOTSUPP:
300f1519c01SAndriy Gapon 		return (SMB_ENOTSUPP);
301f1519c01SAndriy Gapon 	case IIC_ENOADDR:
302f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
303f1519c01SAndriy Gapon 	case IIC_ERESOURCE:
304f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
305f1519c01SAndriy Gapon 	default:
306f1519c01SAndriy Gapon 		return (SMB_EBUSERR);
307f1519c01SAndriy Gapon 	}
308f1519c01SAndriy Gapon }
309f1519c01SAndriy Gapon 
310f1519c01SAndriy Gapon #define	TRANSFER_MSGS(dev, msgs)	iicbus_transfer(dev, msgs, nitems(msgs))
311f1519c01SAndriy Gapon 
312f1519c01SAndriy Gapon static int
313c3e2dc6bSNicolas Souchu iicsmb_quick(device_t dev, u_char slave, int how)
314c3e2dc6bSNicolas Souchu {
315f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
316f1519c01SAndriy Gapon 	     { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
317f1519c01SAndriy Gapon 	};
318c3e2dc6bSNicolas Souchu 	int error;
319c3e2dc6bSNicolas Souchu 
320c3e2dc6bSNicolas Souchu 	switch (how) {
321c3e2dc6bSNicolas Souchu 	case SMB_QWRITE:
322c3e2dc6bSNicolas Souchu 	case SMB_QREAD:
323c3e2dc6bSNicolas Souchu 		break;
324c3e2dc6bSNicolas Souchu 	default:
325f1519c01SAndriy Gapon 		return (SMB_EINVAL);
326c3e2dc6bSNicolas Souchu 	}
327c3e2dc6bSNicolas Souchu 
328f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
329f1519c01SAndriy Gapon 	return (iic2smb_error(error));
330c3e2dc6bSNicolas Souchu }
331c3e2dc6bSNicolas Souchu 
332c3e2dc6bSNicolas Souchu static int
333c3e2dc6bSNicolas Souchu iicsmb_sendb(device_t dev, u_char slave, char byte)
334c3e2dc6bSNicolas Souchu {
335f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
336f1519c01SAndriy Gapon 	     { slave, IIC_M_WR, 1, &byte },
337f1519c01SAndriy Gapon 	};
338f1519c01SAndriy Gapon 	int error;
339c3e2dc6bSNicolas Souchu 
340f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
341f1519c01SAndriy Gapon 	return (iic2smb_error(error));
342c3e2dc6bSNicolas Souchu }
343c3e2dc6bSNicolas Souchu 
344c3e2dc6bSNicolas Souchu static int
345c3e2dc6bSNicolas Souchu iicsmb_recvb(device_t dev, u_char slave, char *byte)
346c3e2dc6bSNicolas Souchu {
347f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
348f1519c01SAndriy Gapon 	     { slave, IIC_M_RD, 1, byte },
349f1519c01SAndriy Gapon 	};
350f1519c01SAndriy Gapon 	int error;
351c3e2dc6bSNicolas Souchu 
352f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
353f1519c01SAndriy Gapon 	return (iic2smb_error(error));
354c3e2dc6bSNicolas Souchu }
355c3e2dc6bSNicolas Souchu 
356c3e2dc6bSNicolas Souchu static int
357c3e2dc6bSNicolas Souchu iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
358c3e2dc6bSNicolas Souchu {
359f1519c01SAndriy Gapon 	uint8_t bytes[] = { cmd, byte };
360f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
361f1519c01SAndriy Gapon 	     { slave, IIC_M_WR, nitems(bytes), bytes },
362f1519c01SAndriy Gapon 	};
363f1519c01SAndriy Gapon 	int error;
364c3e2dc6bSNicolas Souchu 
365f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
366f1519c01SAndriy Gapon 	return (iic2smb_error(error));
367c3e2dc6bSNicolas Souchu }
368c3e2dc6bSNicolas Souchu 
369c3e2dc6bSNicolas Souchu static int
370c3e2dc6bSNicolas Souchu iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
371c3e2dc6bSNicolas Souchu {
372f1519c01SAndriy Gapon 	uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
373f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
374f1519c01SAndriy Gapon 	     { slave, IIC_M_WR, nitems(bytes), bytes },
375f1519c01SAndriy Gapon 	};
376f1519c01SAndriy Gapon 	int error;
377c3e2dc6bSNicolas Souchu 
378f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
379f1519c01SAndriy Gapon 	return (iic2smb_error(error));
380c3e2dc6bSNicolas Souchu }
381c3e2dc6bSNicolas Souchu 
382c3e2dc6bSNicolas Souchu static int
383c3e2dc6bSNicolas Souchu iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
384c3e2dc6bSNicolas Souchu {
385f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
386f1519c01SAndriy Gapon 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
387f1519c01SAndriy Gapon 	     { slave, IIC_M_RD, 1, byte },
388f1519c01SAndriy Gapon 	};
389f1519c01SAndriy Gapon 	int error;
390c3e2dc6bSNicolas Souchu 
391f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
392f1519c01SAndriy Gapon 	return (iic2smb_error(error));
393c3e2dc6bSNicolas Souchu }
394c3e2dc6bSNicolas Souchu 
395c3e2dc6bSNicolas Souchu static int
396c3e2dc6bSNicolas Souchu iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
397c3e2dc6bSNicolas Souchu {
398f1519c01SAndriy Gapon 	uint8_t buf[2];
399f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
400f1519c01SAndriy Gapon 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
401f1519c01SAndriy Gapon 	     { slave, IIC_M_RD, nitems(buf), buf },
402f1519c01SAndriy Gapon 	};
403f1519c01SAndriy Gapon 	int error;
404c3e2dc6bSNicolas Souchu 
405f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
406f1519c01SAndriy Gapon 	if (error == 0)
407f1519c01SAndriy Gapon 		*word = ((uint16_t)buf[1] << 8) | buf[0];
408f1519c01SAndriy Gapon 	return (iic2smb_error(error));
409c3e2dc6bSNicolas Souchu }
410c3e2dc6bSNicolas Souchu 
411c3e2dc6bSNicolas Souchu static int
412c3e2dc6bSNicolas Souchu iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
413c3e2dc6bSNicolas Souchu {
414f1519c01SAndriy Gapon 	uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
415f1519c01SAndriy Gapon 	uint8_t out[2];
416f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
417f1519c01SAndriy Gapon 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
418f1519c01SAndriy Gapon 	     { slave, IIC_M_RD, nitems(out), out },
419f1519c01SAndriy Gapon 	};
420f1519c01SAndriy Gapon 	int error;
421c3e2dc6bSNicolas Souchu 
422f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
423f1519c01SAndriy Gapon 	if (error == 0)
424f1519c01SAndriy Gapon 		*rdata = ((uint16_t)out[1] << 8) | out[0];
425f1519c01SAndriy Gapon 	return (iic2smb_error(error));
426c3e2dc6bSNicolas Souchu }
427c3e2dc6bSNicolas Souchu 
428c3e2dc6bSNicolas Souchu static int
429c3e2dc6bSNicolas Souchu iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
430c3e2dc6bSNicolas Souchu {
431f1519c01SAndriy Gapon 	uint8_t bytes[2] = { cmd, count };
432f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
433f1519c01SAndriy Gapon 	     { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
434f1519c01SAndriy Gapon 	     { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
435f1519c01SAndriy Gapon 	};
436f1519c01SAndriy Gapon 	int error;
437c3e2dc6bSNicolas Souchu 
43888cc0bb9SAndriy Gapon 	if (count > SMB_MAXBLOCKSIZE || count == 0)
439f1519c01SAndriy Gapon 		return (SMB_EINVAL);
440f1519c01SAndriy Gapon 	error = TRANSFER_MSGS(dev, msgs);
441f1519c01SAndriy Gapon 	return (iic2smb_error(error));
442c3e2dc6bSNicolas Souchu }
443c3e2dc6bSNicolas Souchu 
444c3e2dc6bSNicolas Souchu static int
4457048a99cSJohn Baldwin iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
446c3e2dc6bSNicolas Souchu {
447f1519c01SAndriy Gapon 	struct iic_msg msgs[] = {
448f1519c01SAndriy Gapon 	     { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
449f1519c01SAndriy Gapon 	     { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
450f1519c01SAndriy Gapon 	};
451f1519c01SAndriy Gapon 	struct iic_msg block_msg[] = {
452f1519c01SAndriy Gapon 	     { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
453f1519c01SAndriy Gapon 	};
454c3e2dc6bSNicolas Souchu 	device_t parent = device_get_parent(dev);
455f1519c01SAndriy Gapon 	int error;
45604f89a63SNicolas Souchu 
457f1519c01SAndriy Gapon 	/* Have to do this because the command is split in two transfers. */
458f1519c01SAndriy Gapon 	error = iicbus_request_bus(parent, dev, IIC_WAIT);
459f1519c01SAndriy Gapon 	if (error == 0)
460f1519c01SAndriy Gapon 		error = TRANSFER_MSGS(dev, msgs);
461f1519c01SAndriy Gapon 	if (error == 0) {
462f1519c01SAndriy Gapon 		/*
463f1519c01SAndriy Gapon 		 * If the slave offers an empty or a too long reply,
464f1519c01SAndriy Gapon 		 * read one byte to generate the stop or abort.
465f1519c01SAndriy Gapon 		 */
46688cc0bb9SAndriy Gapon 		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
467f1519c01SAndriy Gapon 			block_msg[0].len = 1;
468f1519c01SAndriy Gapon 		else
469f1519c01SAndriy Gapon 			block_msg[0].len = *count;
470f1519c01SAndriy Gapon 		error = TRANSFER_MSGS(dev, block_msg);
47188cc0bb9SAndriy Gapon 		if (*count > SMB_MAXBLOCKSIZE || *count == 0)
472f1519c01SAndriy Gapon 			error = SMB_EINVAL;
473f1519c01SAndriy Gapon 	}
474f1519c01SAndriy Gapon 	(void)iicbus_release_bus(parent, dev);
475f1519c01SAndriy Gapon 	return (iic2smb_error(error));
476c3e2dc6bSNicolas Souchu }
477c3e2dc6bSNicolas Souchu 
478c3e2dc6bSNicolas Souchu DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
4797048a99cSJohn Baldwin DRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
480c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
481c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
482c17d4340SNicolas Souchu MODULE_VERSION(iicsmb, 1);
483