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