xref: /netbsd-src/sys/arch/i386/pci/viapcib.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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@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