138774Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu
338774Snsouch * All rights reserved.
438774Snsouch *
538774Snsouch * Redistribution and use in source and binary forms, with or without
638774Snsouch * modification, are permitted provided that the following conditions
738774Snsouch * are met:
838774Snsouch * 1. Redistributions of source code must retain the above copyright
938774Snsouch *    notice, this list of conditions and the following disclaimer.
1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138774Snsouch *    notice, this list of conditions and the following disclaimer in the
1238774Snsouch *    documentation and/or other materials provided with the distribution.
1338774Snsouch *
1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738774Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438774Snsouch * SUCH DAMAGE.
2538774Snsouch */
2638774Snsouch
27227843Smarius#include <sys/cdefs.h>
28227843Smarius__FBSDID("$FreeBSD: releng/10.3/sys/dev/iicbus/iicsmb.c 294490 2016-01-21 08:32:11Z dumbbell $");
29227843Smarius
3038774Snsouch/*
3138774Snsouch * I2C to SMB bridge
3240782Snsouch *
3340782Snsouch * Example:
3440782Snsouch *
3540782Snsouch *     smb bttv
3640782Snsouch *       \ /
3740782Snsouch *      smbus
3840782Snsouch *       /  \
3940782Snsouch *    iicsmb bti2c
4040782Snsouch *       |
4140782Snsouch *     iicbus
4240782Snsouch *     /  |  \
4340782Snsouch *  iicbb pcf ...
4440782Snsouch *    |
4540782Snsouch *  lpbb
4638774Snsouch */
4738774Snsouch
4838774Snsouch#include <sys/param.h>
49179624Sjhb#include <sys/bus.h>
5038774Snsouch#include <sys/kernel.h>
51179624Sjhb#include <sys/lock.h>
52179624Sjhb#include <sys/module.h>
53179624Sjhb#include <sys/mutex.h>
5438774Snsouch#include <sys/systm.h>
5538774Snsouch#include <sys/uio.h>
5638774Snsouch
5738774Snsouch#include <dev/iicbus/iiconf.h>
5838774Snsouch#include <dev/iicbus/iicbus.h>
5938774Snsouch
6038774Snsouch#include <dev/smbus/smbconf.h>
6138774Snsouch
6238774Snsouch#include "iicbus_if.h"
6338774Snsouch#include "smbus_if.h"
6438774Snsouch
6538774Snsouchstruct iicsmb_softc {
6638774Snsouch
6738774Snsouch#define SMB_WAITING_ADDR	0x0
6838774Snsouch#define SMB_WAITING_LOW		0x1
6938774Snsouch#define SMB_WAITING_HIGH	0x2
7038774Snsouch#define SMB_DONE		0x3
7138774Snsouch	int state;
7238774Snsouch
7338774Snsouch	u_char devaddr;			/* slave device address */
7438774Snsouch
7538774Snsouch	char low;			/* low byte received first */
7638774Snsouch	char high;			/* high byte */
7738774Snsouch
78181304Sjhb	struct mtx lock;
7938774Snsouch	device_t smbus;
8038774Snsouch};
8138774Snsouch
8238774Snsouchstatic int iicsmb_probe(device_t);
8338774Snsouchstatic int iicsmb_attach(device_t);
8493023Snsouchstatic int iicsmb_detach(device_t);
8593023Snsouchstatic void iicsmb_identify(driver_t *driver, device_t parent);
8638774Snsouch
87188461Simpstatic int iicsmb_intr(device_t dev, int event, char *buf);
88162234Sjhbstatic int iicsmb_callback(device_t dev, int index, void *data);
8938774Snsouchstatic int iicsmb_quick(device_t dev, u_char slave, int how);
9038774Snsouchstatic int iicsmb_sendb(device_t dev, u_char slave, char byte);
9138774Snsouchstatic int iicsmb_recvb(device_t dev, u_char slave, char *byte);
9238774Snsouchstatic int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
9338774Snsouchstatic int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
9438774Snsouchstatic int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
9538774Snsouchstatic int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
9638774Snsouchstatic int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
9738774Snsouchstatic int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
98162234Sjhbstatic int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
9938774Snsouch
10038774Snsouchstatic devclass_t iicsmb_devclass;
10138774Snsouch
10238774Snsouchstatic device_method_t iicsmb_methods[] = {
10338774Snsouch	/* device interface */
10493023Snsouch	DEVMETHOD(device_identify,	iicsmb_identify),
10538774Snsouch	DEVMETHOD(device_probe,		iicsmb_probe),
10638774Snsouch	DEVMETHOD(device_attach,	iicsmb_attach),
10793023Snsouch	DEVMETHOD(device_detach,	iicsmb_detach),
10838774Snsouch
10938774Snsouch	/* iicbus interface */
11038774Snsouch	DEVMETHOD(iicbus_intr,		iicsmb_intr),
11138774Snsouch
11238774Snsouch	/* smbus interface */
11340782Snsouch	DEVMETHOD(smbus_callback,	iicsmb_callback),
11438774Snsouch	DEVMETHOD(smbus_quick,		iicsmb_quick),
11538774Snsouch	DEVMETHOD(smbus_sendb,		iicsmb_sendb),
11638774Snsouch	DEVMETHOD(smbus_recvb,		iicsmb_recvb),
11738774Snsouch	DEVMETHOD(smbus_writeb,		iicsmb_writeb),
11838774Snsouch	DEVMETHOD(smbus_writew,		iicsmb_writew),
11938774Snsouch	DEVMETHOD(smbus_readb,		iicsmb_readb),
12038774Snsouch	DEVMETHOD(smbus_readw,		iicsmb_readw),
12138774Snsouch	DEVMETHOD(smbus_pcall,		iicsmb_pcall),
12238774Snsouch	DEVMETHOD(smbus_bwrite,		iicsmb_bwrite),
12338774Snsouch	DEVMETHOD(smbus_bread,		iicsmb_bread),
124227843Smarius
125227843Smarius	DEVMETHOD_END
12638774Snsouch};
12738774Snsouch
12838774Snsouchstatic driver_t iicsmb_driver = {
12938774Snsouch	"iicsmb",
13038774Snsouch	iicsmb_methods,
13138774Snsouch	sizeof(struct iicsmb_softc),
13238774Snsouch};
13338774Snsouch
13493023Snsouch#define IICBUS_TIMEOUT	100	/* us */
13593023Snsouch
13693023Snsouchstatic void
13793023Snsouchiicsmb_identify(driver_t *driver, device_t parent)
13893023Snsouch{
139181304Sjhb
140181304Sjhb	if (device_find_child(parent, "iicsmb", -1) == NULL)
141181304Sjhb		BUS_ADD_CHILD(parent, 0, "iicsmb", -1);
14293023Snsouch}
14393023Snsouch
14438774Snsouchstatic int
14538774Snsouchiicsmb_probe(device_t dev)
14638774Snsouch{
14793023Snsouch	device_set_desc(dev, "SMBus over I2C bridge");
148186833Snwhitehorn	return (BUS_PROBE_NOWILDCARD);
14938774Snsouch}
15038774Snsouch
15138774Snsouchstatic int
15238774Snsouchiicsmb_attach(device_t dev)
15338774Snsouch{
15438774Snsouch	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
15538774Snsouch
156181304Sjhb	mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
157181304Sjhb
15893023Snsouch	sc->smbus = device_add_child(dev, "smbus", -1);
15993023Snsouch
16038774Snsouch	/* probe and attach the smbus */
16193023Snsouch	bus_generic_attach(dev);
16238774Snsouch
16338774Snsouch	return (0);
16438774Snsouch}
16538774Snsouch
16693023Snsouchstatic int
16793023Snsouchiicsmb_detach(device_t dev)
16893023Snsouch{
16993023Snsouch	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
170294490Sdumbbell
17193023Snsouch	bus_generic_detach(dev);
172294490Sdumbbell	device_delete_children(dev);
173181304Sjhb	mtx_destroy(&sc->lock);
17493023Snsouch
17593023Snsouch	return (0);
17693023Snsouch}
17793023Snsouch
17838774Snsouch/*
17938774Snsouch * iicsmb_intr()
18038774Snsouch *
18138774Snsouch * iicbus interrupt handler
18238774Snsouch */
183188461Simpstatic int
18438774Snsouchiicsmb_intr(device_t dev, int event, char *buf)
18538774Snsouch{
18638774Snsouch	struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
18738774Snsouch
188181304Sjhb	mtx_lock(&sc->lock);
18938774Snsouch	switch (event) {
19038774Snsouch	case INTR_GENERAL:
19138774Snsouch	case INTR_START:
19238774Snsouch		sc->state = SMB_WAITING_ADDR;
19338774Snsouch		break;
19438774Snsouch
19538774Snsouch	case INTR_STOP:
19638774Snsouch		/* call smbus intr handler */
19738774Snsouch		smbus_intr(sc->smbus, sc->devaddr,
19838774Snsouch				sc->low, sc->high, SMB_ENOERR);
19938774Snsouch		break;
20038774Snsouch
20138774Snsouch	case INTR_RECEIVE:
20238774Snsouch		switch (sc->state) {
20338774Snsouch		case SMB_DONE:
20438774Snsouch			/* XXX too much data, discard */
20587599Sobrien			printf("%s: too much data from 0x%x\n", __func__,
20638774Snsouch				sc->devaddr & 0xff);
20738774Snsouch			goto end;
20838774Snsouch
20938774Snsouch		case SMB_WAITING_ADDR:
21038774Snsouch			sc->devaddr = (u_char)*buf;
21138774Snsouch			sc->state = SMB_WAITING_LOW;
21238774Snsouch			break;
21338774Snsouch
21438774Snsouch		case SMB_WAITING_LOW:
21538774Snsouch			sc->low = *buf;
21638774Snsouch			sc->state = SMB_WAITING_HIGH;
21738774Snsouch			break;
21838774Snsouch
21938774Snsouch		case SMB_WAITING_HIGH:
22038774Snsouch			sc->high = *buf;
22138774Snsouch			sc->state = SMB_DONE;
22238774Snsouch			break;
22338774Snsouch		}
22438774Snsouchend:
22538774Snsouch		break;
22638774Snsouch
22738774Snsouch	case INTR_TRANSMIT:
22838774Snsouch	case INTR_NOACK:
22938774Snsouch		break;
23038774Snsouch
23138774Snsouch	case INTR_ERROR:
23238774Snsouch		switch (*buf) {
23338774Snsouch		case IIC_EBUSERR:
23438774Snsouch			smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
23538774Snsouch			break;
23638774Snsouch
23738774Snsouch		default:
23887599Sobrien			printf("%s unknown error 0x%x!\n", __func__,
23938774Snsouch								(int)*buf);
24038774Snsouch			break;
24138774Snsouch		}
24238774Snsouch		break;
24338774Snsouch
24438774Snsouch	default:
24587599Sobrien		panic("%s: unknown event (%d)!", __func__, event);
24638774Snsouch	}
247181304Sjhb	mtx_unlock(&sc->lock);
24838774Snsouch
249188461Simp	return (0);
25038774Snsouch}
25138774Snsouch
25238774Snsouchstatic int
253162234Sjhbiicsmb_callback(device_t dev, int index, void *data)
25440782Snsouch{
25540782Snsouch	device_t parent = device_get_parent(dev);
25640782Snsouch	int error = 0;
25740782Snsouch	int how;
25840782Snsouch
25940782Snsouch	switch (index) {
26040782Snsouch	case SMB_REQUEST_BUS:
26140782Snsouch		/* request underlying iicbus */
26240782Snsouch		how = *(int *)data;
26340782Snsouch		error = iicbus_request_bus(parent, dev, how);
26440782Snsouch		break;
26540782Snsouch
26640782Snsouch	case SMB_RELEASE_BUS:
26740782Snsouch		/* release underlying iicbus */
26840782Snsouch		error = iicbus_release_bus(parent, dev);
26940782Snsouch		break;
27040782Snsouch
27140782Snsouch	default:
27240782Snsouch		error = EINVAL;
27340782Snsouch	}
27440782Snsouch
27540782Snsouch	return (error);
27640782Snsouch}
27740782Snsouch
27840782Snsouchstatic int
27938774Snsouchiicsmb_quick(device_t dev, u_char slave, int how)
28038774Snsouch{
28138774Snsouch	device_t parent = device_get_parent(dev);
28238774Snsouch	int error;
28338774Snsouch
28438774Snsouch	switch (how) {
28538774Snsouch	case SMB_QWRITE:
28693023Snsouch		error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
28738774Snsouch		break;
28838774Snsouch
28938774Snsouch	case SMB_QREAD:
29093023Snsouch		error = iicbus_start(parent, slave | LSB, IICBUS_TIMEOUT);
29138774Snsouch		break;
29238774Snsouch
29338774Snsouch	default:
29438774Snsouch		error = EINVAL;
29538774Snsouch		break;
29638774Snsouch	}
29738774Snsouch
29838774Snsouch	if (!error)
29938774Snsouch		error = iicbus_stop(parent);
300179624Sjhb
30138774Snsouch	return (error);
30238774Snsouch}
30338774Snsouch
30438774Snsouchstatic int
30538774Snsouchiicsmb_sendb(device_t dev, u_char slave, char byte)
30638774Snsouch{
30738774Snsouch	device_t parent = device_get_parent(dev);
30838774Snsouch	int error, sent;
30938774Snsouch
31093023Snsouch	error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT);
31138774Snsouch
31238774Snsouch	if (!error) {
31393023Snsouch		error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
31438774Snsouch
31538774Snsouch		iicbus_stop(parent);
31638774Snsouch	}
31738774Snsouch
31838774Snsouch	return (error);
31938774Snsouch}
32038774Snsouch
32138774Snsouchstatic int
32238774Snsouchiicsmb_recvb(device_t dev, u_char slave, char *byte)
32338774Snsouch{
32438774Snsouch	device_t parent = device_get_parent(dev);
32538774Snsouch	int error, read;
32638774Snsouch
32740782Snsouch	error = iicbus_start(parent, slave | LSB, 0);
32838774Snsouch
32940782Snsouch	if (!error) {
33093023Snsouch		error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT);
33138774Snsouch
33240782Snsouch		iicbus_stop(parent);
33340782Snsouch	}
33440782Snsouch
33538774Snsouch	return (error);
33638774Snsouch}
33738774Snsouch
33838774Snsouchstatic int
33938774Snsouchiicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
34038774Snsouch{
34138774Snsouch	device_t parent = device_get_parent(dev);
34238774Snsouch	int error, sent;
34338774Snsouch
34440782Snsouch	error = iicbus_start(parent, slave & ~LSB, 0);
34538774Snsouch
34638774Snsouch	if (!error) {
34793023Snsouch		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
34893023Snsouch			error = iicbus_write(parent, &byte, 1, &sent, IICBUS_TIMEOUT);
34938774Snsouch
35038774Snsouch		iicbus_stop(parent);
35138774Snsouch	}
35238774Snsouch
35338774Snsouch	return (error);
35438774Snsouch}
35538774Snsouch
35638774Snsouchstatic int
35738774Snsouchiicsmb_writew(device_t dev, u_char slave, char cmd, short word)
35838774Snsouch{
35938774Snsouch	device_t parent = device_get_parent(dev);
36038774Snsouch	int error, sent;
36138774Snsouch
36238774Snsouch	char low = (char)(word & 0xff);
36338774Snsouch	char high = (char)((word & 0xff00) >> 8);
36438774Snsouch
36540782Snsouch	error = iicbus_start(parent, slave & ~LSB, 0);
36638774Snsouch
36738774Snsouch	if (!error) {
36893023Snsouch		if (!(error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
36993023Snsouch		  if (!(error = iicbus_write(parent, &low, 1, &sent, IICBUS_TIMEOUT)))
37093023Snsouch		    error = iicbus_write(parent, &high, 1, &sent, IICBUS_TIMEOUT);
37138774Snsouch
37238774Snsouch		iicbus_stop(parent);
37338774Snsouch	}
37438774Snsouch
37538774Snsouch	return (error);
37638774Snsouch}
37738774Snsouch
37838774Snsouchstatic int
37938774Snsouchiicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
38038774Snsouch{
38138774Snsouch	device_t parent = device_get_parent(dev);
38238774Snsouch	int error, sent, read;
38338774Snsouch
384181304Sjhb	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
38540782Snsouch		return (error);
38638774Snsouch
38793023Snsouch	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
38838774Snsouch		goto error;
38938774Snsouch
39093023Snsouch	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
39138774Snsouch		goto error;
39238774Snsouch
39393023Snsouch	if ((error = iicbus_read(parent, byte, 1, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
39438774Snsouch		goto error;
39538774Snsouch
39638774Snsoucherror:
39740782Snsouch	iicbus_stop(parent);
39838774Snsouch	return (error);
39938774Snsouch}
40038774Snsouch
40138774Snsouch#define BUF2SHORT(low,high) \
40238774Snsouch	((short)(((high) & 0xff) << 8) | (short)((low) & 0xff))
40338774Snsouch
40438774Snsouchstatic int
40538774Snsouchiicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
40638774Snsouch{
40738774Snsouch	device_t parent = device_get_parent(dev);
40838774Snsouch	int error, sent, read;
40938774Snsouch	char buf[2];
41038774Snsouch
411181304Sjhb	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
41240782Snsouch		return (error);
41338774Snsouch
41493023Snsouch	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
41538774Snsouch		goto error;
41638774Snsouch
41793023Snsouch	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
41838774Snsouch		goto error;
41938774Snsouch
42093023Snsouch	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
42138774Snsouch		goto error;
42238774Snsouch
42338774Snsouch	/* first, receive low, then high byte */
42438774Snsouch	*word = BUF2SHORT(buf[0], buf[1]);
42538774Snsouch
42638774Snsoucherror:
42740782Snsouch	iicbus_stop(parent);
42838774Snsouch	return (error);
42938774Snsouch}
43038774Snsouch
43138774Snsouchstatic int
43238774Snsouchiicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
43338774Snsouch{
43438774Snsouch	device_t parent = device_get_parent(dev);
43538774Snsouch	int error, sent, read;
43638774Snsouch	char buf[2];
43738774Snsouch
438181304Sjhb	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
43940782Snsouch		return (error);
44038774Snsouch
44193023Snsouch	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
44238774Snsouch		goto error;
44338774Snsouch
44438774Snsouch	/* first, send low, then high byte */
44538774Snsouch	buf[0] = (char)(sdata & 0xff);
44638774Snsouch	buf[1] = (char)((sdata & 0xff00) >> 8);
44738774Snsouch
44893023Snsouch	if ((error = iicbus_write(parent, buf, 2, &sent, IICBUS_TIMEOUT)))
44938774Snsouch		goto error;
45038774Snsouch
45193023Snsouch	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
45238774Snsouch		goto error;
45338774Snsouch
45493023Snsouch	if ((error = iicbus_read(parent, buf, 2, &read, IIC_LAST_READ, IICBUS_TIMEOUT)))
45538774Snsouch		goto error;
45638774Snsouch
45738774Snsouch	/* first, receive low, then high byte */
45838774Snsouch	*rdata = BUF2SHORT(buf[0], buf[1]);
45938774Snsouch
46038774Snsoucherror:
46140782Snsouch	iicbus_stop(parent);
46238774Snsouch	return (error);
46338774Snsouch}
46438774Snsouch
46538774Snsouchstatic int
46638774Snsouchiicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
46738774Snsouch{
46838774Snsouch	device_t parent = device_get_parent(dev);
46938774Snsouch	int error, sent;
47038774Snsouch
47193023Snsouch	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
47238774Snsouch		goto error;
47338774Snsouch
47493023Snsouch	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
47538774Snsouch		goto error;
47638774Snsouch
47793023Snsouch	if ((error = iicbus_write(parent, buf, (int)count, &sent, IICBUS_TIMEOUT)))
47838774Snsouch		goto error;
47938774Snsouch
48038774Snsouch	if ((error = iicbus_stop(parent)))
48138774Snsouch		goto error;
48238774Snsouch
48338774Snsoucherror:
48438774Snsouch	return (error);
48538774Snsouch}
48638774Snsouch
48738774Snsouchstatic int
488162234Sjhbiicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
48938774Snsouch{
49038774Snsouch	device_t parent = device_get_parent(dev);
49138774Snsouch	int error, sent, read;
49238774Snsouch
493181304Sjhb	if ((error = iicbus_start(parent, slave & ~LSB, IICBUS_TIMEOUT)))
49440782Snsouch		return (error);
49538774Snsouch
49693023Snsouch	if ((error = iicbus_write(parent, &cmd, 1, &sent, IICBUS_TIMEOUT)))
49738774Snsouch		goto error;
49838774Snsouch
49993023Snsouch	if ((error = iicbus_repeated_start(parent, slave | LSB, IICBUS_TIMEOUT)))
50038774Snsouch		goto error;
50138774Snsouch
502162234Sjhb	if ((error = iicbus_read(parent, buf, (int)*count, &read,
50393023Snsouch						IIC_LAST_READ, IICBUS_TIMEOUT)))
50438774Snsouch		goto error;
505162234Sjhb	*count = read;
50638774Snsouch
50738774Snsoucherror:
50840782Snsouch	iicbus_stop(parent);
50938774Snsouch	return (error);
51038774Snsouch}
51138774Snsouch
51238774SnsouchDRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, iicsmb_devclass, 0, 0);
513162234SjhbDRIVER_MODULE(smbus, iicsmb, smbus_driver, smbus_devclass, 0, 0);
51493023SnsouchMODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
51593023SnsouchMODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
51693023SnsouchMODULE_VERSION(iicsmb, 1);
517