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