Merge from vendor branch OPENSSH:
[dragonfly.git] / sys / dev / powermng / i386 / viapm / viapm.c
1 /*-
2  * Copyright (c) 2001 Alcove - Nicolas Souchu
3  * Copyright (c) 2002 Nicolas Souchu
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/sys/pci/viapm.c,v 1.1.2.1 2002/04/19 05:52:15 nsouch Exp $
28  * $DragonFly: src/sys/dev/powermng/i386/viapm/viapm.c,v 1.7 2005/06/03 23:10:23 joerg Exp $
29  *
30  */
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 #include <sys/uio.h>
37
38 #include <machine/bus_pio.h>
39 #include <machine/bus_memio.h>
40 #include <machine/bus.h>
41 #include <machine/clock.h>              /* for DELAY */
42 #include <machine/resource.h>
43 #include <sys/rman.h>
44
45 #include <bus/pci/pcivar.h>
46 #include <bus/pci/pcireg.h>
47
48 #include <bus/iicbus/iiconf.h>
49 #include <bus/iicbus/iicbus.h>
50
51 #include <bus/smbus/smbconf.h>
52 #include <bus/smbus/smbus.h>
53
54 #include "iicbb_if.h"
55 #include "smbus_if.h"
56
57 #define VIAPM_DEBUG(x)  if (viapm_debug) (x)
58
59 #ifdef DEBUG
60 static int viapm_debug = 1;
61 #else
62 static int viapm_debug = 0;
63 #endif
64
65 #define VIA_586B_PMU_ID         0x30401106
66 #define VIA_596A_PMU_ID         0x30501106
67 #define VIA_596B_PMU_ID         0x30511106
68 #define VIA_686A_PMU_ID         0x30571106
69 #define VIA_8233_PMU_ID         0x30741106
70
71 #define VIAPM_INB(port) \
72         ((u_char)bus_space_read_1(viapm->st, viapm->sh, port))
73 #define VIAPM_OUTB(port,val) \
74         (bus_space_write_1(viapm->st, viapm->sh, port, (u_char)val))
75
76 #define VIAPM_TYP_UNKNOWN       0
77 #define VIAPM_TYP_586B_3040E    1
78 #define VIAPM_TYP_586B_3040F    2
79 #define VIAPM_TYP_596B          3
80 #define VIAPM_TYP_686A          4
81 #define VIAPM_TYP_8233          5
82
83 struct viapm_softc {
84         int type;
85         u_int32_t base;
86         bus_space_tag_t st;
87         bus_space_handle_t sh;
88         int iorid;
89         int irqrid;
90         struct resource *iores;
91         struct resource *irqres;
92         void *irqih;
93
94         device_t iicbb;
95         device_t smbus;
96 };
97
98 static devclass_t viapm_devclass;
99 static devclass_t viapropm_devclass;
100
101 /*
102  * VT82C586B definitions
103  */
104
105 #define VIAPM_586B_REVID        0x08
106
107 #define VIAPM_586B_3040E_BASE   0x20
108 #define VIAPM_586B_3040E_ACTIV  0x4             /* 16 bits */
109
110 #define VIAPM_586B_3040F_BASE   0x48
111 #define VIAPM_586B_3040F_ACTIV  0x41            /* 8 bits */
112
113 #define VIAPM_586B_OEM_REV_E    0x00
114 #define VIAPM_586B_OEM_REV_F    0x01
115 #define VIAPM_586B_PROD_REV_A   0x10
116
117 #define VIAPM_586B_BA_MASK      0x0000ff00
118
119 #define GPIO_DIR        0x40
120 #define GPIO_VAL        0x42
121 #define EXTSMI_VAL      0x44
122
123 #define VIAPM_SCL       0x02                    /* GPIO1_VAL */
124 #define VIAPM_SDA       0x04                    /* GPIO2_VAL */
125
126 /*
127  * VIAPRO common definitions
128  */
129
130 #define VIAPM_PRO_BA_MASK       0x0000fff0
131 #define VIAPM_PRO_SMBCTRL       0xd2
132 #define VIAPM_PRO_REVID         0xd6
133
134 /*
135  * VT82C686A definitions
136  */
137
138 #define VIAPM_PRO_BASE          0x90
139
140 #define SMBHST                  0x0
141 #define SMBHSL                  0x1
142 #define SMBHCTRL                0x2
143 #define SMBHCMD                 0x3
144 #define SMBHADDR                0x4
145 #define SMBHDATA0               0x5
146 #define SMBHDATA1               0x6
147 #define SMBHBLOCK               0x7
148
149 #define SMBSST                  0x1
150 #define SMBSCTRL                0x8
151 #define SMBSSDWCMD              0x9
152 #define SMBSEVENT               0xa
153 #define SMBSDATA                0xc
154
155 #define SMBHST_RESERVED         0xef    /* reserved bits */
156 #define SMBHST_FAILED           0x10    /* failed bus transaction */
157 #define SMBHST_COLLID           0x08    /* bus collision */
158 #define SMBHST_ERROR            0x04    /* device error */
159 #define SMBHST_INTR             0x02    /* command completed */
160 #define SMBHST_BUSY             0x01    /* host busy */
161
162 #define SMBHCTRL_START          0x40    /* start command */
163 #define SMBHCTRL_PROTO          0x1c    /* command protocol mask */
164 #define SMBHCTRL_QUICK          0x00
165 #define SMBHCTRL_SENDRECV       0x04
166 #define SMBHCTRL_BYTE           0x08
167 #define SMBHCTRL_WORD           0x0c
168 #define SMBHCTRL_BLOCK          0x14
169 #define SMBHCTRL_KILL           0x02    /* stop the current transaction */
170 #define SMBHCTRL_ENABLE         0x01    /* enable interrupts */
171
172 #define SMBSCTRL_ENABLE         0x01    /* enable slave */
173
174
175 /*
176  * VIA8233 definitions
177  */
178
179 #define VIAPM_8233_BASE         0xD0
180
181 static int
182 viapm_586b_probe(device_t dev)
183 {
184         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
185         u_int32_t l;
186         u_int16_t s;
187         u_int8_t c;
188
189         switch (pci_get_devid(dev)) {
190         case VIA_586B_PMU_ID:
191
192                 bzero(viapm, sizeof(struct viapm_softc));
193
194                 l = pci_read_config(dev, VIAPM_586B_REVID, 1);
195                 switch (l) {
196                 case VIAPM_586B_OEM_REV_E:
197                         viapm->type = VIAPM_TYP_586B_3040E;
198                         viapm->iorid = VIAPM_586B_3040E_BASE;
199
200                         /* Activate IO block access */
201                         s = pci_read_config(dev, VIAPM_586B_3040E_ACTIV, 2);
202                         pci_write_config(dev, VIAPM_586B_3040E_ACTIV, s | 0x1, 2);
203                         break;
204
205                 case VIAPM_586B_OEM_REV_F:
206                 case VIAPM_586B_PROD_REV_A:
207                 default:
208                         viapm->type = VIAPM_TYP_586B_3040F;
209                         viapm->iorid = VIAPM_586B_3040F_BASE;
210
211                         /* Activate IO block access */
212                         c = pci_read_config(dev, VIAPM_586B_3040F_ACTIV, 1);
213                         pci_write_config(dev, VIAPM_586B_3040F_ACTIV, c | 0x80, 1);
214                         break;
215                 }
216
217                 viapm->base = pci_read_config(dev, viapm->iorid, 4) &
218                                 VIAPM_586B_BA_MASK;
219
220                 /*
221                  * We have to set the I/O resources by hand because it is
222                  * described outside the viapmope of the traditional maps
223                  */
224                 if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
225                                                         viapm->base, 256)) {
226                         device_printf(dev, "could not set bus resource\n");
227                         return ENXIO;
228                 }
229                 device_set_desc(dev, "VIA VT82C586B Power Management Unit");
230                 return 0;
231
232         default:
233                 break;
234         }
235
236         return ENXIO;
237 }
238
239
240 static int
241 viapm_pro_probe(device_t dev)
242 {
243         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
244 #ifdef VIAPM_BASE_ADDR
245         u_int32_t l;
246 #endif
247         u_int32_t base_cfgreg;
248         char *desc;
249
250         switch (pci_get_devid(dev)) {
251         case VIA_596A_PMU_ID:
252                 desc = "VIA VT82C596A Power Management Unit";
253                 viapm->type = VIAPM_TYP_596B;
254                 base_cfgreg = VIAPM_PRO_BASE;
255                 goto viapro;
256
257         case VIA_596B_PMU_ID:
258                 desc = "VIA VT82C596B Power Management Unit";
259                 viapm->type = VIAPM_TYP_596B;
260                 base_cfgreg = VIAPM_PRO_BASE;
261                 goto viapro;
262
263         case VIA_686A_PMU_ID:
264                 desc = "VIA VT82C686A Power Management Unit";
265                 viapm->type = VIAPM_TYP_686A;
266                 base_cfgreg = VIAPM_PRO_BASE;
267                 goto viapro;
268
269         case VIA_8233_PMU_ID:
270                 desc = "VIA VT8233 Power Management Unit";
271                 viapm->type = VIAPM_TYP_UNKNOWN;
272                 base_cfgreg = VIAPM_8233_BASE;
273                 goto viapro;
274
275         viapro:
276
277 #ifdef VIAPM_BASE_ADDR
278                 /* force VIAPM I/O base address */
279
280                 /* enable the SMBus controller function */
281                 l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
282                 pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
283
284                 /* write the base address */
285                 pci_write_config(dev, base_cfgreg,
286                                  VIAPM_BASE_ADDR & VIAPM_PRO_BA_MASK, 4);
287 #endif
288
289                 viapm->base = pci_read_config(dev, base_cfgreg, 4) & VIAPM_PRO_BA_MASK;
290
291                 /*
292                  * We have to set the I/O resources by hand because it is
293                  * described outside the viapmope of the traditional maps
294                  */
295                 viapm->iorid = base_cfgreg;
296                 if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
297                                      viapm->base, 16)) {
298                         device_printf(dev, "could not set bus resource 0x%x\n",
299                                         viapm->base);
300                         return ENXIO;
301                 }
302
303                 if (1 || bootverbose) {
304                         device_printf(dev, "SMBus I/O base at 0x%x\n", viapm->base);
305                 }
306
307                 device_set_desc(dev, desc);
308                 return 0;
309
310         default:
311                 break;
312         }
313
314         return ENXIO;
315 }
316
317 static int
318 viapm_pro_attach(device_t dev)
319 {
320         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
321         u_int32_t l;
322 #ifdef  notyet
323         int error;
324 #endif
325
326         if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT,
327                 &viapm->iorid, 0l, ~0l, 1, RF_ACTIVE))) {
328                 device_printf(dev, "could not allocate bus space\n");
329                 goto fail;
330         }
331         viapm->st = rman_get_bustag(viapm->iores);
332         viapm->sh = rman_get_bushandle(viapm->iores);
333
334 #ifdef notyet
335         /* force irq 9 */
336         l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
337         pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 0x80, 1);
338
339         viapm->irqrid = 0;
340         if (!(viapm->irqres = bus_alloc_resource(dev, SYS_RES_IRQ,
341                                 &viapm->irqrid, 9, 9, 1,
342                                 RF_SHAREABLE | RF_ACTIVE))) {
343                 device_printf(dev, "could not allocate irq\n");
344                 goto fail;
345         }
346
347         error = bus_setup_intr(dev, viapm->irqres, INTR_TYPE_MISC,
348                                (driver_intr_t *) viasmb_intr, viapm, 
349                                &viapm->irqih, NULL);
350         if (error) {
351                 device_printf(dev, "could not setup irq\n");
352                 goto fail;
353         }
354 #endif
355
356         if (1 | bootverbose) {
357                 l = pci_read_config(dev, VIAPM_PRO_REVID, 1);
358                 device_printf(dev, "SMBus revision code 0x%x\n", l);
359         }
360
361         viapm->smbus = device_add_child(dev, "smbus", -1);
362
363         /* probe and attach the smbus */
364         bus_generic_attach(dev);
365
366         /* disable slave function */
367         VIAPM_OUTB(SMBSCTRL, VIAPM_INB(SMBSCTRL) & ~SMBSCTRL_ENABLE);
368
369         /* enable the SMBus controller function */
370         l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
371         pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
372
373 #ifdef notyet
374         /* enable interrupts */
375         VIAPM_OUTB(SMBHCTRL, VIAPM_INB(SMBHCTRL) | SMBHCTRL_ENABLE);
376 #endif
377
378         return 0;
379
380 fail:
381         if (viapm->iores)
382                 bus_release_resource(dev, SYS_RES_IOPORT, viapm->iorid, viapm->iores);
383 #ifdef notyet
384         if (viapm->irqres)
385                 bus_release_resource(dev, SYS_RES_IRQ, viapm->irqrid, viapm->irqres);
386 #endif
387
388         return ENXIO;
389 }
390
391 static int
392 viapm_586b_attach(device_t dev)
393 {
394         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
395         
396         if (!(viapm->iores = bus_alloc_resource(dev, SYS_RES_IOPORT,
397                 &viapm->iorid, 0ul, ~0ul, 1, RF_ACTIVE | RF_SHAREABLE))) {
398                 device_printf(dev, "could not allocate bus resource\n");
399                 return ENXIO;
400         }
401         viapm->st = rman_get_bustag(viapm->iores);
402         viapm->sh = rman_get_bushandle(viapm->iores);
403
404         VIAPM_OUTB(GPIO_DIR, VIAPM_INB(GPIO_DIR) | VIAPM_SCL | VIAPM_SDA);
405
406         /* add generic bit-banging code */
407         if (!(viapm->iicbb = device_add_child(dev, "iicbb", -1)))
408                 goto error;
409
410         bus_generic_attach(dev);
411
412         return 0;
413
414 error:
415         if (viapm->iores)
416                 bus_release_resource(dev, SYS_RES_IOPORT,
417                                         viapm->iorid, viapm->iores);
418         return ENXIO;
419 }
420
421 static int
422 viapm_586b_detach(device_t dev)
423 {
424         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
425         int error;
426
427         bus_generic_detach(dev);
428         if (viapm->iicbb) {
429                 device_delete_child(dev, viapm->iicbb);
430         }
431
432         if (viapm->iores && (error = bus_release_resource(dev, SYS_RES_IOPORT,
433                                                 viapm->iorid, viapm->iores)))
434                 return (error);
435
436         return 0;
437 }
438
439 static int
440 viapm_pro_detach(device_t dev)
441 {
442         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
443         int error;
444
445         bus_generic_detach(dev);
446         if (viapm->smbus) {
447                 device_delete_child(dev, viapm->smbus);
448         }
449
450         if ((error = bus_release_resource(dev, SYS_RES_IOPORT,
451                                 viapm->iorid, viapm->iores)))
452                 return (error);
453
454 #ifdef notyet
455         if ((error = bus_release_resource(dev, SYS_RES_IRQ,
456                                         viapm->irqrid, viapm->irqres))
457                 return (error);
458 #endif
459
460         return 0;
461 }
462
463 static int
464 viabb_callback(device_t dev, int index, caddr_t *data)
465 {
466         return 0;
467 }
468
469 static void
470 viabb_setscl(device_t dev, int ctrl)
471 {
472         struct viapm_softc *viapm = device_get_softc(dev);
473         u_char val;
474
475         val = VIAPM_INB(GPIO_VAL);
476
477         if (ctrl)
478                 val |= VIAPM_SCL;
479         else
480                 val &= ~VIAPM_SCL;
481
482         VIAPM_OUTB(GPIO_VAL, val);
483
484         return;
485 }
486
487 static void
488 viabb_setsda(device_t dev, int data)
489 {
490         struct viapm_softc *viapm = device_get_softc(dev);
491         u_char val;
492
493         val = VIAPM_INB(GPIO_VAL);
494
495         if (data)
496                 val |= VIAPM_SDA;
497         else
498                 val &= ~VIAPM_SDA;
499
500         VIAPM_OUTB(GPIO_VAL, val);
501
502         return;
503 }
504         
505 static int
506 viabb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
507 {
508         /* reset bus */
509         viabb_setsda(dev, 1);
510         viabb_setscl(dev, 1);
511
512         return (IIC_ENOADDR);
513 }
514
515 #if 0
516 static int
517 viabb_getscl(device_t dev)
518 {
519         struct viapm_softc *viapm = device_get_softc(dev);
520
521         return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SCL) != 0);
522 }
523 #endif
524
525 static int
526 viabb_getsda(device_t dev)
527 {
528         struct viapm_softc *viapm = device_get_softc(dev);
529
530         return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SDA) != 0);
531 }
532
533 static void
534 viabb_setlines(device_t dev, int ctrl, int data)
535 {
536         viabb_setscl(dev, ctrl);
537         viabb_setsda(dev, data);
538
539         return;
540 }
541
542 static int
543 viapm_abort(struct viapm_softc *viapm)
544 {
545         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL);
546         DELAY(10);
547
548         return (0);
549 }
550
551 static int
552 viapm_clear(struct viapm_softc *viapm)
553 {
554         VIAPM_OUTB(SMBHST, SMBHST_FAILED | SMBHST_COLLID |
555                 SMBHST_ERROR | SMBHST_INTR);
556         DELAY(10);
557
558         return (0);
559 }
560
561 static int
562 viapm_busy(struct viapm_softc *viapm)
563 {
564         u_char sts;
565
566         sts = VIAPM_INB(SMBHST);
567
568         VIAPM_DEBUG(printf("viapm: idle? STS=0x%x\n", sts));
569
570         return (sts & SMBHST_BUSY);
571 }
572
573 /*
574  * Poll the SMBus controller
575  */
576 static int
577 viapm_wait(struct viapm_softc *viapm)
578 {
579         int count = 10000;
580         u_char sts = 0;
581         int error;
582
583         /* wait for command to complete and SMBus controller is idle */
584         while(count--) {
585                 DELAY(10);
586                 sts = VIAPM_INB(SMBHST);
587
588                 /* check if the controller is processing a command */
589                 if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
590                         break;
591         }
592
593         VIAPM_DEBUG(printf("viapm: SMBHST=0x%x\n", sts));
594
595         error = SMB_ENOERR;
596
597         if (!count)
598                 error |= SMB_ETIMEOUT;
599
600         if (sts & SMBHST_FAILED)
601                 error |= SMB_EABORT;
602
603         if (sts & SMBHST_COLLID)
604                 error |= SMB_ENOACK;
605
606         if (sts & SMBHST_ERROR)
607                 error |= SMB_EBUSERR;
608
609         if (error != SMB_ENOERR)
610                 viapm_abort(viapm);
611
612         viapm_clear(viapm);
613
614         return (error);
615 }
616
617 static int
618 viasmb_callback(device_t dev, int index, caddr_t *data)
619 {
620         int error = 0;
621
622         switch (index) {
623         case SMB_REQUEST_BUS:
624         case SMB_RELEASE_BUS:
625                 /* ok, bus allocation accepted */
626                 break;
627         default:
628                 error = EINVAL;
629         }
630
631         return (error);
632 }
633
634 static int
635 viasmb_quick(device_t dev, u_char slave, int how)
636 {
637         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
638         int error;
639
640         viapm_clear(viapm);
641         if (viapm_busy(viapm))
642                 return (EBUSY);
643
644         switch (how) {
645         case SMB_QWRITE:
646                 VIAPM_DEBUG(printf("viapm: QWRITE to 0x%x", slave));
647                 VIAPM_OUTB(SMBHADDR, slave & ~LSB);
648                 break;
649         case SMB_QREAD:
650                 VIAPM_DEBUG(printf("viapm: QREAD to 0x%x", slave));
651                 VIAPM_OUTB(SMBHADDR, slave | LSB);
652                 break;
653         default:
654                 panic("%s: unknown QUICK command (%x)!", __func__, how);
655         }
656
657         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_QUICK);
658
659         error = viapm_wait(viapm);
660
661         return (error);
662 }
663
664 static int
665 viasmb_sendb(device_t dev, u_char slave, char byte)
666 {
667         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
668         int error;
669
670         viapm_clear(viapm);
671         if (viapm_busy(viapm))
672                 return (EBUSY);
673
674         VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
675         VIAPM_OUTB(SMBHCMD, byte);
676
677         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
678
679         error = viapm_wait(viapm);
680
681         VIAPM_DEBUG(printf("viapm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
682
683         return (error);
684 }
685
686 static int
687 viasmb_recvb(device_t dev, u_char slave, char *byte)
688 {
689         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
690         int error;
691
692         viapm_clear(viapm);
693         if (viapm_busy(viapm))
694                 return (EBUSY);
695
696         VIAPM_OUTB(SMBHADDR, slave | LSB);
697
698         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
699
700         if ((error = viapm_wait(viapm)) == SMB_ENOERR)
701                 *byte = VIAPM_INB(SMBHDATA0);
702
703         VIAPM_DEBUG(printf("viapm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
704
705         return (error);
706 }
707
708 static int
709 viasmb_writeb(device_t dev, u_char slave, char cmd, char byte)
710 {
711         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
712         int error;
713
714         viapm_clear(viapm);
715         if (viapm_busy(viapm))
716                 return (EBUSY);
717
718         VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
719         VIAPM_OUTB(SMBHCMD, cmd);
720         VIAPM_OUTB(SMBHDATA0, byte);
721
722         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
723
724         error = viapm_wait(viapm);
725
726         VIAPM_DEBUG(printf("viapm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
727
728         return (error);
729 }
730
731 static int
732 viasmb_readb(device_t dev, u_char slave, char cmd, char *byte)
733 {
734         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
735         int error;
736
737         viapm_clear(viapm);
738         if (viapm_busy(viapm))
739                 return (EBUSY);
740
741         VIAPM_OUTB(SMBHADDR, slave | LSB);
742         VIAPM_OUTB(SMBHCMD, cmd);
743
744         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
745
746         if ((error = viapm_wait(viapm)) == SMB_ENOERR)
747                 *byte = VIAPM_INB(SMBHDATA0);
748
749         VIAPM_DEBUG(printf("viapm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
750
751         return (error);
752 }
753
754 static int
755 viasmb_writew(device_t dev, u_char slave, char cmd, short word)
756 {
757         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
758         int error;
759
760         viapm_clear(viapm);
761         if (viapm_busy(viapm))
762                 return (EBUSY);
763
764         VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
765         VIAPM_OUTB(SMBHCMD, cmd);
766         VIAPM_OUTB(SMBHDATA0, word & 0x00ff);
767         VIAPM_OUTB(SMBHDATA1, (word & 0xff00) >> 8);
768
769         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
770
771         error = viapm_wait(viapm);
772
773         VIAPM_DEBUG(printf("viapm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
774
775         return (error);
776 }
777
778 static int
779 viasmb_readw(device_t dev, u_char slave, char cmd, short *word)
780 {
781         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
782         int error;
783         u_char high, low;
784
785         viapm_clear(viapm);
786         if (viapm_busy(viapm))
787                 return (EBUSY);
788
789         VIAPM_OUTB(SMBHADDR, slave | LSB);
790         VIAPM_OUTB(SMBHCMD, cmd);
791
792         VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
793
794         if ((error = viapm_wait(viapm)) == SMB_ENOERR) {
795                 low = VIAPM_INB(SMBHDATA0);
796                 high = VIAPM_INB(SMBHDATA1);
797
798                 *word = ((high & 0xff) << 8) | (low & 0xff);
799         }
800
801         VIAPM_DEBUG(printf("viapm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
802
803         return (error);
804 }
805
806 static int
807 viasmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
808 {
809         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
810         u_char remain, len, i;
811         int error = SMB_ENOERR;
812
813         viapm_clear(viapm);
814         if (viapm_busy(viapm))
815                 return (EBUSY);
816
817         remain = count;
818         while (remain) {
819                 len = min(remain, 32);
820
821                 VIAPM_OUTB(SMBHADDR, slave & ~LSB);
822                 VIAPM_OUTB(SMBHCMD, cmd);
823                 VIAPM_OUTB(SMBHDATA0, len);
824                 i = VIAPM_INB(SMBHCTRL);
825
826                 /* fill the 32-byte internal buffer */
827                 for (i=0; i<len; i++) {
828                         VIAPM_OUTB(SMBHBLOCK, buf[count-remain+i]);
829                         DELAY(2);
830                 }
831                 VIAPM_OUTB(SMBHCMD, cmd);
832                 VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
833
834                 if ((error = viapm_wait(viapm)) != SMB_ENOERR)
835                         goto error;
836
837                 remain -= len;
838         }
839
840 error:
841         VIAPM_DEBUG(printf("viapm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
842
843         return (error);
844
845 }
846
847 static int
848 viasmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
849 {
850         struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
851         u_char remain, len, i;
852         int error = SMB_ENOERR;
853
854         viapm_clear(viapm);
855         if (viapm_busy(viapm))
856                 return (EBUSY);
857
858         remain = count;
859         while (remain) {
860                 VIAPM_OUTB(SMBHADDR, slave | LSB);
861                 VIAPM_OUTB(SMBHCMD, cmd);
862                 VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
863
864                 if ((error = viapm_wait(viapm)) != SMB_ENOERR)
865                         goto error;
866
867                 len = VIAPM_INB(SMBHDATA0);
868                 i = VIAPM_INB(SMBHCTRL);                /* reset counter */
869
870                 len = min(len, remain);
871
872                 /* read the 32-byte internal buffer */
873                 for (i=0; i<len; i++) {
874                         buf[count-remain+i] = VIAPM_INB(SMBHBLOCK);
875                         DELAY(2);
876                 }
877
878                 remain -= len;
879         }
880 error:
881         VIAPM_DEBUG(printf("viapm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
882
883         return (error);
884 }
885
886 static device_method_t viapm_methods[] = {
887         /* device interface */
888         DEVMETHOD(device_probe,         viapm_586b_probe),
889         DEVMETHOD(device_attach,        viapm_586b_attach),
890         DEVMETHOD(device_detach,        viapm_586b_detach),
891
892         /* iicbb interface */
893         DEVMETHOD(iicbb_callback,       viabb_callback),
894         DEVMETHOD(iicbb_setlines,       viabb_setlines),
895         DEVMETHOD(iicbb_getdataline,    viabb_getsda),
896         DEVMETHOD(iicbb_reset,          viabb_reset),
897
898         { 0, 0 }
899 };
900
901 static driver_t viapm_driver = {
902         "viapm",
903         viapm_methods,
904         sizeof(struct viapm_softc),
905 };
906
907 static device_method_t viapropm_methods[] = {
908         /* device interface */
909         DEVMETHOD(device_probe,         viapm_pro_probe),
910         DEVMETHOD(device_attach,        viapm_pro_attach),
911         DEVMETHOD(device_detach,        viapm_pro_detach),
912
913         /* smbus interface */
914         DEVMETHOD(smbus_callback,       viasmb_callback),
915         DEVMETHOD(smbus_quick,          viasmb_quick),
916         DEVMETHOD(smbus_sendb,          viasmb_sendb),
917         DEVMETHOD(smbus_recvb,          viasmb_recvb),
918         DEVMETHOD(smbus_writeb,         viasmb_writeb),
919         DEVMETHOD(smbus_readb,          viasmb_readb),
920         DEVMETHOD(smbus_writew,         viasmb_writew),
921         DEVMETHOD(smbus_readw,          viasmb_readw),
922         DEVMETHOD(smbus_bwrite,         viasmb_bwrite),
923         DEVMETHOD(smbus_bread,          viasmb_bread),
924         
925         { 0, 0 }
926 };
927
928 static driver_t viapropm_driver = {
929         "viapropm",
930         viapropm_methods,
931         sizeof(struct viapm_softc),
932 };
933
934 DECLARE_DUMMY_MODULE(viapm);
935 DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0);
936 DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0);
937
938 MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
939 MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
940 MODULE_VERSION(viapm, 1);