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