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