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