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