viapm.c revision 135577
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: head/sys/pci/viapm.c 135577 2004-09-22 17:16:04Z stefanf $");
29
30#include <sys/param.h>
31#include <sys/kernel.h>
32#include <sys/systm.h>
33#include <sys/module.h>
34#include <sys/bus.h>
35#include <sys/uio.h>
36
37#include <machine/bus_pio.h>
38#include <machine/bus_memio.h>
39#include <machine/bus.h>
40#include <machine/clock.h>		/* for DELAY */
41#include <machine/resource.h>
42#include <sys/rman.h>
43
44#include <dev/pci/pcivar.h>
45#include <dev/pci/pcireg.h>
46
47#include <dev/iicbus/iiconf.h>
48#include <dev/iicbus/iicbus.h>
49
50#include <dev/smbus/smbconf.h>
51#include <dev/smbus/smbus.h>
52
53#include "iicbb_if.h"
54#include "smbus_if.h"
55
56#define VIAPM_DEBUG(x)	if (viapm_debug) (x)
57
58#ifdef DEBUG
59static int viapm_debug = 1;
60#else
61static int viapm_debug = 0;
62#endif
63
64#define VIA_586B_PMU_ID		0x30401106
65#define VIA_596A_PMU_ID		0x30501106
66#define VIA_596B_PMU_ID		0x30511106
67#define VIA_686A_PMU_ID		0x30571106
68#define VIA_8233_PMU_ID		0x30741106
69#define	VIA_8233A_PMU_ID	0x31471106
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
83struct 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
98static devclass_t viapm_devclass;
99static 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
181static int
182viapm_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
240static int
241viapm_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	case VIA_8233A_PMU_ID:
271		desc = "VIA VT8233 Power Management Unit";
272		viapm->type = VIAPM_TYP_UNKNOWN;
273		base_cfgreg = VIAPM_8233_BASE;
274		goto viapro;
275
276	viapro:
277
278#ifdef VIAPM_BASE_ADDR
279		/* force VIAPM I/O base address */
280
281		/* enable the SMBus controller function */
282		l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
283		pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
284
285		/* write the base address */
286		pci_write_config(dev, base_cfgreg,
287				 VIAPM_BASE_ADDR & VIAPM_PRO_BA_MASK, 4);
288#endif
289
290		viapm->base = pci_read_config(dev, base_cfgreg, 4) & VIAPM_PRO_BA_MASK;
291
292		/*
293		 * We have to set the I/O resources by hand because it is
294		 * described outside the viapmope of the traditional maps
295		 */
296		viapm->iorid = base_cfgreg;
297		if (bus_set_resource(dev, SYS_RES_IOPORT, viapm->iorid,
298				     viapm->base, 16)) {
299			device_printf(dev, "could not set bus resource 0x%x\n",
300					viapm->base);
301			return ENXIO;
302		}
303
304		if (1 || bootverbose) {
305			device_printf(dev, "SMBus I/O base at 0x%x\n", viapm->base);
306		}
307
308		device_set_desc(dev, desc);
309		return 0;
310
311	default:
312		break;
313	}
314
315	return ENXIO;
316}
317
318static int
319viapm_pro_attach(device_t dev)
320{
321	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
322	u_int32_t l;
323
324	if (!(viapm->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
325		&viapm->iorid, RF_ACTIVE))) {
326		device_printf(dev, "could not allocate bus space\n");
327		goto error;
328	}
329	viapm->st = rman_get_bustag(viapm->iores);
330	viapm->sh = rman_get_bushandle(viapm->iores);
331
332#if notyet
333	/* force irq 9 */
334	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
335	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 0x80, 1);
336
337	viapm->irqrid = 0;
338	if (!(viapm->irqres = bus_alloc_resource(dev, SYS_RES_IRQ,
339				&viapm->irqrid, 9, 9, 1,
340				RF_SHAREABLE | RF_ACTIVE))) {
341		device_printf(dev, "could not allocate irq\n");
342		goto error;
343	}
344
345	if (bus_setup_intr(dev, viapm->irqres, INTR_TYPE_MISC,
346			(driver_intr_t *) viasmb_intr, viapm, &viapm->irqih)) {
347		device_printf(dev, "could not setup irq\n");
348		goto error;
349	}
350#endif
351
352	if (1 | bootverbose) {
353		l = pci_read_config(dev, VIAPM_PRO_REVID, 1);
354		device_printf(dev, "SMBus revision code 0x%x\n", l);
355	}
356
357	viapm->smbus = device_add_child(dev, "smbus", -1);
358
359	/* probe and attach the smbus */
360	bus_generic_attach(dev);
361
362	/* disable slave function */
363	VIAPM_OUTB(SMBSCTRL, VIAPM_INB(SMBSCTRL) & ~SMBSCTRL_ENABLE);
364
365	/* enable the SMBus controller function */
366	l = pci_read_config(dev, VIAPM_PRO_SMBCTRL, 1);
367	pci_write_config(dev, VIAPM_PRO_SMBCTRL, l | 1, 1);
368
369#if notyet
370	/* enable interrupts */
371	VIAPM_OUTB(SMBHCTRL, VIAPM_INB(SMBHCTRL) | SMBHCTRL_ENABLE);
372#endif
373
374	return 0;
375
376error:
377	if (viapm->iores)
378		bus_release_resource(dev, SYS_RES_IOPORT, viapm->iorid, viapm->iores);
379#if notyet
380	if (viapm->irqres)
381		bus_release_resource(dev, SYS_RES_IRQ, viapm->irqrid, viapm->irqres);
382#endif
383
384	return ENXIO;
385}
386
387static int
388viapm_586b_attach(device_t dev)
389{
390	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
391
392	if (!(viapm->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
393		&viapm->iorid, RF_ACTIVE | RF_SHAREABLE))) {
394		device_printf(dev, "could not allocate bus resource\n");
395		return ENXIO;
396	}
397	viapm->st = rman_get_bustag(viapm->iores);
398	viapm->sh = rman_get_bushandle(viapm->iores);
399
400	VIAPM_OUTB(GPIO_DIR, VIAPM_INB(GPIO_DIR) | VIAPM_SCL | VIAPM_SDA);
401
402	/* add generic bit-banging code */
403	if (!(viapm->iicbb = device_add_child(dev, "iicbb", -1)))
404		goto error;
405
406	bus_generic_attach(dev);
407
408	return 0;
409
410error:
411	if (viapm->iores)
412		bus_release_resource(dev, SYS_RES_IOPORT,
413					viapm->iorid, viapm->iores);
414	return ENXIO;
415}
416
417static int
418viapm_586b_detach(device_t dev)
419{
420	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
421	int error;
422
423	bus_generic_detach(dev);
424	if (viapm->iicbb) {
425		device_delete_child(dev, viapm->iicbb);
426	}
427
428	if (viapm->iores && (error = bus_release_resource(dev, SYS_RES_IOPORT,
429						viapm->iorid, viapm->iores)))
430		return (error);
431
432	return 0;
433}
434
435static int
436viapm_pro_detach(device_t dev)
437{
438	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
439	int error;
440
441	bus_generic_detach(dev);
442	if (viapm->smbus) {
443		device_delete_child(dev, viapm->smbus);
444	}
445
446	if ((error = bus_release_resource(dev, SYS_RES_IOPORT,
447				viapm->iorid, viapm->iores)))
448		return (error);
449
450#if notyet
451	if ((error = bus_release_resource(dev, SYS_RES_IRQ,
452					viapm->irqrid, viapm->irqres))
453		return (error);
454#endif
455
456	return 0;
457}
458
459static int
460viabb_callback(device_t dev, int index, caddr_t *data)
461{
462	return 0;
463}
464
465static void
466viabb_setscl(device_t dev, int ctrl)
467{
468	struct viapm_softc *viapm = device_get_softc(dev);
469	u_char val;
470
471	val = VIAPM_INB(GPIO_VAL);
472
473	if (ctrl)
474		val |= VIAPM_SCL;
475	else
476		val &= ~VIAPM_SCL;
477
478	VIAPM_OUTB(GPIO_VAL, val);
479
480	return;
481}
482
483static void
484viabb_setsda(device_t dev, int data)
485{
486	struct viapm_softc *viapm = device_get_softc(dev);
487	u_char val;
488
489	val = VIAPM_INB(GPIO_VAL);
490
491	if (data)
492		val |= VIAPM_SDA;
493	else
494		val &= ~VIAPM_SDA;
495
496	VIAPM_OUTB(GPIO_VAL, val);
497
498	return;
499}
500
501static int
502viabb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
503{
504	/* reset bus */
505	viabb_setsda(dev, 1);
506	viabb_setscl(dev, 1);
507
508	return (IIC_ENOADDR);
509}
510
511static int
512viabb_getscl(device_t dev)
513{
514	struct viapm_softc *viapm = device_get_softc(dev);
515
516	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SCL) != 0);
517}
518
519static int
520viabb_getsda(device_t dev)
521{
522	struct viapm_softc *viapm = device_get_softc(dev);
523
524	return ((VIAPM_INB(EXTSMI_VAL) & VIAPM_SDA) != 0);
525}
526
527static int
528viapm_abort(struct viapm_softc *viapm)
529{
530	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL);
531	DELAY(10);
532
533	return (0);
534}
535
536static int
537viapm_clear(struct viapm_softc *viapm)
538{
539	VIAPM_OUTB(SMBHST, SMBHST_FAILED | SMBHST_COLLID |
540		SMBHST_ERROR | SMBHST_INTR);
541	DELAY(10);
542
543	return (0);
544}
545
546static int
547viapm_busy(struct viapm_softc *viapm)
548{
549	u_char sts;
550
551	sts = VIAPM_INB(SMBHST);
552
553	VIAPM_DEBUG(printf("viapm: idle? STS=0x%x\n", sts));
554
555	return (sts & SMBHST_BUSY);
556}
557
558/*
559 * Poll the SMBus controller
560 */
561static int
562viapm_wait(struct viapm_softc *viapm)
563{
564	int count = 10000;
565	u_char sts = 0;
566	int error;
567
568	/* wait for command to complete and SMBus controller is idle */
569	while(count--) {
570		DELAY(10);
571		sts = VIAPM_INB(SMBHST);
572
573		/* check if the controller is processing a command */
574		if (!(sts & SMBHST_BUSY) && (sts & SMBHST_INTR))
575			break;
576	}
577
578	VIAPM_DEBUG(printf("viapm: SMBHST=0x%x\n", sts));
579
580	error = SMB_ENOERR;
581
582	if (!count)
583		error |= SMB_ETIMEOUT;
584
585	if (sts & SMBHST_FAILED)
586		error |= SMB_EABORT;
587
588	if (sts & SMBHST_COLLID)
589		error |= SMB_ENOACK;
590
591	if (sts & SMBHST_ERROR)
592		error |= SMB_EBUSERR;
593
594	if (error != SMB_ENOERR)
595		viapm_abort(viapm);
596
597	viapm_clear(viapm);
598
599	return (error);
600}
601
602static int
603viasmb_callback(device_t dev, int index, caddr_t *data)
604{
605	int error = 0;
606
607	switch (index) {
608	case SMB_REQUEST_BUS:
609	case SMB_RELEASE_BUS:
610		/* ok, bus allocation accepted */
611		break;
612	default:
613		error = EINVAL;
614	}
615
616	return (error);
617}
618
619static int
620viasmb_quick(device_t dev, u_char slave, int how)
621{
622	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
623	int error;
624
625	viapm_clear(viapm);
626	if (viapm_busy(viapm))
627		return (EBUSY);
628
629	switch (how) {
630	case SMB_QWRITE:
631		VIAPM_DEBUG(printf("viapm: QWRITE to 0x%x", slave));
632		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
633		break;
634	case SMB_QREAD:
635		VIAPM_DEBUG(printf("viapm: QREAD to 0x%x", slave));
636		VIAPM_OUTB(SMBHADDR, slave | LSB);
637		break;
638	default:
639		panic("%s: unknown QUICK command (%x)!", __func__, how);
640	}
641
642	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_QUICK);
643
644	error = viapm_wait(viapm);
645
646	return (error);
647}
648
649static int
650viasmb_sendb(device_t dev, u_char slave, char byte)
651{
652	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
653	int error;
654
655	viapm_clear(viapm);
656	if (viapm_busy(viapm))
657		return (EBUSY);
658
659	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
660	VIAPM_OUTB(SMBHCMD, byte);
661
662	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
663
664	error = viapm_wait(viapm);
665
666	VIAPM_DEBUG(printf("viapm: SENDB to 0x%x, byte=0x%x, error=0x%x\n", slave, byte, error));
667
668	return (error);
669}
670
671static int
672viasmb_recvb(device_t dev, u_char slave, char *byte)
673{
674	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
675	int error;
676
677	viapm_clear(viapm);
678	if (viapm_busy(viapm))
679		return (EBUSY);
680
681	VIAPM_OUTB(SMBHADDR, slave | LSB);
682
683	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_SENDRECV);
684
685	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
686		*byte = VIAPM_INB(SMBHDATA0);
687
688	VIAPM_DEBUG(printf("viapm: RECVB from 0x%x, byte=0x%x, error=0x%x\n", slave, *byte, error));
689
690	return (error);
691}
692
693static int
694viasmb_writeb(device_t dev, u_char slave, char cmd, char byte)
695{
696	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
697	int error;
698
699	viapm_clear(viapm);
700	if (viapm_busy(viapm))
701		return (EBUSY);
702
703	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
704	VIAPM_OUTB(SMBHCMD, cmd);
705	VIAPM_OUTB(SMBHDATA0, byte);
706
707	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
708
709	error = viapm_wait(viapm);
710
711	VIAPM_DEBUG(printf("viapm: WRITEB to 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, byte, error));
712
713	return (error);
714}
715
716static int
717viasmb_readb(device_t dev, u_char slave, char cmd, char *byte)
718{
719	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
720	int error;
721
722	viapm_clear(viapm);
723	if (viapm_busy(viapm))
724		return (EBUSY);
725
726	VIAPM_OUTB(SMBHADDR, slave | LSB);
727	VIAPM_OUTB(SMBHCMD, cmd);
728
729	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BYTE);
730
731	if ((error = viapm_wait(viapm)) == SMB_ENOERR)
732		*byte = VIAPM_INB(SMBHDATA0);
733
734	VIAPM_DEBUG(printf("viapm: READB from 0x%x, cmd=0x%x, byte=0x%x, error=0x%x\n", slave, cmd, *byte, error));
735
736	return (error);
737}
738
739static int
740viasmb_writew(device_t dev, u_char slave, char cmd, short word)
741{
742	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
743	int error;
744
745	viapm_clear(viapm);
746	if (viapm_busy(viapm))
747		return (EBUSY);
748
749	VIAPM_OUTB(SMBHADDR, slave & ~ LSB);
750	VIAPM_OUTB(SMBHCMD, cmd);
751	VIAPM_OUTB(SMBHDATA0, word & 0x00ff);
752	VIAPM_OUTB(SMBHDATA1, (word & 0xff00) >> 8);
753
754	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
755
756	error = viapm_wait(viapm);
757
758	VIAPM_DEBUG(printf("viapm: WRITEW to 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, word, error));
759
760	return (error);
761}
762
763static int
764viasmb_readw(device_t dev, u_char slave, char cmd, short *word)
765{
766	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
767	int error;
768	u_char high, low;
769
770	viapm_clear(viapm);
771	if (viapm_busy(viapm))
772		return (EBUSY);
773
774	VIAPM_OUTB(SMBHADDR, slave | LSB);
775	VIAPM_OUTB(SMBHCMD, cmd);
776
777	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_WORD);
778
779	if ((error = viapm_wait(viapm)) == SMB_ENOERR) {
780		low = VIAPM_INB(SMBHDATA0);
781		high = VIAPM_INB(SMBHDATA1);
782
783		*word = ((high & 0xff) << 8) | (low & 0xff);
784	}
785
786	VIAPM_DEBUG(printf("viapm: READW from 0x%x, cmd=0x%x, word=0x%x, error=0x%x\n", slave, cmd, *word, error));
787
788	return (error);
789}
790
791static int
792viasmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
793{
794	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
795	u_char remain, len, i;
796	int error = SMB_ENOERR;
797
798	viapm_clear(viapm);
799	if (viapm_busy(viapm))
800		return (EBUSY);
801
802	remain = count;
803	while (remain) {
804		len = min(remain, 32);
805
806		VIAPM_OUTB(SMBHADDR, slave & ~LSB);
807		VIAPM_OUTB(SMBHCMD, cmd);
808		VIAPM_OUTB(SMBHDATA0, len);
809		i = VIAPM_INB(SMBHCTRL);
810
811		/* fill the 32-byte internal buffer */
812		for (i=0; i<len; i++) {
813			VIAPM_OUTB(SMBHBLOCK, buf[count-remain+i]);
814			DELAY(2);
815		}
816		VIAPM_OUTB(SMBHCMD, cmd);
817		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
818
819		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
820			goto error;
821
822		remain -= len;
823	}
824
825error:
826	VIAPM_DEBUG(printf("viapm: WRITEBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
827
828	return (error);
829
830}
831
832static int
833viasmb_bread(device_t dev, u_char slave, char cmd, u_char count, char *buf)
834{
835	struct viapm_softc *viapm = (struct viapm_softc *)device_get_softc(dev);
836	u_char remain, len, i;
837	int error = SMB_ENOERR;
838
839	viapm_clear(viapm);
840	if (viapm_busy(viapm))
841		return (EBUSY);
842
843	remain = count;
844	while (remain) {
845		VIAPM_OUTB(SMBHADDR, slave | LSB);
846		VIAPM_OUTB(SMBHCMD, cmd);
847		VIAPM_OUTB(SMBHCTRL, SMBHCTRL_START | SMBHCTRL_BLOCK);
848
849		if ((error = viapm_wait(viapm)) != SMB_ENOERR)
850			goto error;
851
852		len = VIAPM_INB(SMBHDATA0);
853		i = VIAPM_INB(SMBHCTRL); 		/* reset counter */
854
855		len = min(len, remain);
856
857		/* read the 32-byte internal buffer */
858		for (i=0; i<len; i++) {
859			buf[count-remain+i] = VIAPM_INB(SMBHBLOCK);
860			DELAY(2);
861		}
862
863		remain -= len;
864	}
865error:
866	VIAPM_DEBUG(printf("viapm: READBLK to 0x%x, count=0x%x, cmd=0x%x, error=0x%x", slave, count, cmd, error));
867
868	return (error);
869}
870
871static device_method_t viapm_methods[] = {
872	/* device interface */
873	DEVMETHOD(device_probe,		viapm_586b_probe),
874	DEVMETHOD(device_attach,	viapm_586b_attach),
875	DEVMETHOD(device_detach,	viapm_586b_detach),
876
877	/* iicbb interface */
878	DEVMETHOD(iicbb_callback,	viabb_callback),
879	DEVMETHOD(iicbb_setscl,		viabb_setscl),
880	DEVMETHOD(iicbb_setsda,		viabb_setsda),
881	DEVMETHOD(iicbb_getscl,		viabb_getscl),
882	DEVMETHOD(iicbb_getsda,		viabb_getsda),
883	DEVMETHOD(iicbb_reset,		viabb_reset),
884
885	{ 0, 0 }
886};
887
888static driver_t viapm_driver = {
889	"viapm",
890	viapm_methods,
891	sizeof(struct viapm_softc),
892};
893
894static device_method_t viapropm_methods[] = {
895	/* device interface */
896	DEVMETHOD(device_probe,		viapm_pro_probe),
897	DEVMETHOD(device_attach,	viapm_pro_attach),
898	DEVMETHOD(device_detach,	viapm_pro_detach),
899
900	/* smbus interface */
901	DEVMETHOD(smbus_callback,	viasmb_callback),
902	DEVMETHOD(smbus_quick,		viasmb_quick),
903	DEVMETHOD(smbus_sendb,		viasmb_sendb),
904	DEVMETHOD(smbus_recvb,		viasmb_recvb),
905	DEVMETHOD(smbus_writeb,		viasmb_writeb),
906	DEVMETHOD(smbus_readb,		viasmb_readb),
907	DEVMETHOD(smbus_writew,		viasmb_writew),
908	DEVMETHOD(smbus_readw,		viasmb_readw),
909	DEVMETHOD(smbus_bwrite,		viasmb_bwrite),
910	DEVMETHOD(smbus_bread,		viasmb_bread),
911
912	{ 0, 0 }
913};
914
915static driver_t viapropm_driver = {
916	"viapropm",
917	viapropm_methods,
918	sizeof(struct viapm_softc),
919};
920
921DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0);
922DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0);
923
924MODULE_DEPEND(viapm, pci, 1, 1, 1);
925MODULE_DEPEND(viaprom, pci, 1, 1, 1);
926MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
927MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
928MODULE_VERSION(viapm, 1);
929