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