Home | History | Annotate | Line # | Download | only in pci
viapcib.c revision 1.15
      1 /* $NetBSD: viapcib.c,v 1.15 2016/02/14 19:54:20 chs 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.15 2016/02/14 19:54:20 chs 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 		memset(&iba, 0, sizeof(iba));
    219 #ifdef I2C_TYPE_SMBUS
    220 		iba.iba_type = I2C_TYPE_SMBUS;
    221 #endif
    222 		iba.iba_tag = &sc->sc_i2c;
    223 		iba.iba_tag->ic_cookie = (void *)sc;
    224 		iba.iba_tag->ic_acquire_bus = viapcib_acquire_bus;
    225 		iba.iba_tag->ic_release_bus = viapcib_release_bus;
    226 		iba.iba_tag->ic_exec = viapcib_exec;
    227 
    228 		config_found_ia(self, "i2cbus", &iba, iicbus_print);
    229 	}
    230 }
    231 
    232 static int
    233 viapcib_wait(struct viapcib_softc *sc)
    234 {
    235 	int rv, timeout;
    236 	uint8_t val = 0;
    237 
    238 	timeout = VIAPCIB_SMBUS_TIMEOUT;
    239 	rv = 0;
    240 
    241 	while (timeout--) {
    242 		val = viapcib_smbus_read(sc, SMBHSTSTS);
    243 		if (!(val & SMBHSTSTS_BUSY) && (val & SMBHSTSTS_INTR))
    244 			break;
    245 		DELAY(10);
    246 	}
    247 
    248 	if (timeout == 0)
    249 		rv = EBUSY;
    250 
    251 	if ((val & SMBHSTSTS_FAILED) || (val & SMBHSTSTS_COLLISION) ||
    252 	    (val & SMBHSTSTS_ERROR))
    253 		rv = EIO;
    254 
    255 	viapcib_clear(sc);
    256 
    257 	return rv;
    258 }
    259 
    260 static int
    261 viapcib_clear(struct viapcib_softc *sc)
    262 {
    263 	viapcib_smbus_write(sc, SMBHSTSTS,
    264 	    (SMBHSTSTS_FAILED | SMBHSTSTS_COLLISION | SMBHSTSTS_ERROR |
    265 	     SMBHSTSTS_INTR));
    266 	DELAY(10);
    267 
    268 	return 0;
    269 }
    270 
    271 static int
    272 viapcib_busy(struct viapcib_softc *sc)
    273 {
    274 	uint8_t val;
    275 
    276 	val = viapcib_smbus_read(sc, SMBHSTSTS);
    277 
    278 	return (val & SMBHSTSTS_BUSY);
    279 }
    280 
    281 static int
    282 viapcib_acquire_bus(void *opaque, int flags)
    283 {
    284 	struct viapcib_softc *sc = (struct viapcib_softc *)opaque;
    285 
    286 	DPRINTF(("viapcib_i2c_acquire_bus(%p, 0x%x)\n", opaque, flags));
    287 	mutex_enter(&sc->sc_lock);
    288 
    289 	return 0;
    290 }
    291 
    292 static void
    293 viapcib_release_bus(void *opaque, int flags)
    294 {
    295 	struct viapcib_softc *sc = (struct viapcib_softc *)opaque;
    296 
    297 	mutex_exit(&sc->sc_lock);
    298 	DPRINTF(("viapcib_i2c_release_bus(%p, 0x%x)\n", opaque, flags));
    299 }
    300 
    301 static int
    302 viapcib_exec(void *opaque, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
    303     size_t cmdlen, void *vbuf, size_t buflen, int flags)
    304 {
    305 	struct viapcib_softc *sc;
    306 	uint8_t cmd;
    307 	int rv = -1;
    308 
    309 	DPRINTF(("viapcib_exec(%p, 0x%x, 0x%x, %p, %d, %p, %d, 0x%x)\n",
    310 	    opaque, op, addr, vcmd, cmdlen, vbuf, buflen, flags));
    311 
    312 	sc = (struct viapcib_softc *)opaque;
    313 
    314 	if (op != I2C_OP_READ_WITH_STOP &&
    315 	    op != I2C_OP_WRITE_WITH_STOP)
    316 		return -1;
    317 
    318 	if (cmdlen > 0)
    319 		cmd = *(uint8_t *)(__UNCONST(vcmd)); /* XXX */
    320 
    321 	switch (cmdlen) {
    322 	case 0:
    323 		switch (buflen) {
    324 		case 0:
    325 			/* SMBus quick read/write */
    326 			if (I2C_OP_READ_P(op))
    327 				rv = viapcib_smbus_quick_read(sc, addr);
    328 			else
    329 				rv = viapcib_smbus_quick_write(sc, addr);
    330 
    331 			return rv;
    332 		case 1:
    333 			/* SMBus send/receive byte */
    334 			if (I2C_OP_READ_P(op))
    335 				rv = viapcib_smbus_send_byte(sc, addr,
    336 				    *(uint8_t *)vbuf);
    337 			else
    338 				rv = viapcib_smbus_receive_byte(sc, addr,
    339 				    (uint8_t *)vbuf);
    340 			return rv;
    341 		default:
    342 			return -1;
    343 		}
    344 	case 1:
    345 		switch (buflen) {
    346 		case 0:
    347 			return -1;
    348 		case 1:
    349 			/* SMBus read/write byte */
    350 			if (I2C_OP_READ_P(op))
    351 				rv = viapcib_smbus_read_byte(sc, addr,
    352 				    cmd, (uint8_t *)vbuf);
    353 			else
    354 				rv = viapcib_smbus_write_byte(sc, addr,
    355 				    cmd, *(uint8_t *)vbuf);
    356 			return rv;
    357 		case 2:
    358 			/* SMBus read/write word */
    359 			if (I2C_OP_READ_P(op))
    360 				rv = viapcib_smbus_read_word(sc, addr,
    361 				    cmd, (uint16_t *)vbuf);
    362 			else
    363 				rv = viapcib_smbus_write_word(sc, addr,
    364 				    cmd, *(uint16_t *)vbuf);
    365 			return rv;
    366 		default:
    367 			/* SMBus read/write block */
    368 			if (I2C_OP_READ_P(op))
    369 				rv = viapcib_smbus_block_read(sc, addr,
    370 				    cmd, buflen, vbuf);
    371 			else
    372 				rv = viapcib_smbus_block_write(sc, addr,
    373 				    cmd, buflen, vbuf);
    374 			return rv;
    375 		}
    376 	}
    377 
    378 	return -1; /* XXX ??? */
    379 }
    380 
    381 static int
    382 viapcib_smbus_quick_write(void *opaque, i2c_addr_t slave)
    383 {
    384 	struct viapcib_softc *sc;
    385 
    386 	sc = (struct viapcib_softc *)opaque;
    387 
    388 	DPRINTF(("viapcib_smbus_quick_write(%p, 0x%x)\n", opaque, slave));
    389 
    390 	viapcib_clear(sc);
    391 	if (viapcib_busy(sc))
    392 		return EBUSY;
    393 
    394 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    395 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK);
    396 	if (viapcib_wait(sc))
    397 		return EBUSY;
    398 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK | SMBHSTCNT_START);
    399 
    400 	return viapcib_wait(sc);
    401 }
    402 
    403 static int
    404 viapcib_smbus_quick_read(void *opaque, i2c_addr_t slave)
    405 {
    406 	struct viapcib_softc *sc;
    407 
    408 	sc = (struct viapcib_softc *)opaque;
    409 
    410 	DPRINTF(("viapcib_smbus_quick_read(%p, 0x%x)\n", opaque, slave));
    411 
    412 	viapcib_clear(sc);
    413 	if (viapcib_busy(sc))
    414 		return EBUSY;
    415 
    416 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    417 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK);
    418 	if (viapcib_wait(sc))
    419 		return EBUSY;
    420 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK | SMBHSTCNT_START);
    421 
    422 	return viapcib_wait(sc);
    423 }
    424 
    425 static int
    426 viapcib_smbus_send_byte(void *opaque, i2c_addr_t slave, uint8_t byte)
    427 {
    428 	struct viapcib_softc *sc;
    429 
    430 	sc = (struct viapcib_softc *)opaque;
    431 
    432 	DPRINTF(("viapcib_smbus_send_byte(%p, 0x%x, 0x%x)\n", opaque,
    433 	    slave, byte));
    434 
    435 	viapcib_clear(sc);
    436 	if (viapcib_busy(sc))
    437 		return EBUSY;
    438 
    439 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    440 	viapcib_smbus_write(sc, SMBHSTCMD, byte);
    441 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_SENDRECV);
    442 	if (viapcib_wait(sc))
    443 		return EBUSY;
    444 	viapcib_smbus_write(sc, SMBHSTCNT,
    445 	    SMBHSTCNT_START | SMBHSTCNT_SENDRECV);
    446 
    447 	return viapcib_wait(sc);
    448 }
    449 
    450 static int
    451 viapcib_smbus_receive_byte(void *opaque, i2c_addr_t slave, uint8_t *pbyte)
    452 {
    453 	struct viapcib_softc *sc;
    454 	int rv;
    455 
    456 	sc = (struct viapcib_softc *)opaque;
    457 
    458 	DPRINTF(("viapcib_smbus_receive_byte(%p, 0x%x, %p)\n", opaque,
    459 	    slave, pbyte));
    460 
    461 	viapcib_clear(sc);
    462 	if (viapcib_busy(sc))
    463 		return EBUSY;
    464 
    465 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    466 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_SENDRECV);
    467 	if (viapcib_wait(sc))
    468 		return EBUSY;
    469 	viapcib_smbus_write(sc, SMBHSTCNT,
    470 	    SMBHSTCNT_START | SMBHSTCNT_SENDRECV);
    471 
    472 	rv = viapcib_wait(sc);
    473 	if (rv == 0)
    474 		*pbyte = viapcib_smbus_read(sc, SMBHSTDAT0);
    475 
    476 	return rv;
    477 }
    478 
    479 static int
    480 viapcib_smbus_write_byte(void *opaque, i2c_addr_t slave, uint8_t cmd,
    481 		   int8_t byte)
    482 {
    483 	struct viapcib_softc *sc;
    484 
    485 	sc = (struct viapcib_softc *)opaque;
    486 
    487 	DPRINTF(("viapcib_smbus_write_byte(%p, 0x%x, 0x%x, 0x%x)\n", opaque,
    488 	    slave, cmd, byte));
    489 
    490 	viapcib_clear(sc);
    491 	if (viapcib_busy(sc))
    492 		return EBUSY;
    493 
    494 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    495 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    496 	viapcib_smbus_write(sc, SMBHSTDAT0, byte);
    497 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BYTE);
    498 	if (viapcib_wait(sc))
    499 		return EBUSY;
    500 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_BYTE);
    501 
    502 	return viapcib_wait(sc);
    503 }
    504 
    505 static int
    506 viapcib_smbus_read_byte(void *opaque, i2c_addr_t slave, uint8_t cmd,
    507 		  int8_t *pbyte)
    508 {
    509 	struct viapcib_softc *sc;
    510 	int rv;
    511 
    512 	sc = (struct viapcib_softc *)opaque;
    513 
    514 	DPRINTF(("viapcib_smbus_read_byte(%p, 0x%x, 0x%x, %p)\n", opaque,
    515 	    slave, cmd, pbyte));
    516 
    517 	viapcib_clear(sc);
    518 	if (viapcib_busy(sc))
    519 		return EBUSY;
    520 
    521 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    522 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    523 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BYTE);
    524 	if (viapcib_wait(sc))
    525 		return EBUSY;
    526 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_BYTE);
    527 	rv = viapcib_wait(sc);
    528 	if (rv == 0)
    529 		*pbyte = viapcib_smbus_read(sc, SMBHSTDAT0);
    530 
    531 	return rv;
    532 }
    533 
    534 static int
    535 viapcib_smbus_write_word(void *opaque, i2c_addr_t slave, uint8_t cmd,
    536 		   int16_t word)
    537 {
    538 	struct viapcib_softc *sc;
    539 
    540 	sc = (struct viapcib_softc *)opaque;
    541 
    542 	DPRINTF(("viapcib_smbus_write_word(%p, 0x%x, 0x%x, 0x%x)\n", opaque,
    543 	    slave, cmd, word));
    544 
    545 	viapcib_clear(sc);
    546 	if (viapcib_busy(sc))
    547 		return EBUSY;
    548 
    549 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    550 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    551 	viapcib_smbus_write(sc, SMBHSTDAT0, word & 0x00ff);
    552 	viapcib_smbus_write(sc, SMBHSTDAT1, (word & 0xff00) >> 8);
    553 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_WORD);
    554 	if (viapcib_wait(sc))
    555 		return EBUSY;
    556 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_WORD);
    557 
    558 	return viapcib_wait(sc);
    559 }
    560 
    561 static int
    562 viapcib_smbus_read_word(void *opaque, i2c_addr_t slave, uint8_t cmd,
    563 		  int16_t *pword)
    564 {
    565 	struct viapcib_softc *sc;
    566 	int rv;
    567 	int8_t high, low;
    568 
    569 	sc = (struct viapcib_softc *)opaque;
    570 
    571 	DPRINTF(("viapcib_smbus_read_word(%p, 0x%x, 0x%x, %p)\n", opaque,
    572 	    slave, cmd, pword));
    573 
    574 	viapcib_clear(sc);
    575 	if (viapcib_busy(sc))
    576 		return EBUSY;
    577 
    578 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    579 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    580 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_WORD);
    581 	if (viapcib_wait(sc))
    582 		return EBUSY;
    583 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_WORD);
    584 
    585 	rv = viapcib_wait(sc);
    586 	if (rv == 0) {
    587 		low = viapcib_smbus_read(sc, SMBHSTDAT0);
    588 		high = viapcib_smbus_read(sc, SMBHSTDAT1);
    589 		*pword = ((high & 0xff) << 8) | (low & 0xff);
    590 	}
    591 
    592 	return rv;
    593 }
    594 
    595 static int
    596 viapcib_smbus_block_write(void *opaque, i2c_addr_t slave, uint8_t cmd,
    597 		    int cnt, void *data)
    598 {
    599 	struct viapcib_softc *sc;
    600 	int8_t *buf;
    601 	int remain, len, i;
    602 	int rv;
    603 
    604 	sc = (struct viapcib_softc *)opaque;
    605 	buf = (int8_t *)data;
    606 	rv = 0;
    607 
    608 	DPRINTF(("viapcib_smbus_block_write(%p, 0x%x, 0x%x, %d, %p)\n",
    609 	    opaque, slave, cmd, cnt, data));
    610 
    611 	viapcib_clear(sc);
    612 	if (viapcib_busy(sc))
    613 		return EBUSY;
    614 
    615 	remain = cnt;
    616 	while (remain) {
    617 		len = min(remain, SMB_MAXBLOCKSIZE);
    618 
    619 		viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    620 		viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    621 		viapcib_smbus_write(sc, SMBHSTDAT0, len);
    622 		i = viapcib_smbus_read(sc, SMBHSTCNT);
    623 		/* XXX FreeBSD does this, but it looks wrong */
    624 		for (i = 0; i < len; i++) {
    625 			viapcib_smbus_write(sc, SMBBLKDAT,
    626 			    buf[cnt - remain + i]);
    627 		}
    628 		viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BLOCK);
    629 		if (viapcib_wait(sc))
    630 			return EBUSY;
    631 		viapcib_smbus_write(sc, SMBHSTCNT,
    632 		    SMBHSTCNT_START | SMBHSTCNT_BLOCK);
    633 		if (viapcib_wait(sc))
    634 			return EBUSY;
    635 
    636 		remain -= len;
    637 	}
    638 
    639 	return rv;
    640 }
    641 
    642 static int
    643 viapcib_smbus_block_read(void *opaque, i2c_addr_t slave, uint8_t cmd,
    644 			 int cnt, void *data)
    645 {
    646 	struct viapcib_softc *sc;
    647 	int8_t *buf;
    648 	int remain, len, i;
    649 	int rv;
    650 
    651 	sc = (struct viapcib_softc *)opaque;
    652 	buf = (int8_t *)data;
    653 	rv = 0;
    654 
    655 	DPRINTF(("viapcib_smbus_block_read(%p, 0x%x, 0x%x, %d, %p)\n",
    656 	    opaque, slave, cmd, cnt, data));
    657 
    658 	viapcib_clear(sc);
    659 	if (viapcib_busy(sc))
    660 		return EBUSY;
    661 
    662 	remain = cnt;
    663 	while (remain) {
    664 		viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    665 		viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    666 		viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BLOCK);
    667 		if (viapcib_wait(sc))
    668 			return EBUSY;
    669 		viapcib_smbus_write(sc, SMBHSTCNT,
    670 		    SMBHSTCNT_START | SMBHSTCNT_BLOCK);
    671 		if (viapcib_wait(sc))
    672 			return EBUSY;
    673 
    674 		len = viapcib_smbus_read(sc, SMBHSTDAT0);
    675 		i = viapcib_smbus_read(sc, SMBHSTCNT);
    676 
    677 		len = min(len, remain);
    678 
    679 		/* FreeBSD does this too... */
    680 		for (i = 0; i < len; i++) {
    681 			buf[cnt - remain + i] =
    682 			    viapcib_smbus_read(sc, SMBBLKDAT);
    683 		}
    684 
    685 		remain -= len;
    686 	}
    687 
    688 	return rv;
    689 }
    690