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