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