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