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
61static int viapm_debug = 1;
62#else
63static 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
92struct 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
105static devclass_t viapm_devclass;
106static 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
188static int
189viapm_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
247static int
248viapm_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
343static int
344viapm_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
406error:
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
418static int
419viapm_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
440error:
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
448static int
449viapm_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
466static int
467viapm_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
486static int
487viabb_callback(device_t dev, int index, caddr_t data)
488{
489	return 0;
490}
491
492static void
493viabb_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
512static void
513viabb_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
532static int
533viabb_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
542static int
543viabb_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
554static int
555viabb_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
566static int
567viapm_abort(struct viapm_softc *viapm)
568{
569	VIAPM_OUTB(SMBHCTRL, SMBHCTRL_KILL);
570	DELAY(10);
571
572	return (0);
573}
574
575static int
576viapm_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
585static int
586viapm_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 */
600static int
601viapm_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
643static int
644viasmb_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
660static int
661viasmb_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
694static int
695viasmb_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
720static int
721viasmb_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
746static int
747viasmb_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
773static int
774viasmb_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
800static int
801viasmb_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
828static int
829viasmb_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
860static int
861viasmb_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
899static int
900viasmb_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
935error:
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
942static 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
967static driver_t viapm_driver = {
968	"viapm",
969	viapm_methods,
970	sizeof(struct viapm_softc),
971};
972
973static 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
1002static driver_t viapropm_driver = {
1003	"viapropm",
1004	viapropm_methods,
1005	sizeof(struct viapm_softc),
1006};
1007
1008DRIVER_MODULE(viapm, pci, viapm_driver, viapm_devclass, 0, 0);
1009DRIVER_MODULE(viapropm, pci, viapropm_driver, viapropm_devclass, 0, 0);
1010DRIVER_MODULE(iicbb, viapm, iicbb_driver, iicbb_devclass, 0, 0);
1011DRIVER_MODULE(smbus, viapropm, smbus_driver, smbus_devclass, 0, 0);
1012
1013MODULE_DEPEND(viapm, pci, 1, 1, 1);
1014MODULE_DEPEND(viapropm, pci, 1, 1, 1);
1015MODULE_DEPEND(viapm, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
1016MODULE_DEPEND(viapropm, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
1017MODULE_VERSION(viapm, 1);
1018
1019#ifdef DEV_ISA
1020DRIVER_MODULE(isa, viapm, isa_driver, isa_devclass, 0, 0);
1021DRIVER_MODULE(isa, viapropm, isa_driver, isa_devclass, 0, 0);
1022MODULE_DEPEND(viapm, isa, 1, 1, 1);
1023MODULE_DEPEND(viapropm, isa, 1, 1, 1);
1024#endif
1025