Home | History | Annotate | Line # | Download | only in pci
viapcib.c revision 1.14
      1 /* $NetBSD: viapcib.c,v 1.14 2011/07/05 07:08:17 mrg Exp $ */
      2 /* $FreeBSD: src/sys/pci/viapm.c,v 1.10 2005/05/29 04:42:29 nyan Exp $ */
      3 
      4 /*-
      5  * Copyright (c) 2005, 2006 Jared D. McNeill <jmcneill (at) invisible.ca>
      6  * All rights reserved.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions, and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. The name of the author may not be used to endorse or promote products
     17  *    derived from this software without specific prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     20  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     21  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     22  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     23  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
     24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
     26  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
     27  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     29  * SUCH DAMAGE.
     30  */
     31 /*-
     32  * Copyright (c) 2001 Alcove - Nicolas Souchu
     33  * All rights reserved.
     34  *
     35  * Redistribution and use in source and binary forms, with or without
     36  * modification, are permitted provided that the following conditions
     37  * are met:
     38  * 1. Redistributions of source code must retain the above copyright
     39  *    notice, this list of conditions and the following disclaimer.
     40  * 2. Redistributions in binary form must reproduce the above copyright
     41  *    notice, this list of conditions and the following disclaimer in the
     42  *    documentation and/or other materials provided with the distribution.
     43  *
     44  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     45  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     46  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     47  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     48  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     49  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     50  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     51  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     52  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     53  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     54  * SUCH DAMAGE.
     55  */
     56 
     57 #include <sys/cdefs.h>
     58 __KERNEL_RCSID(0, "$NetBSD: viapcib.c,v 1.14 2011/07/05 07:08:17 mrg Exp $");
     59 
     60 #include <sys/types.h>
     61 #include <sys/param.h>
     62 #include <sys/systm.h>
     63 #include <sys/device.h>
     64 #include <sys/mutex.h>
     65 #include <sys/bus.h>
     66 
     67 #include <dev/pci/pcireg.h>
     68 #include <dev/pci/pcivar.h>
     69 #include <dev/pci/pcidevs.h>
     70 
     71 #include <dev/i2c/i2cvar.h>
     72 
     73 #include <i386/pci/viapcibreg.h>
     74 #include <x86/pci/pcibvar.h>
     75 
     76 /*#define VIAPCIB_DEBUG*/
     77 
     78 #ifdef	VIAPCIB_DEBUG
     79 #define	DPRINTF(x)	printf x
     80 #else
     81 #define	DPRINTF(x)
     82 #endif
     83 
     84 struct viapcib_softc {
     85 	/* we call pcibattach(), which assumes softc starts like this: */
     86 	struct pcib_softc sc_pcib;
     87 
     88 	bus_space_tag_t	sc_iot;
     89 	bus_space_handle_t sc_ioh;
     90 	struct i2c_controller sc_i2c;
     91 
     92 	int sc_revision;
     93 
     94 	kmutex_t sc_lock;
     95 };
     96 
     97 static int	viapcib_match(device_t, cfdata_t, void *);
     98 static void	viapcib_attach(device_t, device_t, void *);
     99 
    100 static int	viapcib_clear(struct viapcib_softc *);
    101 static int	viapcib_busy(struct viapcib_softc *);
    102 
    103 #define		viapcib_smbus_read(sc, o) \
    104 	bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (o))
    105 #define		viapcib_smbus_write(sc, o, v) \
    106 	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (o), (v))
    107 
    108 #define	VIAPCIB_SMBUS_TIMEOUT	10000
    109 
    110 static int	viapcib_acquire_bus(void *, int);
    111 static void	viapcib_release_bus(void *, int);
    112 static int	viapcib_exec(void *, i2c_op_t, i2c_addr_t, const void *,
    113 			     size_t, void *, size_t, int);
    114 
    115 /* SMBus operations */
    116 static int      viapcib_smbus_quick_write(void *, i2c_addr_t);
    117 static int      viapcib_smbus_quick_read(void *, i2c_addr_t);
    118 static int      viapcib_smbus_send_byte(void *, i2c_addr_t, uint8_t);
    119 static int      viapcib_smbus_receive_byte(void *, i2c_addr_t,
    120                                            uint8_t *);
    121 static int      viapcib_smbus_read_byte(void *, i2c_addr_t, uint8_t,
    122 					int8_t *);
    123 static int      viapcib_smbus_write_byte(void *, i2c_addr_t, uint8_t,
    124 					 int8_t);
    125 static int      viapcib_smbus_read_word(void *, i2c_addr_t, uint8_t,
    126 					int16_t *);
    127 static int      viapcib_smbus_write_word(void *, i2c_addr_t, uint8_t,
    128 					 int16_t);
    129 static int      viapcib_smbus_block_write(void *, i2c_addr_t, uint8_t,
    130 					  int, void *);
    131 static int      viapcib_smbus_block_read(void *, i2c_addr_t, uint8_t,
    132 					 int, void *);
    133 /* XXX Should be moved to smbus layer */
    134 #define	SMB_MAXBLOCKSIZE	32
    135 
    136 CFATTACH_DECL_NEW(viapcib, sizeof(struct viapcib_softc),
    137     viapcib_match, viapcib_attach, NULL, NULL);
    138 
    139 static int
    140 viapcib_match(device_t parent, cfdata_t match, void *opaque)
    141 {
    142 	struct pci_attach_args *pa = opaque;
    143 
    144 	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_VIATECH)
    145 		return 0;
    146 
    147 	switch (PCI_PRODUCT(pa->pa_id)) {
    148 	case PCI_PRODUCT_VIATECH_VT8235:
    149 	case PCI_PRODUCT_VIATECH_VT8237:
    150 	case PCI_PRODUCT_VIATECH_VT8237A_ISA:
    151 		return 2; /* match above generic pcib(4) */
    152 	}
    153 
    154 	return 0;
    155 }
    156 
    157 static void
    158 viapcib_attach(device_t parent, device_t self, void *opaque)
    159 {
    160 	struct viapcib_softc *sc = device_private(self);
    161 	struct pci_attach_args *pa = opaque;
    162 	pcireg_t addr, val;
    163 
    164 	/* XXX Only the 8235 is supported for now */
    165 	sc->sc_iot = pa->pa_iot;
    166 	addr = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_BASE3);
    167 	addr &= 0xfff0;
    168 	if (bus_space_map(sc->sc_iot, addr, 8, 0, &sc->sc_ioh)) {
    169 		printf(": failed to map SMBus I/O space\n");
    170 		addr = 0;
    171 		goto core_pcib;
    172 	}
    173 
    174 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_NONE);
    175 
    176 	val = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_HOST_CONFIG);
    177 	if ((val & 0x10000) == 0) {
    178 		printf(": SMBus is disabled\n");
    179 		addr = 0;
    180 		/* XXX We can enable the SMBus here by writing 1 to
    181 		 * SMB_HOST_CONFIG, but do we want to?
    182 		 */
    183 		goto core_pcib;
    184 	}
    185 
    186 #ifdef VIAPCIB_DEBUG
    187 	switch (val & 0x0e) {
    188 	case 8:
    189 		printf(": interrupting at irq 9\n");
    190 		break;
    191 	case 0:
    192 		printf(": interrupting at SMI#\n");
    193 		break;
    194 	default:
    195 		printf(": interrupt misconfigured\n");
    196 		break;
    197 	}
    198 #endif /* !VIAPCIB_DEBUG */
    199 
    200 	val = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_REVISION);
    201 	sc->sc_revision = val >> 16;
    202 
    203 core_pcib:
    204 	pcibattach(parent, self, opaque);
    205 
    206 	if (addr != 0) {
    207 		struct i2cbus_attach_args iba;
    208 		uint8_t b;
    209 
    210 		printf("%s: SMBus found at 0x%x (revision 0x%x)\n",
    211 		    device_xname(self), addr, sc->sc_revision);
    212 
    213 		/* Disable slave function */
    214 		b = viapcib_smbus_read(sc, SMBSLVCNT);
    215 		viapcib_smbus_write(sc, SMBSLVCNT, b & ~1);
    216 
    217 		memset(&sc->sc_i2c, 0, sizeof(sc->sc_i2c));
    218 #ifdef I2C_TYPE_SMBUS
    219 		iba.iba_type = I2C_TYPE_SMBUS;
    220 #endif
    221 		iba.iba_tag = &sc->sc_i2c;
    222 		iba.iba_tag->ic_cookie = (void *)sc;
    223 		iba.iba_tag->ic_acquire_bus = viapcib_acquire_bus;
    224 		iba.iba_tag->ic_release_bus = viapcib_release_bus;
    225 		iba.iba_tag->ic_exec = viapcib_exec;
    226 
    227 		config_found_ia(self, "i2cbus", &iba, iicbus_print);
    228 	}
    229 }
    230 
    231 static int
    232 viapcib_wait(struct viapcib_softc *sc)
    233 {
    234 	int rv, timeout;
    235 	uint8_t val = 0;
    236 
    237 	timeout = VIAPCIB_SMBUS_TIMEOUT;
    238 	rv = 0;
    239 
    240 	while (timeout--) {
    241 		val = viapcib_smbus_read(sc, SMBHSTSTS);
    242 		if (!(val & SMBHSTSTS_BUSY) && (val & SMBHSTSTS_INTR))
    243 			break;
    244 		DELAY(10);
    245 	}
    246 
    247 	if (timeout == 0)
    248 		rv = EBUSY;
    249 
    250 	if ((val & SMBHSTSTS_FAILED) || (val & SMBHSTSTS_COLLISION) ||
    251 	    (val & SMBHSTSTS_ERROR))
    252 		rv = EIO;
    253 
    254 	viapcib_clear(sc);
    255 
    256 	return rv;
    257 }
    258 
    259 static int
    260 viapcib_clear(struct viapcib_softc *sc)
    261 {
    262 	viapcib_smbus_write(sc, SMBHSTSTS,
    263 	    (SMBHSTSTS_FAILED | SMBHSTSTS_COLLISION | SMBHSTSTS_ERROR |
    264 	     SMBHSTSTS_INTR));
    265 	DELAY(10);
    266 
    267 	return 0;
    268 }
    269 
    270 static int
    271 viapcib_busy(struct viapcib_softc *sc)
    272 {
    273 	uint8_t val;
    274 
    275 	val = viapcib_smbus_read(sc, SMBHSTSTS);
    276 
    277 	return (val & SMBHSTSTS_BUSY);
    278 }
    279 
    280 static int
    281 viapcib_acquire_bus(void *opaque, int flags)
    282 {
    283 	struct viapcib_softc *sc = (struct viapcib_softc *)opaque;
    284 
    285 	DPRINTF(("viapcib_i2c_acquire_bus(%p, 0x%x)\n", opaque, flags));
    286 	mutex_enter(&sc->sc_lock);
    287 
    288 	return 0;
    289 }
    290 
    291 static void
    292 viapcib_release_bus(void *opaque, int flags)
    293 {
    294 	struct viapcib_softc *sc = (struct viapcib_softc *)opaque;
    295 
    296 	mutex_exit(&sc->sc_lock);
    297 	DPRINTF(("viapcib_i2c_release_bus(%p, 0x%x)\n", opaque, flags));
    298 }
    299 
    300 static int
    301 viapcib_exec(void *opaque, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
    302     size_t cmdlen, void *vbuf, size_t buflen, int flags)
    303 {
    304 	struct viapcib_softc *sc;
    305 	uint8_t cmd;
    306 	int rv = -1;
    307 
    308 	DPRINTF(("viapcib_exec(%p, 0x%x, 0x%x, %p, %d, %p, %d, 0x%x)\n",
    309 	    opaque, op, addr, vcmd, cmdlen, vbuf, buflen, flags));
    310 
    311 	sc = (struct viapcib_softc *)opaque;
    312 
    313 	if (op != I2C_OP_READ_WITH_STOP &&
    314 	    op != I2C_OP_WRITE_WITH_STOP)
    315 		return -1;
    316 
    317 	if (cmdlen > 0)
    318 		cmd = *(uint8_t *)(__UNCONST(vcmd)); /* XXX */
    319 
    320 	switch (cmdlen) {
    321 	case 0:
    322 		switch (buflen) {
    323 		case 0:
    324 			/* SMBus quick read/write */
    325 			if (I2C_OP_READ_P(op))
    326 				rv = viapcib_smbus_quick_read(sc, addr);
    327 			else
    328 				rv = viapcib_smbus_quick_write(sc, addr);
    329 
    330 			return rv;
    331 		case 1:
    332 			/* SMBus send/receive byte */
    333 			if (I2C_OP_READ_P(op))
    334 				rv = viapcib_smbus_send_byte(sc, addr,
    335 				    *(uint8_t *)vbuf);
    336 			else
    337 				rv = viapcib_smbus_receive_byte(sc, addr,
    338 				    (uint8_t *)vbuf);
    339 			return rv;
    340 		default:
    341 			return -1;
    342 		}
    343 	case 1:
    344 		switch (buflen) {
    345 		case 0:
    346 			return -1;
    347 		case 1:
    348 			/* SMBus read/write byte */
    349 			if (I2C_OP_READ_P(op))
    350 				rv = viapcib_smbus_read_byte(sc, addr,
    351 				    cmd, (uint8_t *)vbuf);
    352 			else
    353 				rv = viapcib_smbus_write_byte(sc, addr,
    354 				    cmd, *(uint8_t *)vbuf);
    355 			return rv;
    356 		case 2:
    357 			/* SMBus read/write word */
    358 			if (I2C_OP_READ_P(op))
    359 				rv = viapcib_smbus_read_word(sc, addr,
    360 				    cmd, (uint16_t *)vbuf);
    361 			else
    362 				rv = viapcib_smbus_write_word(sc, addr,
    363 				    cmd, *(uint16_t *)vbuf);
    364 			return rv;
    365 		default:
    366 			/* SMBus read/write block */
    367 			if (I2C_OP_READ_P(op))
    368 				rv = viapcib_smbus_block_read(sc, addr,
    369 				    cmd, buflen, vbuf);
    370 			else
    371 				rv = viapcib_smbus_block_write(sc, addr,
    372 				    cmd, buflen, vbuf);
    373 			return rv;
    374 		}
    375 	}
    376 
    377 	return -1; /* XXX ??? */
    378 }
    379 
    380 static int
    381 viapcib_smbus_quick_write(void *opaque, i2c_addr_t slave)
    382 {
    383 	struct viapcib_softc *sc;
    384 
    385 	sc = (struct viapcib_softc *)opaque;
    386 
    387 	DPRINTF(("viapcib_smbus_quick_write(%p, 0x%x)\n", opaque, slave));
    388 
    389 	viapcib_clear(sc);
    390 	if (viapcib_busy(sc))
    391 		return EBUSY;
    392 
    393 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    394 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK);
    395 	if (viapcib_wait(sc))
    396 		return EBUSY;
    397 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK | SMBHSTCNT_START);
    398 
    399 	return viapcib_wait(sc);
    400 }
    401 
    402 static int
    403 viapcib_smbus_quick_read(void *opaque, i2c_addr_t slave)
    404 {
    405 	struct viapcib_softc *sc;
    406 
    407 	sc = (struct viapcib_softc *)opaque;
    408 
    409 	DPRINTF(("viapcib_smbus_quick_read(%p, 0x%x)\n", opaque, slave));
    410 
    411 	viapcib_clear(sc);
    412 	if (viapcib_busy(sc))
    413 		return EBUSY;
    414 
    415 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    416 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK);
    417 	if (viapcib_wait(sc))
    418 		return EBUSY;
    419 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK | SMBHSTCNT_START);
    420 
    421 	return viapcib_wait(sc);
    422 }
    423 
    424 static int
    425 viapcib_smbus_send_byte(void *opaque, i2c_addr_t slave, uint8_t byte)
    426 {
    427 	struct viapcib_softc *sc;
    428 
    429 	sc = (struct viapcib_softc *)opaque;
    430 
    431 	DPRINTF(("viapcib_smbus_send_byte(%p, 0x%x, 0x%x)\n", opaque,
    432 	    slave, byte));
    433 
    434 	viapcib_clear(sc);
    435 	if (viapcib_busy(sc))
    436 		return EBUSY;
    437 
    438 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    439 	viapcib_smbus_write(sc, SMBHSTCMD, byte);
    440 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_SENDRECV);
    441 	if (viapcib_wait(sc))
    442 		return EBUSY;
    443 	viapcib_smbus_write(sc, SMBHSTCNT,
    444 	    SMBHSTCNT_START | SMBHSTCNT_SENDRECV);
    445 
    446 	return viapcib_wait(sc);
    447 }
    448 
    449 static int
    450 viapcib_smbus_receive_byte(void *opaque, i2c_addr_t slave, uint8_t *pbyte)
    451 {
    452 	struct viapcib_softc *sc;
    453 	int rv;
    454 
    455 	sc = (struct viapcib_softc *)opaque;
    456 
    457 	DPRINTF(("viapcib_smbus_receive_byte(%p, 0x%x, %p)\n", opaque,
    458 	    slave, pbyte));
    459 
    460 	viapcib_clear(sc);
    461 	if (viapcib_busy(sc))
    462 		return EBUSY;
    463 
    464 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    465 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_SENDRECV);
    466 	if (viapcib_wait(sc))
    467 		return EBUSY;
    468 	viapcib_smbus_write(sc, SMBHSTCNT,
    469 	    SMBHSTCNT_START | SMBHSTCNT_SENDRECV);
    470 
    471 	rv = viapcib_wait(sc);
    472 	if (rv == 0)
    473 		*pbyte = viapcib_smbus_read(sc, SMBHSTDAT0);
    474 
    475 	return rv;
    476 }
    477 
    478 static int
    479 viapcib_smbus_write_byte(void *opaque, i2c_addr_t slave, uint8_t cmd,
    480 		   int8_t byte)
    481 {
    482 	struct viapcib_softc *sc;
    483 
    484 	sc = (struct viapcib_softc *)opaque;
    485 
    486 	DPRINTF(("viapcib_smbus_write_byte(%p, 0x%x, 0x%x, 0x%x)\n", opaque,
    487 	    slave, cmd, byte));
    488 
    489 	viapcib_clear(sc);
    490 	if (viapcib_busy(sc))
    491 		return EBUSY;
    492 
    493 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    494 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    495 	viapcib_smbus_write(sc, SMBHSTDAT0, byte);
    496 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BYTE);
    497 	if (viapcib_wait(sc))
    498 		return EBUSY;
    499 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_BYTE);
    500 
    501 	return viapcib_wait(sc);
    502 }
    503 
    504 static int
    505 viapcib_smbus_read_byte(void *opaque, i2c_addr_t slave, uint8_t cmd,
    506 		  int8_t *pbyte)
    507 {
    508 	struct viapcib_softc *sc;
    509 	int rv;
    510 
    511 	sc = (struct viapcib_softc *)opaque;
    512 
    513 	DPRINTF(("viapcib_smbus_read_byte(%p, 0x%x, 0x%x, %p)\n", opaque,
    514 	    slave, cmd, pbyte));
    515 
    516 	viapcib_clear(sc);
    517 	if (viapcib_busy(sc))
    518 		return EBUSY;
    519 
    520 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    521 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    522 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BYTE);
    523 	if (viapcib_wait(sc))
    524 		return EBUSY;
    525 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_BYTE);
    526 	rv = viapcib_wait(sc);
    527 	if (rv == 0)
    528 		*pbyte = viapcib_smbus_read(sc, SMBHSTDAT0);
    529 
    530 	return rv;
    531 }
    532 
    533 static int
    534 viapcib_smbus_write_word(void *opaque, i2c_addr_t slave, uint8_t cmd,
    535 		   int16_t word)
    536 {
    537 	struct viapcib_softc *sc;
    538 
    539 	sc = (struct viapcib_softc *)opaque;
    540 
    541 	DPRINTF(("viapcib_smbus_write_word(%p, 0x%x, 0x%x, 0x%x)\n", opaque,
    542 	    slave, cmd, word));
    543 
    544 	viapcib_clear(sc);
    545 	if (viapcib_busy(sc))
    546 		return EBUSY;
    547 
    548 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    549 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    550 	viapcib_smbus_write(sc, SMBHSTDAT0, word & 0x00ff);
    551 	viapcib_smbus_write(sc, SMBHSTDAT1, (word & 0xff00) >> 8);
    552 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_WORD);
    553 	if (viapcib_wait(sc))
    554 		return EBUSY;
    555 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_WORD);
    556 
    557 	return viapcib_wait(sc);
    558 }
    559 
    560 static int
    561 viapcib_smbus_read_word(void *opaque, i2c_addr_t slave, uint8_t cmd,
    562 		  int16_t *pword)
    563 {
    564 	struct viapcib_softc *sc;
    565 	int rv;
    566 	int8_t high, low;
    567 
    568 	sc = (struct viapcib_softc *)opaque;
    569 
    570 	DPRINTF(("viapcib_smbus_read_word(%p, 0x%x, 0x%x, %p)\n", opaque,
    571 	    slave, cmd, pword));
    572 
    573 	viapcib_clear(sc);
    574 	if (viapcib_busy(sc))
    575 		return EBUSY;
    576 
    577 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    578 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    579 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_WORD);
    580 	if (viapcib_wait(sc))
    581 		return EBUSY;
    582 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_WORD);
    583 
    584 	rv = viapcib_wait(sc);
    585 	if (rv == 0) {
    586 		low = viapcib_smbus_read(sc, SMBHSTDAT0);
    587 		high = viapcib_smbus_read(sc, SMBHSTDAT1);
    588 		*pword = ((high & 0xff) << 8) | (low & 0xff);
    589 	}
    590 
    591 	return rv;
    592 }
    593 
    594 static int
    595 viapcib_smbus_block_write(void *opaque, i2c_addr_t slave, uint8_t cmd,
    596 		    int cnt, void *data)
    597 {
    598 	struct viapcib_softc *sc;
    599 	int8_t *buf;
    600 	int remain, len, i;
    601 	int rv;
    602 
    603 	sc = (struct viapcib_softc *)opaque;
    604 	buf = (int8_t *)data;
    605 	rv = 0;
    606 
    607 	DPRINTF(("viapcib_smbus_block_write(%p, 0x%x, 0x%x, %d, %p)\n",
    608 	    opaque, slave, cmd, cnt, data));
    609 
    610 	viapcib_clear(sc);
    611 	if (viapcib_busy(sc))
    612 		return EBUSY;
    613 
    614 	remain = cnt;
    615 	while (remain) {
    616 		len = min(remain, SMB_MAXBLOCKSIZE);
    617 
    618 		viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    619 		viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    620 		viapcib_smbus_write(sc, SMBHSTDAT0, len);
    621 		i = viapcib_smbus_read(sc, SMBHSTCNT);
    622 		/* XXX FreeBSD does this, but it looks wrong */
    623 		for (i = 0; i < len; i++) {
    624 			viapcib_smbus_write(sc, SMBBLKDAT,
    625 			    buf[cnt - remain + i]);
    626 		}
    627 		viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BLOCK);
    628 		if (viapcib_wait(sc))
    629 			return EBUSY;
    630 		viapcib_smbus_write(sc, SMBHSTCNT,
    631 		    SMBHSTCNT_START | SMBHSTCNT_BLOCK);
    632 		if (viapcib_wait(sc))
    633 			return EBUSY;
    634 
    635 		remain -= len;
    636 	}
    637 
    638 	return rv;
    639 }
    640 
    641 static int
    642 viapcib_smbus_block_read(void *opaque, i2c_addr_t slave, uint8_t cmd,
    643 			 int cnt, void *data)
    644 {
    645 	struct viapcib_softc *sc;
    646 	int8_t *buf;
    647 	int remain, len, i;
    648 	int rv;
    649 
    650 	sc = (struct viapcib_softc *)opaque;
    651 	buf = (int8_t *)data;
    652 	rv = 0;
    653 
    654 	DPRINTF(("viapcib_smbus_block_read(%p, 0x%x, 0x%x, %d, %p)\n",
    655 	    opaque, slave, cmd, cnt, data));
    656 
    657 	viapcib_clear(sc);
    658 	if (viapcib_busy(sc))
    659 		return EBUSY;
    660 
    661 	remain = cnt;
    662 	while (remain) {
    663 		viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    664 		viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    665 		viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BLOCK);
    666 		if (viapcib_wait(sc))
    667 			return EBUSY;
    668 		viapcib_smbus_write(sc, SMBHSTCNT,
    669 		    SMBHSTCNT_START | SMBHSTCNT_BLOCK);
    670 		if (viapcib_wait(sc))
    671 			return EBUSY;
    672 
    673 		len = viapcib_smbus_read(sc, SMBHSTDAT0);
    674 		i = viapcib_smbus_read(sc, SMBHSTCNT);
    675 
    676 		len = min(len, remain);
    677 
    678 		/* FreeBSD does this too... */
    679 		for (i = 0; i < len; i++) {
    680 			buf[cnt - remain + i] =
    681 			    viapcib_smbus_read(sc, SMBBLKDAT);
    682 		}
    683 
    684 		remain -= len;
    685 	}
    686 
    687 	return rv;
    688 }
    689