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