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