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