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