Home | History | Annotate | Line # | Download | only in pci
      1 /* $NetBSD: viapcib.c,v 1.20 2025/09/15 13:23:01 thorpej 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.20 2025/09/15 13:23:01 thorpej 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 
     95 static int	viapcib_match(device_t, cfdata_t, void *);
     96 static void	viapcib_attach(device_t, device_t, void *);
     97 
     98 static int	viapcib_clear(struct viapcib_softc *);
     99 static int	viapcib_busy(struct viapcib_softc *);
    100 
    101 #define		viapcib_smbus_read(sc, o) \
    102 	bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (o))
    103 #define		viapcib_smbus_write(sc, o, v) \
    104 	bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (o), (v))
    105 
    106 #define	VIAPCIB_SMBUS_TIMEOUT	10000
    107 
    108 static int	viapcib_exec(void *, i2c_op_t, i2c_addr_t, const void *,
    109 			     size_t, void *, size_t, int);
    110 
    111 /* SMBus operations */
    112 static int      viapcib_smbus_quick_write(void *, i2c_addr_t);
    113 static int      viapcib_smbus_quick_read(void *, i2c_addr_t);
    114 static int      viapcib_smbus_send_byte(void *, i2c_addr_t, uint8_t);
    115 static int      viapcib_smbus_receive_byte(void *, i2c_addr_t,
    116                                            uint8_t *);
    117 static int      viapcib_smbus_read_byte(void *, i2c_addr_t, uint8_t,
    118 					int8_t *);
    119 static int      viapcib_smbus_write_byte(void *, i2c_addr_t, uint8_t,
    120 					 int8_t);
    121 static int      viapcib_smbus_read_word(void *, i2c_addr_t, uint8_t,
    122 					int16_t *);
    123 static int      viapcib_smbus_write_word(void *, i2c_addr_t, uint8_t,
    124 					 int16_t);
    125 static int      viapcib_smbus_block_write(void *, i2c_addr_t, uint8_t,
    126 					  int, void *);
    127 static int      viapcib_smbus_block_read(void *, i2c_addr_t, uint8_t,
    128 					 int, void *);
    129 /* XXX Should be moved to smbus layer */
    130 #define	SMB_MAXBLOCKSIZE	32
    131 
    132 CFATTACH_DECL_NEW(viapcib, sizeof(struct viapcib_softc),
    133     viapcib_match, viapcib_attach, NULL, NULL);
    134 
    135 static int
    136 viapcib_match(device_t parent, cfdata_t match, void *opaque)
    137 {
    138 	struct pci_attach_args *pa = opaque;
    139 
    140 	if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_VIATECH)
    141 		return 0;
    142 
    143 	switch (PCI_PRODUCT(pa->pa_id)) {
    144 	case PCI_PRODUCT_VIATECH_VT8235:
    145 	case PCI_PRODUCT_VIATECH_VT8237:
    146 	case PCI_PRODUCT_VIATECH_VT8237A_ISA:
    147 		return 2; /* match above generic pcib(4) */
    148 	}
    149 
    150 	return 0;
    151 }
    152 
    153 static void
    154 viapcib_attach(device_t parent, device_t self, void *opaque)
    155 {
    156 	struct viapcib_softc *sc = device_private(self);
    157 	struct pci_attach_args *pa = opaque;
    158 	pcireg_t addr, val;
    159 
    160 	/* XXX Only the 8235 is supported for now */
    161 	sc->sc_iot = pa->pa_iot;
    162 	addr = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_BASE3);
    163 	addr &= 0xfff0;
    164 	if (bus_space_map(sc->sc_iot, addr, 8, 0, &sc->sc_ioh)) {
    165 		printf(": failed to map SMBus I/O space\n");
    166 		addr = 0;
    167 		goto core_pcib;
    168 	}
    169 
    170 	val = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_HOST_CONFIG);
    171 	if ((val & 0x10000) == 0) {
    172 		printf(": SMBus is disabled\n");
    173 		addr = 0;
    174 		/* XXX We can enable the SMBus here by writing 1 to
    175 		 * SMB_HOST_CONFIG, but do we want to?
    176 		 */
    177 		goto core_pcib;
    178 	}
    179 
    180 #ifdef VIAPCIB_DEBUG
    181 	switch (val & 0x0e) {
    182 	case 8:
    183 		printf(": interrupting at irq 9\n");
    184 		break;
    185 	case 0:
    186 		printf(": interrupting at SMI#\n");
    187 		break;
    188 	default:
    189 		printf(": interrupt misconfigured\n");
    190 		break;
    191 	}
    192 #endif /* !VIAPCIB_DEBUG */
    193 
    194 	val = pci_conf_read(pa->pa_pc, pa->pa_tag, SMB_REVISION);
    195 	sc->sc_revision = val >> 16;
    196 
    197 core_pcib:
    198 	pcibattach(parent, self, opaque);
    199 
    200 	if (addr != 0) {
    201 		uint8_t b;
    202 
    203 		printf("%s: SMBus found at 0x%x (revision 0x%x)\n",
    204 		    device_xname(self), addr, sc->sc_revision);
    205 
    206 		/* Disable slave function */
    207 		b = viapcib_smbus_read(sc, SMBSLVCNT);
    208 		viapcib_smbus_write(sc, SMBSLVCNT, b & ~1);
    209 
    210 		iic_tag_init(&sc->sc_i2c);
    211 		sc->sc_i2c.ic_cookie = (void *)sc;
    212 		sc->sc_i2c.ic_exec = viapcib_exec;
    213 
    214 		iicbus_attach(self, &sc->sc_i2c);
    215 	}
    216 }
    217 
    218 static int
    219 viapcib_wait(struct viapcib_softc *sc)
    220 {
    221 	int rv, timeout;
    222 	uint8_t val = 0;
    223 
    224 	timeout = VIAPCIB_SMBUS_TIMEOUT;
    225 	rv = 0;
    226 
    227 	while (timeout--) {
    228 		val = viapcib_smbus_read(sc, SMBHSTSTS);
    229 		if (!(val & SMBHSTSTS_BUSY) && (val & SMBHSTSTS_INTR))
    230 			break;
    231 		DELAY(10);
    232 	}
    233 
    234 	if (timeout == 0)
    235 		rv = EBUSY;
    236 
    237 	if ((val & SMBHSTSTS_FAILED) || (val & SMBHSTSTS_COLLISION) ||
    238 	    (val & SMBHSTSTS_ERROR))
    239 		rv = EIO;
    240 
    241 	viapcib_clear(sc);
    242 
    243 	return rv;
    244 }
    245 
    246 static int
    247 viapcib_clear(struct viapcib_softc *sc)
    248 {
    249 	viapcib_smbus_write(sc, SMBHSTSTS,
    250 	    (SMBHSTSTS_FAILED | SMBHSTSTS_COLLISION | SMBHSTSTS_ERROR |
    251 	     SMBHSTSTS_INTR));
    252 	DELAY(10);
    253 
    254 	return 0;
    255 }
    256 
    257 static int
    258 viapcib_busy(struct viapcib_softc *sc)
    259 {
    260 	uint8_t val;
    261 
    262 	val = viapcib_smbus_read(sc, SMBHSTSTS);
    263 
    264 	return (val & SMBHSTSTS_BUSY);
    265 }
    266 
    267 static int
    268 viapcib_exec(void *opaque, i2c_op_t op, i2c_addr_t addr, const void *vcmd,
    269     size_t cmdlen, void *vbuf, size_t buflen, int flags)
    270 {
    271 	struct viapcib_softc *sc;
    272 	uint8_t cmd;
    273 	int rv = -1;
    274 
    275 	DPRINTF(("viapcib_exec(%p, 0x%x, 0x%x, %p, %d, %p, %d, 0x%x)\n",
    276 	    opaque, op, addr, vcmd, cmdlen, vbuf, buflen, flags));
    277 
    278 	sc = (struct viapcib_softc *)opaque;
    279 
    280 	if (op != I2C_OP_READ_WITH_STOP &&
    281 	    op != I2C_OP_WRITE_WITH_STOP)
    282 		return -1;
    283 
    284 	if (cmdlen > 0)
    285 		cmd = *(uint8_t *)(__UNCONST(vcmd)); /* XXX */
    286 
    287 	switch (cmdlen) {
    288 	case 0:
    289 		switch (buflen) {
    290 		case 0:
    291 			/* SMBus quick read/write */
    292 			if (I2C_OP_READ_P(op))
    293 				rv = viapcib_smbus_quick_read(sc, addr);
    294 			else
    295 				rv = viapcib_smbus_quick_write(sc, addr);
    296 
    297 			return rv;
    298 		case 1:
    299 			/* SMBus send/receive byte */
    300 			if (I2C_OP_READ_P(op))
    301 				rv = viapcib_smbus_send_byte(sc, addr,
    302 				    *(uint8_t *)vbuf);
    303 			else
    304 				rv = viapcib_smbus_receive_byte(sc, addr,
    305 				    (uint8_t *)vbuf);
    306 			return rv;
    307 		default:
    308 			return -1;
    309 		}
    310 	case 1:
    311 		switch (buflen) {
    312 		case 0:
    313 			return -1;
    314 		case 1:
    315 			/* SMBus read/write byte */
    316 			if (I2C_OP_READ_P(op))
    317 				rv = viapcib_smbus_read_byte(sc, addr,
    318 				    cmd, (uint8_t *)vbuf);
    319 			else
    320 				rv = viapcib_smbus_write_byte(sc, addr,
    321 				    cmd, *(uint8_t *)vbuf);
    322 			return rv;
    323 		case 2:
    324 			/* SMBus read/write word */
    325 			if (I2C_OP_READ_P(op))
    326 				rv = viapcib_smbus_read_word(sc, addr,
    327 				    cmd, (uint16_t *)vbuf);
    328 			else
    329 				rv = viapcib_smbus_write_word(sc, addr,
    330 				    cmd, *(uint16_t *)vbuf);
    331 			return rv;
    332 		default:
    333 			/* SMBus read/write block */
    334 			if (I2C_OP_READ_P(op))
    335 				rv = viapcib_smbus_block_read(sc, addr,
    336 				    cmd, buflen, vbuf);
    337 			else
    338 				rv = viapcib_smbus_block_write(sc, addr,
    339 				    cmd, buflen, vbuf);
    340 			return rv;
    341 		}
    342 	}
    343 
    344 	return -1; /* XXX ??? */
    345 }
    346 
    347 static int
    348 viapcib_smbus_quick_write(void *opaque, i2c_addr_t slave)
    349 {
    350 	struct viapcib_softc *sc;
    351 
    352 	sc = (struct viapcib_softc *)opaque;
    353 
    354 	DPRINTF(("viapcib_smbus_quick_write(%p, 0x%x)\n", opaque, slave));
    355 
    356 	viapcib_clear(sc);
    357 	if (viapcib_busy(sc))
    358 		return EBUSY;
    359 
    360 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    361 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK);
    362 	if (viapcib_wait(sc))
    363 		return EBUSY;
    364 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK | SMBHSTCNT_START);
    365 
    366 	return viapcib_wait(sc);
    367 }
    368 
    369 static int
    370 viapcib_smbus_quick_read(void *opaque, i2c_addr_t slave)
    371 {
    372 	struct viapcib_softc *sc;
    373 
    374 	sc = (struct viapcib_softc *)opaque;
    375 
    376 	DPRINTF(("viapcib_smbus_quick_read(%p, 0x%x)\n", opaque, slave));
    377 
    378 	viapcib_clear(sc);
    379 	if (viapcib_busy(sc))
    380 		return EBUSY;
    381 
    382 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    383 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK);
    384 	if (viapcib_wait(sc))
    385 		return EBUSY;
    386 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_QUICK | SMBHSTCNT_START);
    387 
    388 	return viapcib_wait(sc);
    389 }
    390 
    391 static int
    392 viapcib_smbus_send_byte(void *opaque, i2c_addr_t slave, uint8_t byte)
    393 {
    394 	struct viapcib_softc *sc;
    395 
    396 	sc = (struct viapcib_softc *)opaque;
    397 
    398 	DPRINTF(("viapcib_smbus_send_byte(%p, 0x%x, 0x%x)\n", opaque,
    399 	    slave, byte));
    400 
    401 	viapcib_clear(sc);
    402 	if (viapcib_busy(sc))
    403 		return EBUSY;
    404 
    405 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    406 	viapcib_smbus_write(sc, SMBHSTCMD, byte);
    407 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_SENDRECV);
    408 	if (viapcib_wait(sc))
    409 		return EBUSY;
    410 	viapcib_smbus_write(sc, SMBHSTCNT,
    411 	    SMBHSTCNT_START | SMBHSTCNT_SENDRECV);
    412 
    413 	return viapcib_wait(sc);
    414 }
    415 
    416 static int
    417 viapcib_smbus_receive_byte(void *opaque, i2c_addr_t slave, uint8_t *pbyte)
    418 {
    419 	struct viapcib_softc *sc;
    420 	int rv;
    421 
    422 	sc = (struct viapcib_softc *)opaque;
    423 
    424 	DPRINTF(("viapcib_smbus_receive_byte(%p, 0x%x, %p)\n", opaque,
    425 	    slave, pbyte));
    426 
    427 	viapcib_clear(sc);
    428 	if (viapcib_busy(sc))
    429 		return EBUSY;
    430 
    431 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    432 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_SENDRECV);
    433 	if (viapcib_wait(sc))
    434 		return EBUSY;
    435 	viapcib_smbus_write(sc, SMBHSTCNT,
    436 	    SMBHSTCNT_START | SMBHSTCNT_SENDRECV);
    437 
    438 	rv = viapcib_wait(sc);
    439 	if (rv == 0)
    440 		*pbyte = viapcib_smbus_read(sc, SMBHSTDAT0);
    441 
    442 	return rv;
    443 }
    444 
    445 static int
    446 viapcib_smbus_write_byte(void *opaque, i2c_addr_t slave, uint8_t cmd,
    447 		   int8_t byte)
    448 {
    449 	struct viapcib_softc *sc;
    450 
    451 	sc = (struct viapcib_softc *)opaque;
    452 
    453 	DPRINTF(("viapcib_smbus_write_byte(%p, 0x%x, 0x%x, 0x%x)\n", opaque,
    454 	    slave, cmd, byte));
    455 
    456 	viapcib_clear(sc);
    457 	if (viapcib_busy(sc))
    458 		return EBUSY;
    459 
    460 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    461 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    462 	viapcib_smbus_write(sc, SMBHSTDAT0, byte);
    463 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BYTE);
    464 	if (viapcib_wait(sc))
    465 		return EBUSY;
    466 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_BYTE);
    467 
    468 	return viapcib_wait(sc);
    469 }
    470 
    471 static int
    472 viapcib_smbus_read_byte(void *opaque, i2c_addr_t slave, uint8_t cmd,
    473 		  int8_t *pbyte)
    474 {
    475 	struct viapcib_softc *sc;
    476 	int rv;
    477 
    478 	sc = (struct viapcib_softc *)opaque;
    479 
    480 	DPRINTF(("viapcib_smbus_read_byte(%p, 0x%x, 0x%x, %p)\n", opaque,
    481 	    slave, cmd, pbyte));
    482 
    483 	viapcib_clear(sc);
    484 	if (viapcib_busy(sc))
    485 		return EBUSY;
    486 
    487 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    488 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    489 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BYTE);
    490 	if (viapcib_wait(sc))
    491 		return EBUSY;
    492 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_BYTE);
    493 	rv = viapcib_wait(sc);
    494 	if (rv == 0)
    495 		*pbyte = viapcib_smbus_read(sc, SMBHSTDAT0);
    496 
    497 	return rv;
    498 }
    499 
    500 static int
    501 viapcib_smbus_write_word(void *opaque, i2c_addr_t slave, uint8_t cmd,
    502 		   int16_t word)
    503 {
    504 	struct viapcib_softc *sc;
    505 
    506 	sc = (struct viapcib_softc *)opaque;
    507 
    508 	DPRINTF(("viapcib_smbus_write_word(%p, 0x%x, 0x%x, 0x%x)\n", opaque,
    509 	    slave, cmd, word));
    510 
    511 	viapcib_clear(sc);
    512 	if (viapcib_busy(sc))
    513 		return EBUSY;
    514 
    515 	viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    516 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    517 	viapcib_smbus_write(sc, SMBHSTDAT0, word & 0x00ff);
    518 	viapcib_smbus_write(sc, SMBHSTDAT1, (word & 0xff00) >> 8);
    519 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_WORD);
    520 	if (viapcib_wait(sc))
    521 		return EBUSY;
    522 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_WORD);
    523 
    524 	return viapcib_wait(sc);
    525 }
    526 
    527 static int
    528 viapcib_smbus_read_word(void *opaque, i2c_addr_t slave, uint8_t cmd,
    529 		  int16_t *pword)
    530 {
    531 	struct viapcib_softc *sc;
    532 	int rv;
    533 	int8_t high, low;
    534 
    535 	sc = (struct viapcib_softc *)opaque;
    536 
    537 	DPRINTF(("viapcib_smbus_read_word(%p, 0x%x, 0x%x, %p)\n", opaque,
    538 	    slave, cmd, pword));
    539 
    540 	viapcib_clear(sc);
    541 	if (viapcib_busy(sc))
    542 		return EBUSY;
    543 
    544 	viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    545 	viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    546 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_WORD);
    547 	if (viapcib_wait(sc))
    548 		return EBUSY;
    549 	viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_START | SMBHSTCNT_WORD);
    550 
    551 	rv = viapcib_wait(sc);
    552 	if (rv == 0) {
    553 		low = viapcib_smbus_read(sc, SMBHSTDAT0);
    554 		high = viapcib_smbus_read(sc, SMBHSTDAT1);
    555 		*pword = ((high & 0xff) << 8) | (low & 0xff);
    556 	}
    557 
    558 	return rv;
    559 }
    560 
    561 static int
    562 viapcib_smbus_block_write(void *opaque, i2c_addr_t slave, uint8_t cmd,
    563 		    int cnt, void *data)
    564 {
    565 	struct viapcib_softc *sc;
    566 	int8_t *buf;
    567 	int remain, len, i;
    568 	int rv;
    569 
    570 	sc = (struct viapcib_softc *)opaque;
    571 	buf = (int8_t *)data;
    572 	rv = 0;
    573 
    574 	DPRINTF(("viapcib_smbus_block_write(%p, 0x%x, 0x%x, %d, %p)\n",
    575 	    opaque, slave, cmd, cnt, data));
    576 
    577 	viapcib_clear(sc);
    578 	if (viapcib_busy(sc))
    579 		return EBUSY;
    580 
    581 	remain = cnt;
    582 	while (remain) {
    583 		len = uimin(remain, SMB_MAXBLOCKSIZE);
    584 
    585 		viapcib_smbus_write(sc, SMBHSTADD, (slave & 0x7f) << 1);
    586 		viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    587 		viapcib_smbus_write(sc, SMBHSTDAT0, len);
    588 		i = viapcib_smbus_read(sc, SMBHSTCNT);
    589 		/* XXX FreeBSD does this, but it looks wrong */
    590 		for (i = 0; i < len; i++) {
    591 			viapcib_smbus_write(sc, SMBBLKDAT,
    592 			    buf[cnt - remain + i]);
    593 		}
    594 		viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BLOCK);
    595 		if (viapcib_wait(sc))
    596 			return EBUSY;
    597 		viapcib_smbus_write(sc, SMBHSTCNT,
    598 		    SMBHSTCNT_START | SMBHSTCNT_BLOCK);
    599 		if (viapcib_wait(sc))
    600 			return EBUSY;
    601 
    602 		remain -= len;
    603 	}
    604 
    605 	return rv;
    606 }
    607 
    608 static int
    609 viapcib_smbus_block_read(void *opaque, i2c_addr_t slave, uint8_t cmd,
    610 			 int cnt, void *data)
    611 {
    612 	struct viapcib_softc *sc;
    613 	int8_t *buf;
    614 	int remain, len, i;
    615 	int rv;
    616 
    617 	sc = (struct viapcib_softc *)opaque;
    618 	buf = (int8_t *)data;
    619 	rv = 0;
    620 
    621 	DPRINTF(("viapcib_smbus_block_read(%p, 0x%x, 0x%x, %d, %p)\n",
    622 	    opaque, slave, cmd, cnt, data));
    623 
    624 	viapcib_clear(sc);
    625 	if (viapcib_busy(sc))
    626 		return EBUSY;
    627 
    628 	remain = cnt;
    629 	while (remain) {
    630 		viapcib_smbus_write(sc, SMBHSTADD, ((slave & 0x7f) << 1) | 1);
    631 		viapcib_smbus_write(sc, SMBHSTCMD, cmd);
    632 		viapcib_smbus_write(sc, SMBHSTCNT, SMBHSTCNT_BLOCK);
    633 		if (viapcib_wait(sc))
    634 			return EBUSY;
    635 		viapcib_smbus_write(sc, SMBHSTCNT,
    636 		    SMBHSTCNT_START | SMBHSTCNT_BLOCK);
    637 		if (viapcib_wait(sc))
    638 			return EBUSY;
    639 
    640 		len = viapcib_smbus_read(sc, SMBHSTDAT0);
    641 		i = viapcib_smbus_read(sc, SMBHSTCNT);
    642 
    643 		len = uimin(len, remain);
    644 
    645 		/* FreeBSD does this too... */
    646 		for (i = 0; i < len; i++) {
    647 			buf[cnt - remain + i] =
    648 			    viapcib_smbus_read(sc, SMBBLKDAT);
    649 		}
    650 
    651 		remain -= len;
    652 	}
    653 
    654 	return rv;
    655 }
    656